Revision: 4450
Author: [email protected]
Date: Tue Apr 20 10:33:14 2010
Log: 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
http://code.google.com/p/v8/source/detail?r=4450
Modified:
/branches/bleeding_edge/src/globals.h
/branches/bleeding_edge/src/ia32/codegen-ia32.cc
/branches/bleeding_edge/src/ia32/codegen-ia32.h
/branches/bleeding_edge/src/ia32/ic-ia32.cc
/branches/bleeding_edge/test/mjsunit/string-index.js
=======================================
--- /branches/bleeding_edge/src/globals.h Tue Apr 20 02:26:32 2010
+++ /branches/bleeding_edge/src/globals.h Tue Apr 20 10:33:14 2010
@@ -147,6 +147,9 @@
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;
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Apr 20 02:26:32
2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Apr 20 10:33:14
2010
@@ -2348,7 +2348,7 @@
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 @@
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 @@
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 @@
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()));
-
- __ bind(&end);
- frame_->Push(&temp);
+ __ Set(result.reg(), Immediate(Factory::undefined_value()));
+
+ __ bind(&exit);
+ frame_->Push(&result);
}
@@ -6058,46 +5966,22 @@
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.
- 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 may do a runtime call.
+ frame_->SpillAll();
+
+ Result result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+
+ StringHelper::GenerateCharFromCode(masm_,
+ code.reg(),
+ result.reg(),
+ CALL_FUNCTION);
frame_->Push(&result);
-
- exit.Bind();
}
@@ -8522,7 +8406,7 @@
}
// 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.
@@ -12152,6 +12036,154 @@
include_number_compare_name);
return name_;
}
+
+
+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) {
@@ -12220,8 +12252,8 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 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::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::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::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::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::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 @@
// 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 @@
// 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 @@
// 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);
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.h Tue Apr 20 02:26:32 2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.h Tue Apr 20 10:33:14 2010
@@ -883,53 +883,85 @@
};
-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 @@
};
-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 SubStringStub: public StringStubBase {
+class SubStringStub: public CodeStub {
public:
SubStringStub() {}
@@ -969,7 +1001,7 @@
};
-class StringCompareStub: public StringStubBase {
+class StringCompareStub: public CodeStub {
public:
explicit StringCompareStub() {
}
=======================================
--- /branches/bleeding_edge/src/ia32/ic-ia32.cc Tue Apr 20 02:26:32 2010
+++ /branches/bleeding_edge/src/ia32/ic-ia32.cc Tue Apr 20 10:33:14 2010
@@ -491,39 +491,72 @@
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;
-
- // Pop return address.
- // Performing the load early is better in the common case.
- __ pop(ebx);
-
- __ 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);
+ Label miss;
+ Label not_positive_smi;
+ Label slow_char_code;
+ Label got_char_code;
+
+ Register receiver = edx;
+ Register index = eax;
+ Register code = ebx;
+ Register scratch = ecx;
+
+ 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);
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/string-index.js Tue Apr 6
07:16:39 2010
+++ /branches/bleeding_edge/test/mjsunit/string-index.js Tue Apr 20
10:33:14 2010
@@ -34,6 +34,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");
@@ -178,9 +186,9 @@
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];
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev