Merge "Def/Use mask fix for vldm/vstm" into dalvik-dev
diff --git a/build/Android.common.mk b/build/Android.common.mk
index c62b038..c22deed 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -64,6 +64,7 @@
 	src/calling_convention.cc \
 	src/calling_convention_arm.cc \
 	src/calling_convention_x86.cc \
+	src/context.cc \
 	src/check_jni.cc \
 	src/class_linker.cc \
 	src/class_loader.cc \
@@ -135,6 +136,7 @@
 
 LIBART_TARGET_SRC_FILES := \
 	$(LIBART_COMMON_SRC_FILES) \
+	src/context_arm.cc.arm \
 	src/logging_android.cc \
 	src/runtime_android.cc \
 	src/thread_android.cc \
@@ -142,6 +144,7 @@
 
 LIBART_HOST_SRC_FILES := \
 	$(LIBART_COMMON_SRC_FILES) \
+	src/context_x86.cc \
 	src/logging_linux.cc \
 	src/runtime_linux.cc \
 	src/thread_linux.cc \
diff --git a/src/assembler.h b/src/assembler.h
index 173609e..4d085e0 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -302,15 +302,11 @@
 
   // Emit code that will create an activation on the stack
   virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg,
-                          const std::vector<ManagedRegister>& spill_regs) = 0;
+                          const std::vector<ManagedRegister>& callee_save_regs) = 0;
 
   // Emit code that will remove an activation from the stack
   virtual void RemoveFrame(size_t frame_size,
-                           const std::vector<ManagedRegister>& spill_regs) = 0;
-
-  // Fill list of registers from spill area
-  virtual void FillFromSpillArea(const std::vector<ManagedRegister>& spill_regs,
-                                 size_t displacement) = 0;
+                           const std::vector<ManagedRegister>& callee_save_regs) = 0;
 
   virtual void IncreaseFrameSize(size_t adjust) = 0;
   virtual void DecreaseFrameSize(size_t adjust) = 0;
@@ -395,6 +391,7 @@
                     ManagedRegister scratch) = 0;
   virtual void Call(FrameOffset base, Offset offset,
                     ManagedRegister scratch) = 0;
+  virtual void Call(ThreadOffset offset, ManagedRegister scratch) = 0;
 
   // Generate code to check if Thread::Current()->suspend_count_ is non-zero
   // and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index d0fae17..c4dbbba 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1414,46 +1414,55 @@
 }
 
 void ArmAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
-                              const std::vector<ManagedRegister>& spill_regs) {
+                              const std::vector<ManagedRegister>& callee_save_regs) {
   CHECK(IsAligned(frame_size, kStackAlignment));
   CHECK_EQ(R0, method_reg.AsArm().AsCoreRegister());
-  AddConstant(SP, -frame_size);
-  RegList spill_list = 1 << R0 | 1 << LR;
-  for (size_t i = 0; i < spill_regs.size(); i++) {
-    Register reg = spill_regs.at(i).AsArm().AsCoreRegister();
-    // check assumption LR is the last register that gets spilled
-    CHECK_LT(reg, LR);
-    spill_list |= 1 << reg;
+
+  // Push callee saves and link register
+  RegList push_list = 1 << LR;
+  size_t pushed_values = 1;
+  for (size_t i = 0; i < callee_save_regs.size(); i++) {
+    Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
+    push_list |= 1 << reg;
+    pushed_values++;
   }
-  // Store spill list from (low to high number register) starting at SP
-  // incrementing after each store but not updating SP
-  stm(IA, SP, spill_list, AL);
+  PushList(push_list);
+
+  // Increase frame to required size
+  CHECK_GT(frame_size, pushed_values * kPointerSize);  // Must be at least space to push Method*
+  size_t adjust = frame_size - (pushed_values * kPointerSize);
+  IncreaseFrameSize(adjust);
+
+  // Write out Method*
+  StoreToOffset(kStoreWord, R0, SP, 0);
 }
 
 void ArmAssembler::RemoveFrame(size_t frame_size,
-                              const std::vector<ManagedRegister>& spill_regs) {
+                              const std::vector<ManagedRegister>& callee_save_regs) {
   CHECK(IsAligned(frame_size, kStackAlignment));
-  // Reload LR. TODO: reload any saved callee saves from spill_regs
-  LoadFromOffset(kLoadWord, LR, SP, (spill_regs.size() + 1) * kPointerSize);
-  AddConstant(SP, frame_size);
-  mov(PC, ShifterOperand(LR));
-}
-
-void ArmAssembler::FillFromSpillArea(const std::vector<ManagedRegister>& spill_regs,
-                                     size_t displacement) {
-  for(size_t i = 0; i < spill_regs.size(); i++) {
-    Register reg = spill_regs.at(i).AsArm().AsCoreRegister();
-    LoadFromOffset(kLoadWord, reg, SP, displacement + ((i + 1) * kPointerSize));
+  // Compute callee saves to pop and PC
+  RegList pop_list = 1 << PC;
+  size_t pop_values = 1;
+  for (size_t i = 0; i < callee_save_regs.size(); i++) {
+    Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
+    pop_list |= 1 << reg;
+    pop_values++;
   }
+
+  // Decrease frame to start of callee saves
+  CHECK_GT(frame_size, pop_values * kPointerSize);
+  size_t adjust = frame_size - (pop_values * kPointerSize);
+  DecreaseFrameSize(adjust);
+
+  // Pop callee saves and PC
+  PopList(pop_list);
 }
 
 void ArmAssembler::IncreaseFrameSize(size_t adjust) {
-  CHECK(IsAligned(adjust, kStackAlignment));
   AddConstant(SP, -adjust);
 }
 
 void ArmAssembler::DecreaseFrameSize(size_t adjust) {
-  CHECK(IsAligned(adjust, kStackAlignment));
   AddConstant(SP, adjust);
 }
 
@@ -1752,6 +1761,10 @@
   // TODO: place reference map on call
 }
 
+void ArmAssembler::Call(ThreadOffset offset, ManagedRegister scratch) {
+  UNIMPLEMENTED(FATAL);
+}
+
 void ArmAssembler::GetCurrentThread(ManagedRegister tr) {
   mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR));
 }
diff --git a/src/assembler_arm.h b/src/assembler_arm.h
index cf031c7..e58eb92 100644
--- a/src/assembler_arm.h
+++ b/src/assembler_arm.h
@@ -415,7 +415,6 @@
   // Emit data (e.g. encoded instruction or immediate) to the
   // instruction stream.
   void Emit(int32_t value);
-
   void Bind(Label* label);
 
   //
@@ -424,15 +423,11 @@
 
   // Emit code that will create an activation on the stack
   virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg,
-                          const std::vector<ManagedRegister>& spill_regs);
+                          const std::vector<ManagedRegister>& callee_save_regs);
 
   // Emit code that will remove an activation from the stack
   virtual void RemoveFrame(size_t frame_size,
-                           const std::vector<ManagedRegister>& spill_regs);
-
-  // Fill list of registers from spill area
-  virtual void FillFromSpillArea(const std::vector<ManagedRegister>& spill_regs,
-                                 size_t displacement);
+                           const std::vector<ManagedRegister>& callee_save_regs);
 
   virtual void IncreaseFrameSize(size_t adjust);
   virtual void DecreaseFrameSize(size_t adjust);
@@ -441,8 +436,6 @@
   virtual void Store(FrameOffset offs, ManagedRegister src, size_t size);
   virtual void StoreRef(FrameOffset dest, ManagedRegister src);
   virtual void StoreRawPtr(FrameOffset dest, ManagedRegister src);
-  virtual void StoreSpanning(FrameOffset dest, ManagedRegister src,
-                             FrameOffset in_off, ManagedRegister scratch);
 
   virtual void StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
                                      ManagedRegister scratch);
@@ -456,6 +449,9 @@
 
   virtual void StoreStackPointerToThread(ThreadOffset thr_offs);
 
+  virtual void StoreSpanning(FrameOffset dest, ManagedRegister src,
+                             FrameOffset in_off, ManagedRegister scratch);
+
   // Load routines
   virtual void Load(ManagedRegister dest, FrameOffset src, size_t size);
 
@@ -516,6 +512,7 @@
                     ManagedRegister scratch);
   virtual void Call(FrameOffset base, Offset offset,
                     ManagedRegister scratch);
+  virtual void Call(ThreadOffset offset, ManagedRegister scratch);
 
   // Generate code to check if Thread::Current()->suspend_count_ is non-zero
   // and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/assembler_x86.cc b/src/assembler_x86.cc
index 45c0086..892bf76 100644
--- a/src/assembler_x86.cc
+++ b/src/assembler_x86.cc
@@ -134,6 +134,12 @@
   EmitImmediate(imm);
 }
 
+void X86Assembler::movl(const Address& dst, Label* lbl) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xC7);
+  EmitOperand(0, dst);
+  EmitLabel(lbl, dst.length_ + 5);
+}
 
 void X86Assembler::movzxb(Register dst, ByteRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -1389,11 +1395,6 @@
   ret();
 }
 
-void X86Assembler::FillFromSpillArea(
-    const std::vector<ManagedRegister>& spill_regs, size_t displacement) {
-  CHECK_EQ(0u, spill_regs.size());  // no spilled regs on x86
-}
-
 void X86Assembler::IncreaseFrameSize(size_t adjust) {
   CHECK(IsAligned(adjust, kStackAlignment));
   addl(ESP, Immediate(-adjust));
@@ -1467,6 +1468,10 @@
   fs()->movl(Address::Absolute(thr_offs), ESP);
 }
 
+void X86Assembler::StoreLabelToThread(ThreadOffset thr_offs, Label* lbl) {
+  fs()->movl(Address::Absolute(thr_offs), lbl);
+}
+
 void X86Assembler::StoreSpanning(FrameOffset dest, ManagedRegister src,
                                  FrameOffset in_off, ManagedRegister scratch) {
   UNIMPLEMENTED(FATAL);  // this case only currently exists for ARM
@@ -1654,19 +1659,11 @@
 }
 
 void X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister) {
-  // TODO: Needed for:
-  // JniCompilerTest.CompileAndRunIntObjectObjectMethod
-  // JniCompilerTest.CompileAndRunStaticIntObjectObjectMethod
-  // JniCompilerTest.CompileAndRunStaticSynchronizedIntObjectObjectMethod
-  // JniCompilerTest.ReturnGlobalRef
   UNIMPLEMENTED(FATAL);
 }
 
-// TODO: remove this generator of non-PIC code
-void X86Assembler::Call(uintptr_t addr, ManagedRegister mscratch) {
-  Register scratch = mscratch.AsX86().AsCpuRegister();
-  movl(scratch, Immediate(addr));
-  call(scratch);
+void X86Assembler::Call(ThreadOffset offset, ManagedRegister mscratch) {
+  fs()->call(Address::Absolute(offset));
 }
 
 void X86Assembler::GetCurrentThread(ManagedRegister tr) {
diff --git a/src/assembler_x86.h b/src/assembler_x86.h
index c893633..86069be 100644
--- a/src/assembler_x86.h
+++ b/src/assembler_x86.h
@@ -221,6 +221,7 @@
   void movl(Register dst, const Address& src);
   void movl(const Address& dst, Register src);
   void movl(const Address& dst, const Immediate& imm);
+  void movl(const Address& dst, Label* lbl);
 
   void movzxb(Register dst, ByteRegister src);
   void movzxb(Register dst, const Address& src);
@@ -444,15 +445,11 @@
 
   // Emit code that will create an activation on the stack
   virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg,
-                          const std::vector<ManagedRegister>& spill_regs);
+                          const std::vector<ManagedRegister>& callee_save_regs);
 
   // Emit code that will remove an activation from the stack
   virtual void RemoveFrame(size_t frame_size,
-                           const std::vector<ManagedRegister>& spill_regs);
-
-  // Fill list of registers from spill area
-  virtual void FillFromSpillArea(const std::vector<ManagedRegister>& spill_regs,
-                                 size_t displacement);
+                           const std::vector<ManagedRegister>& callee_save_regs);
 
   virtual void IncreaseFrameSize(size_t adjust);
   virtual void DecreaseFrameSize(size_t adjust);
@@ -474,6 +471,8 @@
 
   virtual void StoreStackPointerToThread(ThreadOffset thr_offs);
 
+  void StoreLabelToThread(ThreadOffset thr_offs, Label* lbl);
+
   virtual void StoreSpanning(FrameOffset dest, ManagedRegister src,
                              FrameOffset in_off, ManagedRegister scratch);
 
@@ -537,7 +536,7 @@
                     ManagedRegister scratch);
   virtual void Call(FrameOffset base, Offset offset,
                     ManagedRegister scratch);
-  virtual void Call(uintptr_t addr, ManagedRegister scratch);
+  virtual void Call(ThreadOffset offset, ManagedRegister scratch);
 
   // Generate code to check if Thread::Current()->suspend_count_ is non-zero
   // and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/calling_convention.h b/src/calling_convention.h
index dcca3bc..4464609 100644
--- a/src/calling_convention.h
+++ b/src/calling_convention.h
@@ -64,6 +64,13 @@
 };
 
 // Abstraction for managed code's calling conventions
+// | { Incoming stack args } |
+// | { Prior Method* }       | <-- Prior SP
+// | { Return address }      |
+// | { Callee saves }        |
+// | { Spills ... }          |
+// | { Outgoing stack args } |
+// | { Method* }             | <-- SP
 class ManagedRuntimeCallingConvention : public CallingConvention {
  public:
   static ManagedRuntimeCallingConvention* Create(Method* native_method,
@@ -90,21 +97,22 @@
 
  protected:
   explicit ManagedRuntimeCallingConvention(Method* method) :
-                                          CallingConvention(method) {}
+                                           CallingConvention(method) {}
 };
 
 // Abstraction for JNI calling conventions
-// | incoming stack args    | <-- Prior SP
-// | { Return address }     |     (x86)
-// | { Return value spill } |     (live on return slow paths)
-// | { Stack Indirect Ref.  |
-// |   Table...             |
-// |   num. refs./link }    |     (here to prior SP is frame size)
-// | { Spill area }         |     (ARM)
-// | Method*                | <-- Anchor SP written to thread
-// | { Outgoing stack args  |
-// |   ... }                | <-- SP at point of call
-// | Native frame           |
+// | { Incoming stack args }         | <-- Prior SP
+// | { Return address }              |
+// | { Callee saves }                |     ([1])
+// | { Return value spill }          |     (live on return slow paths)
+// | { Stack Indirect Ref. Table     |
+// |   num. refs./link }             |     (here to prior SP is frame size)
+// | { Method* }                     | <-- Anchor SP written to thread
+// | { Outgoing stack args }         | <-- SP at point of call
+// | Native frame                    |
+//
+// [1] We must save all callee saves here to enable any exception throws to restore
+// callee saves for frames above this one.
 class JniCallingConvention : public CallingConvention {
  public:
   static JniCallingConvention* Create(Method* native_method,
@@ -118,19 +126,18 @@
   virtual size_t ReturnPcOffset() = 0;
   // Size of outgoing arguments, including alignment
   virtual size_t OutArgSize() = 0;
-  // Size of area used to hold spilled registers
-  virtual size_t SpillAreaSize() = 0;
   // Number of references in stack indirect reference table
   size_t ReferenceCount();
   // Location where the return value of a call can be squirreled if another
   // call is made following the native call
   FrameOffset ReturnValueSaveLocation();
 
-  // Registers that must be spilled (due to clobbering) before the call into
-  // the native routine
-  const std::vector<ManagedRegister>& RegsToSpillPreCall() {
-    return spill_regs_;
-  }
+  // Callee save registers to spill prior to native code (which may clobber)
+  virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const = 0;
+
+  // Spill mask values
+  virtual uint32_t CoreSpillMask() const = 0;
+  virtual uint32_t FpSpillMask() const = 0;
 
   // Returns true if the register will be clobbered by an outgoing
   // argument value.
@@ -152,7 +159,6 @@
   // Position of SIRT and interior fields
   FrameOffset SirtOffset() {
     return FrameOffset(displacement_.Int32Value() +
-                       SpillAreaSize() +
                        kPointerSize);  // above Method*
   }
   FrameOffset SirtNumRefsOffset() {
@@ -182,9 +188,6 @@
 
  protected:
   static size_t NumberOfExtraArgumentsForJni(Method* method);
-
-  // Extra registers to spill before the call into native
-  std::vector<ManagedRegister> spill_regs_;
 };
 
 }  // namespace art
diff --git a/src/calling_convention_arm.cc b/src/calling_convention_arm.cc
index 4b4a146..f8e8f3d 100644
--- a/src/calling_convention_arm.cc
+++ b/src/calling_convention_arm.cc
@@ -99,20 +99,19 @@
 
 // JNI calling convention
 
-ArmJniCallingConvention::ArmJniCallingConvention(Method* method) :
-    JniCallingConvention(method) {
-  // A synchronized method will call monitor enter clobbering R1, R2 and R3
-  // unless they are spilled.
-  if (method->IsSynchronized()) {
-    spill_regs_.push_back(ArmManagedRegister::FromCoreRegister(R1));
-    spill_regs_.push_back(ArmManagedRegister::FromCoreRegister(R2));
-    spill_regs_.push_back(ArmManagedRegister::FromCoreRegister(R3));
+ArmJniCallingConvention::ArmJniCallingConvention(Method* method) : JniCallingConvention(method) {
+  for (int i = R4; i < R12; i++) {
+    callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(static_cast<Register>(i)));
   }
+  // TODO: VFP
+  // for (SRegister i = S16; i <= S31; i++) {
+  //  callee_save_regs_.push_back(ArmManagedRegister::FromSRegister(i));
+  // }
 }
 
 size_t ArmJniCallingConvention::FrameSize() {
-  // Method* and spill area size
-  size_t frame_data_size = kPointerSize + SpillAreaSize();
+  // Method*, LR and callee save area size
+  size_t frame_data_size = (2 + CalleeSaveRegisters().size()) * kPointerSize;
   // References plus 2 words for SIRT header
   size_t sirt_size = (ReferenceCount() + 2) * kPointerSize;
   // Plus return value spill area size
@@ -135,15 +134,8 @@
 }
 
 size_t ArmJniCallingConvention::ReturnPcOffset() {
-  // Link register is always the last value spilled, skip forward one word for
-  // the Method* then skip back one word to get the link register (ie +0)
-  return SpillAreaSize();
-}
-
-size_t ArmJniCallingConvention::SpillAreaSize() {
-  // Space for link register. For synchronized methods we need enough space to
-  // save R1, R2 and R3 (R0 is the method register and always preserved)
-  return GetMethod()->IsSynchronized() ? (4 * kPointerSize) : kPointerSize;
+  // Link register is always the first value pushed when the frame is constructed
+  return FrameSize() - kPointerSize;
 }
 
 // Will reg be crushed by an outgoing argument?
@@ -164,7 +156,7 @@
   int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
   if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
     // itr_slots_ needs to be an even number, according to AAPCS.
-    if (itr_slots_ & 0x1u) {
+    if ((itr_slots_ & 0x1u) != 0) {
       itr_slots_++;
     }
   }
diff --git a/src/calling_convention_arm.h b/src/calling_convention_arm.h
index e9c8ab0..8e5d4c2 100644
--- a/src/calling_convention_arm.h
+++ b/src/calling_convention_arm.h
@@ -38,7 +38,15 @@
   virtual size_t FrameSize();
   virtual size_t ReturnPcOffset();
   virtual size_t OutArgSize();
-  virtual size_t SpillAreaSize();
+  virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const {
+    return callee_save_regs_;
+  }
+  virtual uint32_t CoreSpillMask() const {
+    return 0x0FF0;  // R4 to R12
+  }
+  virtual uint32_t FpSpillMask() const {
+    return 0;
+  }
   virtual bool IsOutArgRegister(ManagedRegister reg);
   virtual bool IsCurrentParamInRegister();
   virtual bool IsCurrentParamOnStack();
@@ -49,6 +57,9 @@
   virtual size_t NumberOfOutgoingStackArgs();
 
  private:
+  // TODO: these values aren't unique and can be shared amongst instances
+  std::vector<ManagedRegister> callee_save_regs_;
+
   DISALLOW_COPY_AND_ASSIGN(ArmJniCallingConvention);
 };
 
diff --git a/src/calling_convention_x86.cc b/src/calling_convention_x86.cc
index 6cc556a..af464af 100644
--- a/src/calling_convention_x86.cc
+++ b/src/calling_convention_x86.cc
@@ -65,6 +65,8 @@
 
 // JNI calling convention
 
+std::vector<ManagedRegister> X86JniCallingConvention::callee_save_regs_;
+
 size_t X86JniCallingConvention::FrameSize() {
   // Return address and Method*
   size_t frame_data_size = 2 * kPointerSize;
@@ -84,12 +86,6 @@
   return FrameSize() - kPointerSize;
 }
 
-
-size_t X86JniCallingConvention::SpillAreaSize() {
-  // No spills, return address was pushed at the top of the frame
-  return 0;
-}
-
 bool X86JniCallingConvention::IsOutArgRegister(ManagedRegister) {
   return false;  // Everything is passed by stack
 }
@@ -117,7 +113,7 @@
   // regular argument parameters and this
   size_t param_args = GetMethod()->NumArgs() +
                       GetMethod()->NumLongOrDoubleArgs();
-  return static_args + param_args + 1;  // count JNIEnv*
+  return static_args + param_args + 2;  // count JNIEnv* and return pc (pushed after Method*)
 }
 
 }  // namespace x86
diff --git a/src/calling_convention_x86.h b/src/calling_convention_x86.h
index d8dda57..8230754 100644
--- a/src/calling_convention_x86.h
+++ b/src/calling_convention_x86.h
@@ -39,7 +39,16 @@
   virtual size_t FrameSize();
   virtual size_t ReturnPcOffset();
   virtual size_t OutArgSize();
-  virtual size_t SpillAreaSize();
+  virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const {
+    DCHECK(callee_save_regs_.empty());
+    return callee_save_regs_;
+  }
+  virtual uint32_t CoreSpillMask() const {
+    return 0;
+  }
+  virtual uint32_t FpSpillMask() const {
+    return 0;
+  }
   virtual bool IsOutArgRegister(ManagedRegister reg);
   virtual bool IsCurrentParamInRegister();
   virtual bool IsCurrentParamOnStack();
@@ -50,6 +59,8 @@
   virtual size_t NumberOfOutgoingStackArgs();
 
  private:
+  static std::vector<ManagedRegister> callee_save_regs_;
+
   DISALLOW_COPY_AND_ASSIGN(X86JniCallingConvention);
 };
 
diff --git a/src/class_linker.cc b/src/class_linker.cc
index dde29cf..037887d 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1168,8 +1168,29 @@
   return NULL;
 }
 
+void ClassLinker::VerifyClass(Class* klass) {
+  if (klass->IsVerified()) {
+    return;
+  }
+
+  CHECK_EQ(klass->GetStatus(), Class::kStatusResolved);
+
+  klass->SetStatus(Class::kStatusVerifying);
+  if (!DexVerifier::VerifyClass(klass)) {
+    LOG(ERROR) << "Verification failed on class "
+               << klass->GetDescriptor()->ToModifiedUtf8();
+    Object* exception = Thread::Current()->GetException();
+    klass->SetVerifyErrorClass(exception->GetClass());
+    klass->SetStatus(Class::kStatusError);
+    return;
+  }
+
+  klass->SetStatus(Class::kStatusVerified);
+}
+
 bool ClassLinker::InitializeClass(Class* klass) {
   CHECK(klass->GetStatus() == Class::kStatusResolved ||
+      klass->GetStatus() == Class::kStatusVerified ||
       klass->GetStatus() == Class::kStatusInitializing ||
       klass->GetStatus() == Class::kStatusError)
           << PrettyDescriptor(klass->GetDescriptor()) << " is " << klass->GetStatus();
@@ -1185,18 +1206,10 @@
         return false;
       }
 
-      CHECK(klass->GetStatus() == Class::kStatusResolved);
-
-      klass->SetStatus(Class::kStatusVerifying);
-      if (!DexVerifier::VerifyClass(klass)) {
-        LG << "Verification failed";  // TODO: ThrowVerifyError
-        Object* exception = self->GetException();
-        klass->SetVerifyErrorClass(exception->GetClass());
-        klass->SetStatus(Class::kStatusError);
+      VerifyClass(klass);
+      if (klass->GetStatus() != Class::kStatusVerified) {
         return false;
       }
-
-      klass->SetStatus(Class::kStatusVerified);
     }
 
     if (klass->GetStatus() == Class::kStatusInitialized) {
diff --git a/src/class_linker.h b/src/class_linker.h
index e143fd8..108059c 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -155,6 +155,8 @@
 
   ObjectArray<StackTraceElement>* AllocStackTraceElementArray(size_t length);
 
+  void VerifyClass(Class* klass);
+
  private:
   ClassLinker(InternTable*);
 
@@ -181,7 +183,12 @@
   Class* AllocClass(size_t class_size);
   DexCache* AllocDexCache(const DexFile& dex_file);
   Field* AllocField();
+
+  // TODO: have no friends, we need this currently to create a special method
+  // to describe callee save registers for throwing exceptions
+  friend class Thread;
   Method* AllocMethod();
+
   CodeAndDirectMethods* AllocCodeAndDirectMethods(size_t length);
   InterfaceEntry* AllocInterfaceEntry(Class* interface);
 
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 4b48892..32f2eec 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -108,6 +108,7 @@
 
   void AssertMethod(Class* klass, Method* method) {
     EXPECT_TRUE(method != NULL);
+    EXPECT_TRUE(method->GetClass() != NULL);
     EXPECT_TRUE(method->GetName() != NULL);
     EXPECT_TRUE(method->GetSignature() != NULL);
 
@@ -133,6 +134,7 @@
 
   void AssertField(Class* klass, Field* field) {
     EXPECT_TRUE(field != NULL);
+    EXPECT_TRUE(field->GetClass() != NULL);
     EXPECT_EQ(klass, field->GetDeclaringClass());
     EXPECT_TRUE(field->GetName() != NULL);
     EXPECT_TRUE(field->GetType() != NULL);
@@ -146,6 +148,8 @@
       EXPECT_TRUE(klass->HasSuperClass());
       EXPECT_TRUE(klass->GetSuperClass() != NULL);
     }
+    EXPECT_TRUE(klass->GetClass() != NULL);
+    EXPECT_EQ(klass->GetClass(), klass->GetClass()->GetClass());
     EXPECT_TRUE(klass->GetDexCache() != NULL);
     EXPECT_TRUE(klass->IsResolved());
     EXPECT_FALSE(klass->IsErroneous());
@@ -545,6 +549,8 @@
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, dex_cache_strings_),                    "shadow$_dex_cache_strings_"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, invoke_stub_array_),                    "shadow$_invoke_stub_array_"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, mapping_table_),                        "shadow$_mapping_table_"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, register_map_data_),                    "shadow$_register_map_data_"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, register_map_header_),                  "shadow$_register_map_header_"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, shorty_),                               "shadow$_shorty_"));
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Method, signature_),                            "shadow$_signature_"));
 
diff --git a/src/compiler.cc b/src/compiler.cc
index 8a382f5..b88fd5a 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -14,43 +14,27 @@
 
 namespace art {
 
-typedef void (*ThrowAme)(Method*, Thread*);
-
-void ThrowAbstractMethodError(Method* method, Thread* thread) {
-  LOG(FATAL) << "Unimplemented Exception Handling. Remove this when ThrowException works.";
-  thread->ThrowNewException("Ljava/lang/AbstractMethodError",
-                            "abstract method \"%s\"",
-                            PrettyMethod(method).c_str());
-}
-
 namespace arm {
-  ByteArray* CreateAbstractMethodErrorStub(ThrowAme);
+  ByteArray* CreateAbstractMethodErrorStub();
 }
 
 namespace x86 {
-  ByteArray* CreateAbstractMethodErrorStub(ThrowAme);
+  ByteArray* CreateAbstractMethodErrorStub();
 }
 
 Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns),
     verbose_(false) {
   if (insns == kArm || insns == kThumb2) {
-    abstract_method_error_stub_ = arm::CreateAbstractMethodErrorStub(&ThrowAbstractMethodError);
+    abstract_method_error_stub_ = arm::CreateAbstractMethodErrorStub();
   } else if (insns == kX86) {
-    abstract_method_error_stub_ = x86::CreateAbstractMethodErrorStub(&ThrowAbstractMethodError);
+    abstract_method_error_stub_ = x86::CreateAbstractMethodErrorStub();
   }
 }
 
 void Compiler::CompileAll(const ClassLoader* class_loader) {
   Resolve(class_loader);
-  // TODO: add verification step
-
-  // TODO: mark all verified classes initialized if they have no <clinit>
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Class* Class_class = class_linker->FindSystemClass("Ljava/lang/Class;");
-  Method* Class_clinit = Class_class->FindDirectMethod("<clinit>", "()V");
-  CHECK(Class_clinit == NULL);
-  Class_class->SetStatus(Class::kStatusInitialized);
-
+  Verify(class_loader);
+  InitializeClassesWithoutClinit(class_loader);
   Compile(class_loader);
   SetCodeAndDirectMethods(class_loader);
 }
@@ -58,7 +42,8 @@
 void Compiler::CompileOne(Method* method) {
   const ClassLoader* class_loader = method->GetDeclaringClass()->GetClassLoader();
   Resolve(class_loader);
-  // TODO: add verification step
+  Verify(class_loader);
+  InitializeClassesWithoutClinit(class_loader);
   CompileMethod(method);
   SetCodeAndDirectMethods(class_loader);
 }
@@ -83,7 +68,8 @@
 
   // Class derived values are more complicated, they require the linker and loader
   for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-    class_linker->ResolveType(dex_file, i, dex_cache, class_loader);
+    Class* klass = class_linker->ResolveType(dex_file, i, dex_cache, class_loader);
+    CHECK(klass->IsResolved());
   }
   for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
     // unknown if direct or virtual, try both
@@ -101,6 +87,62 @@
   }
 }
 
+void Compiler::Verify(const ClassLoader* class_loader) {
+  const std::vector<const DexFile*>& class_path = ClassLoader::GetClassPath(class_loader);
+  for (size_t i = 0; i != class_path.size(); ++i) {
+    const DexFile* dex_file = class_path[i];
+    CHECK(dex_file != NULL);
+    VerifyDexFile(class_loader, *dex_file);
+  }
+}
+
+void Compiler::VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+    const char* descriptor = dex_file.GetClassDescriptor(class_def);
+    Class* klass = class_linker->FindClass(descriptor, class_loader);
+    CHECK(klass->IsResolved());
+    CHECK(klass != NULL);
+    class_linker->VerifyClass(klass);
+    CHECK(klass->IsVerified() || klass->IsErroneous());
+  }
+}
+
+void Compiler::InitializeClassesWithoutClinit(const ClassLoader* class_loader) {
+  const std::vector<const DexFile*>& class_path = ClassLoader::GetClassPath(class_loader);
+  for (size_t i = 0; i != class_path.size(); ++i) {
+    const DexFile* dex_file = class_path[i];
+    CHECK(dex_file != NULL);
+    InitializeClassesWithoutClinit(class_loader, *dex_file);
+  }
+}
+
+void Compiler::InitializeClassesWithoutClinit(const ClassLoader* class_loader, const DexFile& dex_file) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+    const char* descriptor = dex_file.GetClassDescriptor(class_def);
+    Class* klass = class_linker->FindClass(descriptor, class_loader);
+    CHECK(klass != NULL);
+    if (klass->IsInitialized()) {
+      continue;
+    }
+    CHECK(klass->IsVerified() || klass->IsErroneous());
+    if (!klass->IsVerified()) {
+      continue;
+    }
+    Method* clinit = klass->FindDirectMethod("<clinit>", "()V");
+    if (clinit != NULL) {
+      continue;
+    }
+    klass->SetStatus(Class::kStatusInitialized);
+    // TODO: enable after crash investigation
+    // DexCache* dex_cache = class_linker->FindDexCache(dex_file);
+    // dex_cache->GetInitializedStaticStorage()->Set(i, klass);
+  }
+}
+
 void Compiler::Compile(const ClassLoader* class_loader) {
   const std::vector<const DexFile*>& class_path = ClassLoader::GetClassPath(class_loader);
   for (size_t i = 0; i != class_path.size(); ++i) {
@@ -141,7 +183,10 @@
   if (method->IsNative()) {
     jni_compiler_.Compile(method);
     // unregister will install the stub to lookup via dlsym
-    method->UnregisterNative();
+    // TODO: this is only necessary for tests
+    if (!method->IsRegistered()) {
+      method->UnregisterNative();
+    }
   } else if (method->IsAbstract()) {
     DCHECK(abstract_method_error_stub_ != NULL);
     if (instruction_set_ == kX86) {
diff --git a/src/compiler.h b/src/compiler.h
index 958d9ab..26b33b8 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -35,6 +35,12 @@
   void Resolve(const ClassLoader* class_loader);
   void ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
 
+  void Verify(const ClassLoader* class_loader);
+  void VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
+
+  void InitializeClassesWithoutClinit(const ClassLoader* class_loader);
+  void InitializeClassesWithoutClinit(const ClassLoader* class_loader, const DexFile& dex_file);
+
   void Compile(const ClassLoader* class_loader);
   void CompileDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
   void CompileClass(Class* klass);
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 9e6617e..fdcce9c 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -876,19 +876,19 @@
     }
 
     art::ByteArray* managed_code =
-        art::ByteArray::Alloc(cUnit.codeBuffer.size() *
-        sizeof(cUnit.codeBuffer[0]));
+        art::ByteArray::Alloc(cUnit.codeBuffer.size() * sizeof(cUnit.codeBuffer[0]));
     memcpy(managed_code->GetData(),
            reinterpret_cast<const int8_t*>(&cUnit.codeBuffer[0]),
            managed_code->GetLength());
-    art::ByteArray* mapping_table =
-        art::ByteArray::Alloc(cUnit.mappingTable.size() *
-        sizeof(cUnit.mappingTable[0]));
+    art::IntArray* mapping_table =
+        art::IntArray::Alloc(cUnit.mappingTable.size());
+    DCHECK_EQ(mapping_table->GetClass()->GetComponentSize(), sizeof(cUnit.mappingTable[0]));
     memcpy(mapping_table->GetData(),
-           reinterpret_cast<const int8_t*>(&cUnit.mappingTable[0]),
-           mapping_table->GetLength());
+           reinterpret_cast<const int32_t*>(&cUnit.mappingTable[0]),
+           mapping_table->GetLength() * sizeof(cUnit.mappingTable[0]));
     method->SetCode(managed_code, art::kThumb2, mapping_table);
     method->SetFrameSizeInBytes(cUnit.frameSize);
+    method->SetReturnPcOffsetInBytes(cUnit.frameSize - sizeof(intptr_t));
     method->SetCoreSpillMask(cUnit.coreSpillMask);
     method->SetFpSpillMask(cUnit.fpSpillMask);
     if (compiler.IsVerbose()) {
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index c54a0a8..2f63085 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -684,9 +684,8 @@
 {
     loadWordDisp(cUnit, rSELF,
                  OFFSETOF_MEMBER(Thread, pThrowException), rLR);
-    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get exception object
-    genRegCopy(cUnit, r0, rSELF);
-    callUnwindableHelper(cUnit, rLR); // artThrowException(thread, exception);
+    loadValueDirectFixed(cUnit, rlSrc, r0);  // Get exception object
+    callNoUnwindHelper(cUnit, rLR); // art_throw_exception(exception);
 }
 
 static void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
diff --git a/src/compiler_test.cc b/src/compiler_test.cc
index 5c1bbef..5207561 100644
--- a/src/compiler_test.cc
+++ b/src/compiler_test.cc
@@ -227,8 +227,7 @@
   AssertStaticIntMethod(2222, LoadDex("IntMath"), "IntMath", "constClassTest", "(I)I", 1111);
 }
 
-// TODO: Need native nativeFillInStackTrace()
-TEST_F(CompilerTest, DISABLED_CatchTest) {
+TEST_F(CompilerTest, CatchTest) {
   CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
   CompileDirectMethod(NULL, "java.lang.NullPointerException", "<init>", "()V");
   CompileDirectMethod(NULL, "java.lang.RuntimeException", "<init>", "()V");
diff --git a/src/context.cc b/src/context.cc
new file mode 100644
index 0000000..1c001d6
--- /dev/null
+++ b/src/context.cc
@@ -0,0 +1,18 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "context.h"
+
+#include "context_arm.h"
+#include "context_x86.h"
+
+namespace art {
+
+Context* Context::Create() {
+#if defined(__arm__)
+  return new arm::ArmContext();
+#else
+  return new x86::X86Context();
+#endif
+}
+
+}  // namespace art
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..05cd43b
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,36 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_CONTEXT_H_
+#define ART_SRC_CONTEXT_H_
+
+#include <stdint.h>
+
+namespace art {
+
+class Frame;
+
+// Representation of a thread's context on the executing machine
+class Context {
+ public:
+  // Creates a context for the running architecture
+  static Context* Create();
+
+  virtual ~Context() {}
+
+  // Read values from callee saves in the given frame. The frame also holds
+  // the method that holds the layout.
+  virtual void FillCalleeSaves(const Frame& fr) = 0;
+
+  // Set the stack pointer value
+  virtual void SetSP(uintptr_t new_sp) = 0;
+
+  // Set the program counter value
+  virtual void SetPC(uintptr_t new_pc) = 0;
+
+  // Switch execution of the executing context to this context
+  virtual void DoLongJump() = 0;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_CONTEXT_H_
diff --git a/src/context_arm.cc b/src/context_arm.cc
new file mode 100644
index 0000000..3c2af94
--- /dev/null
+++ b/src/context_arm.cc
@@ -0,0 +1,56 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "context_arm.h"
+
+#include "object.h"
+
+namespace art {
+namespace arm {
+
+ArmContext::ArmContext() {
+  for (int i=0; i < 16; i++) {
+    gprs_[i] = 0xEBAD6070;
+  }
+  memset(fprs_, 0, sizeof(fprs_));
+}
+
+void ArmContext::FillCalleeSaves(const Frame& fr) {
+  Method* method = fr.GetMethod();
+  uint32_t core_spills = method->GetCoreSpillMask();
+  size_t spill_count = __builtin_popcount(core_spills);
+  CHECK_EQ(method->GetFpSpillMask(), 0u);
+  if (spill_count > 0) {
+    // Lowest number spill is furthest away, walk registers and fill into context
+    int j = 1;
+    for(int i = 0; i < 16; i++) {
+      if (((core_spills >> i) & 1) != 0) {
+        gprs_[i] = fr.LoadCalleeSave(spill_count - j);
+        j++;
+      }
+    }
+  }
+}
+
+void ArmContext::DoLongJump() {
+  // TODO: Load all GPRs and FPRs, currently the code restores registers R4 to PC
+  asm volatile ( "mov %%r0, %0\n"
+                 "mov %%r1, %1\n"
+                 "ldm %%r0, {%%r4, %%r5, %%r6, %%r7,%%r8,%%r9,%%r10,%%r11,%%r12,%%r13,%%r14}\n"
+                 "mov %%pc,%%r1\n"
+      : // output
+      : "r"(&gprs_[4]), "r"(gprs_[R15])  // input
+#if 0  // TODO: FPRs..
+        "w0" (fprs_[0] ), "w1" (fprs_[1] ), "w2" (fprs_[2] ), "w3" (fprs_[3]),
+        "w4" (fprs_[4] ), "w5" (fprs_[5] ), "w6" (fprs_[6] ), "w7" (fprs_[7]),
+        "w8" (fprs_[8] ), "w9" (fprs_[9] ), "w10"(fprs_[10]), "w11"(fprs_[11]),
+        "w12"(fprs_[12]), "w13"(fprs_[13]), "w14"(fprs_[14]), "w15"(fprs_[15]),
+        "w16"(fprs_[16]), "w17"(fprs_[17]), "w18"(fprs_[18]), "w19"(fprs_[19]),
+        "w20"(fprs_[20]), "w21"(fprs_[21]), "w22"(fprs_[22]), "w23"(fprs_[23]),
+        "w24"(fprs_[24]), "w25"(fprs_[25]), "w26"(fprs_[26]), "w27"(fprs_[27]),
+        "w28"(fprs_[28]), "w29"(fprs_[29]), "w30"(fprs_[30]), "w31"(fprs_[31])
+#endif
+      :);  // clobber
+}
+
+}  // namespace arm
+}  // namespace art
diff --git a/src/context_arm.h b/src/context_arm.h
new file mode 100644
index 0000000..e5a5118
--- /dev/null
+++ b/src/context_arm.h
@@ -0,0 +1,37 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_CONTEXT_ARM_H_
+#define ART_SRC_CONTEXT_ARM_H_
+
+#include "constants_arm.h"
+#include "context.h"
+
+namespace art {
+namespace arm {
+
+class ArmContext : public Context {
+ public:
+  ArmContext();
+  virtual ~ArmContext() {}
+
+  virtual void FillCalleeSaves(const Frame& fr);
+
+  virtual void SetSP(uintptr_t new_sp) {
+    gprs_[SP] = new_sp;
+  }
+
+  virtual void SetPC(uintptr_t new_pc) {
+    gprs_[PC] = new_pc;
+  }
+
+  virtual void DoLongJump();
+
+ private:
+  uintptr_t gprs_[16];
+  float fprs_[32];
+};
+
+}  // namespace arm
+}  // namespace art
+
+#endif  // ART_SRC_CONTEXT_ARM_H_
diff --git a/src/context_x86.cc b/src/context_x86.cc
new file mode 100644
index 0000000..2f328e1
--- /dev/null
+++ b/src/context_x86.cc
@@ -0,0 +1,18 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "context_x86.h"
+
+namespace art {
+namespace x86 {
+
+void X86Context::DoLongJump() {
+  // Load ESP and EIP
+  asm volatile ( "movl %%esp, %0\n"
+                 "jmp *%1"
+      : // output
+      : "m"(esp_), "r"(&eip_)  // input
+      :);  // clobber
+}
+
+}  // namespace x86
+}  // namespace art
diff --git a/src/context_x86.h b/src/context_x86.h
new file mode 100644
index 0000000..0e31b25
--- /dev/null
+++ b/src/context_x86.h
@@ -0,0 +1,37 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_CONTEXT_X86_H_
+#define ART_SRC_CONTEXT_X86_H_
+
+#include "context.h"
+
+namespace art {
+namespace x86 {
+
+class X86Context : public Context {
+ public:
+  X86Context() : esp_(0), eip_(0) {}
+  virtual ~X86Context() {}
+
+  // No callee saves on X86
+  virtual void FillCalleeSaves(const Frame& fr) {}
+
+  virtual void SetSP(uintptr_t new_sp) {
+    esp_ = new_sp;
+  }
+
+  virtual void SetPC(uintptr_t new_pc) {
+    eip_ = new_pc;
+  }
+
+  virtual void DoLongJump();
+
+ private:
+  // Currently just ESP and EIP are used
+  uintptr_t esp_;
+  uintptr_t eip_;
+};
+}  // namespace x86
+}  // namespace art
+
+#endif  // ART_SRC_CONTEXT_X86_H_
diff --git a/src/dex_file.h b/src/dex_file.h
index 4448981..e4f68e8 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -276,8 +276,8 @@
 
     private:
       CatchHandlerItem handler_;
-      const byte *current_data_;  // the current handlder in dex file.
-      int32_t remaining_count_;   // number of handler not read.
+      const byte *current_data_;  // the current handler in dex file.
+      int32_t remaining_count_;   // number of handlers not read.
       bool catch_all_;            // is there a handler that will catch all exceptions in case
                                   // that all typed handler does not match.
   };
@@ -438,6 +438,11 @@
     return GetTypeDescriptor(type_id);
   }
 
+  // Returns the prototype of a method id.
+  const char* GetMethodPrototype(const MethodId& method_id) const {
+    return dexStringById(method_id.proto_idx_);
+  }
+
   // Returns the name of a method id.
   const char* GetMethodName(const MethodId& method_id) const {
     return dexStringById(method_id.name_idx_);
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 3380981..b6739f8 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -104,16 +104,16 @@
   for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
     Method* method = klass->GetDirectMethod(i);
     if (!VerifyMethod(method)) {
-        LOG(ERROR) << "Verifier rejected class "
-                   << klass->GetDescriptor()->ToModifiedUtf8();
+      LOG(ERROR) << "Verifier rejected class "
+                 << klass->GetDescriptor()->ToModifiedUtf8();
       return false;
     }
   }
   for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
     Method* method = klass->GetVirtualMethod(i);
     if (!VerifyMethod(method)) {
-        LOG(ERROR) << "Verifier rejected class "
-                   << klass->GetDescriptor()->ToModifiedUtf8();
+      LOG(ERROR) << "Verifier rejected class "
+                 << klass->GetDescriptor()->ToModifiedUtf8();
       return false;
     }
   }
@@ -351,13 +351,19 @@
 
   /* Generate a register map. */
   if (generate_register_map) {
-    RegisterMap* map = GenerateRegisterMapV(vdata);
+    UniquePtr<RegisterMap> map(GenerateRegisterMapV(vdata));
     /*
      * Tuck the map into the Method. It will either get used directly or, if
      * we're in dexopt, will be packed up and appended to the DEX file.
      */
-    // TODO: Put the map somewhere...
-    delete map;
+    ByteArray* header = ByteArray::Alloc(sizeof(RegisterMapHeader));
+    ByteArray* data = ByteArray::Alloc(ComputeRegisterMapSize(map.get()));
+
+    memcpy(header->GetData(), map.get()->header_, sizeof(RegisterMapHeader));
+    memcpy(data->GetData(), map.get()->data_, ComputeRegisterMapSize(map.get()));
+
+    method->SetRegisterMapHeader(header);
+    method->SetRegisterMapData(data);
   }
 
   return true;
@@ -1646,7 +1652,7 @@
       break;
     case Instruction::CONST_CLASS:
       /* make sure we can resolve the class; access check is important */
-      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      res_class = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
       if (res_class == NULL) {
         const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
         LOG(ERROR) << "VFY: unable to resolve const-class " << dec_insn.vB_
@@ -1697,7 +1703,7 @@
        * If it fails, an exception is thrown, which we deal with later
        * by ignoring the update to dec_insn.vA_ when branching to a handler.
        */
-      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      res_class = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
       if (res_class == NULL) {
         const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
         LOG(ERROR) << "VFY: unable to resolve check-cast " << dec_insn.vB_
@@ -1726,7 +1732,7 @@
       }
 
       /* make sure we can resolve the class; access check is important */
-      res_class = class_linker->ResolveType(*dex_file, dec_insn.vC_, klass);
+      res_class = ResolveClassAndCheckAccess(dex_file, dec_insn.vC_, klass, &failure);
       if (res_class == NULL) {
         const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vC_);
         LOG(ERROR) << "VFY: unable to resolve instanceof " << dec_insn.vC_
@@ -1752,7 +1758,7 @@
       break;
 
     case Instruction::NEW_INSTANCE:
-      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      res_class = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
       if (res_class == NULL) {
         const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
         LOG(ERROR) << "VFY: unable to resolve new-instance " << dec_insn.vB_
@@ -1787,7 +1793,7 @@
       }
       break;
    case Instruction::NEW_ARRAY:
-      res_class = class_linker->ResolveType(*dex_file, dec_insn.vC_, klass);
+      res_class = ResolveClassAndCheckAccess(dex_file, dec_insn.vC_, klass, &failure);
       if (res_class == NULL) {
         const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vC_);
         LOG(ERROR) << "VFY: unable to resolve new-array " << dec_insn.vC_
@@ -1806,7 +1812,7 @@
       break;
     case Instruction::FILLED_NEW_ARRAY:
     case Instruction::FILLED_NEW_ARRAY_RANGE:
-      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      res_class = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
       if (res_class == NULL) {
         const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
         LOG(ERROR) << "VFY: unable to resolve filled-array " << dec_insn.vB_
@@ -3396,13 +3402,18 @@
     }
 
   if (failure != VERIFY_ERROR_NONE) {
-    //if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing)
     if (failure == VERIFY_ERROR_GENERIC) {
       /* immediate failure, reject class */
       LOG(ERROR) << "VFY:  rejecting opcode 0x" << std::hex
                  << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
       return false;
     } else {
+      // TODO: CHECK IF THIS WILL WORK!
+      /* ignore the failure and move on */
+      LOG(ERROR) << "VFY: failing opcode 0x" << std::hex
+                 << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
+      failure = VERIFY_ERROR_NONE;
+#if 0
       /* replace opcode and continue on */
       LOG(ERROR) << "VFY: replacing opcode 0x" << std::hex
                  << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
@@ -3419,6 +3430,7 @@
       /* continue on as if we just handled a throw-verification-error */
       failure = VERIFY_ERROR_NONE;
       opcode_flag = Instruction::kThrow;
+#endif
     }
   }
 
@@ -3717,6 +3729,7 @@
   // TODO: REPLACE FAILING OPCODES
   //assert(width == 2 || width == 3);
   //uint16_t new_val = Instruction::THROW_VERIFICATION_ERROR |
+  //uint16_t new_val = Instruction::UNUSED_ED |
       //(failure << 8) | (ref_type << (8 + kVerifyErrorRefTypeShift));
   //UpdateCodeUnit(method, insns, new_val);
 
@@ -3921,8 +3934,8 @@
         if (handler.type_idx_ == DexFile::kDexNoIndex) {
           klass = class_linker->FindSystemClass("Ljava/lang/Throwable;");
         } else {
-          klass = class_linker->ResolveType(*dex_file, handler.type_idx_,
-              method->GetDeclaringClass());
+          klass = ResolveClassAndCheckAccess(dex_file, handler.type_idx_,
+              method->GetDeclaringClass(), failure);
         }
 
         if (klass == NULL) {
@@ -4418,13 +4431,33 @@
   return DigForSuperclass(c1, c2);
 }
 
+Class* DexVerifier::ResolveClassAndCheckAccess(const DexFile* dex_file,
+      uint32_t class_idx, const Class* referrer, VerifyError* failure) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Class* res_class = class_linker->ResolveType(*dex_file, class_idx, referrer);
+
+  if (res_class == NULL) {
+    *failure = VERIFY_ERROR_NO_CLASS;
+    return NULL;
+  }
+
+  /* Check if access is allowed. */
+  if (!referrer->CanAccess(res_class)) {
+    LOG(ERROR) << "VFY: illegal class access: "
+               << referrer->GetDescriptor()->ToModifiedUtf8() << " -> "
+               << res_class->GetDescriptor()->ToModifiedUtf8();
+    *failure = VERIFY_ERROR_ACCESS_CLASS;
+    return NULL;
+  }
+
+  return res_class;
+}
+
 DexVerifier::RegType DexVerifier::MergeTypes(RegType type1, RegType type2,
     bool* changed) {
   RegType result;
 
-  /*
-   * Check for trivial case so we don't have to hit memory.
-   */
+  /* Check for trivial case so we don't have to hit memory. */
   if (type1 == type2)
     return type1;
 
@@ -4973,17 +5006,17 @@
   res_method = class_linker->ResolveMethod(*dex_file, dec_insn->vB_, dex_cache,
       class_loader, (method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
 
-  /* Scan all implemented interfaces for the method */
-  if (method_type == METHOD_INTERFACE && res_method == NULL) {
-
-  }
-
   if (res_method == NULL) {
-    LOG(ERROR) << "VFY: unable to resolve called method";
+    const DexFile::MethodId& method_id = dex_file->GetMethodId(dec_insn->vB_);
+    const char* method_name = dex_file->GetMethodName(method_id);
+    const char* method_proto = dex_file->GetMethodPrototype(method_id);
+    const char* class_descriptor = dex_file->GetMethodClassDescriptor(method_id);
+
+    LOG(ERROR) << "VFY: unable to resolve method " << dec_insn->vB_ << ": "
+               << class_descriptor << "." << method_name << " " << method_proto;
     *failure = VERIFY_ERROR_NO_METHOD;
     return NULL;
   }
-  Class* res_class = res_method->GetDeclaringClass();
 
   /*
    * Only time you can explicitly call a method starting with '<' is when
@@ -4993,7 +5026,7 @@
   if (res_method->GetName()->Equals("<init>")) {
     if (method_type != METHOD_DIRECT || !IsInitMethod(res_method)) {
       LOG(ERROR) << "VFY: invalid call to "
-                 << res_class->GetDescriptor()->ToModifiedUtf8()
+                 << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
                  << "." << res_method->GetName();
       goto bad_sig;
     }
@@ -5005,7 +5038,7 @@
    */
   if (!IsCorrectInvokeKind(method_type, res_method)) {
     LOG(ERROR) << "VFY: invoke type does not match method type of "
-               << res_class->GetDescriptor()->ToModifiedUtf8()
+               << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
                << "." << res_method->GetName()->ToModifiedUtf8();
 
     *failure = VERIFY_ERROR_GENERIC;
@@ -5275,7 +5308,7 @@
       true, data_size);
 
   /* Populate it. */
-  uint8_t* map_data = map->data_.get();
+  uint8_t* map_data = map->data_;
   for (i = 0; i < (int) vdata->code_item_->insns_size_; i++) {
     if (InsnIsGcPoint(vdata->insn_flags_.get(), i)) {
       assert(vdata->register_lines_[i].reg_types_.get() != NULL);
@@ -5291,7 +5324,7 @@
     }
   }
 
-  assert((uint32_t) map_data - (uint32_t) map->data_.get() == data_size);
+  assert((uint32_t) map_data - (uint32_t) map->data_ == data_size);
 
   // TODO: Remove this check when it's really running...
 #if 1
@@ -5360,14 +5393,14 @@
 }
 
 bool DexVerifier::VerifyMap(VerifierData* vdata, const RegisterMap* map) {
-  const uint8_t* raw_map = map->data_.get();
-  uint8_t format = map->format_;
-  const int num_entries = map->num_entries_;
+  const uint8_t* raw_map = map->data_;
+  uint8_t format = map->header_->format_;
+  const int num_entries = map->header_->num_entries_;
   int ent;
 
-  if ((vdata->code_item_->registers_size_ + 7) / 8 != map->reg_width_) {
+  if ((vdata->code_item_->registers_size_ + 7) / 8 != map->header_->reg_width_) {
     LOG(ERROR) << "GLITCH: registersSize=" << vdata->code_item_->registers_size_
-               << ", reg_width=" << map->reg_width_;
+               << ", reg_width=" << map->header_->reg_width_;
     return false;
   }
 
@@ -5434,12 +5467,13 @@
     return false;
   }
 
-  if (map1->format_ != map2->format_ || map1->reg_width_ != map2->reg_width_ ||
-      map1->num_entries_ != map2->num_entries_ ||
-      map1->format_on_heap_ != map2->format_on_heap_) {
+  if (map1->header_->format_ != map2->header_->format_ ||
+      map1->header_->reg_width_ != map2->header_->reg_width_ ||
+      map1->header_->num_entries_ != map2->header_->num_entries_ ||
+      map1->header_->format_on_heap_ != map2->header_->format_on_heap_) {
     LOG(ERROR) << "CompareMaps: fields mismatch";
   }
-  if (memcmp(map1->data_.get(), map2->data_.get(), size1) != 0) {
+  if (memcmp(map1->data_, map2->data_, size1) != 0) {
     LOG(ERROR) << "CompareMaps: data mismatch";
     return false;
   }
@@ -5448,8 +5482,8 @@
 }
 
 size_t DexVerifier::ComputeRegisterMapSize(const RegisterMap* map) {
-  uint8_t format = map->format_;
-  uint16_t num_entries = map->num_entries_;
+  uint8_t format = map->header_->format_;
+  uint16_t num_entries = map->header_->num_entries_;
 
   assert(map != NULL);
 
@@ -5457,13 +5491,13 @@
     case kRegMapFormatNone:
       return 1;
     case kRegMapFormatCompact8:
-      return (1 + map->reg_width_) * num_entries;
+      return (1 + map->header_->reg_width_) * num_entries;
     case kRegMapFormatCompact16:
-      return (2 + map->reg_width_) * num_entries;
+      return (2 + map->header_->reg_width_) * num_entries;
     case kRegMapFormatDifferential:
       {
         /* Decoded ULEB128 length. */
-        const uint8_t* ptr = map->data_.get();
+        const uint8_t* ptr = map->data_;
         return DecodeUnsignedLeb128(&ptr);
       }
     default:
@@ -5527,7 +5561,7 @@
   uint8_t* tmp_ptr;
   int addr_width;
 
-  uint8_t format = map->format_;
+  uint8_t format = map->header_->format_;
   switch (format) {
     case kRegMapFormatCompact8:
       addr_width = 1;
@@ -5540,8 +5574,8 @@
       return NULL;
   }
 
-  int reg_width = map->reg_width_;
-  int num_entries = map->num_entries_;
+  int reg_width = map->header_->reg_width_;
+  int num_entries = map->header_->num_entries_;
 
   if (num_entries <= 1) {
     return NULL;
@@ -5568,7 +5602,7 @@
 
   tmp_ptr = tmp_buf.get();
 
-  const uint8_t* map_data = map->data_.get();
+  const uint8_t* map_data = map->data_;
   const uint8_t* prev_bits;
   uint16_t addr, prev_addr;
 
@@ -5687,7 +5721,7 @@
   RegisterMap* new_map = new RegisterMap(kRegMapFormatDifferential, reg_width,
       num_entries, true, new_map_size);
 
-  tmp_ptr = new_map->data_.get();
+  tmp_ptr = new_map->data_;
   tmp_ptr = WriteUnsignedLeb128(tmp_ptr, new_data_size);
   memcpy(tmp_ptr, tmp_buf.get(), new_data_size);
 
@@ -5696,7 +5730,7 @@
 
 DexVerifier::RegisterMap* DexVerifier::UncompressMapDifferential(
     const RegisterMap* map) {
-  uint8_t format = map->format_;
+  uint8_t format = map->header_->format_;
   RegisterMapFormat new_format;
   int reg_width, num_entries, new_addr_width, new_data_size;
 
@@ -5705,11 +5739,11 @@
     return NULL;
   }
 
-  reg_width = map->reg_width_;
-  num_entries = map->num_entries_;
+  reg_width = map->header_->reg_width_;
+  num_entries = map->header_->num_entries_;
 
   /* Get the data size; we can check this at the end. */
-  const uint8_t* src_ptr = map->data_.get();
+  const uint8_t* src_ptr = map->data_;
   int expected_src_len = DecodeUnsignedLeb128(&src_ptr);
   const uint8_t* src_start = src_ptr;
 
@@ -5730,7 +5764,7 @@
       true, new_data_size);
 
   /* Write the start address and initial bits to the new map. */
-  uint8_t* dst_ptr = new_map->data_.get();
+  uint8_t* dst_ptr = new_map->data_;
 
   *dst_ptr++ = addr & 0xff;
   if (new_addr_width > 1)
@@ -5796,8 +5830,8 @@
     dst_ptr += reg_width;
   }
 
-  if (dst_ptr - new_map->data_.get() != new_data_size) {
-    LOG(ERROR) << "ERROR: output " << dst_ptr - new_map->data_.get()
+  if (dst_ptr - new_map->data_ != new_data_size) {
+    LOG(ERROR) << "ERROR: output " << dst_ptr - new_map->data_
                << " bytes, expected " << new_data_size;
     free(new_map);
     return NULL;
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index a8351df..404227d 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -357,6 +357,20 @@
     }
   };
 
+  /* Header for RegisterMap */
+  struct RegisterMapHeader {
+    uint8_t format_;          /* enum RegisterMapFormat; MUST be first entry */
+    uint8_t reg_width_;       /* bytes per register line, 1+ */
+    uint16_t num_entries_;    /* number of entries */
+    bool    format_on_heap_;  /* indicates allocation on heap */
+
+    RegisterMapHeader(uint8_t format, uint8_t reg_width, uint16_t num_entries,
+        bool format_on_heap)
+        : format_(format), reg_width_(reg_width), num_entries_(num_entries),
+          format_on_heap_(format_on_heap) {
+    }
+  };
+
   /*
    * This is a single variable-size structure. It may be allocated on the
    * heap or mapped out of a (post-dexopt) DEX file.
@@ -370,19 +384,29 @@
    * Size of (format==FormatCompact16): 4 + (2 + reg_width) * num_entries
    */
   struct RegisterMap {
-    /* header */
-    uint8_t format_;          /* enum RegisterMapFormat; MUST be first entry */
-    uint8_t reg_width_;       /* bytes per register line, 1+ */
-    uint16_t num_entries_;    /* number of entries */
-    bool    format_on_heap_;  /* indicates allocation on heap */
+    RegisterMapHeader* header_;
+    uint8_t* data_;
+    bool needs_free_;
 
-    /* raw data starts here; need not be aligned */
-    UniquePtr<uint8_t[]> data_;
+    RegisterMap(ByteArray* header, ByteArray* data) {
+      header_ = (RegisterMapHeader*) header->GetData();
+      data_ = (uint8_t*) data->GetData();
+      needs_free_ = false;
+    }
 
     RegisterMap(uint8_t format, uint8_t reg_width, uint16_t num_entries,
-        bool format_on_heap, uint32_t data_size)
-        : format_(format), reg_width_(reg_width), num_entries_(num_entries),
-          format_on_heap_(format_on_heap), data_(new uint8_t[data_size]()) {
+        bool format_on_heap, uint32_t data_size) {
+      header_ = new RegisterMapHeader(format, reg_width, num_entries,
+          format_on_heap);
+      data_ = new uint8_t[data_size]();
+      needs_free_ = true;
+    }
+
+    ~RegisterMap() {
+      if (needs_free_) {
+        delete header_;
+        delete [] data_;
+      }
     }
   };
 
@@ -1240,6 +1264,17 @@
   static Class* FindCommonSuperclass(Class* c1, Class* c2);
 
   /*
+   * Resolves a class based on an index and performs access checks to ensure
+   * the referrer can access the resolved class.
+   *
+   * Exceptions caused by failures are cleared before returning.
+   *
+   * Sets "*failure" on failure.
+   */
+  static Class* ResolveClassAndCheckAccess(const DexFile* dex_file,
+      uint32_t class_idx, const Class* referrer, VerifyError* failure);
+
+  /*
    * Merge two RegType values.
    *
    * Sets "*changed" to "true" if the result doesn't match "type1".
@@ -1252,7 +1287,7 @@
    *
    * The merge is a simple bitwise AND.
    *
-   * Sets "*pChanged" to "true" if the result doesn't match "ents1".
+   * Sets "*changed" to "true" if the result doesn't match "ents1".
    */
   static MonitorEntries MergeMonitorEntries(MonitorEntries ents1,
       MonitorEntries ents2, bool* changed);
diff --git a/src/exception_test.cc b/src/exception_test.cc
index d77f1c0..8816530 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -72,27 +72,22 @@
     ASSERT_TRUE(class_loader != NULL);
     my_klass_ = class_linker_->FindClass("Ljava/lang/MyClass;", class_loader);
     ASSERT_TRUE(my_klass_ != NULL);
+    ByteArray* fake_code = ByteArray::Alloc(12);
+    ASSERT_TRUE(fake_code != NULL);
+    IntArray* fake_mapping_data = IntArray::Alloc(2);
+    ASSERT_TRUE(fake_mapping_data!= NULL);
+    fake_mapping_data->Set(0, 3);  // offset 3
+    fake_mapping_data->Set(1, 3);  // maps to dex offset 3
     method_f_ = my_klass_->FindVirtualMethod("f", "()I");
     ASSERT_TRUE(method_f_ != NULL);
     method_f_->SetFrameSizeInBytes(kStackAlignment);
-    method_f_->SetReturnPcOffsetInBytes(4);
+    method_f_->SetReturnPcOffsetInBytes(kStackAlignment-kPointerSize);
+    method_f_->SetCode(fake_code, kThumb2, fake_mapping_data);
     method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
     ASSERT_TRUE(method_g_ != NULL);
     method_g_->SetFrameSizeInBytes(kStackAlignment);
-    method_g_->SetReturnPcOffsetInBytes(4);
-  }
-
-  DexFile::CatchHandlerItem FindCatchHandlerItem(Method* method,
-                                                 const char exception_type[],
-                                                 uint32_t addr) {
-    const DexFile::CodeItem* code_item = dex_->GetCodeItem(method->GetCodeItemOffset());
-    for (DexFile::CatchHandlerIterator iter = dex_->dexFindCatchHandler(*code_item, addr);
-         !iter.HasNext(); iter.Next()) {
-      if (strcmp(exception_type, dex_->dexStringByTypeIdx(iter.Get().type_idx_)) == 0) {
-        return iter.Get();
-      }
-    }
-    return DexFile::CatchHandlerItem();
+    method_g_->SetReturnPcOffsetInBytes(kStackAlignment-kPointerSize);
+    method_g_->SetCode(fake_code, kThumb2, fake_mapping_data);
   }
 
   UniquePtr<const DexFile> dex_;
@@ -146,21 +141,22 @@
 
   // Create/push fake 16byte stack frame for method g
   fake_stack[top_of_stack++] = reinterpret_cast<uintptr_t>(method_g_);
-  fake_stack[top_of_stack++] = 3;
   fake_stack[top_of_stack++] = 0;
   fake_stack[top_of_stack++] = 0;
+  fake_stack[top_of_stack++] = reinterpret_cast<uintptr_t>(method_f_->GetCode()) + 3;  // return pc
 
   // Create/push fake 16byte stack frame for method f
   fake_stack[top_of_stack++] = reinterpret_cast<uintptr_t>(method_f_);
-  fake_stack[top_of_stack++] = 3;
   fake_stack[top_of_stack++] = 0;
   fake_stack[top_of_stack++] = 0;
+  fake_stack[top_of_stack++] = 0xEBAD6070;  // return pc
 
   // Pull Method* of NULL to terminate the trace
   fake_stack[top_of_stack++] = NULL;
 
+  // Set up thread to appear as if we called out of method_g_ at pc 3
   Thread* thread = Thread::Current();
-  thread->SetTopOfStack(fake_stack);
+  thread->SetTopOfStack(fake_stack, reinterpret_cast<uintptr_t>(method_g_->GetCode()) + 3);
 
   jobject internal = thread->CreateInternalStackTrace();
   jobjectArray ste_array =
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index eb84f14..2088f7d 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -76,19 +76,12 @@
   // Cache of IsStatic as we call it often enough
   const bool is_static = native_method->IsStatic();
 
-  // TODO: Need to make sure that the stub is copied into the image. I.e.,
-  // ByteArray* needs to be reachable either as a root or from the object graph.
-
-  // 1. Build the frame
+  // 1. Build the frame saving all callee saves
   const size_t frame_size(jni_conv->FrameSize());
-  const std::vector<ManagedRegister>& spill_regs = jni_conv->RegsToSpillPreCall();
-  __ BuildFrame(frame_size, mr_conv->MethodRegister(), spill_regs);
+  const std::vector<ManagedRegister>& callee_save_regs = jni_conv->CalleeSaveRegisters();
+  __ BuildFrame(frame_size, mr_conv->MethodRegister(), callee_save_regs);
 
-  // 2. Save callee save registers that aren't callee save in the native code
-  // TODO: implement computing the difference of the callee saves
-  // and saving
-
-  // 3. Set up the StackIndirectReferenceTable
+  // 2. Set up the StackIndirectReferenceTable
   mr_conv->ResetIterator(FrameOffset(frame_size));
   jni_conv->ResetIterator(FrameOffset(0));
   __ StoreImmediateToFrame(jni_conv->SirtNumRefsOffset(),
@@ -101,9 +94,9 @@
                               jni_conv->SirtOffset(),
                               mr_conv->InterproceduralScratchRegister());
 
-  // 4. Place incoming reference arguments into SIRT
+  // 3. Place incoming reference arguments into SIRT
   jni_conv->Next();  // Skip JNIEnv*
-  // 4.5. Create Class argument for static methods out of passed method
+  // 3.5. Create Class argument for static methods out of passed method
   if (is_static) {
     FrameOffset sirt_offset = jni_conv->CurrentParamSirtEntryOffset();
     // Check sirt offset is within frame
@@ -144,24 +137,44 @@
     jni_conv->Next();
   }
 
-  // 5. Transition from being in managed to native code
+  // 4. Transition from being in managed to native code. Save the top_of_managed_stack_
+  // so that the managed stack can be crawled while in native code. Clear the corresponding
+  // PC value that has no meaning for the this frame.
   // TODO: ensure the transition to native follow a store fence.
   __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset());
+  __ StoreImmediateToThread(Thread::TopOfManagedStackPcOffset(), 0,
+                            mr_conv->InterproceduralScratchRegister());
   __ StoreImmediateToThread(Thread::StateOffset(), Thread::kNative,
-                                  mr_conv->InterproceduralScratchRegister());
+                            mr_conv->InterproceduralScratchRegister());
 
-  // 6. Move frame down to allow space for out going args. Do for as short a
+  // 5. Move frame down to allow space for out going args. Do for as short a
   //    time as possible to aid profiling..
   const size_t out_arg_size = jni_conv->OutArgSize();
   __ IncreaseFrameSize(out_arg_size);
 
-  // 7. Acquire lock for synchronized methods.
+  // 6. Acquire lock for synchronized methods.
   if (native_method->IsSynchronized()) {
-    // TODO: preserve incoming arguments in registers
-    mr_conv->ResetIterator(FrameOffset(frame_size+out_arg_size));
+    // Compute arguments in registers to preserve
+    mr_conv->ResetIterator(FrameOffset(frame_size + out_arg_size));
+    std::vector<ManagedRegister> live_argument_regs;
+    while (mr_conv->HasNext()) {
+      if (mr_conv->IsCurrentParamInRegister()) {
+        live_argument_regs.push_back(mr_conv->CurrentParamRegister());
+      }
+      mr_conv->Next();
+    }
+
+    // Copy arguments to preserve to callee save registers
+    CHECK_LE(live_argument_regs.size(), callee_save_regs.size());
+    for (size_t i = 0; i < live_argument_regs.size(); i++) {
+      __ Move(callee_save_regs.at(i), live_argument_regs.at(i));
+    }
+
+    // Get SIRT entry for 1st argument (jclass or this) to be 1st argument to
+    // monitor enter
+    mr_conv->ResetIterator(FrameOffset(frame_size + out_arg_size));
     jni_conv->ResetIterator(FrameOffset(out_arg_size));
     jni_conv->Next();  // Skip JNIEnv*
-    // Get SIRT entry for 1st argument
     if (is_static) {
       FrameOffset sirt_offset = jni_conv->CurrentParamSirtEntryOffset();
       if (jni_conv->IsCurrentParamOnStack()) {
@@ -178,21 +191,29 @@
       CopyParameter(jni_asm.get(), mr_conv.get(), jni_conv.get(), frame_size,
                     out_arg_size);
     }
+
     // Generate JNIEnv* in place and leave a copy in jni_fns_register
     jni_conv->ResetIterator(FrameOffset(out_arg_size));
     ManagedRegister jni_fns_register =
         jni_conv->InterproceduralScratchRegister();
     __ LoadRawPtrFromThread(jni_fns_register, Thread::JniEnvOffset());
     SetNativeParameter(jni_asm.get(), jni_conv.get(), jni_fns_register);
+
     // Call JNIEnv->MonitorEnter(object)
     __ LoadRawPtr(jni_fns_register, jni_fns_register, functions);
     __ Call(jni_fns_register, monitor_enter,
                   jni_conv->InterproceduralScratchRegister());
-    __ FillFromSpillArea(spill_regs, out_arg_size);
+
+    // Check for exceptions
     __ ExceptionPoll(jni_conv->InterproceduralScratchRegister());
+
+    // Restore live arguments
+    for (size_t i = 0; i < live_argument_regs.size(); i++) {
+      __ Move(live_argument_regs.at(i), callee_save_regs.at(i));
+    }
   }
 
-  // 8. Iterate over arguments placing values from managed calling convention in
+  // 7. Iterate over arguments placing values from managed calling convention in
   //    to the convention required for a native call (shuffling). For references
   //    place an index/pointer to the reference after checking whether it is
   //    NULL (which must be encoded as NULL).
@@ -240,7 +261,7 @@
                          ManagedRegister::NoRegister(), false);
     }
   }
-  // 9. Create 1st argument, the JNI environment ptr
+  // 8. Create 1st argument, the JNI environment ptr
   jni_conv->ResetIterator(FrameOffset(out_arg_size));
   if (jni_conv->IsCurrentParamInRegister()) {
     __ LoadRawPtrFromThread(jni_conv->CurrentParamRegister(),
@@ -251,7 +272,7 @@
                             jni_conv->InterproceduralScratchRegister());
   }
 
-  // 10. Plant call to native code associated with method
+  // 9. Plant call to native code associated with method
   if (!jni_conv->IsOutArgRegister(mr_conv->MethodRegister())) {
     // Method register shouldn't have been crushed by setting up outgoing
     // arguments
@@ -261,7 +282,8 @@
     __ Call(jni_conv->MethodStackOffset(), Method::NativeMethodOffset(),
             mr_conv->InterproceduralScratchRegister());
   }
-  // 11. Release lock for synchronized methods.
+
+  // 10. Release lock for synchronized methods.
   if (native_method->IsSynchronized()) {
     mr_conv->ResetIterator(FrameOffset(frame_size+out_arg_size));
     jni_conv->ResetIterator(FrameOffset(out_arg_size));
@@ -308,12 +330,12 @@
     }
   }
 
-  // 12. Release outgoing argument area
+  // 11. Release outgoing argument area
   __ DecreaseFrameSize(out_arg_size);
   mr_conv->ResetIterator(FrameOffset(frame_size));
   jni_conv->ResetIterator(FrameOffset(0));
 
-  // 13. Transition from being in native to managed code, possibly entering a
+  // 12. Transition from being in native to managed code, possibly entering a
   //     safepoint
   CHECK(!jni_conv->InterproceduralScratchRegister()
         .Equals(jni_conv->ReturnRegister()));  // don't clobber result
@@ -329,7 +351,7 @@
                             jni_conv->InterproceduralScratchRegister());
 
 
-  // 14. Place result in correct register possibly loading from indirect
+  // 13. Place result in correct register possibly loading from indirect
   //     reference table
   if (jni_conv->IsReturnAReference()) {
     __ IncreaseFrameSize(out_arg_size);
@@ -350,8 +372,7 @@
     } else {
       __ GetCurrentThread(jni_conv->CurrentParamStackOffset(),
                           jni_conv->InterproceduralScratchRegister());
-      __ Call(jni_conv->CurrentParamStackOffset(),
-              Offset(OFFSETOF_MEMBER(Thread, pDecodeJObjectInThread)),
+      __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pDecodeJObjectInThread)),
               jni_conv->InterproceduralScratchRegister());
     }
 
@@ -360,14 +381,18 @@
   }
   __ Move(mr_conv->ReturnRegister(), jni_conv->ReturnRegister());
 
-  // 15. Remove SIRT from thread
+  // 14. Remove SIRT from thread
   __ CopyRawPtrToThread(Thread::TopSirtOffset(), jni_conv->SirtLinkOffset(),
                         jni_conv->InterproceduralScratchRegister());
 
-  // 16. Remove activation
-  __ RemoveFrame(frame_size, spill_regs);
+  // 15. Remove activation
+  if (native_method->IsSynchronized()) {
+    __ RemoveFrame(frame_size, callee_save_regs);
+  } else {
+    __ RemoveFrame(frame_size, std::vector<ManagedRegister>());
+  }
 
-  // 17. Finalize code generation
+  // 16. Finalize code generation
   __ EmitSlowPaths();
   size_t cs = __ CodeSize();
   ByteArray* managed_code = ByteArray::Alloc(cs);
@@ -377,6 +402,8 @@
   native_method->SetCode(managed_code, instruction_set_);
   native_method->SetFrameSizeInBytes(frame_size);
   native_method->SetReturnPcOffsetInBytes(jni_conv->ReturnPcOffset());
+  native_method->SetCoreSpillMask(jni_conv->CoreSpillMask());
+  native_method->SetFpSpillMask(jni_conv->FpSpillMask());
 #undef __
 }
 
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index 8b5f31a..d78404b 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -40,6 +40,7 @@
                           JniCallingConvention* jni_conv,
                           ManagedRegister in_reg);
 
+  // Architecture to generate code for
   InstructionSet instruction_set_;
 
   DISALLOW_COPY_AND_ASSIGN(JniCompiler);
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 20c403c..9328e85 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -53,9 +53,11 @@
     }
     ASSERT_TRUE(jmethod_ != NULL);
 
-    if (native_fnptr) {
+    if (native_fnptr != NULL) {
       JNINativeMethod methods[] = {{method_name, method_sig, native_fnptr}};
       ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1));
+    } else {
+      env_->UnregisterNatives(jklass_);
     }
 
     jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
@@ -233,10 +235,6 @@
 }
 
 TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
-#if !defined(__arm__)
-  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
-  return;
-#endif
   SetupForTest(false, "fooIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooIOO));
@@ -336,10 +334,6 @@
 
 
 TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
-#if !defined(__arm__)
-  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
-  return;
-#endif
   SetupForTest(true, "fooSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
@@ -390,10 +384,6 @@
 }
 
 TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
-#if !defined(__arm__)
-  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
-  return;
-#endif
   SetupForTest(true, "fooSSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
@@ -546,10 +536,6 @@
 }
 
 TEST_F(JniCompilerTest, ReturnGlobalRef) {
-#if !defined(__arm__)
-  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
-  return;
-#endif
   SetupForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooO));
   jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, jobj_);
diff --git a/src/object.cc b/src/object.cc
index 24fe628..b5e66d9 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -460,11 +460,82 @@
           this->GetSignature()->Equals(that->GetSignature()));
 }
 
+uint32_t Method::ToDexPC(const uintptr_t pc) const {
+  IntArray* mapping_table = GetMappingTable();
+  if (mapping_table == NULL) {
+    DCHECK(pc == 0);
+    return DexFile::kDexNoIndex;   // Special no mapping/pc == -1 case
+  }
+  size_t mapping_table_length = mapping_table->GetLength();
+  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetCode());
+  CHECK_LT(sought_offset, static_cast<uint32_t>(GetCodeArray()->GetLength()));
+  uint32_t best_offset = 0;
+  uint32_t best_dex_offset = 0;
+  for (size_t i = 0; i < mapping_table_length; i += 2) {
+    uint32_t map_offset = mapping_table->Get(i);
+    uint32_t map_dex_offset = mapping_table->Get(i + 1);
+    if (map_offset == sought_offset) {
+      best_offset = map_offset;
+      best_dex_offset = map_dex_offset;
+      break;
+    }
+    if (map_offset < sought_offset && map_offset > best_offset) {
+      best_offset = map_offset;
+      best_dex_offset = map_dex_offset;
+    }
+  }
+  return best_dex_offset;
+}
+
+uintptr_t Method::ToNativePC(const uint32_t dex_pc) const {
+  IntArray* mapping_table = GetMappingTable();
+  if (mapping_table == NULL) {
+    DCHECK(dex_pc == 0);
+    return 0;   // Special no mapping/pc == 0 case
+  }
+  size_t mapping_table_length = mapping_table->GetLength();
+  for (size_t i = 0; i < mapping_table_length; i += 2) {
+    uint32_t map_offset = mapping_table->Get(i);
+    uint32_t map_dex_offset = mapping_table->Get(i + 1);
+    if (map_dex_offset == dex_pc) {
+      DCHECK_LT(map_offset, static_cast<uint32_t>(GetCodeArray()->GetLength()));
+      return reinterpret_cast<uintptr_t>(GetCode()) + map_offset;
+    }
+  }
+  LOG(FATAL) << "Looking up Dex PC not contained in method";
+  return 0;
+}
+
+uint32_t Method::FindCatchBlock(Class* exception_type, uint32_t dex_pc) const {
+  DexCache* dex_cache = GetDeclaringClass()->GetDexCache();
+  const ClassLoader* class_loader = GetDeclaringClass()->GetClassLoader();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+  const DexFile::CodeItem* code_item = dex_file.GetCodeItem(GetCodeItemOffset());
+  // Iterate over the catch handlers associated with dex_pc
+  for (DexFile::CatchHandlerIterator iter = dex_file.dexFindCatchHandler(*code_item, dex_pc);
+       !iter.HasNext(); iter.Next()) {
+    uint32_t iter_type_idx = iter.Get().type_idx_;
+    // Catch all case
+    if(iter_type_idx == DexFile::kDexNoIndex) {
+      return iter.Get().address_;
+    }
+    // Does this catch exception type apply?
+    Class* iter_exception_type =
+        class_linker->ResolveType(dex_file, iter_type_idx, dex_cache, class_loader);
+    if (iter_exception_type->IsAssignableFrom(exception_type)) {
+      return iter.Get().address_;
+    }
+  }
+  // Handler not found
+  return DexFile::kDexNoIndex;
+}
+
 void Method::SetCode(ByteArray* code_array, InstructionSet instruction_set,
-                     ByteArray* mapping_table) {
+                     IntArray* mapping_table) {
   CHECK(GetCode() == NULL || IsNative());
   SetFieldPtr<ByteArray*>(OFFSET_OF_OBJECT_MEMBER(Method, code_array_), code_array, false);
-  SetFieldPtr<ByteArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_),
+  SetFieldPtr<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_),
        mapping_table, false);
   int8_t* code = code_array->GetData();
   uintptr_t address = reinterpret_cast<uintptr_t>(code);
@@ -475,6 +546,19 @@
   SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_), reinterpret_cast<const void*>(address), false);
 }
 
