Decoding references. Otherwise, buggy code works only on stack refs.

Now, we handle not only SIRT (stack stuff), but because now we call
DecodeJObject(), we can also handle Globals, Global Weak, and Local.

Change-Id: Ibaf2f6ff81765bda6c14491b35ea088a5bf2bb27
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index e613307..472d000 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1694,6 +1694,23 @@
   // TODO: place reference map on call
 }
 
+void Assembler::Call(uintptr_t addr,
+                     ManagedRegister scratch) {
+  CHECK(scratch.IsCoreRegister());
+  CHECK(sizeof(uintptr_t) == sizeof(int32_t));
+  LoadImmediate(scratch.AsCoreRegister(), static_cast<int32_t>(addr));
+  blx(scratch.AsCoreRegister());
+  // TODO: place reference map on call
+}
+
+void Assembler::GetCurrentThread(ManagedRegister tr) {
+  mov(tr.AsCoreRegister(), ShifterOperand(TR));
+}
+
+void Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister scratch) {
+  StoreToOffset(kStoreWord, TR, SP, offset.Int32Value(), AL);
+}
+
 void Assembler::SuspendPoll(ManagedRegister scratch, ManagedRegister return_reg,
                             FrameOffset return_save_location,
                             size_t return_size) {
diff --git a/src/assembler_arm.h b/src/assembler_arm.h
index c35934e..af3ce40 100644
--- a/src/assembler_arm.h
+++ b/src/assembler_arm.h
@@ -471,6 +471,10 @@
 
   void Call(ManagedRegister base, Offset offset, ManagedRegister scratch);
   void Call(FrameOffset base, Offset offset, ManagedRegister scratch);
+  void Call(uintptr_t addr, ManagedRegister scratch);
+
+  void GetCurrentThread(ManagedRegister tr);
+  void GetCurrentThread(FrameOffset 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 dc04f87..59fd33e 100644
--- a/src/assembler_x86.cc
+++ b/src/assembler_x86.cc
@@ -1619,6 +1619,21 @@
   UNIMPLEMENTED(FATAL);
 }
 
+void Assembler::Call(uintptr_t addr,
+                     ManagedRegister scratch) {
+  leal(scratch.AsCpuRegister(), Address::Absolute(addr));
+  call(scratch.AsCpuRegister());
+}
+
+void Assembler::GetCurrentThread(ManagedRegister tr) {
+  fs()->movl(tr.AsCpuRegister(), Address::Absolute(Thread::SelfOffset()));
+}
+
+void Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister scratch) {
+  fs()->movl(scratch.AsCpuRegister(), Address::Absolute(Thread::SelfOffset()));
+  movl(Address(ESP, offset), scratch.AsCpuRegister());
+}
+
 void Assembler::SuspendPoll(ManagedRegister scratch, ManagedRegister return_reg,
                             FrameOffset return_save_location,
                             size_t return_size) {
diff --git a/src/assembler_x86.h b/src/assembler_x86.h
index d26d33f..1b882e0 100644
--- a/src/assembler_x86.h
+++ b/src/assembler_x86.h
@@ -482,6 +482,10 @@
 
   void Call(ManagedRegister base, Offset offset, ManagedRegister scratch);
   void Call(FrameOffset base, Offset offset, ManagedRegister scratch);
+  void Call(uintptr_t addr, ManagedRegister scratch);
+
+  void GetCurrentThread(ManagedRegister tr);
+  void GetCurrentThread(FrameOffset 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.cc b/src/calling_convention.cc
index 12dab2e..83f7a00 100644
--- a/src/calling_convention.cc
+++ b/src/calling_convention.cc
@@ -29,6 +29,9 @@
     itr_longs_and_doubles_++;
     itr_slots_++;
   }
+  if (IsCurrentParamAReference()) {
+    itr_refs_++;
+  }
   itr_args_++;
   itr_slots_++;
 }
@@ -84,6 +87,9 @@
       itr_slots_++;
     }
   }
+  if (IsCurrentParamAReference()) {
+    itr_refs_++;
+  }
   itr_args_++;
   itr_slots_++;
 }
@@ -108,15 +114,7 @@
   CHECK_GT(SirtLinkOffset(), SirtNumRefsOffset());
   // Address of 1st SIRT entry
   int result = SirtLinkOffset().Int32Value() + kPointerSize;
-  if (itr_args_ != kObjectOrClass) {
-    const Method *method = GetMethod();
-    int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
-    int previous_refs = GetMethod()->NumReferenceArgsBefore(arg_pos);
-    if (method->IsStatic()) {
-      previous_refs++;  // account for jclass
-    }
-    result += previous_refs * kPointerSize;
-  }
+  result += itr_refs_ * kPointerSize;
   CHECK_GT(result, SirtLinkOffset().Int32Value());
   return FrameOffset(result);
 }
