String Compression for MIPS32 and MIPS64
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
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 0677dad..c9dde7c 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1914,6 +1914,8 @@
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 @@
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 @@
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) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 4c8dabf..5be0da4 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1490,6 +1490,8 @@
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 @@
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 @@
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 @@
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) {
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 6cf9b83..64a6840 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2004,31 +2004,48 @@
__ 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 @@
__ 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()) {
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 00a1fa1..3888828 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1607,31 +1607,42 @@
__ 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 @@
__ 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);
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 10b690a..ec8ae85 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2042,67 +2042,158 @@
/* $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 defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6)
- seleqz $a2, $a2, $t1 # fromIndex = 0;
+#if (STRING_COMPRESSION_FEATURE)
+ lw $a3, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this
#else
- movn $a2, $zero, $t1 # fromIndex = 0;
+ lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
#endif
- subu $t0, $t0, $a2 # this.length() - fromIndex
- blez $t0, 6f # if this.length()-fromIndex <= 0
- li $v0, -1 # return -1;
+ slt $t1, $a2, $zero # if fromIndex < 0
+#if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6)
+ seleqz $a2, $a2, $t1 # fromIndex = 0;
+#else
+ movn $a2, $zero, $t1 # fromIndex = 0;
+#endif
+#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
- addu $a0, $a0, $v0 # " ditto "
- move $v0, $a2 # Set i to fromIndex.
+#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)
- 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())
+#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
- 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
+ MINu $t2, $a2, $a3
+ # $t2 now holds min(this.length(),anotherString.length())
-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
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index fff6d25..28d7c77 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1900,32 +1900,91 @@
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)
- 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())
+#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
- 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
+ MINu $t2, $a2, $a3
+ # $t2 now holds min(this.length(),anotherString.length())
-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 @@
/* $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