Port string keyed load IC improvements (r4444) to x64.
authorpodivilov@chromium.org <podivilov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 28 Apr 2010 17:16:51 +0000 (17:16 +0000)
committerpodivilov@chromium.org <podivilov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 28 Apr 2010 17:16:51 +0000 (17:16 +0000)
http://compute1.aar:9013/golem/r4502-v8-podivilov-string-char-at2-x64-vs-4502-v8-x64.html
Review URL: http://codereview.chromium.org/1750017

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4542 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/ia32/ic-ia32.cc
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
src/x64/ic-x64.cc
test/mjsunit/string-index.js

index cd6a30e..64bc1d6 100644 (file)
@@ -5950,6 +5950,7 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
                                        result.reg(),
                                        &slow_case,
                                        &slow_case,
+                                       &slow_case,
                                        &slow_case);
   __ jmp(&exit);
 
@@ -12048,7 +12049,8 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
                                           Register scratch,
                                           Register result,
                                           Label* receiver_not_string,
-                                          Label* index_not_positive_smi,
+                                          Label* index_not_smi,
+                                          Label* index_out_of_range,
                                           Label* slow_case) {
   Label not_a_flat_string;
   Label try_again_with_new_string;
@@ -12067,11 +12069,10 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
   __ test(result, Immediate(kIsNotStringMask));
   __ j(not_zero, receiver_not_string);
 
-  // If the index is negative or non-smi trigger the non-positive-smi
-  // case.
+  // If the index is non-smi trigger the non-smi case.
   ASSERT(kSmiTag == 0);
-  __ test(index, Immediate(kSmiTagMask | kSmiSignMask));
-  __ j(not_zero, index_not_positive_smi);
+  __ test(index, Immediate(kSmiTagMask));
+  __ j(not_zero, index_not_smi);
 
   // Put untagged index into scratch register.
   __ mov(scratch, index);
@@ -12079,13 +12080,13 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
 
   // Check for index out of range.
   __ cmp(scratch, FieldOperand(object, String::kLengthOffset));
-  __ j(greater_equal, slow_case);
+  __ j(above_equal, index_out_of_range);
 
   __ bind(&try_again_with_new_string);
   // ----------- S t a t e -------------
   //  -- object  : string to access
   //  -- result  : instance type of the string
-  //  -- scratch : positive smi index < length
+  //  -- scratch : non-negative index < length
   // -----------------------------------
 
   // We need special handling for non-flat strings.
@@ -12099,7 +12100,7 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
   __ j(not_zero, &ascii_string);
 
   // 2-byte string.
-  // Load the 2-byte character code into the temp register.
+  // Load the 2-byte character code into the result register.
   __ movzx_w(result, FieldOperand(object,
                                   scratch, times_2,
                                   SeqTwoByteString::kHeaderSize));
@@ -12127,7 +12128,7 @@ void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
 
   // ASCII string.
   __ bind(&ascii_string);
-  // Load the byte into the temp register.
+  // Load the byte into the result register.
   __ movzx_b(result, FieldOperand(object,
                                   scratch, times_1,
                                   SeqAsciiString::kHeaderSize));
index b3db0c4..16e827e 100644 (file)
@@ -886,14 +886,15 @@ class GenericBinaryOpStub: public CodeStub {
 class StringHelper : public AllStatic {
  public:
   // Generates fast code for getting a char code out of a string
-  // object at the given index. May bail out for three reasons (in the
+  // object at the given index. May bail out for four reasons (in the
   // listed order):
   //   * Receiver is not a string (receiver_not_string label).
-  //   * Index is not a positive smi (index_not_positive_smi label).
+  //   * Index is not a smi (index_not_smi label).
+  //   * Index is out of range (index_out_of_range).
   //   * Some other reason (slow_case label). In this case it's
   //     guaranteed that the above conditions are not violated,
   //     e.g. it's safe to assume the receiver is a string and the
-  //     index is a positive smi.
+  //     index is a non-negative smi < length.
   // When successful, object, index, and scratch are clobbered.
   // Otherwise, scratch and result are clobbered.
   static void GenerateFastCharCodeAt(MacroAssembler* masm,
@@ -902,7 +903,8 @@ class StringHelper : public AllStatic {
                                      Register scratch,
                                      Register result,
                                      Label* receiver_not_string,
-                                     Label* index_not_positive_smi,
+                                     Label* index_not_smi,
+                                     Label* index_out_of_range,
                                      Label* slow_case);
 
   // Generates code for creating a one-char string from the given char
index 34b32be..bc7a33c 100644 (file)
@@ -496,7 +496,8 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
   //  -- esp[0] : return address
   // -----------------------------------
   Label miss;
-  Label not_positive_smi;
+  Label index_not_smi;
+  Label index_out_of_range;
   Label slow_char_code;
   Label got_char_code;
 
@@ -511,7 +512,8 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
                                        scratch,
                                        code,
                                        &miss,  // When not a string.
-                                       &not_positive_smi,
+                                       &index_not_smi,
+                                       &index_out_of_range,
                                        &slow_char_code);
   // If we didn't bail out, code register contains smi tagged char
   // code.
@@ -521,14 +523,9 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
   __ Abort("Unexpected fall-through from char from code tail call");
 #endif
 
-  // Check if key is a smi or a heap number.
-  __ bind(&not_positive_smi);
-  ASSERT(kSmiTag == 0);
-  __ test(index, Immediate(kSmiTagMask));
-  __ j(zero, &slow_char_code);
-  __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
-  __ cmp(ecx, Factory::heap_number_map());
-  __ j(not_equal, &miss);
+  // Check if key is a heap number.
+  __ bind(&index_not_smi);
+  __ CheckMap(index, Factory::heap_number_map(), &miss, true);
 
   // Push receiver and key on the stack (now that we know they are a
   // string and a number), and call runtime.
@@ -553,6 +550,7 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
   }
   __ cmp(code, Factory::nan_value());
   __ j(not_equal, &got_char_code);
+  __ bind(&index_out_of_range);
   __ Set(eax, Immediate(Factory::undefined_value()));
   __ ret(0);
 
index 3e21f0c..ecf7154 100644 (file)
@@ -3863,43 +3863,11 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
   Comment(masm_, "[ GenerateFastCharCodeAt");
   ASSERT(args->length() == 2);
 
-  Label slow_case;
-  Label end;
-  Label not_a_flat_string;
-  Label try_again_with_new_string;
-  Label ascii_string;
-  Label got_char_code;
-
   Load(args->at(0));
   Load(args->at(1));
   Result index = frame_->Pop();
   Result object = frame_->Pop();
 
-  // Get register rcx to use as shift amount later.
-  Result shift_amount;
-  if (object.is_register() && object.reg().is(rcx)) {
-    Result fresh = allocator_->Allocate();
-    shift_amount = object;
-    object = fresh;
-    __ movq(object.reg(), rcx);
-  }
-  if (index.is_register() && index.reg().is(rcx)) {
-    Result fresh = allocator_->Allocate();
-    shift_amount = index;
-    index = fresh;
-    __ movq(index.reg(), rcx);
-  }
-  // There could be references to ecx in the frame. Allocating will
-  // spill them, otherwise spill explicitly.
-  if (shift_amount.is_valid()) {
-    frame_->Spill(rcx);
-  } else {
-    shift_amount = allocator()->Allocate(rcx);
-  }
-  ASSERT(shift_amount.is_register());
-  ASSERT(shift_amount.reg().is(rcx));
-  ASSERT(allocator_->count(rcx) == 1);
-
   // We will mutate the index register and possibly the object register.
   // The case where they are somehow the same register is handled
   // because we only mutate them in the case where the receiver is a
@@ -3909,89 +3877,34 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
   frame_->Spill(object.reg());
   frame_->Spill(index.reg());
 
-  // We need a single extra temporary register.
-  Result temp = allocator()->Allocate();
-  ASSERT(temp.is_valid());
+  // We need two extra registers.
+  Result result = allocator()->Allocate();
+  ASSERT(result.is_valid());
+  Result scratch = allocator()->Allocate();
+  ASSERT(scratch.is_valid());
 
   // There is no virtual frame effect from here up to the final result
   // push.
-
-  // If the receiver is a smi trigger the slow case.
-  __ JumpIfSmi(object.reg(), &slow_case);
-
-  // If the index is negative or non-smi trigger the slow case.
-  __ JumpIfNotPositiveSmi(index.reg(), &slow_case);
-
-  // Untag the index.
-  __ SmiToInteger32(index.reg(), index.reg());
-
-  __ bind(&try_again_with_new_string);
-  // Fetch the instance type of the receiver into rcx.
-  __ movq(rcx, FieldOperand(object.reg(), HeapObject::kMapOffset));
-  __ movzxbl(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset));
-  // If the receiver is not a string trigger the slow case.
-  __ testb(rcx, Immediate(kIsNotStringMask));
-  __ j(not_zero, &slow_case);
-
-  // Check for index out of range.
-  __ cmpl(index.reg(), FieldOperand(object.reg(), String::kLengthOffset));
-  __ j(greater_equal, &slow_case);
-  // Reload the instance type (into the temp register this time)..
-  __ movq(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
-  __ movzxbl(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
-
-  // We need special handling for non-flat strings.
-  ASSERT_EQ(0, kSeqStringTag);
-  __ testb(temp.reg(), Immediate(kStringRepresentationMask));
-  __ j(not_zero, &not_a_flat_string);
-  // Check for 1-byte or 2-byte string.
-  ASSERT_EQ(0, kTwoByteStringTag);
-  __ testb(temp.reg(), Immediate(kStringEncodingMask));
-  __ j(not_zero, &ascii_string);
-
-  // 2-byte string.
-  // Load the 2-byte character code into the temp register.
-  __ movzxwl(temp.reg(), FieldOperand(object.reg(),
-                                      index.reg(),
-                                      times_2,
-                                      SeqTwoByteString::kHeaderSize));
-  __ jmp(&got_char_code);
-
-  // ASCII string.
-  __ bind(&ascii_string);
-  // Load the byte into the temp register.
-  __ movzxbl(temp.reg(), FieldOperand(object.reg(),
-                                      index.reg(),
-                                      times_1,
-                                      SeqAsciiString::kHeaderSize));
-  __ bind(&got_char_code);
-  __ Integer32ToSmi(temp.reg(), temp.reg());
-  __ jmp(&end);
-
-  // Handle non-flat strings.
-  __ bind(&not_a_flat_string);
-  __ and_(temp.reg(), Immediate(kStringRepresentationMask));
-  __ cmpb(temp.reg(), Immediate(kConsStringTag));
-  __ j(not_equal, &slow_case);
-
-  // ConsString.
-  // Check that the right hand side is the empty string (ie if this is really a
-  // flat string in a cons string).  If that is not the case we would rather go
-  // to the runtime system now, to flatten the string.
-  __ movq(temp.reg(), FieldOperand(object.reg(), ConsString::kSecondOffset));
-  __ CompareRoot(temp.reg(), Heap::kEmptyStringRootIndex);
-  __ j(not_equal, &slow_case);
-  // Get the first of the two strings.
-  __ movq(object.reg(), FieldOperand(object.reg(), ConsString::kFirstOffset));
-  __ jmp(&try_again_with_new_string);
+  Label slow_case;
+  Label exit;
+  StringHelper::GenerateFastCharCodeAt(masm_,
+                                       object.reg(),
+                                       index.reg(),
+                                       scratch.reg(),
+                                       result.reg(),
+                                       &slow_case,
+                                       &slow_case,
+                                       &slow_case,
+                                       &slow_case);
+  __ jmp(&exit);
 
   __ bind(&slow_case);
   // Move the undefined value into the result register, which will
   // trigger the slow case.
-  __ LoadRoot(temp.reg(), Heap::kUndefinedValueRootIndex);
+  __ LoadRoot(result.reg(), Heap::kUndefinedValueRootIndex);
 
-  __ bind(&end);
-  frame_->Push(&temp);
+  __ bind(&exit);
+  frame_->Push(&result);
 }
 
 
@@ -4000,41 +3913,25 @@ void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
   ASSERT(args->length() == 1);
 
   Load(args->at(0));
+
   Result code = frame_->Pop();
   code.ToRegister();
   ASSERT(code.is_valid());
 
-  Result temp = allocator()->Allocate();
-  ASSERT(temp.is_valid());
-
-  JumpTarget slow_case;
-  JumpTarget exit;
-
-  // Fast case of Heap::LookupSingleCharacterStringFromCode.
-  Condition is_smi = __ CheckSmi(code.reg());
-  slow_case.Branch(NegateCondition(is_smi), &code, not_taken);
-
-  __ SmiToInteger32(kScratchRegister, code.reg());
-  __ cmpl(kScratchRegister, Immediate(String::kMaxAsciiCharCode));
-  slow_case.Branch(above, &code, not_taken);
-
-  __ Move(temp.reg(), Factory::single_character_string_cache());
-  __ movq(temp.reg(), FieldOperand(temp.reg(),
-                                   kScratchRegister, times_pointer_size,
-                                   FixedArray::kHeaderSize));
-  __ CompareRoot(temp.reg(), Heap::kUndefinedValueRootIndex);
-  slow_case.Branch(equal, &code, not_taken);
-  code.Unuse();
+  // StringHelper::GenerateCharFromCode may do a runtime call.
+  frame_->SpillAll();
 
-  frame_->Push(&temp);
-  exit.Jump();
+  Result result = allocator()->Allocate();
+  ASSERT(result.is_valid());
+  Result scratch = allocator()->Allocate();
+  ASSERT(scratch.is_valid());
 
-  slow_case.Bind(&code);
-  frame_->Push(&code);
-  Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
+  StringHelper::GenerateCharFromCode(masm_,
+                                     code.reg(),
+                                     result.reg(),
+                                     scratch.reg(),
+                                     CALL_FUNCTION);
   frame_->Push(&result);
-
-  exit.Bind();
 }
 
 
@@ -10006,6 +9903,146 @@ const char* CompareStub::GetName() {
 }
 
 
+void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
+                                          Register object,
+                                          Register index,
+                                          Register scratch,
+                                          Register result,
+                                          Label* receiver_not_string,
+                                          Label* index_not_smi,
+                                          Label* index_out_of_range,
+                                          Label* slow_case) {
+  Label not_a_flat_string;
+  Label try_again_with_new_string;
+  Label ascii_string;
+  Label got_char_code;
+
+  // If the receiver is a smi trigger the non-string case.
+  __ JumpIfSmi(object, receiver_not_string);
+
+  // Fetch the instance type of the receiver into result register.
+  __ movq(result, FieldOperand(object, HeapObject::kMapOffset));
+  __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
+  // If the receiver is not a string trigger the non-string case.
+  __ testb(result, Immediate(kIsNotStringMask));
+  __ j(not_zero, receiver_not_string);
+
+  // If the index is non-smi trigger the non-smi case.
+  __ JumpIfNotSmi(index, index_not_smi);
+
+  // Put untagged index into scratch register.
+  __ SmiToInteger32(scratch, index);
+
+  // Check for index out of range.
+  __ cmpl(scratch, FieldOperand(object, String::kLengthOffset));
+  __ j(above_equal, index_out_of_range);
+
+  __ bind(&try_again_with_new_string);
+  // ----------- S t a t e -------------
+  //  -- object  : string to access
+  //  -- result  : instance type of the string
+  //  -- scratch : non-negative index < length
+  // -----------------------------------
+
+  // We need special handling for non-flat strings.
+  ASSERT_EQ(0, kSeqStringTag);
+  __ testb(result, Immediate(kStringRepresentationMask));
+  __ j(not_zero, &not_a_flat_string);
+
+  // Check for 1-byte or 2-byte string.
+  ASSERT_EQ(0, kTwoByteStringTag);
+  __ testb(result, Immediate(kStringEncodingMask));
+  __ j(not_zero, &ascii_string);
+
+  // 2-byte string.
+  // Load the 2-byte character code into the result register.
+  __ movzxwl(result, FieldOperand(object,
+                                  scratch,
+                                  times_2,
+                                  SeqTwoByteString::kHeaderSize));
+  __ jmp(&got_char_code);
+
+  // Handle non-flat strings.
+  __ bind(&not_a_flat_string);
+  __ and_(result, Immediate(kStringRepresentationMask));
+  __ cmpb(result, Immediate(kConsStringTag));
+  __ j(not_equal, slow_case);
+
+  // ConsString.
+  // Check that the right hand side is the empty string (ie if this is really a
+  // flat string in a cons string).  If that is not the case we would rather go
+  // to the runtime system now, to flatten the string.
+  __ movq(result, FieldOperand(object, ConsString::kSecondOffset));
+  __ CompareRoot(result, Heap::kEmptyStringRootIndex);
+  __ j(not_equal, slow_case);
+  // Get the first of the two strings and load its instance type.
+  __ movq(object, FieldOperand(object, ConsString::kFirstOffset));
+  __ movq(result, FieldOperand(object, HeapObject::kMapOffset));
+  __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
+  __ jmp(&try_again_with_new_string);
+
+  // ASCII string.
+  __ bind(&ascii_string);
+  // Load the byte into the result register.
+  __ movzxbl(result, FieldOperand(object,
+                                  scratch,
+                                  times_1,
+                                  SeqAsciiString::kHeaderSize));
+  __ bind(&got_char_code);
+  __ Integer32ToSmi(result, result);
+}
+
+
+void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
+                                        Register code,
+                                        Register result,
+                                        Register scratch,
+                                        InvokeFlag flag) {
+  ASSERT(!code.is(result));
+
+  Label slow_case;
+  Label exit;
+
+  // Fast case of Heap::LookupSingleCharacterStringFromCode.
+  __ JumpIfNotSmi(code, &slow_case);
+  __ SmiToInteger32(scratch, code);
+  __ cmpl(scratch, Immediate(String::kMaxAsciiCharCode));
+  __ j(above, &slow_case);
+
+  __ Move(result, Factory::single_character_string_cache());
+  __ movq(result, FieldOperand(result,
+                               scratch,
+                               times_pointer_size,
+                               FixedArray::kHeaderSize));
+
+  __ CompareRoot(result, Heap::kUndefinedValueRootIndex);
+  __ j(equal, &slow_case);
+  __ jmp(&exit);
+
+  __ bind(&slow_case);
+  if (flag == CALL_FUNCTION) {
+    __ push(code);
+    __ CallRuntime(Runtime::kCharFromCode, 1);
+    if (!result.is(rax)) {
+      __ movq(result, rax);
+    }
+  } else {
+    ASSERT(flag == JUMP_FUNCTION);
+    ASSERT(result.is(rax));
+    __ pop(rax);  // Save return address.
+    __ push(code);
+    __ push(rax);  // Restore return address.
+    __ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
+  }
+
+  __ bind(&exit);
+  if (flag == JUMP_FUNCTION) {
+    ASSERT(result.is(rax));
+    __ ret(0);
+  }
+}
+
+
 void StringAddStub::Generate(MacroAssembler* masm) {
   Label string_add_runtime;
 
@@ -10086,8 +10123,8 @@ void StringAddStub::Generate(MacroAssembler* masm) {
   // 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, rbx, rcx, r14, r12, rdi, r15,
-                                       &make_two_character_string);
+  StringHelper::GenerateTwoCharacterSymbolTableProbe(
+      masm, rbx, rcx, r14, r12, rdi, r15, &make_two_character_string);
   __ IncrementCounter(&Counters::string_add_native, 1);
   __ ret(2 * kPointerSize);
 
@@ -10178,7 +10215,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
   // rcx: first character of result
   // rdx: second string
   // rdi: length of first argument
-  GenerateCopyCharacters(masm, rcx, rax, rdi, true);
+  StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, true);
   // Locate first character of second argument.
   __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
   __ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
@@ -10186,7 +10223,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
   // rcx: next character of result
   // rdx: first char of second argument
   // rdi: length of second argument
-  GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
+  StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
   __ movq(rax, rbx);
   __ IncrementCounter(&Counters::string_add_native, 1);
   __ ret(2 * kPointerSize);
@@ -10215,7 +10252,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
   // rcx: first character of result
   // rdx: second argument
   // rdi: length of first argument
-  GenerateCopyCharacters(masm, rcx, rax, rdi, false);
+  StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, false);
   // Locate first character of second argument.
   __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
   __ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
@@ -10223,7 +10260,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
   // rcx: next character of result
   // rdx: first char of second argument
   // rdi: length of second argument
-  GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
+  StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
   __ movq(rax, rbx);
   __ IncrementCounter(&Counters::string_add_native, 1);
   __ ret(2 * kPointerSize);
@@ -10234,11 +10271,11 @@ void StringAddStub::Generate(MacroAssembler* masm) {
 }
 
 
-void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
-                                            Register dest,
-                                            Register src,
-                                            Register count,
-                                            bool ascii) {
+void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
+                                          Register dest,
+                                          Register src,
+                                          Register count,
+                                          bool ascii) {
   Label loop;
   __ bind(&loop);
   // This loop just copies one character at a time, as it is only used for very
@@ -10259,11 +10296,11 @@ void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
 }
 
 
