OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |