[ic] Support data handlers that represent transitioning stores.
BUG=v8:5561 Review-Url: https://codereview.chromium.org/2488673004 Cr-Commit-Position: refs/heads/master@{#40946}
This commit is contained in:
parent
5d3ce7ea4f
commit
bcb3af59be
@ -5635,8 +5635,7 @@ void CodeStubAssembler::HandleLoadICHandlerCase(
|
||||
|
||||
Bind(&try_proto_handler);
|
||||
{
|
||||
GotoIf(WordEqual(LoadMap(handler), LoadRoot(Heap::kCodeMapRootIndex)),
|
||||
&call_handler);
|
||||
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
|
||||
HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler,
|
||||
&if_smi_handler, miss);
|
||||
}
|
||||
@ -6086,11 +6085,26 @@ void CodeStubAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::HandleStoreFieldAndReturn(
|
||||
Node* handler_word, Node* holder, Representation representation,
|
||||
Node* value, bool transition_to_field, Label* miss) {
|
||||
void CodeStubAssembler::HandleStoreFieldAndReturn(Node* handler_word,
|
||||
Node* holder,
|
||||
Representation representation,
|
||||
Node* value, Node* transition,
|
||||
Label* miss) {
|
||||
bool transition_to_field = transition != nullptr;
|
||||
Node* prepared_value = PrepareValueForWrite(value, representation, miss);
|
||||
|
||||
if (transition_to_field) {
|
||||
Label storage_extended(this);
|
||||
GotoUnless(IsSetWord<StoreHandler::ExtendStorageBits>(handler_word),
|
||||
&storage_extended);
|
||||
Comment("[ Extend storage");
|
||||
ExtendPropertiesBackingStore(holder);
|
||||
Comment("] Extend storage");
|
||||
Goto(&storage_extended);
|
||||
|
||||
Bind(&storage_extended);
|
||||
}
|
||||
|
||||
Node* offset = DecodeWord<StoreHandler::FieldOffsetBits>(handler_word);
|
||||
Label if_inobject(this), if_out_of_object(this);
|
||||
Branch(IsSetWord<StoreHandler::IsInobjectBits>(handler_word), &if_inobject,
|
||||
@ -6100,6 +6114,9 @@ void CodeStubAssembler::HandleStoreFieldAndReturn(
|
||||
{
|
||||
StoreNamedField(holder, offset, true, representation, prepared_value,
|
||||
transition_to_field);
|
||||
if (transition_to_field) {
|
||||
StoreObjectField(holder, JSObject::kMapOffset, transition);
|
||||
}
|
||||
Return(value);
|
||||
}
|
||||
|
||||
@ -6107,15 +6124,33 @@ void CodeStubAssembler::HandleStoreFieldAndReturn(
|
||||
{
|
||||
StoreNamedField(holder, offset, false, representation, prepared_value,
|
||||
transition_to_field);
|
||||
if (transition_to_field) {
|
||||
StoreObjectField(holder, JSObject::kMapOffset, transition);
|
||||
}
|
||||
Return(value);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
|
||||
Node* holder, Node* value,
|
||||
bool transition_to_field,
|
||||
Node* transition,
|
||||
Label* miss) {
|
||||
Comment(transition_to_field ? "transitioning field store" : "field store");
|
||||
Comment(transition ? "transitioning field store" : "field store");
|
||||
|
||||
#ifdef DEBUG
|
||||
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
|
||||
if (transition) {
|
||||
CSA_ASSERT(
|
||||
this,
|
||||
WordOr(WordEqual(handler_kind,
|
||||
IntPtrConstant(StoreHandler::kTransitionToField)),
|
||||
WordEqual(handler_kind,
|
||||
IntPtrConstant(StoreHandler::kTransitionToConstant))));
|
||||
} else {
|
||||
CSA_ASSERT(this, WordEqual(handler_kind,
|
||||
IntPtrConstant(StoreHandler::kStoreField)));
|
||||
}
|
||||
#endif
|
||||
|
||||
Node* field_representation =
|
||||
DecodeWord<StoreHandler::FieldRepresentationBits>(handler_word);
|
||||
@ -6138,14 +6173,14 @@ void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
|
||||
{
|
||||
Comment("store tagged field");
|
||||
HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
|
||||
value, transition_to_field, miss);
|
||||
value, transition, miss);
|
||||
}
|
||||
|
||||
Bind(&if_double_field);
|
||||
{
|
||||
Comment("store double field");
|
||||
HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(),
|
||||
value, transition_to_field, miss);
|
||||
value, transition, miss);
|
||||
}
|
||||
|
||||
Bind(&if_heap_object_field);
|
||||
@ -6156,7 +6191,8 @@ void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
|
||||
PrepareValueForWrite(value, Representation::HeapObject(), miss);
|
||||
Node* value_index_in_descriptor =
|
||||
DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
|
||||
Node* descriptors = LoadMapDescriptors(LoadMap(holder));
|
||||
Node* descriptors =
|
||||
LoadMapDescriptors(transition ? transition : LoadMap(holder));
|
||||
Node* maybe_field_type = LoadFixedArrayElement(
|
||||
descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS);
|
||||
Label do_store(this);
|
||||
@ -6168,22 +6204,23 @@ void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
|
||||
}
|
||||
Bind(&do_store);
|
||||
HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
|
||||
prepared_value, transition_to_field, miss);
|
||||
prepared_value, transition, miss);
|
||||
}
|
||||
|
||||
Bind(&if_smi_field);
|
||||
{
|
||||
Comment("store smi field");
|
||||
HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(),
|
||||
value, transition_to_field, miss);
|
||||
value, transition, miss);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
Node* handler, Label* miss) {
|
||||
Label if_smi_handler(this), call_handler(this);
|
||||
Label if_smi_handler(this);
|
||||
Label try_proto_handler(this), call_handler(this);
|
||||
|
||||
Branch(TaggedIsSmi(handler), &if_smi_handler, &call_handler);
|
||||
Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler);
|
||||
|
||||
// |handler| is a Smi, encoding what to do. See SmiHandler methods
|
||||
// for the encoding format.
|
||||
@ -6192,8 +6229,14 @@ void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
Node* holder = p->receiver;
|
||||
Node* handler_word = SmiUntag(handler);
|
||||
|
||||
// Handle non-transitioning stores.
|
||||
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, false, miss);
|
||||
// Handle non-transitioning field stores.
|
||||
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
|
||||
}
|
||||
|
||||
Bind(&try_proto_handler);
|
||||
{
|
||||
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
|
||||
HandleStoreICProtoHandler(p, handler, miss);
|
||||
}
|
||||
|
||||
// |handler| is a heap object. Must be code, call it.
|
||||
@ -6205,6 +6248,99 @@ void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::HandleStoreICProtoHandler(const StoreICParameters* p,
|
||||
Node* handler, Label* miss) {
|
||||
// IC dispatchers rely on these assumptions to be held.
|
||||
STATIC_ASSERT(FixedArray::kLengthOffset ==
|
||||
StoreHandler::kTransitionCellOffset);
|
||||
DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex),
|
||||
StoreHandler::kSmiHandlerOffset);
|
||||
DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex),
|
||||
StoreHandler::kValidityCellOffset);
|
||||
|
||||
// Both FixedArray and Tuple3 handlers have validity cell at the same offset.
|
||||
Label validity_cell_check_done(this);
|
||||
Node* validity_cell =
|
||||
LoadObjectField(handler, StoreHandler::kValidityCellOffset);
|
||||
GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
|
||||
&validity_cell_check_done);
|
||||
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
|
||||
GotoIf(WordNotEqual(cell_value,
|
||||
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
|
||||
miss);
|
||||
Goto(&validity_cell_check_done);
|
||||
|
||||
Bind(&validity_cell_check_done);
|
||||
Node* smi_handler = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset);
|
||||
CSA_ASSERT(this, TaggedIsSmi(smi_handler));
|
||||
|
||||
Node* maybe_transition_cell =
|
||||
LoadObjectField(handler, StoreHandler::kTransitionCellOffset);
|
||||
Label array_handler(this), tuple_handler(this);
|
||||
Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler);
|
||||
|
||||
Variable var_transition(this, MachineRepresentation::kTagged);
|
||||
Label if_transition(this), if_transition_to_constant(this);
|
||||
Bind(&tuple_handler);
|
||||
{
|
||||
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
|
||||
var_transition.Bind(transition);
|
||||
Goto(&if_transition);
|
||||
}
|
||||
|
||||
Bind(&array_handler);
|
||||
{
|
||||
Node* length = SmiUntag(maybe_transition_cell);
|
||||
BuildFastLoop(MachineType::PointerRepresentation(),
|
||||
IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length,
|
||||
[this, p, handler, miss](CodeStubAssembler*, Node* current) {
|
||||
Node* prototype_cell = LoadFixedArrayElement(
|
||||
handler, current, 0, INTPTR_PARAMETERS);
|
||||
CheckPrototype(prototype_cell, p->name, miss);
|
||||
},
|
||||
1, IndexAdvanceMode::kPost);
|
||||
|
||||
Node* maybe_transition_cell = LoadFixedArrayElement(
|
||||
handler, IntPtrConstant(StoreHandler::kTransitionCellIndex), 0,
|
||||
INTPTR_PARAMETERS);
|
||||
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
|
||||
var_transition.Bind(transition);
|
||||
Goto(&if_transition);
|
||||
}
|
||||
|
||||
Bind(&if_transition);
|
||||
{
|
||||
Node* holder = p->receiver;
|
||||
Node* transition = var_transition.value();
|
||||
Node* handler_word = SmiUntag(smi_handler);
|
||||
|
||||
GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(transition)), miss);
|
||||
|
||||
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
|
||||
GotoIf(WordEqual(handler_kind,
|
||||
IntPtrConstant(StoreHandler::kTransitionToConstant)),
|
||||
&if_transition_to_constant);
|
||||
|
||||
// Handle transitioning field stores.
|
||||
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition,
|
||||
miss);
|
||||
|
||||
Bind(&if_transition_to_constant);
|
||||
{
|
||||
// Check that constant matches value.
|
||||
Node* value_index_in_descriptor =
|
||||
DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
|
||||
Node* descriptors = LoadMapDescriptors(transition);
|
||||
Node* constant = LoadFixedArrayElement(
|
||||
descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS);
|
||||
GotoIf(WordNotEqual(p->value, constant), miss);
|
||||
|
||||
StoreObjectField(p->receiver, JSObject::kMapOffset, transition);
|
||||
Return(p->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreIC(const StoreICParameters* p) {
|
||||
Variable var_handler(this, MachineRepresentation::kTagged);
|
||||
// TODO(ishell): defer blocks when it works.
|
||||
|
@ -24,6 +24,7 @@ enum class IterationKind { kKeys, kValues, kEntries };
|
||||
|
||||
#define HEAP_CONSTANT_LIST(V) \
|
||||
V(BooleanMap, BooleanMap) \
|
||||
V(CodeMap, CodeMap) \
|
||||
V(empty_string, EmptyString) \
|
||||
V(EmptyFixedArray, EmptyFixedArray) \
|
||||
V(FalseValue, False) \
|
||||
@ -1152,16 +1153,23 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
void NameDictionaryNegativeLookup(compiler::Node* object,
|
||||
compiler::Node* name, Label* miss);
|
||||
|
||||
// If |transition| is nullptr then the normal field store is generated or
|
||||
// transitioning store otherwise.
|
||||
void HandleStoreFieldAndReturn(compiler::Node* handler_word,
|
||||
compiler::Node* holder,
|
||||
Representation representation,
|
||||
compiler::Node* value,
|
||||
bool transition_to_field, Label* miss);
|
||||
compiler::Node* transition, Label* miss);
|
||||
|
||||
// If |transition| is nullptr then the normal field store is generated or
|
||||
// transitioning store otherwise.
|
||||
void HandleStoreICSmiHandlerCase(compiler::Node* handler_word,
|
||||
compiler::Node* holder,
|
||||
compiler::Node* value,
|
||||
bool transition_to_field, Label* miss);
|
||||
compiler::Node* transition, Label* miss);
|
||||
|
||||
void HandleStoreICProtoHandler(const StoreICParameters* p,
|
||||
compiler::Node* handler, Label* miss);
|
||||
|
||||
compiler::Node* TryToIntptr(compiler::Node* key, Label* miss);
|
||||
void EmitFastElementsBoundsCheck(compiler::Node* object,
|
||||
|
@ -800,6 +800,7 @@ class RuntimeCallTimer {
|
||||
V(StoreIC_StoreNormal) \
|
||||
V(StoreIC_StoreScriptContextFieldStub) \
|
||||
V(StoreIC_StoreTransition) \
|
||||
V(StoreIC_StoreTransitionDH) \
|
||||
V(StoreIC_StoreViaSetter)
|
||||
|
||||
class RuntimeCallStats {
|
||||
|
@ -79,9 +79,10 @@ Handle<Object> LoadHandler::LoadElement(Isolate* isolate,
|
||||
return handle(Smi::FromInt(config), isolate);
|
||||
}
|
||||
|
||||
Handle<Object> StoreHandler::StoreField(Isolate* isolate, int descriptor,
|
||||
FieldIndex field_index,
|
||||
Representation representation) {
|
||||
Handle<Object> StoreHandler::StoreField(Isolate* isolate, Kind kind,
|
||||
int descriptor, FieldIndex field_index,
|
||||
Representation representation,
|
||||
bool extend_storage) {
|
||||
StoreHandler::FieldRepresentation field_rep;
|
||||
switch (representation.kind()) {
|
||||
case Representation::kSmi:
|
||||
@ -102,7 +103,11 @@ Handle<Object> StoreHandler::StoreField(Isolate* isolate, int descriptor,
|
||||
}
|
||||
int value_index = DescriptorArray::ToValueIndex(descriptor);
|
||||
|
||||
int config = StoreHandler::KindBits::encode(StoreHandler::kForFields) |
|
||||
DCHECK(kind == kStoreField || kind == kTransitionToField);
|
||||
DCHECK_IMPLIES(kind == kStoreField, !extend_storage);
|
||||
|
||||
int config = StoreHandler::KindBits::encode(kind) |
|
||||
StoreHandler::ExtendStorageBits::encode(extend_storage) |
|
||||
StoreHandler::IsInobjectBits::encode(field_index.is_inobject()) |
|
||||
StoreHandler::FieldRepresentationBits::encode(field_rep) |
|
||||
StoreHandler::DescriptorValueIndexBits::encode(value_index) |
|
||||
@ -110,6 +115,30 @@ Handle<Object> StoreHandler::StoreField(Isolate* isolate, int descriptor,
|
||||
return handle(Smi::FromInt(config), isolate);
|
||||
}
|
||||
|
||||
Handle<Object> StoreHandler::StoreField(Isolate* isolate, int descriptor,
|
||||
FieldIndex field_index,
|
||||
Representation representation) {
|
||||
return StoreField(isolate, kStoreField, descriptor, field_index,
|
||||
representation, false);
|
||||
}
|
||||
|
||||
Handle<Object> StoreHandler::TransitionToField(Isolate* isolate, int descriptor,
|
||||
FieldIndex field_index,
|
||||
Representation representation,
|
||||
bool extend_storage) {
|
||||
return StoreField(isolate, kTransitionToField, descriptor, field_index,
|
||||
representation, extend_storage);
|
||||
}
|
||||
|
||||
Handle<Object> StoreHandler::TransitionToConstant(Isolate* isolate,
|
||||
int descriptor) {
|
||||
int value_index = DescriptorArray::ToValueIndex(descriptor);
|
||||
int config =
|
||||
StoreHandler::KindBits::encode(StoreHandler::kTransitionToConstant) |
|
||||
StoreHandler::DescriptorValueIndexBits::encode(value_index);
|
||||
return handle(Smi::FromInt(config), isolate);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
@ -37,6 +37,7 @@ class LoadHandler {
|
||||
|
||||
class IsAccessorInfoBits
|
||||
: public BitField<bool, DoNegativeLookupOnReceiverBits::kNext, 1> {};
|
||||
// Index of a value entry in the descriptor array.
|
||||
// +2 here is because each descriptor entry occupies 3 slots in array.
|
||||
class DescriptorValueIndexBits
|
||||
: public BitField<unsigned, IsAccessorInfoBits::kNext,
|
||||
@ -117,32 +118,83 @@ class LoadHandler {
|
||||
// A set of bit fields representing Smi handlers for stores.
|
||||
class StoreHandler {
|
||||
public:
|
||||
enum Kind { kForElements, kForFields };
|
||||
class KindBits : public BitField<Kind, 0, 1> {};
|
||||
enum Kind {
|
||||
kStoreElement,
|
||||
kStoreField,
|
||||
kTransitionToField,
|
||||
kTransitionToConstant
|
||||
};
|
||||
class KindBits : public BitField<Kind, 0, 2> {};
|
||||
|
||||
enum FieldRepresentation { kSmi, kDouble, kHeapObject, kTagged };
|
||||
|
||||
//
|
||||
// Encoding when KindBits contains kForFields.
|
||||
//
|
||||
class IsInobjectBits : public BitField<bool, KindBits::kNext, 1> {};
|
||||
class FieldRepresentationBits
|
||||
: public BitField<FieldRepresentation, IsInobjectBits::kNext, 2> {};
|
||||
// Applicable to kStoreField, kTransitionToField and kTransitionToConstant
|
||||
// kinds.
|
||||
|
||||
// Index of a value entry in the descriptor array.
|
||||
// +2 here is because each descriptor entry occupies 3 slots in array.
|
||||
class DescriptorValueIndexBits
|
||||
: public BitField<unsigned, FieldRepresentationBits::kNext,
|
||||
: public BitField<unsigned, KindBits::kNext,
|
||||
kDescriptorIndexBitCount + 2> {};
|
||||
//
|
||||
// Encoding when KindBits contains kTransitionToConstant.
|
||||
//
|
||||
|
||||
// Make sure we don't overflow the smi.
|
||||
STATIC_ASSERT(DescriptorValueIndexBits::kNext <= kSmiValueSize);
|
||||
|
||||
//
|
||||
// Encoding when KindBits contains kStoreField or kTransitionToField.
|
||||
//
|
||||
class ExtendStorageBits
|
||||
: public BitField<bool, DescriptorValueIndexBits::kNext, 1> {};
|
||||
class IsInobjectBits : public BitField<bool, ExtendStorageBits::kNext, 1> {};
|
||||
class FieldRepresentationBits
|
||||
: public BitField<FieldRepresentation, IsInobjectBits::kNext, 2> {};
|
||||
// +1 here is to cover all possible JSObject header sizes.
|
||||
class FieldOffsetBits
|
||||
: public BitField<unsigned, DescriptorValueIndexBits::kNext,
|
||||
: public BitField<unsigned, FieldRepresentationBits::kNext,
|
||||
kDescriptorIndexBitCount + 1 + kPointerSizeLog2> {};
|
||||
// Make sure we don't overflow the smi.
|
||||
STATIC_ASSERT(FieldOffsetBits::kNext <= kSmiValueSize);
|
||||
|
||||
// The layout of an Tuple3 handler representing a transitioning store
|
||||
// when prototype chain checks do not include non-existing lookups or access
|
||||
// checks.
|
||||
static const int kTransitionCellOffset = Tuple3::kValue1Offset;
|
||||
static const int kSmiHandlerOffset = Tuple3::kValue2Offset;
|
||||
static const int kValidityCellOffset = Tuple3::kValue3Offset;
|
||||
|
||||
// The layout of an array handler representing a transitioning store
|
||||
// when prototype chain checks include non-existing lookups and access checks.
|
||||
static const int kSmiHandlerIndex = 0;
|
||||
static const int kValidityCellIndex = 1;
|
||||
static const int kTransitionCellIndex = 2;
|
||||
static const int kFirstPrototypeIndex = 3;
|
||||
|
||||
// Creates a Smi-handler for storing a field to fast object.
|
||||
static inline Handle<Object> StoreField(Isolate* isolate, int descriptor,
|
||||
FieldIndex field_index,
|
||||
Representation representation);
|
||||
|
||||
// Creates a Smi-handler for transitioning store to a field.
|
||||
static inline Handle<Object> TransitionToField(Isolate* isolate,
|
||||
int descriptor,
|
||||
FieldIndex field_index,
|
||||
Representation representation,
|
||||
bool extend_storage);
|
||||
|
||||
// Creates a Smi-handler for transitioning store to a constant field (in this
|
||||
// case the only thing that needs to be done is an update of a map).
|
||||
static inline Handle<Object> TransitionToConstant(Isolate* isolate,
|
||||
int descriptor);
|
||||
|
||||
private:
|
||||
static inline Handle<Object> StoreField(Isolate* isolate, Kind kind,
|
||||
int descriptor,
|
||||
FieldIndex field_index,
|
||||
Representation representation,
|
||||
bool extend_storage);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
104
src/ic/ic.cc
104
src/ic/ic.cc
@ -852,10 +852,10 @@ Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) {
|
||||
|
||||
namespace {
|
||||
|
||||
template <bool fill_array>
|
||||
template <bool fill_array = true>
|
||||
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder, Handle<FixedArray> array,
|
||||
Handle<Name> name) {
|
||||
Handle<Name> name, int first_index) {
|
||||
DCHECK(holder.is_null() || holder->HasFastProperties());
|
||||
|
||||
// The following kinds of receiver maps require custom handler compilation.
|
||||
@ -898,8 +898,7 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
global, name, PropertyCellType::kInvalidated);
|
||||
DCHECK(cell->value()->IsTheHole(isolate));
|
||||
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
|
||||
array->set(LoadHandler::kFirstPrototypeIndex + checks_count,
|
||||
*weak_cell);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
|
||||
@ -910,8 +909,7 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
current->property_dictionary()->FindEntry(name));
|
||||
Handle<WeakCell> weak_cell =
|
||||
Map::GetOrCreatePrototypeWeakCell(current, isolate);
|
||||
array->set(LoadHandler::kFirstPrototypeIndex + checks_count,
|
||||
*weak_cell);
|
||||
array->set(first_index + checks_count, *weak_cell);
|
||||
}
|
||||
checks_count++;
|
||||
}
|
||||
@ -919,19 +917,25 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
return checks_count;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int LoadIC::GetPrototypeCheckCount(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder) {
|
||||
return InitPrototypeChecks<false>(isolate(), receiver_map, holder,
|
||||
Handle<FixedArray>(), Handle<Name>());
|
||||
// Returns 0 if the validity cell check is enough to ensure that the
|
||||
// prototype chain from |receiver_map| till |holder| did not change.
|
||||
// If the |holder| is an empty handle then the full prototype chain is
|
||||
// checked.
|
||||
// Returns -1 if the handler has to be compiled or the number of prototype
|
||||
// checks otherwise.
|
||||
int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder) {
|
||||
return InitPrototypeChecks<false>(isolate, receiver_map, holder,
|
||||
Handle<FixedArray>(), Handle<Name>(), 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Object> smi_handler) {
|
||||
int checks_count = GetPrototypeCheckCount(receiver_map, holder);
|
||||
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder);
|
||||
DCHECK_LE(0, checks_count);
|
||||
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||
|
||||
@ -961,15 +965,15 @@ Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
|
||||
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(LoadHandler::kHolderCellIndex, *holder_cell);
|
||||
InitPrototypeChecks<true>(isolate(), receiver_map, holder, handler_array,
|
||||
name);
|
||||
InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, name,
|
||||
LoadHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
Handle<Object> LoadIC::LoadNonExistent(Handle<Map> receiver_map,
|
||||
Handle<Name> name) {
|
||||
Handle<JSObject> holder; // null handle
|
||||
int checks_count = GetPrototypeCheckCount(receiver_map, holder);
|
||||
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder);
|
||||
DCHECK_LE(0, checks_count);
|
||||
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||
|
||||
@ -1003,8 +1007,8 @@ Handle<Object> LoadIC::LoadNonExistent(Handle<Map> receiver_map,
|
||||
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(LoadHandler::kHolderCellIndex, *factory->null_value());
|
||||
InitPrototypeChecks<true>(isolate(), receiver_map, holder, handler_array,
|
||||
name);
|
||||
InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, name,
|
||||
LoadHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
@ -1861,6 +1865,62 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
|
||||
TRACE_IC("StoreIC", lookup->name());
|
||||
}
|
||||
|
||||
Handle<Object> StoreIC::StoreTransition(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Map> transition,
|
||||
Handle<Name> name) {
|
||||
int descriptor = transition->LastAdded();
|
||||
Handle<DescriptorArray> descriptors(transition->instance_descriptors());
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
Representation representation = details.representation();
|
||||
DCHECK(!representation.IsNone());
|
||||
|
||||
// Declarative handlers don't support access checks.
|
||||
DCHECK(!transition->is_access_check_needed());
|
||||
|
||||
Handle<Object> smi_handler;
|
||||
if (details.type() == DATA_CONSTANT) {
|
||||
smi_handler = StoreHandler::TransitionToConstant(isolate(), descriptor);
|
||||
|
||||
} else {
|
||||
DCHECK_EQ(DATA, details.type());
|
||||
bool extend_storage =
|
||||
Map::cast(transition->GetBackPointer())->unused_property_fields() == 0;
|
||||
|
||||
FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor);
|
||||
smi_handler = StoreHandler::TransitionToField(
|
||||
isolate(), descriptor, index, representation, extend_storage);
|
||||
}
|
||||
|
||||
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder);
|
||||
DCHECK_LE(0, checks_count);
|
||||
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (validity_cell.is_null()) {
|
||||
// This must be a case when receiver's prototype is null.
|
||||
DCHECK_EQ(*isolate()->factory()->null_value(),
|
||||
receiver_map->GetPrototypeChainRootMap(isolate())->prototype());
|
||||
DCHECK_EQ(0, checks_count);
|
||||
validity_cell = handle(Smi::FromInt(0), isolate());
|
||||
}
|
||||
|
||||
Handle<WeakCell> transition_cell = Map::WeakCellForMap(transition);
|
||||
|
||||
Factory* factory = isolate()->factory();
|
||||
if (checks_count == 0) {
|
||||
return factory->NewTuple3(transition_cell, smi_handler, validity_cell);
|
||||
}
|
||||
Handle<FixedArray> handler_array(factory->NewFixedArray(
|
||||
StoreHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||
handler_array->set(StoreHandler::kSmiHandlerIndex, *smi_handler);
|
||||
handler_array->set(StoreHandler::kValidityCellIndex, *validity_cell);
|
||||
handler_array->set(StoreHandler::kTransitionCellIndex, *transition_cell);
|
||||
InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, name,
|
||||
StoreHandler::kFirstPrototypeIndex);
|
||||
return handler_array;
|
||||
}
|
||||
|
||||
static Handle<Code> PropertyCellStoreHandler(
|
||||
Isolate* isolate, Handle<JSObject> receiver, Handle<JSGlobalObject> holder,
|
||||
@ -1897,8 +1957,13 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||
return slow_stub();
|
||||
}
|
||||
|
||||
DCHECK(lookup->IsCacheableTransition());
|
||||
if (FLAG_tf_store_ic_stub) {
|
||||
Handle<Map> transition = lookup->transition_map();
|
||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreTransitionDH);
|
||||
return StoreTransition(receiver_map(), holder, transition,
|
||||
lookup->name());
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
|
||||
@ -2038,6 +2103,7 @@ Handle<Object> StoreIC::CompileHandler(LookupIterator* lookup,
|
||||
cell->set_value(isolate()->heap()->the_hole_value());
|
||||
return code;
|
||||
}
|
||||
DCHECK(!FLAG_tf_store_ic_stub);
|
||||
Handle<Map> transition = lookup->transition_map();
|
||||
// Currently not handled by CompileStoreTransition.
|
||||
DCHECK(holder->HasFastProperties());
|
||||
|
12
src/ic/ic.h
12
src/ic/ic.h
@ -314,14 +314,6 @@ class LoadIC : public IC {
|
||||
// Creates a data handler that represents a load of a field by given index.
|
||||
Handle<Object> SimpleFieldLoad(FieldIndex index);
|
||||
|
||||
// Returns 0 if the validity cell check is enough to ensure that the
|
||||
// prototype chain from |receiver_map| till |holder| did not change.
|
||||
// If the |holder| is an empty handle then the full prototype chain is
|
||||
// checked.
|
||||
// Returns -1 if the handler has to be compiled or the number of prototype
|
||||
// checks otherwise.
|
||||
int GetPrototypeCheckCount(Handle<Map> receiver_map, Handle<JSObject> holder);
|
||||
|
||||
// Creates a data handler that represents a prototype chain check followed
|
||||
// by given Smi-handler that encoded a load from the holder.
|
||||
// Can be used only if GetPrototypeCheckCount() returns non negative value.
|
||||
@ -425,6 +417,10 @@ class StoreIC : public IC {
|
||||
CacheHolderFlag cache_holder) override;
|
||||
|
||||
private:
|
||||
Handle<Object> StoreTransition(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Map> transition, Handle<Name> name);
|
||||
|
||||
friend class IC;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user