| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_inliner.h" | 5 #include "vm/flow_graph_inliner.h" |
| 6 | 6 |
| 7 #include "vm/compiler.h" | 7 #include "vm/compiler.h" |
| 8 #include "vm/flags.h" | 8 #include "vm/flags.h" |
| 9 #include "vm/flow_graph.h" | 9 #include "vm/flow_graph.h" |
| 10 #include "vm/flow_graph_builder.h" | 10 #include "vm/flow_graph_builder.h" |
| 11 #include "vm/flow_graph_optimizer.h" | 11 #include "vm/flow_graph_optimizer.h" |
| 12 #include "vm/il_printer.h" | 12 #include "vm/il_printer.h" |
| 13 #include "vm/intrinsifier.h" | 13 #include "vm/intrinsifier.h" |
| 14 #include "vm/longjump.h" | 14 #include "vm/longjump.h" |
| 15 #include "vm/object.h" | 15 #include "vm/object.h" |
| 16 #include "vm/object_store.h" | 16 #include "vm/object_store.h" |
| 17 #include "vm/timer.h" |
| 17 | 18 |
| 18 namespace dart { | 19 namespace dart { |
| 19 | 20 |
| 20 DEFINE_FLAG(bool, trace_inlining, false, "Trace inlining"); | 21 DEFINE_FLAG(bool, trace_inlining, false, "Trace inlining"); |
| 21 DEFINE_FLAG(charp, inlining_filter, NULL, "Inline only in named function"); | 22 DEFINE_FLAG(charp, inlining_filter, NULL, "Inline only in named function"); |
| 22 DEFINE_FLAG(int, inlining_size_threshold, 50, | 23 DEFINE_FLAG(int, inlining_size_threshold, 50, |
| 23 "Inline only functions with up to threshold instructions (default 50)"); | 24 "Inline only functions with up to threshold instructions (default 50)"); |
| 24 // TODO(srdjan): set to 3 once crash in apidoc.dart is resolved. | 25 // TODO(srdjan): set to 3 once crash in apidoc.dart is resolved. |
| 25 DEFINE_FLAG(int, inlining_depth_threshold, 3, | 26 DEFINE_FLAG(int, inlining_depth_threshold, 3, |
| 26 "Inline recursively up to threshold depth (default 3)"); | 27 "Inline recursively up to threshold depth (default 3)"); |
| 27 DEFINE_FLAG(bool, inline_control_flow, true, | 28 DEFINE_FLAG(bool, inline_control_flow, true, |
| 28 "Inline functions with control flow."); | 29 "Inline functions with control flow."); |
| 29 DECLARE_FLAG(bool, print_flow_graph); | 30 DECLARE_FLAG(bool, print_flow_graph); |
| 30 DECLARE_FLAG(int, deoptimization_counter_threshold); | 31 DECLARE_FLAG(int, deoptimization_counter_threshold); |
| 31 DECLARE_FLAG(bool, verify_compiler); | 32 DECLARE_FLAG(bool, verify_compiler); |
| 33 DECLARE_FLAG(bool, compiler_stats); |
| 32 | 34 |
| 33 #define TRACE_INLINING(statement) \ | 35 #define TRACE_INLINING(statement) \ |
| 34 do { \ | 36 do { \ |
| 35 if (FLAG_trace_inlining) statement; \ | 37 if (FLAG_trace_inlining) statement; \ |
| 36 } while (false) | 38 } while (false) |
| 37 | 39 |
| 38 | 40 |
| 39 // Test if a call is recursive by looking in the deoptimization environment. | 41 // Test if a call is recursive by looking in the deoptimization environment. |
| 40 static bool IsCallRecursive(const Function& function, Definition* call) { | 42 static bool IsCallRecursive(const Function& function, Definition* call) { |
| 41 Environment* env = call->env(); | 43 Environment* env = call->env(); |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 // Save and clear deopt id. | 264 // Save and clear deopt id. |
| 263 const intptr_t prev_deopt_id = isolate->deopt_id(); | 265 const intptr_t prev_deopt_id = isolate->deopt_id(); |
| 264 isolate->set_deopt_id(0); | 266 isolate->set_deopt_id(0); |
| 265 // Install bailout jump. | 267 // Install bailout jump. |
| 266 LongJump* base = isolate->long_jump_base(); | 268 LongJump* base = isolate->long_jump_base(); |
| 267 LongJump jump; | 269 LongJump jump; |
| 268 isolate->set_long_jump_base(&jump); | 270 isolate->set_long_jump_base(&jump); |
| 269 if (setjmp(*jump.Set()) == 0) { | 271 if (setjmp(*jump.Set()) == 0) { |
| 270 // Parse the callee function. | 272 // Parse the callee function. |
| 271 bool in_cache; | 273 bool in_cache; |
| 272 ParsedFunction* parsed_function = ParseFunction(function, &in_cache); | 274 ParsedFunction* parsed_function; |
| 275 { |
| 276 TimerScope timer(FLAG_compiler_stats, |
| 277 &CompilerStats::graphinliner_parse_timer, |
| 278 isolate); |
| 279 parsed_function = ParseFunction(function, &in_cache); |
| 280 } |
| 273 | 281 |
| 274 // Load IC data for the callee. | 282 // Load IC data for the callee. |
| 275 if (function.HasCode()) { | 283 if (function.HasCode()) { |
| 276 const Code& unoptimized_code = | 284 const Code& unoptimized_code = |
| 277 Code::Handle(function.unoptimized_code()); | 285 Code::Handle(function.unoptimized_code()); |
| 278 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); | 286 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); |
| 279 } | 287 } |
| 280 | 288 |
| 281 // Build the callee graph. | 289 // Build the callee graph. |
| 282 FlowGraphBuilder builder(*parsed_function); | 290 FlowGraphBuilder builder(*parsed_function); |
| 283 builder.SetInitialBlockId(caller_graph_->max_block_id()); | 291 builder.SetInitialBlockId(caller_graph_->max_block_id()); |
| 284 FlowGraph* callee_graph = | 292 FlowGraph* callee_graph; |
| 285 builder.BuildGraph(FlowGraphBuilder::kValueContext); | 293 { |
| 294 TimerScope timer(FLAG_compiler_stats, |
| 295 &CompilerStats::graphinliner_build_timer, |
| 296 isolate); |
| 297 callee_graph = builder.BuildGraph(FlowGraphBuilder::kValueContext); |
| 298 } |
| 286 | 299 |
| 287 // Abort if the callee graph contains control flow. | 300 // Abort if the callee graph contains control flow. |
| 288 if (!FLAG_inline_control_flow && | 301 if (!FLAG_inline_control_flow && |
| 289 (callee_graph->preorder().length() != 2)) { | 302 (callee_graph->preorder().length() != 2)) { |
| 290 function.set_is_inlinable(false); | 303 function.set_is_inlinable(false); |
| 291 isolate->set_long_jump_base(base); | 304 isolate->set_long_jump_base(base); |
| 292 isolate->set_ic_data_array(prev_ic_data.raw()); | 305 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 293 TRACE_INLINING(OS::Print(" Bailout: control flow\n")); | 306 TRACE_INLINING(OS::Print(" Bailout: control flow\n")); |
| 294 return false; | 307 return false; |
| 295 } | 308 } |
| 296 | 309 |
| 297 // Compute SSA on the callee graph, catching bailouts. | 310 { |
| 298 callee_graph->ComputeSSA(next_ssa_temp_index_); | 311 TimerScope timer(FLAG_compiler_stats, |
| 299 callee_graph->ComputeUseLists(); | 312 &CompilerStats::graphinliner_ssa_timer, |
| 313 isolate); |
| 314 // Compute SSA on the callee graph, catching bailouts. |
| 315 callee_graph->ComputeSSA(next_ssa_temp_index_); |
| 316 callee_graph->ComputeUseLists(); |
| 317 } |
| 300 | 318 |
| 301 // TODO(zerny): Do more optimization passes on the callee graph. | 319 { |
| 302 FlowGraphOptimizer optimizer(callee_graph); | 320 TimerScope timer(FLAG_compiler_stats, |
| 303 optimizer.ApplyICData(); | 321 &CompilerStats::graphinliner_opt_timer, |
| 304 callee_graph->ComputeUseLists(); | 322 isolate); |
| 323 // TODO(zerny): Do more optimization passes on the callee graph. |
| 324 FlowGraphOptimizer optimizer(callee_graph); |
| 325 optimizer.ApplyICData(); |
| 326 callee_graph->ComputeUseLists(); |
| 327 } |
| 305 | 328 |
| 306 if (FLAG_trace_inlining && FLAG_print_flow_graph) { | 329 if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
| 307 OS::Print("Callee graph for inlining %s\n", | 330 OS::Print("Callee graph for inlining %s\n", |
| 308 function.ToFullyQualifiedCString()); | 331 function.ToFullyQualifiedCString()); |
| 309 FlowGraphPrinter printer(*callee_graph); | 332 FlowGraphPrinter printer(*callee_graph); |
| 310 printer.PrintBlocks(); | 333 printer.PrintBlocks(); |
| 311 } | 334 } |
| 312 | 335 |
| 313 // If result is more than size threshold then abort. | 336 // If result is more than size threshold then abort. |
| 314 // TODO(zerny): Do this after CP and dead code elimination. | 337 // TODO(zerny): Do this after CP and dead code elimination. |
| 315 intptr_t size = callee_graph->InstructionCount(); | 338 intptr_t size = callee_graph->InstructionCount(); |
| 316 if (size > FLAG_inlining_size_threshold) { | 339 if (size > FLAG_inlining_size_threshold) { |
| 317 function.set_is_inlinable(false); | 340 function.set_is_inlinable(false); |
| 318 isolate->set_long_jump_base(base); | 341 isolate->set_long_jump_base(base); |
| 319 isolate->set_deopt_id(prev_deopt_id); | 342 isolate->set_deopt_id(prev_deopt_id); |
| 320 isolate->set_ic_data_array(prev_ic_data.raw()); | 343 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 321 TRACE_INLINING(OS::Print(" Bailout: graph size %"Pd"\n", size)); | 344 TRACE_INLINING(OS::Print(" Bailout: graph size %"Pd"\n", size)); |
| 322 return false; | 345 return false; |
| 323 } | 346 } |
| 324 | 347 |
| 325 // If depth is less or equal to threshold recursively add call sites. | 348 // If depth is less or equal to threshold recursively add call sites. |
| 326 if (inlining_depth_ < FLAG_inlining_depth_threshold) { | 349 if (inlining_depth_ < FLAG_inlining_depth_threshold) { |
| 327 collected_call_sites_->FindCallSites(callee_graph); | 350 collected_call_sites_->FindCallSites(callee_graph); |
| 328 } | 351 } |
| 329 | 352 |
| 330 // Plug result in the caller graph. | 353 { |
| 331 caller_graph_->InlineCall(call, callee_graph); | 354 TimerScope timer(FLAG_compiler_stats, |
| 332 next_ssa_temp_index_ = caller_graph_->max_virtual_register_number(); | 355 &CompilerStats::graphinliner_subst_timer, |
| 356 isolate); |
| 333 | 357 |
| 334 // Remove push arguments of the call. | 358 // Plug result in the caller graph. |
| 335 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 359 caller_graph_->InlineCall(call, callee_graph); |
| 336 PushArgumentInstr* push = call->ArgumentAt(i); | 360 next_ssa_temp_index_ = caller_graph_->max_virtual_register_number(); |
| 337 push->ReplaceUsesWith(push->value()->definition()); | 361 |
| 338 push->RemoveFromGraph(); | 362 // Remove push arguments of the call. |
| 363 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 364 PushArgumentInstr* push = call->ArgumentAt(i); |
| 365 push->ReplaceUsesWith(push->value()->definition()); |
| 366 push->RemoveFromGraph(); |
| 367 } |
| 368 |
| 369 // Replace formal parameters with actuals. |
| 370 intptr_t arg_index = 0; |
| 371 GrowableArray<Definition*>* defns = |
| 372 callee_graph->graph_entry()->initial_definitions(); |
| 373 for (intptr_t i = 0; i < defns->length(); ++i) { |
| 374 ParameterInstr* param = (*defns)[i]->AsParameter(); |
| 375 if (param != NULL) { |
| 376 param->ReplaceUsesWith((*arguments)[arg_index++]->definition()); |
| 377 } |
| 378 } |
| 379 ASSERT(arg_index == arguments->length()); |
| 380 |
| 381 // Replace callee's null constant with caller's null constant. |
| 382 callee_graph->graph_entry()->constant_null()->ReplaceUsesWith( |
| 383 caller_graph_->graph_entry()->constant_null()); |
| 339 } | 384 } |
| 340 | 385 |
| 341 // Replace formal parameters with actuals. | |
| 342 intptr_t arg_index = 0; | |
| 343 GrowableArray<Definition*>* defns = | |
| 344 callee_graph->graph_entry()->initial_definitions(); | |
| 345 for (intptr_t i = 0; i < defns->length(); ++i) { | |
| 346 ParameterInstr* param = (*defns)[i]->AsParameter(); | |
| 347 if (param != NULL) { | |
| 348 param->ReplaceUsesWith((*arguments)[arg_index++]->definition()); | |
| 349 } | |
| 350 } | |
| 351 ASSERT(arg_index == arguments->length()); | |
| 352 | |
| 353 // Replace callee's null constant with caller's null constant. | |
| 354 callee_graph->graph_entry()->constant_null()->ReplaceUsesWith( | |
| 355 caller_graph_->graph_entry()->constant_null()); | |
| 356 | |
| 357 TRACE_INLINING(OS::Print(" Success\n")); | 386 TRACE_INLINING(OS::Print(" Success\n")); |
| 358 | 387 |
| 359 // Add the function to the cache. | 388 // Add the function to the cache. |
| 360 if (!in_cache) function_cache.Add(parsed_function); | 389 if (!in_cache) function_cache.Add(parsed_function); |
| 361 | 390 |
| 362 // Check that inlining maintains use lists. | 391 // Check that inlining maintains use lists. |
| 363 DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); | 392 DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); |
| 364 | 393 |
| 365 // Build succeeded so we restore the bailout jump. | 394 // Build succeeded so we restore the bailout jump. |
| 366 inlined_ = true; | 395 inlined_ = true; |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 OS::Print("After Inlining of %s\n", flow_graph_-> | 535 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 507 parsed_function().function().ToFullyQualifiedCString()); | 536 parsed_function().function().ToFullyQualifiedCString()); |
| 508 FlowGraphPrinter printer(*flow_graph_); | 537 FlowGraphPrinter printer(*flow_graph_); |
| 509 printer.PrintBlocks(); | 538 printer.PrintBlocks(); |
| 510 } | 539 } |
| 511 } | 540 } |
| 512 } | 541 } |
| 513 } | 542 } |
| 514 | 543 |
| 515 } // namespace dart | 544 } // namespace dart |
| OLD | NEW |