Fix JNI stubs for synchronized native methods with double/long arguments in low registers.

Change-Id: I3639f7a7a9564630ae01651119fcad237a5e675d
diff --git a/build/Android.common.mk b/build/Android.common.mk
index c950298..ce97a5d 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -97,11 +97,7 @@
 	src/dex2oat.cc
 
 OATDUMP_SRC_FILES := \
-	src/oatdump.cc \
-	src/disassembler.cc \
-	src/disassembler_arm.cc \
-	src/disassembler_mips.cc \
-	src/disassembler_x86.cc
+	src/oatdump.cc
 
 OATEXEC_SRC_FILES := \
 	src/oatexec.cc
@@ -119,6 +115,10 @@
 	src/dex_file.cc \
 	src/dex_file_verifier.cc \
 	src/dex_instruction.cc \
+	src/disassembler.cc \
+	src/disassembler_arm.cc \
+	src/disassembler_mips.cc \
+	src/disassembler_x86.cc \
 	src/dlmalloc.c \
 	src/file.cc \
 	src/file_linux.cc \
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index f8b7013..2c763b2 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -261,6 +261,31 @@
   EXPECT_EQ(2, gJava_MyClassNatives_fooDD_calls);
 }
 
+int gJava_MyClassNatives_fooJJ_synchronized_calls = 0;
+jlong Java_MyClassNatives_fooJJ_synchronized(JNIEnv* env, jobject thisObj, jlong x, jlong y) {
+  // 2 = SirtRef<ClassLoader> + thisObj
+  EXPECT_EQ(2U, Thread::Current()->NumStackReferences());
+  EXPECT_EQ(kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  gJava_MyClassNatives_fooJJ_synchronized_calls++;
+  return x | y;
+}
+
+TEST_F(JniCompilerTest, CompileAndRun_fooJJ_synchronized) {
+  SirtRef<ClassLoader> class_loader(LoadDex("MyClassNatives"));
+  SetUpForTest(class_loader.get(), false, "fooJJ_synchronized", "(JJ)J",
+               reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ_synchronized));
+
+  EXPECT_EQ(0, gJava_MyClassNatives_fooJJ_synchronized_calls);
+  jlong a = 0x1000000020000000ULL;
+  jlong b = 0x00ff000000aa0000ULL;
+  jlong result = env_->CallNonvirtualLongMethod(jobj_, jklass_, jmethod_, a, b);
+  EXPECT_EQ(a | b, result);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooJJ_synchronized_calls);
+}
+
 int gJava_MyClassNatives_fooIOO_calls = 0;
 jobject Java_MyClassNatives_fooIOO(JNIEnv* env, jobject thisObj, jint x, jobject y,
                             jobject z) {
diff --git a/src/oat/jni/jni_compiler.cc b/src/oat/jni/jni_compiler.cc
index c1ae664..a81d884 100644
--- a/src/oat/jni/jni_compiler.cc
+++ b/src/oat/jni/jni_compiler.cc
@@ -21,12 +21,15 @@
 #include "class_linker.h"
 #include "compiled_method.h"
 #include "compiler.h"
+#include "disassembler.h"
 #include "jni_internal.h"
 #include "logging.h"
 #include "macros.h"
 #include "oat/runtime/oat_support_entrypoints.h"
 #include "oat/utils/assembler.h"
 #include "oat/utils/managed_register.h"
+#include "oat/utils/arm/managed_register_arm.h"
+#include "oat/utils/x86/managed_register_x86.h"
 #include "thread.h"
 #include "UniquePtr.h"
 
@@ -164,6 +167,11 @@
   }
 }
 
+static bool IsRegisterPair(InstructionSet instruction_set, ManagedRegister r) {
+  return ((instruction_set == kArm && r.AsArm().IsRegisterPair()) ||
+          (instruction_set == kX86 && r.AsX86().IsRegisterPair()));
+}
+
 // 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
@@ -188,6 +196,7 @@
 
   // Assembler that holds generated instructions
   UniquePtr<Assembler> jni_asm(Assembler::Create(instruction_set));
+  bool should_disassemble = false;
 
   // Offsets into data structures
   // TODO: if cross compiling these offsets are for the host not the target
@@ -289,8 +298,18 @@
 
     // 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), live_argument_regs_size.at(i));
+    for (size_t in = 0, out = 0; in < live_argument_regs.size(); ++in) {
+      size_t size = live_argument_regs_size.at(in);
+      if (IsRegisterPair(instruction_set, live_argument_regs.at(in))) {
+        CHECK_EQ(instruction_set, kArm);
+        arm::ArmManagedRegister pair(live_argument_regs.at(in).AsArm());
+        arm::Register lo(pair.AsRegisterPairLow());
+        arm::Register hi(pair.AsRegisterPairHigh());
+        __ Move(callee_save_regs.at(out++), arm::ArmManagedRegister::FromCoreRegister(lo), size / 2);
+        __ Move(callee_save_regs.at(out++), arm::ArmManagedRegister::FromCoreRegister(hi), size / 2);
+      } else {
+        __ Move(callee_save_regs.at(out++), live_argument_regs.at(in), size);
+      }
     }
 
     // Get SIRT entry for 1st argument (jclass or this) to be 1st argument to
@@ -331,8 +350,18 @@
     __ 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), live_argument_regs_size.at(i));
+    for (size_t in = 0, out = 0; out < live_argument_regs.size(); ++out) {
+      size_t size = live_argument_regs_size.at(out);
+      if (IsRegisterPair(instruction_set, live_argument_regs.at(out))) {
+        CHECK_EQ(instruction_set, kArm);
+        arm::ArmManagedRegister pair(live_argument_regs.at(out).AsArm());
+        arm::Register lo(pair.AsRegisterPairLow());
+        arm::Register hi(pair.AsRegisterPairHigh());
+        __ Move(arm::ArmManagedRegister::FromCoreRegister(lo), callee_save_regs.at(in++), size / 2);
+        __ Move(arm::ArmManagedRegister::FromCoreRegister(hi), callee_save_regs.at(in++), size / 2);
+      } else {
+        __ Move(live_argument_regs.at(out), callee_save_regs.at(in++), size);
+      }
     }
   }
 
@@ -553,6 +582,10 @@
   std::vector<uint8_t> managed_code(cs);
   MemoryRegion code(&managed_code[0], managed_code.size());
   __ FinalizeInstructions(code);
+  if (should_disassemble) {
+    UniquePtr<Disassembler> disassembler(Disassembler::Create(instruction_set));
+    disassembler->Dump(LOG(INFO), &managed_code[0], &managed_code[managed_code.size()]);
+  }
   return new CompiledMethod(instruction_set,
                             managed_code,
                             frame_size,
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 9a03999..2121adf 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -24,6 +24,7 @@
     native long fooJJ(long x, long y);
     native Object fooO(Object x);
     native double fooDD(double x, double y);
+    synchronized native long fooJJ_synchronized(long x, long y);
     native Object fooIOO(int x, Object y, Object z);
     static native Object fooSIOO(int x, Object y, Object z);
     static native int fooSII(int x, int y);
@@ -38,4 +39,4 @@
 
     native void instanceMethodThatShouldTakeClass(int i, Class c);
     static native void staticMethodThatShouldTakeClass(int i, Class c);
-  }
+}