+bool Method::IsWithinCode(uintptr_t pc) const {
+  if (GetCode() == NULL) {
+    return false;
+  }
+  if (pc == 0) {
+    // assume that this is some initial value that will always lie in code
+    return true;
+  } else {
+    uint32_t rel_offset = pc - reinterpret_cast<uintptr_t>(GetCodeArray()->GetData());
+    return rel_offset < static_cast<uint32_t>(GetCodeArray()->GetLength());
+  }
+}
+
 void Method::SetInvokeStub(const ByteArray* invoke_stub_array) {
   const InvokeStub* invoke_stub = reinterpret_cast<InvokeStub*>(invoke_stub_array->GetData());
   SetFieldPtr<const ByteArray*>(
@@ -502,6 +586,7 @@
   if (have_executable_code && stub != NULL) {
     LOG(INFO) << "invoking " << PrettyMethod(this) << " code=" << (void*) GetCode() << " stub=" << (void*) stub;
     (*stub)(this, receiver, self, args, result);
+    LOG(INFO) << "returning " << PrettyMethod(this) << " code=" << (void*) GetCode() << " stub=" << (void*) stub;
   } else {
     LOG(WARNING) << "Not invoking method with no associated code: " << PrettyMethod(this);
     if (result != NULL) {
@@ -534,7 +619,7 @@
 
 void Class::SetStatus(Status new_status) {
   CHECK(new_status > GetStatus() || new_status == kStatusError ||
-      !Runtime::Current()->IsStarted());
+      !Runtime::Current()->IsStarted()) << GetDescriptor()->ToModifiedUtf8();
   CHECK(sizeof(Status) == sizeof(uint32_t));
   return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_),
                     new_status, false);
@@ -799,6 +884,7 @@
 
 Method* Class::FindVirtualMethodForInterface(Method* method) {
   Class* declaring_class = method->GetDeclaringClass();
+  DCHECK(declaring_class != NULL);
   DCHECK(declaring_class->IsInterface());
   // TODO cache to improve lookup speed
   int32_t iftable_count = GetIfTableCount();
diff --git a/src/object.h b/src/object.h
index 5e09f2e..7292582 100644
--- a/src/object.h
+++ b/src/object.h
@@ -636,6 +636,14 @@
 
   void SetName(String* new_name);
 
+  ByteArray* GetRegisterMapData() const;
+
+  void SetRegisterMapData(ByteArray* new_data);
+
+  ByteArray* GetRegisterMapHeader() const;
+
+  void SetRegisterMapHeader(ByteArray* new_header);
+
   String* GetShorty() const;
 
   void SetShorty(String* new_shorty);
@@ -858,12 +866,20 @@
   }
 
   void SetCode(ByteArray* code_array, InstructionSet instruction_set,
-               ByteArray* mapping_table = NULL);
+               IntArray* mapping_table = NULL);
 
   static MemberOffset GetCodeOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Method, code_);
   }
 
+  // Is the given PC within the code array?
+  bool IsWithinCode(uintptr_t pc) const;
+
+  IntArray* GetMappingTable() const {
+    return GetFieldObject<IntArray*>(
+        OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), false);
+  }
+
   size_t GetFrameSizeInBytes() const {
     DCHECK(sizeof(size_t) == sizeof(uint32_t));
     size_t result = GetField32(
@@ -935,10 +951,8 @@
 
   void SetInvokeStub(const ByteArray* invoke_stub_array);
 
-  void SetFpSpillMask(uint32_t fp_spill_mask) {
-    // Computed during compilation
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_),
-               fp_spill_mask, false);
+  uint32_t GetCoreSpillMask() {
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Method, core_spill_mask_), false);
   }
 
   void SetCoreSpillMask(uint32_t core_spill_mask) {
@@ -947,17 +961,26 @@
                core_spill_mask, false);
   }
 
+  uint32_t GetFpSpillMask() {
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_), false);
+  }
+
+  void SetFpSpillMask(uint32_t fp_spill_mask) {
+    // Computed during compilation
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_),
+               fp_spill_mask, false);
+  }
+
   // Converts a native PC to a dex PC.  TODO: this is a no-op
   // until we associate a PC mapping table with each method.
-  uintptr_t ToDexPC(const uintptr_t pc) const {
-    return pc;
-  }
+  uint32_t ToDexPC(const uintptr_t pc) const;
 
   // Converts a dex PC to a native PC.  TODO: this is a no-op
   // until we associate a PC mapping table with each method.
-  uintptr_t ToNativePC(const uintptr_t pc) const {
-    return pc;
-  }
+  uintptr_t ToNativePC(const uint32_t dex_pc) const;
+
+  // Find the catch block for the given exception type and dex_pc
+  uint32_t FindCatchBlock(Class* exception_type, uint32_t dex_pc) const;
 
   static Class* GetJavaLangReflectMethod() {
     DCHECK(java_lang_reflect_Method_ != NULL);
@@ -1010,7 +1033,11 @@
   const ByteArray* invoke_stub_array_;
 
   // Storage for mapping_table_
-  const ByteArray* mapping_table_;
+  IntArray* mapping_table_;
+
+  // Byte arrays that hold data for the register maps
+  const ByteArray* register_map_data_;
+  const ByteArray* register_map_header_;
 
   // The short-form method descriptor string.
   String* shorty_;
@@ -2515,6 +2542,26 @@
 
 }
 
+inline ByteArray* Method::GetRegisterMapData() const {
+  return GetFieldObject<ByteArray*>(
+      OFFSET_OF_OBJECT_MEMBER(Method, register_map_data_), false);
+}
+
+inline void Method::SetRegisterMapData(ByteArray* new_data) {
+  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, register_map_data_),
+                 new_data, false);
+}
+
+inline ByteArray* Method::GetRegisterMapHeader() const {
+  return GetFieldObject<ByteArray*>(
+      OFFSET_OF_OBJECT_MEMBER(Method, register_map_header_), false);
+}
+
+inline void Method::SetRegisterMapHeader(ByteArray* new_header) {
+  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, register_map_header_),
+                 new_header, false);
+}
+
 inline String* Method::GetShorty() const {
   DCHECK(GetDeclaringClass()->IsLoaded());
   return GetFieldObject<String*>(
diff --git a/src/runtime_support.S b/src/runtime_support.S
index fe40cf2..458102d 100644
--- a/src/runtime_support.S
+++ b/src/runtime_support.S
@@ -2,6 +2,20 @@
 
     .balign 4
 
+    .global art_throw_exception
+    .extern artThrowExceptionHelper
+    /*
+     * Called by managed code, saves all registers (forms basis of long jump context).
+     * artThrowExceptionHelper will place a mock Method* at the bottom of the thread.
+     * r0 holds Throwable
+     */
+art_throw_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*
+    mov r1, r9
+    mov r2, sp
+    b artThrowExceptionHelper  @ artThrowExceptionHelper(Throwable*, SP)
+
     .global art_invoke_interface_trampoline
     .extern artFindInterfaceMethodInCache
     .extern artFailedInvokeInterface
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 04c464f..671d7dc 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -9,6 +9,7 @@
   extern "C" uint64_t art_shr_long(uint64_t, uint32_t);
   extern "C" uint64_t art_ushr_long(uint64_t, uint32_t);
   extern "C" void art_invoke_interface_trampoline(void*, void*, void*, void*);
+  extern "C" void art_throw_exception(void*);
 
   /* Conversions */
   extern "C" float __aeabi_i2f(int op1);             // OP_INT_TO_FLOAT
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
index 0711085..986d2b0 100644
--- a/src/stub_arm.cc
+++ b/src/stub_arm.cc
@@ -11,7 +11,7 @@
 
 typedef void (*ThrowAme)(Method*, Thread*);
 
-ByteArray* CreateAbstractMethodErrorStub(ThrowAme throw_ame) {
+ByteArray* CreateAbstractMethodErrorStub() {
   UniquePtr<ArmAssembler> assembler( static_cast<ArmAssembler*>(Assembler::Create(kArm)) );
 
   // R0 is the Method* already.
@@ -19,8 +19,8 @@
   // Pass Thread::Current() in R1
   __ mov(R1, ShifterOperand(R9));
 
-  // Call throw_ame to throw AbstractMethodError
-  __ LoadImmediate(R12, reinterpret_cast<int32_t>(throw_ame));
+  // Call to throw AbstractMethodError
+  __ LoadFromOffset(kLoadWord, R12, TR, OFFSETOF_MEMBER(Thread, pThrowAbstractMethodErrorFromCode));
   // Leaf call to routine that never returns
   __ mov(PC, ShifterOperand(R12));
 
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
index fd8d0f2..c23c751 100644
--- a/src/stub_x86.cc
+++ b/src/stub_x86.cc
@@ -11,7 +11,7 @@
 
 typedef void (*ThrowAme)(Method*, Thread*);
 
-ByteArray* CreateAbstractMethodErrorStub(ThrowAme throw_ame) {
+ByteArray* CreateAbstractMethodErrorStub() {
   UniquePtr<X86Assembler> assembler( static_cast<X86Assembler*>(Assembler::Create(kX86)) );
 
   // Pad stack to ensure 16-byte alignment
@@ -20,10 +20,9 @@
   __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // Thread*
   __ pushl(EDI); // Method*
 
-  // Call throw_ame to throw AbstractMethodError
-  // TODO: make this PIC (throw_ame will not be in the same location after image load)
-  // TODO: remove X86Assembler::Call(uintptr_t addr, ManagedRegister scratch)
-  __ Call(reinterpret_cast<int32_t>(throw_ame), X86ManagedRegister::FromCpuRegister(ECX));
+  // Call to throw AbstractMethodError
+  __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pThrowAbstractMethodErrorFromCode)),
+          X86ManagedRegister::FromCpuRegister(ECX));
 
   // Because the call above never returns, we do not need to do ESP+=16 here.
 
@@ -50,9 +49,8 @@
   __ fs()->movl(ECX, Address::Absolute(Thread::SelfOffset()));
   __ pushl(ECX);  // Thread*
 
-  // TODO: make this PIC (FindNativeMethod will not be in the same location after image load)
-  // TODO: remove X86Assembler::Call(uintptr_t addr, ManagedRegister scratch)
-  __ Call(reinterpret_cast<int32_t>(&FindNativeMethod), X86ManagedRegister::FromCpuRegister(ECX));
+  __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pFindNativeMethod)),
+          X86ManagedRegister::FromCpuRegister(ECX));
 
   __ addl(ESP, Immediate(16));
 
diff --git a/src/thread.cc b/src/thread.cc
index d879cea..513c019 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -27,6 +27,7 @@
 #include <list>
 
 #include "class_linker.h"
+#include "context.h"
 #include "heap.h"
 #include "jni_internal.h"
 #include "object.h"
@@ -48,18 +49,26 @@
     LOG(INFO) << "Info: " << info;
 }
 
-// TODO: placeholder.  This is what generated code will call to throw
-void ThrowException(Thread* thread, Throwable* exception) {
+}  // namespace art
+
+// Called by generated call to throw an exception
+extern "C" void artThrowExceptionHelper(art::Throwable* exception,
+                                        art::Thread* thread,
+                                        art::Method** sp) {
   /*
    * exception may be NULL, in which case this routine should
    * throw NPE.  NOTE: this is a convenience for generated code,
    * which previously did the null check inline and constructed
    * and threw a NPE if NULL.  This routine responsible for setting
-   * exception_ in thread.
+   * exception_ in thread and delivering the exception.
    */
-  UNIMPLEMENTED(FATAL) << "Unimplemented exception throw: " << PrettyType(exception);
+  *sp = thread->CalleeSaveMethod();
+  thread->SetTopOfStack(sp, 0);
+  thread->DeliverException(exception);
 }
 
