java中stringCompareTo的用法如下:
- 如果两个string相等返回0
- 如果在lengthMin(s1, s2)的范围中,重叠的部分相等,则返回length的差
-
如果在比较的过程中发现有不相等,则返回char的差值,其中如果为十六进制的数,比较的范围为16位,具体情况如图
compareto
在intrinsic的实现中,函数调用到了kQuickStringCompareTo,该函数的申明在art/runtime/arch/riscv64/entrypoints_init_riscv64.cc中,具体的实现在同目录下的qucik_entrypoints_riscv64.S中,经过runtest测试过的riscv已经实现。
首先该函数时通过mips64移植过来的,在对于mips的理解上有一个比较重要的概念:如果涉及到跳转指令如beqz等,如果没有在跳转指令后面加上c,比如beqzc,那么一律默认都是延时跳转的。如:

mips中的延时跳转
如果a0=a1是成立的,那么会先进行move的指令,然后再进行跳转到 .Lstring_compareto_length_diff中,这是一个与riscv差距比较大的汇编指令,如果不熟悉的话很容易觉得它的代码写错了。但是如果在跳转指令后加上字母'c',在这种情况下是不需要延时跳转的。接下来我们看compareTo的实现,像其他的函数如Equal等其实思路差不多。
ENTRY art_quick_string_compareto
/* a0 holds address of "this" */
/* a1 holds address of "anotherString" */
mv a2, zero
mv a3, zero
beq a0, a1, .Lstring_compareto_length_diff # this and anotherString are the same object
# a0,a1为入参,分别代表要进行比较的this string 和another string,a2,a3寄存器专门用来存放记录this string 和another string的length长度,初始化为0。
# 此时将两个参数进行比较,如果地址相等,说明他两存放的是一个东西,没什么好比较的,跳转到比较长度,但是由于此时a2, a3都是0,即返回的是0
#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
sraiw a2, a4, 1 # this.length()
sraiw a3, a5, 1 # anotherString.length()
# 这里涉及到一个我不怎么懂,且网上还没什么资料的知识,还是通过不断测试发现的规律。
# 即string_compression的概念,什么叫做字符串压缩?我正常输入abc这种肯定不是压缩过的指令吧?实际上abc这种才是压缩的……
# 为什么呢?因为这种string系列最后也会转化成为机器码,最常见的就是ascii码,当然最常用的还是涵盖了更多的UTF-16
# 也就是说,这些在我们眼中看起来的字符串,其实就是压缩过的string,那么什么才是未压缩过的指令呢?
# 就是回到开头我写的例子中的s4, s5,已经通过十六进制来表示string
# 我不懂的就是这个MIRROR_STRING_COUNT_OFFSET函数时如何计算出一个带flag的值,如果0位为0就是被压缩过的
# 如果第0位为1,就是没有被压缩过的,我只是通过大量的实验证明了这个情况……
# 而且这个分支肯定会被走到,无论你输入的是压缩过的还是没有被压缩过的,因为if的的情况只是表明,你是否支持这个功能,显然我们的强大的art是支持的
# 计算出一个带flag的数之后给到一个寄存器中,分别是a4, a5,将他们的值右移一位就得到了他们的真实length
#else
lw a2, MIRROR_STRING_COUNT_OFFSET(a0) # this.length()
lw a3, MIRROR_STRING_COUNT_OFFSET(a1) # anotherString.length()
#endif
ble a2, a3, 1f
mv t2, a3
j 2f
1:
mv t2, a2
# t2 now holds min(this.length(),anotherString.length())
2:
beqz t2, .Lstring_compareto_length_diff # if t2==0
# return (this.length() - anotherString.length())
# t2寄存器保存的是两个string的最小值,为什么要计算这个最小值呢?因为compareto函数的功能是如果重叠部分相同,则返回length差。也就是说,我一个个比,比的最大长度是a2, a3中的最小值
#if (STRING_COMPRESSION_FEATURE)
# Differ cases:
andi a6, a4, 0x1
andi a7, a5, 0x1
beqz a6, .Lstring_compareto_this_is_compressed
beqz a7, .Lstring_compareto_that_is_compressed
j .Lstring_compareto_both_not_compressed
# 这里我用了一个投机取巧的方法,通过runtest测试证明是对的,但是我不知道这样做是不是严谨,因为在mips中的指令是:
# dext $a6, $a4, 0, 1
# 大概的意思是将第0位上的size位1的左移到最高位,然后再右移到第0位,那么按我的理解,不就是要第0位的值是不是1么?
# 那我直接与1相与不就行了,显然我也不知道mips这么大动干戈的目的是什么。而且我用riscv的方法尝试了一下还老报错……
# 意思很明显了,就是如果第0位如果是1,就是没有被压缩过的,但如果是0,就是被压缩过的,其中还分为一个压缩,另一个没有被压缩的情况
# 在不同分支中,比较的思路都是差不多的,唯一的区别是怎么比?
# 如果是被压缩的,那么一下子取得是‘lb’,即一个字节,是8位,但如果是没有被压缩过的,一下子取得是‘lh’,half Word,是16位。
# 取出来,比较,如果相等,那么将之前得到的minLength(s1, s2) - 1, 继续比,循环。
# dext $a6, $a4, 0, 1如果在比较的过程中发现有不一样的情况,则返回char的差值,但是如果t2等于0了,说明重叠部分相等,只要返回length的差值就可以了
.Lstring_compareto_this_is_compressed:
beqz a7, .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)
bne t0, t1, .Lstring_compareto_char_diff
addi a0, a0, 1 # point at this.charAt(i++) - compressed
addi a1, a1, 2 # point at anotherString.charAt(i++) - uncompressed
addi t2, t2, -1 # new value of min(this.length(),anotherString.length())-i
bnez t2, .Lstring_compareto_loop_comparison_this_compressed
subw a0, a2, a3 # return (this.length() - anotherString.length())
ret
.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
addi a0, a0, 2 # point at this.charAt(i++) - uncompressed
addi a1, a1, 1 # point at anotherString.charAt(i++) - compressed
addi t2, t2, -1 # new value of min(this.length(),anotherString.length())-i
bnez t2, .Lstring_compareto_that_is_compressed
subw a0, a2, a3 # return (this.length() - anotherString.length())
ret
.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
addi a0, a0, 1 # point at this.charAt(i++) - compressed
addi a1, a1, 1 # point at anotherString.charAt(i++) - compressed
addi t2, t2, -1 # new value of min(this.length(),anotherString.length())-i
bnez t2, .Lstring_compareto_both_compressed
subw a0, a2, a3 # return (this.length() - anotherString.length())
ret
#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
addi a0, a0, 2
addi a1, a1, 2
addi t2, t2, -1
bnez t2, .Lstring_compareto_both_not_compressed
.Lstring_compareto_length_diff:
subw a0, a2, a3 # return (this.length() - anotherString.length())
ret
.Lstring_compareto_char_diff:
subw a0, t0, t1 # return (this.charAt(i) - anotherString.charAt(i))
ret
END art_quick_string_compareto
