Home | History | Annotate | Download | only in compiler
      1 // Copyright 2014 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/osr.h"
      6 #include "src/ast/scopes.h"
      7 #include "src/compilation-info.h"
      8 #include "src/compiler/all-nodes.h"
      9 #include "src/compiler/common-operator-reducer.h"
     10 #include "src/compiler/common-operator.h"
     11 #include "src/compiler/dead-code-elimination.h"
     12 #include "src/compiler/frame.h"
     13 #include "src/compiler/graph-reducer.h"
     14 #include "src/compiler/graph-trimmer.h"
     15 #include "src/compiler/graph-visualizer.h"
     16 #include "src/compiler/graph.h"
     17 #include "src/compiler/js-graph.h"
     18 #include "src/compiler/loop-analysis.h"
     19 #include "src/compiler/node-marker.h"
     20 #include "src/compiler/node.h"
     21 #include "src/objects-inl.h"
     22 
     23 namespace v8 {
     24 namespace internal {
     25 namespace compiler {
     26 
     27 OsrHelper::OsrHelper(CompilationInfo* info)
     28     : parameter_count_(
     29           info->is_optimizing_from_bytecode()
     30               ? info->shared_info()->bytecode_array()->parameter_count()
     31               : info->scope()->num_parameters()),
     32       stack_slot_count_(
     33           info->is_optimizing_from_bytecode()
     34               ? info->shared_info()->bytecode_array()->register_count() +
     35                     InterpreterFrameConstants::kExtraSlotCount
     36               : info->scope()->num_stack_slots() +
     37                     info->osr_expr_stack_height()) {}
     38 
     39 #ifdef DEBUG
     40 #define TRACE_COND (FLAG_trace_turbo_graph && FLAG_trace_osr)
     41 #else
     42 #define TRACE_COND false
     43 #endif
     44 
     45 #define TRACE(...)                       \
     46   do {                                   \
     47     if (TRACE_COND) PrintF(__VA_ARGS__); \
     48   } while (false)
     49 
     50 namespace {
     51 
     52 // Peel outer loops and rewire the graph so that control reduction can
     53 // produce a properly formed graph.
     54 void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
     55                           Zone* tmp_zone, Node* dead, LoopTree* loop_tree,
     56                           LoopTree::Loop* osr_loop, Node* osr_normal_entry,
     57                           Node* osr_loop_entry) {
     58   const size_t original_count = graph->NodeCount();
     59   AllNodes all(tmp_zone, graph);
     60   NodeVector tmp_inputs(tmp_zone);
     61   Node* sentinel = graph->NewNode(dead->op());
     62 
     63   // Make a copy of the graph for each outer loop.
     64   ZoneVector<NodeVector*> copies(tmp_zone);
     65   for (LoopTree::Loop* loop = osr_loop->parent(); loop; loop = loop->parent()) {
     66     void* stuff = tmp_zone->New(sizeof(NodeVector));
     67     NodeVector* mapping =
     68         new (stuff) NodeVector(original_count, sentinel, tmp_zone);
     69     copies.push_back(mapping);
     70     TRACE("OsrDuplication #%zu, depth %zu, header #%d:%s\n", copies.size(),
     71           loop->depth(), loop_tree->HeaderNode(loop)->id(),
     72           loop_tree->HeaderNode(loop)->op()->mnemonic());
     73 
     74     // Prepare the mapping for OSR values and the OSR loop entry.
     75     mapping->at(osr_normal_entry->id()) = dead;
     76     mapping->at(osr_loop_entry->id()) = dead;
     77 
     78     // The outer loops are dead in this copy.
     79     for (LoopTree::Loop* outer = loop->parent(); outer;
     80          outer = outer->parent()) {
     81       for (Node* node : loop_tree->HeaderNodes(outer)) {
     82         mapping->at(node->id()) = dead;
     83         TRACE(" ---- #%d:%s -> dead (header)\n", node->id(),
     84               node->op()->mnemonic());
     85       }
     86     }
     87 
     88     // Copy all nodes.
     89     for (size_t i = 0; i < all.reachable.size(); i++) {
     90       Node* orig = all.reachable[i];
     91       Node* copy = mapping->at(orig->id());
     92       if (copy != sentinel) {
     93         // Mapping already exists.
     94         continue;
     95       }
     96       if (orig->InputCount() == 0 || orig->opcode() == IrOpcode::kParameter ||
     97           orig->opcode() == IrOpcode::kOsrValue ||
     98           orig->opcode() == IrOpcode::kOsrGuard) {
     99         // No need to copy leaf nodes or parameters.
    100         mapping->at(orig->id()) = orig;
    101         continue;
    102       }
    103 
    104       // Copy the node.
    105       tmp_inputs.clear();
    106       for (Node* input : orig->inputs()) {
    107         tmp_inputs.push_back(mapping->at(input->id()));
    108       }
    109       copy = graph->NewNode(orig->op(), orig->InputCount(), &tmp_inputs[0]);
    110       if (NodeProperties::IsTyped(orig)) {
    111         NodeProperties::SetType(copy, NodeProperties::GetType(orig));
    112       }
    113       mapping->at(orig->id()) = copy;
    114       TRACE(" copy #%d:%s -> #%d\n", orig->id(), orig->op()->mnemonic(),
    115             copy->id());
    116     }
    117 
    118     // Fix missing inputs.
    119     for (Node* orig : all.reachable) {
    120       Node* copy = mapping->at(orig->id());
    121       for (int j = 0; j < copy->InputCount(); j++) {
    122         if (copy->InputAt(j) == sentinel) {
    123           copy->ReplaceInput(j, mapping->at(orig->InputAt(j)->id()));
    124         }
    125       }
    126     }
    127 
    128     // Construct the entry into this loop from previous copies.
    129 
    130     // Gather the live loop header nodes, {loop_header} first.
    131     Node* loop_header = loop_tree->HeaderNode(loop);
    132     NodeVector header_nodes(tmp_zone);
    133     header_nodes.reserve(loop->HeaderSize());
    134     header_nodes.push_back(loop_header);  // put the loop header first.
    135     for (Node* node : loop_tree->HeaderNodes(loop)) {
    136       if (node != loop_header && all.IsLive(node)) {
    137         header_nodes.push_back(node);
    138       }
    139     }
    140 
    141     // Gather backedges from the previous copies of the inner loops of {loop}.
    142     NodeVectorVector backedges(tmp_zone);
    143     TRACE("Gathering backedges...\n");
    144     for (int i = 1; i < loop_header->InputCount(); i++) {
    145       if (TRACE_COND) {
    146         Node* control = loop_header->InputAt(i);
    147         size_t incoming_depth = 0;
    148         for (int j = 0; j < control->op()->ControlInputCount(); j++) {
    149           Node* k = NodeProperties::GetControlInput(control, j);
    150           incoming_depth =
    151               std::max(incoming_depth, loop_tree->ContainingLoop(k)->depth());
    152         }
    153 
    154         TRACE(" edge @%d #%d:%s, incoming depth %zu\n", i, control->id(),
    155               control->op()->mnemonic(), incoming_depth);
    156       }
    157 
    158       for (int pos = static_cast<int>(copies.size()) - 1; pos >= 0; pos--) {
    159         backedges.push_back(NodeVector(tmp_zone));
    160         backedges.back().reserve(header_nodes.size());
    161 
    162         NodeVector* previous_map = pos > 0 ? copies[pos - 1] : nullptr;
    163 
    164         for (Node* node : header_nodes) {
    165           Node* input = node->InputAt(i);
    166           if (previous_map) input = previous_map->at(input->id());
    167           backedges.back().push_back(input);
    168           TRACE("   node #%d:%s(@%d) = #%d:%s\n", node->id(),
    169                 node->op()->mnemonic(), i, input->id(),
    170                 input->op()->mnemonic());
    171         }
    172       }
    173     }
    174 
    175     int backedge_count = static_cast<int>(backedges.size());
    176     if (backedge_count == 1) {
    177       // Simple case of single backedge, therefore a single entry.
    178       int index = 0;
    179       for (Node* node : header_nodes) {
    180         Node* copy = mapping->at(node->id());
    181         Node* input = backedges[0][index];
    182         copy->ReplaceInput(0, input);
    183         TRACE(" header #%d:%s(0) => #%d:%s\n", copy->id(),
    184               copy->op()->mnemonic(), input->id(), input->op()->mnemonic());
    185         index++;
    186       }
    187     } else {
    188       // Complex case of multiple backedges from previous copies requires
    189       // merging the backedges to create the entry into the loop header.
    190       Node* merge = nullptr;
    191       int index = 0;
    192       for (Node* node : header_nodes) {
    193         // Gather edge inputs into {tmp_inputs}.
    194         tmp_inputs.clear();
    195         for (int edge = 0; edge < backedge_count; edge++) {
    196           tmp_inputs.push_back(backedges[edge][index]);
    197         }
    198         Node* copy = mapping->at(node->id());
    199         Node* input;
    200         if (node == loop_header) {
    201           // Create the merge for the entry into the loop header.
    202           input = merge = graph->NewNode(common->Merge(backedge_count),
    203                                          backedge_count, &tmp_inputs[0]);
    204           copy->ReplaceInput(0, merge);
    205         } else {
    206           // Create a phi that merges values at entry into the loop header.
    207           DCHECK_NOT_NULL(merge);
    208           DCHECK(IrOpcode::IsPhiOpcode(node->opcode()));
    209           tmp_inputs.push_back(merge);
    210           Node* phi = input = graph->NewNode(
    211               common->ResizeMergeOrPhi(node->op(), backedge_count),
    212               backedge_count + 1, &tmp_inputs[0]);
    213           copy->ReplaceInput(0, phi);
    214         }
    215 
    216         // Print the merge.
    217         if (TRACE_COND) {
    218           TRACE(" header #%d:%s(0) => #%d:%s(", copy->id(),
    219                 copy->op()->mnemonic(), input->id(), input->op()->mnemonic());
    220           for (size_t i = 0; i < tmp_inputs.size(); i++) {
    221             if (i > 0) TRACE(", ");
    222             Node* input = tmp_inputs[i];
    223             TRACE("#%d:%s", input->id(), input->op()->mnemonic());
    224           }
    225           TRACE(")\n");
    226         }
    227 
    228         index++;
    229       }
    230     }
    231   }
    232 
    233   // Kill the outer loops in the original graph.
    234   TRACE("Killing outer loop headers...\n");
    235   for (LoopTree::Loop* outer = osr_loop->parent(); outer;
    236        outer = outer->parent()) {
    237     Node* loop_header = loop_tree->HeaderNode(outer);
    238     loop_header->ReplaceUses(dead);
    239     TRACE(" ---- #%d:%s\n", loop_header->id(), loop_header->op()->mnemonic());
    240   }
    241 
    242   // Merge the ends of the graph copies.
    243   Node* const end = graph->end();
    244   int const input_count = end->InputCount();
    245   for (int i = 0; i < input_count; ++i) {
    246     NodeId const id = end->InputAt(i)->id();
    247     for (NodeVector* const copy : copies) {
    248       end->AppendInput(graph->zone(), copy->at(id));
    249       NodeProperties::ChangeOp(end, common->End(end->InputCount()));
    250     }
    251   }
    252 
    253   if (FLAG_trace_turbo_graph) {  // Simple textual RPO.
    254     OFStream os(stdout);
    255     os << "-- Graph after OSR duplication -- " << std::endl;
    256     os << AsRPO(*graph);
    257   }
    258 }
    259 
    260 void SetTypeForOsrValue(Node* osr_value, Node* loop,
    261                         CommonOperatorBuilder* common) {
    262   Node* osr_guard = nullptr;
    263   for (Node* use : osr_value->uses()) {
    264     if (use->opcode() == IrOpcode::kOsrGuard) {
    265       DCHECK_EQ(use->InputAt(0), osr_value);
    266       osr_guard = use;
    267       break;
    268     }
    269   }
    270 
    271   NodeProperties::ChangeOp(osr_guard, common->OsrGuard(OsrGuardType::kAny));
    272 }
    273 
    274 }  // namespace
    275 
    276 void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
    277                             Zone* tmp_zone) {
    278   Graph* graph = jsgraph->graph();
    279   Node* osr_normal_entry = nullptr;
    280   Node* osr_loop_entry = nullptr;
    281   Node* osr_loop = nullptr;
    282 
    283   for (Node* node : graph->start()->uses()) {
    284     if (node->opcode() == IrOpcode::kOsrLoopEntry) {
    285       osr_loop_entry = node;  // found the OSR loop entry
    286     } else if (node->opcode() == IrOpcode::kOsrNormalEntry) {
    287       osr_normal_entry = node;
    288     }
    289   }
    290 
    291   CHECK_NOT_NULL(osr_normal_entry);  // Should have found the OSR normal entry.
    292   CHECK_NOT_NULL(osr_loop_entry);    // Should have found the OSR loop entry.
    293 
    294   for (Node* use : osr_loop_entry->uses()) {
    295     if (use->opcode() == IrOpcode::kLoop) {
    296       CHECK(!osr_loop);             // should be only one OSR loop.
    297       osr_loop = use;               // found the OSR loop.
    298     }
    299   }
    300 
    301   CHECK(osr_loop);  // Should have found the OSR loop.
    302 
    303   for (Node* use : osr_loop_entry->uses()) {
    304     if (use->opcode() == IrOpcode::kOsrValue) {
    305       SetTypeForOsrValue(use, osr_loop, common);
    306     }
    307   }
    308 
    309   // Analyze the graph to determine how deeply nested the OSR loop is.
    310   LoopTree* loop_tree = LoopFinder::BuildLoopTree(graph, tmp_zone);
    311 
    312   Node* dead = jsgraph->Dead();
    313   LoopTree::Loop* loop = loop_tree->ContainingLoop(osr_loop);
    314   if (loop->depth() > 0) {
    315     PeelOuterLoopsForOsr(graph, common, tmp_zone, dead, loop_tree, loop,
    316                          osr_normal_entry, osr_loop_entry);
    317   }
    318 
    319   // Replace the normal entry with {Dead} and the loop entry with {Start}
    320   // and run the control reducer to clean up the graph.
    321   osr_normal_entry->ReplaceUses(dead);
    322   osr_normal_entry->Kill();
    323   osr_loop_entry->ReplaceUses(graph->start());
    324   osr_loop_entry->Kill();
    325 
    326   // Remove the first input to the {osr_loop}.
    327   int const live_input_count = osr_loop->InputCount() - 1;
    328   CHECK_NE(0, live_input_count);
    329   for (Node* const use : osr_loop->uses()) {
    330     if (NodeProperties::IsPhi(use)) {
    331       use->RemoveInput(0);
    332       NodeProperties::ChangeOp(
    333           use, common->ResizeMergeOrPhi(use->op(), live_input_count));
    334     }
    335   }
    336   osr_loop->RemoveInput(0);
    337   NodeProperties::ChangeOp(
    338       osr_loop, common->ResizeMergeOrPhi(osr_loop->op(), live_input_count));
    339 
    340   // Run control reduction and graph trimming.
    341   // TODO(bmeurer): The OSR deconstruction could be a regular reducer and play
    342   // nice together with the rest, instead of having this custom stuff here.
    343   GraphReducer graph_reducer(tmp_zone, graph);
    344   DeadCodeElimination dce(&graph_reducer, graph, common);
    345   CommonOperatorReducer cor(&graph_reducer, graph, common, jsgraph->machine());
    346   graph_reducer.AddReducer(&dce);
    347   graph_reducer.AddReducer(&cor);
    348   graph_reducer.ReduceGraph();
    349   GraphTrimmer trimmer(tmp_zone, graph);
    350   NodeVector roots(tmp_zone);
    351   jsgraph->GetCachedNodes(&roots);
    352   trimmer.TrimGraph(roots.begin(), roots.end());
    353 }
    354 
    355 
    356 void OsrHelper::SetupFrame(Frame* frame) {
    357   // The optimized frame will subsume the unoptimized frame. Do so by reserving
    358   // the first spill slots.
    359   frame->ReserveSpillSlots(UnoptimizedFrameSlots());
    360 }
    361 
    362 }  // namespace compiler
    363 }  // namespace internal
    364 }  // namespace v8
    365