OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 #if !defined(DART_PRECOMPILED_RUNTIME) | 4 #if !defined(DART_PRECOMPILED_RUNTIME) |
5 #include "vm/flow_graph_inliner.h" | 5 #include "vm/flow_graph_inliner.h" |
6 | 6 |
7 #include "vm/aot_optimizer.h" | 7 #include "vm/aot_optimizer.h" |
8 #include "vm/precompiler.h" | 8 #include "vm/precompiler.h" |
9 #include "vm/block_scheduler.h" | 9 #include "vm/block_scheduler.h" |
10 #include "vm/branch_optimizer.h" | 10 #include "vm/branch_optimizer.h" |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
170 continue; | 170 continue; |
171 } | 171 } |
172 if (current->IsPolymorphicInstanceCall()) { | 172 if (current->IsPolymorphicInstanceCall()) { |
173 PolymorphicInstanceCallInstr* call = | 173 PolymorphicInstanceCallInstr* call = |
174 current->AsPolymorphicInstanceCall(); | 174 current->AsPolymorphicInstanceCall(); |
175 // These checks make sure that the number of call-sites counted does | 175 // These checks make sure that the number of call-sites counted does |
176 // not change relative to the time when the current set of inlining | 176 // not change relative to the time when the current set of inlining |
177 // parameters was fixed. | 177 // parameters was fixed. |
178 // TODO(fschneider): Determine new heuristic parameters that avoid | 178 // TODO(fschneider): Determine new heuristic parameters that avoid |
179 // these checks entirely. | 179 // these checks entirely. |
180 if (!call->HasSingleRecognizedTarget() && | 180 if (!call->targets().HasSingleRecognizedTarget() && |
181 (call->instance_call()->token_kind() != Token::kEQ)) { | 181 (call->instance_call()->token_kind() != Token::kEQ)) { |
182 ++call_site_count_; | 182 ++call_site_count_; |
183 } | 183 } |
184 } | 184 } |
185 } | 185 } |
186 } | 186 } |
187 } | 187 } |
188 | 188 |
189 intptr_t call_site_count() const { return call_site_count_; } | 189 intptr_t call_site_count() const { return call_site_count_; } |
190 intptr_t instruction_count() const { return instruction_count_; } | 190 intptr_t instruction_count() const { return instruction_count_; } |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
283 intptr_t instance_call_start_ix) { | 283 intptr_t instance_call_start_ix) { |
284 const intptr_t num_static_calls = | 284 const intptr_t num_static_calls = |
285 static_calls_.length() - static_call_start_ix; | 285 static_calls_.length() - static_call_start_ix; |
286 const intptr_t num_instance_calls = | 286 const intptr_t num_instance_calls = |
287 instance_calls_.length() - instance_call_start_ix; | 287 instance_calls_.length() - instance_call_start_ix; |
288 | 288 |
289 intptr_t max_count = 0; | 289 intptr_t max_count = 0; |
290 GrowableArray<intptr_t> instance_call_counts(num_instance_calls); | 290 GrowableArray<intptr_t> instance_call_counts(num_instance_calls); |
291 for (intptr_t i = 0; i < num_instance_calls; ++i) { | 291 for (intptr_t i = 0; i < num_instance_calls; ++i) { |
292 const intptr_t aggregate_count = | 292 const intptr_t aggregate_count = |
293 instance_calls_[i + instance_call_start_ix] | 293 instance_calls_[i + instance_call_start_ix].call->CallCount(); |
294 .call->ic_data() | |
295 .AggregateCount(); | |
296 instance_call_counts.Add(aggregate_count); | 294 instance_call_counts.Add(aggregate_count); |
297 if (aggregate_count > max_count) max_count = aggregate_count; | 295 if (aggregate_count > max_count) max_count = aggregate_count; |
298 } | 296 } |
299 | 297 |
300 GrowableArray<intptr_t> static_call_counts(num_static_calls); | 298 GrowableArray<intptr_t> static_call_counts(num_static_calls); |
301 for (intptr_t i = 0; i < num_static_calls; ++i) { | 299 for (intptr_t i = 0; i < num_static_calls; ++i) { |
302 intptr_t aggregate_count = 0; | 300 intptr_t aggregate_count = 0; |
303 if (static_calls_[i + static_call_start_ix].call->ic_data() == NULL) { | 301 if (static_calls_[i + static_call_start_ix].call->ic_data() == NULL) { |
304 aggregate_count = 0; | 302 aggregate_count = 0; |
305 } else { | 303 } else { |
(...skipping 30 matching lines...) Expand all Loading... | |
336 Function& target = Function::ZoneHandle(); | 334 Function& target = Function::ZoneHandle(); |
337 for (BlockIterator block_it = graph->postorder_iterator(); !block_it.Done(); | 335 for (BlockIterator block_it = graph->postorder_iterator(); !block_it.Done(); |
338 block_it.Advance()) { | 336 block_it.Advance()) { |
339 for (ForwardInstructionIterator it(block_it.Current()); !it.Done(); | 337 for (ForwardInstructionIterator it(block_it.Current()); !it.Done(); |
340 it.Advance()) { | 338 it.Advance()) { |
341 Instruction* current = it.Current(); | 339 Instruction* current = it.Current(); |
342 Definition* call = NULL; | 340 Definition* call = NULL; |
343 if (current->IsPolymorphicInstanceCall()) { | 341 if (current->IsPolymorphicInstanceCall()) { |
344 PolymorphicInstanceCallInstr* instance_call = | 342 PolymorphicInstanceCallInstr* instance_call = |
345 current->AsPolymorphicInstanceCall(); | 343 current->AsPolymorphicInstanceCall(); |
346 target = instance_call->ic_data().GetTargetAt(0); | 344 target ^= instance_call->targets().FirstTarget().raw(); |
347 call = instance_call; | 345 call = instance_call; |
348 } else if (current->IsStaticCall()) { | 346 } else if (current->IsStaticCall()) { |
349 StaticCallInstr* static_call = current->AsStaticCall(); | 347 StaticCallInstr* static_call = current->AsStaticCall(); |
350 target = static_call->function().raw(); | 348 target ^= static_call->function().raw(); |
351 call = static_call; | 349 call = static_call; |
352 } else if (current->IsClosureCall()) { | 350 } else if (current->IsClosureCall()) { |
353 // TODO(srdjan): Add data for closure calls. | 351 // TODO(srdjan): Add data for closure calls. |
354 } | 352 } |
355 if (call != NULL) { | 353 if (call != NULL) { |
356 inlined_info->Add( | 354 inlined_info->Add( |
357 InlinedInfo(caller, &target, depth + 1, call, "Too deep")); | 355 InlinedInfo(caller, &target, depth + 1, call, "Too deep")); |
358 } | 356 } |
359 } | 357 } |
360 } | 358 } |
(...skipping 20 matching lines...) Expand all Loading... | |
381 const intptr_t static_call_start_ix = static_calls_.length(); | 379 const intptr_t static_call_start_ix = static_calls_.length(); |
382 for (BlockIterator block_it = graph->postorder_iterator(); !block_it.Done(); | 380 for (BlockIterator block_it = graph->postorder_iterator(); !block_it.Done(); |
383 block_it.Advance()) { | 381 block_it.Advance()) { |
384 for (ForwardInstructionIterator it(block_it.Current()); !it.Done(); | 382 for (ForwardInstructionIterator it(block_it.Current()); !it.Done(); |
385 it.Advance()) { | 383 it.Advance()) { |
386 Instruction* current = it.Current(); | 384 Instruction* current = it.Current(); |
387 if (current->IsPolymorphicInstanceCall()) { | 385 if (current->IsPolymorphicInstanceCall()) { |
388 PolymorphicInstanceCallInstr* instance_call = | 386 PolymorphicInstanceCallInstr* instance_call = |
389 current->AsPolymorphicInstanceCall(); | 387 current->AsPolymorphicInstanceCall(); |
390 if (!inline_only_recognized_methods || | 388 if (!inline_only_recognized_methods || |
391 instance_call->HasSingleRecognizedTarget() || | 389 instance_call->targets().HasSingleRecognizedTarget() || |
392 instance_call->ic_data() | 390 instance_call->HasOnlyDispatcherOrImplicitAccessorTargets()) { |
393 .HasOnlyDispatcherOrImplicitAccessorTargets()) { | |
394 instance_calls_.Add(InstanceCallInfo(instance_call, graph)); | 391 instance_calls_.Add(InstanceCallInfo(instance_call, graph)); |
395 } else { | 392 } else { |
396 // Method not inlined because inlining too deep and method | 393 // Method not inlined because inlining too deep and method |
397 // not recognized. | 394 // not recognized. |
398 if (FLAG_print_inlining_tree) { | 395 if (FLAG_print_inlining_tree) { |
399 const Function* caller = &graph->function(); | 396 const Function* caller = &graph->function(); |
400 const Function* target = &Function::ZoneHandle( | 397 const Function* target = &instance_call->targets().FirstTarget(); |
401 instance_call->ic_data().GetTargetAt(0)); | |
402 inlined_info->Add(InlinedInfo(caller, target, depth + 1, | 398 inlined_info->Add(InlinedInfo(caller, target, depth + 1, |
403 instance_call, "Too deep")); | 399 instance_call, "Too deep")); |
404 } | 400 } |
405 } | 401 } |
406 } else if (current->IsStaticCall()) { | 402 } else if (current->IsStaticCall()) { |
407 StaticCallInstr* static_call = current->AsStaticCall(); | 403 StaticCallInstr* static_call = current->AsStaticCall(); |
408 if (!inline_only_recognized_methods || | 404 if (!inline_only_recognized_methods || |
409 static_call->function().IsRecognized() || | 405 static_call->function().IsRecognized() || |
410 static_call->function().IsDispatcherOrImplicitAccessor()) { | 406 static_call->function().IsDispatcherOrImplicitAccessor()) { |
411 static_calls_.Add(StaticCallInfo(static_call, graph)); | 407 static_calls_.Add(StaticCallInfo(static_call, graph)); |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
484 TargetEntryInstr* BuildDecisionGraph(); | 480 TargetEntryInstr* BuildDecisionGraph(); |
485 | 481 |
486 Isolate* isolate() const; | 482 Isolate* isolate() const; |
487 Zone* zone() const; | 483 Zone* zone() const; |
488 intptr_t AllocateBlockId() const; | 484 intptr_t AllocateBlockId() const; |
489 inline bool trace_inlining() const; | 485 inline bool trace_inlining() const; |
490 | 486 |
491 CallSiteInliner* const owner_; | 487 CallSiteInliner* const owner_; |
492 PolymorphicInstanceCallInstr* const call_; | 488 PolymorphicInstanceCallInstr* const call_; |
493 const intptr_t num_variants_; | 489 const intptr_t num_variants_; |
494 GrowableArray<CidRangeTarget> variants_; | 490 const CallTargets& variants_; |
495 | 491 |
496 GrowableArray<CidRangeTarget> inlined_variants_; | 492 CallTargets inlined_variants_; |
497 GrowableArray<CidRangeTarget> non_inlined_variants_; | 493 CallTargets* non_inlined_variants_; |
Vyacheslav Egorov (Google)
2017/04/20 16:45:33
Add a comment that non_inlined_variants_ can be us
erikcorry
2017/04/21 13:04:44
Done.
| |
498 GrowableArray<BlockEntryInstr*> inlined_entries_; | 494 GrowableArray<BlockEntryInstr*> inlined_entries_; |
499 InlineExitCollector* exit_collector_; | 495 InlineExitCollector* exit_collector_; |
500 | 496 |
501 const Function& caller_function_; | 497 const Function& caller_function_; |
502 const intptr_t caller_inlining_id_; | 498 const intptr_t caller_inlining_id_; |
503 }; | 499 }; |
504 | 500 |
505 | 501 |
506 static bool HasAnnotation(const Function& function, const char* annotation) { | 502 static bool HasAnnotation(const Function& function, const char* annotation) { |
507 const Class& owner = Class::Handle(function.Owner()); | 503 const Class& owner = Class::Handle(function.Owner()); |
(...skipping 790 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1298 continue; | 1294 continue; |
1299 } | 1295 } |
1300 const Function& cl = call_info[call_idx].caller(); | 1296 const Function& cl = call_info[call_idx].caller(); |
1301 intptr_t caller_inlining_id = | 1297 intptr_t caller_inlining_id = |
1302 call_info[call_idx].caller_graph->inlining_id(); | 1298 call_info[call_idx].caller_graph->inlining_id(); |
1303 PolymorphicInliner inliner(this, call, cl, caller_inlining_id); | 1299 PolymorphicInliner inliner(this, call, cl, caller_inlining_id); |
1304 inliner.Inline(); | 1300 inliner.Inline(); |
1305 continue; | 1301 continue; |
1306 } | 1302 } |
1307 | 1303 |
1308 const ICData& ic_data = call->ic_data(); | 1304 const Function& target = call->targets().MostPopularTarget(); |
1309 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | |
1310 if (!inliner_->AlwaysInline(target) && | 1305 if (!inliner_->AlwaysInline(target) && |
1311 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 1306 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
1312 if (trace_inlining()) { | 1307 if (trace_inlining()) { |
1313 String& name = String::Handle(target.QualifiedUserVisibleName()); | 1308 String& name = String::Handle(target.QualifiedUserVisibleName()); |
1314 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", | 1309 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", |
1315 name.ToCString(), target.deoptimization_counter(), | 1310 name.ToCString(), target.deoptimization_counter(), |
1316 call_info[call_idx].ratio); | 1311 call_info[call_idx].ratio); |
1317 } | 1312 } |
1318 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), &target, | 1313 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), &target, |
1319 call); | 1314 call); |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1440 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); | 1435 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); |
1441 }; | 1436 }; |
1442 | 1437 |
1443 | 1438 |
1444 PolymorphicInliner::PolymorphicInliner(CallSiteInliner* owner, | 1439 PolymorphicInliner::PolymorphicInliner(CallSiteInliner* owner, |
1445 PolymorphicInstanceCallInstr* call, | 1440 PolymorphicInstanceCallInstr* call, |
1446 const Function& caller_function, | 1441 const Function& caller_function, |
1447 intptr_t caller_inlining_id) | 1442 intptr_t caller_inlining_id) |
1448 : owner_(owner), | 1443 : owner_(owner), |
1449 call_(call), | 1444 call_(call), |
1450 num_variants_(call->ic_data().NumberOfChecks()), | 1445 num_variants_(call->NumberOfChecks()), |
1451 variants_(num_variants_), | 1446 variants_(call->targets_), |
1452 inlined_variants_(num_variants_), | 1447 inlined_variants_(), |
1453 non_inlined_variants_(num_variants_), | 1448 non_inlined_variants_(new (zone()) CallTargets()), |
1454 inlined_entries_(num_variants_), | 1449 inlined_entries_(num_variants_), |
1455 exit_collector_(new (Z) InlineExitCollector(owner->caller_graph(), call)), | 1450 exit_collector_(new (Z) InlineExitCollector(owner->caller_graph(), call)), |
1456 caller_function_(caller_function), | 1451 caller_function_(caller_function), |
1457 caller_inlining_id_(caller_inlining_id) {} | 1452 caller_inlining_id_(caller_inlining_id) {} |
1458 | 1453 |
1459 | 1454 |
1460 Isolate* PolymorphicInliner::isolate() const { | 1455 Isolate* PolymorphicInliner::isolate() const { |
1461 return owner_->caller_graph()->isolate(); | 1456 return owner_->caller_graph()->isolate(); |
1462 } | 1457 } |
1463 | 1458 |
(...skipping 12 matching lines...) Expand all Loading... | |
1476 // inlined target. This sharing is represented by using three different | 1471 // inlined target. This sharing is represented by using three different |
1477 // types of entries in the inlined_entries_ array: | 1472 // types of entries in the inlined_entries_ array: |
1478 // | 1473 // |
1479 // * GraphEntry: the inlined body is not shared. | 1474 // * GraphEntry: the inlined body is not shared. |
1480 // | 1475 // |
1481 // * TargetEntry: the inlined body is shared and this is the first variant. | 1476 // * TargetEntry: the inlined body is shared and this is the first variant. |
1482 // | 1477 // |
1483 // * JoinEntry: the inlined body is shared and this is a subsequent variant. | 1478 // * JoinEntry: the inlined body is shared and this is a subsequent variant. |
1484 bool PolymorphicInliner::CheckInlinedDuplicate(const Function& target) { | 1479 bool PolymorphicInliner::CheckInlinedDuplicate(const Function& target) { |
1485 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { | 1480 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { |
1486 if ((target.raw() == inlined_variants_[i].target->raw()) && | 1481 if ((target.raw() == inlined_variants_.At(i).target->raw()) && |
Vyacheslav Egorov (Google)
2017/04/20 16:45:33
just [i] should work?
erikcorry
2017/04/21 13:04:44
Done.
| |
1487 !MethodRecognizer::PolymorphicTarget(target)) { | 1482 !MethodRecognizer::PolymorphicTarget(target)) { |
1488 // The call target is shared with a previous inlined variant. Share | 1483 // The call target is shared with a previous inlined variant. Share |
1489 // the graph. This requires a join block at the entry, and edge-split | 1484 // the graph. This requires a join block at the entry, and edge-split |
1490 // form requires a target for each branch. | 1485 // form requires a target for each branch. |
1491 // | 1486 // |
1492 // Represent the sharing by recording a fresh target for the first | 1487 // Represent the sharing by recording a fresh target for the first |
1493 // variant and the shared join for all later variants. | 1488 // variant and the shared join for all later variants. |
1494 if (inlined_entries_[i]->IsGraphEntry()) { | 1489 if (inlined_entries_[i]->IsGraphEntry()) { |
1495 // Convert the old target entry to a new join entry. | 1490 // Convert the old target entry to a new join entry. |
1496 TargetEntryInstr* old_target = | 1491 TargetEntryInstr* old_target = |
(...skipping 30 matching lines...) Expand all Loading... | |
1527 inlined_entries_.Add(join); | 1522 inlined_entries_.Add(join); |
1528 return true; | 1523 return true; |
1529 } | 1524 } |
1530 } | 1525 } |
1531 | 1526 |
1532 return false; | 1527 return false; |
1533 } | 1528 } |
1534 | 1529 |
1535 | 1530 |
1536 bool PolymorphicInliner::CheckNonInlinedDuplicate(const Function& target) { | 1531 bool PolymorphicInliner::CheckNonInlinedDuplicate(const Function& target) { |
1537 for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) { | 1532 for (intptr_t i = 0; i < non_inlined_variants_->length(); ++i) { |
1538 if (target.raw() == non_inlined_variants_[i].target->raw()) { | 1533 if (target.raw() == non_inlined_variants_->At(i).target->raw()) { |
1539 return true; | 1534 return true; |
1540 } | 1535 } |
1541 } | 1536 } |
1542 | 1537 |
1543 return false; | 1538 return false; |
1544 } | 1539 } |
1545 | 1540 |
1546 | 1541 |
1547 bool PolymorphicInliner::TryInliningPoly(const CidRangeTarget& range) { | 1542 bool PolymorphicInliner::TryInliningPoly(const CidRangeTarget& range) { |
1548 if ((!FLAG_precompiled_mode || | 1543 if ((!FLAG_precompiled_mode || |
1549 owner_->inliner_->use_speculative_inlining()) && | 1544 owner_->inliner_->use_speculative_inlining()) && |
1550 range.cid_start == range.cid_end && | 1545 range.cid_start == range.cid_end && |
1551 TryInlineRecognizedMethod(range.cid_start, *range.target)) { | 1546 TryInlineRecognizedMethod(range.cid_start, *range.target)) { |
1552 owner_->inlined_ = true; | 1547 owner_->inlined_ = true; |
1553 return true; | 1548 return true; |
1554 } | 1549 } |
1555 | 1550 |
1556 GrowableArray<Value*> arguments(call_->ArgumentCount()); | 1551 GrowableArray<Value*> arguments(call_->ArgumentCount()); |
1557 for (int i = 0; i < call_->ArgumentCount(); ++i) { | 1552 for (int i = 0; i < call_->ArgumentCount(); ++i) { |
1558 arguments.Add(call_->PushArgumentAt(i)->value()); | 1553 arguments.Add(call_->PushArgumentAt(i)->value()); |
1559 } | 1554 } |
1560 InlinedCallData call_data(call_, &arguments, caller_function_, | 1555 InlinedCallData call_data(call_, &arguments, caller_function_, |
1561 caller_inlining_id_); | 1556 caller_inlining_id_); |
1562 if (!owner_->TryInlining(*range.target, | 1557 Function& target = Function::ZoneHandle(zone(), range.target->raw()); |
1563 call_->instance_call()->argument_names(), | 1558 if (!owner_->TryInlining(target, call_->instance_call()->argument_names(), |
1564 &call_data)) { | 1559 &call_data)) { |
1565 return false; | 1560 return false; |
1566 } | 1561 } |
1567 | 1562 |
1568 FlowGraph* callee_graph = call_data.callee_graph; | 1563 FlowGraph* callee_graph = call_data.callee_graph; |
1569 call_data.exit_collector->PrepareGraphs(callee_graph); | 1564 call_data.exit_collector->PrepareGraphs(callee_graph); |
1570 inlined_entries_.Add(callee_graph->graph_entry()); | 1565 inlined_entries_.Add(callee_graph->graph_entry()); |
1571 exit_collector_->Union(call_data.exit_collector); | 1566 exit_collector_->Union(call_data.exit_collector); |
1572 | 1567 |
1573 // Replace parameter stubs and constants. Replace the receiver argument | 1568 // Replace parameter stubs and constants. Replace the receiver argument |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1701 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { | 1696 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { |
1702 const CidRangeTarget& variant = inlined_variants_[i]; | 1697 const CidRangeTarget& variant = inlined_variants_[i]; |
1703 bool test_is_range = (variant.cid_start != variant.cid_end); | 1698 bool test_is_range = (variant.cid_start != variant.cid_end); |
1704 bool is_last_test = (i == inlined_variants_.length() - 1); | 1699 bool is_last_test = (i == inlined_variants_.length() - 1); |
1705 // 1. Guard the body with a class id check. We don't need any check if | 1700 // 1. Guard the body with a class id check. We don't need any check if |
1706 // it's the last test and global analysis has told us that the call is | 1701 // it's the last test and global analysis has told us that the call is |
1707 // complete. TODO(erikcorry): Enhance CheckClassIdInstr so it can take an | 1702 // complete. TODO(erikcorry): Enhance CheckClassIdInstr so it can take an |
1708 // arbitrary CidRangeTarget. Currently we don't go into this branch if the | 1703 // arbitrary CidRangeTarget. Currently we don't go into this branch if the |
1709 // last test is a range test - instead we set the follow_with_deopt flag. | 1704 // last test is a range test - instead we set the follow_with_deopt flag. |
1710 if (is_last_test && (!test_is_range || call_->complete()) && | 1705 if (is_last_test && (!test_is_range || call_->complete()) && |
1711 non_inlined_variants_.is_empty()) { | 1706 non_inlined_variants_->is_empty()) { |
1712 // If it is the last variant use a check class id instruction which can | 1707 // If it is the last variant use a check class id instruction which can |
1713 // deoptimize, followed unconditionally by the body. Omit the check if | 1708 // deoptimize, followed unconditionally by the body. Omit the check if |
1714 // we know that we have covered all possible classes. | 1709 // we know that we have covered all possible classes. |
1715 if (!call_->complete()) { | 1710 if (!call_->complete()) { |
1716 ASSERT(!test_is_range); // See condition above. | 1711 ASSERT(!test_is_range); // See condition above. |
1717 RedefinitionInstr* cid_redefinition = | 1712 RedefinitionInstr* cid_redefinition = |
1718 new RedefinitionInstr(new (Z) Value(load_cid)); | 1713 new RedefinitionInstr(new (Z) Value(load_cid)); |
1719 cid_redefinition->set_ssa_temp_index( | 1714 cid_redefinition->set_ssa_temp_index( |
1720 owner_->caller_graph()->alloc_ssa_temp_index()); | 1715 owner_->caller_graph()->alloc_ssa_temp_index()); |
1721 cursor = AppendInstruction(cursor, cid_redefinition); | 1716 cursor = AppendInstruction(cursor, cid_redefinition); |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1877 false_target2->InheritDeoptTarget(zone(), call_); | 1872 false_target2->InheritDeoptTarget(zone(), call_); |
1878 goto_1->InheritDeoptTarget(zone(), call_); | 1873 goto_1->InheritDeoptTarget(zone(), call_); |
1879 goto_2->InheritDeoptTarget(zone(), call_); | 1874 goto_2->InheritDeoptTarget(zone(), call_); |
1880 | 1875 |
1881 cursor = current_block = join; | 1876 cursor = current_block = join; |
1882 } | 1877 } |
1883 } | 1878 } |
1884 } | 1879 } |
1885 | 1880 |
1886 // Handle any non-inlined variants. | 1881 // Handle any non-inlined variants. |
1887 if (!non_inlined_variants_.is_empty()) { | 1882 if (!non_inlined_variants_->is_empty()) { |
1888 // Move push arguments of the call. | 1883 // Move push arguments of the call. |
1889 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { | 1884 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { |
1890 PushArgumentInstr* push = call_->PushArgumentAt(i); | 1885 PushArgumentInstr* push = call_->PushArgumentAt(i); |
1891 push->ReplaceUsesWith(push->value()->definition()); | 1886 push->ReplaceUsesWith(push->value()->definition()); |
1892 push->previous()->LinkTo(push->next()); | 1887 push->previous()->LinkTo(push->next()); |
1893 cursor->LinkTo(push); | 1888 cursor->LinkTo(push); |
1894 cursor = push; | 1889 cursor = push; |
1895 } | 1890 } |
1896 const ICData& old_checks = call_->ic_data(); | |
1897 const ICData& new_checks = ICData::ZoneHandle(ICData::New( | |
1898 Function::Handle(old_checks.Owner()), | |
1899 String::Handle(old_checks.target_name()), | |
1900 Array::Handle(old_checks.arguments_descriptor()), old_checks.deopt_id(), | |
1901 1, // Number of args tested. | |
1902 false)); // is_static_call | |
1903 for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) { | |
1904 // We are adding all the cids in each range. They will be joined | |
1905 // together again by the PolymorphicInstanceCall instruction, which is a | |
1906 // bit messy. | |
1907 intptr_t count = non_inlined_variants_[i].count; | |
1908 | |
1909 for (intptr_t j = non_inlined_variants_[i].cid_start; | |
1910 j <= non_inlined_variants_[i].cid_end; j++) { | |
1911 new_checks.AddReceiverCheck(j, *non_inlined_variants_[i].target, count); | |
1912 count = 0; | |
1913 } | |
1914 } | |
1915 PolymorphicInstanceCallInstr* fallback_call = | 1891 PolymorphicInstanceCallInstr* fallback_call = |
1916 new PolymorphicInstanceCallInstr(call_->instance_call(), new_checks, | 1892 new PolymorphicInstanceCallInstr( |
1917 /* with_checks = */ true, | 1893 call_->instance_call(), *non_inlined_variants_, |
1918 call_->complete()); | 1894 /* with_checks = */ true, call_->complete()); |
1919 fallback_call->set_ssa_temp_index( | 1895 fallback_call->set_ssa_temp_index( |
1920 owner_->caller_graph()->alloc_ssa_temp_index()); | 1896 owner_->caller_graph()->alloc_ssa_temp_index()); |
1921 fallback_call->InheritDeoptTarget(zone(), call_); | 1897 fallback_call->InheritDeoptTarget(zone(), call_); |
1922 fallback_call->set_total_call_count(call_->CallCount()); | 1898 fallback_call->set_total_call_count(call_->CallCount()); |
1923 ReturnInstr* fallback_return = new ReturnInstr( | 1899 ReturnInstr* fallback_return = new ReturnInstr( |
1924 call_->instance_call()->token_pos(), new Value(fallback_call)); | 1900 call_->instance_call()->token_pos(), new Value(fallback_call)); |
1925 fallback_return->InheritDeoptTargetAfter(owner_->caller_graph(), call_, | 1901 fallback_return->InheritDeoptTargetAfter(owner_->caller_graph(), call_, |
1926 fallback_call); | 1902 fallback_call); |
1927 AppendInstruction(AppendInstruction(cursor, fallback_call), | 1903 AppendInstruction(AppendInstruction(cursor, fallback_call), |
1928 fallback_return); | 1904 fallback_return); |
(...skipping 29 matching lines...) Expand all Loading... | |
1958 percent, message); | 1934 percent, message); |
1959 } | 1935 } |
1960 | 1936 |
1961 | 1937 |
1962 bool PolymorphicInliner::trace_inlining() const { | 1938 bool PolymorphicInliner::trace_inlining() const { |
1963 return owner_->trace_inlining(); | 1939 return owner_->trace_inlining(); |
1964 } | 1940 } |
1965 | 1941 |
1966 | 1942 |
1967 void PolymorphicInliner::Inline() { | 1943 void PolymorphicInliner::Inline() { |
1968 // Consider the polymorphic variants in order by frequency. | 1944 ASSERT(&variants_ == &call_->targets_); |
1969 FlowGraphCompiler::SortICDataByCount(call_->ic_data(), &variants_, | 1945 |
1970 /* drop_smi = */ false); | |
1971 intptr_t total = call_->total_call_count(); | 1946 intptr_t total = call_->total_call_count(); |
1972 for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) { | 1947 for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) { |
1973 if (variants_.length() > FLAG_max_polymorphic_checks) { | 1948 if (variants_.length() > FLAG_max_polymorphic_checks) { |
1974 non_inlined_variants_.Add(variants_[var_idx]); | 1949 non_inlined_variants_->Add(variants_[var_idx]); |
1975 continue; | 1950 continue; |
1976 } | 1951 } |
1977 | 1952 |
1978 // We we almost inlined all the cases then try a little harder to inline | 1953 // We we almost inlined all the cases then try a little harder to inline |
1979 // the last two, because it's a big win if we inline all of them (compiler | 1954 // the last two, because it's a big win if we inline all of them (compiler |
1980 // can see all side effects). | 1955 // can see all side effects). |
1981 const bool try_harder = (var_idx >= variants_.length() - 2) && | 1956 const bool try_harder = (var_idx >= variants_.length() - 2) && |
1982 non_inlined_variants_.length() == 0; | 1957 non_inlined_variants_->length() == 0; |
1983 const Function& target = *variants_[var_idx].target; | 1958 const Function& target = *variants_[var_idx].target; |
1984 const intptr_t count = variants_[var_idx].count; | 1959 const intptr_t count = variants_[var_idx].count; |
1985 | 1960 |
1986 intptr_t size = target.optimized_instruction_count(); | 1961 intptr_t size = target.optimized_instruction_count(); |
1987 bool small = (size != 0 && size < FLAG_inlining_size_threshold); | 1962 bool small = (size != 0 && size < FLAG_inlining_size_threshold); |
1988 | 1963 |
1989 // If it's less than 3% of the dispatches, we won't even consider | 1964 // If it's less than 3% of the dispatches, we won't even consider |
1990 // checking for the class ID and branching to another already-inlined | 1965 // checking for the class ID and branching to another already-inlined |
1991 // version. | 1966 // version. |
1992 if (!try_harder && count < (total >> 5)) { | 1967 if (!try_harder && count < (total >> 5)) { |
1993 TRACE_INLINING( | 1968 TRACE_INLINING( |
1994 TracePolyInlining(variants_[var_idx], total, "way too infrequent")); | 1969 TracePolyInlining(variants_[var_idx], total, "way too infrequent")); |
1995 non_inlined_variants_.Add(variants_[var_idx]); | 1970 non_inlined_variants_->Add(variants_[var_idx]); |
1996 continue; | 1971 continue; |
1997 } | 1972 } |
1998 | 1973 |
1999 // First check if this is the same target as an earlier inlined variant. | 1974 // First check if this is the same target as an earlier inlined variant. |
2000 if (CheckInlinedDuplicate(target)) { | 1975 if (CheckInlinedDuplicate(target)) { |
2001 TRACE_INLINING(TracePolyInlining(variants_[var_idx], total, | 1976 TRACE_INLINING(TracePolyInlining(variants_[var_idx], total, |
2002 "duplicate already inlined")); | 1977 "duplicate already inlined")); |
2003 inlined_variants_.Add(variants_[var_idx]); | 1978 inlined_variants_.Add(variants_[var_idx]); |
2004 continue; | 1979 continue; |
2005 } | 1980 } |
2006 | 1981 |
2007 // If it's less than 12% of the dispatches and it's not already inlined, we | 1982 // If it's less than 12% of the dispatches and it's not already inlined, we |
2008 // don't consider inlining. For very small functions we are willing to | 1983 // don't consider inlining. For very small functions we are willing to |
2009 // consider inlining for 6% of the cases. | 1984 // consider inlining for 6% of the cases. |
2010 if (!try_harder && count < (total >> (small ? 4 : 3))) { | 1985 if (!try_harder && count < (total >> (small ? 4 : 3))) { |
2011 TRACE_INLINING( | 1986 TRACE_INLINING( |
2012 TracePolyInlining(variants_[var_idx], total, "too infrequent")); | 1987 TracePolyInlining(variants_[var_idx], total, "too infrequent")); |
2013 non_inlined_variants_.Add(variants_[var_idx]); | 1988 non_inlined_variants_->Add(variants_[var_idx]); |
2014 continue; | 1989 continue; |
2015 } | 1990 } |
2016 | 1991 |
2017 // Also check if this is the same target as an earlier non-inlined | 1992 // Also check if this is the same target as an earlier non-inlined |
2018 // variant. If so and since inlining decisions are costly, do not try | 1993 // variant. If so and since inlining decisions are costly, do not try |
2019 // to inline this variant. | 1994 // to inline this variant. |
2020 if (CheckNonInlinedDuplicate(target)) { | 1995 if (CheckNonInlinedDuplicate(target)) { |
2021 TRACE_INLINING( | 1996 TRACE_INLINING( |
2022 TracePolyInlining(variants_[var_idx], total, "already not inlined")); | 1997 TracePolyInlining(variants_[var_idx], total, "already not inlined")); |
2023 non_inlined_variants_.Add(variants_[var_idx]); | 1998 non_inlined_variants_->Add(variants_[var_idx]); |
2024 continue; | 1999 continue; |
2025 } | 2000 } |
2026 | 2001 |
2027 // Make an inlining decision. | 2002 // Make an inlining decision. |
2028 if (TryInliningPoly(variants_[var_idx])) { | 2003 if (TryInliningPoly(variants_[var_idx])) { |
2029 TRACE_INLINING(TracePolyInlining(variants_[var_idx], total, "inlined")); | 2004 TRACE_INLINING(TracePolyInlining(variants_[var_idx], total, "inlined")); |
2030 inlined_variants_.Add(variants_[var_idx]); | 2005 inlined_variants_.Add(variants_[var_idx]); |
2031 } else { | 2006 } else { |
2032 TRACE_INLINING( | 2007 TRACE_INLINING( |
2033 TracePolyInlining(variants_[var_idx], total, "not inlined")); | 2008 TracePolyInlining(variants_[var_idx], total, "not inlined")); |
2034 non_inlined_variants_.Add(variants_[var_idx]); | 2009 non_inlined_variants_->Add(variants_[var_idx]); |
2035 } | 2010 } |
2036 } | 2011 } |
2037 | 2012 |
2038 // If there are no inlined variants, leave the call in place. | 2013 // If there are no inlined variants, leave the call in place. |
2039 if (inlined_variants_.is_empty()) return; | 2014 if (inlined_variants_.is_empty()) return; |
2040 | 2015 |
2041 // Now build a decision tree (a DAG because of shared inline variants) and | 2016 // Now build a decision tree (a DAG because of shared inline variants) and |
2042 // inline it at the call site. | 2017 // inline it at the call site. |
2043 TargetEntryInstr* entry = BuildDecisionGraph(); | 2018 TargetEntryInstr* entry = BuildDecisionGraph(); |
2044 exit_collector_->ReplaceCall(entry); | 2019 exit_collector_->ReplaceCall(entry); |
(...skipping 1787 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3832 } | 3807 } |
3833 | 3808 |
3834 default: | 3809 default: |
3835 return false; | 3810 return false; |
3836 } | 3811 } |
3837 } | 3812 } |
3838 | 3813 |
3839 | 3814 |
3840 } // namespace dart | 3815 } // namespace dart |
3841 #endif // !defined(DART_PRECOMPILED_RUNTIME) | 3816 #endif // !defined(DART_PRECOMPILED_RUNTIME) |
OLD | NEW |