Index: src/compiler/load-elimination.cc |
diff --git a/src/compiler/load-elimination.cc b/src/compiler/load-elimination.cc |
index 493c9cafd6c248973c14f7671874b9a90e4c5c32..c5a4290c04c63d047fd09eafaf496c0c680a86f1 100644 |
--- a/src/compiler/load-elimination.cc |
+++ b/src/compiler/load-elimination.cc |
@@ -4,42 +4,15 @@ |
#include "src/compiler/load-elimination.h" |
-#include "src/compiler/graph.h" |
-#include "src/compiler/node-marker.h" |
#include "src/compiler/node-properties.h" |
-#include "src/compiler/node.h" |
#include "src/compiler/simplified-operator.h" |
namespace v8 { |
namespace internal { |
namespace compiler { |
-#ifdef DEBUG |
-#define TRACE(...) \ |
- do { \ |
- if (FLAG_trace_turbo_load_elimination) PrintF(__VA_ARGS__); \ |
- } while (false) |
-#else |
-#define TRACE(...) |
-#endif |
- |
namespace { |
-const size_t kMaxTrackedFields = 16; |
- |
-Node* ActualValue(Node* node) { |
- switch (node->opcode()) { |
- case IrOpcode::kCheckBounds: |
- case IrOpcode::kCheckNumber: |
- case IrOpcode::kCheckTaggedPointer: |
- case IrOpcode::kCheckTaggedSigned: |
- case IrOpcode::kFinishRegion: |
- return ActualValue(NodeProperties::GetValueInput(node, 0)); |
- default: |
- return node; |
- } |
-} |
- |
enum Aliasing { kNoAlias, kMayAlias, kMustAlias }; |
Aliasing QueryAlias(Node* a, Node* b) { |
@@ -50,6 +23,8 @@ Aliasing QueryAlias(Node* a, Node* b) { |
case IrOpcode::kHeapConstant: |
case IrOpcode::kParameter: |
return kNoAlias; |
+ case IrOpcode::kFinishRegion: |
+ return QueryAlias(a->InputAt(0), b); |
default: |
break; |
} |
@@ -59,6 +34,8 @@ Aliasing QueryAlias(Node* a, Node* b) { |
case IrOpcode::kHeapConstant: |
case IrOpcode::kParameter: |
return kNoAlias; |
+ case IrOpcode::kFinishRegion: |
+ return QueryAlias(a, b->InputAt(0)); |
default: |
break; |
} |
@@ -70,373 +47,503 @@ bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; } |
bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; } |
-// Abstract state to approximate the current state of a certain field along the |
-// effect paths through the graph. |
-class AbstractField final : public ZoneObject { |
- public: |
- explicit AbstractField(Zone* zone) : info_for_node_(zone) {} |
- AbstractField(Node* object, Node* value, Zone* zone) : info_for_node_(zone) { |
- info_for_node_.insert(std::make_pair(object, value)); |
- } |
+} // namespace |
- AbstractField const* Extend(Node* object, Node* value, Zone* zone) const { |
- AbstractField* that = new (zone) AbstractField(zone); |
- that->info_for_node_ = this->info_for_node_; |
- that->info_for_node_.insert(std::make_pair(object, value)); |
- return that; |
+Reduction LoadElimination::Reduce(Node* node) { |
+ switch (node->opcode()) { |
+ case IrOpcode::kLoadField: |
+ return ReduceLoadField(node); |
+ case IrOpcode::kStoreField: |
+ return ReduceStoreField(node); |
+ case IrOpcode::kLoadElement: |
+ return ReduceLoadElement(node); |
+ case IrOpcode::kStoreElement: |
+ return ReduceStoreElement(node); |
+ case IrOpcode::kEffectPhi: |
+ return ReduceEffectPhi(node); |
+ case IrOpcode::kDead: |
+ break; |
+ case IrOpcode::kStart: |
+ return ReduceStart(node); |
+ default: |
+ return ReduceOtherNode(node); |
} |
- Node* Lookup(Node* object) const { |
- for (auto pair : info_for_node_) { |
- if (MustAlias(object, pair.first)) return pair.second; |
+ return NoChange(); |
+} |
+ |
+Node* LoadElimination::AbstractElements::Lookup(Node* object, |
+ Node* index) const { |
+ for (Element const element : elements_) { |
+ if (element.object == nullptr) continue; |
+ DCHECK_NOT_NULL(element.index); |
+ DCHECK_NOT_NULL(element.value); |
+ if (MustAlias(object, element.object) && MustAlias(index, element.index)) { |
+ return element.value; |
} |
- return nullptr; |
- } |
- AbstractField const* Kill(Node* object, Zone* zone) const { |
- for (auto pair : this->info_for_node_) { |
- if (MayAlias(object, pair.first)) { |
- AbstractField* that = new (zone) AbstractField(zone); |
- for (auto pair : this->info_for_node_) { |
- if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair); |
+ } |
+ return nullptr; |
+} |
+ |
+LoadElimination::AbstractElements const* |
+LoadElimination::AbstractElements::Kill(Node* object, Node* index, |
+ Zone* zone) const { |
+ for (Element const element : this->elements_) { |
+ if (element.object == nullptr) continue; |
+ if (MayAlias(object, element.object)) { |
+ AbstractElements* that = new (zone) AbstractElements(zone); |
+ for (Element const element : this->elements_) { |
+ if (element.object == nullptr) continue; |
+ DCHECK_NOT_NULL(element.index); |
+ DCHECK_NOT_NULL(element.value); |
+ if (!MayAlias(object, element.object) || |
+ !MayAlias(index, element.index)) { |
+ that->elements_[that->next_index_++] = element; |
} |
- return that; |
- } |
- } |
- return this; |
- } |
- bool Equals(AbstractField const* that) const { |
- return this == that || this->info_for_node_ == that->info_for_node_; |
- } |
- AbstractField const* Merge(AbstractField const* that, Zone* zone) const { |
- if (this->Equals(that)) return this; |
- AbstractField* copy = new (zone) AbstractField(zone); |
- for (auto this_it : this->info_for_node_) { |
- Node* this_object = this_it.first; |
- Node* this_value = this_it.second; |
- auto that_it = that->info_for_node_.find(this_object); |
- if (that_it != that->info_for_node_.end() && |
- that_it->second == this_value) { |
- copy->info_for_node_.insert(this_it); |
} |
+ return that; |
} |
- return copy; |
- } |
- |
- private: |
- ZoneMap<Node*, Node*> info_for_node_; |
-}; |
- |
-// Abstract state to track the state of all fields along the effect paths |
-// through the graph. |
-class AbstractState final : public ZoneObject { |
- public: |
- AbstractState() { |
- for (size_t i = 0; i < kMaxTrackedFields; ++i) fields_[i] = nullptr; |
} |
+ return this; |
+} |
- AbstractState const* Extend(Node* object, size_t index, Node* value, |
- Zone* zone) const { |
- AbstractState* that = new (zone) AbstractState(*this); |
- AbstractField const* that_field = that->fields_[index]; |
- if (that_field) { |
- that_field = that_field->Extend(object, value, zone); |
- } else { |
- that_field = new (zone) AbstractField(object, value, zone); |
- } |
- that->fields_[index] = that_field; |
- return that; |
- } |
- AbstractState const* Kill(Node* object, size_t index, Zone* zone) const { |
- if (!this->fields_[index]) return this; |
- AbstractState* that = new (zone) AbstractState(*this); |
- that->fields_[index] = nullptr; |
- return that; |
- } |
- AbstractState const* Merge(AbstractState const* that, Zone* zone) const { |
- if (this->Equals(that)) return this; |
- AbstractState* copy = new (zone) AbstractState(); |
- for (size_t i = 0; i < kMaxTrackedFields; ++i) { |
- AbstractField const* this_field = this->fields_[i]; |
- AbstractField const* that_field = that->fields_[i]; |
- if (this_field && that_field) { |
- copy->fields_[i] = this_field->Merge(that_field, zone); |
+bool LoadElimination::AbstractElements::Equals( |
+ AbstractElements const* that) const { |
+ if (this == that) return true; |
+ for (size_t i = 0; i < arraysize(elements_); ++i) { |
+ Element this_element = this->elements_[i]; |
+ if (this_element.object == nullptr) continue; |
+ for (size_t j = 0;; ++j) { |
+ if (j == arraysize(elements_)) return false; |
+ Element that_element = that->elements_[j]; |
+ if (this_element.object == that_element.object && |
+ this_element.index == that_element.index && |
+ this_element.value == that_element.value) { |
+ break; |
} |
} |
- return copy; |
- } |
- Node* Lookup(Node* object, size_t index) const { |
- AbstractField const* this_field = this->fields_[index]; |
- if (this_field) return this_field->Lookup(object); |
- return nullptr; |
- } |
- bool Equals(AbstractState const* that) const { |
- if (this == that) return true; |
- for (size_t i = 0; i < kMaxTrackedFields; ++i) { |
- AbstractField const* this_field = this->fields_[i]; |
- AbstractField const* that_field = that->fields_[i]; |
- if (this_field) { |
- if (!that_field || !this_field->Equals(that_field)) return false; |
- } else if (that_field) { |
- return false; |
+ } |
+ for (size_t i = 0; i < arraysize(elements_); ++i) { |
+ Element that_element = that->elements_[i]; |
+ if (that_element.object == nullptr) continue; |
+ for (size_t j = 0;; ++j) { |
+ if (j == arraysize(elements_)) return false; |
+ Element this_element = this->elements_[j]; |
+ if (that_element.object == this_element.object && |
+ that_element.index == this_element.index && |
+ that_element.value == this_element.value) { |
+ break; |
} |
- DCHECK(this_field == that_field || this_field->Equals(that_field)); |
} |
- return true; |
- } |
- |
- private: |
- AbstractField const* fields_[kMaxTrackedFields]; |
-}; |
- |
-class LoadEliminationAnalysis final { |
- public: |
- LoadEliminationAnalysis(Graph* graph, Zone* zone) |
- : candidates_(zone), |
- empty_state_(), |
- queue_(zone), |
- node_states_(graph->NodeCount(), zone) {} |
- |
- void Run(Node* start) { |
- TRACE("--{Analysis phase}--\n"); |
- UpdateState(start, empty_state()); |
- while (!queue_.empty()) { |
- Node* const node = queue_.top(); |
- queue_.pop(); |
- VisitNode(node); |
- } |
- TRACE("--{Replacement phase}--\n"); |
- ZoneMap<Node*, Node*> replacements(zone()); |
- for (Node* const node : candidates_) { |
- switch (node->opcode()) { |
- case IrOpcode::kLoadField: { |
- FieldAccess const& access = FieldAccessOf(node->op()); |
- Node* const object = |
- ActualValue(NodeProperties::GetValueInput(node, 0)); |
- Node* const effect = NodeProperties::GetEffectInput(node); |
- AbstractState const* state = GetState(effect); |
- int field_index = FieldIndexOf(access); |
- DCHECK_LE(0, field_index); |
- if (Node* value = state->Lookup(object, field_index)) { |
- auto it = replacements.find(value); |
- if (it != replacements.end()) value = it->second; |
- Type* const value_type = NodeProperties::GetType(value); |
- if (value_type->Is(access.type)) { |
- replacements.insert(std::make_pair(node, value)); |
- TRACE(" - Replacing redundant #%d:LoadField with #%d:%s\n", |
- node->id(), value->id(), value->op()->mnemonic()); |
- NodeProperties::ReplaceUses(node, value, effect); |
- node->Kill(); |
- } else { |
- TRACE( |
- " - Cannot replace redundant #%d:LoadField with #%d:%s," |
- " because types don't agree", |
- node->id(), value->id(), value->op()->mnemonic()); |
- } |
- } |
- break; |
- } |
- case IrOpcode::kStoreField: { |
- FieldAccess const& access = FieldAccessOf(node->op()); |
- Node* const object = |
- ActualValue(NodeProperties::GetValueInput(node, 0)); |
- Node* const value = |
- ActualValue(NodeProperties::GetValueInput(node, 1)); |
- Node* const effect = NodeProperties::GetEffectInput(node); |
- AbstractState const* state = GetState(effect); |
- int field_index = FieldIndexOf(access); |
- DCHECK_LE(0, field_index); |
- if (value == state->Lookup(object, field_index)) { |
- TRACE(" - Killing redundant #%d:StoreField\n", node->id()); |
- NodeProperties::ReplaceUses(node, value, effect); |
- node->Kill(); |
- } |
- break; |
- } |
- default: |
- UNREACHABLE(); |
+ } |
+ return true; |
+} |
+ |
+LoadElimination::AbstractElements const* |
+LoadElimination::AbstractElements::Merge(AbstractElements const* that, |
+ Zone* zone) const { |
+ if (this->Equals(that)) return this; |
+ AbstractElements* copy = new (zone) AbstractElements(zone); |
+ for (Element const this_element : this->elements_) { |
+ if (this_element.object == nullptr) continue; |
+ for (Element const that_element : that->elements_) { |
+ if (this_element.object == that_element.object && |
+ this_element.index == that_element.index && |
+ this_element.value == that_element.value) { |
+ copy->elements_[copy->next_index_++] = this_element; |
} |
} |
} |
+ return copy; |
+} |
- private: |
- void VisitNode(Node* node) { |
- TRACE(" - Visiting node #%d:%s\n", node->id(), node->op()->mnemonic()); |
- switch (node->opcode()) { |
- case IrOpcode::kEffectPhi: |
- return VisitEffectPhi(node); |
- case IrOpcode::kLoadField: |
- return VisitLoadField(node); |
- case IrOpcode::kStoreElement: |
- return VisitStoreElement(node); |
- case IrOpcode::kStoreField: |
- return VisitStoreField(node); |
- case IrOpcode::kDeoptimize: |
- case IrOpcode::kReturn: |
- case IrOpcode::kTerminate: |
- case IrOpcode::kThrow: |
- break; |
- default: |
- return VisitOtherNode(node); |
- } |
+Node* LoadElimination::AbstractField::Lookup(Node* object) const { |
+ for (auto pair : info_for_node_) { |
+ if (MustAlias(object, pair.first)) return pair.second; |
} |
+ return nullptr; |
+} |
- void VisitEffectPhi(Node* node) { |
- int const input_count = node->InputCount() - 1; |
- DCHECK_LT(0, input_count); |
- Node* const control = NodeProperties::GetControlInput(node); |
- Node* const effect0 = NodeProperties::GetEffectInput(node, 0); |
- AbstractState const* state = GetState(effect0); |
- if (state == nullptr) return; |
- if (control->opcode() == IrOpcode::kMerge) { |
- for (int i = 1; i < input_count; ++i) { |
- Node* const effecti = NodeProperties::GetEffectInput(node, i); |
- if (GetState(effecti) == nullptr) return; |
+LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill( |
+ Node* object, Zone* zone) const { |
+ for (auto pair : this->info_for_node_) { |
+ if (MayAlias(object, pair.first)) { |
+ AbstractField* that = new (zone) AbstractField(zone); |
+ for (auto pair : this->info_for_node_) { |
+ if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair); |
} |
+ return that; |
} |
- for (int i = 1; i < input_count; ++i) { |
- Node* const effecti = NodeProperties::GetEffectInput(node, i); |
- if (AbstractState const* statei = GetState(effecti)) { |
- state = state->Merge(statei, zone()); |
- } |
+ } |
+ return this; |
+} |
+ |
+bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { |
+ if (this->elements_) { |
+ if (!that->elements_ || !that->elements_->Equals(this->elements_)) { |
+ return false; |
} |
- UpdateState(node, state); |
- } |
- |
- void VisitLoadField(Node* node) { |
- FieldAccess const& access = FieldAccessOf(node->op()); |
- Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0)); |
- Node* const effect = NodeProperties::GetEffectInput(node); |
- AbstractState const* state = GetState(effect); |
- int field_index = FieldIndexOf(access); |
- if (field_index >= 0) { |
- Node* const value = state->Lookup(object, field_index); |
- if (!value) { |
- TRACE(" Node #%d:LoadField is not redundant\n", node->id()); |
- state = state->Extend(object, field_index, node, zone()); |
- } else if (!NodeProperties::GetType(value)->Is(access.type)) { |
- TRACE( |
- " Node #%d:LoadField is redundant for #%d:%s, but" |
- " types don't agree\n", |
- node->id(), value->id(), value->op()->mnemonic()); |
- state = state->Extend(object, field_index, node, zone()); |
- } else if (value) { |
- TRACE(" Node #%d:LoadField is fully redundant for #%d:%s\n", |
- node->id(), value->id(), value->op()->mnemonic()); |
- candidates_.insert(node); |
- } |
- } else { |
- TRACE(" Node #%d:LoadField is unsupported\n", node->id()); |
+ } else if (that->elements_) { |
+ return false; |
+ } |
+ for (size_t i = 0u; i < arraysize(fields_); ++i) { |
+ AbstractField const* this_field = this->fields_[i]; |
+ AbstractField const* that_field = that->fields_[i]; |
+ if (this_field) { |
+ if (!that_field || !that_field->Equals(this_field)) return false; |
+ } else if (that_field) { |
+ return false; |
} |
- UpdateState(node, state); |
- } |
- |
- void VisitStoreField(Node* node) { |
- FieldAccess const& access = FieldAccessOf(node->op()); |
- Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0)); |
- Node* const new_value = NodeProperties::GetValueInput(node, 1); |
- Node* const effect = NodeProperties::GetEffectInput(node); |
- AbstractState const* state = GetState(effect); |
- int field_index = FieldIndexOf(access); |
- if (field_index >= 0) { |
- Node* const old_value = state->Lookup(object, field_index); |
- if (old_value == new_value) { |
- TRACE(" Node #%d:StoreField is fully redundant, storing #%d:%s\n", |
- node->id(), new_value->id(), new_value->op()->mnemonic()); |
- candidates_.insert(node); |
+ } |
+ return true; |
+} |
+ |
+void LoadElimination::AbstractState::Merge(AbstractState const* that, |
+ Zone* zone) { |
+ // Merge the information we have about the elements. |
+ if (this->elements_) { |
+ this->elements_ = that->elements_ |
+ ? that->elements_->Merge(this->elements_, zone) |
+ : that->elements_; |
+ } else { |
+ this->elements_ = that->elements_; |
+ } |
+ |
+ // Merge the information we have about the fields. |
+ for (size_t i = 0; i < arraysize(fields_); ++i) { |
+ if (this->fields_[i]) { |
+ if (that->fields_[i]) { |
+ this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone); |
+ } else { |
+ this->fields_[i] = nullptr; |
} |
- TRACE(" Killing all potentially aliasing stores for %d on #%d:%s\n", |
- field_index, object->id(), object->op()->mnemonic()); |
- state = state->Kill(object, field_index, zone()); |
- TRACE(" Node #%d:StoreField provides #%d:%s for %d on #%d:%s\n", |
- node->id(), new_value->id(), new_value->op()->mnemonic(), |
- field_index, object->id(), object->op()->mnemonic()); |
- state = state->Extend(object, field_index, new_value, zone()); |
- } else { |
- TRACE(" Node #%d:StoreField is unsupported\n", node->id()); |
- state = empty_state(); |
} |
- UpdateState(node, state); |
} |
+} |
- void VisitStoreElement(Node* node) { |
- Node* const effect = NodeProperties::GetEffectInput(node); |
- AbstractState const* state = GetState(effect); |
- UpdateState(node, state); |
+Node* LoadElimination::AbstractState::LookupElement(Node* object, |
+ Node* index) const { |
+ if (this->elements_) { |
+ return this->elements_->Lookup(object, index); |
} |
+ return nullptr; |
+} |
- void VisitOtherNode(Node* node) { |
- DCHECK_EQ(1, node->op()->EffectInputCount()); |
- DCHECK_EQ(1, node->op()->EffectOutputCount()); |
- Node* const effect = NodeProperties::GetEffectInput(node); |
- AbstractState const* state = node->op()->HasProperty(Operator::kNoWrite) |
- ? GetState(effect) |
- : empty_state(); |
- UpdateState(node, state); |
+LoadElimination::AbstractState const* |
+LoadElimination::AbstractState::AddElement(Node* object, Node* index, |
+ Node* value, Zone* zone) const { |
+ AbstractState* that = new (zone) AbstractState(*this); |
+ if (that->elements_) { |
+ that->elements_ = that->elements_->Extend(object, index, value, zone); |
+ } else { |
+ that->elements_ = new (zone) AbstractElements(object, index, value, zone); |
} |
+ return that; |
+} |
- int FieldIndexOf(FieldAccess const& access) const { |
- switch (access.machine_type.representation()) { |
- case MachineRepresentation::kNone: |
- case MachineRepresentation::kBit: |
- UNREACHABLE(); |
- break; |
- case MachineRepresentation::kWord8: |
- case MachineRepresentation::kWord16: |
- case MachineRepresentation::kWord32: |
- case MachineRepresentation::kWord64: |
- case MachineRepresentation::kFloat32: |
- return -1; // Currently untracked. |
- case MachineRepresentation::kFloat64: |
- case MachineRepresentation::kSimd128: |
- case MachineRepresentation::kTagged: |
- // TODO(bmeurer): Check that we never do overlapping load/stores of |
- // individual parts of Float64/Simd128 values. |
- break; |
+LoadElimination::AbstractState const* |
+LoadElimination::AbstractState::KillElement(Node* object, Node* index, |
+ Zone* zone) const { |
+ if (this->elements_) { |
+ AbstractElements const* that_elements = |
+ this->elements_->Kill(object, index, zone); |
+ if (this->elements_ != that_elements) { |
+ AbstractState* that = new (zone) AbstractState(*this); |
+ that->elements_ = that_elements; |
+ return that; |
} |
- DCHECK_EQ(kTaggedBase, access.base_is_tagged); |
- DCHECK_EQ(0, access.offset % kPointerSize); |
- int field_index = access.offset / kPointerSize; |
- if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1; |
- return field_index; |
} |
+ return this; |
+} |
- AbstractState const* GetState(Node* node) const { |
- return node_states_[node->id()]; |
+LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField( |
+ Node* object, size_t index, Node* value, Zone* zone) const { |
+ AbstractState* that = new (zone) AbstractState(*this); |
+ if (that->fields_[index]) { |
+ that->fields_[index] = that->fields_[index]->Extend(object, value, zone); |
+ } else { |
+ that->fields_[index] = new (zone) AbstractField(object, value, zone); |
} |
- void SetState(Node* node, AbstractState const* state) { |
- node_states_[node->id()] = state; |
+ return that; |
+} |
+ |
+LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField( |
+ Node* object, size_t index, Zone* zone) const { |
+ if (AbstractField const* this_field = this->fields_[index]) { |
+ this_field = this_field->Kill(object, zone); |
+ if (this->fields_[index] != this_field) { |
+ AbstractState* that = new (zone) AbstractState(*this); |
+ that->fields_[index] = this_field; |
+ return that; |
+ } |
} |
+ return this; |
+} |
- void UpdateState(Node* node, AbstractState const* new_state) { |
- AbstractState const* old_state = GetState(node); |
- if (old_state && old_state->Equals(new_state)) return; |
- SetState(node, new_state); |
- EnqueueUses(node); |
+Node* LoadElimination::AbstractState::LookupField(Node* object, |
+ size_t index) const { |
+ if (AbstractField const* this_field = this->fields_[index]) { |
+ return this_field->Lookup(object); |
} |
+ return nullptr; |
+} |
+ |
+LoadElimination::AbstractState const* |
+LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const { |
+ size_t const id = node->id(); |
+ if (id < info_for_node_.size()) return info_for_node_[id]; |
+ return nullptr; |
+} |
+ |
+void LoadElimination::AbstractStateForEffectNodes::Set( |
+ Node* node, AbstractState const* state) { |
+ size_t const id = node->id(); |
+ if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr); |
+ info_for_node_[id] = state; |
+} |
- void EnqueueUses(Node* node) { |
- for (Edge const edge : node->use_edges()) { |
- if (NodeProperties::IsEffectEdge(edge)) { |
- queue_.push(edge.from()); |
+Reduction LoadElimination::ReduceLoadField(Node* node) { |
+ FieldAccess const& access = FieldAccessOf(node->op()); |
+ 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 field_index = FieldIndexOf(access); |
+ if (field_index >= 0) { |
+ if (Node* const replacement = state->LookupField(object, field_index)) { |
+ // Make sure the {replacement} has at least as good type |
+ // as the original {node}. |
+ if (NodeProperties::GetType(replacement) |
+ ->Is(NodeProperties::GetType(node))) { |
+ ReplaceWithValue(node, replacement, effect); |
+ DCHECK(!replacement->IsDead()); |
+ return Replace(replacement); |
} |
} |
+ state = state->AddField(object, field_index, node, zone()); |
} |
+ return UpdateState(node, state); |
+} |
- AbstractState const* empty_state() const { return &empty_state_; } |
- Zone* zone() const { return node_states_.get_allocator().zone(); } |
+Reduction LoadElimination::ReduceStoreField(Node* node) { |
+ FieldAccess const& access = FieldAccessOf(node->op()); |
+ Node* const object = NodeProperties::GetValueInput(node, 0); |
+ Node* const new_value = NodeProperties::GetValueInput(node, 1); |
+ 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); |
+ } |
+ // 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 = empty_state(); |
+ } |
+ return UpdateState(node, state); |
+} |
- ZoneSet<Node*> candidates_; |
- AbstractState const empty_state_; |
- ZoneStack<Node*> queue_; |
- ZoneVector<AbstractState const*> node_states_; |
+Reduction LoadElimination::ReduceLoadElement(Node* node) { |
+ Node* const object = NodeProperties::GetValueInput(node, 0); |
+ Node* const index = NodeProperties::GetValueInput(node, 1); |
+ Node* const effect = NodeProperties::GetEffectInput(node); |
+ AbstractState const* state = node_states_.Get(effect); |
+ if (state == nullptr) return NoChange(); |
+ if (Node* const replacement = state->LookupElement(object, index)) { |
+ // Make sure the {replacement} has at least as good type |
+ // as the original {node}. |
+ if (NodeProperties::GetType(replacement) |
+ ->Is(NodeProperties::GetType(node))) { |
+ ReplaceWithValue(node, replacement, effect); |
+ DCHECK(!replacement->IsDead()); |
+ return Replace(replacement); |
+ } |
+ } |
+ state = state->AddElement(object, index, node, zone()); |
+ return UpdateState(node, state); |
+} |
- DISALLOW_COPY_AND_ASSIGN(LoadEliminationAnalysis); |
-}; |
+Reduction LoadElimination::ReduceStoreElement(Node* node) { |
+ ElementAccess const& access = ElementAccessOf(node->op()); |
+ Node* const object = NodeProperties::GetValueInput(node, 0); |
+ Node* const index = NodeProperties::GetValueInput(node, 1); |
+ Node* const new_value = NodeProperties::GetValueInput(node, 2); |
+ Node* const effect = NodeProperties::GetEffectInput(node); |
+ AbstractState const* state = node_states_.Get(effect); |
+ if (state == nullptr) return NoChange(); |
+ Node* const old_value = state->LookupElement(object, index); |
+ if (old_value == new_value) { |
+ // This store is fully redundant. |
+ return Replace(effect); |
+ } |
+ // Kill all potentially aliasing elements. |
+ state = state->KillElement(object, index, zone()); |
+ // Only record the new value if the store doesn't have an implicit truncation. |
+ switch (access.machine_type.representation()) { |
+ case MachineRepresentation::kNone: |
+ case MachineRepresentation::kBit: |
+ UNREACHABLE(); |
+ break; |
+ case MachineRepresentation::kWord8: |
+ case MachineRepresentation::kWord16: |
+ case MachineRepresentation::kWord32: |
+ case MachineRepresentation::kWord64: |
+ case MachineRepresentation::kFloat32: |
+ // TODO(turbofan): Add support for doing the truncations. |
+ break; |
+ case MachineRepresentation::kFloat64: |
+ case MachineRepresentation::kSimd128: |
+ case MachineRepresentation::kTagged: |
+ state = state->AddElement(object, index, new_value, zone()); |
+ break; |
+ } |
+ return UpdateState(node, state); |
+} |
-} // namespace |
+Reduction LoadElimination::ReduceEffectPhi(Node* node) { |
+ Node* const effect0 = NodeProperties::GetEffectInput(node, 0); |
+ Node* const control = NodeProperties::GetControlInput(node); |
+ AbstractState const* state0 = node_states_.Get(effect0); |
+ if (state0 == nullptr) return NoChange(); |
+ if (control->opcode() == IrOpcode::kLoop) { |
+ // Here we rely on having only reducible loops: |
+ // The loop entry edge always dominates the header, so we can just take |
+ // the state from the first input, and compute the loop state based on it. |
+ AbstractState const* state = ComputeLoopState(node, state0); |
+ return UpdateState(node, state); |
+ } |
+ DCHECK_EQ(IrOpcode::kMerge, control->opcode()); |
+ |
+ // Shortcut for the case when we do not know anything about some input. |
+ int const input_count = node->op()->EffectInputCount(); |
+ for (int i = 1; i < input_count; ++i) { |
+ Node* const effect = NodeProperties::GetEffectInput(node, i); |
+ if (node_states_.Get(effect) == nullptr) return NoChange(); |
+ } |
+ |
+ // Make a copy of the first input's state and merge with the state |
+ // from other inputs. |
+ AbstractState* state = new (zone()) AbstractState(*state0); |
+ for (int i = 1; i < input_count; ++i) { |
+ Node* const input = NodeProperties::GetEffectInput(node, i); |
+ state->Merge(node_states_.Get(input), zone()); |
+ } |
+ return UpdateState(node, state); |
+} |
+ |
+Reduction LoadElimination::ReduceStart(Node* node) { |
+ return UpdateState(node, empty_state()); |
+} |
+ |
+Reduction LoadElimination::ReduceOtherNode(Node* node) { |
+ if (node->op()->EffectInputCount() == 1) { |
+ if (node->op()->EffectOutputCount() == 1) { |
+ Node* const effect = NodeProperties::GetEffectInput(node); |
+ AbstractState const* state = node_states_.Get(effect); |
+ // If we do not know anything about the predecessor, do not propagate |
+ // just yet because we will have to recompute anyway once we compute |
+ // the predecessor. |
+ if (state == nullptr) return NoChange(); |
+ // Check if this {node} has some uncontrolled side effects. |
+ if (!node->op()->HasProperty(Operator::kNoWrite)) { |
+ state = empty_state(); |
+ } |
+ return UpdateState(node, state); |
+ } else { |
+ // Effect terminators should be handled specially. |
+ return NoChange(); |
+ } |
+ } |
+ DCHECK_EQ(0, node->op()->EffectInputCount()); |
+ DCHECK_EQ(0, node->op()->EffectOutputCount()); |
+ return NoChange(); |
+} |
-void LoadElimination::Run() { |
- LoadEliminationAnalysis analysis(graph(), zone()); |
- analysis.Run(graph()->start()); |
+Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) { |
+ AbstractState const* original = node_states_.Get(node); |
+ // Only signal that the {node} has Changed, if the information about {state} |
+ // has changed wrt. the {original}. |
+ if (state != original) { |
+ if (original == nullptr || !state->Equals(original)) { |
+ node_states_.Set(node, state); |
+ return Changed(node); |
+ } |
+ } |
+ return NoChange(); |
+} |
+ |
+LoadElimination::AbstractState const* LoadElimination::ComputeLoopState( |
+ Node* node, AbstractState const* state) const { |
+ Node* const control = NodeProperties::GetControlInput(node); |
+ ZoneQueue<Node*> queue(zone()); |
+ ZoneSet<Node*> visited(zone()); |
+ visited.insert(node); |
+ for (int i = 1; i < control->InputCount(); ++i) { |
+ queue.push(node->InputAt(i)); |
+ } |
+ while (!queue.empty()) { |
+ Node* const current = queue.front(); |
+ queue.pop(); |
+ if (visited.find(current) == visited.end()) { |
+ visited.insert(current); |
+ if (!current->op()->HasProperty(Operator::kNoWrite)) { |
+ switch (current->opcode()) { |
+ 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) return empty_state(); |
+ state = state->KillField(object, field_index, zone()); |
+ break; |
+ } |
+ case IrOpcode::kStoreElement: { |
+ Node* const object = NodeProperties::GetValueInput(current, 0); |
+ Node* const index = NodeProperties::GetValueInput(current, 1); |
+ state = state->KillElement(object, index, zone()); |
+ break; |
+ } |
+ default: |
+ return empty_state(); |
+ } |
+ } |
+ for (int i = 0; i < current->op()->EffectInputCount(); ++i) { |
+ queue.push(NodeProperties::GetEffectInput(current, i)); |
+ } |
+ } |
+ } |
+ return state; |
+} |
+ |
+// static |
+int LoadElimination::FieldIndexOf(FieldAccess const& access) { |
+ switch (access.machine_type.representation()) { |
+ case MachineRepresentation::kNone: |
+ case MachineRepresentation::kBit: |
+ UNREACHABLE(); |
+ break; |
+ case MachineRepresentation::kWord8: |
+ case MachineRepresentation::kWord16: |
+ case MachineRepresentation::kWord32: |
+ case MachineRepresentation::kWord64: |
+ case MachineRepresentation::kFloat32: |
+ return -1; // Currently untracked. |
+ case MachineRepresentation::kFloat64: |
+ case MachineRepresentation::kSimd128: |
+ case MachineRepresentation::kTagged: |
+ // TODO(bmeurer): Check that we never do overlapping load/stores of |
+ // individual parts of Float64/Simd128 values. |
+ break; |
+ } |
+ DCHECK_EQ(kTaggedBase, access.base_is_tagged); |
+ DCHECK_EQ(0, access.offset % kPointerSize); |
+ int field_index = access.offset / kPointerSize; |
+ if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1; |
+ return field_index; |
} |
} // namespace compiler |