Optimize KeyedStoreGeneric for Smi arrays.
BUG= TEST= Review URL: http://codereview.chromium.org/8022002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9456 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
f2ff85ad71
commit
1b5a2381ec
@ -734,7 +734,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
|
|||||||
// -- edx : receiver
|
// -- edx : receiver
|
||||||
// -- esp[0] : return address
|
// -- esp[0] : return address
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
Label slow, fast, array, extra;
|
Label slow, fast_object_with_map_check, fast_object_without_map_check;
|
||||||
|
Label fast_double_with_map_check, fast_double_without_map_check;
|
||||||
|
Label check_if_double_array, array, extra;
|
||||||
|
|
||||||
// Check that the object isn't a smi.
|
// Check that the object isn't a smi.
|
||||||
__ JumpIfSmi(edx, &slow);
|
__ JumpIfSmi(edx, &slow);
|
||||||
@ -761,11 +763,11 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
|
|||||||
// eax: value
|
// eax: value
|
||||||
// edx: JSObject
|
// edx: JSObject
|
||||||
// ecx: key (a smi)
|
// ecx: key (a smi)
|
||||||
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
|
// edi: receiver map
|
||||||
|
__ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
|
||||||
// Check that the object is in fast mode and writable.
|
// Check that the object is in fast mode and writable.
|
||||||
__ CheckMap(edi, FACTORY->fixed_array_map(), &slow, DONT_DO_SMI_CHECK);
|
__ cmp(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
|
||||||
__ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
|
__ j(below, &fast_object_with_map_check);
|
||||||
__ j(below, &fast);
|
|
||||||
|
|
||||||
// Slow case: call runtime.
|
// Slow case: call runtime.
|
||||||
__ bind(&slow);
|
__ bind(&slow);
|
||||||
@ -778,16 +780,26 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
|
|||||||
// eax: value
|
// eax: value
|
||||||
// edx: receiver, a JSArray
|
// edx: receiver, a JSArray
|
||||||
// ecx: key, a smi.
|
// ecx: key, a smi.
|
||||||
// edi: receiver->elements, a FixedArray
|
// ebx: receiver->elements, a FixedArray
|
||||||
|
// edi: receiver map
|
||||||
// flags: compare (ecx, edx.length())
|
// flags: compare (ecx, edx.length())
|
||||||
// do not leave holes in the array:
|
// do not leave holes in the array:
|
||||||
__ j(not_equal, &slow);
|
__ j(not_equal, &slow);
|
||||||
__ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
|
__ cmp(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
|
||||||
__ j(above_equal, &slow);
|
__ j(above_equal, &slow);
|
||||||
// Add 1 to receiver->length, and go to fast array write.
|
__ CheckMap(ebx, FACTORY->fixed_array_map(),
|
||||||
|
&check_if_double_array, DONT_DO_SMI_CHECK);
|
||||||
|
// Add 1 to receiver->length, and go to common element store code for Objects.
|
||||||
__ add(FieldOperand(edx, JSArray::kLengthOffset),
|
__ add(FieldOperand(edx, JSArray::kLengthOffset),
|
||||||
Immediate(Smi::FromInt(1)));
|
Immediate(Smi::FromInt(1)));
|
||||||
__ jmp(&fast);
|
__ jmp(&fast_object_without_map_check);
|
||||||
|
|
||||||
|
__ bind(&check_if_double_array);
|
||||||
|
__ CheckMap(ebx, FACTORY->fixed_double_array_map(), &slow, DONT_DO_SMI_CHECK);
|
||||||
|
// Add 1 to receiver->length, and go to common element store code for doubles.
|
||||||
|
__ add(FieldOperand(edx, JSArray::kLengthOffset),
|
||||||
|
Immediate(Smi::FromInt(1)));
|
||||||
|
__ jmp(&fast_double_without_map_check);
|
||||||
|
|
||||||
// Array case: Get the length and the elements array from the JS
|
// Array case: Get the length and the elements array from the JS
|
||||||
// array. Check that the array is in fast mode (and writable); if it
|
// array. Check that the array is in fast mode (and writable); if it
|
||||||
@ -796,39 +808,59 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
|
|||||||
// eax: value
|
// eax: value
|
||||||
// edx: receiver, a JSArray
|
// edx: receiver, a JSArray
|
||||||
// ecx: key, a smi.
|
// ecx: key, a smi.
|
||||||
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
|
// edi: receiver map
|
||||||
__ CheckMap(edi, FACTORY->fixed_array_map(), &slow, DONT_DO_SMI_CHECK);
|
__ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
|
||||||
|
|
||||||
// Check the key against the length in the array, compute the
|
// Check the key against the length in the array and fall through to the
|
||||||
// address to store into and fall through to fast case.
|
// common store code.
|
||||||
__ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
|
__ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
|
||||||
__ j(above_equal, &extra);
|
__ j(above_equal, &extra);
|
||||||
|
|
||||||
// Fast case: Do the store.
|
// Fast case: Do the store, could either Object or double.
|
||||||
__ bind(&fast);
|
__ bind(&fast_object_with_map_check);
|
||||||
// eax: value
|
// eax: value
|
||||||
// ecx: key (a smi)
|
// ecx: key (a smi)
|
||||||
// edx: receiver
|
// edx: receiver
|
||||||
// edi: FixedArray receiver->elements
|
// ebx: FixedArray receiver->elements
|
||||||
|
// edi: receiver map
|
||||||
|
__ CheckMap(ebx, FACTORY->fixed_array_map(),
|
||||||
|
&fast_double_with_map_check, DONT_DO_SMI_CHECK);
|
||||||
|
__ bind(&fast_object_without_map_check);
|
||||||
|
// Smi stores don't require further checks.
|
||||||
Label non_smi_value;
|
Label non_smi_value;
|
||||||
__ JumpIfNotSmi(eax, &non_smi_value);
|
__ JumpIfNotSmi(eax, &non_smi_value);
|
||||||
// It's irrelevant whether array is smi-only or not when writing a smi.
|
// It's irrelevant whether array is smi-only or not when writing a smi.
|
||||||
__ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax);
|
__ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax);
|
||||||
__ ret(0);
|
__ ret(0);
|
||||||
|
|
||||||
__ bind(&non_smi_value);
|
__ bind(&non_smi_value);
|
||||||
if (FLAG_smi_only_arrays) {
|
if (FLAG_smi_only_arrays) {
|
||||||
// Escape to slow case when writing non-smi into smi-only array.
|
// Escape to slow case when writing non-smi into smi-only array.
|
||||||
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
|
__ CheckFastObjectElements(edi, &slow, Label::kNear);
|
||||||
__ CheckFastObjectElements(ebx, &slow, Label::kNear);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast elements array, store the value to the elements backing store.
|
// Fast elements array, store the value to the elements backing store.
|
||||||
__ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax);
|
__ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax);
|
||||||
// Update write barrier for the elements array address.
|
// Update write barrier for the elements array address.
|
||||||
__ mov(edx, Operand(eax)); // Preserve the value which is returned.
|
__ mov(edx, Operand(eax)); // Preserve the value which is returned.
|
||||||
__ RecordWriteArray(
|
__ RecordWriteArray(
|
||||||
edi, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
|
ebx, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
|
||||||
|
__ ret(0);
|
||||||
|
|
||||||
|
__ bind(&fast_double_with_map_check);
|
||||||
|
// Check for fast double array case. If this fails, call through to the
|
||||||
|
// runtime.
|
||||||
|
__ CheckMap(ebx, FACTORY->fixed_double_array_map(), &slow, DONT_DO_SMI_CHECK);
|
||||||
|
__ bind(&fast_double_without_map_check);
|
||||||
|
// If the value is a number, store it as a double in the FastDoubleElements
|
||||||
|
// array.
|
||||||
|
__ StoreNumberToDoubleElements(eax,
|
||||||
|
ebx,
|
||||||
|
ecx,
|
||||||
|
edx,
|
||||||
|
xmm0,
|
||||||
|
&slow,
|
||||||
|
false);
|
||||||
__ ret(0);
|
__ ret(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +406,79 @@ void MacroAssembler::CheckFastSmiOnlyElements(Register map,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MacroAssembler::StoreNumberToDoubleElements(
|
||||||
|
Register maybe_number,
|
||||||
|
Register elements,
|
||||||
|
Register key,
|
||||||
|
Register scratch1,
|
||||||
|
XMMRegister scratch2,
|
||||||
|
Label* fail,
|
||||||
|
bool specialize_for_processor) {
|
||||||
|
Label smi_value, done, maybe_nan, not_nan, is_nan, have_double_value;
|
||||||
|
JumpIfSmi(maybe_number, &smi_value, Label::kNear);
|
||||||
|
|
||||||
|
CheckMap(maybe_number,
|
||||||
|
isolate()->factory()->heap_number_map(),
|
||||||
|
fail,
|
||||||
|
DONT_DO_SMI_CHECK);
|
||||||
|
|
||||||
|
// Double value, canonicalize NaN.
|
||||||
|
uint32_t offset = HeapNumber::kValueOffset + sizeof(kHoleNanLower32);
|
||||||
|
cmp(FieldOperand(maybe_number, offset),
|
||||||
|
Immediate(kNaNOrInfinityLowerBoundUpper32));
|
||||||
|
j(greater_equal, &maybe_nan, Label::kNear);
|
||||||
|
|
||||||
|
bind(¬_nan);
|
||||||
|
ExternalReference canonical_nan_reference =
|
||||||
|
ExternalReference::address_of_canonical_non_hole_nan();
|
||||||
|
if (CpuFeatures::IsSupported(SSE2) && specialize_for_processor) {
|
||||||
|
CpuFeatures::Scope use_sse2(SSE2);
|
||||||
|
movdbl(scratch2, FieldOperand(maybe_number, HeapNumber::kValueOffset));
|
||||||
|
bind(&have_double_value);
|
||||||
|
movdbl(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize),
|
||||||
|
scratch2);
|
||||||
|
} else {
|
||||||
|
fld_d(FieldOperand(maybe_number, HeapNumber::kValueOffset));
|
||||||
|
bind(&have_double_value);
|
||||||
|
fstp_d(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize));
|
||||||
|
}
|
||||||
|
jmp(&done);
|
||||||
|
|
||||||
|
bind(&maybe_nan);
|
||||||
|
// Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise
|
||||||
|
// it's an Infinity, and the non-NaN code path applies.
|
||||||
|
j(greater, &is_nan, Label::kNear);
|
||||||
|
cmp(FieldOperand(maybe_number, HeapNumber::kValueOffset), Immediate(0));
|
||||||
|
j(zero, ¬_nan);
|
||||||
|
bind(&is_nan);
|
||||||
|
if (CpuFeatures::IsSupported(SSE2) && specialize_for_processor) {
|
||||||
|
CpuFeatures::Scope use_sse2(SSE2);
|
||||||
|
movdbl(scratch2, Operand::StaticVariable(canonical_nan_reference));
|
||||||
|
} else {
|
||||||
|
fld_d(Operand::StaticVariable(canonical_nan_reference));
|
||||||
|
}
|
||||||
|
jmp(&have_double_value, Label::kNear);
|
||||||
|
|
||||||
|
bind(&smi_value);
|
||||||
|
// Value is a smi. Convert to a double and store.
|
||||||
|
// Preserve original value.
|
||||||
|
mov(scratch1, maybe_number);
|
||||||
|
SmiUntag(scratch1);
|
||||||
|
if (CpuFeatures::IsSupported(SSE2) && specialize_for_processor) {
|
||||||
|
CpuFeatures::Scope fscope(SSE2);
|
||||||
|
cvtsi2sd(scratch2, Operand(scratch1));
|
||||||
|
movdbl(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize),
|
||||||
|
scratch2);
|
||||||
|
} else {
|
||||||
|
push(scratch1);
|
||||||
|
fild_s(Operand(esp, 0));
|
||||||
|
pop(scratch1);
|
||||||
|
fstp_d(FieldOperand(elements, key, times_4, FixedDoubleArray::kHeaderSize));
|
||||||
|
}
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MacroAssembler::CheckMap(Register obj,
|
void MacroAssembler::CheckMap(Register obj,
|
||||||
Handle<Map> map,
|
Handle<Map> map,
|
||||||
Label* fail,
|
Label* fail,
|
||||||
|
@ -324,6 +324,17 @@ class MacroAssembler: public Assembler {
|
|||||||
Label* fail,
|
Label* fail,
|
||||||
Label::Distance distance = Label::kFar);
|
Label::Distance distance = Label::kFar);
|
||||||
|
|
||||||
|
// Check to see if maybe_number can be stored as a double in
|
||||||
|
// FastDoubleElements. If it can, store it at the index specified by key in
|
||||||
|
// the FastDoubleElements array elements, otherwise jump to fail.
|
||||||
|
void StoreNumberToDoubleElements(Register maybe_number,
|
||||||
|
Register elements,
|
||||||
|
Register key,
|
||||||
|
Register scratch1,
|
||||||
|
XMMRegister scratch2,
|
||||||
|
Label* fail,
|
||||||
|
bool specialize_for_processor);
|
||||||
|
|
||||||
// Check if the map of an object is equal to a specified map and branch to
|
// Check if the map of an object is equal to a specified map and branch to
|
||||||
// label if not. Skip the smi check if not required (object is known to be a
|
// label if not. Skip the smi check if not required (object is known to be a
|
||||||
// heap object)
|
// heap object)
|
||||||
|
@ -3977,8 +3977,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
|
|||||||
// -- edx : receiver
|
// -- edx : receiver
|
||||||
// -- esp[0] : return address
|
// -- esp[0] : return address
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
Label miss_force_generic, smi_value, is_nan, maybe_nan;
|
Label miss_force_generic;
|
||||||
Label have_double_value, not_nan;
|
|
||||||
|
|
||||||
// This stub is meant to be tail-jumped to, the receiver must already
|
// This stub is meant to be tail-jumped to, the receiver must already
|
||||||
// have been verified by the caller to not be a smi.
|
// have been verified by the caller to not be a smi.
|
||||||
@ -3999,59 +3998,13 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
|
|||||||
}
|
}
|
||||||
__ j(above_equal, &miss_force_generic);
|
__ j(above_equal, &miss_force_generic);
|
||||||
|
|
||||||
__ JumpIfSmi(eax, &smi_value, Label::kNear);
|
__ StoreNumberToDoubleElements(eax,
|
||||||
|
edi,
|
||||||
__ CheckMap(eax,
|
ecx,
|
||||||
masm->isolate()->factory()->heap_number_map(),
|
edx,
|
||||||
&miss_force_generic,
|
xmm0,
|
||||||
DONT_DO_SMI_CHECK);
|
&miss_force_generic,
|
||||||
|
true);
|
||||||
// Double value, canonicalize NaN.
|
|
||||||
uint32_t offset = HeapNumber::kValueOffset + sizeof(kHoleNanLower32);
|
|
||||||
__ cmp(FieldOperand(eax, offset), Immediate(kNaNOrInfinityLowerBoundUpper32));
|
|
||||||
__ j(greater_equal, &maybe_nan, Label::kNear);
|
|
||||||
|
|
||||||
__ bind(¬_nan);
|
|
||||||
ExternalReference canonical_nan_reference =
|
|
||||||
ExternalReference::address_of_canonical_non_hole_nan();
|
|
||||||
if (CpuFeatures::IsSupported(SSE2)) {
|
|
||||||
CpuFeatures::Scope use_sse2(SSE2);
|
|
||||||
__ movdbl(xmm0, FieldOperand(eax, HeapNumber::kValueOffset));
|
|
||||||
__ bind(&have_double_value);
|
|
||||||
__ movdbl(FieldOperand(edi, ecx, times_4, FixedDoubleArray::kHeaderSize),
|
|
||||||
xmm0);
|
|
||||||
__ ret(0);
|
|
||||||
} else {
|
|
||||||
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
|
|
||||||
__ bind(&have_double_value);
|
|
||||||
__ fstp_d(FieldOperand(edi, ecx, times_4, FixedDoubleArray::kHeaderSize));
|
|
||||||
__ ret(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
__ bind(&maybe_nan);
|
|
||||||
// Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise
|
|
||||||
// it's an Infinity, and the non-NaN code path applies.
|
|
||||||
__ j(greater, &is_nan, Label::kNear);
|
|
||||||
__ cmp(FieldOperand(eax, HeapNumber::kValueOffset), Immediate(0));
|
|
||||||
__ j(zero, ¬_nan);
|
|
||||||
__ bind(&is_nan);
|
|
||||||
if (CpuFeatures::IsSupported(SSE2)) {
|
|
||||||
CpuFeatures::Scope use_sse2(SSE2);
|
|
||||||
__ movdbl(xmm0, Operand::StaticVariable(canonical_nan_reference));
|
|
||||||
} else {
|
|
||||||
__ fld_d(Operand::StaticVariable(canonical_nan_reference));
|
|
||||||
}
|
|
||||||
__ jmp(&have_double_value, Label::kNear);
|
|
||||||
|
|
||||||
__ bind(&smi_value);
|
|
||||||
// Value is a smi. convert to a double and store.
|
|
||||||
// Preserve original value.
|
|
||||||
__ mov(edx, eax);
|
|
||||||
__ SmiUntag(edx);
|
|
||||||
__ push(edx);
|
|
||||||
__ fild_s(Operand(esp, 0));
|
|
||||||
__ pop(edx);
|
|
||||||
__ fstp_d(FieldOperand(edi, ecx, times_4, FixedDoubleArray::kHeaderSize));
|
|
||||||
__ ret(0);
|
__ ret(0);
|
||||||
|
|
||||||
// Handle store cache miss, replacing the ic with the generic stub.
|
// Handle store cache miss, replacing the ic with the generic stub.
|
||||||
|
@ -494,6 +494,10 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) {
|
|||||||
UNCLASSIFIED,
|
UNCLASSIFIED,
|
||||||
44,
|
44,
|
||||||
"Factory::arguments_marker().location()");
|
"Factory::arguments_marker().location()");
|
||||||
|
Add(ExternalReference::address_of_canonical_non_hole_nan().address(),
|
||||||
|
UNCLASSIFIED,
|
||||||
|
45,
|
||||||
|
"canonical_nan");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user