aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraph <none@none>2014-06-27 11:25:47 +0100
committeraph <none@none>2014-06-27 11:25:47 +0100
commitebe6abc9df2b34a3eee8e3d4d350f287a2504e3b (patch)
tree3f11295691d41dd5ae253aa827473cd8963a36f3
parent42b911a8cd82864eefc141b2163ad81feaf2dd76 (diff)
Fast string comparison
-rw-r--r--src/cpu/aarch64/vm/aarch64.ad83
-rw-r--r--src/cpu/aarch64/vm/macroAssembler_aarch64.cpp83
-rw-r--r--src/cpu/aarch64/vm/macroAssembler_aarch64.hpp4
3 files changed, 169 insertions, 1 deletions
diff --git a/src/cpu/aarch64/vm/aarch64.ad b/src/cpu/aarch64/vm/aarch64.ad
index 22563e570..fb1c6a00d 100644
--- a/src/cpu/aarch64/vm/aarch64.ad
+++ b/src/cpu/aarch64/vm/aarch64.ad
@@ -375,6 +375,15 @@ reg_class any_reg32(
R30
);
+// Singleton class for R0 int register
+reg_class int_r0_reg(R0);
+
+// Singleton class for R2 int register
+reg_class int_r2_reg(R2);
+
+// Singleton class for R4 int register
+reg_class int_r4_reg(R4);
+
// Class for all long integer registers (including RSP)
reg_class any_reg(
R0, R0_H,
@@ -482,11 +491,21 @@ reg_class r0_reg(
R0, R0_H
);
+// Class for 64 bit register r1
+reg_class r1_reg(
+ R1, R1_H
+);
+
// Class for 64 bit register r2
reg_class r2_reg(
R2, R2_H
);
+// Class for 64 bit register r3
+reg_class r3_reg(
+ R3, R3_H
+);
+
// Class for 64 bit register r4
reg_class r4_reg(
R4, R4_H
@@ -3986,6 +4005,18 @@ operand iRegP_R0()
interface(REG_INTER);
%}
+// Pointer 64 bit Register R1 only
+operand iRegP_R1()
+%{
+ constraint(ALLOC_IN_RC(r1_reg));
+ match(RegP);
+ // match(iRegP);
+ match(iRegPNoSp);
+ op_cost(0);
+ format %{ %}
+ interface(REG_INTER);
+%}
+
// Pointer 64 bit Register R2 only
operand iRegP_R2()
%{
@@ -3998,6 +4029,18 @@ operand iRegP_R2()
interface(REG_INTER);
%}
+// Pointer 64 bit Register R3 only
+operand iRegP_R3()
+%{
+ constraint(ALLOC_IN_RC(r3_reg));
+ match(RegP);
+ // match(iRegP);
+ match(iRegPNoSp);
+ op_cost(0);
+ format %{ %}
+ interface(REG_INTER);
+%}
+
// Pointer 64 bit Register R4 only
operand iRegP_R4()
%{
@@ -4059,7 +4102,7 @@ operand iRegP_FP()
// Register R0 only
operand iRegI_R0()
%{
- constraint(ALLOC_IN_RC(r0_reg));
+ constraint(ALLOC_IN_RC(int_r0_reg));
match(RegI);
match(iRegINoSp);
op_cost(0);
@@ -4067,6 +4110,29 @@ operand iRegI_R0()
interface(REG_INTER);
%}
+// Register R2 only
+operand iRegI_R2()
+%{
+ constraint(ALLOC_IN_RC(int_r2_reg));
+ match(RegI);
+ match(iRegINoSp);
+ op_cost(0);
+ format %{ %}
+ interface(REG_INTER);
+%}
+
+// Register R2 only
+operand iRegI_R4()
+%{
+ constraint(ALLOC_IN_RC(int_r4_reg));
+ match(RegI);
+ match(iRegINoSp);
+ op_cost(0);
+ format %{ %}
+ interface(REG_INTER);
+%}
+
+
// Pointer Register Operands
// Narrow Pointer Register
operand iRegN()
@@ -11267,6 +11333,21 @@ instruct partialSubtypeCheckVsZero(iRegP_R4 sub, iRegP_R0 super, iRegP_R2 temp,
ins_pipe(pipe_class_memory);
%}
+instruct string_compare(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2,
+ iRegI_R0 result, iRegP tmp1, rFlagsReg cr)
+%{
+ match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
+ effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);
+
+ format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result # KILL $tmp1" %}
+ ins_encode %{
+ __ string_compare($str1$$Register, $str2$$Register,
+ $cnt1$$Register, $cnt2$$Register, $result$$Register,
+ $tmp1$$Register);
+ %}
+ ins_pipe(pipe_class_memory);
+%}
+
// ============================================================================
// This name is KNOWN by the ADLC and cannot be changed.
// The ADLC forces a 'TypeRawPtr::BOTTOM' output type
diff --git a/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp b/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp
index cba988f60..03f85ddb8 100644
--- a/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp
+++ b/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp
@@ -3353,3 +3353,86 @@ void MacroAssembler::remove_frame(int framesize) {
}
}
+// Compare strings.
+void MacroAssembler::string_compare(Register str1, Register str2,
+ Register cnt1, Register cnt2, Register result,
+ Register tmp1) {
+ Label LENGTH_DIFF, DONE, SHORT_LOOP, SHORT_STRING,
+ NEXT_WORD, DIFFERENCE;
+
+ BLOCK_COMMENT("string_compare {");
+
+ // Compute the minimum of the string lengths and save the difference.
+ subsw(tmp1, cnt1, cnt2);
+ cselw(cnt2, cnt1, cnt2, Assembler::LE); // min
+
+ // A very short string
+ cmpw(cnt2, 4);
+ br(Assembler::LT, SHORT_STRING);
+
+ // Check if the strings start at the same location.
+ cmp(str1, str2);
+ br(Assembler::EQ, LENGTH_DIFF);
+
+ // Compare longwords
+ {
+ subw(cnt2, cnt2, 4); // The last longword is a special case
+
+ // Move both string pointers to the last longword of their
+ // strings, negate the remaining count, and convert it to bytes.
+ lea(str1, Address(str1, cnt2, Address::uxtw(1)));
+ lea(str2, Address(str2, cnt2, Address::uxtw(1)));
+ sub(cnt2, zr, cnt2, LSL, 1);
+
+ // Loop, loading longwords and comparing them into rscratch2.
+ bind(NEXT_WORD);
+ ldr(result, Address(str1, cnt2));
+ ldr(cnt1, Address(str2, cnt2));
+ adds(cnt2, cnt2, wordSize);
+ eor(rscratch2, result, cnt1);
+ cbnz(rscratch2, DIFFERENCE);
+ br(Assembler::LT, NEXT_WORD);
+
+ // Last longword. In the case where length == 4 we compare the
+ // same longword twice, but that's still faster than another
+ // conditional branch.
+
+ ldr(result, Address(str1));
+ ldr(cnt1, Address(str2));
+ eor(rscratch2, result, cnt1);
+ cbz(rscratch2, LENGTH_DIFF);
+
+ // Find the first different characters in the longwords and
+ // compute their difference.
+ bind(DIFFERENCE);
+ rev(rscratch2, rscratch2);
+ clz(rscratch2, rscratch2);
+ andr(rscratch2, rscratch2, -16);
+ lsrv(result, result, rscratch2);
+ lsrv(cnt1, cnt1, rscratch2);
+ sub(result, result, cnt1);
+ sxthw(result, result);
+ b(DONE);
+ }
+
+ bind(SHORT_STRING);
+ // Is the minimum length zero?
+ cbz(cnt2, LENGTH_DIFF);
+
+ bind(SHORT_LOOP);
+ load_unsigned_short(result, Address(post(str1, 2)));
+ load_unsigned_short(cnt1, Address(post(str2, 2)));
+ subw(result, result, cnt1);
+ cbnz(result, DONE);
+ sub(cnt2, cnt2, 1);
+ cbnz(cnt2, SHORT_LOOP);
+
+ // Strings are equal up to min length. Return the length difference.
+ bind(LENGTH_DIFF);
+ mov(result, tmp1);
+
+ // That's it
+ bind(DONE);
+
+ BLOCK_COMMENT("} string_compare");
+}
diff --git a/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp b/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp
index fe583c7ae..4db9b87ef 100644
--- a/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp
+++ b/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp
@@ -1290,6 +1290,10 @@ public:
void update_word_crc32(Register crc, Register v, Register tmp,
Register table0, Register table1, Register table2, Register table3,
bool upper = false);
+
+ void string_compare(Register str1, Register str2,
+ Register cnt1, Register cnt2, Register result,
+ Register tmp1);
};
// Used by aarch64.ad to control code generation