Index: src/compiler/load-elimination.cc |
diff --git a/src/compiler/load-elimination.cc b/src/compiler/load-elimination.cc |
index 5ea1635441a5669a59e56439f8058817d733e6ab..4a5114625189483820ba5c64dfc7f90de0d9e6ff 100644 |
--- a/src/compiler/load-elimination.cc |
+++ b/src/compiler/load-elimination.cc |
@@ -8,6 +8,8 @@ |
#include "src/compiler/js-graph.h" |
#include "src/compiler/node-properties.h" |
#include "src/compiler/simplified-operator.h" |
+#include "src/factory.h" |
+#include "src/objects-inl.h" |
namespace v8 { |
namespace internal { |
@@ -320,6 +322,42 @@ void LoadElimination::AbstractField::Print() const { |
} |
} |
+bool LoadElimination::AbstractMaps::Lookup( |
+ Node* object, ZoneHandleSet<Map>* object_maps) const { |
+ for (auto pair : info_for_node_) { |
+ if (MustAlias(object, pair.first)) { |
+ *object_maps = pair.second; |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Kill( |
+ Node* object, Zone* zone) const { |
+ for (auto pair : this->info_for_node_) { |
+ if (MayAlias(object, pair.first)) { |
+ AbstractMaps* that = new (zone) AbstractMaps(zone); |
+ for (auto pair : this->info_for_node_) { |
+ if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair); |
+ } |
+ return that; |
+ } |
+ } |
+ return this; |
+} |
+ |
+void LoadElimination::AbstractMaps::Print() const { |
+ for (auto pair : info_for_node_) { |
+ PrintF(" #%d:%s\n", pair.first->id(), pair.first->op()->mnemonic()); |
+ OFStream os(stdout); |
+ ZoneHandleSet<Map> const& maps = pair.second; |
+ for (size_t i = 0; i < maps.size(); ++i) { |
+ os << " - " << Brief(*maps[i]) << "\n"; |
+ } |
+ } |
+} |
+ |
bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { |
if (this->checks_) { |
if (!that->checks_ || !that->checks_->Equals(this->checks_)) { |
@@ -344,6 +382,13 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { |
return false; |
} |
} |
+ if (this->maps_) { |
+ if (!that->maps_ || !that->maps_->Equals(this->maps_)) { |
+ return false; |
+ } |
+ } else if (that->maps_) { |
+ return false; |
+ } |
return true; |
} |
@@ -372,6 +417,11 @@ void LoadElimination::AbstractState::Merge(AbstractState const* that, |
} |
} |
} |
+ |
+ // Merge the information we have about the maps. |
+ if (this->maps_) { |
+ this->maps_ = that->maps_ ? that->maps_->Merge(this->maps_, zone) : nullptr; |
+ } |
} |
Node* LoadElimination::AbstractState::LookupCheck(Node* node) const { |
@@ -389,6 +439,35 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck( |
return that; |
} |
+bool LoadElimination::AbstractState::LookupMaps( |
+ Node* object, ZoneHandleSet<Map>* object_map) const { |
+ return this->maps_ && this->maps_->Lookup(object, object_map); |
+} |
+ |
+LoadElimination::AbstractState const* LoadElimination::AbstractState::AddMaps( |
+ Node* object, ZoneHandleSet<Map> maps, Zone* zone) const { |
+ AbstractState* that = new (zone) AbstractState(*this); |
+ if (that->maps_) { |
+ that->maps_ = that->maps_->Extend(object, maps, zone); |
+ } else { |
+ that->maps_ = new (zone) AbstractMaps(object, maps, zone); |
+ } |
+ return that; |
+} |
+ |
+LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps( |
+ Node* object, Zone* zone) const { |
+ if (this->maps_) { |
+ AbstractMaps const* that_maps = this->maps_->Kill(object, zone); |
+ if (this->maps_ != that_maps) { |
+ AbstractState* that = new (zone) AbstractState(*this); |
+ that->maps_ = that_maps; |
+ return that; |
+ } |
+ } |
+ return this; |
+} |
+ |
Node* LoadElimination::AbstractState::LookupElement(Node* object, |
Node* index) const { |
if (this->elements_) { |
@@ -481,6 +560,10 @@ void LoadElimination::AbstractState::Print() const { |
PrintF(" checks:\n"); |
checks_->Print(); |
} |
+ if (maps_) { |
+ PrintF(" maps:\n"); |
+ maps_->Print(); |
+ } |
if (elements_) { |
PrintF(" elements:\n"); |
elements_->Print(); |
@@ -520,23 +603,18 @@ Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) { |
} |
Reduction LoadElimination::ReduceCheckMaps(Node* node) { |
+ ZoneHandleSet<Map> const maps = CheckMapsParametersOf(node->op()).maps(); |
Node* const object = NodeProperties::GetValueInput(node, 0); |
Node* const effect = NodeProperties::GetEffectInput(node); |
AbstractState const* state = node_states_.Get(effect); |
if (state == nullptr) return NoChange(); |
- int const map_input_count = node->op()->ValueInputCount() - 1; |
- if (Node* const object_map = |
- state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) { |
- for (int i = 0; i < map_input_count; ++i) { |
- Node* map = NodeProperties::GetValueInput(node, 1 + i); |
- if (map == object_map) return Replace(effect); |
- } |
- } |
- if (map_input_count == 1) { |
- Node* const map0 = NodeProperties::GetValueInput(node, 1); |
- state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset), map0, |
- zone()); |
+ ZoneHandleSet<Map> object_maps; |
+ if (state->LookupMaps(object, &object_maps)) { |
+ if (maps.contains(object_maps)) return Replace(effect); |
+ state = state->KillMaps(object, zone()); |
+ // TODO(turbofan): Compute the intersection. |
} |
+ state = state->AddMaps(object, maps, zone()); |
return UpdateState(node, state); |
} |
@@ -546,18 +624,16 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) { |
Node* const effect = NodeProperties::GetEffectInput(node); |
AbstractState const* state = node_states_.Get(effect); |
if (state == nullptr) return NoChange(); |
- Node* fixed_array_map = jsgraph()->FixedArrayMapConstant(); |
- if (Node* const elements_map = |
- state->LookupField(elements, FieldIndexOf(HeapObject::kMapOffset))) { |
// Check if the {elements} already have the fixed array map. |
- if (elements_map == fixed_array_map) { |
- ReplaceWithValue(node, elements, effect); |
- return Replace(elements); |
- } |
+ ZoneHandleSet<Map> elements_maps; |
+ ZoneHandleSet<Map> fixed_array_maps(factory()->fixed_array_map()); |
+ if (state->LookupMaps(elements, &elements_maps) && |
+ fixed_array_maps.contains(elements_maps)) { |
+ ReplaceWithValue(node, elements, effect); |
+ return Replace(elements); |
} |
// We know that the resulting elements have the fixed array map. |
- state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset), |
- fixed_array_map, zone()); |
+ state = state->AddMaps(node, fixed_array_maps, zone()); |
// Kill the previous elements on {object}. |
state = |
state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone()); |
@@ -575,14 +651,12 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) { |
if (state == nullptr) return NoChange(); |
if (flags & GrowFastElementsFlag::kDoubleElements) { |
// We know that the resulting elements have the fixed double array map. |
- Node* fixed_double_array_map = jsgraph()->FixedDoubleArrayMapConstant(); |
- state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset), |
- fixed_double_array_map, zone()); |
+ state = state->AddMaps( |
+ node, ZoneHandleSet<Map>(factory()->fixed_double_array_map()), zone()); |
} else { |
// We know that the resulting elements have the fixed array map. |
- Node* fixed_array_map = jsgraph()->FixedArrayMapConstant(); |
- state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset), |
- fixed_array_map, zone()); |
+ state = state->AddMaps( |
+ node, ZoneHandleSet<Map>(factory()->fixed_array_map()), zone()); |
} |
if (flags & GrowFastElementsFlag::kArrayObject) { |
// Kill the previous Array::length on {object}. |
@@ -599,31 +673,30 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) { |
} |
Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) { |
+ ElementsTransition transition = ElementsTransitionOf(node->op()); |
Node* const object = NodeProperties::GetValueInput(node, 0); |
- Node* const source_map = NodeProperties::GetValueInput(node, 1); |
- Node* const target_map = NodeProperties::GetValueInput(node, 2); |
+ Handle<Map> source_map(transition.source()); |
+ Handle<Map> target_map(transition.target()); |
Node* const effect = NodeProperties::GetEffectInput(node); |
AbstractState const* state = node_states_.Get(effect); |
if (state == nullptr) return NoChange(); |
- if (Node* const object_map = |
- state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) { |
- if (target_map == object_map) { |
+ ZoneHandleSet<Map> object_maps; |
+ if (state->LookupMaps(object, &object_maps)) { |
+ if (ZoneHandleSet<Map>(target_map).contains(object_maps)) { |
// The {object} already has the {target_map}, so this TransitionElements |
// {node} is fully redundant (independent of what {source_map} is). |
return Replace(effect); |
} |
- state = |
- state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone()); |
- if (source_map == object_map) { |
- state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset), |
- target_map, zone()); |
+ if (object_maps.contains(ZoneHandleSet<Map>(source_map))) { |
+ object_maps.remove(source_map, zone()); |
+ object_maps.insert(target_map, zone()); |
+ state = state->KillMaps(object, zone()); |
+ state = state->AddMaps(object, object_maps, zone()); |
} |
} else { |
- state = |
- state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone()); |
+ state = state->KillMaps(object, zone()); |
} |
- ElementsTransition transition = ElementsTransitionOf(node->op()); |
- switch (transition) { |
+ switch (transition.mode()) { |
case ElementsTransition::kFastTransition: |
break; |
case ElementsTransition::kSlowTransition: |
@@ -642,23 +715,34 @@ Reduction LoadElimination::ReduceLoadField(Node* node) { |
Node* const control = NodeProperties::GetControlInput(node); |
AbstractState const* state = node_states_.Get(effect); |
if (state == nullptr) return NoChange(); |
- int field_index = FieldIndexOf(access); |
- if (field_index >= 0) { |
- if (Node* replacement = state->LookupField(object, field_index)) { |
- // Make sure we don't resurrect dead {replacement} nodes. |
- if (!replacement->IsDead()) { |
- // We might need to guard the {replacement} if the type of the |
- // {node} is more precise than the type of the {replacement}. |
- Type* const node_type = NodeProperties::GetType(node); |
- if (!NodeProperties::GetType(replacement)->Is(node_type)) { |
- replacement = graph()->NewNode(common()->TypeGuard(node_type), |
- replacement, control); |
+ if (access.offset == HeapObject::kMapOffset && |
+ access.base_is_tagged == kTaggedBase) { |
+ DCHECK(IsAnyTagged(access.machine_type.representation())); |
+ ZoneHandleSet<Map> object_maps; |
+ if (state->LookupMaps(object, &object_maps) && object_maps.size() == 1) { |
+ Node* value = jsgraph()->HeapConstant(object_maps[0]); |
+ ReplaceWithValue(node, value, effect); |
+ return Replace(value); |
+ } |
+ } else { |
+ int field_index = FieldIndexOf(access); |
+ if (field_index >= 0) { |
+ if (Node* replacement = state->LookupField(object, field_index)) { |
+ // Make sure we don't resurrect dead {replacement} nodes. |
+ if (!replacement->IsDead()) { |
+ // We might need to guard the {replacement} if the type of the |
+ // {node} is more precise than the type of the {replacement}. |
+ Type* const node_type = NodeProperties::GetType(node); |
+ if (!NodeProperties::GetType(replacement)->Is(node_type)) { |
+ replacement = graph()->NewNode(common()->TypeGuard(node_type), |
+ replacement, control); |
+ } |
+ ReplaceWithValue(node, replacement, effect); |
+ return Replace(replacement); |
} |
- ReplaceWithValue(node, replacement, effect); |
- return Replace(replacement); |
} |
+ state = state->AddField(object, field_index, node, zone()); |
} |
- state = state->AddField(object, field_index, node, zone()); |
} |
return UpdateState(node, state); |
} |
@@ -670,19 +754,33 @@ Reduction LoadElimination::ReduceStoreField(Node* node) { |
Node* const effect = NodeProperties::GetEffectInput(node); |
AbstractState const* state = node_states_.Get(effect); |
if (state == nullptr) return NoChange(); |
- int field_index = FieldIndexOf(access); |
- if (field_index >= 0) { |
- Node* const old_value = state->LookupField(object, field_index); |
- if (old_value == new_value) { |
- // This store is fully redundant. |
- return Replace(effect); |
+ if (access.offset == HeapObject::kMapOffset && |
+ access.base_is_tagged == kTaggedBase) { |
+ DCHECK(IsAnyTagged(access.machine_type.representation())); |
+ // Kill all potential knowledge about the {object}s map. |
+ state = state->KillMaps(object, zone()); |
+ Type* const new_value_type = NodeProperties::GetType(new_value); |
+ if (new_value_type->IsHeapConstant()) { |
+ // Record the new {object} map information. |
+ ZoneHandleSet<Map> object_maps( |
+ Handle<Map>::cast(new_value_type->AsHeapConstant()->Value())); |
+ state = state->AddMaps(object, object_maps, zone()); |
} |
- // Kill all potentially aliasing fields and record the new value. |
- state = state->KillField(object, field_index, zone()); |
- state = state->AddField(object, field_index, new_value, zone()); |
} else { |
- // Unsupported StoreField operator. |
- state = state->KillFields(object, zone()); |
+ int field_index = FieldIndexOf(access); |
+ if (field_index >= 0) { |
+ Node* const old_value = state->LookupField(object, field_index); |
+ if (old_value == new_value) { |
+ // This store is fully redundant. |
+ return Replace(effect); |
+ } |
+ // Kill all potentially aliasing fields and record the new value. |
+ state = state->KillField(object, field_index, zone()); |
+ state = state->AddField(object, field_index, new_value, zone()); |
+ } else { |
+ // Unsupported StoreField operator. |
+ state = state->KillFields(object, zone()); |
+ } |
} |
return UpdateState(node, state); |
} |
@@ -865,13 +963,13 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState( |
break; |
} |
case IrOpcode::kTransitionElementsKind: { |
+ ElementsTransition transition = ElementsTransitionOf(current->op()); |
Node* const object = NodeProperties::GetValueInput(current, 0); |
- Node* const target_map = NodeProperties::GetValueInput(current, 2); |
- Node* const object_map = state->LookupField( |
- object, FieldIndexOf(HeapObject::kMapOffset)); |
- if (target_map != object_map) { |
- state = state->KillField( |
- object, FieldIndexOf(HeapObject::kMapOffset), zone()); |
+ ZoneHandleSet<Map> object_maps; |
+ if (!state->LookupMaps(object, &object_maps) || |
+ !ZoneHandleSet<Map>(transition.target()) |
+ .contains(object_maps)) { |
+ state = state->KillMaps(object, zone()); |
state = state->KillField( |
object, FieldIndexOf(JSObject::kElementsOffset), zone()); |
} |
@@ -880,11 +978,16 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState( |
case IrOpcode::kStoreField: { |
FieldAccess const& access = FieldAccessOf(current->op()); |
Node* const object = NodeProperties::GetValueInput(current, 0); |
- int field_index = FieldIndexOf(access); |
- if (field_index < 0) { |
- state = state->KillFields(object, zone()); |
+ if (access.offset == HeapObject::kMapOffset) { |
+ // Invalidate what we know about the {object}s map. |
+ state = state->KillMaps(object, zone()); |
} else { |
- state = state->KillField(object, field_index, zone()); |
+ int field_index = FieldIndexOf(access); |
+ if (field_index < 0) { |
+ state = state->KillFields(object, zone()); |
+ } else { |
+ state = state->KillField(object, field_index, zone()); |
+ } |
} |
break; |
} |
@@ -916,7 +1019,8 @@ int LoadElimination::FieldIndexOf(int offset) { |
DCHECK_EQ(0, offset % kPointerSize); |
int field_index = offset / kPointerSize; |
if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1; |
- return field_index; |
+ DCHECK_LT(0, field_index); |
+ return field_index - 1; |
} |
// static |
@@ -962,6 +1066,8 @@ CommonOperatorBuilder* LoadElimination::common() const { |
Graph* LoadElimination::graph() const { return jsgraph()->graph(); } |
+Factory* LoadElimination::factory() const { return jsgraph()->factory(); } |
+ |
} // namespace compiler |
} // namespace internal |
} // namespace v8 |