Skip to content
This repository has been archived by the owner on Mar 25, 2018. It is now read-only.

Commit

Permalink
[ic] Support data handlers that represent transitioning stores.
Browse files Browse the repository at this point in the history
BUG=v8:5561

Review-Url: https://codereview.chromium.org/2488673004
Cr-Commit-Position: refs/heads/master@{#40946}
  • Loading branch information
isheludko authored and Commit bot committed Nov 13, 2016
1 parent 5d3ce7e commit bcb3af5
Show file tree
Hide file tree
Showing 7 changed files with 346 additions and 58 deletions.
168 changes: 152 additions & 16 deletions src/code-stub-assembler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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,
Expand All @@ -6100,22 +6114,43 @@ void CodeStubAssembler::HandleStoreFieldAndReturn(
{
StoreNamedField(holder, offset, true, representation, prepared_value,
transition_to_field);
if (transition_to_field) {
StoreObjectField(holder, JSObject::kMapOffset, transition);
}
Return(value);
}

Bind(&if_out_of_object);
{
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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand Down
12 changes: 10 additions & 2 deletions src/code-stub-assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/counters.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,7 @@ class RuntimeCallTimer {
V(StoreIC_StoreNormal) \
V(StoreIC_StoreScriptContextFieldStub) \
V(StoreIC_StoreTransition) \
V(StoreIC_StoreTransitionDH) \
V(StoreIC_StoreViaSetter)

class RuntimeCallStats {
Expand Down
37 changes: 33 additions & 4 deletions src/ic/handler-configuration-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -102,14 +103,42 @@ 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) |
StoreHandler::FieldOffsetBits::encode(field_index.offset());
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

Expand Down
Loading

0 comments on commit bcb3af5

Please sign in to comment.