Chromium Code Reviews| 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" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 "Inline only functions with up to threshold instructions (default 50)"); | 24 "Inline only functions with up to threshold instructions (default 50)"); |
| 25 // 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. |
| 26 DEFINE_FLAG(int, inlining_depth_threshold, 3, | 26 DEFINE_FLAG(int, inlining_depth_threshold, 3, |
| 27 "Inline recursively up to threshold depth (default 3)"); | 27 "Inline recursively up to threshold depth (default 3)"); |
| 28 DEFINE_FLAG(bool, inline_control_flow, true, | 28 DEFINE_FLAG(bool, inline_control_flow, true, |
| 29 "Inline functions with control flow."); | 29 "Inline functions with control flow."); |
| 30 DECLARE_FLAG(bool, print_flow_graph); | 30 DECLARE_FLAG(bool, print_flow_graph); |
| 31 DECLARE_FLAG(int, deoptimization_counter_threshold); | 31 DECLARE_FLAG(int, deoptimization_counter_threshold); |
| 32 DECLARE_FLAG(bool, verify_compiler); | 32 DECLARE_FLAG(bool, verify_compiler); |
| 33 DECLARE_FLAG(bool, compiler_stats); | 33 DECLARE_FLAG(bool, compiler_stats); |
| 34 DECLARE_FLAG(bool, reject_named_argument_as_positional); | |
|
srdjan
2012/10/23 16:00:52
Remove?
srdjan
2012/10/23 16:02:37
Actually not yet :-)... sorry
| |
| 34 | 35 |
| 35 #define TRACE_INLINING(statement) \ | 36 #define TRACE_INLINING(statement) \ |
| 36 do { \ | 37 do { \ |
| 37 if (FLAG_trace_inlining) statement; \ | 38 if (FLAG_trace_inlining) statement; \ |
| 38 } while (false) | 39 } while (false) |
| 39 | 40 |
| 40 | 41 |
| 41 // Test if a call is recursive by looking in the deoptimization environment. | 42 // Test if a call is recursive by looking in the deoptimization environment. |
| 42 static bool IsCallRecursive(const Function& function, Definition* call) { | 43 static bool IsCallRecursive(const Function& function, Definition* call) { |
| 43 Environment* env = call->env(); | 44 Environment* env = call->env(); |
| 44 while (env != NULL) { | 45 while (env != NULL) { |
| 45 if (function.raw() == env->function().raw()) return true; | 46 if (function.raw() == env->function().raw()) return true; |
| 46 env = env->outer(); | 47 env = env->outer(); |
| 47 } | 48 } |
| 48 return false; | 49 return false; |
| 49 } | 50 } |
| 50 | 51 |
| 51 | 52 |
| 52 // TODO(zerny): Remove the following classes once we have moved the label/join | 53 // TODO(zerny): Remove the ChildrenVisitor and SourceLabelResetter once we have |
| 53 // map for control flow out of the AST an into the flow graph builder. | 54 // moved the label/join map for control flow out of the AST an into the flow |
| 55 // graph builder. | |
| 54 | 56 |
| 55 // Default visitor to traverse child nodes. | 57 // Default visitor to traverse child nodes. |
| 56 class ChildrenVisitor : public AstNodeVisitor { | 58 class ChildrenVisitor : public AstNodeVisitor { |
| 57 public: | 59 public: |
| 58 ChildrenVisitor() { } | 60 ChildrenVisitor() { } |
| 59 #define DEFINE_VISIT(type, name) \ | 61 #define DEFINE_VISIT(type, name) \ |
| 60 virtual void Visit##type(type* node) { node->VisitChildren(this); } | 62 virtual void Visit##type(type* node) { node->VisitChildren(this); } |
| 61 NODE_LIST(DEFINE_VISIT); | 63 NODE_LIST(DEFINE_VISIT); |
| 62 #undef DEFINE_VISIT | 64 #undef DEFINE_VISIT |
| 63 }; | 65 }; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 90 } | 92 } |
| 91 void Reset(AstNode* node, SourceLabel* lbl) { | 93 void Reset(AstNode* node, SourceLabel* lbl) { |
| 92 node->VisitChildren(this); | 94 node->VisitChildren(this); |
| 93 if (lbl == NULL) return; | 95 if (lbl == NULL) return; |
| 94 lbl->join_for_break_ = NULL; | 96 lbl->join_for_break_ = NULL; |
| 95 lbl->join_for_continue_ = NULL; | 97 lbl->join_for_continue_ = NULL; |
| 96 } | 98 } |
| 97 }; | 99 }; |
| 98 | 100 |
| 99 | 101 |
| 102 // Helper to create a parameter stub from an actual argument. | |
| 103 static Definition* CreateParameterStub(intptr_t i, | |
| 104 Value* argument, | |
| 105 FlowGraph* graph) { | |
| 106 ConstantInstr* constant = argument->definition()->AsConstant(); | |
| 107 if (constant != NULL) { | |
| 108 return new ConstantInstr(constant->value()); | |
| 109 } else { | |
| 110 return new ParameterInstr(i, graph->graph_entry()); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 | |
| 115 // Helper to get the default value of a formal parameter. | |
| 116 static ConstantInstr* GetDefaultValue(intptr_t i, | |
| 117 const ParsedFunction& parsed_function) { | |
| 118 return new ConstantInstr(Object::ZoneHandle( | |
| 119 parsed_function.default_parameter_values().At(i))); | |
| 120 } | |
| 121 | |
| 122 | |
| 123 // Pair of an argument name and its value. | |
| 124 struct NamedArgument : ZoneAllocated { | |
|
Kevin Millikin (Google)
2012/10/23 11:34:42
It should work to make this ValueObject.
zerny-google
2012/10/23 13:03:42
Done.
| |
| 125 public: | |
| 126 String* name; | |
| 127 Value* value; | |
| 128 NamedArgument(String* name, Value* value) | |
| 129 : name(name), value(value) { } | |
| 130 }; | |
| 131 | |
| 132 | |
| 100 // A collection of call sites to consider for inlining. | 133 // A collection of call sites to consider for inlining. |
| 101 class CallSites : public FlowGraphVisitor { | 134 class CallSites : public FlowGraphVisitor { |
| 102 public: | 135 public: |
| 103 explicit CallSites(FlowGraph* flow_graph) | 136 explicit CallSites(FlowGraph* flow_graph) |
| 104 : FlowGraphVisitor(flow_graph->postorder()), // We don't use this order. | 137 : FlowGraphVisitor(flow_graph->postorder()), // We don't use this order. |
| 105 static_calls_(), | 138 static_calls_(), |
| 106 closure_calls_(), | 139 closure_calls_(), |
| 107 instance_calls_() { } | 140 instance_calls_() { } |
| 108 | 141 |
| 109 GrowableArray<StaticCallInstr*>* static_calls() { | 142 GrowableArray<StaticCallInstr*>* static_calls() { |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 | 240 |
| 208 bool inlined() const { return inlined_; } | 241 bool inlined() const { return inlined_; } |
| 209 | 242 |
| 210 double GrowthFactor() const { | 243 double GrowthFactor() const { |
| 211 return static_cast<double>(inlined_size_) / | 244 return static_cast<double>(inlined_size_) / |
| 212 static_cast<double>(initial_size_); | 245 static_cast<double>(initial_size_); |
| 213 } | 246 } |
| 214 | 247 |
| 215 private: | 248 private: |
| 216 bool TryInlining(const Function& function, | 249 bool TryInlining(const Function& function, |
| 250 const Array& argument_names, | |
| 217 GrowableArray<Value*>* arguments, | 251 GrowableArray<Value*>* arguments, |
| 218 Definition* call) { | 252 Definition* call) { |
| 219 TRACE_INLINING(OS::Print(" => %s (deopt count %d)\n", | 253 TRACE_INLINING(OS::Print(" => %s (deopt count %d)\n", |
| 220 function.ToCString(), | 254 function.ToCString(), |
| 221 function.deoptimization_counter())); | 255 function.deoptimization_counter())); |
| 222 | 256 |
| 223 // Abort if the inlinable bit on the function is low. | 257 // Abort if the inlinable bit on the function is low. |
| 224 if (!function.IsInlineable()) { | 258 if (!function.IsInlineable()) { |
| 225 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); | 259 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); |
| 226 return false; | 260 return false; |
| 227 } | 261 } |
| 228 | 262 |
| 229 // Abort if the callee has optional parameters. | |
| 230 if (function.HasOptionalParameters()) { | |
| 231 TRACE_INLINING(OS::Print(" Bailout: optional parameters\n")); | |
| 232 return false; | |
| 233 } | |
| 234 | |
| 235 // Assuming no optional parameters the actual/formal count should match. | |
| 236 ASSERT(arguments->length() == function.num_fixed_parameters()); | |
| 237 | |
| 238 // Abort if this function has deoptimized too much. | 263 // Abort if this function has deoptimized too much. |
| 239 if (function.deoptimization_counter() >= | 264 if (function.deoptimization_counter() >= |
| 240 FLAG_deoptimization_counter_threshold) { | 265 FLAG_deoptimization_counter_threshold) { |
| 241 function.set_is_inlinable(false); | 266 function.set_is_inlinable(false); |
| 242 TRACE_INLINING(OS::Print(" Bailout: deoptimization threshold\n")); | 267 TRACE_INLINING(OS::Print(" Bailout: deoptimization threshold\n")); |
| 243 return false; | 268 return false; |
| 244 } | 269 } |
| 245 | 270 |
| 246 // Abort if this is a recursive occurrence. | 271 // Abort if this is a recursive occurrence. |
| 247 if (IsCallRecursive(function, call)) { | 272 if (IsCallRecursive(function, call)) { |
| 248 function.set_is_inlinable(false); | 273 function.set_is_inlinable(false); |
| 249 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); | 274 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); |
| 250 return false; | 275 return false; |
| 251 } | 276 } |
| 252 | 277 |
| 253 // Abort if the callee has an intrinsic translation. | 278 // Abort if the callee has an intrinsic translation. |
| 254 if (Intrinsifier::CanIntrinsify(function)) { | 279 if (Intrinsifier::CanIntrinsify(function)) { |
| 255 function.set_is_inlinable(false); | 280 function.set_is_inlinable(false); |
| 256 TRACE_INLINING(OS::Print(" Bailout: can intrinsify\n")); | 281 TRACE_INLINING(OS::Print(" Bailout: can intrinsify\n")); |
| 257 return false; | 282 return false; |
| 258 } | 283 } |
| 259 | 284 |
| 285 // Abort if we are running legacy support for optional parameters. | |
| 286 if (!FLAG_reject_named_argument_as_positional && | |
| 287 function.HasOptionalPositionalParameters() && | |
| 288 (!argument_names.IsNull() && (argument_names.Length() > 0))) { | |
| 289 function.set_is_inlinable(false); | |
| 290 TRACE_INLINING(OS::Print( | |
| 291 " Bailout: named optional positional parameter\n")); | |
| 292 return false; | |
| 293 } | |
| 294 | |
| 260 Isolate* isolate = Isolate::Current(); | 295 Isolate* isolate = Isolate::Current(); |
| 261 // Save and clear IC data. | 296 // Save and clear IC data. |
| 262 const Array& prev_ic_data = Array::Handle(isolate->ic_data_array()); | 297 const Array& prev_ic_data = Array::Handle(isolate->ic_data_array()); |
| 263 isolate->set_ic_data_array(Array::null()); | 298 isolate->set_ic_data_array(Array::null()); |
| 264 // Save and clear deopt id. | 299 // Save and clear deopt id. |
| 265 const intptr_t prev_deopt_id = isolate->deopt_id(); | 300 const intptr_t prev_deopt_id = isolate->deopt_id(); |
| 266 isolate->set_deopt_id(0); | 301 isolate->set_deopt_id(0); |
| 267 // Install bailout jump. | 302 // Install bailout jump. |
| 268 LongJump* base = isolate->long_jump_base(); | 303 LongJump* base = isolate->long_jump_base(); |
| 269 LongJump jump; | 304 LongJump jump; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 300 // Abort if the callee graph contains control flow. | 335 // Abort if the callee graph contains control flow. |
| 301 if (!FLAG_inline_control_flow && | 336 if (!FLAG_inline_control_flow && |
| 302 (callee_graph->preorder().length() != 2)) { | 337 (callee_graph->preorder().length() != 2)) { |
| 303 function.set_is_inlinable(false); | 338 function.set_is_inlinable(false); |
| 304 isolate->set_long_jump_base(base); | 339 isolate->set_long_jump_base(base); |
| 305 isolate->set_ic_data_array(prev_ic_data.raw()); | 340 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 306 TRACE_INLINING(OS::Print(" Bailout: control flow\n")); | 341 TRACE_INLINING(OS::Print(" Bailout: control flow\n")); |
| 307 return false; | 342 return false; |
| 308 } | 343 } |
| 309 | 344 |
| 345 // The parameter stubs are a copy of the actual arguments providing | |
| 346 // concrete information about the values, for example constant values, | |
| 347 // without linking between the caller and callee graphs. | |
| 348 // TODO(zerny): Put more information in the stubs, eg, type information. | |
| 349 GrowableArray<Definition*> param_stubs(function.NumParameters()); | |
| 350 | |
| 351 // Create a parameter stub for each fixed positional parameter. | |
| 352 for (intptr_t i = 0; i < function.num_fixed_parameters(); ++i) { | |
| 353 param_stubs.Add(CreateParameterStub(i, (*arguments)[i], callee_graph)); | |
| 354 } | |
| 355 | |
| 356 // If the callee has optional parameters, rebuild the argument and stub | |
| 357 // arrays so that actual arguments are in one-to-one with the formal | |
| 358 // parameters. | |
| 359 if (function.HasOptionalParameters()) { | |
| 360 TRACE_INLINING(OS::Print(" adjusting for optional parameters\n")); | |
| 361 AdjustForOptionalParameters(*parsed_function, | |
| 362 argument_names, | |
| 363 arguments, | |
| 364 ¶m_stubs, | |
| 365 callee_graph); | |
| 366 // Add a bogus parameter at the end for the (unused) argument descriptor | |
| 367 // slot. The parser allocates an extra slot between locals and | |
| 368 // parameters to hold the argument descriptor in case it escapes. We | |
| 369 // currently bailout if there are argument test expressions or escaping | |
| 370 // variables so this parameter and the stack slot are not used. | |
| 371 if (parsed_function->GetSavedArgumentsDescriptorVar() != NULL) { | |
| 372 param_stubs.Add(new ParameterInstr( | |
| 373 function.NumParameters(), callee_graph->graph_entry())); | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 // After treating optional parameters the actual/formal count must match. | |
| 378 ASSERT(arguments->length() == function.NumParameters()); | |
| 379 ASSERT(param_stubs.length() == callee_graph->parameter_count()); | |
| 380 | |
| 310 { | 381 { |
| 311 TimerScope timer(FLAG_compiler_stats, | 382 TimerScope timer(FLAG_compiler_stats, |
| 312 &CompilerStats::graphinliner_ssa_timer, | 383 &CompilerStats::graphinliner_ssa_timer, |
| 313 isolate); | 384 isolate); |
| 314 // Compute SSA on the callee graph, catching bailouts. | 385 // Compute SSA on the callee graph, catching bailouts. |
| 315 callee_graph->ComputeSSA(next_ssa_temp_index_); | 386 callee_graph->ComputeSSA(next_ssa_temp_index_, ¶m_stubs); |
| 316 callee_graph->ComputeUseLists(); | 387 callee_graph->ComputeUseLists(); |
| 317 } | 388 } |
| 318 | 389 |
| 319 { | 390 { |
| 320 TimerScope timer(FLAG_compiler_stats, | 391 TimerScope timer(FLAG_compiler_stats, |
| 321 &CompilerStats::graphinliner_opt_timer, | 392 &CompilerStats::graphinliner_opt_timer, |
| 322 isolate); | 393 isolate); |
| 323 // TODO(zerny): Do more optimization passes on the callee graph. | 394 // TODO(zerny): Do more optimization passes on the callee graph. |
| 324 FlowGraphOptimizer optimizer(callee_graph); | 395 FlowGraphOptimizer optimizer(callee_graph); |
| 325 optimizer.ApplyICData(); | 396 optimizer.ApplyICData(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 359 caller_graph_->InlineCall(call, callee_graph); | 430 caller_graph_->InlineCall(call, callee_graph); |
| 360 next_ssa_temp_index_ = caller_graph_->max_virtual_register_number(); | 431 next_ssa_temp_index_ = caller_graph_->max_virtual_register_number(); |
| 361 | 432 |
| 362 // Remove push arguments of the call. | 433 // Remove push arguments of the call. |
| 363 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 434 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 364 PushArgumentInstr* push = call->ArgumentAt(i); | 435 PushArgumentInstr* push = call->ArgumentAt(i); |
| 365 push->ReplaceUsesWith(push->value()->definition()); | 436 push->ReplaceUsesWith(push->value()->definition()); |
| 366 push->RemoveFromGraph(); | 437 push->RemoveFromGraph(); |
| 367 } | 438 } |
| 368 | 439 |
| 369 // Replace formal parameters with actuals. | 440 // Replace each stub with the actual argument or the caller's constant. |
| 370 intptr_t arg_index = 0; | 441 // Nulls denote optional parameters for which no actual was given. |
| 442 for (intptr_t i = 0; i < arguments->length(); ++i) { | |
| 443 Definition* stub = param_stubs[i]; | |
| 444 Value* actual = (*arguments)[i]; | |
| 445 if (actual != NULL) stub->ReplaceUsesWith(actual->definition()); | |
| 446 } | |
| 447 | |
| 448 // Replace remaining constants with uses by constants in the caller's | |
| 449 // initial definitions. | |
| 371 GrowableArray<Definition*>* defns = | 450 GrowableArray<Definition*>* defns = |
| 372 callee_graph->graph_entry()->initial_definitions(); | 451 callee_graph->graph_entry()->initial_definitions(); |
| 373 for (intptr_t i = 0; i < defns->length(); ++i) { | 452 for (intptr_t i = 0; i < defns->length(); ++i) { |
| 374 ParameterInstr* param = (*defns)[i]->AsParameter(); | 453 ConstantInstr* constant = (*defns)[i]->AsConstant(); |
| 375 if (param != NULL) { | 454 if (constant == NULL || |
| 376 param->ReplaceUsesWith((*arguments)[arg_index++]->definition()); | 455 ((constant->input_use_list() == NULL) && |
| 456 (constant->env_use_list() == NULL))) { | |
| 457 continue; | |
| 377 } | 458 } |
| 459 constant->ReplaceUsesWith( | |
| 460 caller_graph_->AddConstantToInitialDefinitions(constant->value())); | |
| 378 } | 461 } |
| 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()); | |
| 384 } | 462 } |
| 385 | 463 |
| 386 TRACE_INLINING(OS::Print(" Success\n")); | 464 TRACE_INLINING(OS::Print(" Success\n")); |
| 387 | 465 |
| 388 // Add the function to the cache. | 466 // Add the function to the cache. |
| 389 if (!in_cache) function_cache.Add(parsed_function); | 467 if (!in_cache) function_cache.Add(parsed_function); |
| 390 | 468 |
| 391 // Check that inlining maintains use lists. | 469 // Check that inlining maintains use lists. |
| 392 DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); | 470 DEBUG_ASSERT(!FLAG_verify_compiler || caller_graph_->ValidateUseLists()); |
| 393 | 471 |
| 394 // Build succeeded so we restore the bailout jump. | 472 // Build succeeded so we restore the bailout jump. |
| 395 inlined_ = true; | 473 inlined_ = true; |
| 396 inlined_size_ += size; | 474 inlined_size_ += size; |
| 397 isolate->set_long_jump_base(base); | 475 isolate->set_long_jump_base(base); |
| 398 isolate->set_deopt_id(prev_deopt_id); | 476 isolate->set_deopt_id(prev_deopt_id); |
| 399 isolate->set_ic_data_array(prev_ic_data.raw()); | 477 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 400 return true; | 478 return true; |
| 401 } else { | 479 } else { |
| 402 Error& error = Error::Handle(); | 480 Error& error = Error::Handle(); |
| 403 error = isolate->object_store()->sticky_error(); | 481 error = isolate->object_store()->sticky_error(); |
| 404 isolate->object_store()->clear_sticky_error(); | 482 isolate->object_store()->clear_sticky_error(); |
| 405 isolate->set_long_jump_base(base); | 483 isolate->set_long_jump_base(base); |
| 406 isolate->set_deopt_id(prev_deopt_id); | 484 isolate->set_deopt_id(prev_deopt_id); |
| 407 isolate->set_ic_data_array(prev_ic_data.raw()); | 485 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 408 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); | 486 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); |
| 409 return false; | 487 return false; |
| 410 } | 488 } |
| 411 } | 489 } |
| 412 | 490 |
| 413 // Parse a function reusing the cache if possible. Returns true if the | 491 // Parse a function reusing the cache if possible. |
| 414 // function was in the cache. | |
| 415 ParsedFunction* ParseFunction(const Function& function, bool* in_cache) { | 492 ParsedFunction* ParseFunction(const Function& function, bool* in_cache) { |
| 416 // TODO(zerny): Use a hash map for the cache. | 493 // TODO(zerny): Use a hash map for the cache. |
| 417 for (intptr_t i = 0; i < function_cache.length(); ++i) { | 494 for (intptr_t i = 0; i < function_cache.length(); ++i) { |
| 418 ParsedFunction* parsed_function = function_cache[i]; | 495 ParsedFunction* parsed_function = function_cache[i]; |
| 419 if (parsed_function->function().raw() == function.raw()) { | 496 if (parsed_function->function().raw() == function.raw()) { |
| 420 *in_cache = true; | 497 *in_cache = true; |
| 421 SourceLabelResetter reset; | 498 SourceLabelResetter reset; |
| 422 parsed_function->node_sequence()->Visit(&reset); | 499 parsed_function->node_sequence()->Visit(&reset); |
| 423 return parsed_function; | 500 return parsed_function; |
| 424 } | 501 } |
| 425 } | 502 } |
| 426 *in_cache = false; | 503 *in_cache = false; |
| 427 ParsedFunction* parsed_function = new ParsedFunction(function); | 504 ParsedFunction* parsed_function = new ParsedFunction(function); |
| 428 Parser::ParseFunction(parsed_function); | 505 Parser::ParseFunction(parsed_function); |
| 429 parsed_function->AllocateVariables(); | 506 parsed_function->AllocateVariables(); |
| 430 return parsed_function; | 507 return parsed_function; |
| 431 } | 508 } |
| 432 | 509 |
| 433 void InlineStaticCalls() { | 510 void InlineStaticCalls() { |
| 434 const GrowableArray<StaticCallInstr*>& calls = | 511 const GrowableArray<StaticCallInstr*>& calls = |
| 435 *inlining_call_sites_->static_calls(); | 512 *inlining_call_sites_->static_calls(); |
| 436 TRACE_INLINING(OS::Print(" Static Calls (%d)\n", calls.length())); | 513 TRACE_INLINING(OS::Print(" Static Calls (%d)\n", calls.length())); |
| 437 for (intptr_t i = 0; i < calls.length(); ++i) { | 514 for (intptr_t i = 0; i < calls.length(); ++i) { |
| 438 StaticCallInstr* call = calls[i]; | 515 StaticCallInstr* call = calls[i]; |
| 439 GrowableArray<Value*> arguments(call->ArgumentCount()); | 516 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 440 for (int i = 0; i < call->ArgumentCount(); ++i) { | 517 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 441 arguments.Add(call->ArgumentAt(i)->value()); | 518 arguments.Add(call->ArgumentAt(i)->value()); |
| 442 } | 519 } |
| 443 TryInlining(call->function(), &arguments, call); | 520 TryInlining(call->function(), call->argument_names(), &arguments, call); |
| 444 } | 521 } |
| 445 } | 522 } |
| 446 | 523 |
| 447 void InlineClosureCalls() { | 524 void InlineClosureCalls() { |
| 448 const GrowableArray<ClosureCallInstr*>& calls = | 525 const GrowableArray<ClosureCallInstr*>& calls = |
| 449 *inlining_call_sites_->closure_calls(); | 526 *inlining_call_sites_->closure_calls(); |
| 450 TRACE_INLINING(OS::Print(" Closure Calls (%d)\n", calls.length())); | 527 TRACE_INLINING(OS::Print(" Closure Calls (%d)\n", calls.length())); |
| 451 for (intptr_t i = 0; i < calls.length(); ++i) { | 528 for (intptr_t i = 0; i < calls.length(); ++i) { |
| 452 ClosureCallInstr* call = calls[i]; | 529 ClosureCallInstr* call = calls[i]; |
| 453 // Find the closure of the callee. | 530 // Find the closure of the callee. |
| 454 ASSERT(call->ArgumentCount() > 0); | 531 ASSERT(call->ArgumentCount() > 0); |
| 455 const CreateClosureInstr* closure = | 532 const CreateClosureInstr* closure = |
| 456 call->ArgumentAt(0)->value()->definition()->AsCreateClosure(); | 533 call->ArgumentAt(0)->value()->definition()->AsCreateClosure(); |
| 457 if (closure == NULL) { | 534 if (closure == NULL) { |
| 458 TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n")); | 535 TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n")); |
| 459 continue; | 536 continue; |
| 460 } | 537 } |
| 461 GrowableArray<Value*> arguments(call->ArgumentCount() - 1); | 538 GrowableArray<Value*> arguments(call->ArgumentCount() - 1); |
| 462 for (int i = 1; i < call->ArgumentCount(); ++i) { | 539 for (int i = 1; i < call->ArgumentCount(); ++i) { |
| 463 arguments.Add(call->ArgumentAt(i)->value()); | 540 arguments.Add(call->ArgumentAt(i)->value()); |
| 464 } | 541 } |
| 465 TryInlining(closure->function(), &arguments, call); | 542 TryInlining(closure->function(), |
| 543 call->argument_names(), | |
| 544 &arguments, | |
| 545 call); | |
| 466 } | 546 } |
| 467 } | 547 } |
| 468 | 548 |
| 469 void InlineInstanceCalls() { | 549 void InlineInstanceCalls() { |
| 470 const GrowableArray<PolymorphicInstanceCallInstr*>& calls = | 550 const GrowableArray<PolymorphicInstanceCallInstr*>& calls = |
| 471 *inlining_call_sites_->instance_calls(); | 551 *inlining_call_sites_->instance_calls(); |
| 472 TRACE_INLINING(OS::Print(" Polymorphic Instance Calls (%d)\n", | 552 TRACE_INLINING(OS::Print(" Polymorphic Instance Calls (%d)\n", |
| 473 calls.length())); | 553 calls.length())); |
| 474 for (intptr_t i = 0; i < calls.length(); ++i) { | 554 for (intptr_t i = 0; i < calls.length(); ++i) { |
| 475 PolymorphicInstanceCallInstr* instr = calls[i]; | 555 PolymorphicInstanceCallInstr* instr = calls[i]; |
| 476 const ICData& ic_data = instr->ic_data(); | 556 const ICData& ic_data = instr->ic_data(); |
| 477 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | 557 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
| 478 if (instr->with_checks()) { | 558 if (instr->with_checks()) { |
| 479 TRACE_INLINING(OS::Print( | 559 TRACE_INLINING(OS::Print( |
| 480 " => %s (deopt count %d)\n Bailout: %"Pd" checks\n", | 560 " => %s (deopt count %d)\n Bailout: %"Pd" checks\n", |
| 481 target.ToCString(), | 561 target.ToCString(), |
| 482 target.deoptimization_counter(), | 562 target.deoptimization_counter(), |
| 483 ic_data.NumberOfChecks())); | 563 ic_data.NumberOfChecks())); |
| 484 continue; | 564 continue; |
| 485 } | 565 } |
| 486 GrowableArray<Value*> arguments(instr->ArgumentCount()); | 566 GrowableArray<Value*> arguments(instr->ArgumentCount()); |
| 487 for (int i = 0; i < instr->ArgumentCount(); ++i) { | 567 for (int i = 0; i < instr->ArgumentCount(); ++i) { |
| 488 arguments.Add(instr->ArgumentAt(i)->value()); | 568 arguments.Add(instr->ArgumentAt(i)->value()); |
| 489 } | 569 } |
| 490 TryInlining(target, &arguments, instr); | 570 TryInlining(target, |
| 571 instr->instance_call()->argument_names(), | |
| 572 &arguments, | |
| 573 instr); | |
| 491 } | 574 } |
| 492 } | 575 } |
| 493 | 576 |
| 577 void AdjustForOptionalParameters(const ParsedFunction& parsed_function, | |
| 578 const Array& argument_names, | |
| 579 GrowableArray<Value*>* arguments, | |
| 580 GrowableArray<Definition*>* param_stubs, | |
| 581 FlowGraph* callee_graph) { | |
| 582 const Function& function = parsed_function.function(); | |
| 583 // The language and this code does not support both optional positional | |
| 584 // and optional named parameters for the same function. | |
| 585 ASSERT(!function.HasOptionalPositionalParameters() || | |
| 586 !function.HasOptionalNamedParameters()); | |
| 587 | |
| 588 intptr_t arg_count = arguments->length(); | |
| 589 intptr_t param_count = function.NumParameters(); | |
| 590 intptr_t fixed_param_count = function.num_fixed_parameters(); | |
| 591 ASSERT(fixed_param_count <= arg_count); | |
| 592 ASSERT(arg_count <= param_count); | |
| 593 | |
| 594 if (function.HasOptionalPositionalParameters()) { | |
| 595 // Create a stub for each optional positional parameters with an actual. | |
| 596 for (intptr_t i = fixed_param_count; i < arg_count; ++i) { | |
| 597 param_stubs->Add(CreateParameterStub(i, (*arguments)[i], callee_graph)); | |
| 598 } | |
| 599 ASSERT(function.NumOptionalPositionalParameters() == | |
| 600 (param_count - fixed_param_count)); | |
| 601 // For each optional positional parameter without an actual, add its | |
| 602 // default value. | |
| 603 for (intptr_t i = arg_count - fixed_param_count; | |
|
Kevin Millikin (Google)
2012/10/23 11:34:42
I think it's a bit weird to adjust the initial val
zerny-google
2012/10/23 13:03:42
Done.
| |
| 604 i < param_count - fixed_param_count; | |
| 605 ++i) { | |
| 606 const Object& object = | |
| 607 Object::ZoneHandle( | |
| 608 parsed_function.default_parameter_values().At(i)); | |
| 609 ConstantInstr* constant = new ConstantInstr(object); | |
| 610 arguments->Add(NULL); | |
| 611 param_stubs->Add(constant); | |
| 612 } | |
| 613 return; | |
| 614 } | |
| 615 | |
| 616 ASSERT(function.HasOptionalNamedParameters()); | |
| 617 | |
| 618 // Passed arguments must match fixed parameters plus named arguments. | |
| 619 intptr_t argument_names_count = | |
| 620 (argument_names.IsNull()) ? 0 : argument_names.Length(); | |
| 621 ASSERT(arg_count == (fixed_param_count + argument_names_count)); | |
| 622 | |
| 623 // Fast path when no optional named parameters are given. | |
| 624 if (argument_names_count == 0) { | |
| 625 for (intptr_t i = 0; i < param_count - fixed_param_count; i++) { | |
| 626 arguments->Add(NULL); | |
| 627 param_stubs->Add(GetDefaultValue(i, parsed_function)); | |
| 628 } | |
| 629 return; | |
| 630 } | |
| 631 | |
| 632 // Otherwise, build a collection of name/argument pairs. | |
| 633 GrowableArray<NamedArgument*> named_args(argument_names_count); | |
| 634 for (intptr_t i = 0; i < argument_names.Length(); ++i) { | |
| 635 String& arg_name = String::Handle(Isolate::Current()); | |
| 636 arg_name ^= argument_names.At(i); | |
| 637 named_args.Add( | |
| 638 new NamedArgument(&arg_name, (*arguments)[i + fixed_param_count])); | |
| 639 } | |
| 640 | |
| 641 // Truncate the arguments array to just fixed parameters. | |
| 642 arguments->TruncateTo(fixed_param_count); | |
| 643 | |
| 644 // For each optional named parameter, add the actual argument or its | |
| 645 // default if no argument is passed. | |
| 646 for (intptr_t i = fixed_param_count; i < param_count; i++) { | |
|
Kevin Millikin (Google)
2012/10/23 11:34:42
There's a mix of ++i and i++ in loops in this func
zerny-google
2012/10/23 13:03:42
Done.
| |
| 647 String& param_name = String::Handle(function.ParameterNameAt(i)); | |
| 648 // Search for and add the named argument. | |
| 649 Value* arg = NULL; | |
| 650 for (intptr_t j = 0; j < named_args.length(); j++) { | |
| 651 if (param_name.Equals(*named_args[j]->name)) { | |
| 652 arg = named_args[j]->value; | |
| 653 break; | |
| 654 } | |
| 655 } | |
| 656 arguments->Add(arg); | |
| 657 // Create a stub parameter for the named argument or its default. | |
| 658 if (arg != NULL) { | |
| 659 param_stubs->Add(CreateParameterStub(i, arg, callee_graph)); | |
| 660 } else { | |
| 661 param_stubs->Add( | |
| 662 GetDefaultValue(i - fixed_param_count, parsed_function)); | |
| 663 } | |
| 664 } | |
| 665 } | |
| 666 | |
| 667 | |
| 494 FlowGraph* caller_graph_; | 668 FlowGraph* caller_graph_; |
| 495 intptr_t next_ssa_temp_index_; | 669 intptr_t next_ssa_temp_index_; |
| 496 bool inlined_; | 670 bool inlined_; |
| 497 intptr_t initial_size_; | 671 intptr_t initial_size_; |
| 498 intptr_t inlined_size_; | 672 intptr_t inlined_size_; |
| 499 intptr_t inlining_depth_; | 673 intptr_t inlining_depth_; |
| 500 CallSites* collected_call_sites_; | 674 CallSites* collected_call_sites_; |
| 501 CallSites* inlining_call_sites_; | 675 CallSites* inlining_call_sites_; |
| 502 GrowableArray<ParsedFunction*> function_cache; | 676 GrowableArray<ParsedFunction*> function_cache; |
| 503 | 677 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 535 OS::Print("After Inlining of %s\n", flow_graph_-> | 709 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 536 parsed_function().function().ToFullyQualifiedCString()); | 710 parsed_function().function().ToFullyQualifiedCString()); |
| 537 FlowGraphPrinter printer(*flow_graph_); | 711 FlowGraphPrinter printer(*flow_graph_); |
| 538 printer.PrintBlocks(); | 712 printer.PrintBlocks(); |
| 539 } | 713 } |
| 540 } | 714 } |
| 541 } | 715 } |
| 542 } | 716 } |
| 543 | 717 |
| 544 } // namespace dart | 718 } // namespace dart |
| OLD | NEW |