Commit 56791805 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for elements kind transitions.

Extend the ElementAccessInfo machinery with support for elements kind
transitions, which can be either in-place updates of the map (i.e. when
going from FAST_SMI_ELEMENTS to FAST_ELEMENTS) or instance migrations
utilizing the TransitionElementsKindStub.

R=jarin@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1452563002

Cr-Commit-Position: refs/heads/master@{#32003}
parent 43ef9bc6
......@@ -81,6 +81,14 @@ PropertyAccessInfo PropertyAccessInfo::DataField(
ElementAccessInfo::ElementAccessInfo() : receiver_type_(Type::None()) {}
ElementAccessInfo::ElementAccessInfo(Type* receiver_type,
ElementsKind elements_kind,
MaybeHandle<JSObject> holder)
: elements_kind_(elements_kind),
holder_(holder),
receiver_type_(receiver_type) {}
PropertyAccessInfo::PropertyAccessInfo()
: kind_(kInvalid), receiver_type_(Type::None()), field_type_(Type::Any()) {}
......@@ -153,15 +161,50 @@ bool AccessInfoFactory::ComputeElementAccessInfo(
bool AccessInfoFactory::ComputeElementAccessInfos(
MapHandleList const& maps, AccessMode access_mode,
ZoneVector<ElementAccessInfo>* access_infos) {
// Collect possible transition targets.
MapHandleList possible_transition_targets(maps.length());
for (Handle<Map> map : maps) {
if (Map::TryUpdate(map).ToHandle(&map)) {
ElementAccessInfo access_info;
if (!ComputeElementAccessInfo(map, access_mode, &access_info)) {
return false;
if (CanInlineElementAccess(map) &&
IsFastElementsKind(map->elements_kind()) &&
GetInitialFastElementsKind() != map->elements_kind()) {
possible_transition_targets.Add(map);
}
access_infos->push_back(access_info);
}
}
// Separate the actual receiver maps and the possible transition sources.
MapHandleList receiver_maps(maps.length());
MapTransitionList transitions(maps.length());
for (Handle<Map> map : maps) {
if (Map::TryUpdate(map).ToHandle(&map)) {
Handle<Map> transition_target =
Map::FindTransitionedMap(map, &possible_transition_targets);
if (transition_target.is_null()) {
receiver_maps.Add(map);
} else {
transitions.push_back(std::make_pair(map, transition_target));
}
}
}
for (Handle<Map> receiver_map : receiver_maps) {
// Compute the element access information.
ElementAccessInfo access_info;
if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
return false;
}
// Collect the possible transitions for the {receiver_map}.
for (auto transition : transitions) {
if (transition.second.is_identical_to(receiver_map)) {
access_info.transitions().push_back(transition);
}
}
// Schedule the access information.
access_infos->push_back(access_info);
}
return true;
}
......
......@@ -28,24 +28,28 @@ enum class AccessMode { kLoad, kStore };
std::ostream& operator<<(std::ostream&, AccessMode);
// Mapping of transition source to transition target.
typedef std::vector<std::pair<Handle<Map>, Handle<Map>>> MapTransitionList;
// This class encapsulates all information required to access a certain element.
class ElementAccessInfo final {
public:
ElementAccessInfo();
ElementAccessInfo(Type* receiver_type, ElementsKind elements_kind,
MaybeHandle<JSObject> holder)
: elements_kind_(elements_kind),
holder_(holder),
receiver_type_(receiver_type) {}
MaybeHandle<JSObject> holder);
MaybeHandle<JSObject> holder() const { return holder_; }
ElementsKind elements_kind() const { return elements_kind_; }
Type* receiver_type() const { return receiver_type_; }
MapTransitionList& transitions() { return transitions_; }
MapTransitionList const& transitions() const { return transitions_; }
private:
ElementsKind elements_kind_;
MaybeHandle<JSObject> holder_;
Type* receiver_type_;
MapTransitionList transitions_;
};
......
......@@ -500,6 +500,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty);
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
......@@ -547,7 +548,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
Node* this_receiver = receiver;
Node* this_value = value;
Node* this_index = index;
Node* this_effect = effect;
Node* this_effect;
Node* this_control;
// Perform map check on {receiver}.
......@@ -555,24 +556,77 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
bool receiver_is_jsarray = true;
{
ZoneVector<Node*> this_controls(zone());
ZoneVector<Node*> this_effects(zone());
for (auto i = access_info.receiver_type()->Classes(); !i.Done();
i.Advance()) {
Handle<Map> map = i.Current();
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
graph()->NewNode(simplified()->ReferenceEqual(Type::Any()),
receiver_map, jsgraph()->Constant(map));
Node* branch =
graph()->NewNode(common()->Branch(), check, fallthrough_control);
this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
this_effects.push_back(effect);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
if (!map->IsJSArrayMap()) receiver_is_jsarray = false;
}
// Generate possible elements kind transitions.
for (auto transition : access_info.transitions()) {
Handle<Map> transition_source = transition.first;
Handle<Map> transition_target = transition.second;
// Check if {receiver} has the specified {transition_source} map.
Node* check = graph()->NewNode(
simplified()->ReferenceEqual(Type::Any()), receiver_map,
jsgraph()->HeapConstant(transition_source));
Node* branch =
graph()->NewNode(common()->Branch(), check, fallthrough_control);
// Migrate {receiver} from {transition_source} to {transition_target}.
Node* transition_control = graph()->NewNode(common()->IfTrue(), branch);
Node* transition_effect = effect;
if (IsSimpleMapChangeTransition(transition_source->elements_kind(),
transition_target->elements_kind())) {
// In-place migration, just store the {transition_target} map.
transition_effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForMap()), receiver,
jsgraph()->HeapConstant(transition_target), transition_effect,
transition_control);
} else {
// Instance migration, let the stub deal with the {receiver}.
TransitionElementsKindStub stub(isolate(),
transition_source->elements_kind(),
transition_target->elements_kind(),
transition_source->IsJSArrayMap());
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 0,
CallDescriptor::kNeedsFrameState, node->op()->properties());
transition_effect = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(stub.GetCode()),
receiver, jsgraph()->HeapConstant(transition_target), context,
frame_state, transition_effect, transition_control);
}
this_controls.push_back(transition_control);
this_effects.push_back(transition_effect);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
}
// Create single chokepoint for the control.
int const this_control_count = static_cast<int>(this_controls.size());
this_control =
(this_control_count == 1)
? this_controls.front()
: graph()->NewNode(common()->Merge(this_control_count),
this_control_count, &this_controls.front());
if (this_control_count == 1) {
this_control = this_controls.front();
this_effect = this_effects.front();
} else {
this_control =
graph()->NewNode(common()->Merge(this_control_count),
this_control_count, &this_controls.front());
this_effects.push_back(this_control);
this_effect =
graph()->NewNode(common()->EffectPhi(this_control_count),
this_control_count + 1, &this_effects.front());
}
}
// Certain stores need a prototype chain check because shape changes
......
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