| Index: src/compiler/loop-peeling.cc
|
| diff --git a/src/compiler/loop-peeling.cc b/src/compiler/loop-peeling.cc
|
| index a09f5060e0e24f2be5b2ba903b7a750855ece5ed..d48e9fbc476cd901bed5defa1c574f560f9ca84d 100644
|
| --- a/src/compiler/loop-peeling.cc
|
| +++ b/src/compiler/loop-peeling.cc
|
| @@ -126,8 +126,14 @@ struct Peeling {
|
| // Copy all the nodes first.
|
| for (Node* node : nodes) {
|
| inputs.clear();
|
| - for (Node* input : node->inputs()) inputs.push_back(map(input));
|
| - Insert(node, graph->NewNode(node->op(), node->InputCount(), &inputs[0]));
|
| + for (Node* input : node->inputs()) {
|
| + inputs.push_back(map(input));
|
| + }
|
| + Node* copy = graph->NewNode(node->op(), node->InputCount(), &inputs[0]);
|
| + if (NodeProperties::IsTyped(node)) {
|
| + NodeProperties::SetType(copy, NodeProperties::GetType(node));
|
| + }
|
| + Insert(node, copy);
|
| }
|
|
|
| // Fix remaining inputs of the copies.
|
| @@ -160,56 +166,54 @@ Node* PeeledIteration::map(Node* node) {
|
| return node;
|
| }
|
|
|
| -
|
| -static void FindLoopExits(LoopTree* loop_tree, LoopTree::Loop* loop,
|
| - NodeVector& exits, NodeVector& rets) {
|
| +bool LoopPeeler::CanPeel(LoopTree* loop_tree, LoopTree::Loop* loop) {
|
| // Look for returns and if projections that are outside the loop but whose
|
| // control input is inside the loop.
|
| + Node* loop_node = loop_tree->GetLoopControl(loop);
|
| for (Node* node : loop_tree->LoopNodes(loop)) {
|
| for (Node* use : node->uses()) {
|
| if (!loop_tree->Contains(loop, use)) {
|
| - if (IrOpcode::IsIfProjectionOpcode(use->opcode())) {
|
| - // This is a branch from inside the loop to outside the loop.
|
| - exits.push_back(use);
|
| - } else if (use->opcode() == IrOpcode::kReturn &&
|
| - loop_tree->Contains(loop,
|
| - NodeProperties::GetControlInput(use))) {
|
| - // This is a return from inside the loop.
|
| - rets.push_back(use);
|
| + bool unmarked_exit;
|
| + switch (node->opcode()) {
|
| + case IrOpcode::kLoopExit:
|
| + unmarked_exit = (node->InputAt(1) != loop_node);
|
| + break;
|
| + case IrOpcode::kLoopExitValue:
|
| + case IrOpcode::kLoopExitEffect:
|
| + unmarked_exit = (node->InputAt(1)->InputAt(1) != loop_node);
|
| + break;
|
| + default:
|
| + unmarked_exit = (use->opcode() != IrOpcode::kTerminate);
|
| + }
|
| + if (unmarked_exit) {
|
| + if (FLAG_trace_turbo_graph) {
|
| + Node* loop_node = loop_tree->GetLoopControl(loop);
|
| + PrintF(
|
| + "Cannot peel loop %i. Loop exit without explicit mark: Node %i "
|
| + "(%s) is inside "
|
| + "loop, but its use %i (%s) is outside.\n",
|
| + loop_node->id(), node->id(), node->op()->mnemonic(), use->id(),
|
| + use->op()->mnemonic());
|
| + }
|
| + return false;
|
| }
|
| }
|
| }
|
| }
|
| -}
|
| -
|
| -
|
| -bool LoopPeeler::CanPeel(LoopTree* loop_tree, LoopTree::Loop* loop) {
|
| - Zone zone(loop_tree->zone()->allocator());
|
| - NodeVector exits(&zone);
|
| - NodeVector rets(&zone);
|
| - FindLoopExits(loop_tree, loop, exits, rets);
|
| - return exits.size() <= 1u;
|
| + return true;
|
| }
|
|
|
|
|
| PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
|
| LoopTree* loop_tree, LoopTree::Loop* loop,
|
| Zone* tmp_zone) {
|
| - //============================================================================
|
| - // Find the loop exit region to determine if this loop can be peeled.
|
| - //============================================================================
|
| - NodeVector exits(tmp_zone);
|
| - NodeVector rets(tmp_zone);
|
| - FindLoopExits(loop_tree, loop, exits, rets);
|
| -
|
| - if (exits.size() != 1) return nullptr; // not peelable currently.
|
| + if (!CanPeel(loop_tree, loop)) return nullptr;
|
|
|
| //============================================================================
|
| // Construct the peeled iteration.
|
| //============================================================================
|
| PeeledIterationImpl* iter = new (tmp_zone) PeeledIterationImpl(tmp_zone);
|
| - size_t estimated_peeled_size =
|
| - 5 + (loop->TotalSize() + exits.size() + rets.size()) * 2;
|
| + size_t estimated_peeled_size = 5 + (loop->TotalSize()) * 2;
|
| Peeling peeling(graph, tmp_zone, estimated_peeled_size, &iter->node_pairs_);
|
|
|
| Node* dead = graph->NewNode(common->Dead());
|
| @@ -260,77 +264,64 @@ PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
|
| // Only one backedge, simply replace the input to loop with output of
|
| // peeling.
|
| for (Node* node : loop_tree->HeaderNodes(loop)) {
|
| - node->ReplaceInput(0, peeling.map(node->InputAt(0)));
|
| + node->ReplaceInput(0, peeling.map(node->InputAt(1)));
|
| }
|
| new_entry = peeling.map(loop_node->InputAt(1));
|
| }
|
| loop_node->ReplaceInput(0, new_entry);
|
|
|
| //============================================================================
|
| - // Duplicate the loop exit region and add a merge.
|
| + // Change the exit and exit markers to merge/phi/effect-phi.
|
| //============================================================================
|
| -
|
| - // Currently we are limited to peeling loops with a single exit. The exit is
|
| - // the postdominator of the loop (ignoring returns).
|
| - Node* postdom = exits[0];
|
| - for (Node* node : rets) exits.push_back(node);
|
| - for (Node* use : postdom->uses()) {
|
| - if (NodeProperties::IsPhi(use)) exits.push_back(use);
|
| + for (Node* exit : loop_tree->ExitNodes(loop)) {
|
| + switch (exit->opcode()) {
|
| + case IrOpcode::kLoopExit:
|
| + // Change the loop exit node to a merge node.
|
| + exit->ReplaceInput(1, peeling.map(exit->InputAt(0)));
|
| + NodeProperties::ChangeOp(exit, common->Merge(2));
|
| + break;
|
| + case IrOpcode::kLoopExitValue:
|
| + // Change exit marker to phi.
|
| + exit->InsertInput(graph->zone(), 1, peeling.map(exit->InputAt(0)));
|
| + NodeProperties::ChangeOp(
|
| + exit, common->Phi(MachineRepresentation::kTagged, 2));
|
| + break;
|
| + case IrOpcode::kLoopExitEffect:
|
| + // Change effect exit marker to effect phi.
|
| + exit->InsertInput(graph->zone(), 1, peeling.map(exit->InputAt(0)));
|
| + NodeProperties::ChangeOp(exit, common->EffectPhi(2));
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| }
|
| + return iter;
|
| +}
|
|
|
| - NodeRange exit_range(&exits[0], &exits[0] + exits.size());
|
| - peeling.CopyNodes(graph, tmp_zone, dead, exit_range);
|
| -
|
| - Node* merge = graph->NewNode(common->Merge(2), postdom, peeling.map(postdom));
|
| - postdom->ReplaceUses(merge);
|
| - merge->ReplaceInput(0, postdom); // input 0 overwritten by above line.
|
| -
|
| - // Find and update all the edges into either the loop or exit region.
|
| - for (int i = 0; i < 2; i++) {
|
| - NodeRange range = i == 0 ? loop_tree->LoopNodes(loop) : exit_range;
|
| - ZoneVector<Edge> value_edges(tmp_zone);
|
| - ZoneVector<Edge> effect_edges(tmp_zone);
|
| -
|
| - for (Node* node : range) {
|
| - // Gather value and effect edges from outside the region.
|
| - for (Edge edge : node->use_edges()) {
|
| - if (!peeling.Marked(edge.from())) {
|
| - // Edge from outside the loop into the region.
|
| - if (NodeProperties::IsValueEdge(edge) ||
|
| - NodeProperties::IsContextEdge(edge)) {
|
| - value_edges.push_back(edge);
|
| - } else if (NodeProperties::IsEffectEdge(edge)) {
|
| - effect_edges.push_back(edge);
|
| - } else {
|
| - // don't do anything for control edges.
|
| - // TODO(titzer): should update control edges to peeled?
|
| - }
|
| - }
|
| - }
|
| +namespace {
|
|
|
| - // Update all the value and effect edges at once.
|
| - if (!value_edges.empty()) {
|
| - // TODO(titzer): machine type is wrong here.
|
| - Node* phi =
|
| - graph->NewNode(common->Phi(MachineRepresentation::kTagged, 2), node,
|
| - peeling.map(node), merge);
|
| - for (Edge edge : value_edges) edge.UpdateTo(phi);
|
| - value_edges.clear();
|
| - }
|
| - if (!effect_edges.empty()) {
|
| - Node* effect_phi = graph->NewNode(common->EffectPhi(2), node,
|
| - peeling.map(node), merge);
|
| - for (Edge edge : effect_edges) edge.UpdateTo(effect_phi);
|
| - effect_edges.clear();
|
| - }
|
| +void PeelInnerLoops(Graph* graph, CommonOperatorBuilder* common,
|
| + LoopTree* loop_tree, LoopTree::Loop* loop,
|
| + Zone* temp_zone) {
|
| + // If the loop has nested loops, peel inside those.
|
| + if (!loop->children().empty()) {
|
| + for (LoopTree::Loop* inner_loop : loop->children()) {
|
| + PeelInnerLoops(graph, common, loop_tree, inner_loop, temp_zone);
|
| + }
|
| + return;
|
| + }
|
| + // Only peel small-enough loops.
|
| + if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
|
| + if (FLAG_trace_turbo_graph) {
|
| + PrintF("Peeling loop with header: ");
|
| + for (Node* node : loop_tree->HeaderNodes(loop)) {
|
| + PrintF("%i ", node->id());
|
| }
|
| }
|
|
|
| - return iter;
|
| + LoopPeeler::Peel(graph, common, loop_tree, loop, temp_zone);
|
| }
|
|
|
| -namespace {
|
| -
|
| void EliminateLoopExit(Node* node) {
|
| DCHECK_EQ(IrOpcode::kLoopExit, node->opcode());
|
| // The exit markers take the loop exit as input. We iterate over uses
|
| @@ -356,6 +347,17 @@ void EliminateLoopExit(Node* node) {
|
| } // namespace
|
|
|
| // static
|
| +void LoopPeeler::PeelInnerLoopsOfTree(Graph* graph,
|
| + CommonOperatorBuilder* common,
|
| + LoopTree* loop_tree, Zone* temp_zone) {
|
| + for (LoopTree::Loop* loop : loop_tree->outer_loops()) {
|
| + PeelInnerLoops(graph, common, loop_tree, loop, temp_zone);
|
| + }
|
| +
|
| + EliminateLoopExits(graph, temp_zone);
|
| +}
|
| +
|
| +// static
|
| void LoopPeeler::EliminateLoopExits(Graph* graph, Zone* temp_zone) {
|
| ZoneQueue<Node*> queue(temp_zone);
|
| ZoneVector<bool> visited(graph->NodeCount(), false, temp_zone);
|
|
|