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

Unified Diff: src/compiler/osr.cc

Issue 898353002: [turbofan] Use heavy-handed graph duplication to do loop peeling for OSR. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 10 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/loop-analysis.cc ('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/osr.cc
diff --git a/src/compiler/osr.cc b/src/compiler/osr.cc
index e3da5c52973c7264f815493a7fcb0c1a3b6df09e..2eccf302f50212f49d44eebd8d27c32193a4aa25 100644
--- a/src/compiler/osr.cc
+++ b/src/compiler/osr.cc
@@ -3,10 +3,12 @@
// found in the LICENSE file.
#include "src/compiler.h"
+#include "src/compiler/all-nodes.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/control-reducer.h"
#include "src/compiler/frame.h"
#include "src/compiler/graph.h"
+#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/loop-analysis.h"
#include "src/compiler/node.h"
@@ -24,6 +26,154 @@ OsrHelper::OsrHelper(CompilationInfo* info)
info->osr_expr_stack_height()) {}
+// Peel outer loops and rewire the graph so that control reduction can
+// produce a properly formed graph.
+static void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
+ Zone* tmp_zone, Node* dead,
+ LoopTree* loop_tree, LoopTree::Loop* osr_loop,
+ Node* osr_normal_entry, Node* osr_loop_entry) {
+ const int original_count = graph->NodeCount();
+ AllNodes all(tmp_zone, graph);
+ NodeVector tmp_inputs(tmp_zone);
+ Node* sentinel = graph->NewNode(dead->op());
+
+ // Make a copy of the graph for each outer loop.
+ ZoneVector<NodeVector*> copies(tmp_zone);
+ for (LoopTree::Loop* loop = osr_loop->parent(); loop; loop = loop->parent()) {
+ void* stuff = tmp_zone->New(sizeof(NodeVector));
+ NodeVector* mapping =
+ new (stuff) NodeVector(original_count, sentinel, tmp_zone);
+ copies.push_back(mapping);
+
+ // Prepare the mapping for OSR values and the OSR loop entry.
+ mapping->at(osr_normal_entry->id()) = dead;
+ mapping->at(osr_loop_entry->id()) = dead;
+ // Don't duplicate the OSR values.
+ for (Node* use : osr_loop_entry->uses()) {
+ if (use->opcode() == IrOpcode::kOsrValue) mapping->at(use->id()) = use;
+ }
+
+ // The outer loops are dead in this copy.
+ for (LoopTree::Loop* outer = loop->parent(); outer;
+ outer = outer->parent()) {
+ for (Node* node : loop_tree->HeaderNodes(outer)) {
+ mapping->at(node->id()) = dead;
+ }
+ }
+
+ // Copy all nodes.
+ for (size_t i = 0; i < all.live.size(); i++) {
+ Node* orig = all.live[i];
+ Node* copy = mapping->at(orig->id());
+ if (copy != sentinel) {
+ // Mapping already exists.
+ continue;
+ }
+ if (orig->InputCount() == 0) {
+ // No need to copy leaf nodes.
+ mapping->at(orig->id()) = orig;
+ continue;
+ }
+
+ // Copy the node.
+ tmp_inputs.clear();
+ for (Node* input : orig->inputs()) {
+ tmp_inputs.push_back(mapping->at(input->id()));
+ }
+ copy = graph->NewNode(orig->op(), orig->InputCount(), &tmp_inputs[0]);
+ if (NodeProperties::IsTyped(orig)) {
+ NodeProperties::SetBounds(copy, NodeProperties::GetBounds(orig));
+ }
+ mapping->at(orig->id()) = copy;
+ }
+
+ // Fix missing inputs.
+ for (size_t i = 0; i < all.live.size(); i++) {
+ Node* orig = all.live[i];
+ Node* copy = mapping->at(orig->id());
+ for (int j = 0; j < copy->InputCount(); j++) {
+ Node* input = copy->InputAt(j);
+ if (input == sentinel)
+ copy->ReplaceInput(j, mapping->at(orig->InputAt(j)->id()));
+ }
+ }
+
+ // Construct the transfer from the previous graph copies to the new copy.
+ Node* loop_header = loop_tree->HeaderNode(loop);
+ NodeVector* previous =
+ copies.size() > 1 ? copies[copies.size() - 2] : nullptr;
+ const int backedges = loop_header->op()->ControlInputCount() - 1;
+ if (backedges == 1) {
+ // Simple case. Map the incoming edges to the loop to the previous copy.
+ for (Node* node : loop_tree->HeaderNodes(loop)) {
+ Node* copy = mapping->at(node->id());
+ Node* backedge = node->InputAt(1);
+ if (previous) backedge = previous->at(backedge->id());
+ copy->ReplaceInput(0, backedge);
+ }
+ } else {
+ // Complex case. Multiple backedges. Introduce a merge for incoming edges.
+ tmp_inputs.clear();
+ for (int i = 0; i < backedges; i++) {
+ Node* backedge = loop_header->InputAt(i + 1);
+ if (previous) backedge = previous->at(backedge->id());
+ tmp_inputs.push_back(backedge);
+ }
+ Node* merge =
+ graph->NewNode(common->Merge(backedges), backedges, &tmp_inputs[0]);
+ for (Node* node : loop_tree->HeaderNodes(loop)) {
+ Node* copy = mapping->at(node->id());
+ if (node == loop_header) {
+ // The entry to the loop is the merge.
+ copy->ReplaceInput(0, merge);
+ } else {
+ // Merge inputs to the phi at the loop entry.
+ tmp_inputs.clear();
+ for (int i = 0; i < backedges; i++) {
+ Node* backedge = node->InputAt(i + 1);
+ if (previous) backedge = previous->at(backedge->id());
+ tmp_inputs.push_back(backedge);
+ }
+ tmp_inputs.push_back(merge);
+ Node* phi =
+ graph->NewNode(common->ResizeMergeOrPhi(node->op(), backedges),
+ backedges + 1, &tmp_inputs[0]);
+ copy->ReplaceInput(0, phi);
+ }
+ }
+ }
+ }
+
+ // Kill the outer loops in the original graph.
+ for (LoopTree::Loop* outer = osr_loop->parent(); outer;
+ outer = outer->parent()) {
+ loop_tree->HeaderNode(outer)->ReplaceUses(dead);
+ }
+
+ // Merge the ends of the graph copies.
+ Node* end = graph->end();
+ tmp_inputs.clear();
+ for (int i = -1; i < static_cast<int>(copies.size()); i++) {
+ Node* input = end->InputAt(0);
+ if (i >= 0) input = copies[i]->at(input->id());
+ if (input->opcode() == IrOpcode::kMerge) {
+ for (Node* node : input->inputs()) tmp_inputs.push_back(node);
+ } else {
+ tmp_inputs.push_back(input);
+ }
+ }
+ int count = static_cast<int>(tmp_inputs.size());
+ Node* merge = graph->NewNode(common->Merge(count), count, &tmp_inputs[0]);
+ end->ReplaceInput(0, merge);
+
+ if (FLAG_trace_turbo_graph) { // Simple textual RPO.
+ OFStream os(stdout);
+ os << "-- Graph after OSR duplication -- " << std::endl;
+ os << AsRPO(*graph);
+ }
+}
+
+
bool OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
Zone* tmp_zone) {
Graph* graph = jsgraph->graph();
@@ -57,14 +207,16 @@ bool OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
// Analyze the graph to determine how deeply nested the OSR loop is.
LoopTree* loop_tree = LoopFinder::BuildLoopTree(graph, tmp_zone);
+ Node* dead = graph->NewNode(common->Dead());
LoopTree::Loop* loop = loop_tree->ContainingLoop(osr_loop);
- if (loop->depth() > 0) return false; // cannot OSR inner loops yet.
-
- // TODO(titzer): perform loop peeling or graph duplication.
+ if (loop->depth() > 0) {
+ PeelOuterLoopsForOsr(graph, common, tmp_zone, dead, loop_tree, loop,
+ osr_normal_entry, osr_loop_entry);
+ }
// Replace the normal entry with {Dead} and the loop entry with {Start}
// and run the control reducer to clean up the graph.
- osr_normal_entry->ReplaceUses(graph->NewNode(common->Dead()));
+ osr_normal_entry->ReplaceUses(dead);
osr_loop_entry->ReplaceUses(graph->start());
ControlReducer::ReduceGraph(tmp_zone, jsgraph, common);
« no previous file with comments | « src/compiler/loop-analysis.cc ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698