Handle double and long on the Managed side, and follow AAPCS on the Native side.

Change-Id: I43a8fa36cb79fb438f075986a46c66ab8258e725
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index a728239..70dd20c 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1516,14 +1516,21 @@
 void Assembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch,
                      size_t size) {
   CHECK(scratch.IsCoreRegister());
+  CHECK(size == 4 || size == 8);
   if (size == 4) {
     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
                    SP, src.Int32Value());
     StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
                   SP, dest.Int32Value());
-  } else {
-    // TODO: size != 4
-    LOG(FATAL) << "Unimplemented";
+  } else if (size == 8) {
+    LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+                   SP, src.Int32Value());
+    StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+                  SP, dest.Int32Value());
+    LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
+                   SP, src.Int32Value() + 4);
+    StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
+                  SP, dest.Int32Value() + 4);
   }
 }
 
diff --git a/src/calling_convention.cc b/src/calling_convention.cc
index bfd0e34..ec8daeb 100644
--- a/src/calling_convention.cc
+++ b/src/calling_convention.cc
@@ -20,29 +20,34 @@
 }
 
 bool ManagedRuntimeCallingConvention::HasNext() {
-  return itr_position_ < GetMethod()->NumArgs();
+  return itr_args_ < GetMethod()->NumArgs();
 }
 
 void ManagedRuntimeCallingConvention::Next() {
   CHECK(HasNext());
-  if (((itr_position_ != 0) || GetMethod()->IsStatic()) &&
-      GetMethod()->IsParamALongOrDouble(itr_position_)) {
+  if (IsCurrentUserArg() && 
+      GetMethod()->IsParamALongOrDouble(itr_args_)) {
     itr_longs_and_doubles_++;
+    itr_slots_++;
   }
-  itr_position_++;
+  itr_args_++;
+  itr_slots_++;
 }
 
-bool ManagedRuntimeCallingConvention::IsCurrentParamPossiblyNull() {
-  // for a virtual method, this should never be NULL
-  return GetMethod()->IsStatic() || (itr_position_ != 0);
+bool ManagedRuntimeCallingConvention::IsCurrentUserArg() {
+  if (GetMethod()->IsStatic()) {
+    return true;
+  }
+  // For a virtual method, "this" should never be NULL.
+  return (itr_args_ != 0);
 }
 
 size_t ManagedRuntimeCallingConvention::CurrentParamSize() {
-  return GetMethod()->ParamSize(itr_position_);
+  return GetMethod()->ParamSize(itr_args_);
 }
 
 bool ManagedRuntimeCallingConvention::IsCurrentParamAReference() {
-  return GetMethod()->IsParamAReference(itr_position_);
+  return GetMethod()->IsParamAReference(itr_args_);
 }
 
 // JNI calling convention
@@ -71,33 +76,35 @@
 }
 
 bool JniCallingConvention::HasNext() {
-  if (itr_position_ <= kObjectOrClass) {
+  if (itr_args_ <= kObjectOrClass) {
     return true;
   } else {
-    unsigned int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+    unsigned int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
     return arg_pos < GetMethod()->NumArgs();
   }
 }
 
 void JniCallingConvention::Next() {
   CHECK(HasNext());
-  if (itr_position_ > kObjectOrClass) {
-    int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+  if (itr_args_ > kObjectOrClass) {
+    int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
     if (GetMethod()->IsParamALongOrDouble(arg_pos)) {
       itr_longs_and_doubles_++;
+      itr_slots_++;
     }
   }
-  itr_position_++;
+  itr_args_++;
+  itr_slots_++;
 }
 
 bool JniCallingConvention::IsCurrentParamAReference() {
-  switch (itr_position_) {
+  switch (itr_args_) {
     case kJniEnv:
       return false;  // JNIEnv*
     case kObjectOrClass:
       return true;   // jobject or jclass
     default: {
-      int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+      int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
       return GetMethod()->IsParamAReference(arg_pos);
     }
   }
@@ -109,11 +116,11 @@
   CHECK_GT(ShbLinkOffset(), ShbNumRefsOffset());
   // Address of 1st handle
   int result = ShbLinkOffset().Int32Value() + kPointerSize;
-  if (itr_position_ != kObjectOrClass) {
-    bool is_static = GetMethod()->IsStatic();
-    int arg_pos = itr_position_ - (is_static ? 2 : 1);
+  if (itr_args_ != kObjectOrClass) {
+    const Method *method = GetMethod();
+    int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
     int previous_refs = GetMethod()->NumReferenceArgsBefore(arg_pos);
-    if (is_static) {
+    if (method->IsStatic()) {
       previous_refs++;  // account for jclass
     }
     result += previous_refs * kPointerSize;
@@ -123,12 +130,19 @@
 }
 
 size_t JniCallingConvention::CurrentParamSize() {
-  if (itr_position_ <= kObjectOrClass) {
+  if (itr_args_ <= kObjectOrClass) {
     return kPointerSize;  // JNIEnv or jobject/jclass
   } else {
-    int arg_pos = itr_position_ - (GetMethod()->IsStatic() ? 2 : 1);
+    int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(GetMethod());
     return GetMethod()->ParamSize(arg_pos);
   }
 }
 
+size_t JniCallingConvention::NumberOfExtraArgumentsForJni(
+    const Method* method) {
+  // The first argument is the JNIEnv*.
+  // Static methods have an extra argument which is the jclass.
+  return (method->IsStatic() ? 2 : 1);
+}
+
 }  // namespace art
diff --git a/src/calling_convention.h b/src/calling_convention.h
index 23ca6d6..b335597 100644
--- a/src/calling_convention.h
+++ b/src/calling_convention.h
@@ -36,7 +36,8 @@
   // below the one being iterated over.
   void ResetIterator(FrameOffset displacement) {
     displacement_ = displacement;
-    itr_position_ = 0;
+    itr_slots_ = 0;
+    itr_args_ = 0;
     itr_longs_and_doubles_ = 0;
   }
 
@@ -45,9 +46,13 @@
                                                method_(method) {}
   const Method* GetMethod() const { return method_; }
 
-  // position along argument list
-  unsigned int itr_position_;
-  // number of longs and doubles seen along argument list
+  // 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_slots_;
+  // The argument number along argument list for current argument
+  unsigned int itr_args_;
+  // Number of longs and doubles seen along argument list
   unsigned int itr_longs_and_doubles_;
   // Space for frames below this on the stack
   FrameOffset displacement_;
@@ -70,7 +75,7 @@
   bool IsCurrentParamAReference();
   bool IsCurrentParamInRegister();
   bool IsCurrentParamOnStack();
-  bool IsCurrentParamPossiblyNull();
+  bool IsCurrentUserArg();
   size_t CurrentParamSize();
   ManagedRegister CurrentParamRegister();
   FrameOffset CurrentParamStackOffset();
@@ -149,6 +154,7 @@
   // located
   size_t NumberOfOutgoingStackArgs();
 
+  static size_t NumberOfExtraArgumentsForJni(const Method* method);
   DISALLOW_COPY_AND_ASSIGN(JniCallingConvention);
 };
 
diff --git a/src/calling_convention_arm.cc b/src/calling_convention_arm.cc
index f93dc9b..a7a080e 100644
--- a/src/calling_convention_arm.cc
+++ b/src/calling_convention_arm.cc
@@ -32,36 +32,44 @@
 // Managed runtime calling convention
 
 bool ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
-  return itr_position_ < 3;
+  if (itr_slots_ < 3) {
+    return true;
+  }
+  return false;  // everything else on the stack
 }
 
 bool ManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
-  return itr_position_ >= 3;
+  return !IsCurrentParamInRegister();
 }
 
 static const Register kManagedArgumentRegisters[] = {
   R1, R2, R3
 };
 ManagedRegister ManagedRuntimeCallingConvention::CurrentParamRegister() {
-  CHECK_LT(itr_position_, 3u);
+  CHECK_LT(itr_slots_, 3u); // Otherwise, should have gone through stack
   const Method* method = GetMethod();
-  if (method->IsParamALongOrDouble(itr_position_)) {
-    // TODO: handle a long/double split between registers and the stack, also
-    // itr_position_ 0
-    if (itr_position_ != 1u) {
-      LOG(WARNING) << "Unimplemented";
+  if (method->IsParamALongOrDouble(itr_args_)) {
+    if (itr_slots_ == 0) {
+      // return R1 to denote R1_R2
+      return ManagedRegister::FromCoreRegister(kManagedArgumentRegisters
+                                               [itr_slots_]);
+    } else if (itr_slots_ == 1) {
+      return ManagedRegister::FromRegisterPair(R2_R3);
+    } else {
+      // This is a long/double split between registers and the stack
+      return ManagedRegister::FromCoreRegister(
+        kManagedArgumentRegisters[itr_slots_]);
     }
-    return ManagedRegister::FromRegisterPair(R2_R3);
   } else {
     return
-      ManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_position_]);
+      ManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_slots_]);
   }
 }
 
 FrameOffset ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
-  CHECK_GE(itr_position_, 3u);
+  CHECK_GE(itr_slots_, 3u);
   return FrameOffset(displacement_.Int32Value() +
-                 ((itr_position_ + itr_longs_and_doubles_ - 3) * kPointerSize));
+             ((itr_slots_ - 3) * kPointerSize));
 }
 
 // JNI calling convention
