Index: src/ia32/codegen-ia32.cc |
=================================================================== |
--- src/ia32/codegen-ia32.cc (revision 3833) |
+++ src/ia32/codegen-ia32.cc (working copy) |
@@ -9641,13 +9641,34 @@ |
// ecx: length of second string |
// edx: second string |
// Look at the length of the result of adding the two strings. |
- Label string_add_flat_result; |
+ Label string_add_flat_result, longer_than_two; |
__ bind(&both_not_zero_length); |
__ add(ebx, Operand(ecx)); |
// Use the runtime system when adding two one character strings, as it |
// contains optimizations for this specific case using the symbol table. |
__ cmp(ebx, 2); |
- __ j(equal, &string_add_runtime); |
+ __ j(not_equal, &longer_than_two); |
+ |
+ // Check that both strings are non-external ascii strings. |
+ __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, |
+ &string_add_runtime); |
+ |
+ // Get the two characters forming the sub string. |
+ __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize)); |
+ __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize)); |
+ |
+ // Try to lookup two character string in symbol table. If it is not found |
+ // just allocate a new one. |
+ Label make_two_character_string, make_flat_ascii_string; |
+ GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi, |
+ &make_two_character_string); |
+ __ ret(2 * kPointerSize); |
+ |
+ __ bind(&make_two_character_string); |
+ __ Set(ebx, Immediate(2)); |
+ __ jmp(&make_flat_ascii_string); |
+ |
+ __ bind(&longer_than_two); |
// Check if resulting string will be flat. |
__ cmp(ebx, String::kMinNonFlatLength); |
__ j(below, &string_add_flat_result); |
@@ -9714,7 +9735,10 @@ |
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); |
__ test(ecx, Immediate(kAsciiStringTag)); |
__ j(zero, &string_add_runtime); |
+ |
+ __ bind(&make_flat_ascii_string); |
// Both strings are ascii strings. As they are short they are both flat. |
+ // ebx: length of resulting flat string |
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
// eax: result string |
__ mov(ecx, eax); |
@@ -9871,6 +9895,190 @@ |
} |
+void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, |
+ Register c1, |
+ Register c2, |
+ Register scratch1, |
+ Register scratch2, |
+ Register scratch3, |
+ Label* not_found) { |
+ // Register scratch3 is the general scratch register in this function. |
+ Register scratch = scratch3; |
+ |
+ // Make sure that both characters are not digits as such strings has a |
+ // different hash algorithm. Don't try to look for these in the symbol table. |
+ Label not_array_index; |
+ __ mov(scratch, c1); |
+ __ sub(Operand(scratch), Immediate(static_cast<int>('0'))); |
+ __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0'))); |
+ __ j(above, ¬_array_index); |
+ __ mov(scratch, c2); |
+ __ sub(Operand(scratch), Immediate(static_cast<int>('0'))); |
+ __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0'))); |
+ __ j(below_equal, not_found); |
+ |
+ __ bind(¬_array_index); |
+ // Calculate the two character string hash. |
+ Register hash = scratch1; |
+ GenerateHashInit(masm, hash, c1, scratch); |
+ GenerateHashAddCharacter(masm, hash, c2, scratch); |
+ GenerateHashGetHash(masm, hash, scratch); |
+ |
+ // Collect the two characters in a register. |
+ Register chars = c1; |
+ __ shl(c2, kBitsPerByte); |
+ __ or_(chars, Operand(c2)); |
+ |
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1. |
+ // hash: hash of two character string. |
+ |
+ // Load the symbol table. |
+ Register symbol_table = c2; |
+ ExternalReference roots_address = ExternalReference::roots_address(); |
+ __ mov(scratch, Immediate(Heap::kSymbolTableRootIndex)); |
+ __ mov(symbol_table, |
+ Operand::StaticArray(scratch, times_pointer_size, roots_address)); |
+ |
+ // Calculate capacity mask from the symbol table capacity. |
+ Register mask = scratch2; |
+ static const int kCapacityOffset = |
+ FixedArray::kHeaderSize + |
+ SymbolTable::kCapacityIndex * kPointerSize; |
+ __ mov(mask, FieldOperand(symbol_table, kCapacityOffset)); |
+ __ SmiUntag(mask); |
+ __ sub(Operand(mask), Immediate(1)); |
+ |
+ // Registers |
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1. |
+ // hash: hash of two character string |
+ // symbol_table: symbol table |
+ // mask: capacity mask |
+ // scratch: - |
+ |
+ // Perform a number of probes in the symbol table. |
+ static const int kProbes = 4; |
+ Label found_in_symbol_table; |
+ Label next_probe[kProbes], next_probe_pop_mask[kProbes]; |
+ for (int i = 0; i < kProbes; i++) { |
+ // Calculate entry in symbol table. |
+ __ mov(scratch, hash); |
+ if (i > 0) { |
+ __ add(Operand(scratch), Immediate(SymbolTable::GetProbeOffset(i))); |
+ } |
+ __ and_(scratch, Operand(mask)); |
+ |
+ // Load the entry from the symble table. |
+ Register candidate = scratch; // Scratch register contains candidate. |
+ ASSERT_EQ(1, SymbolTableShape::kEntrySize); |
+ static const int kFirstElementOffset = |
+ FixedArray::kHeaderSize + |
+ SymbolTable::kPrefixStartIndex * kPointerSize + |
+ SymbolTableShape::kPrefixSize * kPointerSize; |
+ __ mov(candidate, |
+ FieldOperand(symbol_table, |
+ scratch, |
+ times_pointer_size, |
+ kFirstElementOffset)); |
+ |
+ // If entry is undefined no string with this hash can be found. |
+ __ cmp(candidate, Factory::undefined_value()); |
+ __ j(equal, not_found); |
+ |
+ // If length is not 2 the string is not a candidate. |
+ __ cmp(FieldOperand(candidate, String::kLengthOffset), Immediate(2)); |
+ __ j(not_equal, &next_probe[i]); |
+ |
+ // As we are out of registers save the mask on the stack and use that as a |
+ // temporary. |
+ __ push(mask); |
+ Register temp = mask; |
+ |
+ // Check that the candidate is a non-external ascii string. |
+ __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset)); |
+ __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset)); |
+ __ JumpIfInstanceTypeIsNotSequentialAscii( |
+ temp, temp, &next_probe_pop_mask[i]); |
+ |
+ // Check if the two characters match. |
+ __ mov(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize)); |
+ __ and_(temp, 0x0000ffff); |
+ __ cmp(chars, Operand(temp)); |
+ __ j(equal, &found_in_symbol_table); |
+ __ bind(&next_probe_pop_mask[i]); |
+ __ pop(mask); |
+ __ bind(&next_probe[i]); |
+ } |
+ |
+ // No matching 2 character string found by probing. |
+ __ jmp(not_found); |
+ |
+ // Scratch register contains result when we fall through to here. |
+ Register result = scratch; |
+ __ bind(&found_in_symbol_table); |
+ __ pop(mask); // Pop temporally saved mask from the stack. |
+ if (!result.is(eax)) { |
+ __ mov(eax, result); |
+ } |
+} |
+ |
+ |
+void StringStubBase::GenerateHashInit(MacroAssembler* masm, |
+ Register hash, |
+ Register character, |
+ Register scratch) { |
+ // hash = character + (character << 10); |
+ __ mov(hash, character); |
+ __ shl(hash, 10); |
+ __ add(hash, Operand(character)); |
+ // hash ^= hash >> 6; |
+ __ mov(scratch, hash); |
+ __ sar(scratch, 6); |
+ __ xor_(hash, Operand(scratch)); |
+} |
+ |
+ |
+void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm, |
+ Register hash, |
+ Register character, |
+ Register scratch) { |
+ // hash += character; |
+ __ add(hash, Operand(character)); |
+ // hash += hash << 10; |
+ __ mov(scratch, hash); |
+ __ shl(scratch, 10); |
+ __ add(hash, Operand(scratch)); |
+ // hash ^= hash >> 6; |
+ __ mov(scratch, hash); |
+ __ sar(scratch, 6); |
+ __ xor_(hash, Operand(scratch)); |
+} |
+ |
+ |
+void StringStubBase::GenerateHashGetHash(MacroAssembler* masm, |
+ Register hash, |
+ Register scratch) { |
+ // hash += hash << 3; |
+ __ mov(scratch, hash); |
+ __ shl(scratch, 3); |
+ __ add(hash, Operand(scratch)); |
+ // hash ^= hash >> 11; |
+ __ mov(scratch, hash); |
+ __ sar(scratch, 11); |
+ __ xor_(hash, Operand(scratch)); |
+ // hash += hash << 15; |
+ __ mov(scratch, hash); |
+ __ shl(scratch, 15); |
+ __ add(hash, Operand(scratch)); |
+ |
+ // if (hash == 0) hash = 27; |
+ Label hash_not_zero; |
+ __ test(hash, Operand(hash)); |
+ __ j(not_zero, &hash_not_zero); |
+ __ mov(hash, Immediate(27)); |
+ __ bind(&hash_not_zero); |
+} |
+ |
+ |
void SubStringStub::Generate(MacroAssembler* masm) { |
Label runtime; |
@@ -9891,26 +10099,55 @@ |
// eax: string |
// ebx: instance type |
// Calculate length of sub string using the smi values. |
- __ mov(ecx, Operand(esp, 1 * kPointerSize)); // to |
+ Label result_longer_than_two; |
+ __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index. |
__ test(ecx, Immediate(kSmiTagMask)); |
__ j(not_zero, &runtime); |
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // from |
+ __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index. |
__ test(edx, Immediate(kSmiTagMask)); |
__ j(not_zero, &runtime); |
__ sub(ecx, Operand(edx)); |
- // Handle sub-strings of length 2 and less in the runtime system. |
+ // Special handling of sub-strings of length 1 and 2. One character strings |
+ // are handled in the runtime system (looked up in the single character |
+ // cache). Two character strings are looked for in the symbol cache. |
__ SmiUntag(ecx); // Result length is no longer smi. |
__ cmp(ecx, 2); |
- __ j(below_equal, &runtime); |
+ __ j(greater, &result_longer_than_two); |
+ __ j(less, &runtime); |
+ // Sub string of length 2 requested. |
// eax: string |
// ebx: instance type |
+ // ecx: sub string length (value is 2) |
+ // edx: from index (smi) |
+ __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &runtime); |
+ |
+ // Get the two characters forming the sub string. |
+ __ SmiUntag(edx); // From index is no longer smi. |
+ __ movzx_b(ebx, FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize)); |
+ __ movzx_b(ecx, |
+ FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1)); |
+ |
+ // Try to lookup two character string in symbol table. |
+ Label make_two_character_string; |
+ GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi, |
+ &make_two_character_string); |
+ __ ret(2 * kPointerSize); |
+ |
+ __ bind(&make_two_character_string); |
+ // Setup registers for allocating the two character string. |
+ __ mov(eax, Operand(esp, 3 * kPointerSize)); |
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); |
+ __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); |
+ __ Set(ecx, Immediate(2)); |
+ |
+ __ bind(&result_longer_than_two); |
+ // eax: string |
+ // ebx: instance type |
// ecx: result string length |
// Check for flat ascii string |
Label non_ascii_flat; |
- __ and_(ebx, kStringRepresentationMask | kStringEncodingMask); |
- __ cmp(ebx, kSeqStringTag | kAsciiStringTag); |
- __ j(not_equal, &non_ascii_flat); |
+ __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &non_ascii_flat); |
// Allocate the result. |
__ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime); |