+namespace art {
+
 // TODO: placeholder.  Helper function to type
 Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
   /*
@@ -167,6 +176,14 @@
     UNIMPLEMENTED(FATAL) << "No such method, idx: " << method_idx;
 }
 
+void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread) {
+  thread->ThrowNewException("Ljava/lang/AbstractMethodError",
+                            "abstract method \"%s\"",
+                            PrettyMethod(method).c_str());
+  thread->DeliverException(thread->GetException());
+}
+
+
 /*
  * Temporary placeholder.  Should include run-time checks for size
  * of fill data <= size of array.  If not, throw arrayOutOfBoundsException.
@@ -289,6 +306,7 @@
   pLdivmod = __aeabi_ldivmod;
   pLmul = __aeabi_lmul;
   pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
+  pThrowException = art_throw_exception;
 #endif
   pF2l = F2L;
   pD2l = D2L;
@@ -304,7 +322,6 @@
   pGetObjStatic = Field::GetObjStaticFromCode;
   pSetObjStatic = Field::SetObjStaticFromCode;
   pCanPutArrayElementFromCode = Class::CanPutArrayElementFromCode;
-  pThrowException = ThrowException;
   pInitializeTypeFromCode = InitializeTypeFromCode;
   pResolveMethodFromCode = ResolveMethodFromCode;
   pInitializeStaticStorage = ClassLinker::InitializeStaticStorageFromCode;
@@ -323,6 +340,7 @@
   pThrowRuntimeExceptionFromCode = ThrowRuntimeExceptionFromCode;
   pThrowInternalErrorFromCode = ThrowInternalErrorFromCode;
   pThrowNoSuchMethodFromCode = ThrowNoSuchMethodFromCode;
+  pThrowAbstractMethodErrorFromCode = ThrowAbstractMethodErrorFromCode;
   pFindNativeMethod = FindNativeMethod;
   pDecodeJObjectInThread = DecodeJObjectInThread;
   pDebugMe = DebugMe;
@@ -334,12 +352,22 @@
   sp_ = reinterpret_cast<Method**>(next_sp);
 }
 
-uintptr_t Frame::GetPC() const {
+uintptr_t Frame::GetReturnPC() const {
   byte* pc_addr = reinterpret_cast<byte*>(sp_) +
       GetMethod()->GetReturnPcOffsetInBytes();
   return *reinterpret_cast<uintptr_t*>(pc_addr);
 }
 
+uintptr_t Frame::LoadCalleeSave(int num) const {
+  // Callee saves are held at the top of the frame
+  Method* method = GetMethod();
+  DCHECK(method != NULL);
+  size_t frame_size = method->GetFrameSizeInBytes();
+  byte* save_addr = reinterpret_cast<byte*>(sp_) + frame_size -
+                    ((num + 1) * kPointerSize);
+  return *reinterpret_cast<uintptr_t*>(save_addr);
+}
+
 Method* Frame::NextMethod() const {
   byte* next_sp = reinterpret_cast<byte*>(sp_) +
       GetMethod()->GetFrameSizeInBytes();
@@ -656,10 +684,10 @@
   StackDumpVisitor(std::ostream& os) : os(os) {
   }
 
-  ~StackDumpVisitor() {
+  virtual ~StackDumpVisitor() {
   }
 
-  void VisitFrame(const Frame& frame) {
+  void VisitFrame(const Frame& frame, uintptr_t pc) {
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
     Method* m = frame.GetMethod();
@@ -670,7 +698,7 @@
     if (m->IsNative()) {
       os << "(Native method)";
     } else {
-      int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(frame.GetPC()));
+      int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc));
       os << "(" << c->GetSourceFile()->ToModifiedUtf8() << ":" << line_number << ")";
     }
     os << "\n";
@@ -946,7 +974,7 @@
  public:
   CountStackDepthVisitor() : depth_(0) {}
 
-  virtual void VisitFrame(const Frame&) {
+  virtual void VisitFrame(const Frame&, uintptr_t pc) {
     ++depth_;
   }
 
@@ -979,9 +1007,9 @@
 
   virtual ~BuildInternalStackTraceVisitor() {}
 
-  virtual void VisitFrame(const Frame& frame) {
+  virtual void VisitFrame(const Frame& frame, uintptr_t pc) {
     method_trace_->Set(count_, frame.GetMethod());
-    pc_trace_->Set(count_, frame.GetPC());
+    pc_trace_->Set(count_, pc);
     ++count_;
   }
 
@@ -1003,19 +1031,36 @@
 
 void Thread::WalkStack(StackVisitor* visitor) const {
   Frame frame = GetTopOfStack();
+  uintptr_t pc = top_of_managed_stack_pc_;
   // TODO: enable this CHECK after native_to_managed_record_ is initialized during startup.
   // CHECK(native_to_managed_record_ != NULL);
   NativeToManagedRecord* record = native_to_managed_record_;
 
-  while (frame.GetSP()) {
+  while (frame.GetSP() != 0) {
     for ( ; frame.GetMethod() != 0; frame.Next()) {
-      visitor->VisitFrame(frame);
+      DCHECK(frame.GetMethod()->IsWithinCode(pc));
+      visitor->VisitFrame(frame, pc);
+      pc = frame.GetReturnPC();
     }
     if (record == NULL) {
       break;
     }
-    frame.SetSP(reinterpret_cast<art::Method**>(record->last_top_of_managed_stack));  // last_tos should return Frame instead of sp?
-    record = record->link;
+    // last_tos should return Frame instead of sp?
+    frame.SetSP(reinterpret_cast<art::Method**>(record->last_top_of_managed_stack_));
+    pc = record->last_top_of_managed_stack_pc_;
+    record = record->link_;
+  }
+}
+
+void Thread::WalkStackUntilUpCall(StackVisitor* visitor) const {
+  Frame frame = GetTopOfStack();
+  uintptr_t pc = top_of_managed_stack_pc_;
+
+  if (frame.GetSP() != 0) {
+    for ( ; frame.GetMethod() != 0; frame.Next()) {
+      visitor->VisitFrame(frame, pc);
+      pc = frame.GetReturnPC();
+    }
   }
 }
 
@@ -1104,65 +1149,88 @@
   UNIMPLEMENTED(FATAL);
 }
 
-Frame Thread::FindExceptionHandler(void* throw_pc, void** handler_pc) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  DCHECK(class_linker != NULL);
+Method* Thread::CalleeSaveMethod() const {
+  // TODO: we should only allocate this once
+  // TODO: this code is ARM specific
+  Method* method = Runtime::Current()->GetClassLinker()->AllocMethod();
+  method->SetCode(NULL, art::kThumb2, NULL);
+  method->SetFrameSizeInBytes(64);
+  method->SetReturnPcOffsetInBytes(60);
+  method->SetCoreSpillMask(0x4FFE);
+  method->SetFpSpillMask(0);
+  return method;
+}
 
-  Frame cur_frame = GetTopOfStack();
-  for (int unwind_depth = 0; ; unwind_depth++) {
-    const Method* cur_method = cur_frame.GetMethod();
-    DexCache* dex_cache = cur_method->GetDeclaringClass()->GetDexCache();
-    const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+class CatchBlockStackVisitor : public Thread::StackVisitor {
+ public:
+  CatchBlockStackVisitor(Class* to_find, Context* ljc)
+      : found_(false), to_find_(to_find), long_jump_context_(ljc) {}
 
-    void* handler_addr = FindExceptionHandlerInMethod(cur_method,
-                                                      throw_pc,
-                                                      dex_file,
-                                                      class_linker);
-    if (handler_addr) {
-      *handler_pc = handler_addr;
-      return cur_frame;
-    } else {
-      // Check if we are at the last frame
-      if (cur_frame.HasNext()) {
-        cur_frame.Next();
-      } else {
-        // Either at the top of stack or next frame is native.
-        break;
+  virtual void VisitFrame(const Frame& fr, uintptr_t pc) {
+    if (!found_) {
+      last_pc_ = pc;
+      handler_frame_ = fr;
+      Method* method = fr.GetMethod();
+      if (pc > 0) {
+        // Move the PC back 2 bytes as a call will frequently terminate the
+        // decoding of a particular instruction and we want to make sure we
+        // get the Dex PC of the instruction with the call and not the
+        // instruction following.
+        pc -= 2;
+      }
+      uint32_t dex_pc = method->ToDexPC(pc);
+      if (dex_pc != DexFile::kDexNoIndex) {
+        uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc);
+        if (found_dex_pc != DexFile::kDexNoIndex) {
+          found_ = true;
+          handler_dex_pc_ = found_dex_pc;
+        }
+      }
+      if (!found_) {
+        // Caller may be handler, fill in callee saves in context
+        long_jump_context_->FillCalleeSaves(fr);
       }
     }
   }
-  *handler_pc = NULL;
-  return Frame();
+
+  // Did we find a catch block yet?
+  bool found_;
+  // The type of the exception catch block to find
+  Class* to_find_;
+  // Frame with found handler or last frame if no handler found
+  Frame handler_frame_;
+  // Found dex PC of the handler block
+  uint32_t handler_dex_pc_;
+  // Context that will be the target of the long jump
+  Context* long_jump_context_;
+  uintptr_t last_pc_;
+};
+
+void Thread::DeliverException(Throwable* exception) {
+  SetException(exception);  // Set exception on thread
+
+  Context* long_jump_context = GetLongJumpContext();
+  CatchBlockStackVisitor catch_finder(exception->GetClass(), long_jump_context);
+  WalkStackUntilUpCall(&catch_finder);
+
+  long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP()));
+  uintptr_t long_jump_pc;
+  if (catch_finder.found_) {
+    long_jump_pc = catch_finder.handler_frame_.GetMethod()->ToNativePC(catch_finder.handler_dex_pc_);
+  } else {
+    long_jump_pc = catch_finder.last_pc_;
+  }
+  long_jump_context->SetPC(long_jump_pc);
+  long_jump_context->DoLongJump();
 }
 
-void* Thread::FindExceptionHandlerInMethod(const Method* method,
-                                           void* throw_pc,
-                                           const DexFile& dex_file,
-                                           ClassLinker* class_linker) {
-  Throwable* exception_obj = exception_;
-  exception_ = NULL;
-
-  intptr_t dex_pc = -1;
-  const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->GetCodeItemOffset());
-  DexFile::CatchHandlerIterator iter;
-  for (iter = dex_file.dexFindCatchHandler(*code_item,
-                                           method->ToDexPC(reinterpret_cast<intptr_t>(throw_pc)));
-       !iter.HasNext();
-       iter.Next()) {
-    Class* klass = class_linker->FindSystemClass(dex_file.dexStringByTypeIdx(iter.Get().type_idx_));
-    DCHECK(klass != NULL);
-    if (exception_obj->InstanceOf(klass)) {
-      dex_pc = iter.Get().address_;
-      break;
-    }
+Context* Thread::GetLongJumpContext() {
+  Context* result = long_jump_context_.get();
+  if (result == NULL) {
+    result = Context::Create();
+    long_jump_context_.reset(result);
   }
-
-  exception_ = exception_obj;
-  if (iter.HasNext()) {
-    return NULL;
-  } else {
-    return reinterpret_cast<void*>( method->ToNativePC(dex_pc) );
-  }
+  return result;
 }
 
 void Thread::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
diff --git a/src/thread.h b/src/thread.h
index 66eba85..2e7b615 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -32,6 +32,7 @@
 #include "mutex.h"
 #include "mem_map.h"
 #include "offsets.h"
+#include "UniquePtr.h"
 
 namespace art {
 
@@ -39,6 +40,7 @@
 class Class;
 class ClassLinker;
 class ClassLoader;
+class Context;
 class Method;
 class Monitor;
 class Object;
@@ -94,8 +96,9 @@
 };
 
 struct NativeToManagedRecord {
-  NativeToManagedRecord* link;
-  void* last_top_of_managed_stack;
+  NativeToManagedRecord* link_;
+  void* last_top_of_managed_stack_;
+  uintptr_t last_top_of_managed_stack_pc_;
 };
 
 // Iterator over managed frames up to the first native-to-managed transition
@@ -113,7 +116,9 @@
 
   void Next();
 
-  uintptr_t GetPC() const;
+  uintptr_t GetReturnPC() const;
+
+  uintptr_t LoadCalleeSave(int num) const;
 
   Method** GetSP() const {
     return sp_;
@@ -207,7 +212,7 @@
   Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
   void (*pUnlockObjectFromCode)(Thread*, Object*);
   void (*pLockObjectFromCode)(Thread*, Object*);
-  void (*pThrowException)(Thread*, Throwable*);
+  void (*pThrowException)(void*);
   void (*pHandleFillArrayDataFromCode)(Array*, const uint16_t*);
   Class* (*pInitializeTypeFromCode)(uint32_t, Method*);
   void (*pResolveMethodFromCode)(Method*, uint32_t);
@@ -224,13 +229,14 @@
   void (*pThrowRuntimeExceptionFromCode)(int32_t);
   void (*pThrowInternalErrorFromCode)(int32_t);
   void (*pThrowNoSuchMethodFromCode)(int32_t);
+  void (*pThrowAbstractMethodErrorFromCode)(Method* method, Thread* thread);
   void* (*pFindNativeMethod)(Thread* thread);
   Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
 
   class StackVisitor {
    public:
     virtual ~StackVisitor() {}
-    virtual void VisitFrame(const Frame& frame) = 0;
+    virtual void VisitFrame(const Frame& frame, uintptr_t pc) = 0;
   };
 
   // Creates a new thread.
@@ -324,16 +330,30 @@
     exception_ = NULL;
   }
 
+  // Find catch block and perform long jump to appropriate exception handle
+  void DeliverException(Throwable* exception);
+
+  Context* GetLongJumpContext();
+
   Frame GetTopOfStack() const {
     return top_of_managed_stack_;
   }
 
   // TODO: this is here for testing, remove when we have exception unit tests
   // that use the real stack
-  void SetTopOfStack(void* stack) {
+  void SetTopOfStack(void* stack, uintptr_t pc) {
     top_of_managed_stack_.SetSP(reinterpret_cast<Method**>(stack));
+    top_of_managed_stack_pc_ = pc;
   }
 
+  void SetTopOfStackPC(uintptr_t pc) {
+    top_of_managed_stack_pc_ = pc;
+  }
+
+  // Returns a special method that describes all callee saves being spilt to the
+  // stack.
+  Method* CalleeSaveMethod() const;
+
   void ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...)
       __attribute__ ((format(printf, 3, 4)));
 
@@ -390,14 +410,16 @@
 
   // Linked list recording transitions from native to managed code
   void PushNativeToManagedRecord(NativeToManagedRecord* record) {
-    record->last_top_of_managed_stack = reinterpret_cast<void*>(top_of_managed_stack_.GetSP());
-    record->link = native_to_managed_record_;
+    record->last_top_of_managed_stack_ = reinterpret_cast<void*>(top_of_managed_stack_.GetSP());
+    record->last_top_of_managed_stack_pc_ = top_of_managed_stack_pc_;
+    record->link_ = native_to_managed_record_;
     native_to_managed_record_ = record;
     top_of_managed_stack_.SetSP(NULL);
   }
   void PopNativeToManagedRecord(const NativeToManagedRecord& record) {
-    native_to_managed_record_ = record.link;
-    top_of_managed_stack_.SetSP(reinterpret_cast<Method**>(record.last_top_of_managed_stack));
+    native_to_managed_record_ = record.link_;
+    top_of_managed_stack_.SetSP(reinterpret_cast<Method**>(record.last_top_of_managed_stack_));
+    top_of_managed_stack_pc_ = record.last_top_of_managed_stack_pc_;
   }
 
   const ClassLoader* GetClassLoaderOverride() {
@@ -461,6 +483,10 @@
         OFFSETOF_MEMBER(Frame, sp_));
   }
 
+  static ThreadOffset TopOfManagedStackPcOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_pc_));
+  }
+
   static ThreadOffset TopSirtOffset() {
     return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
   }
@@ -495,6 +521,8 @@
 
   void WalkStack(StackVisitor* visitor) const;
 
+  void WalkStackUntilUpCall(StackVisitor* visitor) const;
+
   // Thin lock thread id. This is a small integer used by the thin lock implementation.
   // This is not to be confused with the native thread's tid, nor is it the value returned
   // by java.lang.Thread.getId --- this is a distinct value, used only for locking. One
@@ -530,6 +558,9 @@
   // a managed stack when a thread is in native code.
   Frame top_of_managed_stack_;
 
+  // PC corresponding to the call out of the top_of_managed_stack_ frame
+  uintptr_t top_of_managed_stack_pc_;
+
   // A linked list (of stack allocated records) recording transitions from
   // native to managed code.
   NativeToManagedRecord* native_to_managed_record_;
@@ -560,6 +591,9 @@
   // useful for testing.
   const ClassLoader* class_loader_override_;
 
+  // Thread local, lazily allocated, long jump context. Used to deliver exceptions.
+  UniquePtr<Context> long_jump_context_;
+
   // TLS key used to retrieve the VM thread object.
   static pthread_key_t pthread_key_self_;
 
@@ -571,6 +605,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
+
 std::ostream& operator<<(std::ostream& os, const Thread& thread);
 std::ostream& operator<<(std::ostream& os, const Thread::State& state);