| 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" |
| 6 |
| 5 #include "src/ast.h" | 7 #include "src/ast.h" |
| 6 #include "src/ast-numbering.h" | 8 #include "src/ast-numbering.h" |
| 7 #include "src/compiler/access-builder.h" | 9 #include "src/compiler/access-builder.h" |
| 8 #include "src/compiler/ast-graph-builder.h" | 10 #include "src/compiler/ast-graph-builder.h" |
| 9 #include "src/compiler/common-operator.h" | 11 #include "src/compiler/common-operator.h" |
| 10 #include "src/compiler/graph-inl.h" | 12 #include "src/compiler/graph-inl.h" |
| 11 #include "src/compiler/graph-visualizer.h" | 13 #include "src/compiler/js-graph.h" |
| 12 #include "src/compiler/js-inlining.h" | |
| 13 #include "src/compiler/js-intrinsic-builder.h" | 14 #include "src/compiler/js-intrinsic-builder.h" |
| 14 #include "src/compiler/js-operator.h" | 15 #include "src/compiler/js-operator.h" |
| 15 #include "src/compiler/node-aux-data-inl.h" | |
| 16 #include "src/compiler/node-matchers.h" | 16 #include "src/compiler/node-matchers.h" |
| 17 #include "src/compiler/node-properties-inl.h" | 17 #include "src/compiler/node-properties-inl.h" |
| 18 #include "src/compiler/simplified-operator.h" | 18 #include "src/compiler/simplified-operator.h" |
| 19 #include "src/compiler/typer.h" | |
| 20 #include "src/full-codegen.h" | |
| 21 #include "src/parser.h" | 19 #include "src/parser.h" |
| 22 #include "src/rewriter.h" | 20 #include "src/rewriter.h" |
| 23 #include "src/scopes.h" | 21 #include "src/scopes.h" |
| 24 | 22 |
| 25 | 23 |
| 26 namespace v8 { | 24 namespace v8 { |
| 27 namespace internal { | 25 namespace internal { |
| 28 namespace compiler { | 26 namespace compiler { |
| 29 | 27 |
| 30 class InlinerVisitor : public NullNodeVisitor { | 28 class InlinerVisitor : public NullNodeVisitor { |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 100 | 98 |
| 101 // Ensure that only a single return reaches the end node. | 99 // Ensure that only a single return reaches the end node. |
| 102 static void UnifyReturn(JSGraph* jsgraph); | 100 static void UnifyReturn(JSGraph* jsgraph); |
| 103 | 101 |
| 104 private: | 102 private: |
| 105 Node* start_; | 103 Node* start_; |
| 106 Node* end_; | 104 Node* end_; |
| 107 }; | 105 }; |
| 108 | 106 |
| 109 | 107 |
| 108 // static |
| 110 void Inlinee::UnifyReturn(JSGraph* jsgraph) { | 109 void Inlinee::UnifyReturn(JSGraph* jsgraph) { |
| 111 Graph* graph = jsgraph->graph(); | 110 Graph* graph = jsgraph->graph(); |
| 112 | 111 |
| 113 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); | 112 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); |
| 114 if (final_merge->opcode() == IrOpcode::kReturn) { | 113 if (final_merge->opcode() == IrOpcode::kReturn) { |
| 115 // nothing to do | 114 // nothing to do |
| 116 return; | 115 return; |
| 117 } | 116 } |
| 118 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); | 117 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); |
| 119 | 118 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 } else if (NodeProperties::IsControlEdge(edge)) { | 266 } else if (NodeProperties::IsControlEdge(edge)) { |
| 268 edge.UpdateTo(control); | 267 edge.UpdateTo(control); |
| 269 } else { | 268 } else { |
| 270 UNREACHABLE(); | 269 UNREACHABLE(); |
| 271 } | 270 } |
| 272 break; | 271 break; |
| 273 } | 272 } |
| 274 } | 273 } |
| 275 | 274 |
| 276 NodeProperties::ReplaceWithValue(call, value_output(), effect_output()); | 275 NodeProperties::ReplaceWithValue(call, value_output(), effect_output()); |
| 277 call->RemoveAllInputs(); | 276 call->Kill(); |
| 278 DCHECK_EQ(0, call->UseCount()); | |
| 279 } | 277 } |
| 280 | 278 |
| 281 | 279 |
| 282 // TODO(turbofan) Provide such accessors for every node, possibly even | 280 template <typename InputIterator> |
| 283 // generate them. | 281 Node* JSInliner::CreateArgumentsAdaptorFrameState( |
| 284 class JSCallFunctionAccessor { | 282 const Handle<JSFunction>& function, InputIterator first, InputIterator last, |
| 285 public: | 283 Node* frame_state) { |
| 286 explicit JSCallFunctionAccessor(Node* call) : call_(call) { | 284 NodeVector parameters(zone()); |
| 287 DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode()); | 285 parameters.insert(parameters.begin(), first, last); |
| 288 } | 286 int const num_parameters = static_cast<int>(parameters.size()); |
| 289 | 287 Node* state_values0 = graph()->NewNode(common()->StateValues(0)); |
| 290 Node* jsfunction() { return call_->InputAt(0); } | 288 Node* state_values = graph()->NewNode(common()->StateValues(num_parameters), |
| 291 | 289 num_parameters, ¶meters.front()); |
| 292 Node* receiver() { return call_->InputAt(1); } | 290 return graph()->NewNode( |
| 293 | 291 common()->FrameState(FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1), |
| 294 Node* formal_argument(size_t index) { | 292 OutputFrameStateCombine::Ignore(), function), |
| 295 DCHECK(index < formal_arguments()); | 293 state_values, state_values0, state_values0, |
| 296 return call_->InputAt(static_cast<int>(2 + index)); | 294 jsgraph()->UndefinedConstant(), frame_state); |
| 297 } | |
| 298 | |
| 299 size_t formal_arguments() { | |
| 300 // {value_inputs} includes jsfunction and receiver. | |
| 301 size_t value_inputs = call_->op()->ValueInputCount(); | |
| 302 DCHECK_GE(call_->InputCount(), 2); | |
| 303 return value_inputs - 2; | |
| 304 } | |
| 305 | |
| 306 Node* frame_state() { return NodeProperties::GetFrameStateInput(call_); } | |
| 307 | |
| 308 private: | |
| 309 Node* call_; | |
| 310 }; | |
| 311 | |
| 312 | |
| 313 void JSInliner::AddClosureToFrameState(Node* frame_state, | |
| 314 Handle<JSFunction> jsfunction) { | |
| 315 FrameStateCallInfo call_info = OpParameter<FrameStateCallInfo>(frame_state); | |
| 316 const Operator* op = jsgraph_->common()->FrameState( | |
| 317 FrameStateType::JS_FRAME, call_info.bailout_id(), | |
| 318 call_info.state_combine(), jsfunction); | |
| 319 frame_state->set_op(op); | |
| 320 } | 295 } |
| 321 | 296 |
| 322 | 297 |
| 323 Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call, | 298 void JSInliner::TryInlineJSCall(Node* node) { |
| 324 Handle<JSFunction> jsfunction, | 299 JSCallFunctionMatcher const m(node); |
| 325 Zone* temp_zone) { | 300 if (!m.function().HasValue()) return; |
| 326 const Operator* op = jsgraph_->common()->FrameState( | 301 Handle<JSFunction> const function = m.function().Value().handle(); |
| 327 FrameStateType::ARGUMENTS_ADAPTOR, BailoutId(-1), | |
| 328 OutputFrameStateCombine::Ignore(), jsfunction); | |
| 329 const Operator* op0 = jsgraph_->common()->StateValues(0); | |
| 330 Node* node0 = jsgraph_->graph()->NewNode(op0); | |
| 331 NodeVector params(temp_zone); | |
| 332 params.push_back(call->receiver()); | |
| 333 for (size_t argument = 0; argument != call->formal_arguments(); ++argument) { | |
| 334 params.push_back(call->formal_argument(argument)); | |
| 335 } | |
| 336 const Operator* op_param = | |
| 337 jsgraph_->common()->StateValues(static_cast<int>(params.size())); | |
| 338 Node* params_node = jsgraph_->graph()->NewNode( | |
| 339 op_param, static_cast<int>(params.size()), ¶ms.front()); | |
| 340 return jsgraph_->graph()->NewNode(op, params_node, node0, node0, | |
| 341 jsgraph_->UndefinedConstant(), | |
| 342 call->frame_state()); | |
| 343 } | |
| 344 | |
| 345 | |
| 346 void JSInliner::TryInlineJSCall(Node* call_node) { | |
| 347 JSCallFunctionAccessor call(call_node); | |
| 348 | |
| 349 HeapObjectMatcher<JSFunction> match(call.jsfunction()); | |
| 350 if (!match.HasValue()) { | |
| 351 return; | |
| 352 } | |
| 353 | |
| 354 Handle<JSFunction> function = match.Value().handle(); | |
| 355 | 302 |
| 356 if (function->shared()->native()) { | 303 if (function->shared()->native()) { |
| 357 if (FLAG_trace_turbo_inlining) { | 304 if (FLAG_trace_turbo_inlining) { |
| 358 SmartArrayPointer<char> name = | 305 SmartArrayPointer<char> name = |
| 359 function->shared()->DebugName()->ToCString(); | 306 function->shared()->DebugName()->ToCString(); |
| 360 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(), | 307 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(), |
| 361 info_->shared_info()->DebugName()->ToCString().get()); | 308 info_->shared_info()->DebugName()->ToCString().get()); |
| 362 } | 309 } |
| 363 return; | 310 return; |
| 364 } | 311 } |
| 365 | 312 |
| 366 CompilationInfoWithZone info(function); | 313 CompilationInfoWithZone info(function); |
| 367 // TODO(wingo): ParseAndAnalyze can fail due to stack overflow. | 314 // TODO(wingo): ParseAndAnalyze can fail due to stack overflow. |
| 368 CHECK(Compiler::ParseAndAnalyze(&info)); | 315 CHECK(Compiler::ParseAndAnalyze(&info)); |
| 369 CHECK(Compiler::EnsureDeoptimizationSupport(&info)); | 316 CHECK(Compiler::EnsureDeoptimizationSupport(&info)); |
| 370 | 317 |
| 371 if (info.scope()->arguments() != NULL && info.strict_mode() != STRICT) { | 318 if (info.scope()->arguments() && info.strict_mode() != STRICT) { |
| 372 // For now do not inline functions that use their arguments array. | 319 // For now do not inline functions that use their arguments array. |
| 373 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | 320 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
| 374 if (FLAG_trace_turbo_inlining) { | 321 if (FLAG_trace_turbo_inlining) { |
| 375 PrintF( | 322 PrintF( |
| 376 "Not Inlining %s into %s because inlinee uses arguments " | 323 "Not Inlining %s into %s because inlinee uses arguments " |
| 377 "array\n", | 324 "array\n", |
| 378 name.get(), info_->shared_info()->DebugName()->ToCString().get()); | 325 name.get(), info_->shared_info()->DebugName()->ToCString().get()); |
| 379 } | 326 } |
| 380 return; | 327 return; |
| 381 } | 328 } |
| 382 | 329 |
| 383 if (FLAG_trace_turbo_inlining) { | 330 if (FLAG_trace_turbo_inlining) { |
| 384 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | 331 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
| 385 PrintF("Inlining %s into %s\n", name.get(), | 332 PrintF("Inlining %s into %s\n", name.get(), |
| 386 info_->shared_info()->DebugName()->ToCString().get()); | 333 info_->shared_info()->DebugName()->ToCString().get()); |
| 387 } | 334 } |
| 388 | 335 |
| 389 Graph graph(info.zone()); | 336 Graph inlinee_graph(info.zone()); |
| 390 JSGraph jsgraph(&graph, jsgraph_->common(), jsgraph_->javascript(), | 337 JSGraph inlinee_jsgraph(&inlinee_graph, common(), javascript(), machine()); |
| 391 jsgraph_->machine()); | |
| 392 | 338 |
| 393 AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph); | 339 AstGraphBuilder graph_builder(zone(), &info, &inlinee_jsgraph); |
| 394 graph_builder.CreateGraph(); | 340 graph_builder.CreateGraph(); |
| 395 Inlinee::UnifyReturn(&jsgraph); | 341 Inlinee::UnifyReturn(&inlinee_jsgraph); |
| 396 | 342 |
| 397 CopyVisitor visitor(&graph, jsgraph_->graph(), info.zone()); | 343 CopyVisitor visitor(&inlinee_graph, graph(), info.zone()); |
| 398 visitor.CopyGraph(); | 344 visitor.CopyGraph(); |
| 399 | 345 |
| 400 Inlinee inlinee(visitor.GetCopy(graph.start()), visitor.GetCopy(graph.end())); | 346 Inlinee inlinee(visitor.GetCopy(inlinee_graph.start()), |
| 347 visitor.GetCopy(inlinee_graph.end())); |
| 401 | 348 |
| 402 if (FLAG_turbo_deoptimization) { | 349 if (FLAG_turbo_deoptimization) { |
| 403 Node* outer_frame_state = call.frame_state(); | 350 Node* outer_frame_state = m.frame_state(); |
| 404 // Insert argument adaptor frame if required. | 351 // Insert argument adaptor frame if required. |
| 405 if (call.formal_arguments() != inlinee.formal_parameters()) { | 352 if (m.size() != inlinee.formal_parameters()) { |
| 406 outer_frame_state = | 353 outer_frame_state = CreateArgumentsAdaptorFrameState( |
| 407 CreateArgumentsAdaptorFrameState(&call, function, info.zone()); | 354 function, m.begin(), m.end(), outer_frame_state); |
| 408 } | 355 } |
| 409 | 356 |
| 410 for (NodeVectorConstIter it = visitor.copies().begin(); | 357 for (auto frame_state : visitor.copies()) { |
| 411 it != visitor.copies().end(); ++it) { | 358 if (frame_state && frame_state->opcode() == IrOpcode::kFrameState) { |
| 412 Node* node = *it; | 359 FrameStateCallInfo info = OpParameter<FrameStateCallInfo>(frame_state); |
| 413 if (node != NULL && node->opcode() == IrOpcode::kFrameState) { | 360 frame_state->set_op( |
| 414 AddClosureToFrameState(node, function); | 361 common()->FrameState(FrameStateType::JS_FRAME, info.bailout_id(), |
| 415 NodeProperties::ReplaceFrameStateInput(node, outer_frame_state); | 362 info.state_combine(), function)); |
| 363 NodeProperties::ReplaceFrameStateInput(frame_state, outer_frame_state); |
| 416 } | 364 } |
| 417 } | 365 } |
| 418 } | 366 } |
| 419 | 367 |
| 420 inlinee.InlineAtCall(jsgraph_, call_node); | 368 inlinee.InlineAtCall(jsgraph(), node); |
| 421 } | 369 } |
| 422 | 370 |
| 423 | 371 |
| 424 class JSCallRuntimeAccessor { | 372 void JSInliner::TryInlineRuntimeCall(Node* node) { |
| 425 public: | 373 JSCallRuntimeMatcher m(node); |
| 426 explicit JSCallRuntimeAccessor(Node* call) : call_(call) { | 374 const Runtime::Function* f = m.function(); |
| 427 DCHECK_EQ(IrOpcode::kJSCallRuntime, call->opcode()); | 375 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return; |
| 428 } | |
| 429 | 376 |
| 430 Node* formal_argument(size_t index) { | 377 JSIntrinsicBuilder intrinsic_builder(jsgraph()); |
| 431 DCHECK(index < formal_arguments()); | |
| 432 return call_->InputAt(static_cast<int>(index)); | |
| 433 } | |
| 434 | 378 |
| 435 size_t formal_arguments() { | 379 // TODO(turbofan): Looks like no one is verifying these arguments in the |
| 436 size_t value_inputs = call_->op()->ValueInputCount(); | 380 // IntrinsicsBuilder? Do we plan to do it there, or should be add code to |
| 437 return value_inputs; | 381 // validate the arguments here? |
| 438 } | 382 NodeVector arguments(zone()); |
| 439 | 383 arguments.insert(arguments.begin(), node->inputs().begin(), |
| 440 Node* frame_state() const { | 384 node->inputs().end()); |
| 441 return NodeProperties::GetFrameStateInput(call_); | 385 ResultAndEffect r = |
| 442 } | 386 intrinsic_builder.BuildGraphFor(f->function_id, arguments); |
| 443 Node* context() const { return NodeProperties::GetContextInput(call_); } | 387 if (r.first) { |
| 444 Node* control() const { return NodeProperties::GetControlInput(call_); } | |
| 445 Node* effect() const { return NodeProperties::GetEffectInput(call_); } | |
| 446 | |
| 447 const Runtime::Function* function() const { | |
| 448 return Runtime::FunctionForId(CallRuntimeParametersOf(call_->op()).id()); | |
| 449 } | |
| 450 | |
| 451 NodeVector inputs(Zone* zone) const { | |
| 452 NodeVector inputs(zone); | |
| 453 for (Node* const node : call_->inputs()) { | |
| 454 inputs.push_back(node); | |
| 455 } | |
| 456 return inputs; | |
| 457 } | |
| 458 | |
| 459 private: | |
| 460 Node* call_; | |
| 461 }; | |
| 462 | |
| 463 | |
| 464 void JSInliner::TryInlineRuntimeCall(Node* call_node) { | |
| 465 JSCallRuntimeAccessor call(call_node); | |
| 466 const Runtime::Function* f = call.function(); | |
| 467 | |
| 468 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) { | |
| 469 return; | |
| 470 } | |
| 471 | |
| 472 JSIntrinsicBuilder intrinsic_builder(jsgraph_); | |
| 473 | |
| 474 ResultAndEffect r = intrinsic_builder.BuildGraphFor( | |
| 475 f->function_id, call.inputs(jsgraph_->zone())); | |
| 476 | |
| 477 if (r.first != NULL) { | |
| 478 if (FLAG_trace_turbo_inlining) { | 388 if (FLAG_trace_turbo_inlining) { |
| 479 PrintF("Inlining %s into %s\n", f->name, | 389 PrintF("Inlining %s into %s\n", f->name, |
| 480 info_->shared_info()->DebugName()->ToCString().get()); | 390 info_->shared_info()->DebugName()->ToCString().get()); |
| 481 } | 391 } |
| 482 NodeProperties::ReplaceWithValue(call_node, r.first, r.second); | 392 NodeProperties::ReplaceWithValue(node, r.first, r.second); |
| 483 call_node->RemoveAllInputs(); | 393 node->Kill(); |
| 484 DCHECK_EQ(0, call_node->UseCount()); | |
| 485 } | 394 } |
| 486 } | 395 } |
| 396 |
| 397 |
| 398 Graph* JSInliner::graph() const { return jsgraph()->graph(); } |
| 399 |
| 400 |
| 401 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
| 402 |
| 403 |
| 404 JSOperatorBuilder* JSInliner::javascript() const { |
| 405 return jsgraph()->javascript(); |
| 487 } | 406 } |
| 407 |
| 408 |
| 409 MachineOperatorBuilder* JSInliner::machine() const { |
| 410 return jsgraph()->machine(); |
| 488 } | 411 } |
| 489 } // namespace v8::internal::compiler | 412 |
| 413 } // namespace compiler |
| 414 } // namespace internal |
| 415 } // namespace v8 |
| OLD | NEW |