@@ -72,37 +80,50 @@
   return true;
 }
 
+// JniCallingConvention ABI follows AAPCS
+//
+// In processing each parameter, we know that IsCurrentParamInRegister()
+// or IsCurrentParamOnStack() will be called first.
+// Both functions will ensure that we conform to AAPCS.
+//
 bool JniCallingConvention::IsCurrentParamInRegister() {
-  return (itr_position_ + itr_longs_and_doubles_) < 4;
+  // AAPCS processing
+  const Method* method = GetMethod();
+  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) {
+      itr_slots_++;
+    }
+  }
+
+  return itr_slots_ < 4;
 }
 
 bool JniCallingConvention::IsCurrentParamOnStack() {
-  return (itr_position_ + itr_longs_and_doubles_) >= 4;
+  return !IsCurrentParamInRegister();
 }
 
 static const Register kJniArgumentRegisters[] = {
   R0, R1, R2, R3
 };
 ManagedRegister JniCallingConvention::CurrentParamRegister() {
-  CHECK_LT(itr_position_ + itr_longs_and_doubles_, 4u);
+  CHECK_LT(itr_slots_, 4u);
   const Method* method = GetMethod();
-  int arg_pos = itr_position_ - (method->IsStatic() ? 2 : 1);
-  if ((itr_position_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
-    // TODO: handle a long/double split between registers and the stack
-    if (itr_position_ != 2u) {
-      LOG(WARNING) << "Unimplemented";
-    }
+  int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
+  if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
+    CHECK_EQ(itr_slots_, 2u);
     return ManagedRegister::FromRegisterPair(R2_R3);
   } else {
     return
-      ManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_position_]);
+      ManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
   }
 }
 
 FrameOffset JniCallingConvention::CurrentParamStackOffset() {
-  CHECK_GE(itr_position_ + itr_longs_and_doubles_, 4u);
+  CHECK_GE(itr_slots_, 4u);
   return FrameOffset(displacement_.Int32Value() - OutArgSize()
-               + ((itr_position_ + itr_longs_and_doubles_ - 4) * kPointerSize));
+               + ((itr_slots_ - 4) * kPointerSize));
 }
 
 size_t JniCallingConvention::NumberOfOutgoingStackArgs() {
diff --git a/src/calling_convention_x86.cc b/src/calling_convention_x86.cc
index 2724cba..d245d81 100644
--- a/src/calling_convention_x86.cc
+++ b/src/calling_convention_x86.cc
@@ -44,8 +44,7 @@
 }
 
 FrameOffset ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
