parent 62e8d5a7
......@@ -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));
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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() {}
......
......@@ -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);
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment