OSDN Git Service

String Compression for MIPS32 and MIPS64
authorGoran Jakovljevic <Goran.Jakovljevic@imgtec.com>
Fri, 10 Feb 2017 16:48:52 +0000 (17:48 +0100)
committerGoran Jakovljevic <Goran.Jakovljevic@imgtec.com>
Mon, 13 Feb 2017 09:05:26 +0000 (09:05 +0000)
Changes on intrinsics and Code Generation on MIPS32 and MIPS64 for
string compression feature.

Testing is done with STRING_COMPRESSION_ENABLED = true (in libcore),
mirror::kUseStringCompression = true and STRING_COMPRESSION_FEATURE set
to 1.

Test: booted MIPS32 and MIPS64 in QEMU
Test: mma test-art-target-run-test on CI20 (MIPS32R2)
Test: mma test-art-target-run-test in QEMU (MIPS64R6)

Change-Id: If50a6b6c0792bfa34d4fdff6bf2c7542211d2689

compiler/optimizing/code_generator_mips.cc
compiler/optimizing/code_generator_mips64.cc
compiler/optimizing/intrinsics_mips.cc
compiler/optimizing/intrinsics_mips64.cc
runtime/arch/mips/quick_entrypoints_mips.S
runtime/arch/mips64/quick_entrypoints_mips64.S

index 0677dad..c9dde7c 100644 (file)
@@ -1914,6 +1914,8 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
   auto null_checker = GetImplicitNullChecker(instruction);
 
   Primitive::Type type = instruction->GetType();
+  const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+                                        instruction->IsStringCharAt();
   switch (type) {
     case Primitive::kPrimBoolean: {
       Register out = locations->Out().AsRegister<Register>();
@@ -1957,14 +1959,54 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
 
     case Primitive::kPrimChar: {
       Register out = locations->Out().AsRegister<Register>();
+      if (maybe_compressed_char_at) {
+        uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+        __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker);
+        __ Sll(TMP, TMP, 31);    // Extract compression flag into the most significant bit of TMP.
+        static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+                      "Expecting 0=compressed, 1=uncompressed");
+      }
       if (index.IsConstant()) {
-        size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset, null_checker);
+        int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+        if (maybe_compressed_char_at) {
+          MipsLabel uncompressed_load, done;
+          __ Bnez(TMP, &uncompressed_load);
+          __ LoadFromOffset(kLoadUnsignedByte,
+                            out,
+                            obj,
+                            data_offset + (const_index << TIMES_1));
+          __ B(&done);
+          __ Bind(&uncompressed_load);
+          __ LoadFromOffset(kLoadUnsignedHalfword,
+                            out,
+                            obj,
+                            data_offset + (const_index << TIMES_2));
+          __ Bind(&done);
+        } else {
+          __ LoadFromOffset(kLoadUnsignedHalfword,
+                            out,
+                            obj,
+                            data_offset + (const_index << TIMES_2),
+                            null_checker);
+        }
       } else {
-        __ Sll(TMP, index.AsRegister<Register>(), TIMES_2);
-        __ Addu(TMP, obj, TMP);
-        __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
+        Register index_reg = index.AsRegister<Register>();
+        if (maybe_compressed_char_at) {
+          MipsLabel uncompressed_load, done;
+          __ Bnez(TMP, &uncompressed_load);
+          __ Addu(TMP, obj, index_reg);
+          __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
+          __ B(&done);
+          __ Bind(&uncompressed_load);
+          __ Sll(TMP, index_reg, TIMES_2);
+          __ Addu(TMP, obj, TMP);
+          __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+          __ Bind(&done);
+        } else {
+          __ Sll(TMP, index_reg, TIMES_2);
+          __ Addu(TMP, obj, TMP);
+          __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
+        }
       }
       break;
     }
