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