ARM64: Improve String.equals() intrinsic for const strings.

And add additional tests to 021-string2.

aosp_angler-userdebug:
  before:
    arm64 boot*.oat: 43324664
    arm64 boot*.oat/string compression: 43411112
  after:
    arm64 boot*.oat: 43300136 (-24528)
    arm64 boot*.oat/string compression: 43345464 (-65648)

The string compression code size difference drops from
86448 to 45328.

Test: m test-art-target on Nexus 6P
Test: m test-art-target on Nexus 6P with string compression enabled.
Bug: 31040547
Change-Id: I99a3777b91b248da2b0ac25abd260f9e5abb2c09
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index bbf826c..b107280 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -23,7 +23,7 @@
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "intrinsics.h"
 #include "mirror/array-inl.h"
-#include "mirror/string.h"
+#include "mirror/string-inl.h"
 #include "thread.h"
 #include "utils/arm64/assembler_arm64.h"
 
@@ -1444,16 +1444,47 @@
   }
 }
 
+// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
+// The normal loop plus the pre-header is 9 instructions without string compression and 12
+// instructions with string compression. We can compare up to 8 bytes in 4 instructions
+// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
+// to 10 instructions for the unrolled loop.
+constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
+
+static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
+  if (candidate->IsLoadString()) {
+    HLoadString* load_string = candidate->AsLoadString();
+    const DexFile& dex_file = load_string->GetDexFile();
+    return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
+  }
+  return nullptr;
+}
+
 void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
                                                             LocationSummary::kNoCall,
                                                             kIntrinsified);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  // Temporary registers to store lengths of strings and for calculations.
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
 
+  // For the generic implementation and for long const strings we need a temporary.
+  // We do not need it for short const strings, up to 8 bytes, see code generation below.
+  uint32_t const_string_length = 0u;
+  const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+  if (const_string == nullptr) {
+    const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+  }
+  bool is_compressed =
+      mirror::kUseStringCompression &&
+      const_string != nullptr &&
+      mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+  if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
+    locations->AddTemp(Location::RequiresRegister());
+  }
+
+  // TODO: If the String.equals() is used only for an immediately following HIf, we can
+  // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
+  // Then we shall need an extra temporary register instead of the output register.
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
 }
 
@@ -1467,8 +1498,7 @@
 
   UseScratchRegisterScope scratch_scope(masm);
   Register temp = scratch_scope.AcquireW();
-  Register temp1 = WRegisterFrom(locations->GetTemp(0));
-  Register temp2 = WRegisterFrom(locations->GetTemp(1));
+  Register temp1 = scratch_scope.AcquireW();
 
   vixl::aarch64::Label loop;
   vixl::aarch64::Label end;
@@ -1504,47 +1534,99 @@
     __ B(&return_false, ne);
   }
 
-  // Load `count` fields of this and argument strings.
-  __ Ldr(temp, MemOperand(str.X(), count_offset));
-  __ Ldr(temp1, MemOperand(arg.X(), count_offset));
-  // Check if `count` fields are equal, return false if they're not.
-  // Also compares the compression style, if differs return false.
-  __ Cmp(temp, temp1);
-  __ B(&return_false, ne);
-  // Return true if both strings are empty. Even with string compression `count == 0` means empty.
-  static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
-                "Expecting 0=compressed, 1=uncompressed");
-  __ Cbz(temp, &return_true);
+  // Check if one of the inputs is a const string. Do not special-case both strings
+  // being const, such cases should be handled by constant folding if needed.
+  uint32_t const_string_length = 0u;
+  const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+  if (const_string == nullptr) {
+    const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+    if (const_string != nullptr) {
+      std::swap(str, arg);  // Make sure the const string is in `str`.
+    }
+  }
+  bool is_compressed =
+      mirror::kUseStringCompression &&
+      const_string != nullptr &&
+      mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+
+  if (const_string != nullptr) {
+    // Load `count` field of the argument string and check if it matches the const string.
+    // Also compares the compression style, if differs return false.
+    __ Ldr(temp, MemOperand(arg.X(), count_offset));
+    __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+    __ B(&return_false, ne);
+  } else {
+    // Load `count` fields of this and argument strings.
+    __ Ldr(temp, MemOperand(str.X(), count_offset));
+    __ Ldr(temp1, MemOperand(arg.X(), count_offset));
+    // Check if `count` fields are equal, return false if they're not.
+    // Also compares the compression style, if differs return false.
+    __ Cmp(temp, temp1);
+    __ B(&return_false, ne);
+  }
 
   // Assertions that must hold in order to compare strings 8 bytes at a time.
   DCHECK_ALIGNED(value_offset, 8);
   static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
 