@@ -2046,6 +2088,10 @@ void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) {
   Register out = locations->Out().AsRegister<Register>();
   __ LoadFromOffset(kLoadWord, out, obj, offset);
   codegen_->MaybeRecordImplicitNullCheck(instruction);
+  // Mask out compression flag from String's array length.
+  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+    __ Srl(out, out, 1u);
+  }
 }
 
 Location LocationsBuilderMIPS::RegisterOrZeroConstant(HInstruction* instruction) {
index 4c8dabf..5be0da4 100644 (file)
@@ -1490,6 +1490,8 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
 
   Primitive::Type type = instruction->GetType();
+  const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+                                        instruction->IsStringCharAt();
   switch (type) {
     case Primitive::kPrimBoolean: {
       GpuRegister out = locations->Out().AsRegister<GpuRegister>();
@@ -1533,14 +1535,54 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
 
     case Primitive::kPrimChar: {
       GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+      if (maybe_compressed_char_at) {
+        uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+        __ LoadFromOffset(kLoadWord, TMP, obj, count_offset);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ Dext(TMP, TMP, 0, 1);
+        static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+                      "Expecting 0=compressed, 1=uncompressed");
+      }
       if (index.IsConstant()) {
-        size_t offset =
-            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+        int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+        if (maybe_compressed_char_at) {
+          Mips64Label uncompressed_load, done;
+          __ Bnezc(TMP, &uncompressed_load);
+          __ LoadFromOffset(kLoadUnsignedByte,
+                            out,
+                            obj,
+                            data_offset + (const_index << TIMES_1));
+          __ Bc(&done);
+          __ Bind(&uncompressed_load);
+          __ LoadFromOffset(kLoadUnsignedHalfword,
+                            out,
+                            obj,
+                            data_offset + (const_index << TIMES_2));
+          __ Bind(&done);
+        } else {
+          __ LoadFromOffset(kLoadUnsignedHalfword,
+                            out,
+                            obj,
+                            data_offset + (const_index << TIMES_2));
+        }
       } else {
-        __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2);
-        __ Daddu(TMP, obj, TMP);
-        __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+        GpuRegister index_reg = index.AsRegister<GpuRegister>();
+        if (maybe_compressed_char_at) {
+          Mips64Label uncompressed_load, done;
+          __ Bnezc(TMP, &uncompressed_load);
+          __ Daddu(TMP, obj, index_reg);
+          __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
+          __ Bc(&done);
+          __ Bind(&uncompressed_load);
+          __ Dsll(TMP, index_reg, TIMES_2);
+          __ Daddu(TMP, obj, TMP);
+          __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+          __ Bind(&done);
+        } else {
+          __ Dsll(TMP, index_reg, TIMES_2);
+          __ Daddu(TMP, obj, TMP);
+          __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+        }
       }
       break;
     }
@@ -1608,7 +1650,9 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
       UNREACHABLE();
   }
-  codegen_->MaybeRecordImplicitNullCheck(instruction);
+  if (!maybe_compressed_char_at) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
 }
 
 void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) {
@@ -1624,6 +1668,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction)
   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
   __ LoadFromOffset(kLoadWord, out, obj, offset);
   codegen_->MaybeRecordImplicitNullCheck(instruction);
+  // Mask out compression flag from String's array length.
+  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+    __ Srl(out, out, 1u);
+  }
 }
 
 void LocationsBuilderMIPS64::VisitArraySet(HArraySet* instruction) {
index 6cf9b83..64a6840 100644 (file)
@@ -2004,31 +2004,48 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) {
   __ Lw(temp2, arg, class_offset);
   __ Bne(temp1, temp2, &return_false);
 
-  // Load lengths of this and argument strings.
+  // Load `count` fields of this and argument strings.
   __ Lw(temp1, str, count_offset);
   __ Lw(temp2, arg, count_offset);
-  // Check if lengths are equal, return false if they're not.
+  // Check if `count` fields are equal, return false if they're not.
+  // Also compares the compression style, if differs return false.
   __ Bne(temp1, temp2, &return_false);
-  // Return true if both strings are empty.
+  // 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");
   __ Beqz(temp1, &return_true);
 
   // Don't overwrite input registers
   __ Move(TMP, str);
   __ Move(temp3, arg);
 
-  // Assertions that must hold in order to compare strings 2 characters at a time.
+  // Assertions that must hold in order to compare strings 4 bytes at a time.
   DCHECK_ALIGNED(value_offset, 4);
   static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
 
-  // Loop to compare strings 2 characters at a time starting at the beginning of the string.
-  // Ok to do this because strings are zero-padded.
+  // For string compression, calculate the number of bytes to compare (not chars).
+  if (mirror::kUseStringCompression) {
+    // Extract compression flag.
+    if (IsR2OrNewer()) {
+      __ Ext(temp2, temp1, 0, 1);
+    } else {
+      __ Sll(temp2, temp1, 31);
+      __ Srl(temp2, temp2, 31);
+    }
+    __ Srl(temp1, temp1, 1);             // Extract length.
+    __ Sllv(temp1, temp1, temp2);        // Double the byte count if uncompressed.
+  }
+
+  // Loop to compare strings 4 bytes at a time starting at the beginning of the string.
+  // Ok to do this because strings are zero-padded to kObjectAlignment.
   __ Bind(&loop);
   __ Lw(out, TMP, value_offset);
   __ Lw(temp2, temp3, value_offset);
   __ Bne(out, temp2, &return_false);
   __ Addiu(TMP, TMP, 4);
   __ Addiu(temp3, temp3, 4);
-  __ Addiu(temp1, temp1, -2);
+  // With string compression, we have compared 4 bytes, otherwise 2 chars.
+  __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -4 : -2);
   __ Bgtz(temp1, &loop);
 
   // Return true and exit the function.
