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