-  if (mirror::kUseStringCompression) {
-    // For string compression, calculate the number of bytes to compare (not chars).
-    // This could in theory exceed INT32_MAX, so treat temp as unsigned.
-    __ Lsr(temp, temp, 1u);             // Extract length.
-    __ And(temp1, temp1, Operand(1));   // Extract compression flag.
-    __ Lsl(temp, temp, temp1);          // Calculate number of bytes to compare.
+  if (const_string != nullptr &&
+      const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
+                                           : kShortConstStringEqualsCutoffInBytes / 2u)) {
+    // Load and compare the contents. Though we know the contents of the short const string
+    // at compile time, materializing constants may be more code than loading from memory.
+    int32_t offset = value_offset;
+    size_t remaining_bytes =
+        RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
+    temp = temp.X();
+    temp1 = temp1.X();
+    while (remaining_bytes > 8u) {
+      Register temp2 = XRegisterFrom(locations->GetTemp(0));
+      __ Ldp(temp, temp1, MemOperand(str.X(), offset));
+      __ Ldp(temp2, out, MemOperand(arg.X(), offset));
+      __ Cmp(temp, temp2);
+      __ Ccmp(temp1, out, NoFlag, eq);
+      __ B(&return_false, ne);
+      offset += 2u * sizeof(uint64_t);
+      remaining_bytes -= 2u * sizeof(uint64_t);
+    }
+    if (remaining_bytes != 0u) {
+      __ Ldr(temp, MemOperand(str.X(), offset));
+      __ Ldr(temp1, MemOperand(arg.X(), offset));
+      __ Cmp(temp, temp1);
+      __ B(&return_false, ne);
+    }
+  } else {
+    // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+    static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+                  "Expecting 0=compressed, 1=uncompressed");
+    __ Cbz(temp, &return_true);
+
+    if (mirror::kUseStringCompression) {
+      // For string compression, calculate the number of bytes to compare (not chars).
+      // This could in theory exceed INT32_MAX, so treat temp as unsigned.
+      __ And(temp1, temp, Operand(1));    // Extract compression flag.
+      __ Lsr(temp, temp, 1u);             // Extract length.
+      __ Lsl(temp, temp, temp1);          // Calculate number of bytes to compare.
+    }
+
+    // Store offset of string value in preparation for comparison loop
+    __ Mov(temp1, value_offset);
+
+    temp1 = temp1.X();
+    Register temp2 = XRegisterFrom(locations->GetTemp(0));
+    // Loop to compare strings 8 bytes at a time starting at the front of the string.
+    // Ok to do this because strings are zero-padded to kObjectAlignment.
+    __ Bind(&loop);
+    __ Ldr(out, MemOperand(str.X(), temp1));
+    __ Ldr(temp2, MemOperand(arg.X(), temp1));
+    __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
+    __ Cmp(out, temp2);
+    __ B(&return_false, ne);
+    // With string compression, we have compared 8 bytes, otherwise 4 chars.
+    __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
+    __ B(&loop, hi);
   }
 
-  // Store offset of string value in preparation for comparison loop
-  __ Mov(temp1, value_offset);
-
-  temp1 = temp1.X();
-  temp2 = temp2.X();
-  // Loop to compare strings 8 bytes at a time starting at the front of the string.
-  // Ok to do this because strings are zero-padded to kObjectAlignment.
-  __ Bind(&loop);
-  __ Ldr(out, MemOperand(str.X(), temp1));
-  __ Ldr(temp2, MemOperand(arg.X(), temp1));
-  __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
-  __ Cmp(out, temp2);
-  __ B(&return_false, ne);
-  // With string compression, we have compared 8 bytes, otherwise 4 chars.
-  __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
-  __ B(&loop, hi);
-
   // Return true and exit the function.
   // If loop does not result in returning false, we return true.
   __ Bind(&return_true);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 9b8445d..c2407d7 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -308,7 +308,7 @@
 }
 
 template<typename MemoryType>