@@ -2578,6 +2595,30 @@ void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
     __ Addu(dstPtr, dstPtr, AT);
   }
 
+  if (mirror::kUseStringCompression) {
+    MipsLabel uncompressed_copy, compressed_loop;
+    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+    // Load count field and extract compression flag.
+    __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset);
+    __ Sll(TMP, TMP, 31);
+
+    // If string is uncompressed, use memcpy() path.
+    __ Bnez(TMP, &uncompressed_copy);
+
+    // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
+    __ Addu(srcPtr, srcObj, srcBegin);
+    __ Bind(&compressed_loop);
+    __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset);
+    __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0);
+    __ Addiu(numChrs, numChrs, -1);
+    __ Addiu(srcPtr, srcPtr, 1);
+    __ Addiu(dstPtr, dstPtr, 2);
+    __ Bnez(numChrs, &compressed_loop);
+
+    __ B(&done);
+    __ Bind(&uncompressed_copy);
+  }
+
   // Calculate source address.
   __ Addiu(srcPtr, srcObj, value_offset);
   if (IsR6()) {
index 00a1fa1..3888828 100644 (file)
@@ -1607,31 +1607,42 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringEquals(HInvoke* invoke) {
   __ Lw(temp2, arg, class_offset);
   __ Bnec(temp1, temp2, &return_false);
 
-  // Load lengths of this and argument strings.
+  // Load `count` fields of this and argument strings.
   __ Lw(temp1, str, count_offset);
   __ Lw(temp2, arg, count_offset);
-  // Check if lengths are equal, return false if they're not.
+  // Check if `count` fields are equal, return false if they're not.
+  // Also compares the compression style, if differs return false.
   __ Bnec(temp1, temp2, &return_false);
-  // Return true if both strings are empty.
+  // 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");
   __ Beqzc(temp1, &return_true);
 
   // Don't overwrite input registers
   __ Move(TMP, str);
   __ Move(temp3, arg);
 
-  // Assertions that must hold in order to compare strings 4 characters at a time.
+  // 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");
 
-  // Loop to compare strings 4 characters at a time starting at the beginning of the string.
-  // Ok to do this because strings are zero-padded to be 8-byte aligned.
+  if (mirror::kUseStringCompression) {
+    // For string compression, calculate the number of bytes to compare (not chars).
+    __ Dext(temp2, temp1, 0, 1);         // Extract compression flag.
+    __ Srl(temp1, temp1, 1);             // Extract length.
+    __ Sllv(temp1, temp1, temp2);        // Double the byte count if uncompressed.
+  }
+
+  // Loop to compare strings 8 bytes at a time starting at the beginning of the string.
+  // Ok to do this because strings are zero-padded to kObjectAlignment.
   __ Bind(&loop);
   __ Ld(out, TMP, value_offset);
   __ Ld(temp2, temp3, value_offset);
   __ Bnec(out, temp2, &return_false);
   __ Daddiu(TMP, TMP, 8);
   __ Daddiu(temp3, temp3, 8);
-  __ Addiu(temp1, temp1, -4);
+  // With string compression, we have compared 8 bytes, otherwise 4 chars.
+  __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -8 : -4);
   __ Bgtzc(temp1, &loop);
 
   // Return true and exit the function.
@@ -1912,6 +1923,30 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
   __ Daddiu(dstPtr, dstObj, data_offset);
   __ Dlsa(dstPtr, dstBegin, dstPtr, char_shift);
 
+  if (mirror::kUseStringCompression) {
+    Mips64Label uncompressed_copy, compressed_loop;
+    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+    // Load count field and extract compression flag.
+    __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset);
+    __ Dext(TMP, TMP, 0, 1);
+
+    // If string is uncompressed, use memcpy() path.
+    __ Bnezc(TMP, &uncompressed_copy);
+
+    // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
+    __ Daddu(srcPtr, srcObj, srcBegin);
+    __ Bind(&compressed_loop);
+    __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset);
+    __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0);
+    __ Daddiu(numChrs, numChrs, -1);
+    __ Daddiu(srcPtr, srcPtr, 1);
+    __ Daddiu(dstPtr, dstPtr, 2);
+    __ Bnezc(numChrs, &compressed_loop);
+
+    __ Bc(&done);
+    __ Bind(&uncompressed_copy);
+  }
+
   // Calculate source address.
   __ Daddiu(srcPtr, srcObj, value_offset);
   __ Dlsa(srcPtr, srcBegin, srcPtr, char_shift);
