ART: A workaround for a wrongly-read caller's frame

The quick-trampoline treats caller's frame in a different way
then it was formed by quck-invoke stub for hard float ABI targets
such as x86_64 and aarch64. The patch workarounds the issue of
wrongly-read stack arguments due to no contract between quick-invoke
stub and quick-trampoline/shadow-frame-builder regarding the reg-
spill area.

Change-Id: I302f212b4ea5e38632527a5ace03f136b21ef806
Signed-off-by: Vladimir Kostyukov <vladimir.kostyukov@intel.com>
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index a0797f3..6b5e55e 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -1278,4 +1278,217 @@
   EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
 }
 
+template <typename U, typename V> V convert(U in) {
+  DCHECK_LE(sizeof(U), sizeof(V));
+  union { U u; V v; } tmp;
+  tmp.u = in;
+  return tmp.v;
+}
+
+void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1, jint i2, jint i3,
+                                            jint i4, jint i5, jint i6, jint i7, jint i8, jint i9,
+                                            jint i10, jfloat f1, jfloat f2, jfloat f3, jfloat f4,
+                                            jfloat f5, jfloat f6, jfloat f7, jfloat f8, jfloat f9,
+                                            jfloat f10) {
+  EXPECT_EQ(i1, 1);
+  EXPECT_EQ(i2, 2);
+  EXPECT_EQ(i3, 3);
+  EXPECT_EQ(i4, 4);
+  EXPECT_EQ(i5, 5);
+  EXPECT_EQ(i6, 6);
+  EXPECT_EQ(i7, 7);
+  EXPECT_EQ(i8, 8);
+  EXPECT_EQ(i9, 9);
+  EXPECT_EQ(i10, 10);
+
+  jint i11 = convert<jfloat, jint>(f1);
+  EXPECT_EQ(i11, 11);
+  jint i12 = convert<jfloat, jint>(f2);
+  EXPECT_EQ(i12, 12);
+  jint i13 = convert<jfloat, jint>(f3);
+  EXPECT_EQ(i13, 13);
+  jint i14 = convert<jfloat, jint>(f4);
+  EXPECT_EQ(i14, 14);
+  jint i15 = convert<jfloat, jint>(f5);
+  EXPECT_EQ(i15, 15);
+  jint i16 = convert<jfloat, jint>(f6);
+  EXPECT_EQ(i16, 16);
+  jint i17 = convert<jfloat, jint>(f7);
+  EXPECT_EQ(i17, 17);
+  jint i18 = convert<jfloat, jint>(f8);
+  EXPECT_EQ(i18, 18);
+  jint i19 = convert<jfloat, jint>(f9);
+  EXPECT_EQ(i19, 19);
+  jint i20 = convert<jfloat, jint>(f10);
+  EXPECT_EQ(i20, 20);
+}
+
+TEST_F(JniCompilerTest, StackArgsIntsFirst) {
+  TEST_DISABLED_FOR_PORTABLE();
+  SetUpForTest(true, "stackArgsIntsFirst", "(IIIIIIIIIIFFFFFFFFFF)V",
+               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsIntsFirst));
+
+  jint i1 = 1;
+  jint i2 = 2;
+  jint i3 = 3;
+  jint i4 = 4;
+  jint i5 = 5;
+  jint i6 = 6;
+  jint i7 = 7;
+  jint i8 = 8;
+  jint i9 = 9;
+  jint i10 = 10;
+
+  jfloat f1 = convert<jint, jfloat>(11);
+  jfloat f2 = convert<jint, jfloat>(12);
+  jfloat f3 = convert<jint, jfloat>(13);
+  jfloat f4 = convert<jint, jfloat>(14);
+  jfloat f5 = convert<jint, jfloat>(15);
+  jfloat f6 = convert<jint, jfloat>(16);
+  jfloat f7 = convert<jint, jfloat>(17);
+  jfloat f8 = convert<jint, jfloat>(18);
+  jfloat f9 = convert<jint, jfloat>(19);
+  jfloat f10 = convert<jint, jfloat>(20);
+
+  env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, f1, f2,
+                             f3, f4, f5, f6, f7, f8, f9, f10);
+}
+
+void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv* env, jclass klass, jfloat f1, jfloat f2,
+                                              jfloat f3, jfloat f4, jfloat f5, jfloat f6, jfloat f7,
+                                              jfloat f8, jfloat f9, jfloat f10, jint i1, jint i2,
+                                              jint i3, jint i4, jint i5, jint i6, jint i7, jint i8,
+                                              jint i9, jint i10) {
+  EXPECT_EQ(i1, 1);
+  EXPECT_EQ(i2, 2);
+  EXPECT_EQ(i3, 3);
+  EXPECT_EQ(i4, 4);
+  EXPECT_EQ(i5, 5);
+  EXPECT_EQ(i6, 6);
+  EXPECT_EQ(i7, 7);
+  EXPECT_EQ(i8, 8);
+  EXPECT_EQ(i9, 9);
+  EXPECT_EQ(i10, 10);
+
+  jint i11 = convert<jfloat, jint>(f1);
+  EXPECT_EQ(i11, 11);
+  jint i12 = convert<jfloat, jint>(f2);
+  EXPECT_EQ(i12, 12);
+  jint i13 = convert<jfloat, jint>(f3);
+  EXPECT_EQ(i13, 13);
+  jint i14 = convert<jfloat, jint>(f4);
+  EXPECT_EQ(i14, 14);
+  jint i15 = convert<jfloat, jint>(f5);
+  EXPECT_EQ(i15, 15);
+  jint i16 = convert<jfloat, jint>(f6);
+  EXPECT_EQ(i16, 16);
+  jint i17 = convert<jfloat, jint>(f7);
+  EXPECT_EQ(i17, 17);
+  jint i18 = convert<jfloat, jint>(f8);
+  EXPECT_EQ(i18, 18);
+  jint i19 = convert<jfloat, jint>(f9);
+  EXPECT_EQ(i19, 19);
+  jint i20 = convert<jfloat, jint>(f10);
+  EXPECT_EQ(i20, 20);
+}
+
+TEST_F(JniCompilerTest, StackArgsFloatsFirst) {
+  TEST_DISABLED_FOR_PORTABLE();
+  SetUpForTest(true, "stackArgsFloatsFirst", "(FFFFFFFFFFIIIIIIIIII)V",
+               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsFloatsFirst));
+
+  jint i1 = 1;
+  jint i2 = 2;
+  jint i3 = 3;
+  jint i4 = 4;
+  jint i5 = 5;
+  jint i6 = 6;
+  jint i7 = 7;
+  jint i8 = 8;
+  jint i9 = 9;
+  jint i10 = 10;
+
+  jfloat f1 = convert<jint, jfloat>(11);
+  jfloat f2 = convert<jint, jfloat>(12);
+  jfloat f3 = convert<jint, jfloat>(13);
+  jfloat f4 = convert<jint, jfloat>(14);
+  jfloat f5 = convert<jint, jfloat>(15);
+  jfloat f6 = convert<jint, jfloat>(16);
+  jfloat f7 = convert<jint, jfloat>(17);
+  jfloat f8 = convert<jint, jfloat>(18);
+  jfloat f9 = convert<jint, jfloat>(19);
+  jfloat f10 = convert<jint, jfloat>(20);
+
+  env_->CallStaticVoidMethod(jklass_, jmethod_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, i1, i2, i3,
+                             i4, i5, i6, i7, i8, i9, i10);
+}
+
+void Java_MyClassNatives_stackArgsMixed(JNIEnv* env, jclass klass, jint i1, jfloat f1, jint i2,
+                                        jfloat f2, jint i3, jfloat f3, jint i4, jfloat f4, jint i5,
+                                        jfloat f5, jint i6, jfloat f6, jint i7, jfloat f7, jint i8,
+                                        jfloat f8, jint i9, jfloat f9, jint i10, jfloat f10) {
+  EXPECT_EQ(i1, 1);
+  EXPECT_EQ(i2, 2);
+  EXPECT_EQ(i3, 3);
+  EXPECT_EQ(i4, 4);
+  EXPECT_EQ(i5, 5);
+  EXPECT_EQ(i6, 6);
+  EXPECT_EQ(i7, 7);
+  EXPECT_EQ(i8, 8);
+  EXPECT_EQ(i9, 9);
+  EXPECT_EQ(i10, 10);
+
+  jint i11 = convert<jfloat, jint>(f1);
+  EXPECT_EQ(i11, 11);
+  jint i12 = convert<jfloat, jint>(f2);
+  EXPECT_EQ(i12, 12);
+  jint i13 = convert<jfloat, jint>(f3);
+  EXPECT_EQ(i13, 13);
+  jint i14 = convert<jfloat, jint>(f4);
+  EXPECT_EQ(i14, 14);
+  jint i15 = convert<jfloat, jint>(f5);
+  EXPECT_EQ(i15, 15);
+  jint i16 = convert<jfloat, jint>(f6);
+  EXPECT_EQ(i16, 16);
+  jint i17 = convert<jfloat, jint>(f7);
+  EXPECT_EQ(i17, 17);
+  jint i18 = convert<jfloat, jint>(f8);
+  EXPECT_EQ(i18, 18);
+  jint i19 = convert<jfloat, jint>(f9);
+  EXPECT_EQ(i19, 19);
+  jint i20 = convert<jfloat, jint>(f10);
+  EXPECT_EQ(i20, 20);
+}
+
+TEST_F(JniCompilerTest, StackArgsMixed) {
+  TEST_DISABLED_FOR_PORTABLE();
+  SetUpForTest(true, "stackArgsMixed", "(IFIFIFIFIFIFIFIFIFIF)V",
+               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsMixed));
+
+  jint i1 = 1;
+  jint i2 = 2;
+  jint i3 = 3;
+  jint i4 = 4;
+  jint i5 = 5;
+  jint i6 = 6;
+  jint i7 = 7;
+  jint i8 = 8;
+  jint i9 = 9;
+  jint i10 = 10;
+
+  jfloat f1 = convert<jint, jfloat>(11);
+  jfloat f2 = convert<jint, jfloat>(12);
+  jfloat f3 = convert<jint, jfloat>(13);
+  jfloat f4 = convert<jint, jfloat>(14);
+  jfloat f5 = convert<jint, jfloat>(15);
+  jfloat f6 = convert<jint, jfloat>(16);
+  jfloat f7 = convert<jint, jfloat>(17);
+  jfloat f8 = convert<jint, jfloat>(18);
+  jfloat f9 = convert<jint, jfloat>(19);
+  jfloat f10 = convert<jint, jfloat>(20);
+
+  env_->CallStaticVoidMethod(jklass_, jmethod_, i1, f1, i2, f2, i3, f3, i4, f4, i5, f5, i6, f6, i7,
+                             f7, i8, f8, i9, f9, i10, f10);
+}
+
 }  // namespace art
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index fcbcac2..2b29591 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -250,6 +250,7 @@
         if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
           return fpr_args_ + (fpr_index_ * kBytesPerFprSpillLocation);
         }
+        return stack_args_ + (stack_index_ * kBytesStackArgLocation);
       }
     }
     if (gpr_index_ < kNumQuickGprArgs) {
@@ -283,6 +284,12 @@
   }
 
   void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // This implementation doesn't support reg-spill area for hard float
+    // ABI targets such as x86_64 and aarch64. So, for those targets whose
+    // 'kQuickSoftFloatAbi' is 'false':
+    //     (a) 'stack_args_' should point to the first method's argument
+    //     (b) whatever the argument type it is, the 'stack_index_' should
+    //         be moved forward along with every visiting.
     gpr_index_ = 0;
     fpr_index_ = 0;
     stack_index_ = 0;
@@ -290,10 +297,11 @@
       cur_type_ = Primitive::kPrimNot;
       is_split_long_or_double_ = false;
       Visit();
+      if (!kQuickSoftFloatAbi || kNumQuickGprArgs == 0) {
+        stack_index_++;
+      }
       if (kNumQuickGprArgs > 0) {
         gpr_index_++;
-      } else {
-        stack_index_++;
       }
     }
     for (uint32_t shorty_index = 1; shorty_index < shorty_len_; ++shorty_index) {
@@ -307,10 +315,11 @@
         case Primitive::kPrimInt:
           is_split_long_or_double_ = false;
           Visit();
+          if (!kQuickSoftFloatAbi || kNumQuickGprArgs == gpr_index_) {
+            stack_index_++;
+          }
           if (gpr_index_ < kNumQuickGprArgs) {
             gpr_index_++;
-          } else {
-            stack_index_++;
           }
           break;
         case Primitive::kPrimFloat:
@@ -325,9 +334,8 @@
           } else {
             if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
               fpr_index_++;
-            } else {
-              stack_index_++;
             }
+            stack_index_++;
           }
           break;
         case Primitive::kPrimDouble:
@@ -336,16 +344,7 @@
             is_split_long_or_double_ = (kBytesPerGprSpillLocation == 4) &&
                 ((gpr_index_ + 1) == kNumQuickGprArgs);
             Visit();
-            if (gpr_index_ < kNumQuickGprArgs) {
-              gpr_index_++;
-              if (kBytesPerGprSpillLocation == 4) {
-                if (gpr_index_ < kNumQuickGprArgs) {
-                  gpr_index_++;
-                } else {
-                  stack_index_++;
-                }
-              }
-            } else {
+            if (!kQuickSoftFloatAbi || kNumQuickGprArgs == gpr_index_) {
               if (kBytesStackArgLocation == 4) {
                 stack_index_+= 2;
               } else {
@@ -353,6 +352,16 @@
                 stack_index_++;
               }
             }
+            if (gpr_index_ < kNumQuickGprArgs) {
+              gpr_index_++;
+              if (kBytesPerGprSpillLocation == 4) {
+                if (gpr_index_ < kNumQuickGprArgs) {
+                  gpr_index_++;
+                } else if (kQuickSoftFloatAbi) {
+                  stack_index_++;
+                }
+              }
+            }
           } else {
             is_split_long_or_double_ = (kBytesPerFprSpillLocation == 4) &&
                 ((fpr_index_ + 1) == kNumQuickFprArgs);
@@ -362,17 +371,14 @@
               if (kBytesPerFprSpillLocation == 4) {
                 if ((kNumQuickFprArgs != 0) && (fpr_index_ + 1 < kNumQuickFprArgs + 1)) {
                   fpr_index_++;
-                } else {
-                  stack_index_++;
                 }
               }
+            }
+            if (kBytesStackArgLocation == 4) {
+              stack_index_+= 2;
             } else {
-              if (kBytesStackArgLocation == 4) {
-                stack_index_+= 2;
-              } else {
-                CHECK_EQ(kBytesStackArgLocation, 8U);
-                stack_index_++;
-              }
+              CHECK_EQ(kBytesStackArgLocation, 8U);
+              stack_index_++;
             }
           }
           break;
