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:
vitalyr@chromium.org 2010-04-20 17:33:14 +00:00
parent 1e46286091
commit b8ba4deacc
5 changed files with 352 additions and 244 deletions

View File

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

View File

@ -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, &not_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(&not_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, &not_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(&not_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);

View File

@ -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() {
}

View File

@ -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.
&not_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(&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);
__ 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);
}

View File

@ -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];