index 10b690a..ec8ae85 100644 (file)
@@ -2042,67 +2042,158 @@ ENTRY_NO_GP art_quick_indexof
 /* $a0 holds address of "this" */
 /* $a1 holds "ch" */
 /* $a2 holds "fromIndex" */
-  lw    $t0, MIRROR_STRING_COUNT_OFFSET($a0)    # this.length()
-  slt   $t1, $a2, $zero # if fromIndex < 0
+#if (STRING_COMPRESSION_FEATURE)
+    lw    $a3, MIRROR_STRING_COUNT_OFFSET($a0)    # 'count' field of this
+#else
+    lw    $t0, MIRROR_STRING_COUNT_OFFSET($a0)    # this.length()
+#endif
+    slt   $t1, $a2, $zero # if fromIndex < 0
 #if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6)
-  seleqz $a2, $a2, $t1  #     fromIndex = 0;
+    seleqz $a2, $a2, $t1  #     fromIndex = 0;
 #else
-  movn   $a2, $zero, $t1 #    fromIndex = 0;
+    movn   $a2, $zero, $t1 #    fromIndex = 0;
 #endif
-  subu  $t0, $t0, $a2   # this.length() - fromIndex
-  blez  $t0, 6f         # if this.length()-fromIndex <= 0
-  li    $v0, -1         #     return -1;
-
-  sll   $v0, $a2, 1     # $a0 += $a2 * 2
-  addu  $a0, $a0, $v0   #  "  ditto  "
-  move  $v0, $a2        # Set i to fromIndex.
+#if (STRING_COMPRESSION_FEATURE)
+    srl   $t0, $a3, 1     # $a3 holds count (with flag) and $t0 holds actual length
+#endif
+    subu  $t0, $t0, $a2   # this.length() - fromIndex
+    blez  $t0, 6f         # if this.length()-fromIndex <= 0
+    li    $v0, -1         #     return -1;
+
+#if (STRING_COMPRESSION_FEATURE)
+    sll   $a3, $a3, 31    # Extract compression flag.
+    beqz  $a3, .Lstring_indexof_compressed
+    move  $t2, $a0        # Save a copy in $t2 to later compute result (in branch delay slot).
+#endif
+    sll   $v0, $a2, 1     # $a0 += $a2 * 2
+    addu  $a0, $a0, $v0   #  "  ditto  "
+    move  $v0, $a2        # Set i to fromIndex.
 
 1:
-  lhu   $t3, MIRROR_STRING_VALUE_OFFSET($a0)    # if this.charAt(i) == ch
-  beq   $t3, $a1, 6f                            #     return i;
-  addu  $a0, $a0, 2     # i++
-  subu  $t0, $t0, 1     # this.length() - i
-  bnez  $t0, 1b         # while this.length() - i > 0
-  addu  $v0, $v0, 1     # i++
+    lhu   $t3, MIRROR_STRING_VALUE_OFFSET($a0)    # if this.charAt(i) == ch
+    beq   $t3, $a1, 6f                            #     return i;
+    addu  $a0, $a0, 2     # i++
+    subu  $t0, $t0, 1     # this.length() - i
+    bnez  $t0, 1b         # while this.length() - i > 0
+    addu  $v0, $v0, 1     # i++
 
