| 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));
 | 
| 
 |