From: podivilov@chromium.org Date: Wed, 28 Apr 2010 17:16:51 +0000 (+0000) Subject: Port string keyed load IC improvements (r4444) to x64. X-Git-Tag: upstream/4.7.83~21910 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1872574f4d0944464fbf2c5027de5c726f0cede6;p=platform%2Fupstream%2Fv8.git Port string keyed load IC improvements (r4444) to x64. 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 --- diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index cd6a30e..64bc1d6 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -5950,6 +5950,7 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList* 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)); diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index b3db0c4..16e827e 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -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 diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 34b32be..bc7a33c 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -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. - ¬_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(¬_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); diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 3e21f0c..ecf7154 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -3863,43 +3863,11 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList* 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* 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, ¬_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(¬_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* 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, ¬_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(¬_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); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index a894a5f..397419c 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -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() {} diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 7bfccd5..88fcfd1 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -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); diff --git a/test/mjsunit/string-index.js b/test/mjsunit/string-index.js index 6f11a5b..866faa8 100644 --- a/test/mjsunit/string-index.js +++ b/test/mjsunit/string-index.js @@ -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