@@ -389,59 +395,10 @@
       CHECK_EQ(kNumQuickFprArgs, 0U);
       return (kNumQuickGprArgs * kBytesPerGprSpillLocation) + kBytesPerGprSpillLocation /* ArtMethod* */;
     } else {
-      size_t offset = kBytesPerGprSpillLocation;  // Skip Method*.
-      size_t gprs_seen = 0;
-      size_t fprs_seen = 0;
-      if (!is_static && (gprs_seen < kNumQuickGprArgs)) {
-        gprs_seen++;
-        offset += kBytesStackArgLocation;
-      }
-      for (uint32_t i = 1; i < shorty_len; ++i) {
-        switch (shorty[i]) {
-          case 'Z':
-          case 'B':
-          case 'C':
-          case 'S':
-          case 'I':
-          case 'L':
-            if (gprs_seen < kNumQuickGprArgs) {
-              gprs_seen++;
-              offset += kBytesStackArgLocation;
-            }
-            break;
-          case 'J':
-            if (gprs_seen < kNumQuickGprArgs) {
-              gprs_seen++;
-              offset += 2 * kBytesStackArgLocation;
-              if (kBytesPerGprSpillLocation == 4) {
-                if (gprs_seen < kNumQuickGprArgs) {
-                  gprs_seen++;
-                }
-              }
-            }
-            break;
-          case 'F':
-            if ((kNumQuickFprArgs != 0) && (fprs_seen + 1 < kNumQuickFprArgs + 1)) {
-              fprs_seen++;
-              offset += kBytesStackArgLocation;
-            }
-            break;
-          case 'D':
-            if ((kNumQuickFprArgs != 0) && (fprs_seen + 1 < kNumQuickFprArgs + 1)) {
-              fprs_seen++;
-              offset += 2 * kBytesStackArgLocation;
-              if (kBytesPerFprSpillLocation == 4) {
-                if ((kNumQuickFprArgs != 0) && (fprs_seen + 1 < kNumQuickFprArgs + 1)) {
-                  fprs_seen++;
-                }
-              }
-            }
-            break;
-          default:
-            LOG(FATAL) << "Unexpected shorty character: " << shorty[i] << " in " << shorty;
-        }
-      }
-      return offset;
+      // For now, there is no reg-spill area for the targets with
+      // hard float ABI. So, the offset pointing to the first method's
+      // parameter ('this' for non-static methods) should be returned.
+      return kBytesPerGprSpillLocation;  // Skip Method*.
     }
   }
 
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 09f1783..b5e0204 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -80,4 +80,16 @@
         Object o248, Object o249, Object o250, Object o251, Object o252, Object o253);
 
     native void withoutImplementation();
+    
+    native static void stackArgsIntsFirst(int i1, int i2, int i3, int i4, int i5, int i6, int i7,
+        int i8, int i9, int i10, float f1, float f2, float f3, float f4, float f5, float f6,
+        float f7, float f8, float f9, float f10);
+    
+    native static void stackArgsFloatsFirst(float f1, float f2, float f3, float f4, float f5,
+        float f6, float f7, float f8, float f9, float f10, int i1, int i2, int i3, int i4, int i5,
+        int i6, int i7, int i8, int i9, int i10);
+    
+    native static void stackArgsMixed(int i1, float f1, int i2, float f2, int i3, float f3, int i4,
+        float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9,
+        float f9, int i10, float f10);
 }