-  li    $v0, -1         # if this.length() - i <= 0
-                        #     return -1;
+    li    $v0, -1         # if this.length() - i <= 0
+                          #     return -1;
 
 6:
-  j     $ra
-  nop
+    j     $ra
+    nop
+
+#if (STRING_COMPRESSION_FEATURE)
+.Lstring_indexof_compressed:
+    addu  $a0, $a0, $a2   # $a0 += $a2
+
+.Lstring_indexof_compressed_loop:
+    lbu   $t3, MIRROR_STRING_VALUE_OFFSET($a0)
+    beq   $t3, $a1, .Lstring_indexof_compressed_matched
+    subu  $t0, $t0, 1
+    bgtz  $t0, .Lstring_indexof_compressed_loop
+    addu  $a0, $a0, 1
+
+.Lstring_indexof_nomatch:
+    jalr  $zero, $ra
+    li    $v0, -1         # return -1;
+
+.Lstring_indexof_compressed_matched:
+    jalr  $zero, $ra
+    subu  $v0, $a0, $t2   # return (current - start);
+#endif
 END art_quick_indexof
 
 /* java.lang.String.compareTo(String anotherString) */
 ENTRY_NO_GP art_quick_string_compareto
 /* $a0 holds address of "this" */
 /* $a1 holds address of "anotherString" */
-  beq    $a0, $a1, 9f   # this and anotherString are the same object
-  move   $v0, $zero
+    beq    $a0, $a1, .Lstring_compareto_length_diff   # this and anotherString are the same object
+    move   $a3, $a2                                   # trick to return 0 (it returns a2 - a3)
+
+#if (STRING_COMPRESSION_FEATURE)
+    lw     $t0, MIRROR_STRING_COUNT_OFFSET($a0)   # 'count' field of this
+    lw     $t1, MIRROR_STRING_COUNT_OFFSET($a1)   # 'count' field of anotherString
+    sra    $a2, $t0, 1                            # this.length()
+    sra    $a3, $t1, 1                            # anotherString.length()
+#else
+    lw     $a2, MIRROR_STRING_COUNT_OFFSET($a0)   # this.length()
+    lw     $a3, MIRROR_STRING_COUNT_OFFSET($a1)   # anotherString.length()
+#endif
 
-  lw     $a2, MIRROR_STRING_COUNT_OFFSET($a0)   # this.length()
-  lw     $a3, MIRROR_STRING_COUNT_OFFSET($a1)   # anotherString.length()
-  MINu   $t2, $a2, $a3
-# $t2 now holds min(this.length(),anotherString.length())
+    MINu   $t2, $a2, $a3
+    # $t2 now holds min(this.length(),anotherString.length())
 