diff --git a/src/calling_convention.h b/src/calling_convention.h
index 1385337..44ce93f 100644
--- a/src/calling_convention.h
+++ b/src/calling_convention.h
@@ -26,6 +26,8 @@
   // Register reserved for scratch usage during procedure calls
   ManagedRegister InterproceduralScratchRegister();
 
+  ManagedRegister ThreadRegister();
+
   // Offset of Method within the frame
   FrameOffset MethodStackOffset();
 
@@ -38,6 +40,7 @@
     displacement_ = displacement;
     itr_slots_ = 0;
     itr_args_ = 0;
+    itr_refs_ = 0;
     itr_longs_and_doubles_ = 0;
   }
 
@@ -49,6 +52,7 @@
   // The slot number for current calling_convention argument.
   // Note that each slot is 32-bit. When the current argument is bigger
   // than 32 bits, return the first slot number for this argument.
+  unsigned int itr_refs_;
   unsigned int itr_slots_;
   // The argument number along argument list for current argument
   unsigned int itr_args_;
diff --git a/src/calling_convention_arm.cc b/src/calling_convention_arm.cc
index 00720de..9c7ba6f 100644
--- a/src/calling_convention_arm.cc
+++ b/src/calling_convention_arm.cc
@@ -9,6 +9,10 @@
   return ManagedRegister::FromCoreRegister(R0);
 }
 
+ManagedRegister CallingConvention::ThreadRegister() {
+  return ManagedRegister::FromCoreRegister(TR);
+}
+
 ManagedRegister CallingConvention::InterproceduralScratchRegister() {
   return ManagedRegister::FromCoreRegister(R12);
 }
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 238b9ef..1789e1b 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -16,6 +16,10 @@
 
 namespace art {
 
+static Object* DecodeJObjectInThread(Thread* thread, jobject obj) {
+  return thread->DecodeJObject(obj);
+}
+
 // Generate the JNI bridge for the given method, general contract:
 // - Arguments are in the managed runtime format, either on stack or in
 //   registers, a reference to the method object is supplied as part of this
@@ -128,21 +132,14 @@
     } else {
       CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
     }
-    // Generate JNIEnv* in place and leave a copy in jni_env_register
+    // Generate JNIEnv* in place and leave a copy in jni_fns_register
     jni_conv.ResetIterator(FrameOffset(out_arg_size));
-    ManagedRegister jni_env_register =
+    ManagedRegister jni_fns_register =
         jni_conv.InterproceduralScratchRegister();
-    if (jni_conv.IsCurrentParamInRegister()) {
-      jni_env_register = jni_conv.CurrentParamRegister();
-    }
-    jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
-    if (!jni_conv.IsCurrentParamInRegister()) {
-      FrameOffset out_off = jni_conv.CurrentParamStackOffset();
-      jni_asm->StoreRawPtr(out_off, jni_env_register);
-    }
+    jni_asm->LoadRawPtrFromThread(jni_fns_register, Thread::JniEnvOffset());
+    SetNativeParameter(jni_asm, &jni_conv, jni_fns_register);
     // Call JNIEnv->MonitorEnter(object)
-    ManagedRegister jni_fns_register = jni_conv.InterproceduralScratchRegister();
-    jni_asm->LoadRawPtr(jni_fns_register, jni_env_register, functions);
+    jni_asm->LoadRawPtr(jni_fns_register, jni_fns_register, functions);
     jni_asm->Call(jni_fns_register, monitor_enter,
                   jni_conv.InterproceduralScratchRegister());
     jni_asm->FillFromSpillArea(spill_regs, out_arg_size);
@@ -247,30 +244,23 @@
     jni_conv.ResetIterator(FrameOffset(out_arg_size));
     ManagedRegister jni_env_register =
         jni_conv.InterproceduralScratchRegister();
-    if (jni_conv.IsCurrentParamInRegister()) {
-      jni_env_register = jni_conv.CurrentParamRegister();
-    }
     jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
-    if (!jni_conv.IsCurrentParamInRegister()) {
-      FrameOffset out_off = jni_conv.CurrentParamStackOffset();
-      jni_asm->StoreRawPtr(out_off, jni_env_register);
-    }
+    SetNativeParameter(jni_asm, &jni_conv, jni_env_register);
     // Call JNIEnv->MonitorExit(object)
-    ManagedRegister jni_fns_register = jni_conv.InterproceduralScratchRegister();
-    jni_asm->LoadRawPtr(jni_fns_register, jni_env_register, functions);
-    jni_asm->Call(jni_fns_register, monitor_exit,
+    jni_asm->LoadRawPtr(jni_env_register, jni_env_register, functions);
+    jni_asm->Call(jni_env_register, monitor_exit,
                   jni_conv.InterproceduralScratchRegister());
     // Reload return value
     jni_asm->Load(jni_conv.ReturnRegister(), return_save_location,
                   jni_conv.SizeOfReturnValue());
   }
 
