Commit 37b4b2f1 authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

[turbofan] Prune control flow based on failed map checks and comparisons.

This introduces unreachable state into load elimination. We mark state
as unreachable if we know statically that a map check would fail.
When processing effect phis, we disconnect unreachable state's
control from the effect phi's merge, and point it to RuntimeAbort.
The control input to the merge is then updated with Dead. Dead
code elimination prunes the merge, phis and effect phis.

Bug: v8:6396
Change-Id: I01874b576e548747a915c7b645b96ebaa6f6700d
Reviewed-on: https://chromium-review.googlesource.com/730754
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48810}
parent c38e37f3
...@@ -141,6 +141,7 @@ namespace internal { ...@@ -141,6 +141,7 @@ namespace internal {
V(kUnexpectedReturnFromFrameDropper, \ V(kUnexpectedReturnFromFrameDropper, \
"Unexpectedly returned from dropping frames") \ "Unexpectedly returned from dropping frames") \
V(kUnexpectedReturnFromThrow, "Unexpectedly returned from a throw") \ V(kUnexpectedReturnFromThrow, "Unexpectedly returned from a throw") \
V(kUnreachableCodeReached, "Reached unreachable code") \
V(kVariableResolvedToWithContext, "Variable resolved to with context") \ V(kVariableResolvedToWithContext, "Variable resolved to with context") \
V(kWithStatement, "WithStatement") \ V(kWithStatement, "WithStatement") \
V(kWrongFunctionContext, "Wrong context passed to function") \ V(kWrongFunctionContext, "Wrong context passed to function") \
......
...@@ -525,6 +525,8 @@ Node* LoadElimination::AbstractState::LookupCheck(Node* node) const { ...@@ -525,6 +525,8 @@ Node* LoadElimination::AbstractState::LookupCheck(Node* node) const {
LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck( LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck(
Node* node, Zone* zone) const { Node* node, Zone* zone) const {
if (is_unreachable()) return this;
AbstractState* that = new (zone) AbstractState(*this); AbstractState* that = new (zone) AbstractState(*this);
if (that->checks_) { if (that->checks_) {
that->checks_ = that->checks_->Extend(node, zone); that->checks_ = that->checks_->Extend(node, zone);
...@@ -541,6 +543,8 @@ bool LoadElimination::AbstractState::LookupMaps( ...@@ -541,6 +543,8 @@ bool LoadElimination::AbstractState::LookupMaps(
LoadElimination::AbstractState const* LoadElimination::AbstractState::SetMaps( LoadElimination::AbstractState const* LoadElimination::AbstractState::SetMaps(
Node* object, ZoneHandleSet<Map> maps, Zone* zone) const { Node* object, ZoneHandleSet<Map> maps, Zone* zone) const {
if (is_unreachable()) return this;
AbstractState* that = new (zone) AbstractState(*this); AbstractState* that = new (zone) AbstractState(*this);
if (that->maps_) { if (that->maps_) {
that->maps_ = that->maps_->Extend(object, maps, zone); that->maps_ = that->maps_->Extend(object, maps, zone);
...@@ -552,6 +556,8 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::SetMaps( ...@@ -552,6 +556,8 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::SetMaps(
LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps( LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps(
const AliasStateInfo& alias_info, Zone* zone) const { const AliasStateInfo& alias_info, Zone* zone) const {
if (is_unreachable()) return this;
if (this->maps_) { if (this->maps_) {
AbstractMaps const* that_maps = this->maps_->Kill(alias_info, zone); AbstractMaps const* that_maps = this->maps_->Kill(alias_info, zone);
if (this->maps_ != that_maps) { if (this->maps_ != that_maps) {
...@@ -582,6 +588,8 @@ LoadElimination::AbstractState::AddElement(Node* object, Node* index, ...@@ -582,6 +588,8 @@ LoadElimination::AbstractState::AddElement(Node* object, Node* index,
Node* value, Node* value,
MachineRepresentation representation, MachineRepresentation representation,
Zone* zone) const { Zone* zone) const {
if (is_unreachable()) return this;
AbstractState* that = new (zone) AbstractState(*this); AbstractState* that = new (zone) AbstractState(*this);
if (that->elements_) { if (that->elements_) {
that->elements_ = that->elements_ =
...@@ -596,6 +604,8 @@ LoadElimination::AbstractState::AddElement(Node* object, Node* index, ...@@ -596,6 +604,8 @@ LoadElimination::AbstractState::AddElement(Node* object, Node* index,
LoadElimination::AbstractState const* LoadElimination::AbstractState const*
LoadElimination::AbstractState::KillElement(Node* object, Node* index, LoadElimination::AbstractState::KillElement(Node* object, Node* index,
Zone* zone) const { Zone* zone) const {
if (is_unreachable()) return this;
if (this->elements_) { if (this->elements_) {
AbstractElements const* that_elements = AbstractElements const* that_elements =
this->elements_->Kill(object, index, zone); this->elements_->Kill(object, index, zone);
...@@ -611,6 +621,8 @@ LoadElimination::AbstractState::KillElement(Node* object, Node* index, ...@@ -611,6 +621,8 @@ LoadElimination::AbstractState::KillElement(Node* object, Node* index,
LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField( LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
Node* object, size_t index, Node* value, MaybeHandle<Name> name, Node* object, size_t index, Node* value, MaybeHandle<Name> name,
Zone* zone) const { Zone* zone) const {
if (is_unreachable()) return this;
AbstractState* that = new (zone) AbstractState(*this); AbstractState* that = new (zone) AbstractState(*this);
if (that->fields_[index]) { if (that->fields_[index]) {
that->fields_[index] = that->fields_[index] =
...@@ -630,6 +642,8 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField( ...@@ -630,6 +642,8 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField( LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
const AliasStateInfo& alias_info, size_t index, MaybeHandle<Name> name, const AliasStateInfo& alias_info, size_t index, MaybeHandle<Name> name,
Zone* zone) const { Zone* zone) const {
if (is_unreachable()) return this;
if (AbstractField const* this_field = this->fields_[index]) { if (AbstractField const* this_field = this->fields_[index]) {
this_field = this_field->Kill(alias_info, name, zone); this_field = this_field->Kill(alias_info, name, zone);
if (this->fields_[index] != this_field) { if (this->fields_[index] != this_field) {
...@@ -644,6 +658,8 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField( ...@@ -644,6 +658,8 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
LoadElimination::AbstractState const* LoadElimination::AbstractState const*
LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name, LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
Zone* zone) const { Zone* zone) const {
if (is_unreachable()) return this;
AliasStateInfo alias_info(this, object); AliasStateInfo alias_info(this, object);
for (size_t i = 0;; ++i) { for (size_t i = 0;; ++i) {
if (i == arraysize(fields_)) return this; if (i == arraysize(fields_)) return this;
...@@ -691,6 +707,10 @@ bool LoadElimination::AliasStateInfo::MayAlias(Node* other) const { ...@@ -691,6 +707,10 @@ bool LoadElimination::AliasStateInfo::MayAlias(Node* other) const {
} }
void LoadElimination::AbstractState::Print() const { void LoadElimination::AbstractState::Print() const {
if (is_unreachable()) {
PrintF(" Unreachable\n");
}
if (checks_) { if (checks_) {
PrintF(" checks:\n"); PrintF(" checks:\n");
checks_->Print(); checks_->Print();
...@@ -711,6 +731,18 @@ void LoadElimination::AbstractState::Print() const { ...@@ -711,6 +731,18 @@ void LoadElimination::AbstractState::Print() const {
} }
} }
// static
LoadElimination::AbstractState LoadElimination::AbstractState::Empty() {
return AbstractState();
}
// static
LoadElimination::AbstractState LoadElimination::AbstractState::Unreachable() {
AbstractState unreachable;
unreachable.is_unreachable_ = true;
return unreachable;
}
LoadElimination::AbstractState const* LoadElimination::AbstractState const*
LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const { LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
size_t const id = node->id(); size_t const id = node->id();
...@@ -737,6 +769,27 @@ Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) { ...@@ -737,6 +769,27 @@ Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
return UpdateState(node, state); return UpdateState(node, state);
} }
// static
LoadElimination::MapSetComparisonResult LoadElimination::CompareMapSets(
ZoneHandleSet<Map> const& lhs, ZoneHandleSet<Map> const& rhs) {
DCHECK(!lhs.is_empty());
DCHECK(!rhs.is_empty());
MapSetComparisonResult result = rhs.contains(ZoneHandleSet<Map>(lhs.at(0)))
? MapSetComparisonResult::kSubset
: MapSetComparisonResult::kDisjoint;
for (size_t i = 1; i < lhs.size(); i++) {
MapSetComparisonResult current = rhs.contains(ZoneHandleSet<Map>(lhs.at(i)))
? MapSetComparisonResult::kSubset
: MapSetComparisonResult::kDisjoint;
if (current != result) {
return MapSetComparisonResult::kOther;
}
}
return result;
}
Reduction LoadElimination::ReduceMapGuard(Node* node) { Reduction LoadElimination::ReduceMapGuard(Node* node) {
ZoneHandleSet<Map> const maps = MapGuardMapsOf(node->op()).maps(); ZoneHandleSet<Map> const maps = MapGuardMapsOf(node->op()).maps();
Node* const object = NodeProperties::GetValueInput(node, 0); Node* const object = NodeProperties::GetValueInput(node, 0);
...@@ -745,7 +798,15 @@ Reduction LoadElimination::ReduceMapGuard(Node* node) { ...@@ -745,7 +798,15 @@ Reduction LoadElimination::ReduceMapGuard(Node* node) {
if (state == nullptr) return NoChange(); if (state == nullptr) return NoChange();
ZoneHandleSet<Map> object_maps; ZoneHandleSet<Map> object_maps;
if (state->LookupMaps(object, &object_maps)) { if (state->LookupMaps(object, &object_maps)) {
if (maps.contains(object_maps)) return Replace(effect); switch (CompareMapSets(object_maps, maps)) {
case MapSetComparisonResult::kSubset:
return Replace(effect);
case MapSetComparisonResult::kDisjoint:
state = unreachable_state();
break;
case MapSetComparisonResult::kOther:
break;
}
// TODO(turbofan): Compute the intersection. // TODO(turbofan): Compute the intersection.
} }
state = state->SetMaps(object, maps, zone()); state = state->SetMaps(object, maps, zone());
...@@ -760,7 +821,15 @@ Reduction LoadElimination::ReduceCheckMaps(Node* node) { ...@@ -760,7 +821,15 @@ Reduction LoadElimination::ReduceCheckMaps(Node* node) {
if (state == nullptr) return NoChange(); if (state == nullptr) return NoChange();
ZoneHandleSet<Map> object_maps; ZoneHandleSet<Map> object_maps;
if (state->LookupMaps(object, &object_maps)) { if (state->LookupMaps(object, &object_maps)) {
if (maps.contains(object_maps)) return Replace(effect); switch (CompareMapSets(object_maps, maps)) {
case MapSetComparisonResult::kSubset:
return Replace(effect);
case MapSetComparisonResult::kDisjoint:
state = unreachable_state();
break;
case MapSetComparisonResult::kOther:
break;
}
// TODO(turbofan): Compute the intersection. // TODO(turbofan): Compute the intersection.
} }
state = state->SetMaps(object, maps, zone()); state = state->SetMaps(object, maps, zone());
...@@ -775,12 +844,20 @@ Reduction LoadElimination::ReduceCompareMaps(Node* node) { ...@@ -775,12 +844,20 @@ Reduction LoadElimination::ReduceCompareMaps(Node* node) {
if (state == nullptr) return NoChange(); if (state == nullptr) return NoChange();
ZoneHandleSet<Map> object_maps; ZoneHandleSet<Map> object_maps;
if (state->LookupMaps(object, &object_maps)) { if (state->LookupMaps(object, &object_maps)) {
if (maps.contains(object_maps)) { switch (CompareMapSets(object_maps, maps)) {
Node* value = jsgraph()->TrueConstant(); case MapSetComparisonResult::kSubset: {
ReplaceWithValue(node, value, effect); Node* value = jsgraph()->TrueConstant();
return Replace(value); ReplaceWithValue(node, value, effect);
return Replace(value);
}
case MapSetComparisonResult::kDisjoint: {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value, effect);
return Replace(value);
}
case MapSetComparisonResult::kOther:
break;
} }
// TODO(turbofan): Compute the intersection.
} }
return UpdateState(node, state); return UpdateState(node, state);
} }
...@@ -1106,9 +1183,29 @@ Reduction LoadElimination::ReduceEffectPhi(Node* node) { ...@@ -1106,9 +1183,29 @@ Reduction LoadElimination::ReduceEffectPhi(Node* node) {
// Shortcut for the case when we do not know anything about some input. // Shortcut for the case when we do not know anything about some input.
int const input_count = node->op()->EffectInputCount(); int const input_count = node->op()->EffectInputCount();
for (int i = 1; i < input_count; ++i) { for (int i = 0; i < input_count; ++i) {
Node* const effect = NodeProperties::GetEffectInput(node, i); Node* effect = NodeProperties::GetEffectInput(node, i);
if (node_states_.Get(effect) == nullptr) return NoChange(); AbstractState const* input_state = node_states_.Get(effect);
if (input_state == nullptr) return NoChange();
if (input_state->is_unreachable()) {
// The state is unreachable, so we connect the control to the
// end.
Node* input_control = NodeProperties::GetControlInput(control, i);
effect = graph()->NewNode(jsgraph()->simplified()->RuntimeAbort(
BailoutReason::kUnreachableCodeReached),
effect, input_control);
input_control =
graph()->NewNode(common()->Throw(), effect, input_control);
NodeProperties::MergeControlToEnd(graph(), common(), input_control);
Revisit(graph()->end());
// Update the merge node with unreachable control input, and trigger
// revisit so that dead code elimination prunes the merge and its phis.
control->ReplaceInput(i, jsgraph()->Dead());
Revisit(control);
return NoChange();
}
} }
// Make a copy of the first input's state and merge with the state // Make a copy of the first input's state and merge with the state
...@@ -1131,6 +1228,13 @@ Reduction LoadElimination::ReduceEffectPhi(Node* node) { ...@@ -1131,6 +1228,13 @@ Reduction LoadElimination::ReduceEffectPhi(Node* node) {
return UpdateState(node, state_with_phis); return UpdateState(node, state_with_phis);
} }
LoadElimination::LoadElimination(Editor* editor, JSGraph* jsgraph, Zone* zone)
: AdvancedReducer(editor),
empty_state_(AbstractState::Empty()),
unreachable_state_(AbstractState::Unreachable()),
node_states_(zone),
jsgraph_(jsgraph) {}
Reduction LoadElimination::ReduceStart(Node* node) { Reduction LoadElimination::ReduceStart(Node* node) {
return UpdateState(node, empty_state()); return UpdateState(node, empty_state());
} }
......
...@@ -28,8 +28,7 @@ class JSGraph; ...@@ -28,8 +28,7 @@ class JSGraph;
class V8_EXPORT_PRIVATE LoadElimination final class V8_EXPORT_PRIVATE LoadElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) { : public NON_EXPORTED_BASE(AdvancedReducer) {
public: public:
LoadElimination(Editor* editor, JSGraph* jsgraph, Zone* zone) LoadElimination(Editor* editor, JSGraph* jsgraph, Zone* zone);
: AdvancedReducer(editor), node_states_(zone), jsgraph_(jsgraph) {}
~LoadElimination() final {} ~LoadElimination() final {}
const char* reducer_name() const override { return "LoadElimination"; } const char* reducer_name() const override { return "LoadElimination"; }
...@@ -188,6 +187,15 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -188,6 +187,15 @@ class V8_EXPORT_PRIVATE LoadElimination final
static size_t const kMaxTrackedFields = 32; static size_t const kMaxTrackedFields = 32;
enum class MapSetComparisonResult {
kSubset,
kDisjoint,
kOther,
};
static MapSetComparisonResult CompareMapSets(ZoneHandleSet<Map> const& lhs,
ZoneHandleSet<Map> const& rhs);
// Abstract state to approximate the current map of an object along the // Abstract state to approximate the current map of an object along the
// effect paths through the graph. // effect paths through the graph.
class AbstractMaps final : public ZoneObject { class AbstractMaps final : public ZoneObject {
...@@ -213,12 +221,6 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -213,12 +221,6 @@ class V8_EXPORT_PRIVATE LoadElimination final
class AbstractState final : public ZoneObject { class AbstractState final : public ZoneObject {
public: public:
AbstractState() {
for (size_t i = 0; i < arraysize(fields_); ++i) {
fields_[i] = nullptr;
}
}
bool Equals(AbstractState const* that) const; bool Equals(AbstractState const* that) const;
void Merge(AbstractState const* that, Zone* zone); void Merge(AbstractState const* that, Zone* zone);
...@@ -251,13 +253,26 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -251,13 +253,26 @@ class V8_EXPORT_PRIVATE LoadElimination final
AbstractState const* AddCheck(Node* node, Zone* zone) const; AbstractState const* AddCheck(Node* node, Zone* zone) const;
Node* LookupCheck(Node* node) const; Node* LookupCheck(Node* node) const;
bool is_unreachable() const { return is_unreachable_; }
void Print() const; void Print() const;
static AbstractState Unreachable();
static AbstractState Empty();
private: private:
AbstractState() {
for (size_t i = 0; i < arraysize(fields_); ++i) {
fields_[i] = nullptr;
}
}
AbstractChecks const* checks_ = nullptr; AbstractChecks const* checks_ = nullptr;
AbstractElements const* elements_ = nullptr; AbstractElements const* elements_ = nullptr;
AbstractField const* fields_[kMaxTrackedFields]; AbstractField const* fields_[kMaxTrackedFields];
AbstractMaps const* maps_ = nullptr; AbstractMaps const* maps_ = nullptr;
bool is_unreachable_ = false;
}; };
class AbstractStateForEffectNodes final : public ZoneObject { class AbstractStateForEffectNodes final : public ZoneObject {
...@@ -301,12 +316,14 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -301,12 +316,14 @@ class V8_EXPORT_PRIVATE LoadElimination final
CommonOperatorBuilder* common() const; CommonOperatorBuilder* common() const;
AbstractState const* empty_state() const { return &empty_state_; } AbstractState const* empty_state() const { return &empty_state_; }
AbstractState const* unreachable_state() const { return &unreachable_state_; }
Factory* factory() const; Factory* factory() const;
Graph* graph() const; Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
Zone* zone() const { return node_states_.zone(); } Zone* zone() const { return node_states_.zone(); }
AbstractState const empty_state_; AbstractState const empty_state_;
AbstractState const unreachable_state_;
AbstractStateForEffectNodes node_states_; AbstractStateForEffectNodes node_states_;
JSGraph* const jsgraph_; JSGraph* const jsgraph_;
......
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