Chromium Code Reviews| Index: src/compiler/control-reducer.cc |
| diff --git a/src/compiler/control-reducer.cc b/src/compiler/control-reducer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..47ea66c2df01b3a1d004fa7df0648b5dde011064 |
| --- /dev/null |
| +++ b/src/compiler/control-reducer.cc |
| @@ -0,0 +1,327 @@ |
| +// Copyright 2014 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/common-operator.h" |
| +#include "src/compiler/common-operator-reducer.h" |
| +#include "src/compiler/control-reducer.h" |
|
conradw
2015/06/19 21:38:53
I think this file was reintroduced by mistake with
|
| +#include "src/compiler/graph.h" |
| +#include "src/compiler/graph-reducer.h" |
| +#include "src/compiler/js-graph.h" |
| +#include "src/compiler/node-marker.h" |
| +#include "src/compiler/node-matchers.h" |
| +#include "src/compiler/node-properties.h" |
| +#include "src/zone-containers.h" |
| + |
| +namespace v8 { |
| +namespace internal { |
| +namespace compiler { |
| + |
| +#define TRACE(...) \ |
| + do { \ |
| + if (FLAG_trace_turbo_reduction) PrintF(__VA_ARGS__); \ |
| + } while (false) |
| + |
| +enum Decision { kFalse, kUnknown, kTrue }; |
| + |
| +class ControlReducerImpl final : public AdvancedReducer { |
| + public: |
| + Zone* zone_; |
| + JSGraph* jsgraph_; |
| + int max_phis_for_select_; |
| + |
| + ControlReducerImpl(Editor* editor, Zone* zone, JSGraph* jsgraph) |
| + : AdvancedReducer(editor), |
| + zone_(zone), |
| + jsgraph_(jsgraph), |
| + max_phis_for_select_(0) {} |
| + |
| + Graph* graph() { return jsgraph_->graph(); } |
| + CommonOperatorBuilder* common() { return jsgraph_->common(); } |
| + Node* dead() { return jsgraph_->DeadControl(); } |
| + |
| + //=========================================================================== |
| + // Reducer implementation: perform reductions on a node. |
| + //=========================================================================== |
| + Reduction Reduce(Node* node) override { |
| + if (node->op()->ControlInputCount() == 1 || |
| + node->opcode() == IrOpcode::kLoop) { |
| + // If a node has only one control input and it is dead, replace with dead. |
| + Node* control = NodeProperties::GetControlInput(node); |
| + if (control->opcode() == IrOpcode::kDeadControl) { |
| + TRACE("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()); |
| + return Replace(control); |
| + } |
| + } |
| + |
| + Node* result = node; |
| + // Reduce branches, phis, and merges. |
| + switch (node->opcode()) { |
| + case IrOpcode::kBranch: |
| + result = ReduceBranch(node); |
| + break; |
| + case IrOpcode::kIfTrue: |
| + result = ReduceIfProjection(node, kTrue); |
| + break; |
| + case IrOpcode::kIfFalse: |
| + result = ReduceIfProjection(node, kFalse); |
| + break; |
| + case IrOpcode::kLoop: // fallthrough |
| + case IrOpcode::kMerge: |
| + result = ReduceMerge(node); |
| + break; |
| + case IrOpcode::kEnd: |
| + result = ReduceEnd(node); |
| + break; |
| + default: |
| + break; |
| + } |
| + |
| + return result == node ? NoChange() : Replace(result); |
| + } |
| + |
| + // Try to statically fold a condition. |
| + Decision DecideCondition(Node* cond) { |
| + switch (cond->opcode()) { |
| + case IrOpcode::kInt32Constant: |
| + return Int32Matcher(cond).Is(0) ? kFalse : kTrue; |
| + case IrOpcode::kInt64Constant: |
| + return Int64Matcher(cond).Is(0) ? kFalse : kTrue; |
| + case IrOpcode::kHeapConstant: { |
| + Handle<HeapObject> object = HeapObjectMatcher(cond).Value().handle(); |
| + return object->BooleanValue() ? kTrue : kFalse; |
| + } |
| + default: |
| + break; |
| + } |
| + return kUnknown; |
| + } |
| + |
| + // Reduce branches. |
| + Node* ReduceBranch(Node* branch) { |
| + if (DecideCondition(branch->InputAt(0)) != kUnknown) { |
| + for (Node* use : branch->uses()) Revisit(use); |
| + } |
| + return branch; |
| + } |
| + |
| + // Reduce end by trimming away dead inputs. |
| + Node* ReduceEnd(Node* node) { |
| + // Count the number of live inputs. |
| + int live = 0; |
| + for (int index = 0; index < node->InputCount(); ++index) { |
| + // Skip dead inputs. |
| + if (node->InputAt(index)->opcode() == IrOpcode::kDeadControl) continue; |
| + // Compact live inputs. |
| + if (index != live) node->ReplaceInput(live, node->InputAt(index)); |
| + ++live; |
| + } |
| + |
| + TRACE("ReduceEnd: #%d:%s (%d of %d live)\n", node->id(), |
| + node->op()->mnemonic(), live, node->InputCount()); |
| + |
| + if (live == 0) return dead(); // No remaining inputs. |
| + |
| + if (live < node->InputCount()) { |
| + node->set_op(common()->End(live)); |
| + node->TrimInputCount(live); |
| + } |
| + |
| + return node; |
| + } |
| + |
| + // Reduce merges by trimming away dead inputs from the merge and phis. |
| + Node* ReduceMerge(Node* node) { |
| + // Count the number of live inputs. |
| + int live = 0; |
| + int index = 0; |
| + int live_index = 0; |
| + for (Node* const input : node->inputs()) { |
| + if (input->opcode() != IrOpcode::kDeadControl) { |
| + live++; |
| + live_index = index; |
| + } |
| + index++; |
| + } |
| + |
| + TRACE("ReduceMerge: #%d:%s (%d of %d live)\n", node->id(), |
| + node->op()->mnemonic(), live, index); |
| + |
| + if (live == 0) return dead(); // no remaining inputs. |
| + |
| + // Gather terminates, phis and effect phis to be edited. |
| + NodeVector phis(zone_); |
| + Node* terminate = nullptr; |
| + for (Node* const use : node->uses()) { |
| + if (NodeProperties::IsPhi(use)) { |
| + phis.push_back(use); |
| + } else if (use->opcode() == IrOpcode::kTerminate) { |
| + DCHECK_NULL(terminate); |
| + terminate = use; |
| + } |
| + } |
| + |
| + if (live == 1) { |
| + // All phis are redundant. Replace them with their live input. |
| + for (Node* const phi : phis) { |
| + Replace(phi, phi->InputAt(live_index)); |
| + } |
| + // The terminate is not needed anymore. |
| + if (terminate) Replace(terminate, dead()); |
| + // The merge itself is redundant. |
| + return node->InputAt(live_index); |
| + } |
| + |
| + DCHECK_LE(2, live); |
| + |
| + if (live < node->InputCount()) { |
| + // Edit phis in place, removing dead inputs and revisiting them. |
| + for (Node* const phi : phis) { |
| + TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), |
| + phi->op()->mnemonic(), live); |
| + RemoveDeadInputs(node, phi); |
| + Revisit(phi); |
| + } |
| + // Edit the merge in place, removing dead inputs. |
| + RemoveDeadInputs(node, node); |
| + } |
| + |
| + DCHECK_EQ(live, node->InputCount()); |
| + |
| + // Try to remove dead diamonds or introduce selects. |
| + if (live == 2 && CheckPhisForSelect(phis)) { |
| + DiamondMatcher matcher(node); |
| + if (matcher.Matched() && matcher.IfProjectionsAreOwned()) { |
| + // Dead diamond, i.e. neither the IfTrue nor the IfFalse nodes |
| + // have uses except for the Merge. Remove the branch if there |
| + // are no phis or replace phis with selects. |
| + Node* control = NodeProperties::GetControlInput(matcher.Branch()); |
| + if (phis.size() == 0) { |
| + // No phis. Remove the branch altogether. |
| + TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", |
| + matcher.Branch()->id(), matcher.IfTrue()->id(), |
| + matcher.IfFalse()->id()); |
| + return control; |
| + } else { |
| + // A small number of phis. Replace with selects. |
| + Node* cond = matcher.Branch()->InputAt(0); |
| + for (Node* phi : phis) { |
| + Node* select = graph()->NewNode( |
| + common()->Select(OpParameter<MachineType>(phi), |
| + BranchHintOf(matcher.Branch()->op())), |
| + cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); |
| + TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", |
| + matcher.Branch()->id(), matcher.IfTrue()->id(), |
| + matcher.IfFalse()->id(), select->id()); |
| + Replace(phi, select); |
| + } |
| + return control; |
| + } |
| + } |
| + } |
| + |
| + return node; |
| + } |
| + |
| + bool CheckPhisForSelect(const NodeVector& phis) { |
| + if (phis.size() > static_cast<size_t>(max_phis_for_select_)) return false; |
| + for (Node* phi : phis) { |
| + if (phi->opcode() != IrOpcode::kPhi) return false; // no EffectPhis. |
| + } |
| + return true; |
| + } |
| + |
| + // Reduce if projections if the branch has a constant input. |
| + Node* ReduceIfProjection(Node* node, Decision decision) { |
| + Node* branch = node->InputAt(0); |
| + DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); |
| + Decision result = DecideCondition(branch->InputAt(0)); |
| + if (result == decision) { |
| + // Fold a branch by replacing IfTrue/IfFalse with the branch control. |
| + TRACE(" BranchReduce: #%d:%s => #%d:%s\n", branch->id(), |
| + branch->op()->mnemonic(), node->id(), node->op()->mnemonic()); |
| + return branch->InputAt(1); |
| + } |
| + return result == kUnknown ? node : dead(); |
| + } |
| + |
| + // Remove inputs to {node} corresponding to the dead inputs to {merge} |
| + // and compact the remaining inputs, updating the operator. |
| + void RemoveDeadInputs(Node* merge, Node* node) { |
| + int live = 0; |
| + for (int i = 0; i < merge->InputCount(); i++) { |
| + // skip dead inputs. |
| + if (merge->InputAt(i)->opcode() == IrOpcode::kDeadControl) continue; |
| + // compact live inputs. |
| + if (live != i) node->ReplaceInput(live, node->InputAt(i)); |
| + live++; |
| + } |
| + // compact remaining inputs. |
| + int total = live; |
| + for (int i = merge->InputCount(); i < node->InputCount(); i++) { |
| + if (total != i) node->ReplaceInput(total, node->InputAt(i)); |
| + total++; |
| + } |
| + DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); |
| + DCHECK_NE(total, node->InputCount()); |
| + node->TrimInputCount(total); |
| + node->set_op(common()->ResizeMergeOrPhi(node->op(), live)); |
| + } |
| +}; |
| + |
| + |
| +void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, |
| + int max_phis_for_select) { |
| + GraphReducer graph_reducer(zone, jsgraph->graph()); |
| + CommonOperatorReducer common(&graph_reducer, jsgraph->graph(), |
| + jsgraph->common(), jsgraph->machine()); |
| + ControlReducerImpl impl(&graph_reducer, zone, jsgraph); |
| + impl.max_phis_for_select_ = max_phis_for_select; |
| + graph_reducer.AddReducer(&impl); |
| + graph_reducer.AddReducer(&common); |
| + graph_reducer.ReduceGraph(); |
| +} |
| + |
| + |
| +namespace { |
| + |
| +class DummyEditor final : public AdvancedReducer::Editor { |
| + public: |
| + void Replace(Node* node, Node* replacement) final { |
| + node->ReplaceUses(replacement); |
| + } |
| + void Revisit(Node* node) final {} |
| + void ReplaceWithValue(Node* node, Node* value, Node* effect, |
| + Node* control) final {} |
| +}; |
| + |
| +} // namespace |
| + |
| + |
| +Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node, |
| + int max_phis_for_select) { |
| + Zone zone; |
| + DummyEditor editor; |
| + ControlReducerImpl impl(&editor, &zone, jsgraph); |
| + impl.max_phis_for_select_ = max_phis_for_select; |
| + return impl.ReduceMerge(node); |
| +} |
| + |
| + |
| +Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, Node* node) { |
| + Zone zone; |
| + DummyEditor editor; |
| + ControlReducerImpl impl(&editor, &zone, jsgraph); |
| + switch (node->opcode()) { |
| + case IrOpcode::kIfTrue: |
| + return impl.ReduceIfProjection(node, kTrue); |
| + case IrOpcode::kIfFalse: |
| + return impl.ReduceIfProjection(node, kFalse); |
| + default: |
| + return node; |
| + } |
| +} |
| + |
| +} // namespace compiler |
| +} // namespace internal |
| +} // namespace v8 |