-  beqz   $t2, 9f        # while min(this.length(),anotherString.length())-i != 0
-  subu   $v0, $a2, $a3  # if $t2==0 return
-                        #     (this.length() - anotherString.length())
-1:
-  lhu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)   # while this.charAt(i) == anotherString.charAt(i)
-  lhu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
-  bne    $t0, $t1, 9f   # if this.charAt(i) != anotherString.charAt(i)
-  subu   $v0, $t0, $t1  #     return (this.charAt(i) - anotherString.charAt(i))
-  addiu  $a0, $a0, 2    # point at this.charAt(i++)
-  subu   $t2, $t2, 1    # new value of
-                        # min(this.length(),anotherString.length())-i
-  bnez   $t2, 1b
-  addiu  $a1, $a1, 2    # point at anotherString.charAt(i++)
-  subu   $v0, $a2, $a3
-
-9:
-  j      $ra
-  nop
+    # while min(this.length(),anotherString.length())-i != 0
+    beqz   $t2, .Lstring_compareto_length_diff # if $t2==0
+    nop                                        #     return (this.length() - anotherString.length())
+
+#if (STRING_COMPRESSION_FEATURE)
+    # Differ cases:
+    sll    $t3, $t0, 31
+    beqz   $t3, .Lstring_compareto_this_is_compressed
+    sll    $t3, $t1, 31                           # In branch delay slot.
+    beqz   $t3, .Lstring_compareto_that_is_compressed
+    nop
+    b      .Lstring_compareto_both_not_compressed
+    nop
+
+.Lstring_compareto_this_is_compressed:
+    beqz   $t3, .Lstring_compareto_both_compressed
+    nop
+    /* If (this->IsCompressed() && that->IsCompressed() == false) */
+.Lstring_compareto_loop_comparison_this_compressed:
+    lbu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+    lhu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bne    $t0, $t1, .Lstring_compareto_char_diff
+    addiu  $a0, $a0, 1    # point at this.charAt(i++) - compressed
+    subu   $t2, $t2, 1    # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_loop_comparison_this_compressed
+    addiu  $a1, $a1, 2    # point at anotherString.charAt(i++) - uncompressed
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3  # return (this.length() - anotherString.length())
+
+.Lstring_compareto_that_is_compressed:
+    lhu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+    lbu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bne    $t0, $t1, .Lstring_compareto_char_diff
+    addiu  $a0, $a0, 2    # point at this.charAt(i++) - uncompressed
+    subu   $t2, $t2, 1    # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_that_is_compressed
+    addiu  $a1, $a1, 1    # point at anotherString.charAt(i++) - compressed
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3  # return (this.length() - anotherString.length())
+
+.Lstring_compareto_both_compressed:
+    lbu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+    lbu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bne    $t0, $t1, .Lstring_compareto_char_diff
+    addiu  $a0, $a0, 1    # point at this.charAt(i++) - compressed
+    subu   $t2, $t2, 1    # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_both_compressed
+    addiu  $a1, $a1, 1    # point at anotherString.charAt(i++) - compressed
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3  # return (this.length() - anotherString.length())
+#endif
+
+.Lstring_compareto_both_not_compressed:
+    lhu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)   # while this.charAt(i) == anotherString.charAt(i)
+    lhu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bne    $t0, $t1, .Lstring_compareto_char_diff # if this.charAt(i) != anotherString.charAt(i)
+                          #     return (this.charAt(i) - anotherString.charAt(i))
+    addiu  $a0, $a0, 2    # point at this.charAt(i++)
+    subu   $t2, $t2, 1    # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_both_not_compressed
+    addiu  $a1, $a1, 2    # point at anotherString.charAt(i++)
+
+.Lstring_compareto_length_diff:
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3  # return (this.length() - anotherString.length())
+
+.Lstring_compareto_char_diff:
+    jalr   $zero, $ra
+    subu   $v0, $t0, $t1  # return (this.charAt(i) - anotherString.charAt(i))
 END art_quick_string_compareto
 
 .extern artInvokePolymorphic
index fff6d25..28d7c77 100644 (file)
@@ -1900,32 +1900,91 @@ END art_quick_deoptimize_from_compiled_code
 ENTRY_NO_GP art_quick_string_compareto
 /* $a0 holds address of "this" */
 /* $a1 holds address of "anotherString" */
-  beq    $a0,$a1,9f     # this and anotherString are the same object
-  move   $v0,$zero
+    move   $a2, $zero
+    beq    $a0, $a1, .Lstring_compareto_length_diff # this and anotherString are the same object
+    move   $a3, $zero                               # return 0 (it returns a2 - a3)
+
+#if (STRING_COMPRESSION_FEATURE)
+    lw     $a4, MIRROR_STRING_COUNT_OFFSET($a0)     # 'count' field of this
+    lw     $a5, MIRROR_STRING_COUNT_OFFSET($a1)     # 'count' field of anotherString
+    sra    $a2, $a4, 1                              # this.length()
+    sra    $a3, $a5, 1                              # anotherString.length()
+#else
+    lw     $a2, MIRROR_STRING_COUNT_OFFSET($a0)     # this.length()
+    lw     $a3, MIRROR_STRING_COUNT_OFFSET($a1)     # anotherString.length()
+#endif
 
-  lw     $a2,MIRROR_STRING_COUNT_OFFSET($a0)    # this.length()
-  lw     $a3,MIRROR_STRING_COUNT_OFFSET($a1)    # anotherString.length()
-  MINu   $t2, $a2, $a3
-# $t2 now holds min(this.length(),anotherString.length())
+    MINu   $t2, $a2, $a3
+    # $t2 now holds min(this.length(),anotherString.length())
 
