Commit bcb3af59 authored by ishell's avatar ishell Committed by Commit bot

[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}
parent 5d3ce7ea
This diff is collapsed.
......@@ -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 };
// 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, KindBits::kNext,
kDescriptorIndexBitCount + 2> {};
//
// Encoding when KindBits contains kForFields.
// Encoding when KindBits contains kTransitionToConstant.
//
class IsInobjectBits : public BitField<bool, KindBits::kNext, 1> {};
// 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> {};
// +2 here is because each descriptor entry occupies 3 slots in array.
class DescriptorValueIndexBits
: public BitField<unsigned, FieldRepresentationBits::kNext,
kDescriptorIndexBitCount + 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
......
......@@ -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());
......
......@@ -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;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment