Faster string equals in generated code.
In my previous change I mixed up "compare" and "equals". This made us miss the fast length check before comparing strings for equality. Now we have a separate helper for "equals". It shares some of the inner loop details with "compare". I'll see if this can be cleaned up without making it unreadable. Review URL: http://codereview.chromium.org/6928020 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7794 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
0af052870f
commit
5b2e2636b7
@ -1568,13 +1568,22 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
||||
__ JumpIfNonSmisNotBothSequentialAsciiStrings(lhs_, rhs_, r2, r3, &slow);
|
||||
|
||||
__ IncrementCounter(isolate->counters()->string_compare_native(), 1, r2, r3);
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
|
||||
if (cc_ == eq) {
|
||||
StringCompareStub::GenerateFlatAsciiStringEquals(masm,
|
||||
lhs_,
|
||||
rhs_,
|
||||
r2,
|
||||
r3,
|
||||
r4,
|
||||
r5);
|
||||
r4);
|
||||
} else {
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
|
||||
lhs_,
|
||||
rhs_,
|
||||
r2,
|
||||
r3,
|
||||
r4,
|
||||
r5);
|
||||
}
|
||||
// Never falls through to here.
|
||||
|
||||
__ bind(&slow);
|
||||
@ -5392,6 +5401,63 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3) {
|
||||
Register length = scratch1;
|
||||
|
||||
// Compare lengths.
|
||||
Label strings_not_equal, check_zero_length;
|
||||
__ ldr(length, FieldMemOperand(left, String::kLengthOffset));
|
||||
__ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
|
||||
__ cmp(length, scratch2);
|
||||
__ b(eq, &check_zero_length);
|
||||
__ bind(&strings_not_equal);
|
||||
__ mov(r0, Operand(Smi::FromInt(NOT_EQUAL)));
|
||||
__ Ret();
|
||||
|
||||
// Check if the length is zero.
|
||||
Label compare_chars;
|
||||
__ bind(&check_zero_length);
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ tst(length, Operand(length));
|
||||
__ b(ne, &compare_chars);
|
||||
__ mov(r0, Operand(Smi::FromInt(EQUAL)));
|
||||
__ Ret();
|
||||
|
||||
// Compare characters.
|
||||
__ bind(&compare_chars);
|
||||
|
||||
// Change index to run from -length to -1 by adding length to string
|
||||
// start. This means that loop ends when index reaches zero, which
|
||||
// doesn't need an additional compare.
|
||||
__ SmiUntag(length);
|
||||
__ add(scratch2, length,
|
||||
Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||
__ add(left, left, Operand(scratch2));
|
||||
__ add(right, right, Operand(scratch2));
|
||||
__ rsb(length, length, Operand(0));
|
||||
Register index = length; // index = -length;
|
||||
|
||||
// Compare loop.
|
||||
Label loop;
|
||||
__ bind(&loop);
|
||||
__ ldrb(scratch2, MemOperand(left, index));
|
||||
__ ldrb(scratch3, MemOperand(right, index));
|
||||
__ cmp(scratch2, scratch3);
|
||||
__ b(ne, &strings_not_equal);
|
||||
__ add(index, index, Operand(1), SetCC);
|
||||
__ b(ne, &loop);
|
||||
|
||||
// Characters are equal.
|
||||
__ mov(r0, Operand(Smi::FromInt(EQUAL)));
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
@ -5955,13 +6021,13 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
&runtime);
|
||||
|
||||
// Compare flat ASCII strings. Returns when done.
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(
|
||||
masm, left, right, tmp1, tmp2, tmp3, tmp4);
|
||||
StringCompareStub::GenerateFlatAsciiStringEquals(
|
||||
masm, left, right, tmp1, tmp2, tmp3);
|
||||
|
||||
// Handle more complex cases in runtime.
|
||||
__ bind(&runtime);
|
||||
__ Push(left, right);
|
||||
__ TailCallRuntime(Runtime::kStringCompare, 2, 1);
|
||||
__ TailCallRuntime(Runtime::kStringEquals, 2, 1);
|
||||
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
|
@ -327,8 +327,7 @@ class StringCompareStub: public CodeStub {
|
||||
public:
|
||||
StringCompareStub() { }
|
||||
|
||||
// Compare two flat ASCII strings and returns result in r0.
|
||||
// Does not use the stack.
|
||||
// Compares two flat ASCII strings and returns result in r0.
|
||||
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
@ -337,6 +336,15 @@ class StringCompareStub: public CodeStub {
|
||||
Register scratch3,
|
||||
Register scratch4);
|
||||
|
||||
// Compares two flat ASCII strings for equality and returns result
|
||||
// in r0.
|
||||
static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3);
|
||||
|
||||
private:
|
||||
Major MajorKey() { return StringCompare; }
|
||||
int MinorKey() { return 0; }
|
||||
|
@ -3901,12 +3901,20 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
||||
&check_unequal_objects);
|
||||
|
||||
// Inline comparison of ascii strings.
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
|
||||
if (cc_ == equal) {
|
||||
StringCompareStub::GenerateFlatAsciiStringEquals(masm,
|
||||
edx,
|
||||
eax,
|
||||
ecx,
|
||||
ebx,
|
||||
edi);
|
||||
ebx);
|
||||
} else {
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
|
||||
edx,
|
||||
eax,
|
||||
ecx,
|
||||
ebx,
|
||||
edi);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
__ Abort("Unexpected fall-through from string comparison");
|
||||
#endif
|
||||
@ -5602,6 +5610,60 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
Register length = scratch1;
|
||||
|
||||
// Compare lengths.
|
||||
NearLabel strings_not_equal, check_zero_length;
|
||||
__ mov(length, FieldOperand(left, String::kLengthOffset));
|
||||
__ cmp(length, FieldOperand(right, String::kLengthOffset));
|
||||
__ j(equal, &check_zero_length);
|
||||
__ bind(&strings_not_equal);
|
||||
__ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
|
||||
__ ret(0);
|
||||
|
||||
// Check if the length is zero.
|
||||
NearLabel compare_chars;
|
||||
__ bind(&check_zero_length);
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ test(length, Operand(length));
|
||||
__ j(not_zero, &compare_chars);
|
||||
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
|
||||
__ ret(0);
|
||||
|
||||
// Compare characters.
|
||||
__ bind(&compare_chars);
|
||||
|
||||
// Change index to run from -length to -1 by adding length to string
|
||||
// start. This means that loop ends when index reaches zero, which
|
||||
// doesn't need an additional compare.
|
||||
__ SmiUntag(length);
|
||||
__ lea(left,
|
||||
FieldOperand(left, length, times_1, SeqAsciiString::kHeaderSize));
|
||||
__ lea(right,
|
||||
FieldOperand(right, length, times_1, SeqAsciiString::kHeaderSize));
|
||||
__ neg(length);
|
||||
Register index = length; // index = -length;
|
||||
|
||||
// Compare loop.
|
||||
NearLabel loop;
|
||||
__ bind(&loop);
|
||||
__ mov_b(scratch2, Operand(left, index, times_1, 0));
|
||||
__ cmpb(scratch2, Operand(right, index, times_1, 0));
|
||||
__ j(not_equal, &strings_not_equal);
|
||||
__ add(Operand(index), Immediate(1));
|
||||
__ j(not_zero, &loop);
|
||||
|
||||
// Characters are equal.
|
||||
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
|
||||
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
@ -5808,6 +5870,7 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
|
||||
|
||||
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
ASSERT(state_ == CompareIC::STRINGS);
|
||||
ASSERT(GetCondition() == equal);
|
||||
Label miss;
|
||||
|
||||
// Registers containing left and right operands respectively.
|
||||
@ -5851,7 +5914,6 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
// Check that both strings are symbols. If they are, we're done
|
||||
// because we already know they are not identical.
|
||||
NearLabel do_compare;
|
||||
ASSERT(GetCondition() == equal);
|
||||
STATIC_ASSERT(kSymbolTag != 0);
|
||||
__ and_(tmp1, Operand(tmp2));
|
||||
__ test(tmp1, Immediate(kIsSymbolMask));
|
||||
@ -5867,8 +5929,8 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
__ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
|
||||
|
||||
// Compare flat ASCII strings. Returns when done.
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(
|
||||
masm, left, right, tmp1, tmp2, tmp3);
|
||||
StringCompareStub::GenerateFlatAsciiStringEquals(
|
||||
masm, left, right, tmp1, tmp2);
|
||||
|
||||
// Handle more complex cases in runtime.
|
||||
__ bind(&runtime);
|
||||
@ -5876,7 +5938,7 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
__ push(left);
|
||||
__ push(right);
|
||||
__ push(tmp1);
|
||||
__ TailCallRuntime(Runtime::kStringCompare, 2, 1);
|
||||
__ TailCallRuntime(Runtime::kStringEquals, 2, 1);
|
||||
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
|
@ -372,11 +372,9 @@ class SubStringStub: public CodeStub {
|
||||
|
||||
class StringCompareStub: public CodeStub {
|
||||
public:
|
||||
explicit StringCompareStub() {
|
||||
}
|
||||
StringCompareStub() { }
|
||||
|
||||
// Compare two flat ascii strings and returns result in eax after popping two
|
||||
// arguments from the stack.
|
||||
// Compares two flat ASCII strings and returns result in eax.
|
||||
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
@ -384,6 +382,14 @@ class StringCompareStub: public CodeStub {
|
||||
Register scratch2,
|
||||
Register scratch3);
|
||||
|
||||
// Compares two flat ASCII strings for equality and returns result
|
||||
// in eax.
|
||||
static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
Register scratch1,
|
||||
Register scratch2);
|
||||
|
||||
private:
|
||||
Major MajorKey() { return StringCompare; }
|
||||
int MinorKey() { return 0; }
|
||||
|
@ -2835,13 +2835,21 @@ void CompareStub::Generate(MacroAssembler* masm) {
|
||||
rdx, rax, rcx, rbx, &check_unequal_objects);
|
||||
|
||||
// Inline comparison of ascii strings.
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
|
||||
if (cc_ == equal) {
|
||||
StringCompareStub::GenerateFlatAsciiStringEquals(masm,
|
||||
rdx,
|
||||
rax,
|
||||
rcx,
|
||||
rbx,
|
||||
rdi,
|
||||
r8);
|
||||
rbx);
|
||||
} else {
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
|
||||
rdx,
|
||||
rax,
|
||||
rcx,
|
||||
rbx,
|
||||
rdi,
|
||||
r8);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
__ Abort("Unexpected fall-through from string comparison");
|
||||
@ -4489,6 +4497,64 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
Register length = scratch1;
|
||||
|
||||
// Compare lengths.
|
||||
NearLabel check_zero_length;
|
||||
__ movq(length, FieldOperand(left, String::kLengthOffset));
|
||||
__ SmiCompare(length, FieldOperand(right, String::kLengthOffset));
|
||||
__ j(equal, &check_zero_length);
|
||||
__ Move(rax, Smi::FromInt(NOT_EQUAL));
|
||||
__ ret(0);
|
||||
|
||||
// Check if the length is zero.
|
||||
NearLabel compare_chars;
|
||||
__ bind(&check_zero_length);
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ SmiTest(length);
|
||||
__ j(not_zero, &compare_chars);
|
||||
__ Move(rax, Smi::FromInt(EQUAL));
|
||||
__ ret(0);
|
||||
|
||||
// Compare characters.
|
||||
__ bind(&compare_chars);
|
||||
|
||||
// Change index to run from -length to -1 by adding length to string
|
||||
// start. This means that loop ends when index reaches zero, which
|
||||
// doesn't need an additional compare.
|
||||
__ SmiToInteger32(length, length);
|
||||
__ lea(left,
|
||||
FieldOperand(left, length, times_1, SeqAsciiString::kHeaderSize));
|
||||
__ lea(right,
|
||||
FieldOperand(right, length, times_1, SeqAsciiString::kHeaderSize));
|
||||
__ neg(length);
|
||||
Register index = length; // index = -length;
|
||||
|
||||
// Compare loop.
|
||||
NearLabel strings_not_equal, loop;
|
||||
__ bind(&loop);
|
||||
__ movb(scratch2, Operand(left, index, times_1, 0));
|
||||
__ cmpb(scratch2, Operand(right, index, times_1, 0));
|
||||
__ j(not_equal, &strings_not_equal);
|
||||
__ addq(index, Immediate(1));
|
||||
__ j(not_zero, &loop);
|
||||
|
||||
// Characters are equal.
|
||||
__ Move(rax, Smi::FromInt(EQUAL));
|
||||
__ ret(0);
|
||||
|
||||
// Characters are not equal.
|
||||
__ bind(&strings_not_equal);
|
||||
__ Move(rax, Smi::FromInt(NOT_EQUAL));
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
|
||||
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
@ -4687,6 +4753,7 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
|
||||
|
||||
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
ASSERT(state_ == CompareIC::STRINGS);
|
||||
ASSERT(GetCondition() == equal);
|
||||
Label miss;
|
||||
|
||||
// Registers containing left and right operands respectively.
|
||||
@ -4695,7 +4762,6 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
Register tmp1 = rcx;
|
||||
Register tmp2 = rbx;
|
||||
Register tmp3 = rdi;
|
||||
Register tmp4 = r8;
|
||||
|
||||
// Check that both operands are heap objects.
|
||||
Condition cond = masm->CheckEitherSmi(left, right, tmp1);
|
||||
@ -4728,7 +4794,6 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
// Check that both strings are symbols. If they are, we're done
|
||||
// because we already know they are not identical.
|
||||
NearLabel do_compare;
|
||||
ASSERT(GetCondition() == equal);
|
||||
STATIC_ASSERT(kSymbolTag != 0);
|
||||
__ and_(tmp1, tmp2);
|
||||
__ testl(tmp1, Immediate(kIsSymbolMask));
|
||||
@ -4744,8 +4809,8 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
__ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
|
||||
|
||||
// Compare flat ASCII strings. Returns when done.
|
||||
StringCompareStub::GenerateCompareFlatAsciiStrings(
|
||||
masm, left, right, tmp1, tmp2, tmp3, tmp4);
|
||||
StringCompareStub::GenerateFlatAsciiStringEquals(
|
||||
masm, left, right, tmp1, tmp2);
|
||||
|
||||
// Handle more complex cases in runtime.
|
||||
__ bind(&runtime);
|
||||
@ -4753,7 +4818,7 @@ void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
|
||||
__ push(left);
|
||||
__ push(right);
|
||||
__ push(tmp1);
|
||||
__ TailCallRuntime(Runtime::kStringCompare, 2, 1);
|
||||
__ TailCallRuntime(Runtime::kStringEquals, 2, 1);
|
||||
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
|
@ -364,10 +364,9 @@ class SubStringStub: public CodeStub {
|
||||
|
||||
class StringCompareStub: public CodeStub {
|
||||
public:
|
||||
explicit StringCompareStub() {}
|
||||
StringCompareStub() {}
|
||||
|
||||
// Compare two flat ascii strings and returns result in rax after popping two
|
||||
// arguments from the stack.
|
||||
// Compares two flat ASCII strings and returns result in rax.
|
||||
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
@ -376,6 +375,14 @@ class StringCompareStub: public CodeStub {
|
||||
Register scratch3,
|
||||
Register scratch4);
|
||||
|
||||
// Compares two flat ASCII strings for equality and returns result
|
||||
// in rax.
|
||||
static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
||||
Register left,
|
||||
Register right,
|
||||
Register scratch1,
|
||||
Register scratch2);
|
||||
|
||||
private:
|
||||
Major MajorKey() { return StringCompare; }
|
||||
int MinorKey() { return 0; }
|
||||
|
Loading…
Reference in New Issue
Block a user