| Index: src/arm/codegen-arm.cc
|
| ===================================================================
|
| --- src/arm/codegen-arm.cc (revision 4029)
|
| +++ src/arm/codegen-arm.cc (working copy)
|
| @@ -7249,6 +7249,170 @@
|
| }
|
|
|
|
|
| +void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
|
| + Register c1,
|
| + Register c2,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register scratch3,
|
| + Register scratch4,
|
| + Register scratch5,
|
| + 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;
|
| + __ sub(scratch, c1, Operand(static_cast<int>('0')));
|
| + __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
|
| + __ b(hi, ¬_array_index);
|
| + __ sub(scratch, c2, Operand(static_cast<int>('0')));
|
| + __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
|
| +
|
| + // If check failed combine both characters into single halfword.
|
| + // This is required by the contract of the method: code at the
|
| + // not_found branch expects this combination in c1 register
|
| + __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
|
| + __ b(ls, not_found);
|
| +
|
| + __ bind(¬_array_index);
|
| + // Calculate the two character string hash.
|
| + Register hash = scratch1;
|
| + GenerateHashInit(masm, hash, c1);
|
| + GenerateHashAddCharacter(masm, hash, c2);
|
| + GenerateHashGetHash(masm, hash);
|
| +
|
| + // Collect the two characters in a register.
|
| + Register chars = c1;
|
| + __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
|
| +
|
| + // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
|
| + // hash: hash of two character string.
|
| +
|
| + // Load symbol table
|
| + // Load address of first element of the symbol table.
|
| + Register symbol_table = c2;
|
| + __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
|
| +
|
| + // Load undefined value
|
| + Register undefined = scratch4;
|
| + __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
|
| +
|
| + // Calculate capacity mask from the symbol table capacity.
|
| + Register mask = scratch2;
|
| + __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
|
| + __ mov(mask, Operand(mask, ASR, 1));
|
| + __ sub(mask, mask, Operand(1));
|
| +
|
| + // Calculate untagged address of the first element of the symbol table.
|
| + Register first_symbol_table_element = symbol_table;
|
| + __ add(first_symbol_table_element, symbol_table,
|
| + Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
|
| +
|
| + // Registers
|
| + // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
|
| + // hash: hash of two character string
|
| + // mask: capacity mask
|
| + // first_symbol_table_element: address of the first element of
|
| + // the symbol table
|
| + // scratch: -
|
| +
|
| + // Perform a number of probes in the symbol table.
|
| + static const int kProbes = 4;
|
| + Label found_in_symbol_table;
|
| + Label next_probe[kProbes];
|
| + for (int i = 0; i < kProbes; i++) {
|
| + Register candidate = scratch5; // Scratch register contains candidate.
|
| +
|
| + // Calculate entry in symbol table.
|
| + if (i > 0) {
|
| + __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
|
| + } else {
|
| + __ mov(candidate, hash);
|
| + }
|
| +
|
| + __ and_(candidate, candidate, Operand(mask));
|
| +
|
| + // Load the entry from the symble table.
|
| + ASSERT_EQ(1, SymbolTable::kEntrySize);
|
| + __ ldr(candidate,
|
| + MemOperand(first_symbol_table_element,
|
| + candidate,
|
| + LSL,
|
| + kPointerSizeLog2));
|
| +
|
| + // If entry is undefined no string with this hash can be found.
|
| + __ cmp(candidate, undefined);
|
| + __ b(eq, not_found);
|
| +
|
| + // If length is not 2 the string is not a candidate.
|
| + __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
|
| + __ cmp(scratch, Operand(2));
|
| + __ b(ne, &next_probe[i]);
|
| +
|
| + // Check that the candidate is a non-external ascii string.
|
| + __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
|
| + __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
|
| + __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
|
| + &next_probe[i]);
|
| +
|
| + // Check if the two characters match.
|
| + // Assumes that word load is little endian.
|
| + __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
|
| + __ cmp(chars, scratch);
|
| + __ b(eq, &found_in_symbol_table);
|
| + __ 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);
|
| + if (!result.is(r0)) {
|
| + __ mov(r0, result);
|
| + }
|
| +}
|
| +
|
| +
|
| +void StringStubBase::GenerateHashInit(MacroAssembler* masm,
|
| + Register hash,
|
| + Register character) {
|
| + // hash = character + (character << 10);
|
| + __ add(hash, character, Operand(character, LSL, 10));
|
| + // hash ^= hash >> 6;
|
| + __ eor(hash, hash, Operand(hash, ASR, 6));
|
| +}
|
| +
|
| +
|
| +void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
|
| + Register hash,
|
| + Register character) {
|
| + // hash += character;
|
| + __ add(hash, hash, Operand(character));
|
| + // hash += hash << 10;
|
| + __ add(hash, hash, Operand(hash, LSL, 10));
|
| + // hash ^= hash >> 6;
|
| + __ eor(hash, hash, Operand(hash, ASR, 6));
|
| +}
|
| +
|
| +
|
| +void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
|
| + Register hash) {
|
| + // hash += hash << 3;
|
| + __ add(hash, hash, Operand(hash, LSL, 3));
|
| + // hash ^= hash >> 11;
|
| + __ eor(hash, hash, Operand(hash, ASR, 11));
|
| + // hash += hash << 15;
|
| + __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
|
| +
|
| + // if (hash == 0) hash = 27;
|
| + __ mov(hash, Operand(27), LeaveCC, nz);
|
| +}
|
| +
|
| +
|
| void SubStringStub::Generate(MacroAssembler* masm) {
|
| Label runtime;
|
|
|
| @@ -7284,11 +7448,14 @@
|
|
|
| __ sub(r2, r2, Operand(r3), SetCC);
|
| __ b(mi, &runtime); // Fail if from > to.
|
| - // 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.
|
| __ cmp(r2, Operand(2));
|
| - __ b(le, &runtime);
|
| + __ b(lt, &runtime);
|
|
|
| // r2: length
|
| + // r3: from index (untaged smi)
|
| // r6: from (smi)
|
| // r7: to (smi)
|
|
|
| @@ -7302,6 +7469,7 @@
|
|
|
| // r1: instance type
|
| // r2: length
|
| + // r3: from index (untaged smi)
|
| // r5: string
|
| // r6: from (smi)
|
| // r7: to (smi)
|
| @@ -7328,6 +7496,7 @@
|
|
|
| // r1: instance type.
|
| // r2: length
|
| + // r3: from index (untaged smi)
|
| // r5: string
|
| // r6: from (smi)
|
| // r7: to (smi)
|
| @@ -7337,6 +7506,7 @@
|
|
|
| // r1: instance type.
|
| // r2: result string length.
|
| + // r3: from index (untaged smi)
|
| // r5: string.
|
| // r6: from offset (smi)
|
| // Check for flat ascii string.
|
| @@ -7345,6 +7515,35 @@
|
| ASSERT_EQ(0, kTwoByteStringTag);
|
| __ b(eq, &non_ascii_flat);
|
|
|
| + Label result_longer_than_two;
|
| + __ cmp(r2, Operand(2));
|
| + __ b(gt, &result_longer_than_two);
|
| +
|
| + // Sub string of length 2 requested.
|
| + // Get the two characters forming the sub string.
|
| + __ add(r5, r5, Operand(r3));
|
| + __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
|
| + __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
|
| +
|
| + // Try to lookup two character string in symbol table.
|
| + Label make_two_character_string;
|
| + GenerateTwoCharacterSymbolTableProbe(masm, r3, r4, r1, r5, r6, r7, r9,
|
| + &make_two_character_string);
|
| + __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
|
| + __ add(sp, sp, Operand(3 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + // r2: result string length.
|
| + // r3: two characters combined into halfword in little endian byte order.
|
| + __ bind(&make_two_character_string);
|
| + __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
|
| + __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
| + __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
|
| + __ add(sp, sp, Operand(3 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&result_longer_than_two);
|
| +
|
| // Allocate the result.
|
| __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
|
|
|
| @@ -7553,14 +7752,52 @@
|
| // r4: first string instance type (if string_check_)
|
| // r5: second string instance type (if string_check_)
|
| // 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;
|
| // Adding two lengths can't overflow.
|
| ASSERT(String::kMaxLength * 2 > String::kMaxLength);
|
| __ add(r6, r2, Operand(r3));
|
| // Use the runtime system when adding two one character strings, as it
|
| // contains optimizations for this specific case using the symbol table.
|
| __ cmp(r6, Operand(2));
|
| - __ b(eq, &string_add_runtime);
|
| + __ b(ne, &longer_than_two);
|
| +
|
| + // Check that both strings are non-external ascii strings.
|
| + if (!string_check_) {
|
| + __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| + __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| + __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| + }
|
| + __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
|
| + &string_add_runtime);
|
| +
|
| + // Get the two characters forming the sub string.
|
| + __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
| + __ ldrb(r3, FieldMemOperand(r1, 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;
|
| + GenerateTwoCharacterSymbolTableProbe(masm, r2, r3, r6, r7, r4, r5, r9,
|
| + &make_two_character_string);
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&make_two_character_string);
|
| + // Resulting string has length 2 and first chars of two strings
|
| + // are combined into single halfword in r2 register.
|
| + // So we can fill resulting string without two loops by a single
|
| + // halfword store instruction (which assumes that processor is
|
| + // in a little endian mode)
|
| + __ mov(r6, Operand(2));
|
| + __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
|
| + __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&longer_than_two);
|
| // Check if resulting string will be flat.
|
| __ cmp(r6, Operand(String::kMinNonFlatLength));
|
| __ b(lt, &string_add_flat_result);
|
| @@ -7639,6 +7876,7 @@
|
|
|
| // Both strings are sequential ASCII strings. We also know that they are
|
| // short (since the sum of the lengths is less than kMinNonFlatLength).
|
| + // r6: length of resulting flat string
|
| __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
|
| // Locate first character of result.
|
| __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
|