-void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
-                                               Register dest,
-                                               Register src,
-                                               Register count,
-                                               bool ascii) {
+void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
+                                             Register dest,
+                                             Register src,
+                                             Register count,
+                                             bool ascii) {
   // Copy characters using rep movs of doublewords. Align destination on 4 byte
   // boundary before starting rep movs. Copy remaining characters after running
   // rep movs.
@@ -10314,14 +10351,14 @@ void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
   __ bind(&done);
 }
 
-void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
-                                                          Register c1,
-                                                          Register c2,
-                                                          Register scratch1,
-                                                          Register scratch2,
-                                                          Register scratch3,
-                                                          Register scratch4,
-                                                          Label* not_found) {
+void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+                                                        Register c1,
+                                                        Register c2,
+                                                        Register scratch1,
+                                                        Register scratch2,
+                                                        Register scratch3,
+                                                        Register scratch4,
+                                                        Label* not_found) {
   // Register scratch3 is the general scratch register in this function.
   Register scratch = scratch3;
 
@@ -10432,10 +10469,10 @@ void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
 }
 
 
-void StringStubBase::GenerateHashInit(MacroAssembler* masm,
-                                      Register hash,
-                                      Register character,
-                                      Register scratch) {
+void StringHelper::GenerateHashInit(MacroAssembler* masm,
+                                    Register hash,
+                                    Register character,
+                                    Register scratch) {
   // hash = character + (character << 10);
   __ movl(hash, character);
   __ shll(hash, Immediate(10));
@@ -10447,10 +10484,10 @@ void StringStubBase::GenerateHashInit(MacroAssembler* masm,
 }
 
 
-void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
-                                              Register hash,
-                                              Register character,
-                                              Register scratch) {
+void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
+                                            Register hash,
+                                            Register character,
+                                            Register scratch) {
   // hash += character;
   __ addl(hash, character);
   // hash += hash << 10;
@@ -10464,9 +10501,9 @@ void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
 }
 
 
-void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
-                                         Register hash,
-                                         Register scratch) {
+void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
+                                       Register hash,
+                                       Register scratch) {
   // hash += hash << 3;
   __ movl(scratch, hash);
   __ shll(scratch, Immediate(3));
@@ -10543,8 +10580,8 @@ void SubStringStub::Generate(MacroAssembler* masm) {
 
   // Try to lookup two character string in symbol table.
   Label make_two_character_string;
-  GenerateTwoCharacterSymbolTableProbe(masm, rbx, rcx, rax, rdx, rdi, r14,
-                                       &make_two_character_string);
+  StringHelper::GenerateTwoCharacterSymbolTableProbe(
+      masm, rbx, rcx, rax, rdx, rdi, r14, &make_two_character_string);
   __ ret(3 * kPointerSize);
 
   __ bind(&make_two_character_string);
@@ -10585,7 +10622,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
   // rdx: original value of rsi
   // rdi: first character of result
   // rsi: character of sub string start
-  GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true);
+  StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true);
   __ movq(rsi, rdx);  // Restore rsi.
   __ IncrementCounter(&Counters::sub_string_native, 1);
   __ ret(kArgumentsSize);
@@ -10620,7 +10657,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
   // rdx: original value of rsi
   // rdi: first character of result
   // rsi: character of sub string start
-  GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false);
+  StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false);
   __ movq(rsi, rdx);  // Restore esi.
   __ IncrementCounter(&Counters::sub_string_native, 1);
   __ ret(kArgumentsSize);
index a894a5f..397419c 100644 (file)
@@ -816,14 +816,45 @@ class GenericBinaryOpStub: public CodeStub {
   }
 };
 
-
-class StringStubBase: public CodeStub {
+class StringHelper : public AllStatic {
  public:
+  // Generates fast code for getting a char code out of a string
+  // object at the given index. May bail out for four reasons (in the
+  // listed order):
+  //   * Receiver is not a string (receiver_not_string label).
+  //   * Index is not a smi (index_not_smi label).
+  //   * Index is out of range (index_out_of_range).
+  //   * Some other reason (slow_case label). In this case it's
+  //     guaranteed that the above conditions are not violated,
+  //     e.g. it's safe to assume the receiver is a string and the
+  //     index is a non-negative smi < length.
+  // When successful, object, index, and scratch are clobbered.
+  // Otherwise, scratch and result are clobbered.
+  static void GenerateFastCharCodeAt(MacroAssembler* masm,
+                                     Register object,
+                                     Register index,
+                                     Register scratch,
+                                     Register result,
+                                     Label* receiver_not_string,
+                                     Label* index_not_smi,
+                                     Label* index_out_of_range,
+                                     Label* slow_case);
+
+  // Generates code for creating a one-char string from the given char
+  // code. May do a runtime call, so any register can be clobbered
+  // and, if the given invoke flag specifies a call, an internal frame
+  // is required. In tail call mode the result must be rax register.
+  static void GenerateCharFromCode(MacroAssembler* masm,
+                                   Register code,
+                                   Register result,
+                                   Register scratch,
+                                   InvokeFlag flag);
+
   // Generate code for copying characters using a simple loop. This should only
   // be used in places where the number of characters is small and the
   // additional setup and checking in GenerateCopyCharactersREP adds too much
   // overhead. Copying of overlapping regions is not supported.
-  void GenerateCopyCharacters(MacroAssembler* masm,
+  static void GenerateCopyCharacters(MacroAssembler* masm,
                               Register dest,
                               Register src,
                               Register count,
@@ -832,7 +863,7 @@ class StringStubBase: public CodeStub {
   // Generate code for copying characters using the rep movs instruction.
   // Copies rcx characters from rsi to rdi. Copying of overlapping regions is
   // not supported.
-  void GenerateCopyCharactersREP(MacroAssembler* masm,
+  static void GenerateCopyCharactersREP(MacroAssembler* masm,
                                  Register dest,     // Must be rdi.
                                  Register src,      // Must be rsi.
                                  Register count,    // Must be rcx.
@@ -843,7 +874,7 @@ class StringStubBase: public CodeStub {
   // not found by probing a jump to the label not_found is performed. This jump
   // does not guarantee that the string is not in the symbol table. If the
   // string is found the code falls through with the string in register rax.
-  void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+  static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
                                             Register c1,
                                             Register c2,
                                             Register scratch1,
@@ -853,17 +884,19 @@ class StringStubBase: public CodeStub {
                                             Label* not_found);
 
   // Generate string hash.
-  void GenerateHashInit(MacroAssembler* masm,
+  static void GenerateHashInit(MacroAssembler* masm,
                         Register hash,
                         Register character,
                         Register scratch);
-  void GenerateHashAddCharacter(MacroAssembler* masm,
+  static void GenerateHashAddCharacter(MacroAssembler* masm,
                                 Register hash,
                                 Register character,
                                 Register scratch);
-  void GenerateHashGetHash(MacroAssembler* masm,
+  static void GenerateHashGetHash(MacroAssembler* masm,
                            Register hash,
                            Register scratch);
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
 };
 
 
@@ -874,7 +907,7 @@ enum StringAddFlags {
 };
 
 
-class StringAddStub: public StringStubBase {
+class StringAddStub: public CodeStub {
  public:
   explicit StringAddStub(StringAddFlags flags) {
     string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
@@ -891,7 +924,7 @@ class StringAddStub: public StringStubBase {
 };
 
 
-class SubStringStub: public StringStubBase {
+class SubStringStub: public CodeStub {
  public:
   SubStringStub() {}
 
index 7bfccd5..88fcfd1 100644 (file)
@@ -523,30 +523,69 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
   //  -- rsp[8] : name
   //  -- rsp[16] : receiver
   // -----------------------------------
+  Label miss;
+  Label index_not_smi;
+  Label index_out_of_range;
+  Label slow_char_code;
+  Label got_char_code;
 
-  Label miss, index_ok;
-
-  // Check that the receiver isn't a smi.
-  __ movq(rcx, Operand(rsp, 2 * kPointerSize));
-  __ JumpIfSmi(rcx, &miss);
+  Register receiver = rdx;
+  Register index = rax;
+  Register code = rbx;
+  Register scratch = rcx;
+
+  __ movq(index, Operand(rsp, 1 * kPointerSize));
+  __ movq(receiver, Operand(rsp, 2 * kPointerSize));
+
+  StringHelper::GenerateFastCharCodeAt(masm,
+                                       receiver,
+                                       index,
+                                       scratch,
+                                       code,
+                                       &miss,  // When not a string.
+                                       &index_not_smi,
+                                       &index_out_of_range,
+                                       &slow_char_code);
+  // If we didn't bail out, code register contains smi tagged char
+  // code.
+  __ bind(&got_char_code);
+  StringHelper::GenerateCharFromCode(masm, code, rax, scratch, JUMP_FUNCTION);
+#ifdef DEBUG
+  __ Abort("Unexpected fall-through from char from code tail call");
+#endif
+
+  // Check if key is a heap number.
+  __ bind(&index_not_smi);
+  __ CompareRoot(FieldOperand(index, HeapObject::kMapOffset),
+                 Heap::kHeapNumberMapRootIndex);
+  __ j(not_equal, &miss);
 
-  // Check that the receiver is a string.
-  Condition is_string = masm->IsObjectStringType(rcx, rax, rbx);
-  __ j(NegateCondition(is_string), &miss);
+  // Push receiver and key on the stack (now that we know they are a
+  // string and a number), and call runtime.
+  __ bind(&slow_char_code);
+  __ EnterInternalFrame();
+  __ push(receiver);
+  __ push(index);
+  __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+  ASSERT(!code.is(rax));
+  __ movq(code, rax);
+  __ LeaveInternalFrame();
 
-  // Check if key is a smi or a heap number.
-  __ movq(rax, Operand(rsp, kPointerSize));
-  __ JumpIfSmi(rax, &index_ok);
-  __ CheckMap(rax, Factory::heap_number_map(), &miss, false);
+  // Check if the runtime call returned NaN char code. If yes, return
+  // undefined. Otherwise, we can continue.
+  if (FLAG_debug_code) {
+    ASSERT(kSmiTag == 0);
+    __ JumpIfSmi(code, &got_char_code);
+    __ CompareRoot(FieldOperand(code, HeapObject::kMapOffset),
+                   Heap::kHeapNumberMapRootIndex);
+    __ Assert(equal, "StringCharCodeAt must return smi or heap number");
+  }
+  __ CompareRoot(code, Heap::kNanValueRootIndex);
+  __ j(not_equal, &got_char_code);
 
-  __ bind(&index_ok);
-  // Duplicate receiver and key since they are expected on the stack after
-  // the KeyedLoadIC call.
-  __ pop(rbx);  // return address
-  __ push(rcx);  // receiver
-  __ push(rax);  // key
-  __ push(rbx);  // return address
-  __ InvokeBuiltin(Builtins::STRING_CHAR_AT, JUMP_FUNCTION);
+  __ bind(&index_out_of_range);
+  __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+  __ ret(0);
 
   __ bind(&miss);
   GenerateMiss(masm);
index 6f11a5b..866faa8 100644 (file)
@@ -195,3 +195,43 @@ for (var i = 0; i < 300; ++i) {
   var actual = str[key];
   assertEquals(expected, actual);
 }
+
+// Test heap number case.
+var keys = [0, Math.floor(2) * 0.5];
+var str = 'ab', arr = ['a', 'b'];
+for (var i = 0; i < 100; ++i) {
+  var index = Math.floor(i / 50);
+  var key = keys[index];
+  var expected = arr[index];
+  var actual = str[key];
+  assertEquals(expected, actual);
+}
+
+// Test out of range case.
+var keys = [0, -1];
+var str = 'ab', arr = ['a', undefined];
+for (var i = 0; i < 100; ++i) {
+  var index = Math.floor(i / 50);
+  var key = keys[index];
+  var expected = arr[index];
+  var actual = str[key];
+  assertEquals(expected, actual);
+}
+
+var keys = [0, 10];
+var str = 'ab', arr = ['a', undefined];
+for (var i = 0; i < 100; ++i) {
+  var index = Math.floor(i / 50);
+  var key = keys[index];
+  var expected = arr[index];
+  var actual = str[key];
+  assertEquals(expected, actual);
+}
+
+// Test two byte string.
+var str = '\u0427', arr = ['\u0427'];
+for (var i = 0; i < 50; ++i) {
+  var expected = arr[0];
+  var actual = str[0];
+  assertEquals(expected, actual);
+}
\ No newline at end of file