| 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
|
|
|