| Index: src/compiler/redundancy-elimination.cc
|
| diff --git a/src/compiler/redundancy-elimination.cc b/src/compiler/redundancy-elimination.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ae87349896921cecc4b54be79fa49ccad2cef580
|
| --- /dev/null
|
| +++ b/src/compiler/redundancy-elimination.cc
|
| @@ -0,0 +1,216 @@
|
| +// Copyright 2016 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/compiler/redundancy-elimination.h"
|
| +
|
| +#include "src/compiler/node-properties.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +RedundancyElimination::RedundancyElimination(Editor* editor, Zone* zone)
|
| + : AdvancedReducer(editor), node_checks_(zone), zone_(zone) {}
|
| +
|
| +RedundancyElimination::~RedundancyElimination() {}
|
| +
|
| +Reduction RedundancyElimination::Reduce(Node* node) {
|
| + switch (node->opcode()) {
|
| + case IrOpcode::kCheckFloat64Hole:
|
| + case IrOpcode::kCheckTaggedHole:
|
| + case IrOpcode::kCheckTaggedPointer:
|
| + case IrOpcode::kCheckTaggedSigned:
|
| + case IrOpcode::kCheckedFloat64ToInt32:
|
| + case IrOpcode::kCheckedInt32Add:
|
| + case IrOpcode::kCheckedInt32Sub:
|
| + case IrOpcode::kCheckedTaggedToFloat64:
|
| + case IrOpcode::kCheckedTaggedToInt32:
|
| + case IrOpcode::kCheckedUint32ToInt32:
|
| + return ReduceCheckNode(node);
|
| + case IrOpcode::kEffectPhi:
|
| + return ReduceEffectPhi(node);
|
| + case IrOpcode::kDead:
|
| + break;
|
| + case IrOpcode::kStart:
|
| + return ReduceStart(node);
|
| + default:
|
| + return ReduceOtherNode(node);
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +// static
|
| +RedundancyElimination::EffectPathChecks*
|
| +RedundancyElimination::EffectPathChecks::Copy(Zone* zone,
|
| + EffectPathChecks const* checks) {
|
| + return new (zone->New(sizeof(EffectPathChecks))) EffectPathChecks(*checks);
|
| +}
|
| +
|
| +// static
|
| +RedundancyElimination::EffectPathChecks const*
|
| +RedundancyElimination::EffectPathChecks::Empty(Zone* zone) {
|
| + return new (zone->New(sizeof(EffectPathChecks))) EffectPathChecks(nullptr, 0);
|
| +}
|
| +
|
| +void RedundancyElimination::EffectPathChecks::Merge(
|
| + EffectPathChecks const* that) {
|
| + // Change the current check list to a longest common tail of this check
|
| + // list and the other list.
|
| +
|
| + // First, we throw away the prefix of the longer list, so that
|
| + // we have lists of the same length.
|
| + Check* that_head = that->head_;
|
| + size_t that_size = that->size_;
|
| + while (that_size > size_) {
|
| + that_head = that_head->next;
|
| + that_size--;
|
| + }
|
| + while (size_ > that_size) {
|
| + head_ = head_->next;
|
| + size_--;
|
| + }
|
| +
|
| + // Then we go through both lists in lock-step until we find
|
| + // the common tail.
|
| + while (head_ != that_head) {
|
| + DCHECK_LT(0u, size_);
|
| + DCHECK_NOT_NULL(head_);
|
| + size_--;
|
| + head_ = head_->next;
|
| + that_head = that_head->next;
|
| + }
|
| +}
|
| +
|
| +RedundancyElimination::EffectPathChecks const*
|
| +RedundancyElimination::EffectPathChecks::AddCheck(Zone* zone,
|
| + Node* node) const {
|
| + Check* head = new (zone->New(sizeof(Check))) Check(node, head_);
|
| + return new (zone->New(sizeof(EffectPathChecks)))
|
| + EffectPathChecks(head, size_ + 1);
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +bool IsCompatibleCheck(Node const* a, Node const* b) {
|
| + if (a->op() != b->op()) return false;
|
| + for (int i = a->op()->ValueInputCount(); --i >= 0;) {
|
| + if (a->InputAt(i) != b->InputAt(i)) return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const {
|
| + for (Check const* check = head_; check != nullptr; check = check->next) {
|
| + if (IsCompatibleCheck(check->node, node)) {
|
| + DCHECK(!check->node->IsDead());
|
| + return check->node;
|
| + }
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +RedundancyElimination::EffectPathChecks const*
|
| +RedundancyElimination::PathChecksForEffectNodes::Get(Node* node) const {
|
| + size_t const id = node->id();
|
| + if (id < info_for_node_.size()) return info_for_node_[id];
|
| + return nullptr;
|
| +}
|
| +
|
| +void RedundancyElimination::PathChecksForEffectNodes::Set(
|
| + Node* node, EffectPathChecks const* checks) {
|
| + size_t const id = node->id();
|
| + if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
|
| + info_for_node_[id] = checks;
|
| +}
|
| +
|
| +Reduction RedundancyElimination::ReduceCheckNode(Node* node) {
|
| + Node* const effect = NodeProperties::GetEffectInput(node);
|
| + EffectPathChecks const* checks = node_checks_.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 (checks == nullptr) return NoChange();
|
| + // See if we have another check that dominates us.
|
| + if (Node* check = checks->LookupCheck(node)) {
|
| + ReplaceWithValue(node, check);
|
| + return Replace(check);
|
| + }
|
| + // Learn from this check.
|
| + return UpdateChecks(node, checks->AddCheck(zone(), node));
|
| +}
|
| +
|
| +Reduction RedundancyElimination::ReduceEffectPhi(Node* node) {
|
| + Node* const control = NodeProperties::GetControlInput(node);
|
| + 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 use
|
| + // the information from the loop entry edge.
|
| + return TakeChecksFromFirstEffect(node);
|
| + }
|
| + 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 = 0; i < input_count; ++i) {
|
| + Node* const effect = NodeProperties::GetEffectInput(node, i);
|
| + if (node_checks_.Get(effect) == nullptr) return NoChange();
|
| + }
|
| +
|
| + // Make a copy of the first input's checks and merge with the checks
|
| + // from other inputs.
|
| + EffectPathChecks* checks = EffectPathChecks::Copy(
|
| + zone(), node_checks_.Get(NodeProperties::GetEffectInput(node, 0)));
|
| + for (int i = 1; i < input_count; ++i) {
|
| + Node* const input = NodeProperties::GetEffectInput(node, i);
|
| + checks->Merge(node_checks_.Get(input));
|
| + }
|
| + return UpdateChecks(node, checks);
|
| +}
|
| +
|
| +Reduction RedundancyElimination::ReduceStart(Node* node) {
|
| + return UpdateChecks(node, EffectPathChecks::Empty(zone()));
|
| +}
|
| +
|
| +Reduction RedundancyElimination::ReduceOtherNode(Node* node) {
|
| + if (node->op()->EffectInputCount() == 1) {
|
| + if (node->op()->EffectOutputCount() == 1) {
|
| + return TakeChecksFromFirstEffect(node);
|
| + } else {
|
| + // Effect terminators should be handled specially.
|
| + return NoChange();
|
| + }
|
| + }
|
| + DCHECK_EQ(0, node->op()->EffectInputCount());
|
| + DCHECK_EQ(0, node->op()->EffectOutputCount());
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction RedundancyElimination::TakeChecksFromFirstEffect(Node* node) {
|
| + DCHECK_EQ(1, node->op()->EffectOutputCount());
|
| + Node* const effect = NodeProperties::GetEffectInput(node);
|
| + EffectPathChecks const* checks = node_checks_.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 (checks == nullptr) return NoChange();
|
| + // We just propagate the information from the effect input (ideally,
|
| + // we would only revisit effect uses if there is change).
|
| + return UpdateChecks(node, checks);
|
| +}
|
| +
|
| +Reduction RedundancyElimination::UpdateChecks(Node* node,
|
| + EffectPathChecks const* checks) {
|
| + EffectPathChecks const* original = node_checks_.Get(node);
|
| + // Only signal that the {node} has Changed, if the information about {checks}
|
| + // has changed wrt. the {original}.
|
| + if (checks != original) {
|
| + node_checks_.Set(node, checks);
|
| + return Changed(node);
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|