const Register StoreDescriptor::ValueRegister() { return r0; }
+const Register ExtendStorageDescriptor::MapRegister() { return r3; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() { return r3; }
const Register StoreDescriptor::ValueRegister() { return x0; }
+const Register ExtendStorageDescriptor::MapRegister() { return x3; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() { return x3; }
HLoadNamedField* BuildLoadNamedField(HValue* object,
FieldIndex index);
void BuildStoreNamedField(HValue* object, HValue* value, FieldIndex index,
- Representation representation);
+ Representation representation,
+ bool transition_to_field);
enum ArgumentClass {
NONE,
void CodeStubGraphBuilderBase::BuildStoreNamedField(
HValue* object, HValue* value, FieldIndex index,
- Representation representation) {
+ Representation representation, bool transition_to_field) {
DCHECK(!index.is_double() || representation.IsDouble());
int offset = index.offset();
HObjectAccess access =
: HObjectAccess::ForBackingStoreOffset(offset, representation);
if (representation.IsDouble()) {
- // Load the heap number.
- object = Add<HLoadNamedField>(
- object, static_cast<HValue*>(NULL),
- access.WithRepresentation(Representation::Tagged()));
- // Store the double value into it.
- access = HObjectAccess::ForHeapNumberValue();
+ HObjectAccess heap_number_access =
+ access.WithRepresentation(Representation::Tagged());
+ if (transition_to_field) {
+ // The store requires a mutable HeapNumber to be allocated.
+ NoObservableSideEffectsScope no_side_effects(this);
+ HInstruction* heap_number_size = Add<HConstant>(HeapNumber::kSize);
+
+ // TODO(hpayer): Allocation site pretenuring support.
+ HInstruction* heap_number =
+ Add<HAllocate>(heap_number_size, HType::HeapObject(), NOT_TENURED,
+ MUTABLE_HEAP_NUMBER_TYPE);
+ AddStoreMapConstant(heap_number,
+ isolate()->factory()->mutable_heap_number_map());
+ Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
+ value);
+ // Store the new mutable heap number into the object.
+ access = heap_number_access;
+ value = heap_number;
+ } else {
+ // Load the heap number.
+ object = Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
+ heap_number_access);
+ // Store the double value into it.
+ access = HObjectAccess::ForHeapNumberValue();
+ }
} else if (representation.IsHeapObject()) {
BuildCheckHeapObject(value);
}
template <>
HValue* CodeStubGraphBuilder<StoreFieldStub>::BuildCodeStub() {
BuildStoreNamedField(GetParameter(0), GetParameter(2), casted_stub()->index(),
- casted_stub()->representation());
+ casted_stub()->representation(), false);
return GetParameter(2);
}
template <>
+HValue* CodeStubGraphBuilder<ExtendStorageStub>::BuildCodeStub() {
+ HValue* object = GetParameter(ExtendStorageDescriptor::kReceiverIndex);
+ HValue* properties =
+ Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
+ HObjectAccess::ForPropertiesPointer());
+ HValue* length = AddLoadFixedArrayLength(properties);
+ HValue* delta = Add<HConstant>(static_cast<int32_t>(JSObject::kFieldsAdded));
+ HValue* new_capacity = AddUncasted<HAdd>(length, delta);
+
+ // Grow properties array.
+ ElementsKind kind = FAST_ELEMENTS;
+ Add<HBoundsCheck>(new_capacity,
+ Add<HConstant>((Page::kMaxRegularHeapObjectSize -
+ FixedArray::kHeaderSize) >>
+ ElementsKindToShiftSize(kind)));
+
+ // Reuse this code for properties backing store allocation.
+ HValue* new_properties = BuildAllocateAndInitializeArray(kind, new_capacity);
+
+ BuildCopyProperties(properties, new_properties, length, new_capacity);
+
+ // Store the new value into the "extended" object.
+ Add<HStoreNamedField>(object, HObjectAccess::ForPropertiesPointer(),
+ new_properties);
+
+ BuildStoreNamedField(
+ object, GetParameter(ExtendStorageDescriptor::kValueIndex),
+ casted_stub()->index(), casted_stub()->representation(), true);
+
+ // And finally update the map after the new field is added.
+ Add<HStoreNamedField>(object, HObjectAccess::ForMap(),
+ GetParameter(ExtendStorageDescriptor::kMapIndex));
+
+ return GetParameter(ExtendStorageDescriptor::kValueIndex);
+}
+
+
+Handle<Code> ExtendStorageStub::GenerateCode() { return DoGenerateCode(this); }
+
+
+template <>
HValue* CodeStubGraphBuilder<StringLengthStub>::BuildCodeStub() {
HValue* string = BuildLoadNamedField(GetParameter(0),
FieldIndex::ForInObjectOffset(JSValue::kValueOffset));
}
+CallInterfaceDescriptor ExtendStorageStub::GetCallInterfaceDescriptor() {
+ return ExtendStorageDescriptor(isolate());
+}
+
+
static void InitializeVectorLoadStub(Isolate* isolate,
CodeStubDescriptor* descriptor,
Address deoptimization_handler) {
V(VectorKeyedLoad) \
V(VectorLoad) \
/* IC Handler stubs */ \
+ V(ExtendStorage) \
V(LoadConstant) \
V(LoadField) \
V(KeyedLoadSloppyArguments) \
};
+// Extend storage is called in a store inline cache when
+// it is necessary to extend the properties array of a
+// JSObject.
+class ExtendStorageStub : public HandlerStub {
+ public:
+ ExtendStorageStub(Isolate* isolate, FieldIndex index,
+ Representation representation)
+ : HandlerStub(isolate) {
+ int property_index_key = index.GetFieldAccessStubKey();
+ uint8_t repr = PropertyDetails::EncodeRepresentation(representation);
+ set_sub_minor_key(StoreFieldByIndexBits::encode(property_index_key) |
+ RepresentationBits::encode(repr));
+ }
+
+ FieldIndex index() const {
+ int property_index_key = StoreFieldByIndexBits::decode(sub_minor_key());
+ return FieldIndex::FromFieldAccessStubKey(property_index_key);
+ }
+
+ Representation representation() {
+ uint8_t repr = RepresentationBits::decode(sub_minor_key());
+ return PropertyDetails::DecodeRepresentation(repr);
+ }
+
+ virtual CallInterfaceDescriptor GetCallInterfaceDescriptor() OVERRIDE;
+
+ protected:
+ virtual Code::Kind kind() const { return Code::STORE_IC; }
+ virtual Code::StubType GetStubType() { return Code::FAST; }
+
+ private:
+ class StoreFieldByIndexBits : public BitField<int, 0, 13> {};
+ class RepresentationBits : public BitField<uint8_t, 13, 4> {};
+
+ DEFINE_HANDLER_CODE_STUB(ExtendStorage, HandlerStub);
+};
+
+
class StoreGlobalStub : public HandlerStub {
public:
StoreGlobalStub(Isolate* isolate, bool is_constant, bool check_global)
}
-HValue* HGraphBuilder::BuildAllocateElementsAndInitializeElementsHeader(
- ElementsKind kind,
- HValue* capacity) {
+HValue* HGraphBuilder::BuildAllocateAndInitializeArray(ElementsKind kind,
+ HValue* capacity) {
// The HForceRepresentation is to prevent possible deopt on int-smi
// conversion after allocation but before the new object fields are set.
capacity = AddUncasted<HForceRepresentation>(capacity, Representation::Smi());
HValue* size_in_bytes = BuildCalculateElementsSize(kind, capacity);
- HValue* new_elements = BuildAllocateElements(kind, size_in_bytes);
- BuildInitializeElementsHeader(new_elements, kind, capacity);
- return new_elements;
+ HValue* new_array = BuildAllocateElements(kind, size_in_bytes);
+ BuildInitializeElementsHeader(new_array, kind, capacity);
+ return new_array;
}
(Page::kMaxRegularHeapObjectSize - FixedArray::kHeaderSize) >>
ElementsKindToShiftSize(new_kind)));
- HValue* new_elements = BuildAllocateElementsAndInitializeElementsHeader(
- new_kind, new_capacity);
+ HValue* new_elements =
+ BuildAllocateAndInitializeArray(new_kind, new_capacity);
BuildCopyElements(elements, kind, new_elements,
new_kind, length, new_capacity);
}
}
- // Since we're about to store a hole value, the store instruction below must
- // assume an elements kind that supports heap object values.
- if (IsFastSmiOrObjectElementsKind(elements_kind)) {
- elements_kind = FAST_HOLEY_ELEMENTS;
- }
-
if (initial_capacity >= 0) {
for (int i = 0; i < initial_capacity; i++) {
HInstruction* key = Add<HConstant>(i);
? Add<HConstant>(factory->the_hole_value())
: Add<HConstant>(nan_double);
+ // Since we're about to store a hole value, the store instruction below must
+ // assume an elements kind that supports heap object values.
+ if (IsFastSmiOrObjectElementsKind(elements_kind)) {
+ elements_kind = FAST_HOLEY_ELEMENTS;
+ }
+
BuildFillElementsWithValue(elements, elements_kind, from, to, hole);
}
+void HGraphBuilder::BuildCopyProperties(HValue* from_properties,
+ HValue* to_properties, HValue* length,
+ HValue* capacity) {
+ ElementsKind kind = FAST_ELEMENTS;
+
+ BuildFillElementsWithValue(to_properties, kind, length, capacity,
+ graph()->GetConstantUndefined());
+
+ LoopBuilder builder(this, context(), LoopBuilder::kPostDecrement);
+
+ HValue* key = builder.BeginBody(length, graph()->GetConstant0(), Token::GT);
+
+ key = AddUncasted<HSub>(key, graph()->GetConstant1());
+ key->ClearFlag(HValue::kCanOverflow);
+
+ HValue* element =
+ Add<HLoadKeyed>(from_properties, key, static_cast<HValue*>(NULL), kind);
+
+ Add<HStoreKeyed>(to_properties, key, element, kind);
+
+ builder.EndBody();
+}
+
+
void HGraphBuilder::BuildCopyElements(HValue* from_elements,
ElementsKind from_elements_kind,
HValue* to_elements,
ElementsKind kind,
HValue* capacity);
- HValue* BuildAllocateElementsAndInitializeElementsHeader(ElementsKind kind,
- HValue* capacity);
+ // Build allocation and header initialization code for respective successor
+ // of FixedArrayBase.
+ HValue* BuildAllocateAndInitializeArray(ElementsKind kind, HValue* capacity);
// |array| must have been allocated with enough room for
// 1) the JSArray and 2) an AllocationMemento if mode requires it.
HValue* from,
HValue* to);
+ void BuildCopyProperties(HValue* from_properties, HValue* to_properties,
+ HValue* length, HValue* capacity);
+
void BuildCopyElements(HValue* from_elements,
ElementsKind from_elements_kind,
HValue* to_elements,
const Register StoreDescriptor::ValueRegister() { return eax; }
+const Register ExtendStorageDescriptor::MapRegister() { return ebx; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() {
return ebx;
}
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ push(receiver_reg);
- __ mov(r2, Operand(transition));
- __ Push(r2, r0);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ mov(ExtendStorageDescriptor::NameRegister(), Operand(name));
+ __ mov(ExtendStorageDescriptor::MapRegister(), Operand(transition));
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ Mov(scratch1, Operand(transition));
- __ Push(receiver_reg, scratch1, value_reg);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ Mov(ExtendStorageDescriptor::NameRegister(), Operand(name));
+ __ Mov(ExtendStorageDescriptor::MapRegister(), Operand(transition));
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ pop(scratch1); // Return address.
- __ push(receiver_reg);
- __ push(Immediate(transition));
- __ push(value_reg);
- __ push(scratch1);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ mov(ExtendStorageDescriptor::NameRegister(), Immediate(name));
+ __ mov(ExtendStorageDescriptor::MapRegister(), Immediate(transition));
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
RUNTIME_FUNCTION(StoreIC_MissFromStubFailure) {
TimerEventScope<TimerEventIcMiss> timer(isolate);
HandleScope scope(isolate);
- DCHECK(args.length() == 3);
+ DCHECK(args.length() == 3 || args.length() == 4);
StoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
Handle<Object> receiver = args.at<Object>(0);
Handle<String> key = args.at<String>(1);
}
-// Extend storage is called in a store inline cache when
-// it is necessary to extend the properties array of a
-// JSObject.
-RUNTIME_FUNCTION(SharedStoreIC_ExtendStorage) {
- TimerEventScope<TimerEventIcMiss> timer(isolate);
- HandleScope shs(isolate);
- DCHECK(args.length() == 3);
-
- // Convert the parameters
- Handle<JSObject> object = args.at<JSObject>(0);
- Handle<Map> transition = args.at<Map>(1);
- Handle<Object> value = args.at<Object>(2);
-
- // Check the object has run out out property space.
- DCHECK(object->HasFastProperties());
- DCHECK(object->map()->unused_property_fields() == 0);
-
- JSObject::MigrateToNewProperty(object, transition, value);
-
- // Return the stored value.
- return *value;
-}
-
-
// Used from ic-<arch>.cc.
RUNTIME_FUNCTION(KeyedStoreIC_Miss) {
TimerEventScope<TimerEventIcMiss> timer(isolate);
ICU(CallIC_Customization_Miss) \
ICU(StoreIC_Miss) \
ICU(StoreIC_Slow) \
- ICU(SharedStoreIC_ExtendStorage) \
ICU(KeyedStoreIC_Miss) \
ICU(KeyedStoreIC_Slow) \
/* Utilities for IC stubs. */ \
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ push(receiver_reg);
- __ li(a2, Operand(transition));
- __ Push(a2, a0);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ mov(ExtendStorageDescriptor::NameRegister(), Operand(name));
+ __ mov(ExtendStorageDescriptor::MapRegister(), Operand(transition));
+
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ push(receiver_reg);
- __ li(a2, Operand(transition));
- __ Push(a2, a0);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ Mov(ExtendStorageDescriptor::NameRegister(), Operand(name));
+ __ Mov(ExtendStorageDescriptor::MapRegister(), Operand(transition));
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ PopReturnAddressTo(scratch1);
- __ Push(receiver_reg);
- __ Push(transition);
- __ Push(value_reg);
- __ PushReturnAddressFrom(scratch1);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ Move(ExtendStorageDescriptor::NameRegister(), name);
+ __ Move(ExtendStorageDescriptor::MapRegister(), transition);
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
if (details.type() == FIELD &&
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ pop(scratch1); // Return address.
- __ push(receiver_reg);
- __ push(Immediate(transition));
- __ push(value_reg);
- __ push(scratch1);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
- isolate()),
- 3, 1);
+ __ mov(ExtendStorageDescriptor::NameRegister(), Immediate(name));
+ __ mov(ExtendStorageDescriptor::MapRegister(), Immediate(transition));
+
+ ExtendStorageStub stub(isolate(),
+ FieldIndex::ForDescriptor(*transition, descriptor),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode());
return;
}
}
+void ExtendStorageDescriptor::Initialize(CallInterfaceDescriptorData* data) {
+ Register registers[] = {ContextRegister(), ReceiverRegister(), NameRegister(),
+ ValueRegister(), MapRegister()};
+ data->Initialize(arraysize(registers), registers, NULL);
+}
+
+
void ElementTransitionAndStoreDescriptor::Initialize(
CallInterfaceDescriptorData* data) {
Register registers[] = {ContextRegister(), ValueRegister(), MapRegister(),
#define INTERFACE_DESCRIPTOR_LIST(V) \
V(Load) \
V(Store) \
+ V(ExtendStorage) \
V(ElementTransitionAndStore) \
V(Instanceof) \
V(VectorLoadICTrampoline) \
};
+class ExtendStorageDescriptor : public StoreDescriptor {
+ public:
+ DECLARE_DESCRIPTOR(ExtendStorageDescriptor, StoreDescriptor)
+
+ // Extends StoreDescriptor with Map parameter.
+ enum ParameterIndices {
+ kReceiverIndex,
+ kNameIndex,
+ kValueIndex,
+ kMapIndex,
+ kParameterCount
+ };
+ static const Register MapRegister();
+};
+
+
class ElementTransitionAndStoreDescriptor : public StoreDescriptor {
public:
DECLARE_DESCRIPTOR(ElementTransitionAndStoreDescriptor, StoreDescriptor)
const Register StoreDescriptor::ValueRegister() { return a0; }
+const Register ExtendStorageDescriptor::MapRegister() { return a3; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() { return a3; }
const Register StoreDescriptor::ValueRegister() { return a0; }
+const Register ExtendStorageDescriptor::MapRegister() { return a3; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() { return a3; }
}
DCHECK(number_of_fields == old_number_of_fields + 1);
- // This migration is a transition from a map that has run out out property
+ // This migration is a transition from a map that has run out of property
// space. Therefore it could be done by extending the backing store.
Handle<FixedArray> old_storage = handle(object->properties(), isolate);
Handle<FixedArray> new_storage =
}
-void JSObject::MigrateToNewProperty(Handle<JSObject> object,
- Handle<Map> map,
- Handle<Object> value) {
- JSObject::MigrateToMap(object, map);
- if (map->GetLastDescriptorDetails().type() != FIELD) return;
- object->WriteToField(map->LastAdded(), *value);
-}
-
-
void JSObject::WriteToField(int descriptor, Object* value) {
DisallowHeapAllocation no_gc;
Handle<Name> name,
Handle<Object> old_value);
- static void MigrateToNewProperty(Handle<JSObject> object,
- Handle<Map> transition,
- Handle<Object> value);
-
private:
friend class DictionaryElementsAccessor;
friend class JSReceiver;
const Register StoreDescriptor::ValueRegister() { return rax; }
+const Register ExtendStorageDescriptor::MapRegister() { return rbx; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() {
return rbx;
}
const Register StoreDescriptor::ValueRegister() { return eax; }
+const Register ExtendStorageDescriptor::MapRegister() { return ebx; }
+
+
const Register ElementTransitionAndStoreDescriptor::MapRegister() {
return ebx;
}