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