-  int num_slots = itr_position_ + itr_longs_and_doubles_;
-  return FrameOffset(displacement_.Int32Value() + (num_slots * kPointerSize));
+  return FrameOffset(displacement_.Int32Value() + (itr_slots_ * kPointerSize));
 }
 
 // JNI calling convention
@@ -68,9 +67,8 @@
 }
 
 FrameOffset JniCallingConvention::CurrentParamStackOffset() {
-  int num_slots = itr_position_ + itr_longs_and_doubles_;
   return FrameOffset(displacement_.Int32Value() - OutArgSize() +
-                     (num_slots * kPointerSize));
+                     (itr_slots_ * kPointerSize));
 }
 
 size_t JniCallingConvention::NumberOfOutgoingStackArgs() {
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 8433c27..436d4d8 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -72,14 +72,16 @@
       // Check handle offset is within frame
       CHECK_LT(handle_offset.Uint32Value(), frame_size);
       bool input_in_reg = mr_conv.IsCurrentParamInRegister();
-      CHECK(input_in_reg || mr_conv.IsCurrentParamOnStack());
+      bool input_on_stack = mr_conv.IsCurrentParamOnStack();
+      CHECK(input_in_reg || input_on_stack);
+
       if (input_in_reg) {
         ManagedRegister in_reg  =  mr_conv.CurrentParamRegister();
-        jni_asm->ValidateRef(in_reg, mr_conv.IsCurrentParamPossiblyNull());
+        jni_asm->ValidateRef(in_reg, mr_conv.IsCurrentUserArg());
         jni_asm->StoreRef(handle_offset, in_reg);
-      } else {
+      } else if (input_on_stack) {
         FrameOffset in_off  = mr_conv.CurrentParamStackOffset();
-        jni_asm->ValidateRef(in_off, mr_conv.IsCurrentParamPossiblyNull());
+        jni_asm->ValidateRef(in_off, mr_conv.IsCurrentUserArg());
         jni_asm->CopyRef(handle_offset, in_off,
                          mr_conv.InterproceduralScratchRegister());
       }
@@ -296,7 +298,7 @@
   CHECK(output_in_reg || jni_conv->IsCurrentParamOnStack());
   // References need handlerization and the handle address passing
   if (ref_param) {
-    null_allowed = mr_conv->IsCurrentParamPossiblyNull();
+    null_allowed = mr_conv->IsCurrentUserArg();
     // Compute handle offset. Note null is placed in the SHB but the jobject
     // passed to the native code must be null (not a pointer into the SHB
     // as with regular references).
diff --git a/src/object.cc b/src/object.cc
index 2297eca..7ea30a5 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -291,7 +291,7 @@
 // The number of reference arguments to this method including implicit this
 // pointer
 size_t Method::NumReferenceArgs() const {
-  size_t result = IsStatic() ? 0 : 1;
+  size_t result = IsStatic() ? 0 : 1;  // The implicit this pointer.
   for (int i = 1; i < shorty_.length(); i++) {
     if ((shorty_[i] == 'L') || (shorty_[i] == '[')) {
       result++;
diff --git a/src/object.h b/src/object.h
index 63f5b16..1451c54 100644
--- a/src/object.h
+++ b/src/object.h
@@ -433,8 +433,21 @@
     return shorty_[0] == 'V';
   }
 
-  // The number of arguments that should be supplied to this method
+  // "Args" may refer to any of the 3 levels of "Args."
+  // To avoid confusion, our code will denote which "Args" clearly:
+  //  1. UserArgs: Args that a user see.
+  //  2. Args: Logical JVM-level Args. E.g., the first in Args will be the
+  //       receiver.
+  //  3. CConvArgs: Calling Convention Args, which is physical-level Args.
+  //       E.g., the first in Args is Method* for both static and non-static
+  //       methods. And CConvArgs doesn't deal with the receiver because
+  //       receiver is hardwired in an implicit register, so CConvArgs doesn't
+  //       need to deal with it.
+  //
+  // The number of Args that should be supplied to this method
   size_t NumArgs() const {
+    // "1 +" because the first in Args is the receiver.
+    // "- 1" because we don't count the return type.
     return (IsStatic() ? 0 : 1) + shorty_.length() - 1;
   }