-  beqz   $t2,9f         # while min(this.length(),anotherString.length())-i != 0
-  subu   $v0,$a2,$a3    # if $t2==0 return
-                        #     (this.length() - anotherString.length())
-1:
-  lhu    $t0,MIRROR_STRING_VALUE_OFFSET($a0)    # while this.charAt(i) == anotherString.charAt(i)
-  lhu    $t1,MIRROR_STRING_VALUE_OFFSET($a1)
-  bne    $t0,$t1,9f     # if this.charAt(i) != anotherString.charAt(i)
-  subu   $v0,$t0,$t1    #     return (this.charAt(i) - anotherString.charAt(i))
-  daddiu $a0,$a0,2      # point at this.charAt(i++)
-  subu   $t2,$t2,1      # new value of
-                        # min(this.length(),anotherString.length())-i
-  bnez   $t2,1b
-  daddiu $a1,$a1,2      # point at anotherString.charAt(i++)
-  subu   $v0,$a2,$a3
-
-9:
-  j      $ra
-  nop
+    # while min(this.length(),anotherString.length())-i != 0
+    beqzc  $t2, .Lstring_compareto_length_diff # if $t2==0
+                                               #     return (this.length() - anotherString.length())
+
+#if (STRING_COMPRESSION_FEATURE)
+    # Differ cases:
+    dext   $a6, $a4, 0, 1
+    beqz   $a6, .Lstring_compareto_this_is_compressed
+    dext   $a6, $a5, 0, 1                      # In branch delay slot.
+    beqz   $a6, .Lstring_compareto_that_is_compressed
+    nop
+    b      .Lstring_compareto_both_not_compressed
+    nop
+
+.Lstring_compareto_this_is_compressed:
+    beqzc  $a6, .Lstring_compareto_both_compressed
+    /* If (this->IsCompressed() && that->IsCompressed() == false) */
+.Lstring_compareto_loop_comparison_this_compressed:
+    lbu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+    lhu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bnec   $t0, $t1, .Lstring_compareto_char_diff
+    daddiu $a0, $a0, 1      # point at this.charAt(i++) - compressed
+    subu   $t2, $t2, 1      # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_loop_comparison_this_compressed
+    daddiu $a1, $a1, 2      # point at anotherString.charAt(i++) - uncompressed
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3    # return (this.length() - anotherString.length())
+
+.Lstring_compareto_that_is_compressed:
+    lhu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+    lbu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bnec   $t0, $t1, .Lstring_compareto_char_diff
+    daddiu $a0, $a0, 2      # point at this.charAt(i++) - uncompressed
+    subu   $t2, $t2, 1      # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_that_is_compressed
+    daddiu $a1, $a1, 1      # point at anotherString.charAt(i++) - compressed
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3    # return (this.length() - anotherString.length())
+
+.Lstring_compareto_both_compressed:
+    lbu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+    lbu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bnec   $t0, $t1, .Lstring_compareto_char_diff
+    daddiu $a0, $a0, 1      # point at this.charAt(i++) - compressed
+    subu   $t2, $t2, 1      # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_both_compressed
+    daddiu $a1, $a1, 1      # point at anotherString.charAt(i++) - compressed
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3    # return (this.length() - anotherString.length())
+#endif
+
+.Lstring_compareto_both_not_compressed:
+    lhu    $t0, MIRROR_STRING_VALUE_OFFSET($a0)    # while this.charAt(i) == anotherString.charAt(i)
+    lhu    $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+    bnec   $t0, $t1, .Lstring_compareto_char_diff  # if this.charAt(i) != anotherString.charAt(i)
+                            #     return (this.charAt(i) - anotherString.charAt(i))
+    daddiu $a0, $a0, 2      # point at this.charAt(i++)
+    subu   $t2, $t2, 1      # new value of min(this.length(),anotherString.length())-i
+    bnez   $t2, .Lstring_compareto_both_not_compressed
+    daddiu $a1, $a1, 2      # point at anotherString.charAt(i++)
+
+.Lstring_compareto_length_diff:
+    jalr   $zero, $ra
+    subu   $v0, $a2, $a3    # return (this.length() - anotherString.length())
+
+.Lstring_compareto_char_diff:
+    jalr   $zero, $ra
+    subu   $v0, $t0, $t1    # return (this.charAt(i) - anotherString.charAt(i))
 END art_quick_string_compareto
 
 /* java.lang.String.indexOf(int ch, int fromIndex=0) */