-  // 11. Release outgoing argument area
+  // 12. Release outgoing argument area
   jni_asm->DecreaseFrameSize(out_arg_size);
   mr_conv.ResetIterator(FrameOffset(frame_size));
   jni_conv.ResetIterator(FrameOffset(0));
 
-  // 12. Transition from being in native to managed code, possibly entering a
+  // 13. Transition from being in native to managed code, possibly entering a
   //     safepoint
   CHECK(!jni_conv.InterproceduralScratchRegister()
                  .Equals(jni_conv.ReturnRegister()));  // don't clobber result
@@ -285,24 +275,40 @@
                                   jni_conv.InterproceduralScratchRegister());
 
 
-  // 15. Place result in correct register possibly loading from indirect
+  // 14. Place result in correct register possibly loading from indirect
   //     reference table
   if (jni_conv.IsReturnAReference()) {
-    // TODO: load from local/global reference tables
-    jni_asm->LoadReferenceFromSirt(mr_conv.ReturnRegister(),
-                                   jni_conv.ReturnRegister());
-  } else {
-    jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
-  }
+    jni_asm->IncreaseFrameSize(kStackAlignment);
+    jni_conv.ResetIterator(FrameOffset(kStackAlignment));
 
-  // 16. Remove SIRT from thread
+    jni_conv.Next();
+    SetNativeParameter(jni_asm, &jni_conv, jni_conv.ReturnRegister());
+
+    jni_conv.ResetIterator(FrameOffset(kStackAlignment));
+
+    if (jni_conv.IsCurrentParamInRegister()) {
+      jni_asm->GetCurrentThread(jni_conv.CurrentParamRegister());
+    } else {
+      jni_asm->GetCurrentThread(jni_conv.CurrentParamStackOffset(),
+                                jni_conv.InterproceduralScratchRegister());
+    }
+
+    jni_asm->Call(reinterpret_cast<uintptr_t>(DecodeJObjectInThread),
+                  jni_conv.InterproceduralScratchRegister());
+
+    jni_asm->DecreaseFrameSize(kStackAlignment);
+    jni_conv.ResetIterator(FrameOffset(0));
+  }
+  jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
+
+  // 15. Remove SIRT from thread
   jni_asm->CopyRawPtrToThread(Thread::TopSirtOffset(), jni_conv.SirtLinkOffset(),
                               jni_conv.InterproceduralScratchRegister());
 
-  // 17. Remove activation
+  // 16. Remove activation
   jni_asm->RemoveFrame(frame_size, spill_regs);
 
-  // 18. Finalize code generation
+  // 17. Finalize code generation
   jni_asm->EmitSlowPaths();
   size_t cs = jni_asm->CodeSize();
   MemoryRegion code(AllocateCode(cs), cs);
@@ -313,6 +319,19 @@
   native_method->SetReturnPcOffsetInBytes(jni_conv.ReturnPcOffset());
 }
 
+void JniCompiler::SetNativeParameter(Assembler *jni_asm,
+                                     JniCallingConvention *jni_conv,
+                                     ManagedRegister in_reg) {
+  if (jni_conv->IsCurrentParamOnStack()) {
+    FrameOffset dest = jni_conv->CurrentParamStackOffset();
+    jni_asm->StoreRawPtr(dest, in_reg);
+  } else {
+    if (!jni_conv->CurrentParamRegister().Equals(in_reg)) {
+      jni_asm->Move(jni_conv->CurrentParamRegister(), in_reg);
+    }
+  }
+}
+
 // Copy a single parameter from the managed to the JNI calling convention
 void JniCompiler::CopyParameter(Assembler* jni_asm,
                                 ManagedRuntimeCallingConvention* mr_conv,
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index a3b2e53..3c0d8b4 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -30,6 +30,10 @@
                      JniCallingConvention* jni_conv,
                      size_t frame_size, size_t out_arg_size);
 
+  void SetNativeParameter(Assembler *jni_asm,
+                          JniCallingConvention *jni_conv,
+                          ManagedRegister in_reg);
+
   // A poor man's code cache
   void* AllocateCode(size_t size);
 
diff --git a/src/thread.h b/src/thread.h
index 0a94151..8a297d6 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -313,6 +313,9 @@
                                      void* throw_pc,
                                      const DexFile& dex_file,
                                      ClassLinker* class_linker);
+  static ThreadOffset SelfOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, self_));
+  }
 
   // Offset of exception within Thread, used by generated code
   static ThreadOffset ExceptionOffset() {