Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Unified Diff: src/compiler/load-elimination.cc

Issue 2164253002: [turbofan] New GraphReducer based LoadElimination. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Address comments. REBASE. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/compiler/load-elimination.h ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/compiler/load-elimination.h ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698