@@ -1933,31 +1992,64 @@ ENTRY_NO_GP art_quick_indexof
 /* $a0 holds address of "this" */
 /* $a1 holds "ch" */
 /* $a2 holds "fromIndex" */
-  lw    $t0,MIRROR_STRING_COUNT_OFFSET($a0)     # this.length()
-  slt   $at, $a2, $zero # if fromIndex < 0
-  seleqz $a2, $a2, $at  #     fromIndex = 0;
-  subu  $t0,$t0,$a2     # this.length() - fromIndex
-  blez  $t0,6f          # if this.length()-fromIndex <= 0
-  li    $v0,-1          #     return -1;
+#if (STRING_COMPRESSION_FEATURE)
+    lw     $a3, MIRROR_STRING_COUNT_OFFSET($a0)     # 'count' field of this
+#else
+    lw     $t0, MIRROR_STRING_COUNT_OFFSET($a0)     # this.length()
+#endif
+    slt    $at, $a2, $zero  # if fromIndex < 0
+    seleqz $a2, $a2, $at    #     fromIndex = 0;
+#if (STRING_COMPRESSION_FEATURE)
+    srl   $t0, $a3, 1       # $a3 holds count (with flag) and $t0 holds actual length
+#endif
+    subu   $t0, $t0, $a2    # this.length() - fromIndex
+    blez   $t0, 6f          # if this.length()-fromIndex <= 0
+    li     $v0, -1          #     return -1;
 
-  sll   $v0,$a2,1       # $a0 += $a2 * 2
-  daddu $a0,$a0,$v0     #  "  ditto  "
-  move  $v0,$a2         # Set i to fromIndex.
+#if (STRING_COMPRESSION_FEATURE)
+    dext   $a3, $a3, 0, 1   # Extract compression flag.
+    beqzc  $a3, .Lstring_indexof_compressed
+#endif
+
+    sll    $v0, $a2, 1      # $a0 += $a2 * 2
+    daddu  $a0, $a0, $v0    #  "  ditto  "
+    move   $v0, $a2         # Set i to fromIndex.
 
 1:
-  lhu   $t3,MIRROR_STRING_VALUE_OFFSET($a0)     # if this.charAt(i) == ch
-  beq   $t3,$a1,6f                              #     return i;
-  daddu $a0,$a0,2       # i++
-  subu  $t0,$t0,1       # this.length() - i
-  bnez  $t0,1b          # while this.length() - i > 0
-  addu  $v0,$v0,1       # i++
+    lhu    $t3, MIRROR_STRING_VALUE_OFFSET($a0)     # if this.charAt(i) == ch
+    beq    $t3, $a1, 6f                             #     return i;
+    daddu  $a0, $a0, 2      # i++
+    subu   $t0, $t0, 1      # this.length() - i
+    bnez   $t0, 1b          # while this.length() - i > 0
+    addu   $v0, $v0, 1      # i++
 
-  li    $v0,-1          # if this.length() - i <= 0
-                        #     return -1;
+    li     $v0, -1          # if this.length() - i <= 0
+                            #     return -1;
 
 6:
-  j     $ra
-  nop
+    j      $ra
+    nop
+
+#if (STRING_COMPRESSION_FEATURE)
+.Lstring_indexof_compressed:
+    move   $a4, $a0         # Save a copy in $a4 to later compute result.
+    daddu  $a0, $a0, $a2    # $a0 += $a2
+
+.Lstring_indexof_compressed_loop:
+    lbu    $t3, MIRROR_STRING_VALUE_OFFSET($a0)
+    beq    $t3, $a1, .Lstring_indexof_compressed_matched
+    subu   $t0, $t0, 1
+    bgtz   $t0, .Lstring_indexof_compressed_loop
+    daddu  $a0, $a0, 1
+
+.Lstring_indexof_nomatch:
+    jalr   $zero, $ra
+    li     $v0, -1          # return -1;
+
+.Lstring_indexof_compressed_matched:
+    jalr   $zero, $ra
+    dsubu  $v0, $a0, $a4    # return (current - start);
+#endif
 END art_quick_indexof
 
 .extern artInvokePolymorphic