-bool String::AllASCII(const MemoryType* const chars, const int length) {
+inline bool String::AllASCII(const MemoryType* chars, const int length) {
   static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType");
   for (int i = 0; i < length; ++i) {
     // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII
@@ -320,6 +320,13 @@
   return true;
 }
 
+inline bool String::DexFileStringAllASCII(const char* chars, const int length) {
+  // For strings from the dex file we just need to check that
+  // the terminating character is at the right position.
+  DCHECK_EQ(AllASCII(reinterpret_cast<const uint8_t*>(chars), length), chars[length] == 0);
+  return chars[length] == 0;
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 409c6c2..38f6dd4 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -184,7 +184,9 @@
   bool IsValueNull() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<typename MemoryType>
-  static bool AllASCII(const MemoryType* const chars, const int length);
+  static bool AllASCII(const MemoryType* chars, const int length);
+
+  static bool DexFileStringAllASCII(const char* chars, const int length);
 
   ALWAYS_INLINE static bool IsCompressed(int32_t count) {
     return GetCompressionFlagFromCount(count) == StringCompressionFlag::kCompressed;
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index df0a3dd..5a43a4f 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -117,6 +117,9 @@
             " " + $noinline$equals(s0_3, s0_1) +
             " " + $noinline$equals(s0_3, s0_2) +
             " " + $noinline$equals(s0_3, s0_3));
+
+        testEqualsConstString();
+        testConstStringEquals();
     }
 
     public static void testCompareToAndEquals() {
@@ -539,6 +542,266 @@
         }
     }
 
+    public static void testEqualsConstString() {
+        Assert.assertTrue($noinline$equalsConstString0(""));
+        Assert.assertFalse($noinline$equalsConstString0("1"));
+
+        Assert.assertTrue($noinline$equalsConstString7("0123456"));
+        Assert.assertFalse($noinline$equalsConstString7("012345"));
+        Assert.assertFalse($noinline$equalsConstString7("01234567"));
+        Assert.assertFalse($noinline$equalsConstString7("012345x"));
+        Assert.assertFalse($noinline$equalsConstString7("012345\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString14("01234567890123"));
+        Assert.assertFalse($noinline$equalsConstString14("0123456789012"));
+        Assert.assertFalse($noinline$equalsConstString14("012345678901234"));
+        Assert.assertFalse($noinline$equalsConstString14("0123456789012x"));
+        Assert.assertFalse($noinline$equalsConstString14("0123456789012\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString24("012345678901234567890123"));
+        Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012"));
+        Assert.assertFalse($noinline$equalsConstString24("0123456789012345678901234"));
+        Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012x"));
+        Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString29("01234567890123456789012345678"));
+        Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567"));
+        Assert.assertFalse($noinline$equalsConstString29("012345678901234567890123456789"));
+        Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567x"));
+        Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstString35("01234567890123456789012345678901234"));
+        Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123"));
+        Assert.assertFalse($noinline$equalsConstString35("012345678901234567890123456789012345"));
+        Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$equalsConstString35("0123456789012345678901234567890123\u0440"));
+
+        Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456"));
+
+        Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString14("01234567890123"));
+
+        Assert.assertTrue($noinline$equalsConstNonAsciiString24("\u044012345678901234567890123"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u0440123456789012345678901234"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString24("\012345678901234567890123"));
+
+        Assert.assertTrue(
+            $noinline$equalsConstNonAsciiString29("\u04401234567890123456789012345678"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString29("\u044012345678901234567890123456789"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString29("01234567890123456789012345678"));
+
+        Assert.assertTrue(
+            $noinline$equalsConstNonAsciiString35("\u04401234567890123456789012345678901234"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("\u044012345678901234567890123456789012345"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$equalsConstNonAsciiString35("01234567890123456789012345678901234"));
+    }
+
+    public static void testConstStringEquals() {
+        Assert.assertTrue($noinline$constString0Equals(""));
+        Assert.assertFalse($noinline$constString0Equals("1"));
+
+        Assert.assertTrue($noinline$constString7Equals("0123456"));
+        Assert.assertFalse($noinline$constString7Equals("012345"));
+        Assert.assertFalse($noinline$constString7Equals("01234567"));
+        Assert.assertFalse($noinline$constString7Equals("012345x"));
+        Assert.assertFalse($noinline$constString7Equals("012345\u0440"));
+
+        Assert.assertTrue($noinline$constString14Equals("01234567890123"));
+        Assert.assertFalse($noinline$constString14Equals("0123456789012"));
+        Assert.assertFalse($noinline$constString14Equals("012345678901234"));
+        Assert.assertFalse($noinline$constString14Equals("0123456789012x"));
+        Assert.assertFalse($noinline$constString14Equals("0123456789012\u0440"));
+
+        Assert.assertTrue($noinline$constString24Equals("012345678901234567890123"));
+        Assert.assertFalse($noinline$constString24Equals("01234567890123456789012"));
+        Assert.assertFalse($noinline$constString24Equals("0123456789012345678901234"));
+        Assert.assertFalse($noinline$constString24Equals("01234567890123456789012x"));
+        Assert.assertFalse($noinline$constString24Equals("01234567890123456789012\u0440"));
+
+        Assert.assertTrue($noinline$constString29Equals("01234567890123456789012345678"));
+        Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567"));
+        Assert.assertFalse($noinline$constString29Equals("012345678901234567890123456789"));
+        Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567x"));
+        Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567\u0440"));
+
+        Assert.assertTrue($noinline$constString35Equals("01234567890123456789012345678901234"));
+        Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123"));
+        Assert.assertFalse($noinline$constString35Equals("012345678901234567890123456789012345"));
+        Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$constString35Equals("0123456789012345678901234567890123\u0040"));
+
+        Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x"));
+        Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456"));
+
+        Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012x"));
+        Assert.assertFalse($noinline$constNonAsciiString14Equals("01234567890123"));
+
+        Assert.assertTrue($noinline$constNonAsciiString24Equals("\u044012345678901234567890123"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\u0440123456789012345678901234"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012x"));
+        Assert.assertFalse($noinline$constNonAsciiString24Equals("\012345678901234567890123"));
+
+        Assert.assertTrue(
+            $noinline$constNonAsciiString29Equals("\u04401234567890123456789012345678"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString29Equals("\u044012345678901234567890123456789"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567x"));
+        Assert.assertFalse($noinline$constNonAsciiString29Equals("01234567890123456789012345678"));
+
+        Assert.assertTrue(
+            $noinline$constNonAsciiString35Equals("\u04401234567890123456789012345678901234"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("\u044012345678901234567890123456789012345"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x"));
+        Assert.assertFalse(
+            $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234"));
+    }
+
+    public static boolean $noinline$equalsConstString0(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("");
+    }
+
+    public static boolean $noinline$equalsConstString7(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("0123456");
+    }
+
+    public static boolean $noinline$equalsConstString14(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("01234567890123");
+    }
+
+    public static boolean $noinline$equalsConstString24(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("012345678901234567890123");
+    }
+
+    public static boolean $noinline$equalsConstString29(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("01234567890123456789012345678");
+    }
+
+    public static boolean $noinline$equalsConstString35(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("01234567890123456789012345678901234");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString7(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u0440123456");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString14(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u04401234567890123");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString24(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u044012345678901234567890123");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString29(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u04401234567890123456789012345678");
+    }
+
+    public static boolean $noinline$equalsConstNonAsciiString35(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("\u04401234567890123456789012345678901234");
+    }
+
+    public static boolean $noinline$constString0Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return s.equals("");
+    }
+
+    public static boolean $noinline$constString7Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "0123456".equals(s);
+    }
+
+    public static boolean $noinline$constString14Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "01234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constString24Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "012345678901234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constString29Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "01234567890123456789012345678".equals(s);
+    }
+
+    public static boolean $noinline$constString35Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "01234567890123456789012345678901234".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString7Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u0440123456".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString14Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u04401234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString24Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u044012345678901234567890123".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString29Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u04401234567890123456789012345678".equals(s);
+    }
+
+    public static boolean $noinline$constNonAsciiString35Equals(String s) {
+        if (doThrow) { throw new Error(); }
+        return "\u04401234567890123456789012345678901234".equals(s);
+    }
+
     public static int $noinline$compareTo(String lhs, String rhs) {
         if (doThrow) { throw new Error(); }
         return lhs.compareTo(rhs);
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index ed7524c..52f3f84 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -329,7 +329,7 @@
   /// CHECK-NOT:      cbz
   // Terminate the scope for the CHECK-NOT search at the reference or length comparison,
   // whichever comes first.
-  /// CHECK:          cmp {{w.*,}} {{w.*}}
+  /// CHECK:          cmp {{w.*,}} {{w.*|#.*}}
   public static boolean stringArgumentNotNull(Object obj) {
     obj.getClass();
     return "foo".equals(obj);
@@ -380,10 +380,10 @@
   // so repeat the check twice.
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
-  /// CHECK:          cmp {{w\d+}}, {{w\d+}}
+  /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
-  /// CHECK:          cmp {{w\d+}}, {{w\d+}}
+  /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
   public static boolean stringArgumentIsString() {
     return "foo".equals(myString);
   }