Version 2.2.13.
Implement Object.getOwnPropertyDescriptor for element indices and strings (issue 599).
Fix bug for windows 64 bit C calls from generated code.
Add new scons flag unalignedaccesses for arm builds.
Performance improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@4756 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 6086258..2275b7c 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -226,8 +226,9 @@
// edx: number of elements
// ecx: start of next object
__ mov(eax, Factory::fixed_array_map());
- __ mov(Operand(edi, JSObject::kMapOffset), eax); // setup the map
- __ mov(Operand(edi, Array::kLengthOffset), edx); // and length
+ __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map
+ __ SmiTag(edx);
+ __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length
// Initialize the fields to undefined.
// ebx: JSObject
@@ -548,6 +549,7 @@
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(ebx,
FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ SmiUntag(ebx);
__ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
__ lea(edx, FieldOperand(edx, Code::kHeaderSize));
__ cmp(eax, Operand(ebx));
@@ -752,15 +754,15 @@
__ lea(scratch1, Operand(result, JSArray::kSize));
__ mov(FieldOperand(result, JSArray::kElementsOffset), scratch1);
- // Initialize the FixedArray and fill it with holes. FixedArray length is not
+ // Initialize the FixedArray and fill it with holes. FixedArray length is
// stored as a smi.
// result: JSObject
// scratch1: elements array
// scratch2: start of next object
- __ mov(FieldOperand(scratch1, JSObject::kMapOffset),
+ __ mov(FieldOperand(scratch1, FixedArray::kMapOffset),
Factory::fixed_array_map());
- __ mov(FieldOperand(scratch1, Array::kLengthOffset),
- Immediate(initial_capacity));
+ __ mov(FieldOperand(scratch1, FixedArray::kLengthOffset),
+ Immediate(Smi::FromInt(initial_capacity)));
// Fill the FixedArray with the hole value. Inline the code if short.
// Reconsider loop unfolding if kPreallocatedArrayElements gets changed.
@@ -847,23 +849,22 @@
__ lea(elements_array, Operand(result, JSArray::kSize));
__ mov(FieldOperand(result, JSArray::kElementsOffset), elements_array);
- // Initialize the fixed array. FixedArray length is not stored as a smi.
+ // Initialize the fixed array. FixedArray length is stored as a smi.
// result: JSObject
// elements_array: elements array
// elements_array_end: start of next object
// array_size: size of array (smi)
- ASSERT(kSmiTag == 0);
- __ SmiUntag(array_size); // Convert from smi to value.
- __ mov(FieldOperand(elements_array, JSObject::kMapOffset),
+ __ mov(FieldOperand(elements_array, FixedArray::kMapOffset),
Factory::fixed_array_map());
// For non-empty JSArrays the length of the FixedArray and the JSArray is the
// same.
- __ mov(FieldOperand(elements_array, Array::kLengthOffset), array_size);
+ __ mov(FieldOperand(elements_array, FixedArray::kLengthOffset), array_size);
// Fill the allocated FixedArray with the hole value if requested.
// result: JSObject
// elements_array: elements array
if (fill_with_hole) {
+ __ SmiUntag(array_size);
__ lea(edi, Operand(elements_array,
FixedArray::kHeaderSize - kHeapObjectTag));
__ mov(eax, Factory::the_hole_value());
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index c55ec7b..95aeba6 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -46,12 +46,12 @@
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm_)
+#define __ ACCESS_MASM(masm)
// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
+// Platform-specific FrameRegisterState functions.
-void DeferredCode::SaveRegisters() {
+void FrameRegisterState::Save(MacroAssembler* masm) const {
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
int action = registers_[i];
if (action == kPush) {
@@ -63,7 +63,7 @@
}
-void DeferredCode::RestoreRegisters() {
+void FrameRegisterState::Restore(MacroAssembler* masm) const {
// Restore registers in reverse order due to the stack.
for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
int action = registers_[i];
@@ -77,6 +77,45 @@
}
+#undef __
+#define __ ACCESS_MASM(masm_)
+
+// -------------------------------------------------------------------------
+// Platform-specific DeferredCode functions.
+
+void DeferredCode::SaveRegisters() {
+ frame_state_.Save(masm_);
+}
+
+
+void DeferredCode::RestoreRegisters() {
+ frame_state_.Restore(masm_);
+}
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ frame_state_->Save(masm);
+}
+
+
+void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ frame_state_->Restore(masm);
+}
+
+
+void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterInternalFrame();
+}
+
+
+void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveInternalFrame();
+}
+
+
// -------------------------------------------------------------------------
// CodeGenState implementation.
@@ -4198,7 +4237,6 @@
frame_->EmitPush(eax); // <- slot 3
frame_->EmitPush(edx); // <- slot 2
__ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset));
- __ SmiTag(eax);
frame_->EmitPush(eax); // <- slot 1
frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
entry.Jump();
@@ -4210,7 +4248,6 @@
// Push the length of the array and the initial index onto the stack.
__ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
- __ SmiTag(eax);
frame_->EmitPush(eax); // <- slot 1
frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
@@ -6020,29 +6057,67 @@
}
-// This generates code that performs a charCodeAt() call or returns
-// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
-// It can handle flat, 8 and 16 bit characters and cons strings where the
-// answer is found in the left hand branch of the cons. The slow case will
-// flatten the string, which will ensure that the answer is in the left hand
-// side the next time around.
-void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateFastCharCodeAt");
+class DeferredStringCharCodeAt : public DeferredCode {
+ public:
+ DeferredStringCharCodeAt(Register object,
+ Register index,
+ Register scratch,
+ Register result)
+ : result_(result),
+ char_code_at_generator_(object,
+ index,
+ scratch,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharCodeAtGenerator* fast_case_generator() {
+ return &char_code_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_code_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move the undefined value into the result register, which will
+ // trigger conversion.
+ __ Set(result_, Immediate(Factory::undefined_value()));
+ __ jmp(exit_label());
+
+ __ bind(&index_out_of_range_);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ Set(result_, Immediate(Factory::nan_value()));
+ __ jmp(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharCodeAtGenerator char_code_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charCodeAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharCodeAt");
ASSERT(args->length() == 2);
Load(args->at(0));
Load(args->at(1));
Result index = frame_->Pop();
Result object = frame_->Pop();
-
- // 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
- // heap object and the index is not.
object.ToRegister();
index.ToRegister();
+ // We might mutate the object register.
frame_->Spill(object.reg());
- frame_->Spill(index.reg());
// We need two extra registers.
Result result = allocator()->Allocate();
@@ -6050,33 +6125,40 @@
Result scratch = allocator()->Allocate();
ASSERT(scratch.is_valid());
- // There is no virtual frame effect from here up to the final result
- // push.
- 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.
- __ Set(result.reg(), Immediate(Factory::undefined_value()));
-
- __ bind(&exit);
+ DeferredStringCharCodeAt* deferred =
+ new DeferredStringCharCodeAt(object.reg(),
+ index.reg(),
+ scratch.reg(),
+ result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->Push(&result);
}
-void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateCharFromCode");
+class DeferredStringCharFromCode : public DeferredCode {
+ public:
+ DeferredStringCharFromCode(Register code,
+ Register result)
+ : char_from_code_generator_(code, result) {}
+
+ StringCharFromCodeGenerator* fast_case_generator() {
+ return &char_from_code_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_from_code_generator_.GenerateSlow(masm(), call_helper);
+ }
+
+ private:
+ StringCharFromCodeGenerator char_from_code_generator_;
+};
+
+
+// Generates code for creating a one-char string from a char code.
+void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharFromCode");
ASSERT(args->length() == 1);
Load(args->at(0));
@@ -6085,16 +6167,97 @@
code.ToRegister();
ASSERT(code.is_valid());
- // 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);
+ DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
+ code.reg(), result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
+ frame_->Push(&result);
+}
+
+
+class DeferredStringCharAt : public DeferredCode {
+ public:
+ DeferredStringCharAt(Register object,
+ Register index,
+ Register scratch1,
+ Register scratch2,
+ Register result)
+ : result_(result),
+ char_at_generator_(object,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharAtGenerator* fast_case_generator() {
+ return &char_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ Set(result_, Immediate(Smi::FromInt(0)));
+ __ jmp(exit_label());
+
+ __ bind(&index_out_of_range_);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ Set(result_, Immediate(Factory::empty_string()));
+ __ jmp(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharAtGenerator char_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharAt");
+ ASSERT(args->length() == 2);
+
+ Load(args->at(0));
+ Load(args->at(1));
+ Result index = frame_->Pop();
+ Result object = frame_->Pop();
+ object.ToRegister();
+ index.ToRegister();
+ // We might mutate the object register.
+ frame_->Spill(object.reg());
+
+ // We need three extra registers.
+ Result result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ Result scratch1 = allocator()->Allocate();
+ ASSERT(scratch1.is_valid());
+ Result scratch2 = allocator()->Allocate();
+ ASSERT(scratch2.is_valid());
+
+ DeferredStringCharAt* deferred =
+ new DeferredStringCharAt(object.reg(),
+ index.reg(),
+ scratch1.reg(),
+ scratch2.reg(),
+ result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->Push(&result);
}
@@ -6600,9 +6763,9 @@
__ mov(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
// Set length.
- __ SmiUntag(ecx);
__ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
// Fill contents of fixed-array with the-hole.
+ __ SmiUntag(ecx);
__ mov(edx, Immediate(Factory::the_hole_value()));
__ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
// Fill fixed array elements with hole.
@@ -6706,7 +6869,6 @@
// Check if we could add new entry to cache.
__ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
- __ SmiTag(ebx);
__ cmp(ebx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset));
__ j(greater, &add_new_entry);
@@ -6904,12 +7066,8 @@
// (or them and test against Smi mask.)
__ mov(tmp2.reg(), tmp1.reg());
- RecordWriteStub recordWrite1(tmp2.reg(), index1.reg(), object.reg());
- __ CallStub(&recordWrite1);
-
- RecordWriteStub recordWrite2(tmp1.reg(), index2.reg(), object.reg());
- __ CallStub(&recordWrite2);
-
+ __ RecordWriteHelper(tmp2.reg(), index1.reg(), object.reg());
+ __ RecordWriteHelper(tmp1.reg(), index2.reg(), object.reg());
__ bind(&done);
deferred->BindExit();
@@ -8608,13 +8766,8 @@
key.ToRegister();
receiver.ToRegister();
- // Use a fresh temporary for the index and later the loaded
- // value.
- result = allocator()->Allocate();
- ASSERT(result.is_valid());
-
DeferredReferenceGetKeyedValue* deferred =
- new DeferredReferenceGetKeyedValue(result.reg(),
+ new DeferredReferenceGetKeyedValue(elements.reg(),
receiver.reg(),
key.reg());
@@ -8647,19 +8800,20 @@
Immediate(Factory::fixed_array_map()));
deferred->Branch(not_equal);
- // Shift the key to get the actual index value and check that
- // it is within bounds. Use unsigned comparison to handle negative keys.
- __ mov(result.reg(), key.reg());
- __ SmiUntag(result.reg());
- __ cmp(result.reg(),
+ // Check that the key is within bounds.
+ __ cmp(key.reg(),
FieldOperand(elements.reg(), FixedArray::kLengthOffset));
deferred->Branch(above_equal);
- __ mov(result.reg(), Operand(elements.reg(),
- result.reg(),
- times_4,
- FixedArray::kHeaderSize - kHeapObjectTag));
- elements.Unuse();
+ // Load and check that the result is not the hole.
+ // Key holds a smi.
+ ASSERT((kSmiTag == 0) && (kSmiTagSize == 1));
+ __ mov(elements.reg(),
+ FieldOperand(elements.reg(),
+ key.reg(),
+ times_2,
+ FixedArray::kHeaderSize));
+ result = elements;
__ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
deferred->Branch(equal);
__ IncrementCounter(&Counters::keyed_load_inline, 1);
@@ -8744,7 +8898,7 @@
// Check whether it is possible to omit the write barrier. If the elements
// array is in new space or the value written is a smi we can safely update
- // the elements array without updating the remembered set.
+ // the elements array without write barrier.
Label in_new_space;
__ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space);
if (!value_is_constant) {
@@ -9014,7 +9168,8 @@
// Setup the object header.
__ mov(FieldOperand(eax, HeapObject::kMapOffset), Factory::context_map());
- __ mov(FieldOperand(eax, Array::kLengthOffset), Immediate(length));
+ __ mov(FieldOperand(eax, Context::kLengthOffset),
+ Immediate(Smi::FromInt(length)));
// Setup the fixed slots.
__ xor_(ebx, Operand(ebx)); // Set to NULL.
@@ -10977,9 +11132,8 @@
__ test(ecx, Operand(ecx));
__ j(zero, &done);
- // Get the parameters pointer from the stack and untag the length.
+ // Get the parameters pointer from the stack.
__ mov(edx, Operand(esp, 2 * kPointerSize));
- __ SmiUntag(ecx);
// Setup the elements pointer in the allocated arguments object and
// initialize the header in the elements fixed array.
@@ -10988,6 +11142,8 @@
__ mov(FieldOperand(edi, FixedArray::kMapOffset),
Immediate(Factory::fixed_array_map()));
__ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
+ // Untag the length for the loop below.
+ __ SmiUntag(ecx);
// Copy the fixed array slots.
Label loop;
@@ -11116,6 +11272,7 @@
// Check that the last match info has space for the capture registers and the
// additional information.
__ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
+ __ SmiUntag(eax);
__ add(Operand(edx), Immediate(RegExpImpl::kLastMatchOverhead));
__ cmp(edx, Operand(eax));
__ j(greater, &runtime);
@@ -11359,7 +11516,7 @@
// Make the hash mask from the length of the number string cache. It
// contains two elements (number and string) for each cache entry.
__ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
- __ shr(mask, 1); // Divide length by two (length is not a smi).
+ __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two.
__ sub(Operand(mask), Immediate(1)); // Make mask.
// Calculate the entry in the number string cache. The hash value in the
@@ -11450,12 +11607,6 @@
}
-void RecordWriteStub::Generate(MacroAssembler* masm) {
- masm->RecordWriteHelper(object_, addr_, scratch_);
- masm->ret(0);
-}
-
-
static int NegativeComparisonResult(Condition cc) {
ASSERT(cc != equal);
ASSERT((cc == less) || (cc == less_equal)
@@ -12390,152 +12541,204 @@
}
-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;
+// -------------------------------------------------------------------------
+// StringCharCodeAtGenerator
+
+void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
+ Label flat_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);
+ __ 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));
+ __ 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);
+ __ test(result_, Immediate(kIsNotStringMask));
+ __ j(not_zero, receiver_not_string_);
// If the index is non-smi trigger the non-smi case.
ASSERT(kSmiTag == 0);
- __ test(index, Immediate(kSmiTagMask));
- __ j(not_zero, index_not_smi);
+ __ test(index_, Immediate(kSmiTagMask));
+ __ j(not_zero, &index_not_smi_);
+
+ // Put smi-tagged index into scratch register.
+ __ mov(scratch_, index_);
+ __ bind(&got_smi_index_);
// Check for index out of range.
- __ cmp(index, 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
- // -----------------------------------
+ __ cmp(scratch_, FieldOperand(object_, String::kLengthOffset));
+ __ j(above_equal, index_out_of_range_);
// 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 result register.
- ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // index is smi (powered by 2).
- __ movzx_w(result, FieldOperand(object,
- index, times_1,
- SeqTwoByteString::kHeaderSize));
- __ jmp(&got_char_code);
+ __ test(result_, Immediate(kStringRepresentationMask));
+ __ j(zero, &flat_string);
// Handle non-flat strings.
- __ bind(¬_a_flat_string);
- __ and_(result, kStringRepresentationMask);
- __ cmp(result, kConsStringTag);
- __ j(not_equal, slow_case);
+ __ test(result_, Immediate(kIsConsStringMask));
+ __ j(zero, &call_runtime_);
// 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);
+ __ cmp(FieldOperand(object_, ConsString::kSecondOffset),
+ Immediate(Factory::empty_string()));
+ __ j(not_equal, &call_runtime_);
// 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);
+ __ mov(object_, FieldOperand(object_, ConsString::kFirstOffset));
+ __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
+ // If the first cons component is also non-flat, then go to runtime.
+ ASSERT(kSeqStringTag == 0);
+ __ test(result_, Immediate(kStringRepresentationMask));
+ __ j(not_zero, &call_runtime_);
+
+ // Check for 1-byte or 2-byte string.
+ __ bind(&flat_string);
+ ASSERT(kAsciiStringTag != 0);
+ __ test(result_, Immediate(kStringEncodingMask));
+ __ j(not_zero, &ascii_string);
+
+ // 2-byte string.
+ // Load the 2-byte character code into the result register.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ movzx_w(result_, FieldOperand(object_,
+ scratch_, times_1, // Scratch is smi-tagged.
+ SeqTwoByteString::kHeaderSize));
+ __ jmp(&got_char_code);
// ASCII string.
- __ bind(&ascii_string);
- // Put untagged index into scratch register.
- __ mov(scratch, index);
- __ SmiUntag(scratch);
-
// Load the byte into the result register.
- __ movzx_b(result, FieldOperand(object,
- scratch, times_1,
- SeqAsciiString::kHeaderSize));
+ __ bind(&ascii_string);
+ __ SmiUntag(scratch_);
+ __ movzx_b(result_, FieldOperand(object_,
+ scratch_, times_1,
+ SeqAsciiString::kHeaderSize));
__ bind(&got_char_code);
- __ SmiTag(result);
+ __ SmiTag(result_);
+ __ bind(&exit_);
}
-void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
- Register code,
- Register result,
- InvokeFlag flag) {
- ASSERT(!code.is(result));
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharCodeAt slow case");
- Label slow_case;
- Label exit;
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_, Factory::heap_number_map(), index_not_number_, true);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_);
+ __ push(result_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ if (!scratch_.is(eax)) {
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ mov(scratch_, eax);
+ }
+ __ pop(result_);
+ __ pop(index_);
+ __ pop(object_);
+ call_helper.AfterCall(masm);
+ // If index is still not a smi, it must be out of range.
+ ASSERT(kSmiTag == 0);
+ __ test(scratch_, Immediate(kSmiTagMask));
+ __ j(not_zero, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ if (!result_.is(eax)) {
+ __ mov(result_, eax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharCodeAt slow case");
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
// Fast case of Heap::LookupSingleCharacterStringFromCode.
ASSERT(kSmiTag == 0);
ASSERT(kSmiShiftSize == 0);
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
- __ test(code,
+ __ test(code_,
Immediate(kSmiTagMask |
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
- __ j(not_zero, &slow_case, not_taken);
+ __ j(not_zero, &slow_case_, not_taken);
- __ Set(result, Immediate(Factory::single_character_string_cache()));
+ __ 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);
+ __ mov(result_, FieldOperand(result_,
+ code_, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(result_, Factory::undefined_value());
+ __ j(equal, &slow_case_, not_taken);
+ __ bind(&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 StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharFromCode slow case");
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result_.is(eax)) {
+ __ mov(result_, eax);
}
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharFromCode slow case");
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharAtGenerator
+
+void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
+ char_code_at_generator_.GenerateFast(masm);
+ char_from_code_generator_.GenerateFast(masm);
+}
+
+
+void StringCharAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ char_code_at_generator_.GenerateSlow(masm, call_helper);
+ char_from_code_generator_.GenerateSlow(masm, call_helper);
}
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index a098dc3..ea182ab 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -38,8 +38,10 @@
// Forward declarations
class CompilationInfo;
class DeferredCode;
+class FrameRegisterState;
class RegisterAllocator;
class RegisterFile;
+class RuntimeCallHelper;
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
@@ -621,10 +623,13 @@
void GenerateSetValueOf(ZoneList<Expression*>* args);
// Fast support for charCodeAt(n).
- void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
+ void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
- void GenerateCharFromCode(ZoneList<Expression*>* args);
+ void GenerateStringCharFromCode(ZoneList<Expression*>* args);
+
+ // Fast support for string.charAt(n) and string[n].
+ void GenerateStringCharAt(ZoneList<Expression*>* args);
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
@@ -910,37 +915,6 @@
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 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
@@ -1083,42 +1057,6 @@
};
-class RecordWriteStub : public CodeStub {
- public:
- RecordWriteStub(Register object, Register addr, Register scratch)
- : object_(object), addr_(addr), scratch_(scratch) { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Register object_;
- Register addr_;
- Register scratch_;
-
-#ifdef DEBUG
- void Print() {
- PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
- object_.code(), addr_.code(), scratch_.code());
- }
-#endif
-
- // Minor key encoding in 12 bits. 4 bits for each of the three
- // registers (object, address and scratch) OOOOAAAASSSS.
- class ScratchBits: public BitField<uint32_t, 0, 4> {};
- class AddressBits: public BitField<uint32_t, 4, 4> {};
- class ObjectBits: public BitField<uint32_t, 8, 4> {};
-
- Major MajorKey() { return RecordWrite; }
-
- int MinorKey() {
- // Encode the registers.
- return ObjectBits::encode(object_.code()) |
- AddressBits::encode(addr_.code()) |
- ScratchBits::encode(scratch_.code());
- }
-};
-
-
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 1b78772..ae64d02 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -1009,7 +1009,6 @@
__ push(eax); // Map.
__ push(edx); // Enumeration cache.
__ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset));
- __ SmiTag(eax);
__ push(eax); // Enumeration cache length (as smi).
__ push(Immediate(Smi::FromInt(0))); // Initial index.
__ jmp(&loop);
@@ -1019,7 +1018,6 @@
__ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check.
__ push(eax);
__ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
- __ SmiTag(eax);
__ push(eax); // Fixed array length (as smi).
__ push(Immediate(Smi::FromInt(0))); // Initial index.
@@ -1904,76 +1902,6 @@
}
-void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
- Handle<String> name = expr->name();
- if (strcmp("_IsSmi", *name->ToCString()) == 0) {
- EmitIsSmi(expr->arguments());
- } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
- EmitIsNonNegativeSmi(expr->arguments());
- } else if (strcmp("_IsObject", *name->ToCString()) == 0) {
- EmitIsObject(expr->arguments());
- } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
- EmitIsUndetectableObject(expr->arguments());
- } else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
- EmitIsFunction(expr->arguments());
- } else if (strcmp("_IsArray", *name->ToCString()) == 0) {
- EmitIsArray(expr->arguments());
- } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
- EmitIsRegExp(expr->arguments());
- } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
- EmitIsConstructCall(expr->arguments());
- } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
- EmitObjectEquals(expr->arguments());
- } else if (strcmp("_Arguments", *name->ToCString()) == 0) {
- EmitArguments(expr->arguments());
- } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
- EmitArgumentsLength(expr->arguments());
- } else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
- EmitClassOf(expr->arguments());
- } else if (strcmp("_Log", *name->ToCString()) == 0) {
- EmitLog(expr->arguments());
- } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
- EmitRandomHeapNumber(expr->arguments());
- } else if (strcmp("_SubString", *name->ToCString()) == 0) {
- EmitSubString(expr->arguments());
- } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
- EmitRegExpExec(expr->arguments());
- } else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
- EmitValueOf(expr->arguments());
- } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
- EmitSetValueOf(expr->arguments());
- } else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
- EmitNumberToString(expr->arguments());
- } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) {
- EmitCharFromCode(expr->arguments());
- } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) {
- EmitFastCharCodeAt(expr->arguments());
- } else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
- EmitStringAdd(expr->arguments());
- } else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
- EmitStringCompare(expr->arguments());
- } else if (strcmp("_MathPow", *name->ToCString()) == 0) {
- EmitMathPow(expr->arguments());
- } else if (strcmp("_MathSin", *name->ToCString()) == 0) {
- EmitMathSin(expr->arguments());
- } else if (strcmp("_MathCos", *name->ToCString()) == 0) {
- EmitMathCos(expr->arguments());
- } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
- EmitMathSqrt(expr->arguments());
- } else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
- EmitCallFunction(expr->arguments());
- } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
- EmitRegExpConstructResult(expr->arguments());
- } else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
- EmitSwapElements(expr->arguments());
- } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
- EmitGetFromCache(expr->arguments());
- } else {
- UNREACHABLE();
- }
-}
-
-
void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
@@ -2432,50 +2360,120 @@
}
-void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) {
+void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
- Label slow_case, done;
- // Fast case of Heap::LookupSingleCharacterStringFromCode.
- ASSERT(kSmiTag == 0);
- ASSERT(kSmiShiftSize == 0);
- ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
- __ test(eax,
- Immediate(kSmiTagMask |
- ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
- __ j(not_zero, &slow_case);
- __ Set(ebx, 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(ebx, FieldOperand(ebx,
- eax, times_half_pointer_size,
- FixedArray::kHeaderSize));
- __ cmp(ebx, Factory::undefined_value());
- __ j(equal, &slow_case);
- __ mov(eax, ebx);
+ Label done;
+ StringCharFromCodeGenerator generator(eax, ebx);
+ generator.GenerateFast(masm_);
__ jmp(&done);
- __ bind(&slow_case);
- __ push(eax);
- __ CallRuntime(Runtime::kCharFromCode, 1);
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
- Apply(context_, eax);
+ Apply(context_, ebx);
}
-void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) {
- // TODO(fsc): Port the complete implementation from the classic back-end.
+void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 2);
+
+ VisitForValue(args->at(0), kStack);
+ VisitForValue(args->at(1), kAccumulator);
+
+ Register object = ebx;
+ Register index = eax;
+ Register scratch = ecx;
+ Register result = edx;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharCodeAtGenerator generator(object,
+ index,
+ scratch,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ Set(result, Immediate(Factory::nan_value()));
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
// Move the undefined value into the result register, which will
- // trigger the slow case.
- __ Set(eax, Immediate(Factory::undefined_value()));
- Apply(context_, eax);
+ // trigger conversion.
+ __ Set(result, Immediate(Factory::undefined_value()));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ Apply(context_, result);
}
+
+void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 2);
+
+ VisitForValue(args->at(0), kStack);
+ VisitForValue(args->at(1), kAccumulator);
+
+ Register object = ebx;
+ Register index = eax;
+ Register scratch1 = ecx;
+ Register scratch2 = edx;
+ Register result = eax;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharAtGenerator generator(object,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ Set(result, Immediate(Factory::empty_string()));
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ Set(result, Immediate(Smi::FromInt(0)));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ Apply(context_, result);
+}
+
+
void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index 644d200..2ba64dc 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -304,7 +304,7 @@
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- Label slow, check_string, index_int, index_string;
+ Label slow, check_string, index_smi, index_string;
Label check_pixel_array, probe_dictionary;
Label check_number_dictionary;
@@ -329,18 +329,17 @@
// Check that the key is a smi.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &check_string, not_taken);
- __ mov(ebx, eax);
- __ SmiUntag(ebx);
// Get the elements array of the object.
- __ bind(&index_int);
+ __ bind(&index_smi);
__ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
__ CheckMap(ecx, Factory::fixed_array_map(), &check_pixel_array, true);
// Check that the key (index) is within bounds.
- __ cmp(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
+ __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset));
__ j(above_equal, &slow);
// Fast case: Do the load.
- __ mov(ecx, FieldOperand(ecx, ebx, times_4, FixedArray::kHeaderSize));
+ ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0));
+ __ mov(ecx, FieldOperand(ecx, eax, times_2, FixedArray::kHeaderSize));
__ cmp(Operand(ecx), Immediate(Factory::the_hole_value()));
// In case the loaded value is the_hole we have to consult GetProperty
// to ensure the prototype chain is searched.
@@ -352,9 +351,10 @@
__ bind(&check_pixel_array);
// Check whether the elements is a pixel array.
// edx: receiver
- // ebx: untagged index
// eax: key
// ecx: elements
+ __ mov(ebx, eax);
+ __ SmiUntag(ebx);
__ CheckMap(ecx, Factory::pixel_array_map(), &check_number_dictionary, true);
__ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
__ j(above_equal, &slow);
@@ -485,9 +485,13 @@
ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
(1 << String::kArrayIndexValueBits));
__ bind(&index_string);
- __ and_(ebx, String::kArrayIndexHashMask);
- __ shr(ebx, String::kHashShift);
- __ jmp(&index_int);
+ // We want the smi-tagged index in eax. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ ASSERT(String::kHashShift >= kSmiTagSize);
+ __ and_(ebx, String::kArrayIndexValueMask);
+ __ shr(ebx, String::kHashShift - kSmiTagSize);
+ __ mov(eax, ebx);
+ __ jmp(&index_smi);
}
@@ -498,60 +502,29 @@
// -- esp[0] : return address
// -----------------------------------
Label miss;
- Label index_not_smi;
Label index_out_of_range;
- Label slow_char_code;
- Label got_char_code;
Register receiver = edx;
Register index = eax;
- Register code = ebx;
- Register scratch = ecx;
+ Register scratch1 = ebx;
+ Register scratch2 = ecx;
+ Register result = eax;
- 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, eax, JUMP_FUNCTION);
-#ifdef DEBUG
- __ Abort("Unexpected fall-through from char from code tail call");
-#endif
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &index_out_of_range,
+ STRING_INDEX_IS_ARRAY_INDEX);
+ char_at_generator.GenerateFast(masm);
+ __ ret(0);
- // Check if key is a heap number.
- __ bind(&index_not_smi);
- __ CheckMap(index, Factory::heap_number_map(), &miss, true);
+ ICRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm, call_helper);
- // 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);
__ bind(&index_out_of_range);
__ Set(eax, Immediate(Factory::undefined_value()));
__ ret(0);
@@ -792,9 +765,7 @@
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
__ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
- __ mov(ebx, Operand(ecx));
- __ SmiUntag(ebx);
- __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
+ __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
__ j(below, &fast, taken);
// Slow case: call runtime.
@@ -804,7 +775,7 @@
// Check whether the elements is a pixel array.
__ bind(&check_pixel_array);
// eax: value
- // ecx: key
+ // ecx: key (a smi)
// edx: receiver
// edi: elements array
__ CheckMap(edi, Factory::pixel_array_map(), &slow, true);
@@ -840,13 +811,11 @@
// edi: receiver->elements, a FixedArray
// flags: compare (ecx, edx.length())
__ j(not_equal, &slow, not_taken); // do not leave holes in the array
- __ mov(ebx, ecx);
- __ SmiUntag(ebx); // untag
- __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
+ __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
__ j(above_equal, &slow, not_taken);
// Add 1 to receiver->length, and go to fast array write.
__ add(FieldOperand(edx, JSArray::kLengthOffset),
- Immediate(1 << kSmiTagSize));
+ Immediate(Smi::FromInt(1)));
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index ba2fe2d..5caa4c4 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -60,49 +60,17 @@
bind(¬_in_new_space);
}
- Label fast;
-
// Compute the page start address from the heap object pointer, and reuse
// the 'object' register for it.
and_(object, ~Page::kPageAlignmentMask);
- Register page_start = object;
- // Compute the bit addr in the remembered set/index of the pointer in the
- // page. Reuse 'addr' as pointer_offset.
- sub(addr, Operand(page_start));
- shr(addr, kObjectAlignmentBits);
- Register pointer_offset = addr;
+ // Compute number of region covering addr. See Page::GetRegionNumberForAddress
+ // method for more details.
+ and_(addr, Page::kPageAlignmentMask);
+ shr(addr, Page::kRegionSizeLog2);
- // If the bit offset lies beyond the normal remembered set range, it is in
- // the extra remembered set area of a large object.
- cmp(pointer_offset, Page::kPageSize / kPointerSize);
- j(less, &fast);
-
- // Adjust 'page_start' so that addressing using 'pointer_offset' hits the
- // extra remembered set after the large object.
-
- // Find the length of the large object (FixedArray).
- mov(scratch, Operand(page_start, Page::kObjectStartOffset
- + FixedArray::kLengthOffset));
- Register array_length = scratch;
-
- // Extra remembered set starts right after the large object (a FixedArray), at
- // page_start + kObjectStartOffset + objectSize
- // where objectSize is FixedArray::kHeaderSize + kPointerSize * array_length.
- // Add the delta between the end of the normal RSet and the start of the
- // extra RSet to 'page_start', so that addressing the bit using
- // 'pointer_offset' hits the extra RSet words.
- lea(page_start,
- Operand(page_start, array_length, times_pointer_size,
- Page::kObjectStartOffset + FixedArray::kHeaderSize
- - Page::kRSetEndOffset));
-
- // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
- // to limit code size. We should probably evaluate this decision by
- // measuring the performance of an equivalent implementation using
- // "simpler" instructions
- bind(&fast);
- bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
+ // Set dirty mark for region.
+ bts(Operand(object, Page::kDirtyFlagOffset), addr);
}
@@ -130,7 +98,7 @@
}
-// Set the remembered set bit for [object+offset].
+// For page containing |object| mark region covering [object+offset] dirty.
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the scratch register contains the array index into
// the elements array represented as a Smi.
@@ -142,9 +110,8 @@
// registers are esi.
ASSERT(!object.is(esi) && !value.is(esi) && !scratch.is(esi));
- // First, check if a remembered set write is even needed. The tests below
- // catch stores of Smis and stores into young gen (which does not have space
- // for the remembered set bits).
+ // First, check if a write barrier is even needed. The tests below
+ // catch stores of Smis and stores into young gen.
Label done;
// Skip barrier if writing a smi.
@@ -160,47 +127,19 @@
ASSERT(IsAligned(offset, kPointerSize) ||
IsAligned(offset + kHeapObjectTag, kPointerSize));
- // We use optimized write barrier code if the word being written to is not in
- // a large object chunk or is in the first page of a large object chunk.
- // We make sure that an offset is inside the right limits whether it is
- // tagged or untagged.
- if ((offset > 0) && (offset < Page::kMaxHeapObjectSize - kHeapObjectTag)) {
- // Compute the bit offset in the remembered set, leave it in 'value'.
- lea(value, Operand(object, offset));
- and_(value, Page::kPageAlignmentMask);
- shr(value, kPointerSizeLog2);
-
- // Compute the page address from the heap object pointer, leave it in
- // 'object'.
- and_(object, ~Page::kPageAlignmentMask);
-
- // NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
- // to limit code size. We should probably evaluate this decision by
- // measuring the performance of an equivalent implementation using
- // "simpler" instructions
- bts(Operand(object, Page::kRSetOffset), value);
+ Register dst = scratch;
+ if (offset != 0) {
+ lea(dst, Operand(object, offset));
} else {
- Register dst = scratch;
- if (offset != 0) {
- lea(dst, Operand(object, offset));
- } else {
- // array access: calculate the destination address in the same manner as
- // KeyedStoreIC::GenerateGeneric. Multiply a smi by 2 to get an offset
- // into an array of words.
- ASSERT_EQ(1, kSmiTagSize);
- ASSERT_EQ(0, kSmiTag);
- lea(dst, Operand(object, dst, times_half_pointer_size,
- FixedArray::kHeaderSize - kHeapObjectTag));
- }
- // If we are already generating a shared stub, not inlining the
- // record write code isn't going to save us any memory.
- if (generating_stub()) {
- RecordWriteHelper(object, dst, value);
- } else {
- RecordWriteStub stub(object, dst, value);
- CallStub(&stub);
- }
+ // Array access: calculate the destination address in the same manner as
+ // KeyedStoreIC::GenerateGeneric. Multiply a smi by 2 to get an offset
+ // into an array of words.
+ ASSERT_EQ(1, kSmiTagSize);
+ ASSERT_EQ(0, kSmiTag);
+ lea(dst, Operand(object, dst, times_half_pointer_size,
+ FixedArray::kHeaderSize - kHeapObjectTag));
}
+ RecordWriteHelper(object, dst, value);
bind(&done);
@@ -1384,6 +1323,7 @@
mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
mov(ebx, FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ SmiUntag(ebx);
mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
lea(edx, FieldOperand(edx, Code::kHeaderSize));
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index 9c8dfb2..387426f 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -59,8 +59,8 @@
// ---------------------------------------------------------------------------
// GC Support
- // Set the remebered set bit for an address which points into an
- // object. RecordWriteHelper only works if the object is not in new
+ // For page containing |object| mark region covering |addr| dirty.
+ // RecordWriteHelper only works if the object is not in new
// space.
void RecordWriteHelper(Register object,
Register addr,
@@ -73,7 +73,7 @@
Condition cc, // equal for new space, not_equal otherwise.
Label* branch);
- // Set the remembered set bit for [object+offset].
+ // For page containing |object| mark region covering [object+offset] dirty.
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the scratch register contains the array index into
// the elements array represented as a Smi.
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index eb555d7..5bb5be6 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -300,203 +300,6 @@
}
-template <class Compiler>
-static void CompileLoadInterceptor(Compiler* compiler,
- StubCompiler* stub_compiler,
- MacroAssembler* masm,
- JSObject* object,
- JSObject* holder,
- String* name,
- LookupResult* lookup,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Label* miss) {
- ASSERT(holder->HasNamedInterceptor());
- ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
-
- // Check that the receiver isn't a smi.
- __ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
-
- // Check that the maps haven't changed.
- Register reg =
- stub_compiler->CheckPrototypes(object, receiver, holder,
- scratch1, scratch2, name, miss);
-
- if (lookup->IsProperty() && lookup->IsCacheable()) {
- compiler->CompileCacheable(masm,
- stub_compiler,
- receiver,
- reg,
- scratch1,
- scratch2,
- holder,
- lookup,
- name,
- miss);
- } else {
- compiler->CompileRegular(masm,
- receiver,
- reg,
- scratch2,
- holder,
- miss);
- }
-}
-
-
-class LoadInterceptorCompiler BASE_EMBEDDED {
- public:
- explicit LoadInterceptorCompiler(Register name) : name_(name) {}
-
- void CompileCacheable(MacroAssembler* masm,
- StubCompiler* stub_compiler,
- Register receiver,
- Register holder,
- Register scratch1,
- Register scratch2,
- JSObject* interceptor_holder,
- LookupResult* lookup,
- String* name,
- Label* miss_label) {
- AccessorInfo* callback = NULL;
- bool optimize = false;
- // So far the most popular follow ups for interceptor loads are FIELD
- // and CALLBACKS, so inline only them, other cases may be added
- // later.
- if (lookup->type() == FIELD) {
- optimize = true;
- } else if (lookup->type() == CALLBACKS) {
- Object* callback_object = lookup->GetCallbackObject();
- if (callback_object->IsAccessorInfo()) {
- callback = AccessorInfo::cast(callback_object);
- optimize = callback->getter() != NULL;
- }
- }
-
- if (!optimize) {
- CompileRegular(masm, receiver, holder, scratch2, interceptor_holder,
- miss_label);
- return;
- }
-
- // Note: starting a frame here makes GC aware of pointers pushed below.
- __ EnterInternalFrame();
-
- if (lookup->type() == CALLBACKS) {
- __ push(receiver);
- }
- __ push(holder);
- __ push(name_);
-
- // Invoke an interceptor. Note: map checks from receiver to
- // interceptor's holder has been compiled before (see a caller
- // of this method.)
- CompileCallLoadPropertyWithInterceptor(masm,
- receiver,
- holder,
- name_,
- interceptor_holder);
-
- // Check if interceptor provided a value for property. If it's
- // the case, return immediately.
- Label interceptor_failed;
- __ cmp(eax, Factory::no_interceptor_result_sentinel());
- __ j(equal, &interceptor_failed);
- __ LeaveInternalFrame();
- __ ret(0);
-
- __ bind(&interceptor_failed);
- __ pop(name_);
- __ pop(holder);
- if (lookup->type() == CALLBACKS) {
- __ pop(receiver);
- }
-
- __ LeaveInternalFrame();
-
- if (lookup->type() == FIELD) {
- // We found FIELD property in prototype chain of interceptor's holder.
- // Check that the maps from interceptor's holder to field's holder
- // haven't changed...
- holder = stub_compiler->CheckPrototypes(interceptor_holder, holder,
- lookup->holder(), scratch1,
- scratch2,
- name,
- miss_label);
- // ... and retrieve a field from field's holder.
- stub_compiler->GenerateFastPropertyLoad(masm, eax,
- holder, lookup->holder(),
- lookup->GetFieldIndex());
- __ ret(0);
- } else {
- // We found CALLBACKS property in prototype chain of interceptor's
- // holder.
- ASSERT(lookup->type() == CALLBACKS);
- ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
- ASSERT(callback != NULL);
- ASSERT(callback->getter() != NULL);
-
- // Prepare for tail call: push receiver to stack after return address.
- Label cleanup;
- __ pop(scratch2); // return address
- __ push(receiver);
- __ push(scratch2);
-
- // Check that the maps from interceptor's holder to callback's holder
- // haven't changed.
- holder = stub_compiler->CheckPrototypes(interceptor_holder, holder,
- lookup->holder(), scratch1,
- scratch2,
- name,
- &cleanup);
-
- // Continue tail call preparation: push remaining parameters after
- // return address.
- __ pop(scratch2); // return address
- __ push(holder);
- __ mov(holder, Immediate(Handle<AccessorInfo>(callback)));
- __ push(holder);
- __ push(FieldOperand(holder, AccessorInfo::kDataOffset));
- __ push(name_);
- __ push(scratch2); // restore return address
-
- // Tail call to runtime.
- ExternalReference ref =
- ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallExternalReference(ref, 5, 1);
-
- // Clean up code: we pushed receiver after return address and
- // need to remove it from there.
- __ bind(&cleanup);
- __ pop(scratch1); // return address.
- __ pop(scratch2); // receiver.
- __ push(scratch1);
- }
- }
-
-
- void CompileRegular(MacroAssembler* masm,
- Register receiver,
- Register holder,
- Register scratch,
- JSObject* interceptor_holder,
- Label* miss_label) {
- __ pop(scratch); // save old return address
- PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
- __ push(scratch); // restore old return address
-
- ExternalReference ref = ExternalReference(
- IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallExternalReference(ref, 5, 1);
- }
-
- private:
- Register name_;
-};
-
-
// Reserves space for the extra arguments to FastHandleApiCall in the
// caller's frame.
//
@@ -683,9 +486,9 @@
Label miss_cleanup;
Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
Register holder =
- stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
- scratch1, scratch2, name,
- depth1, miss);
+ stub_compiler_->CheckPrototypes(object, receiver,
+ interceptor_holder, scratch1,
+ scratch2, name, depth1, miss);
// Invoke an interceptor and if it provides a value,
// branch to |regular_invoke|.
@@ -698,10 +501,17 @@
// Check that the maps from interceptor's holder to constant function's
// holder haven't changed and thus we can use cached constant function.
- stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
- lookup->holder(),
- scratch1, scratch2, name,
- depth2, miss);
+ if (interceptor_holder != lookup->holder()) {
+ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
+ lookup->holder(), scratch1,
+ scratch2, name, depth2, miss);
+ } else {
+ // CheckPrototypes has a side effect of fetching a 'holder'
+ // for API (object which is instanceof for the signature). It's
+ // safe to omit it here, as if present, it should be fetched
+ // by the previous CheckPrototypes.
+ ASSERT(depth2 == kInvalidProtoDepth);
+ }
// Invoke function.
if (can_do_fast_api_call) {
@@ -1060,7 +870,7 @@
void StubCompiler::GenerateLoadInterceptor(JSObject* object,
- JSObject* holder,
+ JSObject* interceptor_holder,
LookupResult* lookup,
Register receiver,
Register name_reg,
@@ -1068,18 +878,130 @@
Register scratch2,
String* name,
Label* miss) {
- LoadInterceptorCompiler compiler(name_reg);
- CompileLoadInterceptor(&compiler,
- this,
- masm(),
- object,
- holder,
- name,
- lookup,
- receiver,
- scratch1,
- scratch2,
- miss);
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // Check that the receiver isn't a smi.
+ __ test(receiver, Immediate(kSmiTagMask));
+ __ j(zero, miss, not_taken);
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsProperty() && lookup->IsCacheable()) {
+ if (lookup->type() == FIELD) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsAccessorInfo() &&
+ AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) {
+ compile_followup_inline = true;
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, name, miss);
+ ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ __ EnterInternalFrame();
+
+ if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
+ // CALLBACKS case needs a receiver to be passed into C++ callback.
+ __ push(receiver);
+ }
+ __ push(holder_reg);
+ __ push(name_reg);
+
+ // Invoke an interceptor. Note: map checks from receiver to
+ // interceptor's holder has been compiled before (see a caller
+ // of this method.)
+ CompileCallLoadPropertyWithInterceptor(masm(),
+ receiver,
+ holder_reg,
+ name_reg,
+ interceptor_holder);
+
+ // Check if interceptor provided a value for property. If it's
+ // the case, return immediately.
+ Label interceptor_failed;
+ __ cmp(eax, Factory::no_interceptor_result_sentinel());
+ __ j(equal, &interceptor_failed);
+ __ LeaveInternalFrame();
+ __ ret(0);
+
+ __ bind(&interceptor_failed);
+ __ pop(name_reg);
+ __ pop(holder_reg);
+ if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
+ __ pop(receiver);
+ }
+
+ __ LeaveInternalFrame();
+
+ // Check that the maps from interceptor's holder to lookup's holder
+ // haven't changed. And load lookup's holder into holder_reg.
+ if (interceptor_holder != lookup->holder()) {
+ holder_reg = CheckPrototypes(interceptor_holder,
+ holder_reg,
+ lookup->holder(),
+ scratch1,
+ scratch2,
+ name,
+ miss);
+ }
+
+ if (lookup->type() == FIELD) {
+ // We found FIELD property in prototype chain of interceptor's holder.
+ // Retrieve a field from field's holder.
+ GenerateFastPropertyLoad(masm(), eax, holder_reg,
+ lookup->holder(), lookup->GetFieldIndex());
+ __ ret(0);
+ } else {
+ // We found CALLBACKS property in prototype chain of interceptor's
+ // holder.
+ ASSERT(lookup->type() == CALLBACKS);
+ ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
+ AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
+ ASSERT(callback != NULL);
+ ASSERT(callback->getter() != NULL);
+
+ // Tail call to runtime.
+ // Important invariant in CALLBACKS case: the code above must be
+ // structured to never clobber |receiver| register.
+ __ pop(scratch2); // return address
+ __ push(receiver);
+ __ push(holder_reg);
+ __ mov(holder_reg, Immediate(Handle<AccessorInfo>(callback)));
+ __ push(holder_reg);
+ __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset));
+ __ push(name_reg);
+ __ push(scratch2); // restore return address
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
+ __ TailCallExternalReference(ref, 5, 1);
+ }
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ Register holder_reg =
+ CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, name, miss);
+ __ pop(scratch2); // save old return address
+ PushInterceptorArguments(masm(), receiver, holder_reg,
+ name_reg, interceptor_holder);
+ __ push(scratch2); // restore old return address
+
+ ExternalReference ref = ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
+ __ TailCallExternalReference(ref, 5, 1);
+ }
}
@@ -1206,7 +1128,7 @@
__ j(not_equal, &miss);
if (argc == 1) { // Otherwise fall through to call builtin.
- Label call_builtin, exit, with_rset_update, attempt_to_grow_elements;
+ Label call_builtin, exit, with_write_barrier, attempt_to_grow_elements;
// Get the array's length into eax and calculate new length.
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
@@ -1216,7 +1138,6 @@
// Get the element's length into ecx.
__ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
- __ SmiTag(ecx);
// Check if we could survive without allocation.
__ cmp(eax, Operand(ecx));
@@ -1234,17 +1155,16 @@
// Check if value is a smi.
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &with_rset_update);
+ __ j(not_zero, &with_write_barrier);
__ bind(&exit);
__ ret((argc + 1) * kPointerSize);
- __ bind(&with_rset_update);
+ __ bind(&with_write_barrier);
__ InNewSpace(ebx, ecx, equal, &exit);
- RecordWriteStub stub(ebx, edx, ecx);
- __ CallStub(&stub);
+ __ RecordWriteHelper(ebx, edx, ecx);
__ ret((argc + 1) * kPointerSize);
__ bind(&attempt_to_grow_elements);
@@ -1284,10 +1204,10 @@
// Increment element's and array's sizes.
__ add(FieldOperand(ebx, FixedArray::kLengthOffset),
- Immediate(kAllocationDelta));
+ Immediate(Smi::FromInt(kAllocationDelta)));
__ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
- // Elements are in new space, so no remembered set updates are necessary.
+ // Elements are in new space, so write barrier is not required.
__ ret((argc + 1) * kPointerSize);
__ bind(&call_builtin);
@@ -1389,6 +1309,140 @@
}
+Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- ecx : function name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ const int argc = arguments().immediate();
+
+ Label miss;
+ Label index_out_of_range;
+
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ eax);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
+ ebx, edx, name, &miss);
+
+ Register receiver = ebx;
+ Register index = ecx;
+ Register scratch = edx;
+ Register result = eax;
+ __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
+ if (argc > 0) {
+ __ mov(index, Operand(esp, (argc - 0) * kPointerSize));
+ } else {
+ __ Set(index, Immediate(Factory::undefined_value()));
+ }
+
+ StringCharCodeAtGenerator char_code_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ char_code_at_generator.GenerateFast(masm());
+ __ ret((argc + 1) * kPointerSize);
+
+ ICRuntimeCallHelper call_helper;
+ char_code_at_generator.GenerateSlow(masm(), call_helper);
+
+ __ bind(&index_out_of_range);
+ __ Set(eax, Immediate(Factory::nan_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&miss);
+ // Restore function name in ecx.
+ __ Set(ecx, Immediate(Handle<String>(name)));
+
+ Handle<Code> ic = ComputeCallMiss(argc);
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- ecx : function name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ const int argc = arguments().immediate();
+
+ Label miss;
+ Label index_out_of_range;
+
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ eax);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
+ ebx, edx, name, &miss);
+
+ Register receiver = eax;
+ Register index = ecx;
+ Register scratch1 = ebx;
+ Register scratch2 = edx;
+ Register result = eax;
+ __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
+ if (argc > 0) {
+ __ mov(index, Operand(esp, (argc - 0) * kPointerSize));
+ } else {
+ __ Set(index, Immediate(Factory::undefined_value()));
+ }
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ char_at_generator.GenerateFast(masm());
+ __ ret((argc + 1) * kPointerSize);
+
+ ICRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm(), call_helper);
+
+ __ bind(&index_out_of_range);
+ __ Set(eax, Immediate(Factory::empty_string()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&miss);
+ // Restore function name in ecx.
+ __ Set(ecx, Immediate(Handle<String>(name)));
+
+ Handle<Code> ic = ComputeCallMiss(argc);
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h
index a8f23b0..48d0fa2 100644
--- a/src/ia32/virtual-frame-ia32.h
+++ b/src/ia32/virtual-frame-ia32.h
@@ -615,7 +615,7 @@
inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
- friend class DeferredCode;
+ friend class FrameRegisterState;
friend class JumpTarget;
};