| 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 | 17 |
| 18 namespace dart { | 18 namespace dart { |
| 19 | 19 |
| 20 DEFINE_FLAG(bool, trace_inlining, false, "Trace inlining"); | 20 DEFINE_FLAG(bool, trace_inlining, false, "Trace inlining"); |
| 21 DEFINE_FLAG(charp, inlining_filter, NULL, "Inline only in named function"); | 21 DEFINE_FLAG(charp, inlining_filter, NULL, "Inline only in named function"); |
| 22 DEFINE_FLAG(int, inlining_size_threshold, 50, | 22 DEFINE_FLAG(int, inlining_size_threshold, 50, |
| 23 "Inline only functions with up to threshold instructions (default 50)"); | 23 "Inline only functions with up to threshold instructions (default 50)"); |
| 24 // TODO(srdjan): set to 3 once crash in apidoc.dart is resolved. | 24 // TODO(srdjan): set to 3 once crash in apidoc.dart is resolved. |
| 25 DEFINE_FLAG(int, inlining_depth_threshold, 3, | 25 DEFINE_FLAG(int, inlining_depth_threshold, 3, |
| 26 "Inline recursively up to threshold depth (default 3)"); | 26 "Inline recursively up to threshold depth (default 3)"); |
| 27 DEFINE_FLAG(bool, inline_control_flow, true, | |
| 28 "Inline functions with control flow."); | |
| 29 DECLARE_FLAG(bool, print_flow_graph); | 27 DECLARE_FLAG(bool, print_flow_graph); |
| 30 DECLARE_FLAG(int, deoptimization_counter_threshold); | 28 DECLARE_FLAG(int, deoptimization_counter_threshold); |
| 31 DECLARE_FLAG(bool, verify_compiler); | 29 DECLARE_FLAG(bool, verify_compiler); |
| 32 | 30 |
| 33 #define TRACE_INLINING(statement) \ | 31 #define TRACE_INLINING(statement) \ |
| 34 do { \ | 32 do { \ |
| 35 if (FLAG_trace_inlining) statement; \ | 33 if (FLAG_trace_inlining) statement; \ |
| 36 } while (false) | 34 } while (false) |
| 37 | 35 |
| 38 | 36 |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 165 public: | 163 public: |
| 166 explicit CallSiteInliner(FlowGraph* flow_graph) | 164 explicit CallSiteInliner(FlowGraph* flow_graph) |
| 167 : caller_graph_(flow_graph), | 165 : caller_graph_(flow_graph), |
| 168 next_ssa_temp_index_(flow_graph->max_virtual_register_number()), | 166 next_ssa_temp_index_(flow_graph->max_virtual_register_number()), |
| 169 inlined_(false), | 167 inlined_(false), |
| 170 initial_size_(flow_graph->InstructionCount()), | 168 initial_size_(flow_graph->InstructionCount()), |
| 171 inlined_size_(0), | 169 inlined_size_(0), |
| 172 inlining_depth_(1), | 170 inlining_depth_(1), |
| 173 collected_call_sites_(NULL), | 171 collected_call_sites_(NULL), |
| 174 inlining_call_sites_(NULL), | 172 inlining_call_sites_(NULL), |
| 175 function_cache() { } | 173 function_cache_() { } |
| 176 | 174 |
| 177 void InlineCalls() { | 175 void InlineCalls() { |
| 178 // If inlining depth is less then one abort. | 176 // If inlining depth is less then one abort. |
| 179 if (FLAG_inlining_depth_threshold < 1) return; | 177 if (FLAG_inlining_depth_threshold < 1) return; |
| 180 // Create two call site collections to swap between. | 178 // Create two call site collections to swap between. |
| 181 CallSites sites1(caller_graph_); | 179 CallSites sites1(caller_graph_); |
| 182 CallSites sites2(caller_graph_); | 180 CallSites sites2(caller_graph_); |
| 183 CallSites* call_sites_temp = NULL; | 181 CallSites* call_sites_temp = NULL; |
| 184 collected_call_sites_ = &sites1; | 182 collected_call_sites_ = &sites1; |
| 185 inlining_call_sites_ = &sites2; | 183 inlining_call_sites_ = &sites2; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 // Save and clear deopt id. | 260 // Save and clear deopt id. |
| 263 const intptr_t prev_deopt_id = isolate->deopt_id(); | 261 const intptr_t prev_deopt_id = isolate->deopt_id(); |
| 264 isolate->set_deopt_id(0); | 262 isolate->set_deopt_id(0); |
| 265 // Install bailout jump. | 263 // Install bailout jump. |
| 266 LongJump* base = isolate->long_jump_base(); | 264 LongJump* base = isolate->long_jump_base(); |
| 267 LongJump jump; | 265 LongJump jump; |
| 268 isolate->set_long_jump_base(&jump); | 266 isolate->set_long_jump_base(&jump); |
| 269 if (setjmp(*jump.Set()) == 0) { | 267 if (setjmp(*jump.Set()) == 0) { |
| 270 // Parse the callee function. | 268 // Parse the callee function. |
| 271 bool in_cache; | 269 bool in_cache; |
| 272 ParsedFunction* parsed_function = ParseFunction(function, &in_cache); | 270 ParsedFunction* parsed_function = GetParsedFunction(function, &in_cache); |
| 273 | 271 |
| 274 // Load IC data for the callee. | 272 // Load IC data for the callee. |
| 275 if (function.HasCode()) { | 273 if (function.HasCode()) { |
| 276 const Code& unoptimized_code = | 274 const Code& unoptimized_code = |
| 277 Code::Handle(function.unoptimized_code()); | 275 Code::Handle(function.unoptimized_code()); |
| 278 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); | 276 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); |
| 279 } | 277 } |
| 280 | 278 |
| 281 // Build the callee graph. | 279 // Build the callee graph. |
| 282 FlowGraphBuilder builder(*parsed_function); | 280 FlowGraphBuilder builder(*parsed_function); |
| 283 builder.SetInitialBlockId(caller_graph_->max_block_id()); | 281 builder.SetInitialBlockId(caller_graph_->max_block_id()); |
| 284 FlowGraph* callee_graph = | 282 FlowGraph* callee_graph = |
| 285 builder.BuildGraph(FlowGraphBuilder::kValueContext); | 283 builder.BuildGraph(FlowGraphBuilder::kValueContext); |
| 286 | 284 |
| 287 // Abort if the callee graph contains control flow. | |
| 288 if (!FLAG_inline_control_flow && | |
| 289 (callee_graph->preorder().length() != 2)) { | |
| 290 function.set_is_inlinable(false); | |
| 291 isolate->set_long_jump_base(base); | |
| 292 isolate->set_ic_data_array(prev_ic_data.raw()); | |
| 293 TRACE_INLINING(OS::Print(" Bailout: control flow\n")); | |
| 294 return false; | |
| 295 } | |
| 296 | |
| 297 // Compute SSA on the callee graph, catching bailouts. | 285 // Compute SSA on the callee graph, catching bailouts. |
| 298 callee_graph->ComputeSSA(next_ssa_temp_index_); | 286 callee_graph->ComputeSSA(next_ssa_temp_index_); |
| 299 callee_graph->ComputeUseLists(); | 287 callee_graph->ComputeUseLists(); |
| 300 | 288 |
| 301 // TODO(zerny): Do more optimization passes on the callee graph. | 289 // TODO(zerny): Do more optimization passes on the callee graph. |
| 302 FlowGraphOptimizer optimizer(callee_graph); | 290 FlowGraphOptimizer optimizer(callee_graph); |
| 303 optimizer.ApplyICData(); | 291 optimizer.ApplyICData(); |
| 304 callee_graph->ComputeUseLists(); | 292 callee_graph->ComputeUseLists(); |
| 305 | 293 |
| 306 if (FLAG_trace_inlining && FLAG_print_flow_graph) { | 294 if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 350 } | 338 } |
| 351 ASSERT(arg_index == arguments->length()); | 339 ASSERT(arg_index == arguments->length()); |
| 352 | 340 |
| 353 // Replace callee's null constant with caller's null constant. | 341 // Replace callee's null constant with caller's null constant. |
| 354 callee_graph->graph_entry()->constant_null()->ReplaceUsesWith( | 342 callee_graph->graph_entry()->constant_null()->ReplaceUsesWith( |
| 355 caller_graph_->graph_entry()->constant_null()); | 343 caller_graph_->graph_entry()->constant_null()); |
| 356 | 344 |
| 357 TRACE_INLINING(OS::Print(" Success\n")); | 345 TRACE_INLINING(OS::Print(" Success\n")); |
| 358 | 346 |
| 359 // Add the function to the cache. | 347 // Add the function to the cache. |
| 360 if (!in_cache) function_cache.Add(parsed_function); | 348 if (!in_cache) function_cache_.Add(parsed_function); |
| 361 | 349 |
| 362 // Check that inlining maintains use lists. | 350 // Check that inlining maintains use lists. |
| 363 DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); | 351 DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); |
| 364 | 352 |
| 365 // Build succeeded so we restore the bailout jump. | 353 // Build succeeded so we restore the bailout jump. |
| 366 inlined_ = true; | 354 inlined_ = true; |
| 367 inlined_size_ += size; | 355 inlined_size_ += size; |
| 368 isolate->set_long_jump_base(base); | 356 isolate->set_long_jump_base(base); |
| 369 isolate->set_deopt_id(prev_deopt_id); | 357 isolate->set_deopt_id(prev_deopt_id); |
| 370 isolate->set_ic_data_array(prev_ic_data.raw()); | 358 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 371 return true; | 359 return true; |
| 372 } else { | 360 } else { |
| 373 Error& error = Error::Handle(); | 361 Error& error = Error::Handle(); |
| 374 error = isolate->object_store()->sticky_error(); | 362 error = isolate->object_store()->sticky_error(); |
| 375 isolate->object_store()->clear_sticky_error(); | 363 isolate->object_store()->clear_sticky_error(); |
| 376 isolate->set_long_jump_base(base); | 364 isolate->set_long_jump_base(base); |
| 377 isolate->set_deopt_id(prev_deopt_id); | 365 isolate->set_deopt_id(prev_deopt_id); |
| 378 isolate->set_ic_data_array(prev_ic_data.raw()); | 366 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 379 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); | 367 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); |
| 380 return false; | 368 return false; |
| 381 } | 369 } |
| 382 } | 370 } |
| 383 | 371 |
| 384 // Parse a function reusing the cache if possible. Returns true if the | 372 // Parse a function reusing the cache if possible. Returns true if the |
| 385 // function was in the cache. | 373 // function was in the cache. |
| 386 ParsedFunction* ParseFunction(const Function& function, bool* in_cache) { | 374 ParsedFunction* GetParsedFunction(const Function& function, bool* in_cache) { |
| 387 // TODO(zerny): Use a hash map for the cache. | 375 // TODO(zerny): Use a hash map for the cache. |
| 388 for (intptr_t i = 0; i < function_cache.length(); ++i) { | 376 for (intptr_t i = 0; i < function_cache_.length(); ++i) { |
| 389 ParsedFunction* parsed_function = function_cache[i]; | 377 ParsedFunction* parsed_function = function_cache_[i]; |
| 390 if (parsed_function->function().raw() == function.raw()) { | 378 if (parsed_function->function().raw() == function.raw()) { |
| 391 *in_cache = true; | 379 *in_cache = true; |
| 392 SourceLabelResetter reset; | 380 SourceLabelResetter reset; |
| 393 parsed_function->node_sequence()->Visit(&reset); | 381 parsed_function->node_sequence()->Visit(&reset); |
| 394 return parsed_function; | 382 return parsed_function; |
| 395 } | 383 } |
| 396 } | 384 } |
| 397 *in_cache = false; | 385 *in_cache = false; |
| 398 ParsedFunction* parsed_function = new ParsedFunction(function); | 386 ParsedFunction* parsed_function = new ParsedFunction(function); |
| 399 Parser::ParseFunction(parsed_function); | 387 Parser::ParseFunction(parsed_function); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 463 } | 451 } |
| 464 | 452 |
| 465 FlowGraph* caller_graph_; | 453 FlowGraph* caller_graph_; |
| 466 intptr_t next_ssa_temp_index_; | 454 intptr_t next_ssa_temp_index_; |
| 467 bool inlined_; | 455 bool inlined_; |
| 468 intptr_t initial_size_; | 456 intptr_t initial_size_; |
| 469 intptr_t inlined_size_; | 457 intptr_t inlined_size_; |
| 470 intptr_t inlining_depth_; | 458 intptr_t inlining_depth_; |
| 471 CallSites* collected_call_sites_; | 459 CallSites* collected_call_sites_; |
| 472 CallSites* inlining_call_sites_; | 460 CallSites* inlining_call_sites_; |
| 473 GrowableArray<ParsedFunction*> function_cache; | 461 GrowableArray<ParsedFunction*> function_cache_; |
| 474 | 462 |
| 475 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); | 463 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); |
| 476 }; | 464 }; |
| 477 | 465 |
| 478 | 466 |
| 479 void FlowGraphInliner::Inline() { | 467 void FlowGraphInliner::Inline() { |
| 480 if ((FLAG_inlining_filter != NULL) && | 468 if ((FLAG_inlining_filter != NULL) && |
| 481 (strstr(flow_graph_-> | 469 (strstr(flow_graph_-> |
| 482 parsed_function().function().ToFullyQualifiedCString(), | 470 parsed_function().function().ToFullyQualifiedCString(), |
| 483 FLAG_inlining_filter) == NULL)) { | 471 FLAG_inlining_filter) == NULL)) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 505 OS::Print("After Inlining of %s\n", flow_graph_-> | 493 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 506 parsed_function().function().ToFullyQualifiedCString()); | 494 parsed_function().function().ToFullyQualifiedCString()); |
| 507 FlowGraphPrinter printer(*flow_graph_); | 495 FlowGraphPrinter printer(*flow_graph_); |
| 508 printer.PrintBlocks(); | 496 printer.PrintBlocks(); |
| 509 } | 497 } |
| 510 } | 498 } |
| 511 } | 499 } |
| 512 } | 500 } |
| 513 | 501 |
| 514 } // namespace dart | 502 } // namespace dart |
| OLD | NEW |