OLD | NEW |
(Empty) | |
| 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/compiler/ast-graph-builder.h" |
| 6 #include "src/compiler/common-operator.h" |
| 7 #include "src/compiler/generic-node-inl.h" |
| 8 #include "src/compiler/graph-inl.h" |
| 9 #include "src/compiler/graph-visualizer.h" |
| 10 #include "src/compiler/js-inlining.h" |
| 11 #include "src/compiler/js-operator.h" |
| 12 #include "src/compiler/node-aux-data-inl.h" |
| 13 #include "src/compiler/node-matchers.h" |
| 14 #include "src/compiler/node-properties-inl.h" |
| 15 #include "src/compiler/simplified-operator.h" |
| 16 #include "src/compiler/typer.h" |
| 17 #include "src/parser.h" |
| 18 #include "src/rewriter.h" |
| 19 #include "src/scopes.h" |
| 20 |
| 21 |
| 22 namespace v8 { |
| 23 namespace internal { |
| 24 namespace compiler { |
| 25 |
| 26 class InlinerVisitor : public NullNodeVisitor { |
| 27 public: |
| 28 explicit InlinerVisitor(JSInliner* inliner) : inliner_(inliner) {} |
| 29 |
| 30 GenericGraphVisit::Control Post(Node* node) { |
| 31 switch (node->opcode()) { |
| 32 case IrOpcode::kJSCallFunction: |
| 33 inliner_->TryInlineCall(node); |
| 34 break; |
| 35 default: |
| 36 break; |
| 37 } |
| 38 return GenericGraphVisit::CONTINUE; |
| 39 } |
| 40 |
| 41 private: |
| 42 JSInliner* inliner_; |
| 43 }; |
| 44 |
| 45 |
| 46 void JSInliner::Inline() { |
| 47 InlinerVisitor visitor(this); |
| 48 jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor); |
| 49 } |
| 50 |
| 51 |
| 52 static void MoveWithDependencies(Graph* graph, Node* node, Node* old_block, |
| 53 Node* new_block) { |
| 54 if (OperatorProperties::HasControlInput(node->op())) { |
| 55 // Check if we have left the old_block. |
| 56 if (NodeProperties::GetControlInput(node) != old_block) return; |
| 57 // If not, move this node to the new_block. |
| 58 NodeProperties::ReplaceControlInput(node, new_block); |
| 59 } |
| 60 // Independent of whether a node has a control input or not, |
| 61 // it might have a dependency that is pinned to old_block. |
| 62 for (InputIter iter = node->inputs().begin(); iter != node->inputs().end(); |
| 63 ++iter) { |
| 64 if (NodeProperties::IsControlEdge(iter.edge())) continue; |
| 65 MoveWithDependencies(graph, *iter, old_block, new_block); |
| 66 } |
| 67 } |
| 68 |
| 69 |
| 70 static void MoveAllControlNodes(Node* from, Node* to) { |
| 71 for (UseIter iter = from->uses().begin(); iter != from->uses().end();) { |
| 72 if (NodeProperties::IsControlEdge(iter.edge())) { |
| 73 iter.UpdateToAndIncrement(to); |
| 74 } else { |
| 75 ++iter; |
| 76 } |
| 77 } |
| 78 } |
| 79 |
| 80 |
| 81 // TODO(sigurds) Find a home for this function and reuse it everywhere (esp. in |
| 82 // test cases, where similar code is currently duplicated). |
| 83 static void Parse(Handle<JSFunction> function, CompilationInfoWithZone* info) { |
| 84 CHECK(Parser::Parse(info)); |
| 85 StrictMode strict_mode = info->function()->strict_mode(); |
| 86 info->SetStrictMode(strict_mode); |
| 87 info->SetOptimizing(BailoutId::None(), Handle<Code>(function->code())); |
| 88 CHECK(Rewriter::Rewrite(info)); |
| 89 CHECK(Scope::Analyze(info)); |
| 90 CHECK_NE(NULL, info->scope()); |
| 91 Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(), info->zone()); |
| 92 info->shared_info()->set_scope_info(*scope_info); |
| 93 } |
| 94 |
| 95 |
| 96 // A facade on a JSFunction's graph to facilitate inlining. It assumes the |
| 97 // that the function graph has only one return statement, and provides |
| 98 // {UnifyReturn} to convert a function graph to that end. |
| 99 // InlineAtCall will create some new nodes using {graph}'s builders (and hence |
| 100 // those nodes will live in {graph}'s zone. |
| 101 class Inlinee { |
| 102 public: |
| 103 explicit Inlinee(JSGraph* graph) : jsgraph_(graph) {} |
| 104 |
| 105 Graph* graph() { return jsgraph_->graph(); } |
| 106 JSGraph* jsgraph() { return jsgraph_; } |
| 107 |
| 108 // Returns the last regular control node, that is |
| 109 // the last control node before the end node. |
| 110 Node* end_block() { return NodeProperties::GetControlInput(unique_return()); } |
| 111 |
| 112 // Return the effect output of the graph, |
| 113 // that is the effect input of the return statement of the inlinee. |
| 114 Node* effect_output() { |
| 115 return NodeProperties::GetEffectInput(unique_return()); |
| 116 } |
| 117 // Return the value output of the graph, |
| 118 // that is the value input of the return statement of the inlinee. |
| 119 Node* value_output() { |
| 120 return NodeProperties::GetValueInput(unique_return(), 0); |
| 121 } |
| 122 // Return the unique return statement of the graph. |
| 123 Node* unique_return() { |
| 124 Node* unique_return = |
| 125 NodeProperties::GetControlInput(jsgraph_->graph()->end()); |
| 126 DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode()); |
| 127 return unique_return; |
| 128 } |
| 129 // Inline this graph at {call}, use {jsgraph} and its zone to create |
| 130 // any new nodes. |
| 131 void InlineAtCall(JSGraph* jsgraph, Node* call); |
| 132 // Ensure that only a single return reaches the end node. |
| 133 void UnifyReturn(); |
| 134 |
| 135 private: |
| 136 JSGraph* jsgraph_; |
| 137 }; |
| 138 |
| 139 |
| 140 void Inlinee::UnifyReturn() { |
| 141 Graph* graph = jsgraph_->graph(); |
| 142 |
| 143 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); |
| 144 if (final_merge->opcode() == IrOpcode::kReturn) { |
| 145 // nothing to do |
| 146 return; |
| 147 } |
| 148 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); |
| 149 |
| 150 int predecessors = |
| 151 OperatorProperties::GetControlInputCount(final_merge->op()); |
| 152 Operator* op_phi = jsgraph_->common()->Phi(predecessors); |
| 153 Operator* op_ephi = jsgraph_->common()->EffectPhi(predecessors); |
| 154 |
| 155 NodeVector values(NodeVector::allocator_type(jsgraph_->zone())); |
| 156 NodeVector effects(NodeVector::allocator_type(jsgraph_->zone())); |
| 157 // Iterate over all control flow predecessors, |
| 158 // which must be return statements. |
| 159 InputIter iter = final_merge->inputs().begin(); |
| 160 while (iter != final_merge->inputs().end()) { |
| 161 Node* input = *iter; |
| 162 switch (input->opcode()) { |
| 163 case IrOpcode::kReturn: |
| 164 values.push_back(NodeProperties::GetValueInput(input, 0)); |
| 165 effects.push_back(NodeProperties::GetEffectInput(input)); |
| 166 iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input)); |
| 167 input->RemoveAllInputs(); |
| 168 break; |
| 169 default: |
| 170 UNREACHABLE(); |
| 171 ++iter; |
| 172 break; |
| 173 } |
| 174 } |
| 175 values.push_back(final_merge); |
| 176 effects.push_back(final_merge); |
| 177 Node* phi = |
| 178 graph->NewNode(op_phi, static_cast<int>(values.size()), &values.front()); |
| 179 Node* ephi = graph->NewNode(op_ephi, static_cast<int>(effects.size()), |
| 180 &effects.front()); |
| 181 Node* new_return = |
| 182 graph->NewNode(jsgraph_->common()->Return(), phi, ephi, final_merge); |
| 183 graph->end()->ReplaceInput(0, new_return); |
| 184 } |
| 185 |
| 186 |
| 187 void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) { |
| 188 MachineOperatorBuilder machine(jsgraph->zone()); |
| 189 |
| 190 Node* control = NodeProperties::GetControlInput(call); |
| 191 // Move all the nodes to the end block. |
| 192 MoveAllControlNodes(control, end_block()); |
| 193 // Now move the ones the call depends on back up. |
| 194 // We have to do this back-and-forth to treat the case where the call is |
| 195 // pinned to the start block. |
| 196 MoveWithDependencies(graph(), call, end_block(), control); |
| 197 |
| 198 // The inlinee uses the context from the JSFunction object. This will |
| 199 // also be the effect dependency for the inlinee as it produces an effect. |
| 200 // TODO(sigurds) Use simplified load once it is ready. |
| 201 Node* context = jsgraph->graph()->NewNode( |
| 202 machine.Load(kMachAnyTagged), NodeProperties::GetValueInput(call, 0), |
| 203 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag), |
| 204 NodeProperties::GetEffectInput(call)); |
| 205 |
| 206 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context, |
| 207 // but not effect, control. |
| 208 int inlinee_inputs = graph()->start()->op()->OutputCount(); |
| 209 // Context is last argument. |
| 210 int inlinee_context_index = inlinee_inputs - 1; |
| 211 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not |
| 212 // context, effect, control. |
| 213 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op()); |
| 214 // Iterate over all uses of the start node. |
| 215 UseIter iter = graph()->start()->uses().begin(); |
| 216 while (iter != graph()->start()->uses().end()) { |
| 217 Node* use = *iter; |
| 218 switch (use->opcode()) { |
| 219 case IrOpcode::kParameter: { |
| 220 int index = 1 + static_cast<Operator1<int>*>(use->op())->parameter(); |
| 221 if (index < inliner_inputs && index < inlinee_context_index) { |
| 222 // There is an input from the call, and the index is a value |
| 223 // projection but not the context, so rewire the input. |
| 224 NodeProperties::ReplaceWithValue(*iter, call->InputAt(index)); |
| 225 } else if (index == inlinee_context_index) { |
| 226 // This is the context projection, rewire it to the context from the |
| 227 // JSFunction object. |
| 228 NodeProperties::ReplaceWithValue(*iter, context); |
| 229 } else if (index < inlinee_context_index) { |
| 230 // Call has fewer arguments than required, fill with undefined. |
| 231 NodeProperties::ReplaceWithValue(*iter, jsgraph->UndefinedConstant()); |
| 232 } else { |
| 233 // We got too many arguments, discard for now. |
| 234 // TODO(sigurds): Fix to treat arguments array correctly. |
| 235 } |
| 236 ++iter; |
| 237 break; |
| 238 } |
| 239 default: |
| 240 if (NodeProperties::IsEffectEdge(iter.edge())) { |
| 241 iter.UpdateToAndIncrement(context); |
| 242 } else if (NodeProperties::IsControlEdge(iter.edge())) { |
| 243 iter.UpdateToAndIncrement(control); |
| 244 } else { |
| 245 UNREACHABLE(); |
| 246 } |
| 247 break; |
| 248 } |
| 249 } |
| 250 |
| 251 // Iterate over all uses of the call node. |
| 252 iter = call->uses().begin(); |
| 253 while (iter != call->uses().end()) { |
| 254 if (NodeProperties::IsEffectEdge(iter.edge())) { |
| 255 iter.UpdateToAndIncrement(effect_output()); |
| 256 } else if (NodeProperties::IsControlEdge(iter.edge())) { |
| 257 UNREACHABLE(); |
| 258 } else { |
| 259 DCHECK(NodeProperties::IsValueEdge(iter.edge())); |
| 260 iter.UpdateToAndIncrement(value_output()); |
| 261 } |
| 262 } |
| 263 call->RemoveAllInputs(); |
| 264 DCHECK_EQ(0, call->UseCount()); |
| 265 // TODO(sigurds) Remove this once we copy. |
| 266 unique_return()->RemoveAllInputs(); |
| 267 } |
| 268 |
| 269 |
| 270 void JSInliner::TryInlineCall(Node* node) { |
| 271 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
| 272 |
| 273 ValueMatcher<Handle<JSFunction> > match(node->InputAt(0)); |
| 274 if (!match.HasValue()) { |
| 275 return; |
| 276 } |
| 277 |
| 278 Handle<JSFunction> function = match.Value(); |
| 279 |
| 280 if (function->shared()->native()) { |
| 281 if (FLAG_trace_turbo_inlining) { |
| 282 SmartArrayPointer<char> name = |
| 283 function->shared()->DebugName()->ToCString(); |
| 284 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(), |
| 285 info_->shared_info()->DebugName()->ToCString().get()); |
| 286 } |
| 287 return; |
| 288 } |
| 289 |
| 290 CompilationInfoWithZone info(function); |
| 291 Parse(function, &info); |
| 292 |
| 293 if (info.scope()->arguments() != NULL) { |
| 294 // For now do not inline functions that use their arguments array. |
| 295 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
| 296 if (FLAG_trace_turbo_inlining) { |
| 297 PrintF( |
| 298 "Not Inlining %s into %s because inlinee uses arguments " |
| 299 "array\n", |
| 300 name.get(), info_->shared_info()->DebugName()->ToCString().get()); |
| 301 } |
| 302 return; |
| 303 } |
| 304 |
| 305 if (FLAG_trace_turbo_inlining) { |
| 306 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
| 307 PrintF("Inlining %s into %s\n", name.get(), |
| 308 info_->shared_info()->DebugName()->ToCString().get()); |
| 309 } |
| 310 |
| 311 Graph graph(info_->zone()); |
| 312 graph.SetNextNodeId(jsgraph_->graph()->NextNodeID()); |
| 313 |
| 314 Typer typer(info_->zone()); |
| 315 CommonOperatorBuilder common(info_->zone()); |
| 316 JSGraph jsgraph(&graph, &common, &typer); |
| 317 |
| 318 AstGraphBuilder graph_builder(&info, &jsgraph); |
| 319 graph_builder.CreateGraph(); |
| 320 |
| 321 Inlinee inlinee(&jsgraph); |
| 322 inlinee.UnifyReturn(); |
| 323 inlinee.InlineAtCall(jsgraph_, node); |
| 324 |
| 325 jsgraph_->graph()->SetNextNodeId(inlinee.graph()->NextNodeID()); |
| 326 } |
| 327 } |
| 328 } |
| 329 } // namespace v8::internal::compiler |
OLD | NEW |