Fix various JNI compiler bugs/unimplementeds.

For both x86 and arm we were under computing the outgoing argument size.
For ARM the managed double/long passing had been assumed to be following AAPCS,
however, currently we split long/doubles across R1_R2 and R3 and the stack.
Add support for this in the managed register and jni compiler code.
Add test and various other clean ups to jni compiler code.

Change-Id: I4129076d052a8bce42304f5331b71aa3ac50210f
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 0a11268..3524974 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -86,11 +86,11 @@
 
       if (input_in_reg) {
         ManagedRegister in_reg  =  mr_conv.CurrentParamRegister();
-        jni_asm->VerifyObject(in_reg, mr_conv.IsCurrentUserArg());
+        jni_asm->VerifyObject(in_reg, mr_conv.IsCurrentArgPossiblyNull());
         jni_asm->StoreRef(sirt_offset, in_reg);
       } else if (input_on_stack) {
         FrameOffset in_off  = mr_conv.CurrentParamStackOffset();
-        jni_asm->VerifyObject(in_off, mr_conv.IsCurrentUserArg());
+        jni_asm->VerifyObject(in_off, mr_conv.IsCurrentArgPossiblyNull());
         jni_asm->CopyRef(sirt_offset, in_off,
                          mr_conv.InterproceduralScratchRegister());
       }
@@ -278,14 +278,15 @@
   // 14. Place result in correct register possibly loading from indirect
   //     reference table
   if (jni_conv.IsReturnAReference()) {
-    jni_asm->IncreaseFrameSize(kStackAlignment);
-    jni_conv.ResetIterator(FrameOffset(kStackAlignment));
+    jni_asm->IncreaseFrameSize(out_arg_size);
+    jni_conv.ResetIterator(FrameOffset(out_arg_size));
 
-    jni_conv.Next();
+    jni_conv.Next();  // Skip Thread* argument
+    // Pass result as arg2
     SetNativeParameter(jni_asm, &jni_conv, jni_conv.ReturnRegister());
 
-    jni_conv.ResetIterator(FrameOffset(kStackAlignment));
-
+    // Pass Thread*
+    jni_conv.ResetIterator(FrameOffset(out_arg_size));
     if (jni_conv.IsCurrentParamInRegister()) {
       jni_asm->GetCurrentThread(jni_conv.CurrentParamRegister());
     } else {
@@ -296,7 +297,7 @@
     jni_asm->Call(reinterpret_cast<uintptr_t>(DecodeJObjectInThread),
                   jni_conv.InterproceduralScratchRegister());
 
-    jni_asm->DecreaseFrameSize(kStackAlignment);
+    jni_asm->DecreaseFrameSize(out_arg_size);
     jni_conv.ResetIterator(FrameOffset(0));
   }
   jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
@@ -344,11 +345,16 @@
   bool null_allowed = false;
   bool ref_param = jni_conv->IsCurrentParamAReference();
   CHECK(!ref_param || mr_conv->IsCurrentParamAReference());
+  // input may be in register, on stack or both - but not none!
   CHECK(input_in_reg || mr_conv->IsCurrentParamOnStack());
-  CHECK(output_in_reg || jni_conv->IsCurrentParamOnStack());
+  if (output_in_reg) {  // output shouldn't straddle registers and stack
+    CHECK(!jni_conv->IsCurrentParamOnStack());
+  } else {
+    CHECK(jni_conv->IsCurrentParamOnStack());
+  }
   // References need placing in SIRT and the entry address passing
   if (ref_param) {
-    null_allowed = mr_conv->IsCurrentUserArg();
+    null_allowed = mr_conv->IsCurrentArgPossiblyNull();
     // Compute SIRT offset. Note null is placed in the SIRT but the jobject
     // passed to the native code must be null (not a pointer into the SIRT
     // as with regular references).
@@ -362,7 +368,12 @@
     if (ref_param) {
       jni_asm->CreateSirtEntry(out_reg, sirt_offset, in_reg, null_allowed);
     } else {
-      jni_asm->Move(out_reg, in_reg);
+      if (!mr_conv->IsCurrentParamOnStack()) {
+        // regular non-straddling move
+        jni_asm->Move(out_reg, in_reg);
+      } else {
+        UNIMPLEMENTED(FATAL);  // we currently don't expect to see this case
+      }
     }
   } else if (!input_in_reg && !output_in_reg) {
     FrameOffset out_off = jni_conv->CurrentParamStackOffset();
@@ -404,7 +415,16 @@
     } else {
       size_t param_size = mr_conv->CurrentParamSize();
       CHECK_EQ(param_size, jni_conv->CurrentParamSize());
-      jni_asm->Store(out_off, in_reg, param_size);
+      if (!mr_conv->IsCurrentParamOnStack()) {
+        // regular non-straddling store
+        jni_asm->Store(out_off, in_reg, param_size);
+      } else {
+        // store where input straddles registers and stack
+        CHECK_EQ(param_size, 8u);
+        FrameOffset in_off = mr_conv->CurrentParamStackOffset();
+        jni_asm->StoreSpanning(out_off, in_reg, in_off,
+                               mr_conv->InterproceduralScratchRegister());
+      }
     }
   }
 }