Reapply: Inline fast cases in string keyed load IC.
(Fixed handling of out-of-bounds keys.) String keyed load used to call STRING_CHAR_AT builtin that performs two steps (get a char code, construct a one-char string from the code), both of which have fast cases implemented as inline runtime functions. In this chage most of the code from these functions is extracted to a set of common generator functions in StringStubBase and the fast cases are grouped together in the IC code. Review URL: http://codereview.chromium.org/1582041 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4450 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1e46286091
commit
b8ba4deacc
@ -147,6 +147,9 @@ const int kPointerSizeLog2 = 2;
|
||||
const intptr_t kIntptrSignBit = 0x80000000;
|
||||
#endif
|
||||
|
||||
// Mask for the sign bit in a smi.
|
||||
const intptr_t kSmiSignMask = kIntptrSignBit;
|
||||
|
||||
const int kObjectAlignmentBits = kPointerSizeLog2;
|
||||
const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits;
|
||||
const intptr_t kObjectAlignmentMask = kObjectAlignment - 1;
|
||||
|
@ -2348,7 +2348,7 @@ Result CodeGenerator::ConstantSmiBinaryOperation(
|
||||
smi_value,
|
||||
overwrite_mode);
|
||||
// Check for negative or non-Smi left hand side.
|
||||
__ test(operand->reg(), Immediate(kSmiTagMask | 0x80000000));
|
||||
__ test(operand->reg(), Immediate(kSmiTagMask | kSmiSignMask));
|
||||
deferred->Branch(not_zero);
|
||||
if (int_value < 0) int_value = -int_value;
|
||||
if (int_value == 1) {
|
||||
@ -5901,7 +5901,7 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
|
||||
Result value = frame_->Pop();
|
||||
value.ToRegister();
|
||||
ASSERT(value.is_valid());
|
||||
__ test(value.reg(), Immediate(kSmiTagMask | 0x80000000));
|
||||
__ test(value.reg(), Immediate(kSmiTagMask | kSmiSignMask));
|
||||
value.Unuse();
|
||||
destination()->Split(zero);
|
||||
}
|
||||
@ -5917,43 +5917,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 ecx to use as shift amount later.
|
||||
Result shift_amount;
|
||||
if (object.is_register() && object.reg().is(ecx)) {
|
||||
Result fresh = allocator_->Allocate();
|
||||
shift_amount = object;
|
||||
object = fresh;
|
||||
__ mov(object.reg(), ecx);
|
||||
}
|
||||
if (index.is_register() && index.reg().is(ecx)) {
|
||||
Result fresh = allocator_->Allocate();
|
||||
shift_amount = index;
|
||||
index = fresh;
|
||||
__ mov(index.reg(), ecx);
|
||||
}
|
||||
// There could be references to ecx in the frame. Allocating will
|
||||
// spill them, otherwise spill explicitly.
|
||||
if (shift_amount.is_valid()) {
|
||||
frame_->Spill(ecx);
|
||||
} else {
|
||||
shift_amount = allocator()->Allocate(ecx);
|
||||
}
|
||||
ASSERT(shift_amount.is_register());
|
||||
ASSERT(shift_amount.reg().is(ecx));
|
||||
ASSERT(allocator_->count(ecx) == 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
|
||||
@ -5963,93 +5931,33 @@ 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.
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ test(object.reg(), Immediate(kSmiTagMask));
|
||||
__ j(zero, &slow_case);
|
||||
|
||||
// If the index is negative or non-smi trigger the slow case.
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ test(index.reg(), Immediate(kSmiTagMask | 0x80000000));
|
||||
__ j(not_zero, &slow_case);
|
||||
// Untag the index.
|
||||
__ SmiUntag(index.reg());
|
||||
|
||||
__ bind(&try_again_with_new_string);
|
||||
// Fetch the instance type of the receiver into ecx.
|
||||
__ mov(ecx, FieldOperand(object.reg(), HeapObject::kMapOffset));
|
||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||
// If the receiver is not a string trigger the slow case.
|
||||
__ test(ecx, Immediate(kIsNotStringMask));
|
||||
__ j(not_zero, &slow_case);
|
||||
|
||||
// Fetch the length field into the temporary register.
|
||||
__ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset));
|
||||
// Check for index out of range.
|
||||
__ cmp(index.reg(), Operand(temp.reg()));
|
||||
__ j(greater_equal, &slow_case);
|
||||
// Reload the instance type (into the temp register this time)..
|
||||
__ mov(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
||||
__ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
|
||||
|
||||
// We need special handling for non-flat strings.
|
||||
ASSERT(kSeqStringTag == 0);
|
||||
__ test(temp.reg(), Immediate(kStringRepresentationMask));
|
||||
__ j(not_zero, ¬_a_flat_string);
|
||||
// Check for 1-byte or 2-byte string.
|
||||
__ test(temp.reg(), Immediate(kStringEncodingMask));
|
||||
__ j(not_zero, &ascii_string);
|
||||
|
||||
// 2-byte string.
|
||||
// Load the 2-byte character code into the temp register.
|
||||
__ movzx_w(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.
|
||||
__ movzx_b(temp.reg(), FieldOperand(object.reg(),
|
||||
index.reg(),
|
||||
times_1,
|
||||
SeqAsciiString::kHeaderSize));
|
||||
__ bind(&got_char_code);
|
||||
__ SmiTag(temp.reg());
|
||||
__ jmp(&end);
|
||||
|
||||
// Handle non-flat strings.
|
||||
__ bind(¬_a_flat_string);
|
||||
__ and_(temp.reg(), kStringRepresentationMask);
|
||||
__ cmp(temp.reg(), 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.
|
||||
__ mov(temp.reg(), FieldOperand(object.reg(), ConsString::kSecondOffset));
|
||||
__ cmp(Operand(temp.reg()), Factory::empty_string());
|
||||
__ j(not_equal, &slow_case);
|
||||
// Get the first of the two strings.
|
||||
__ mov(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);
|
||||
__ jmp(&exit);
|
||||
|
||||
__ bind(&slow_case);
|
||||
// Move the undefined value into the result register, which will
|
||||
// trigger the slow case.
|
||||
__ Set(temp.reg(), Immediate(Factory::undefined_value()));
|
||||
__ Set(result.reg(), Immediate(Factory::undefined_value()));
|
||||
|
||||
__ bind(&end);
|
||||
frame_->Push(&temp);
|
||||
__ bind(&exit);
|
||||
frame_->Push(&result);
|
||||
}
|
||||
|
||||
|
||||
@ -6058,46 +5966,22 @@ 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());
|
||||
// StringHelper::GenerateCharFromCode may do a runtime call.
|
||||
frame_->SpillAll();
|
||||
|
||||
JumpTarget slow_case;
|
||||
JumpTarget exit;
|
||||
Result result = allocator()->Allocate();
|
||||
ASSERT(result.is_valid());
|
||||
|
||||
// Fast case of Heap::LookupSingleCharacterStringFromCode.
|
||||
ASSERT(kSmiTag == 0);
|
||||
ASSERT(kSmiShiftSize == 0);
|
||||
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
|
||||
__ test(code.reg(),
|
||||
Immediate(kSmiTagMask |
|
||||
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
|
||||
slow_case.Branch(not_zero, &code, not_taken);
|
||||
|
||||
__ Set(temp.reg(), Immediate(Factory::single_character_string_cache()));
|
||||
ASSERT(kSmiTag == 0);
|
||||
ASSERT(kSmiTagSize == 1);
|
||||
ASSERT(kSmiShiftSize == 0);
|
||||
// At this point code register contains smi tagged ascii char code.
|
||||
__ mov(temp.reg(), FieldOperand(temp.reg(),
|
||||
code.reg(), times_half_pointer_size,
|
||||
FixedArray::kHeaderSize));
|
||||
__ cmp(temp.reg(), Factory::undefined_value());
|
||||
slow_case.Branch(equal, &code, not_taken);
|
||||
code.Unuse();
|
||||
|
||||
frame_->Push(&temp);
|
||||
exit.Jump();
|
||||
|
||||
slow_case.Bind(&code);
|
||||
frame_->Push(&code);
|
||||
Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
|
||||
StringHelper::GenerateCharFromCode(masm_,
|
||||
code.reg(),
|
||||
result.reg(),
|
||||
CALL_FUNCTION);
|
||||
frame_->Push(&result);
|
||||
|
||||
exit.Bind();
|
||||
}
|
||||
|
||||
|
||||
@ -8522,7 +8406,7 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
|
||||
}
|
||||
|
||||
// Check that the key is a non-negative smi.
|
||||
__ test(key.reg(), Immediate(kSmiTagMask | 0x80000000));
|
||||
__ test(key.reg(), Immediate(kSmiTagMask | kSmiSignMask));
|
||||
deferred->Branch(not_zero);
|
||||
|
||||
// Check that the receiver is not a smi.
|
||||
@ -12154,6 +12038,154 @@ const char* CompareStub::GetName() {
|
||||
}
|
||||
|
||||
|
||||
void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
|
||||
Register object,
|
||||
Register index,
|
||||
Register scratch,
|
||||
Register result,
|
||||
Label* receiver_not_string,
|
||||
Label* index_not_positive_smi,
|
||||
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.
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ test(object, Immediate(kSmiTagMask));
|
||||
__ j(zero, receiver_not_string);
|
||||
|
||||
// Fetch the instance type of the receiver into result register.
|
||||
__ mov(result, FieldOperand(object, HeapObject::kMapOffset));
|
||||
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
||||
// If the receiver is not a string trigger the non-string case.
|
||||
__ test(result, Immediate(kIsNotStringMask));
|
||||
__ j(not_zero, receiver_not_string);
|
||||
|
||||
// If the index is negative or non-smi trigger the non-positive-smi
|
||||
// case.
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ test(index, Immediate(kSmiTagMask | kSmiSignMask));
|
||||
__ j(not_zero, index_not_positive_smi);
|
||||
|
||||
// Put untagged index into scratch register.
|
||||
__ mov(scratch, index);
|
||||
__ SmiUntag(scratch);
|
||||
|
||||
// Check for index out of range.
|
||||
__ cmp(scratch, FieldOperand(object, String::kLengthOffset));
|
||||
__ j(greater_equal, slow_case);
|
||||
|
||||
__ 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
|
||||
// -----------------------------------
|
||||
|
||||
// We need special handling for non-flat strings.
|
||||
ASSERT(kSeqStringTag == 0);
|
||||
__ test(result, Immediate(kStringRepresentationMask));
|
||||
__ j(not_zero, ¬_a_flat_string);
|
||||
|
||||
// Check for 1-byte or 2-byte string.
|
||||
ASSERT(kAsciiStringTag != 0);
|
||||
__ test(result, Immediate(kStringEncodingMask));
|
||||
__ j(not_zero, &ascii_string);
|
||||
|
||||
// 2-byte string.
|
||||
// Load the 2-byte character code into the temp register.
|
||||
__ movzx_w(result, FieldOperand(object,
|
||||
scratch, times_2,
|
||||
SeqTwoByteString::kHeaderSize));
|
||||
__ jmp(&got_char_code);
|
||||
|
||||
// Handle non-flat strings.
|
||||
__ bind(¬_a_flat_string);
|
||||
__ and_(result, kStringRepresentationMask);
|
||||
__ cmp(result, kConsStringTag);
|
||||
__ j(not_equal, slow_case);
|
||||
|
||||
// ConsString.
|
||||
// Check whether the right hand side is the empty string (i.e. 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.
|
||||
__ mov(result, FieldOperand(object, ConsString::kSecondOffset));
|
||||
__ cmp(Operand(result), Factory::empty_string());
|
||||
__ j(not_equal, slow_case);
|
||||
// Get the first of the two strings and load its instance type.
|
||||
__ mov(object, FieldOperand(object, ConsString::kFirstOffset));
|
||||
__ mov(result, FieldOperand(object, HeapObject::kMapOffset));
|
||||
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
||||
__ jmp(&try_again_with_new_string);
|
||||
|
||||
// ASCII string.
|
||||
__ bind(&ascii_string);
|
||||
// Load the byte into the temp register.
|
||||
__ movzx_b(result, FieldOperand(object,
|
||||
scratch, times_1,
|
||||
SeqAsciiString::kHeaderSize));
|
||||
__ bind(&got_char_code);
|
||||
__ SmiTag(result);
|
||||
}
|
||||
|
||||
|
||||
void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
|
||||
Register code,
|
||||
Register result,
|
||||
InvokeFlag flag) {
|
||||
ASSERT(!code.is(result));
|
||||
|
||||
Label slow_case;
|
||||
Label exit;
|
||||
|
||||
// Fast case of Heap::LookupSingleCharacterStringFromCode.
|
||||
ASSERT(kSmiTag == 0);
|
||||
ASSERT(kSmiShiftSize == 0);
|
||||
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
|
||||
__ test(code,
|
||||
Immediate(kSmiTagMask |
|
||||
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
|
||||
__ j(not_zero, &slow_case, not_taken);
|
||||
|
||||
__ Set(result, Immediate(Factory::single_character_string_cache()));
|
||||
ASSERT(kSmiTag == 0);
|
||||
ASSERT(kSmiTagSize == 1);
|
||||
ASSERT(kSmiShiftSize == 0);
|
||||
// At this point code register contains smi tagged ascii char code.
|
||||
__ mov(result, FieldOperand(result,
|
||||
code, times_half_pointer_size,
|
||||
FixedArray::kHeaderSize));
|
||||
__ cmp(result, Factory::undefined_value());
|
||||
__ j(equal, &slow_case, not_taken);
|
||||
__ jmp(&exit);
|
||||
|
||||
__ bind(&slow_case);
|
||||
if (flag == CALL_FUNCTION) {
|
||||
__ push(code);
|
||||
__ CallRuntime(Runtime::kCharFromCode, 1);
|
||||
if (!result.is(eax)) {
|
||||
__ mov(result, eax);
|
||||
}
|
||||
} else {
|
||||
ASSERT(flag == JUMP_FUNCTION);
|
||||
ASSERT(result.is(eax));
|
||||
__ pop(eax); // Save return address.
|
||||
__ push(code);
|
||||
__ push(eax); // Restore return address.
|
||||
__ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
|
||||
}
|
||||
|
||||
__ bind(&exit);
|
||||
if (flag == JUMP_FUNCTION) {
|
||||
ASSERT(result.is(eax));
|
||||
__ ret(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StringAddStub::Generate(MacroAssembler* masm) {
|
||||
Label string_add_runtime;
|
||||
|
||||
@ -12220,8 +12252,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, ebx, ecx, eax, edx, edi,
|
||||
&make_two_character_string);
|
||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||
masm, ebx, ecx, eax, edx, edi, &make_two_character_string);
|
||||
__ IncrementCounter(&Counters::string_add_native, 1);
|
||||
__ ret(2 * kPointerSize);
|
||||
|
||||
@ -12313,7 +12345,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
||||
// ecx: first character of result
|
||||
// edx: first char of first argument
|
||||
// edi: length of first argument
|
||||
GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
|
||||
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
|
||||
// Load second argument and locate first character.
|
||||
__ mov(edx, Operand(esp, 1 * kPointerSize));
|
||||
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
||||
@ -12322,7 +12354,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
||||
// ecx: next character of result
|
||||
// edx: first char of second argument
|
||||
// edi: length of second argument
|
||||
GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
|
||||
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
|
||||
__ IncrementCounter(&Counters::string_add_native, 1);
|
||||
__ ret(2 * kPointerSize);
|
||||
|
||||
@ -12352,7 +12384,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
||||
// ecx: first character of result
|
||||
// edx: first char of first argument
|
||||
// edi: length of first argument
|
||||
GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
|
||||
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
|
||||
// Load second argument and locate first character.
|
||||
__ mov(edx, Operand(esp, 1 * kPointerSize));
|
||||
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
||||
@ -12361,7 +12393,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
||||
// ecx: next character of result
|
||||
// edx: first char of second argument
|
||||
// edi: length of second argument
|
||||
GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
|
||||
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
|
||||
__ IncrementCounter(&Counters::string_add_native, 1);
|
||||
__ ret(2 * kPointerSize);
|
||||
|
||||
@ -12371,12 +12403,12 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
Register dest,
|
||||
Register src,
|
||||
Register count,
|
||||
Register scratch,
|
||||
bool ascii) {
|
||||
void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
Register dest,
|
||||
Register src,
|
||||
Register count,
|
||||
Register scratch,
|
||||
bool ascii) {
|
||||
Label loop;
|
||||
__ bind(&loop);
|
||||
// This loop just copies one character at a time, as it is only used for very
|
||||
@ -12397,12 +12429,12 @@ void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
|
||||
Register dest,
|
||||
Register src,
|
||||
Register count,
|
||||
Register scratch,
|
||||
bool ascii) {
|
||||
void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
|
||||
Register dest,
|
||||
Register src,
|
||||
Register count,
|
||||
Register scratch,
|
||||
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.
|
||||
@ -12457,13 +12489,13 @@ void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
|
||||
Register c1,
|
||||
Register c2,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
Label* not_found) {
|
||||
void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
|
||||
Register c1,
|
||||
Register c2,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
Label* not_found) {
|
||||
// Register scratch3 is the general scratch register in this function.
|
||||
Register scratch = scratch3;
|
||||
|
||||
@ -12577,10 +12609,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);
|
||||
__ mov(hash, character);
|
||||
__ shl(hash, 10);
|
||||
@ -12592,10 +12624,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;
|
||||
__ add(hash, Operand(character));
|
||||
// hash += hash << 10;
|
||||
@ -12609,9 +12641,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;
|
||||
__ mov(scratch, hash);
|
||||
__ shl(scratch, 3);
|
||||
@ -12685,8 +12717,8 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Try to lookup two character string in symbol table.
|
||||
Label make_two_character_string;
|
||||
GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
|
||||
&make_two_character_string);
|
||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||
masm, ebx, ecx, eax, edx, edi, &make_two_character_string);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
__ bind(&make_two_character_string);
|
||||
@ -12725,7 +12757,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
// edx: original value of esi
|
||||
// edi: first character of result
|
||||
// esi: character of sub string start
|
||||
GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
|
||||
StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
|
||||
__ mov(esi, edx); // Restore esi.
|
||||
__ IncrementCounter(&Counters::sub_string_native, 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
@ -12764,7 +12796,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
// edx: original value of esi
|
||||
// edi: first character of result
|
||||
// esi: character of sub string start
|
||||
GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
|
||||
StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
|
||||
__ mov(esi, edx); // Restore esi.
|
||||
__ IncrementCounter(&Counters::sub_string_native, 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
@ -883,53 +883,85 @@ 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 three 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).
|
||||
// * 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.
|
||||
// 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_positive_smi,
|
||||
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 eax register.
|
||||
static void GenerateCharFromCode(MacroAssembler* masm,
|
||||
Register code,
|
||||
Register result,
|
||||
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,
|
||||
Register dest,
|
||||
Register src,
|
||||
Register count,
|
||||
Register scratch,
|
||||
bool ascii);
|
||||
static void GenerateCopyCharacters(MacroAssembler* masm,
|
||||
Register dest,
|
||||
Register src,
|
||||
Register count,
|
||||
Register scratch,
|
||||
bool ascii);
|
||||
|
||||
// Generate code for copying characters using the rep movs instruction.
|
||||
// Copies ecx characters from esi to edi. Copying of overlapping regions is
|
||||
// not supported.
|
||||
void GenerateCopyCharactersREP(MacroAssembler* masm,
|
||||
Register dest, // Must be edi.
|
||||
Register src, // Must be esi.
|
||||
Register count, // Must be ecx.
|
||||
Register scratch, // Neither of the above.
|
||||
bool ascii);
|
||||
static void GenerateCopyCharactersREP(MacroAssembler* masm,
|
||||
Register dest, // Must be edi.
|
||||
Register src, // Must be esi.
|
||||
Register count, // Must be ecx.
|
||||
Register scratch, // Neither of above.
|
||||
bool ascii);
|
||||
|
||||
// Probe the symbol table for a two character string. If the string is
|
||||
// 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 eax.
|
||||
void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
|
||||
Register c1,
|
||||
Register c2,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
Label* not_found);
|
||||
static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
|
||||
Register c1,
|
||||
Register c2,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
Label* not_found);
|
||||
|
||||
// Generate string hash.
|
||||
void GenerateHashInit(MacroAssembler* masm,
|
||||
Register hash,
|
||||
Register character,
|
||||
Register scratch);
|
||||
void GenerateHashAddCharacter(MacroAssembler* masm,
|
||||
Register hash,
|
||||
Register character,
|
||||
Register scratch);
|
||||
void GenerateHashGetHash(MacroAssembler* masm,
|
||||
Register hash,
|
||||
Register scratch);
|
||||
static void GenerateHashInit(MacroAssembler* masm,
|
||||
Register hash,
|
||||
Register character,
|
||||
Register scratch);
|
||||
static void GenerateHashAddCharacter(MacroAssembler* masm,
|
||||
Register hash,
|
||||
Register character,
|
||||
Register scratch);
|
||||
static void GenerateHashGetHash(MacroAssembler* masm,
|
||||
Register hash,
|
||||
Register scratch);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
|
||||
};
|
||||
|
||||
|
||||
@ -940,7 +972,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);
|
||||
@ -957,7 +989,7 @@ class StringAddStub: public StringStubBase {
|
||||
};
|
||||
|
||||
|
||||
class SubStringStub: public StringStubBase {
|
||||
class SubStringStub: public CodeStub {
|
||||
public:
|
||||
SubStringStub() {}
|
||||
|
||||
@ -969,7 +1001,7 @@ class SubStringStub: public StringStubBase {
|
||||
};
|
||||
|
||||
|
||||
class StringCompareStub: public StringStubBase {
|
||||
class StringCompareStub: public CodeStub {
|
||||
public:
|
||||
explicit StringCompareStub() {
|
||||
}
|
||||
|
@ -491,39 +491,72 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
|
||||
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax : key
|
||||
// -- eax : key (index)
|
||||
// -- edx : receiver
|
||||
// -- esp[0] : return address
|
||||
// -----------------------------------
|
||||
Label miss, index_ok;
|
||||
Label miss;
|
||||
Label not_positive_smi;
|
||||
Label slow_char_code;
|
||||
Label got_char_code;
|
||||
|
||||
// Pop return address.
|
||||
// Performing the load early is better in the common case.
|
||||
__ pop(ebx);
|
||||
Register receiver = edx;
|
||||
Register index = eax;
|
||||
Register code = ebx;
|
||||
Register scratch = ecx;
|
||||
|
||||
__ test(edx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &miss);
|
||||
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
|
||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||
__ test(ecx, Immediate(kIsNotStringMask));
|
||||
__ j(not_zero, &miss);
|
||||
StringHelper::GenerateFastCharCodeAt(masm,
|
||||
receiver,
|
||||
index,
|
||||
scratch,
|
||||
code,
|
||||
&miss, // When not a string.
|
||||
¬_positive_smi,
|
||||
&slow_char_code);
|
||||
// If we didn't bail out, code register contains smi tagged char
|
||||
// code.
|
||||
__ bind(&got_char_code);
|
||||
StringHelper::GenerateCharFromCode(masm, code, eax, JUMP_FUNCTION);
|
||||
#ifdef DEBUG
|
||||
__ Abort("Unexpected fall-through from char from code tail call");
|
||||
#endif
|
||||
|
||||
// Check if key is a smi or a heap number.
|
||||
__ test(eax, Immediate(kSmiTagMask));
|
||||
__ j(zero, &index_ok);
|
||||
__ 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);
|
||||
|
||||
__ bind(&index_ok);
|
||||
// Push receiver and key on the stack, and make a tail call.
|
||||
__ push(edx); // receiver
|
||||
__ push(eax); // key
|
||||
__ push(ebx); // return address
|
||||
__ InvokeBuiltin(Builtins::STRING_CHAR_AT, JUMP_FUNCTION);
|
||||
// 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(eax));
|
||||
__ mov(code, eax);
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
// Check if the runtime call returned NaN char code. If yes, return
|
||||
// undefined. Otherwise, we can continue.
|
||||
if (FLAG_debug_code) {
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ test(code, Immediate(kSmiTagMask));
|
||||
__ j(zero, &got_char_code);
|
||||
__ mov(scratch, FieldOperand(code, HeapObject::kMapOffset));
|
||||
__ cmp(scratch, Factory::heap_number_map());
|
||||
__ Assert(equal, "StringCharCodeAt must return smi or heap number");
|
||||
}
|
||||
__ cmp(code, Factory::nan_value());
|
||||
__ j(not_equal, &got_char_code);
|
||||
__ Set(eax, Immediate(Factory::undefined_value()));
|
||||
__ ret(0);
|
||||
|
||||
__ bind(&miss);
|
||||
__ push(ebx);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,14 @@ assertEquals("F", foo[0]);
|
||||
assertEquals("o", foo[1]);
|
||||
assertEquals("o", foo[2]);
|
||||
|
||||
// Test string keyed load IC.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assertEquals("F", foo[0]);
|
||||
assertEquals("o", foo[1]);
|
||||
assertEquals("o", foo[2]);
|
||||
assertEquals("F", (foo[0] + "BarBazQuuxFooBarQuux")[0]);
|
||||
}
|
||||
|
||||
assertEquals("F", foo["0" + ""], "string index");
|
||||
assertEquals("o", foo["1"], "string index");
|
||||
assertEquals("o", foo["2"], "string index");
|
||||
@ -178,9 +186,9 @@ for (var i = 0; i < 200; ++i) {
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
var keys = [0, '1', 2, 3.0];
|
||||
var str = 'abcd', arr = ['a', 'b', 'c', 'd'];
|
||||
for (var i = 0; i < 200; ++i) {
|
||||
var keys = [0, '1', 2, 3.0, -1, 10];
|
||||
var str = 'abcd', arr = ['a', 'b', 'c', 'd', undefined, undefined];
|
||||
for (var i = 0; i < 300; ++i) {
|
||||
var index = Math.floor(i / 50);
|
||||
var key = keys[index];
|
||||
var expected = arr[index];
|
||||
|
Loading…
Reference in New Issue
Block a user