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

Side by Side Diff: src/compiler/js-inlining.cc

Issue 1146393002: [turbofan] Fix UnifyReturn magic in the inliner. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 7 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 unified diff | Download patch
« no previous file with comments | « src/compiler/js-inlining.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 the V8 project authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler/js-inlining.h" 5 #include "src/compiler/js-inlining.h"
6 6
7 #include "src/ast.h" 7 #include "src/ast.h"
8 #include "src/ast-numbering.h" 8 #include "src/ast-numbering.h"
9 #include "src/compiler/all-nodes.h" 9 #include "src/compiler/all-nodes.h"
10 #include "src/compiler/ast-graph-builder.h" 10 #include "src/compiler/ast-graph-builder.h"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 return value_inputs - 2; 52 return value_inputs - 2;
53 } 53 }
54 54
55 Node* frame_state() { return NodeProperties::GetFrameStateInput(call_, 0); } 55 Node* frame_state() { return NodeProperties::GetFrameStateInput(call_, 0); }
56 56
57 private: 57 private:
58 Node* call_; 58 Node* call_;
59 }; 59 };
60 60
61 61
62 // A facade on a JSFunction's graph to facilitate inlining. It assumes
63 // that the function graph has only one return statement, and provides
64 // {UnifyReturn} to convert a function graph to that end.
65 struct Inlinee {
66 Inlinee(Node* start, Node* end) : start_(start), end_(end) {}
67
68 // Returns the last regular control node, that is
69 // the last control node before the end node.
70 Node* end_block() { return NodeProperties::GetControlInput(unique_return()); }
71
72 // Return the effect output of the graph,
73 // that is the effect input of the return statement of the inlinee.
74 Node* effect_output() {
75 return NodeProperties::GetEffectInput(unique_return());
76 }
77 // Return the value output of the graph,
78 // that is the value input of the return statement of the inlinee.
79 Node* value_output() {
80 return NodeProperties::GetValueInput(unique_return(), 0);
81 }
82 // Return the control output of the graph,
83 // that is the control input of the return statement of the inlinee.
84 Node* control_output() {
85 return NodeProperties::GetControlInput(unique_return(), 0);
86 }
87 // Return the unique return statement of the graph.
88 Node* unique_return() {
89 Node* unique_return = NodeProperties::GetControlInput(end_);
90 DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode());
91 return unique_return;
92 }
93
94 // Counts JSFunction, Receiver, arguments, context but not effect, control.
95 size_t total_parameters() { return start_->op()->ValueOutputCount(); }
96
97 // Counts only formal parameters.
98 size_t formal_parameters() {
99 DCHECK_GE(total_parameters(), 3u);
100 return total_parameters() - 3;
101 }
102
103 // Ensure that only a single return reaches the end node.
104 static void UnifyReturn(JSGraph* jsgraph);
105
106 Node* start_;
107 Node* end_;
108 };
109
110
111 void Inlinee::UnifyReturn(JSGraph* jsgraph) {
112 Graph* graph = jsgraph->graph();
113
114 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0);
115 if (final_merge->opcode() == IrOpcode::kReturn) {
116 // nothing to do
117 return;
118 }
119 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode());
120
121 int predecessors = final_merge->op()->ControlInputCount();
122
123 const Operator* op_phi = jsgraph->common()->Phi(kMachAnyTagged, predecessors);
124 const Operator* op_ephi = jsgraph->common()->EffectPhi(predecessors);
125
126 NodeVector values(jsgraph->zone());
127 NodeVector effects(jsgraph->zone());
128 // Iterate over all control flow predecessors,
129 // which must be return statements.
130 for (Edge edge : final_merge->input_edges()) {
131 Node* input = edge.to();
132 switch (input->opcode()) {
133 case IrOpcode::kReturn:
134 values.push_back(NodeProperties::GetValueInput(input, 0));
135 effects.push_back(NodeProperties::GetEffectInput(input));
136 edge.UpdateTo(NodeProperties::GetControlInput(input));
137 input->NullAllInputs();
138 break;
139 default:
140 UNREACHABLE();
141 break;
142 }
143 }
144 values.push_back(final_merge);
145 effects.push_back(final_merge);
146 Node* phi =
147 graph->NewNode(op_phi, static_cast<int>(values.size()), &values.front());
148 Node* ephi = graph->NewNode(op_ephi, static_cast<int>(effects.size()),
149 &effects.front());
150 Node* new_return =
151 graph->NewNode(jsgraph->common()->Return(), phi, ephi, final_merge);
152 graph->end()->ReplaceInput(0, new_return);
153 }
154
155
156 class CopyVisitor { 62 class CopyVisitor {
157 public: 63 public:
158 CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone) 64 CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone)
159 : sentinel_op_(IrOpcode::kDead, Operator::kNoProperties, "Sentinel", 0, 0, 65 : sentinel_op_(IrOpcode::kDead, Operator::kNoProperties, "Sentinel", 0, 0,
160 0, 0, 0, 0), 66 0, 0, 0, 0),
161 sentinel_(target_graph->NewNode(&sentinel_op_)), 67 sentinel_(target_graph->NewNode(&sentinel_op_)),
162 copies_(source_graph->NodeCount(), sentinel_, temp_zone), 68 copies_(source_graph->NodeCount(), sentinel_, temp_zone),
163 source_graph_(source_graph), 69 source_graph_(source_graph),
164 target_graph_(target_graph), 70 target_graph_(target_graph),
165 temp_zone_(temp_zone) {} 71 temp_zone_(temp_zone) {}
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
203 private: 109 private:
204 Operator const sentinel_op_; 110 Operator const sentinel_op_;
205 Node* const sentinel_; 111 Node* const sentinel_;
206 NodeVector copies_; 112 NodeVector copies_;
207 Graph* const source_graph_; 113 Graph* const source_graph_;
208 Graph* const target_graph_; 114 Graph* const target_graph_;
209 Zone* const temp_zone_; 115 Zone* const temp_zone_;
210 }; 116 };
211 117
212 118
213 Reduction JSInliner::InlineCall(Node* call, Inlinee& inlinee) { 119 Reduction JSInliner::InlineCall(Node* call, Node* start, Node* end) {
214 // The scheduler is smart enough to place our code; we just ensure {control} 120 // The scheduler is smart enough to place our code; we just ensure {control}
215 // becomes the control input of the start of the inlinee, and {effect} becomes 121 // becomes the control input of the start of the inlinee, and {effect} becomes
216 // the effect input of the start of the inlinee. 122 // the effect input of the start of the inlinee.
217 Node* control = NodeProperties::GetControlInput(call); 123 Node* control = NodeProperties::GetControlInput(call);
218 Node* effect = NodeProperties::GetEffectInput(call); 124 Node* effect = NodeProperties::GetEffectInput(call);
219 125
220 // Context is last argument. 126 // Context is last argument.
221 int inlinee_context_index = static_cast<int>(inlinee.total_parameters()) - 1; 127 int const inlinee_context_index =
128 static_cast<int>(start->op()->ValueOutputCount()) - 1;
129
222 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not 130 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not
223 // context, effect, control. 131 // context, effect, control.
224 int inliner_inputs = call->op()->ValueInputCount(); 132 int inliner_inputs = call->op()->ValueInputCount();
225 // Iterate over all uses of the start node. 133 // Iterate over all uses of the start node.
226 for (Edge edge : inlinee.start_->use_edges()) { 134 for (Edge edge : start->use_edges()) {
227 Node* use = edge.from(); 135 Node* use = edge.from();
228 switch (use->opcode()) { 136 switch (use->opcode()) {
229 case IrOpcode::kParameter: { 137 case IrOpcode::kParameter: {
230 int index = 1 + ParameterIndexOf(use->op()); 138 int index = 1 + ParameterIndexOf(use->op());
231 if (index < inliner_inputs && index < inlinee_context_index) { 139 if (index < inliner_inputs && index < inlinee_context_index) {
232 // There is an input from the call, and the index is a value 140 // There is an input from the call, and the index is a value
233 // projection but not the context, so rewire the input. 141 // projection but not the context, so rewire the input.
234 ReplaceWithValue(use, call->InputAt(index)); 142 ReplaceWithValue(use, call->InputAt(index));
235 } else if (index == inlinee_context_index) { 143 } else if (index == inlinee_context_index) {
236 // TODO(turbofan): We always context specialize inlinees currently, so 144 // TODO(turbofan): We always context specialize inlinees currently, so
(...skipping 13 matching lines...) Expand all
250 edge.UpdateTo(effect); 158 edge.UpdateTo(effect);
251 } else if (NodeProperties::IsControlEdge(edge)) { 159 } else if (NodeProperties::IsControlEdge(edge)) {
252 edge.UpdateTo(control); 160 edge.UpdateTo(control);
253 } else { 161 } else {
254 UNREACHABLE(); 162 UNREACHABLE();
255 } 163 }
256 break; 164 break;
257 } 165 }
258 } 166 }
259 167
260 ReplaceWithValue(call, inlinee.value_output(), inlinee.effect_output(), 168 // TODO(turbofan): This can be unified once End takes a variable number of
261 inlinee.control_output()); 169 // inputs.
170 Node* value_output;
171 Node* effect_output;
172 Node* control_output;
262 173
263 return Replace(inlinee.value_output()); 174 Node* final_merge = NodeProperties::GetControlInput(end);
175 if (final_merge->opcode() == IrOpcode::kReturn) {
176 value_output = NodeProperties::GetValueInput(final_merge, 0);
177 effect_output = NodeProperties::GetEffectInput(final_merge, 0);
178 control_output = NodeProperties::GetControlInput(final_merge, 0);
179 } else {
180 NodeVector values(local_zone_);
181 NodeVector effects(local_zone_);
182 NodeVector controls(local_zone_);
183 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode());
184 for (Node* const input : final_merge->inputs()) {
185 switch (input->opcode()) {
186 case IrOpcode::kReturn:
187 values.push_back(NodeProperties::GetValueInput(input, 0));
188 effects.push_back(NodeProperties::GetEffectInput(input));
189 controls.push_back(NodeProperties::GetControlInput(input));
190 break;
191 default:
192 // TODO(turbofan): Handle Throw, Terminate and Deoptimize here.
193 UNREACHABLE();
194 break;
195 }
196 }
197 DCHECK_NE(0u, values.size());
198 DCHECK_EQ(values.size(), effects.size());
199 DCHECK_EQ(values.size(), controls.size());
200 int const input_count = static_cast<int>(controls.size());
201 control_output = jsgraph_->graph()->NewNode(
202 jsgraph_->common()->Merge(input_count), input_count, &controls.front());
203 values.push_back(control_output);
204 effects.push_back(control_output);
205 value_output = jsgraph_->graph()->NewNode(
206 jsgraph_->common()->Phi(kMachAnyTagged, input_count),
207 static_cast<int>(values.size()), &values.front());
208 effect_output = jsgraph_->graph()->NewNode(
209 jsgraph_->common()->EffectPhi(input_count),
210 static_cast<int>(effects.size()), &effects.front());
211 }
212
213 ReplaceWithValue(call, value_output, effect_output, control_output);
214
215 return Changed(value_output);
264 } 216 }
265 217
266 218
267 Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call, 219 Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call,
268 Zone* temp_zone) { 220 Zone* temp_zone) {
269 const Operator* op = jsgraph_->common()->FrameState( 221 const Operator* op = jsgraph_->common()->FrameState(
270 FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1), 222 FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1),
271 OutputFrameStateCombine::Ignore()); 223 OutputFrameStateCombine::Ignore());
272 const Operator* op0 = jsgraph_->common()->StateValues(0); 224 const Operator* op0 = jsgraph_->common()->StateValues(0);
273 Node* node0 = jsgraph_->graph()->NewNode(op0); 225 Node* node0 = jsgraph_->graph()->NewNode(op0);
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 // The inlinee specializes to the context from the JSFunction object. 277 // The inlinee specializes to the context from the JSFunction object.
326 // TODO(turbofan): We might want to load the context from the JSFunction at 278 // TODO(turbofan): We might want to load the context from the JSFunction at
327 // runtime in case we only know the SharedFunctionInfo once we have dynamic 279 // runtime in case we only know the SharedFunctionInfo once we have dynamic
328 // type feedback in the compiler. 280 // type feedback in the compiler.
329 AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph); 281 AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph);
330 graph_builder.CreateGraph(true, false); 282 graph_builder.CreateGraph(true, false);
331 JSContextSpecializer context_specializer(&jsgraph); 283 JSContextSpecializer context_specializer(&jsgraph);
332 GraphReducer graph_reducer(&graph, local_zone_); 284 GraphReducer graph_reducer(&graph, local_zone_);
333 graph_reducer.AddReducer(&context_specializer); 285 graph_reducer.AddReducer(&context_specializer);
334 graph_reducer.ReduceGraph(); 286 graph_reducer.ReduceGraph();
335 Inlinee::UnifyReturn(&jsgraph);
336 287
337 CopyVisitor visitor(&graph, jsgraph_->graph(), info.zone()); 288 CopyVisitor visitor(&graph, jsgraph_->graph(), info.zone());
338 visitor.CopyGraph(); 289 visitor.CopyGraph();
339 290
340 Inlinee inlinee(visitor.GetCopy(graph.start()), visitor.GetCopy(graph.end())); 291 Node* start = visitor.GetCopy(graph.start());
292 Node* end = visitor.GetCopy(graph.end());
341 293
342 Node* outer_frame_state = call.frame_state(); 294 Node* outer_frame_state = call.frame_state();
295 size_t const inlinee_formal_parameters = start->op()->ValueOutputCount() - 3;
343 // Insert argument adaptor frame if required. 296 // Insert argument adaptor frame if required.
344 if (call.formal_arguments() != inlinee.formal_parameters()) { 297 if (call.formal_arguments() != inlinee_formal_parameters) {
345 // In strong mode, in case of too few arguments we need to throw a 298 // In strong mode, in case of too few arguments we need to throw a
346 // TypeError so we must not inline this call. 299 // TypeError so we must not inline this call.
347 if (is_strong(info.language_mode()) && 300 if (is_strong(info.language_mode()) &&
348 call.formal_arguments() < inlinee.formal_parameters()) { 301 call.formal_arguments() < inlinee_formal_parameters) {
349 return NoChange(); 302 return NoChange();
350 } 303 }
351 outer_frame_state = CreateArgumentsAdaptorFrameState(&call, info.zone()); 304 outer_frame_state = CreateArgumentsAdaptorFrameState(&call, info.zone());
352 } 305 }
353 306
354 // Fix up all outer frame states from the inlinee. 307 // Fix up all outer frame states from the inlinee.
355 for (Node* const node : visitor.copies()) { 308 for (Node* const node : visitor.copies()) {
356 if (node->opcode() == IrOpcode::kFrameState) { 309 if (node->opcode() == IrOpcode::kFrameState) {
357 DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op())); 310 DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
358 // Don't touch this frame state, if it already has an "outer frame state". 311 // Don't touch this frame state, if it already has an "outer frame state".
359 if (NodeProperties::GetFrameStateInput(node, 0)->opcode() != 312 if (NodeProperties::GetFrameStateInput(node, 0)->opcode() !=
360 IrOpcode::kFrameState) { 313 IrOpcode::kFrameState) {
361 NodeProperties::ReplaceFrameStateInput(node, 0, outer_frame_state); 314 NodeProperties::ReplaceFrameStateInput(node, 0, outer_frame_state);
362 } 315 }
363 } 316 }
364 } 317 }
365 318
366 return InlineCall(node, inlinee); 319 return InlineCall(node, start, end);
367 } 320 }
368 321
369 } // namespace compiler 322 } // namespace compiler
370 } // namespace internal 323 } // namespace internal
371 } // namespace v8 324 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/js-inlining.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698