Implement art_quick_string_compareto entrypoint for x86-64

This adds implementation of the art_quick_string_compareto
entrypoint for the x86-64 platform.

Add a test to stub_test, enabled for arm, x86 and x86-64.

Change-Id: I71b318b03d4c8920ccb3723b59c43542e219bf47
Signed-off-by: Alexei Zavjalov <alexei.zavjalov@intel.com>
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 7027b32..437beb5 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -633,4 +633,79 @@
 #endif
 }
 
+
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+extern "C" void art_quick_string_compareto(void);
+#endif
+
+TEST_F(StubTest, StringCompareTo) {
+  TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+  // TODO: Check the "Unresolved" allocation stubs
+
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  // garbage is created during ClassLinker::Init
+
+  // Create some strings
+  // Use array so we can index into it and use a matrix for expected results
+  constexpr size_t string_count = 7;
+  const char* c[string_count] = { "", "", "a", "aa", "ab", "aac", "aac" };
+
+  SirtRef<mirror::String>* s[string_count];
+
+  for (size_t i = 0; i < string_count; ++i) {
+    s[i] = new SirtRef<mirror::String>(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(),
+                                                                                         c[i]));
+  }
+
+  // TODO: wide characters
+
+  // Matrix of expectations. First component is first parameter. Note we only check against the
+  // sign, not the value.
+  int32_t expected[string_count][string_count] = {
+      {  0,  0, -1, -1, -1, -1, -1 },  // ""
+      {  0,  0, -1, -1, -1, -1, -1 },  // ""
+      {  1,  1,  0, -1, -1, -1, -1 },  // "a"
+      {  1,  1,  1,  0, -1, -1, -1 },  // "aa"
+      {  1,  1,  1,  1,  0,  1,  1 },  // "ab"
+      {  1,  1,  1,  1, -1,  0,  0 },  // "aac"
+      {  1,  1,  1,  1, -1,  0,  0 }   // "aac"
+  //    ""  ""   a  aa  ab  aac aac
+  };
+
+  // Play with it...
+
+  for (size_t x = 0; x < string_count; ++x) {
+    for (size_t y = 0; y < string_count; ++y) {
+      // Test string_compareto x y
+      size_t result = Invoke3(reinterpret_cast<size_t>(s[x]->get()),
+                              reinterpret_cast<size_t>(s[y]->get()), 0U,
+                              reinterpret_cast<uintptr_t>(&art_quick_string_compareto), self);
+
+      EXPECT_FALSE(self->IsExceptionPending());
+
+      // The result is a 32b signed integer
+      union {
+        size_t r;
+        int32_t i;
+      } conv;
+      conv.r = result;
+      int32_t e = expected[x][y];
+      EXPECT_TRUE(e == 0 ? conv.i == 0 : true) << "x=" << c[x] << " y=" << c[y];
+      EXPECT_TRUE(e < 0 ? conv.i < 0 : true)   << "x=" << c[x] << " y="  << c[y];
+      EXPECT_TRUE(e > 0 ? conv.i > 0 : true)   << "x=" << c[x] << " y=" << c[y];
+    }
+  }
+
+  // Tests done.
+#else
+  LOG(INFO) << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA <<
+      std::endl;
+#endif
+}
+
 }  // namespace art
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 4fefd20..cac6cfd 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1169,5 +1169,47 @@
 UNIMPLEMENTED art_quick_deoptimize
 
 UNIMPLEMENTED art_quick_indexof
-UNIMPLEMENTED art_quick_string_compareto
+
+    /*
+     * String's compareTo.
+     *
+     * On entry:
+     *    rdi:   this string object (known non-null)
+     *    rsi:   comp string object (known non-null)
+     */
+DEFINE_FUNCTION art_quick_string_compareto
+    movl STRING_COUNT_OFFSET(%edi), %r8d
+    movl STRING_COUNT_OFFSET(%esi), %r9d
+    movl STRING_VALUE_OFFSET(%edi), %r10d
+    movl STRING_VALUE_OFFSET(%esi), %r11d
+    movl STRING_OFFSET_OFFSET(%edi), %eax
+    movl STRING_OFFSET_OFFSET(%esi), %ecx
+    /* Build pointers to the start of string data */
+    leal STRING_DATA_OFFSET(%r10d, %eax, 2), %esi
+    leal STRING_DATA_OFFSET(%r11d, %ecx, 2), %edi
+    /* Calculate min length and count diff */
+    movl  %r8d, %ecx
+    movl  %r8d, %eax
+    subl  %r9d, %eax
+    cmovg %r9d, %ecx
+    /*
+     * At this point we have:
+     *   eax: value to return if first part of strings are equal
+     *   ecx: minimum among the lengths of the two strings
+     *   esi: pointer to this string data
+     *   edi: pointer to comp string data
+     */
+    jecxz .Lkeep_length
+    repe cmpsw                    // find nonmatching chars in [%esi] and [%edi], up to length %ecx
+    jne .Lnot_equal
+.Lkeep_length:
+    ret
+    .balign 16
+.Lnot_equal:
+    movzwl  -2(%esi), %eax        // get last compared char from this string
+    movzwl  -2(%edi), %ecx        // get last compared char from comp string
+    subl  %ecx, %eax              // return the difference
+    ret
+END_FUNCTION art_quick_string_compareto
+
 UNIMPLEMENTED art_quick_memcmp16