Index: src/x64/code-stubs-x64.cc |
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc |
index ec1c185620579e26087026c554cc0a2ae19b5688..5f0a84d7d1c30ae66bdcf9f5c5ce9d1434dfeb6b 100644 |
--- a/src/x64/code-stubs-x64.cc |
+++ b/src/x64/code-stubs-x64.cc |
@@ -976,7 +976,7 @@ void TypeRecordingBinaryOpStub::GenerateFloatingPointCode( |
// already loaded heap_number_map. |
__ AllocateInNewSpace(HeapNumber::kSize, |
rax, |
- rcx, |
+ rdx, |
no_reg, |
&allocation_failed, |
TAG_OBJECT); |
@@ -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,47 @@ 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); |
+ NearLabel strings_not_equal; |
+ GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2, |
+ &strings_not_equal); |
+ |
+ // 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, |
@@ -4523,35 +4572,11 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, |
__ SmiTest(min_length); |
__ j(zero, &compare_lengths); |
- __ SmiToInteger32(min_length, min_length); |
- |
- // Registers scratch2 and scratch3 are free. |
+ // Compare loop. |
NearLabel result_not_equal; |
- Label loop; |
- { |
- // Check characters 0 .. min_length - 1 in a loop. |
- // Use scratch3 as loop index, min_length as limit and scratch2 |
- // for computation. |
- const Register index = scratch3; |
- __ Set(index, 0); // Index into strings. |
- __ bind(&loop); |
- // Compare characters. |
- // TODO(lrn): Could we load more than one character at a time? |
- __ movb(scratch2, FieldOperand(left, |
- index, |
- times_1, |
- SeqAsciiString::kHeaderSize)); |
- // Increment index and use -1 modifier on next load to give |
- // the previous load extra time to complete. |
- __ addl(index, Immediate(1)); |
- __ cmpb(scratch2, FieldOperand(right, |
- index, |
- times_1, |
- SeqAsciiString::kHeaderSize - 1)); |
- __ j(not_equal, &result_not_equal); |
- __ cmpl(index, min_length); |
- __ j(not_equal, &loop); |
- } |
+ GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2, |
+ &result_not_equal); |
+ |
// Completed loop without finding different characters. |
// Compare lengths (precomputed). |
__ bind(&compare_lengths); |
@@ -4578,6 +4603,35 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, |
} |
+void StringCompareStub::GenerateAsciiCharsCompareLoop( |
+ MacroAssembler* masm, |
+ Register left, |
+ Register right, |
+ Register length, |
+ Register scratch, |
+ NearLabel* chars_not_equal) { |
+ // 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 loop; |
+ __ bind(&loop); |
+ __ movb(scratch, Operand(left, index, times_1, 0)); |
+ __ cmpb(scratch, Operand(right, index, times_1, 0)); |
+ __ j(not_equal, chars_not_equal); |
+ __ addq(index, Immediate(1)); |
+ __ j(not_zero, &loop); |
+} |
+ |
+ |
void StringCompareStub::Generate(MacroAssembler* masm) { |
Label runtime; |
@@ -4685,6 +4739,80 @@ 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. |
+ Register left = rdx; |
+ Register right = rax; |
+ Register tmp1 = rcx; |
+ Register tmp2 = rbx; |
+ Register tmp3 = rdi; |
+ |
+ // Check that both operands are heap objects. |
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1); |
+ __ j(cond, &miss); |
+ |
+ // Check that both operands are strings. This leaves the instance |
+ // types loaded in tmp1 and tmp2. |
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset)); |
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset)); |
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); |
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); |
+ __ movq(tmp3, tmp1); |
+ STATIC_ASSERT(kNotStringTag != 0); |
+ __ or_(tmp3, tmp2); |
+ __ testl(tmp3, Immediate(kIsNotStringMask)); |
+ __ j(not_zero, &miss); |
+ |
+ // Fast check for identical strings. |
+ NearLabel not_same; |
+ __ cmpq(left, right); |
+ __ j(not_equal, ¬_same); |
+ STATIC_ASSERT(EQUAL == 0); |
+ STATIC_ASSERT(kSmiTag == 0); |
+ __ Move(rax, Smi::FromInt(EQUAL)); |
+ __ ret(0); |
+ |
+ // Handle not identical strings. |
+ __ bind(¬_same); |
+ |
+ // Check that both strings are symbols. If they are, we're done |
+ // because we already know they are not identical. |
+ NearLabel do_compare; |
+ STATIC_ASSERT(kSymbolTag != 0); |
+ __ and_(tmp1, tmp2); |
+ __ testl(tmp1, Immediate(kIsSymbolMask)); |
+ __ j(zero, &do_compare); |
+ // Make sure rax is non-zero. At this point input operands are |
+ // guaranteed to be non-zero. |
+ ASSERT(right.is(rax)); |
+ __ ret(0); |
+ |
+ // Check that both strings are sequential ASCII. |
+ Label runtime; |
+ __ bind(&do_compare); |
+ __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime); |
+ |
+ // Compare flat ASCII strings. Returns when done. |
+ StringCompareStub::GenerateFlatAsciiStringEquals( |
+ masm, left, right, tmp1, tmp2); |
+ |
+ // Handle more complex cases in runtime. |
+ __ bind(&runtime); |
+ __ pop(tmp1); // Return address. |
+ __ push(left); |
+ __ push(right); |
+ __ push(tmp1); |
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1); |
+ |
+ __ bind(&miss); |
+ GenerateMiss(masm); |
+} |
+ |
+ |
void ICCompareStub::GenerateObjects(MacroAssembler* masm) { |
ASSERT(state_ == CompareIC::OBJECTS); |
NearLabel miss; |
@@ -4736,6 +4864,203 @@ void ICCompareStub::GenerateMiss(MacroAssembler* masm) { |
} |
+void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, |
+ Label* miss, |
+ Label* done, |
+ Register properties, |
+ String* name, |
+ Register r0) { |
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are |
+ // not equal to the name and kProbes-th slot is not used (its name is the |
+ // undefined value), it guarantees the hash table doesn't contain the |
+ // property. It's true even if some slots represent deleted properties |
+ // (their names are the null value). |
+ for (int i = 0; i < kInlinedProbes; i++) { |
+ // r0 points to properties hash. |
+ // Compute the masked index: (hash + i + i * i) & mask. |
+ Register index = r0; |
+ // Capacity is smi 2^n. |
+ __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset)); |
+ __ decl(index); |
+ __ and_(index, |
+ Immediate(name->Hash() + StringDictionary::GetProbeOffset(i))); |
+ |
+ // Scale the index by multiplying by the entry size. |
+ ASSERT(StringDictionary::kEntrySize == 3); |
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3. |
+ |
+ Register entity_name = r0; |
+ // Having undefined at this place means the name is not contained. |
+ ASSERT_EQ(kSmiTagSize, 1); |
+ __ movq(entity_name, Operand(properties, |
+ index, |
+ times_pointer_size, |
+ kElementsStartOffset - kHeapObjectTag)); |
+ __ Cmp(entity_name, masm->isolate()->factory()->undefined_value()); |
+ __ j(equal, done); |
+ |
+ // Stop if found the property. |
+ __ Cmp(entity_name, Handle<String>(name)); |
+ __ j(equal, miss); |
+ |
+ // Check if the entry name is not a symbol. |
+ __ movq(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset)); |
+ __ testb(FieldOperand(entity_name, Map::kInstanceTypeOffset), |
+ Immediate(kIsSymbolMask)); |
+ __ j(zero, miss); |
+ } |
+ |
+ StringDictionaryLookupStub stub(properties, |
+ r0, |
+ r0, |
+ StringDictionaryLookupStub::NEGATIVE_LOOKUP); |
+ __ Push(Handle<Object>(name)); |
+ __ push(Immediate(name->Hash())); |
+ __ CallStub(&stub); |
+ __ testq(r0, r0); |
+ __ j(not_zero, miss); |
+ __ jmp(done); |
+} |
+ |
+ |
+// Probe the string dictionary in the |elements| register. Jump to the |
+// |done| label if a property with the given name is found leaving the |
+// index into the dictionary in |r1|. Jump to the |miss| label |
+// otherwise. |
+void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, |
+ Label* miss, |
+ Label* done, |
+ Register elements, |
+ Register name, |
+ Register r0, |
+ Register r1) { |
+ // Assert that name contains a string. |
+ if (FLAG_debug_code) __ AbortIfNotString(name); |
+ |
+ __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset)); |
+ __ decl(r0); |
+ |
+ for (int i = 0; i < kInlinedProbes; i++) { |
+ // Compute the masked index: (hash + i + i * i) & mask. |
+ __ movl(r1, FieldOperand(name, String::kHashFieldOffset)); |
+ __ shrl(r1, Immediate(String::kHashShift)); |
+ if (i > 0) { |
+ __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i))); |
+ } |
+ __ and_(r1, r0); |
+ |
+ // Scale the index by multiplying by the entry size. |
+ ASSERT(StringDictionary::kEntrySize == 3); |
+ __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3 |
+ |
+ // Check if the key is identical to the name. |
+ __ cmpq(name, Operand(elements, r1, times_pointer_size, |
+ kElementsStartOffset - kHeapObjectTag)); |
+ __ j(equal, done); |
+ } |
+ |
+ StringDictionaryLookupStub stub(elements, |
+ r0, |
+ r1, |
+ POSITIVE_LOOKUP); |
+ __ push(name); |
+ __ movl(r0, FieldOperand(name, String::kHashFieldOffset)); |
+ __ shrl(r0, Immediate(String::kHashShift)); |
+ __ push(r0); |
+ __ CallStub(&stub); |
+ |
+ __ testq(r0, r0); |
+ __ j(zero, miss); |
+ __ jmp(done); |
+} |
+ |
+ |
+void StringDictionaryLookupStub::Generate(MacroAssembler* masm) { |
+ // Stack frame on entry: |
+ // esp[0 * kPointerSize]: return address. |
+ // esp[1 * kPointerSize]: key's hash. |
+ // esp[2 * kPointerSize]: key. |
+ // Registers: |
+ // dictionary_: StringDictionary to probe. |
+ // result_: used as scratch. |
+ // index_: will hold an index of entry if lookup is successful. |
+ // might alias with result_. |
+ // Returns: |
+ // result_ is zero if lookup failed, non zero otherwise. |
+ |
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary; |
+ |
+ Register scratch = result_; |
+ |
+ __ SmiToInteger32(scratch, FieldOperand(dictionary_, kCapacityOffset)); |
+ __ decl(scratch); |
+ __ push(scratch); |
+ |
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are |
+ // not equal to the name and kProbes-th slot is not used (its name is the |
+ // undefined value), it guarantees the hash table doesn't contain the |
+ // property. It's true even if some slots represent deleted properties |
+ // (their names are the null value). |
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) { |
+ // Compute the masked index: (hash + i + i * i) & mask. |
+ __ movq(scratch, Operand(rsp, 2 * kPointerSize)); |
+ if (i > 0) { |
+ __ addl(scratch, Immediate(StringDictionary::GetProbeOffset(i))); |
+ } |
+ __ and_(scratch, Operand(rsp, 0)); |
+ |
+ // Scale the index by multiplying by the entry size. |
+ ASSERT(StringDictionary::kEntrySize == 3); |
+ __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3. |
+ |
+ // Having undefined at this place means the name is not contained. |
+ __ movq(scratch, Operand(dictionary_, |
+ index_, |
+ times_pointer_size, |
+ kElementsStartOffset - kHeapObjectTag)); |
+ |
+ __ Cmp(scratch, masm->isolate()->factory()->undefined_value()); |
+ __ j(equal, ¬_in_dictionary); |
+ |
+ // Stop if found the property. |
+ __ cmpq(scratch, Operand(rsp, 3 * kPointerSize)); |
+ __ j(equal, &in_dictionary); |
+ |
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) { |
+ // If we hit a non symbol key during negative lookup |
+ // we have to bailout as this key might be equal to the |
+ // key we are looking for. |
+ |
+ // Check if the entry name is not a symbol. |
+ __ movq(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); |
+ __ testb(FieldOperand(scratch, Map::kInstanceTypeOffset), |
+ Immediate(kIsSymbolMask)); |
+ __ j(zero, &maybe_in_dictionary); |
+ } |
+ } |
+ |
+ __ bind(&maybe_in_dictionary); |
+ // If we are doing negative lookup then probing failure should be |
+ // treated as a lookup success. For positive lookup probing failure |
+ // should be treated as lookup failure. |
+ if (mode_ == POSITIVE_LOOKUP) { |
+ __ movq(scratch, Immediate(0)); |
+ __ Drop(1); |
+ __ ret(2 * kPointerSize); |
+ } |
+ |
+ __ bind(&in_dictionary); |
+ __ movq(scratch, Immediate(1)); |
+ __ Drop(1); |
+ __ ret(2 * kPointerSize); |
+ |
+ __ bind(¬_in_dictionary); |
+ __ movq(scratch, Immediate(0)); |
+ __ Drop(1); |
+ __ ret(2 * kPointerSize); |
+} |
+ |
+ |
#undef __ |
} } // namespace v8::internal |