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 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
467 PolymorphicInstanceCallInstr* call, | 467 PolymorphicInstanceCallInstr* call, |
468 const Function& caller_function, | 468 const Function& caller_function, |
469 intptr_t caller_inlining_id); | 469 intptr_t caller_inlining_id); |
470 | 470 |
471 void Inline(); | 471 void Inline(); |
472 | 472 |
473 private: | 473 private: |
474 bool CheckInlinedDuplicate(const Function& target); | 474 bool CheckInlinedDuplicate(const Function& target); |
475 bool CheckNonInlinedDuplicate(const Function& target); | 475 bool CheckNonInlinedDuplicate(const Function& target); |
476 | 476 |
477 bool TryInliningPoly(intptr_t receiver_cid, const Function& target); | 477 bool TryInliningPoly(const CidRangeTarget& target); |
478 bool TryInlineRecognizedMethod(intptr_t receiver_cid, const Function& target); | 478 bool TryInlineRecognizedMethod(intptr_t receiver_cid, const Function& target); |
479 | 479 |
480 TargetEntryInstr* BuildDecisionGraph(); | 480 TargetEntryInstr* BuildDecisionGraph(); |
481 | 481 |
482 Isolate* isolate() const; | 482 Isolate* isolate() const; |
483 Zone* zone() const; | 483 Zone* zone() const; |
484 intptr_t AllocateBlockId() const; | |
485 inline bool trace_inlining() const; | |
484 | 486 |
485 CallSiteInliner* const owner_; | 487 CallSiteInliner* const owner_; |
486 PolymorphicInstanceCallInstr* const call_; | 488 PolymorphicInstanceCallInstr* const call_; |
487 const intptr_t num_variants_; | 489 const intptr_t num_variants_; |
488 GrowableArray<CidTarget> variants_; | 490 GrowableArray<CidRangeTarget> variants_; |
489 | 491 |
490 GrowableArray<CidTarget> inlined_variants_; | 492 GrowableArray<CidRangeTarget> inlined_variants_; |
491 GrowableArray<CidTarget> non_inlined_variants_; | 493 GrowableArray<CidRangeTarget> non_inlined_variants_; |
492 GrowableArray<BlockEntryInstr*> inlined_entries_; | 494 GrowableArray<BlockEntryInstr*> inlined_entries_; |
493 InlineExitCollector* exit_collector_; | 495 InlineExitCollector* exit_collector_; |
494 | 496 |
495 const Function& caller_function_; | 497 const Function& caller_function_; |
496 const intptr_t caller_inlining_id_; | 498 const intptr_t caller_inlining_id_; |
497 }; | 499 }; |
498 | 500 |
499 | 501 |
500 static bool HasAnnotation(const Function& function, const char* annotation) { | 502 static bool HasAnnotation(const Function& function, const char* annotation) { |
501 const Class& owner = Class::Handle(function.Owner()); | 503 const Class& owner = Class::Handle(function.Owner()); |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
638 } else { | 640 } else { |
639 ParameterInstr* param = new (Z) ParameterInstr(i, graph->graph_entry()); | 641 ParameterInstr* param = new (Z) ParameterInstr(i, graph->graph_entry()); |
640 param->UpdateType(*argument->Type()); | 642 param->UpdateType(*argument->Type()); |
641 return param; | 643 return param; |
642 } | 644 } |
643 } | 645 } |
644 | 646 |
645 bool TryInlining(const Function& function, | 647 bool TryInlining(const Function& function, |
646 const Array& argument_names, | 648 const Array& argument_names, |
647 InlinedCallData* call_data) { | 649 InlinedCallData* call_data) { |
648 TRACE_INLINING(THR_Print(" => %s (deopt count %d)\n", function.ToCString(), | 650 if (trace_inlining()) { |
649 function.deoptimization_counter())); | 651 String& name = String::Handle(function.QualifiedUserVisibleName()); |
652 THR_Print(" => %s (deopt count %d)\n", name.ToCString(), | |
653 function.deoptimization_counter()); | |
654 } | |
650 | 655 |
651 // Abort if the inlinable bit on the function is low. | 656 // Abort if the inlinable bit on the function is low. |
652 if (!function.CanBeInlined()) { | 657 if (!function.CanBeInlined()) { |
653 TRACE_INLINING(THR_Print(" Bailout: not inlinable\n")); | 658 TRACE_INLINING(THR_Print(" Bailout: not inlinable\n")); |
654 PRINT_INLINING_TREE("Not inlinable", &call_data->caller, &function, | 659 PRINT_INLINING_TREE("Not inlinable", &call_data->caller, &function, |
655 call_data->call); | 660 call_data->call); |
656 return false; | 661 return false; |
657 } | 662 } |
658 | 663 |
659 // Don't inline any intrinsified functions in precompiled mode | 664 // Don't inline any intrinsified functions in precompiled mode |
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1005 // caller's list of deferred prefixes. | 1010 // caller's list of deferred prefixes. |
1006 caller_graph()->AddToDeferredPrefixes( | 1011 caller_graph()->AddToDeferredPrefixes( |
1007 callee_graph->deferred_prefixes()); | 1012 callee_graph->deferred_prefixes()); |
1008 | 1013 |
1009 FlowGraphInliner::SetInliningId( | 1014 FlowGraphInliner::SetInliningId( |
1010 callee_graph, | 1015 callee_graph, |
1011 inliner_->NextInlineId(callee_graph->function(), | 1016 inliner_->NextInlineId(callee_graph->function(), |
1012 call_data->call->token_pos(), | 1017 call_data->call->token_pos(), |
1013 call_data->caller_inlining_id_)); | 1018 call_data->caller_inlining_id_)); |
1014 TRACE_INLINING(THR_Print(" Success\n")); | 1019 TRACE_INLINING(THR_Print(" Success\n")); |
1020 TRACE_INLINING(THR_Print(" with size %" Pd "\n", | |
1021 function.optimized_instruction_count())); | |
1015 PRINT_INLINING_TREE(NULL, &call_data->caller, &function, call); | 1022 PRINT_INLINING_TREE(NULL, &call_data->caller, &function, call); |
1016 return true; | 1023 return true; |
1017 } else { | 1024 } else { |
1018 error = thread()->sticky_error(); | 1025 error = thread()->sticky_error(); |
1019 thread()->clear_sticky_error(); | 1026 thread()->clear_sticky_error(); |
1020 | 1027 |
1021 if (error.IsLanguageError() && | 1028 if (error.IsLanguageError() && |
1022 (LanguageError::Cast(error).kind() == Report::kBailout)) { | 1029 (LanguageError::Cast(error).kind() == Report::kBailout)) { |
1023 if (error.raw() == Object::background_compilation_error().raw()) { | 1030 if (error.raw() == Object::background_compilation_error().raw()) { |
1024 // Fall through to exit the compilation, and retry it later. | 1031 // Fall through to exit the compilation, and retry it later. |
(...skipping 19 matching lines...) Expand all Loading... | |
1044 // later. | 1051 // later. |
1045 ASSERT(FLAG_precompiled_mode || Compiler::IsBackgroundCompilation() || | 1052 ASSERT(FLAG_precompiled_mode || Compiler::IsBackgroundCompilation() || |
1046 error.IsUnhandledException()); | 1053 error.IsUnhandledException()); |
1047 Thread::Current()->long_jump_base()->Jump(1, error); | 1054 Thread::Current()->long_jump_base()->Jump(1, error); |
1048 UNREACHABLE(); | 1055 UNREACHABLE(); |
1049 return false; | 1056 return false; |
1050 } | 1057 } |
1051 | 1058 |
1052 void PrintInlinedInfo(const Function& top) { | 1059 void PrintInlinedInfo(const Function& top) { |
1053 if (inlined_info_.length() > 0) { | 1060 if (inlined_info_.length() > 0) { |
1054 THR_Print("Inlining into: '%s' growth: %f (%" Pd " -> %" Pd ")\n", | 1061 THR_Print("Inlining into: '%s'\n growth: %f (%" Pd " -> %" Pd ")\n", |
1055 top.ToFullyQualifiedCString(), GrowthFactor(), initial_size_, | 1062 top.ToFullyQualifiedCString(), GrowthFactor(), initial_size_, |
1056 inlined_size_); | 1063 inlined_size_); |
1057 PrintInlinedInfoFor(top, 1); | 1064 PrintInlinedInfoFor(top, 1); |
1058 } | 1065 } |
1059 } | 1066 } |
1060 | 1067 |
1061 private: | 1068 private: |
1062 friend class PolymorphicInliner; | 1069 friend class PolymorphicInliner; |
1063 | 1070 |
1064 static bool Contains(const GrowableArray<intptr_t>& a, intptr_t deopt_id) { | 1071 static bool Contains(const GrowableArray<intptr_t>& a, intptr_t deopt_id) { |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1194 | 1201 |
1195 void InlineStaticCalls() { | 1202 void InlineStaticCalls() { |
1196 const GrowableArray<CallSites::StaticCallInfo>& call_info = | 1203 const GrowableArray<CallSites::StaticCallInfo>& call_info = |
1197 inlining_call_sites_->static_calls(); | 1204 inlining_call_sites_->static_calls(); |
1198 TRACE_INLINING(THR_Print(" Static Calls (%" Pd ")\n", call_info.length())); | 1205 TRACE_INLINING(THR_Print(" Static Calls (%" Pd ")\n", call_info.length())); |
1199 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { | 1206 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { |
1200 StaticCallInstr* call = call_info[call_idx].call; | 1207 StaticCallInstr* call = call_info[call_idx].call; |
1201 const Function& target = call->function(); | 1208 const Function& target = call->function(); |
1202 if (!inliner_->AlwaysInline(target) && | 1209 if (!inliner_->AlwaysInline(target) && |
1203 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 1210 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
1204 TRACE_INLINING( | 1211 if (trace_inlining()) { |
1205 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", | 1212 String& name = String::Handle(target.QualifiedUserVisibleName()); |
1206 target.ToCString(), target.deoptimization_counter(), | 1213 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", |
1207 call_info[call_idx].ratio)); | 1214 name.ToCString(), target.deoptimization_counter(), |
1215 call_info[call_idx].ratio); | |
1216 } | |
1208 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), | 1217 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), |
1209 &call->function(), call); | 1218 &call->function(), call); |
1210 continue; | 1219 continue; |
1211 } | 1220 } |
1212 GrowableArray<Value*> arguments(call->ArgumentCount()); | 1221 GrowableArray<Value*> arguments(call->ArgumentCount()); |
1213 for (int i = 0; i < call->ArgumentCount(); ++i) { | 1222 for (int i = 0; i < call->ArgumentCount(); ++i) { |
1214 arguments.Add(call->PushArgumentAt(i)->value()); | 1223 arguments.Add(call->PushArgumentAt(i)->value()); |
1215 } | 1224 } |
1216 InlinedCallData call_data( | 1225 InlinedCallData call_data( |
1217 call, &arguments, call_info[call_idx].caller(), | 1226 call, &arguments, call_info[call_idx].caller(), |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1281 call_info[call_idx].caller_graph->inlining_id(); | 1290 call_info[call_idx].caller_graph->inlining_id(); |
1282 PolymorphicInliner inliner(this, call, cl, caller_inlining_id); | 1291 PolymorphicInliner inliner(this, call, cl, caller_inlining_id); |
1283 inliner.Inline(); | 1292 inliner.Inline(); |
1284 continue; | 1293 continue; |
1285 } | 1294 } |
1286 | 1295 |
1287 const ICData& ic_data = call->ic_data(); | 1296 const ICData& ic_data = call->ic_data(); |
1288 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | 1297 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
1289 if (!inliner_->AlwaysInline(target) && | 1298 if (!inliner_->AlwaysInline(target) && |
1290 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 1299 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
1291 TRACE_INLINING( | 1300 if (trace_inlining()) { |
1292 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", | 1301 String& name = String::Handle(target.QualifiedUserVisibleName()); |
1293 target.ToCString(), target.deoptimization_counter(), | 1302 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", |
1294 call_info[call_idx].ratio)); | 1303 name.ToCString(), target.deoptimization_counter(), |
1304 call_info[call_idx].ratio); | |
1305 } | |
1295 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), &target, | 1306 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), &target, |
1296 call); | 1307 call); |
1297 continue; | 1308 continue; |
1298 } | 1309 } |
1299 GrowableArray<Value*> arguments(call->ArgumentCount()); | 1310 GrowableArray<Value*> arguments(call->ArgumentCount()); |
1300 for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) { | 1311 for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) { |
1301 arguments.Add(call->PushArgumentAt(arg_i)->value()); | 1312 arguments.Add(call->PushArgumentAt(arg_i)->value()); |
1302 } | 1313 } |
1303 InlinedCallData call_data( | 1314 InlinedCallData call_data( |
1304 call, &arguments, call_info[call_idx].caller(), | 1315 call, &arguments, call_info[call_idx].caller(), |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1437 Isolate* PolymorphicInliner::isolate() const { | 1448 Isolate* PolymorphicInliner::isolate() const { |
1438 return owner_->caller_graph()->isolate(); | 1449 return owner_->caller_graph()->isolate(); |
1439 } | 1450 } |
1440 | 1451 |
1441 | 1452 |
1442 Zone* PolymorphicInliner::zone() const { | 1453 Zone* PolymorphicInliner::zone() const { |
1443 return owner_->caller_graph()->zone(); | 1454 return owner_->caller_graph()->zone(); |
1444 } | 1455 } |
1445 | 1456 |
1446 | 1457 |
1458 intptr_t PolymorphicInliner::AllocateBlockId() const { | |
1459 return owner_->caller_graph()->allocate_block_id(); | |
1460 } | |
1461 | |
1462 | |
1447 // Inlined bodies are shared if two different class ids have the same | 1463 // Inlined bodies are shared if two different class ids have the same |
1448 // inlined target. This sharing is represented by using three different | 1464 // inlined target. This sharing is represented by using three different |
1449 // types of entries in the inlined_entries_ array: | 1465 // types of entries in the inlined_entries_ array: |
1450 // | 1466 // |
1451 // * GraphEntry: the inlined body is not shared. | 1467 // * GraphEntry: the inlined body is not shared. |
1452 // | 1468 // |
1453 // * TargetEntry: the inlined body is shared and this is the first variant. | 1469 // * TargetEntry: the inlined body is shared and this is the first variant. |
1454 // | 1470 // |
1455 // * JoinEntry: the inlined body is shared and this is a subsequent variant. | 1471 // * JoinEntry: the inlined body is shared and this is a subsequent variant. |
1456 bool PolymorphicInliner::CheckInlinedDuplicate(const Function& target) { | 1472 bool PolymorphicInliner::CheckInlinedDuplicate(const Function& target) { |
(...skipping 16 matching lines...) Expand all Loading... | |
1473 | 1489 |
1474 JoinEntryInstr* new_join = | 1490 JoinEntryInstr* new_join = |
1475 BranchSimplifier::ToJoinEntry(zone(), old_target); | 1491 BranchSimplifier::ToJoinEntry(zone(), old_target); |
1476 old_target->ReplaceAsPredecessorWith(new_join); | 1492 old_target->ReplaceAsPredecessorWith(new_join); |
1477 for (intptr_t j = 0; j < old_target->dominated_blocks().length(); ++j) { | 1493 for (intptr_t j = 0; j < old_target->dominated_blocks().length(); ++j) { |
1478 BlockEntryInstr* block = old_target->dominated_blocks()[j]; | 1494 BlockEntryInstr* block = old_target->dominated_blocks()[j]; |
1479 new_join->AddDominatedBlock(block); | 1495 new_join->AddDominatedBlock(block); |
1480 } | 1496 } |
1481 // Create a new target with the join as unconditional successor. | 1497 // Create a new target with the join as unconditional successor. |
1482 TargetEntryInstr* new_target = | 1498 TargetEntryInstr* new_target = |
1483 new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(), | 1499 new TargetEntryInstr(AllocateBlockId(), old_target->try_index()); |
1484 old_target->try_index()); | |
1485 new_target->InheritDeoptTarget(zone(), new_join); | 1500 new_target->InheritDeoptTarget(zone(), new_join); |
1486 GotoInstr* new_goto = new (Z) GotoInstr(new_join); | 1501 GotoInstr* new_goto = new (Z) GotoInstr(new_join); |
1487 new_goto->InheritDeoptTarget(zone(), new_join); | 1502 new_goto->InheritDeoptTarget(zone(), new_join); |
1488 new_target->LinkTo(new_goto); | 1503 new_target->LinkTo(new_goto); |
1489 new_target->set_last_instruction(new_goto); | 1504 new_target->set_last_instruction(new_goto); |
1490 new_join->predecessors_.Add(new_target); | 1505 new_join->predecessors_.Add(new_target); |
1491 | 1506 |
1492 // Record the new target for the first variant. | 1507 // Record the new target for the first variant. |
1493 inlined_entries_[i] = new_target; | 1508 inlined_entries_[i] = new_target; |
1494 } | 1509 } |
(...skipping 15 matching lines...) Expand all Loading... | |
1510 for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) { | 1525 for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) { |
1511 if (target.raw() == non_inlined_variants_[i].target->raw()) { | 1526 if (target.raw() == non_inlined_variants_[i].target->raw()) { |
1512 return true; | 1527 return true; |
1513 } | 1528 } |
1514 } | 1529 } |
1515 | 1530 |
1516 return false; | 1531 return false; |
1517 } | 1532 } |
1518 | 1533 |
1519 | 1534 |
1520 bool PolymorphicInliner::TryInliningPoly(intptr_t receiver_cid, | 1535 bool PolymorphicInliner::TryInliningPoly(const CidRangeTarget& range) { |
1521 const Function& target) { | |
1522 if ((!FLAG_precompiled_mode || | 1536 if ((!FLAG_precompiled_mode || |
1523 owner_->inliner_->use_speculative_inlining()) && | 1537 owner_->inliner_->use_speculative_inlining()) && |
1524 TryInlineRecognizedMethod(receiver_cid, target)) { | 1538 range.cid_start == range.cid_end && |
Vyacheslav Egorov (Google)
2017/03/13 11:59:10
Maybe CidRangeTarget needs a helper method
range.
| |
1539 TryInlineRecognizedMethod(range.cid_start, *range.target)) { | |
1525 owner_->inlined_ = true; | 1540 owner_->inlined_ = true; |
1526 return true; | 1541 return true; |
1527 } | 1542 } |
1528 | 1543 |
1529 GrowableArray<Value*> arguments(call_->ArgumentCount()); | 1544 GrowableArray<Value*> arguments(call_->ArgumentCount()); |
1530 for (int i = 0; i < call_->ArgumentCount(); ++i) { | 1545 for (int i = 0; i < call_->ArgumentCount(); ++i) { |
1531 arguments.Add(call_->PushArgumentAt(i)->value()); | 1546 arguments.Add(call_->PushArgumentAt(i)->value()); |
1532 } | 1547 } |
1533 InlinedCallData call_data(call_, &arguments, caller_function_, | 1548 InlinedCallData call_data(call_, &arguments, caller_function_, |
1534 caller_inlining_id_); | 1549 caller_inlining_id_); |
1535 if (!owner_->TryInlining(target, call_->instance_call()->argument_names(), | 1550 if (!owner_->TryInlining(*range.target, |
1551 call_->instance_call()->argument_names(), | |
1536 &call_data)) { | 1552 &call_data)) { |
1537 return false; | 1553 return false; |
1538 } | 1554 } |
1539 | 1555 |
1540 FlowGraph* callee_graph = call_data.callee_graph; | 1556 FlowGraph* callee_graph = call_data.callee_graph; |
1541 call_data.exit_collector->PrepareGraphs(callee_graph); | 1557 call_data.exit_collector->PrepareGraphs(callee_graph); |
1542 inlined_entries_.Add(callee_graph->graph_entry()); | 1558 inlined_entries_.Add(callee_graph->graph_entry()); |
1543 exit_collector_->Union(call_data.exit_collector); | 1559 exit_collector_->Union(call_data.exit_collector); |
1544 | 1560 |
1545 // Replace parameter stubs and constants. Replace the receiver argument | 1561 // Replace parameter stubs and constants. Replace the receiver argument |
1546 // with a redefinition to prevent code from the inlined body from being | 1562 // with a redefinition to prevent code from the inlined body from being |
1547 // hoisted above the inlined entry. | 1563 // hoisted above the inlined entry. |
1548 ASSERT(arguments.length() > 0); | 1564 ASSERT(arguments.length() > 0); |
1549 Value* actual = arguments[0]; | 1565 Value* actual = arguments[0]; |
1550 RedefinitionInstr* redefinition = new (Z) RedefinitionInstr(actual->Copy(Z)); | 1566 RedefinitionInstr* redefinition = new (Z) RedefinitionInstr(actual->Copy(Z)); |
1551 redefinition->set_ssa_temp_index( | 1567 redefinition->set_ssa_temp_index( |
1552 owner_->caller_graph()->alloc_ssa_temp_index()); | 1568 owner_->caller_graph()->alloc_ssa_temp_index()); |
1553 redefinition->UpdateType(CompileType::FromCid(receiver_cid)); | 1569 if (range.cid_start == range.cid_end) { |
1570 redefinition->UpdateType(CompileType::FromCid(range.cid_start)); | |
1571 } | |
1554 redefinition->InsertAfter(callee_graph->graph_entry()->normal_entry()); | 1572 redefinition->InsertAfter(callee_graph->graph_entry()->normal_entry()); |
1555 Definition* stub = (*call_data.parameter_stubs)[0]; | 1573 Definition* stub = (*call_data.parameter_stubs)[0]; |
1556 stub->ReplaceUsesWith(redefinition); | 1574 stub->ReplaceUsesWith(redefinition); |
1557 | 1575 |
1558 for (intptr_t i = 1; i < arguments.length(); ++i) { | 1576 for (intptr_t i = 1; i < arguments.length(); ++i) { |
1559 actual = arguments[i]; | 1577 actual = arguments[i]; |
1560 if (actual != NULL) { | 1578 if (actual != NULL) { |
1561 stub = (*call_data.parameter_stubs)[i]; | 1579 stub = (*call_data.parameter_stubs)[i]; |
1562 stub->ReplaceUsesWith(actual->definition()); | 1580 stub->ReplaceUsesWith(actual->definition()); |
1563 } | 1581 } |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1640 } | 1658 } |
1641 | 1659 |
1642 | 1660 |
1643 // Build a DAG to dispatch to the inlined function bodies. Load the class | 1661 // Build a DAG to dispatch to the inlined function bodies. Load the class |
1644 // id of the receiver and make explicit comparisons for each inlined body, | 1662 // id of the receiver and make explicit comparisons for each inlined body, |
1645 // in frequency order. If all variants are inlined, the entry to the last | 1663 // in frequency order. If all variants are inlined, the entry to the last |
1646 // inlined body is guarded by a CheckClassId instruction which can deopt. | 1664 // inlined body is guarded by a CheckClassId instruction which can deopt. |
1647 // If not all variants are inlined, we add a PolymorphicInstanceCall | 1665 // If not all variants are inlined, we add a PolymorphicInstanceCall |
1648 // instruction to handle the non-inlined variants. | 1666 // instruction to handle the non-inlined variants. |
1649 TargetEntryInstr* PolymorphicInliner::BuildDecisionGraph() { | 1667 TargetEntryInstr* PolymorphicInliner::BuildDecisionGraph() { |
1668 const intptr_t try_idx = call_->GetBlock()->try_index(); | |
1669 | |
1650 // Start with a fresh target entry. | 1670 // Start with a fresh target entry. |
1651 TargetEntryInstr* entry = | 1671 TargetEntryInstr* entry = |
1652 new (Z) TargetEntryInstr(owner_->caller_graph()->allocate_block_id(), | 1672 new (Z) TargetEntryInstr(AllocateBlockId(), try_idx); |
1653 call_->GetBlock()->try_index()); | |
1654 entry->InheritDeoptTarget(zone(), call_); | 1673 entry->InheritDeoptTarget(zone(), call_); |
1655 | 1674 |
1656 // This function uses a cursor (a pointer to the 'current' instruction) to | 1675 // This function uses a cursor (a pointer to the 'current' instruction) to |
1657 // build the graph. The next instruction will be inserted after the | 1676 // build the graph. The next instruction will be inserted after the |
1658 // cursor. | 1677 // cursor. |
1659 TargetEntryInstr* current_block = entry; | 1678 BlockEntryInstr* current_block = entry; |
1660 Instruction* cursor = entry; | 1679 Instruction* cursor = entry; |
1661 | 1680 |
1662 Definition* receiver = call_->ArgumentAt(0); | 1681 Definition* receiver = call_->ArgumentAt(0); |
1663 // There are at least two variants including non-inlined ones, so we have | 1682 // There are at least two variants including non-inlined ones, so we have |
1664 // at least one branch on the class id. | 1683 // at least one branch on the class id. |
1665 LoadClassIdInstr* load_cid = | 1684 LoadClassIdInstr* load_cid = |
1666 new (Z) LoadClassIdInstr(new (Z) Value(receiver)); | 1685 new (Z) LoadClassIdInstr(new (Z) Value(receiver)); |
1667 load_cid->set_ssa_temp_index(owner_->caller_graph()->alloc_ssa_temp_index()); | 1686 load_cid->set_ssa_temp_index(owner_->caller_graph()->alloc_ssa_temp_index()); |
1668 cursor = AppendInstruction(cursor, load_cid); | 1687 cursor = AppendInstruction(cursor, load_cid); |
1688 bool follow_with_deopt = false; | |
1669 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { | 1689 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { |
1670 // 1. Guard the body with a class id check. | 1690 const CidRangeTarget& variant = inlined_variants_[i]; |
1671 if ((i == (inlined_variants_.length() - 1)) && | 1691 bool test_is_range = (variant.cid_start != variant.cid_end); |
Vyacheslav Egorov (Google)
2017/03/13 11:59:10
try to make more variable cost - this helps readab
| |
1692 bool is_last_test = (i == inlined_variants_.length() - 1); | |
1693 // 1. Guard the body with a class id check. We don't need any check if | |
1694 // it's the last test and global analysis has told us that the call is | |
1695 // complete. TODO(erikcorry): Enhance CheckClassIdInstr so it can take an | |
1696 // arbitrary CidRangeTarget. Currently we don't go into this branch if the | |
1697 // last test is a range test - instead we set the follow_with_deopt flag. | |
1698 if (is_last_test && (!test_is_range || call_->complete()) && | |
1672 non_inlined_variants_.is_empty()) { | 1699 non_inlined_variants_.is_empty()) { |
1673 // If it is the last variant use a check class id instruction which can | 1700 // If it is the last variant use a check class id instruction which can |
1674 // deoptimize, followed unconditionally by the body. Omit the check if | 1701 // deoptimize, followed unconditionally by the body. Omit the check if |
1675 // we know that we have covered all possible classes. | 1702 // we know that we have covered all possible classes. |
1676 if (!call_->complete()) { | 1703 if (!call_->complete()) { |
1704 ASSERT(!test_is_range); // See condition above. | |
1677 RedefinitionInstr* cid_redefinition = | 1705 RedefinitionInstr* cid_redefinition = |
1678 new RedefinitionInstr(new (Z) Value(load_cid)); | 1706 new RedefinitionInstr(new (Z) Value(load_cid)); |
1679 cid_redefinition->set_ssa_temp_index( | 1707 cid_redefinition->set_ssa_temp_index( |
1680 owner_->caller_graph()->alloc_ssa_temp_index()); | 1708 owner_->caller_graph()->alloc_ssa_temp_index()); |
1681 cursor = AppendInstruction(cursor, cid_redefinition); | 1709 cursor = AppendInstruction(cursor, cid_redefinition); |
1682 CheckClassIdInstr* check_class_id = new (Z) | 1710 CheckClassIdInstr* check_class_id = |
1683 CheckClassIdInstr(new (Z) Value(cid_redefinition), | 1711 new (Z) CheckClassIdInstr(new (Z) Value(cid_redefinition), |
1684 inlined_variants_[i].cid, call_->deopt_id()); | 1712 variant.cid_start, call_->deopt_id()); |
1685 check_class_id->InheritDeoptTarget(zone(), call_); | 1713 check_class_id->InheritDeoptTarget(zone(), call_); |
1686 cursor = AppendInstruction(cursor, check_class_id); | 1714 cursor = AppendInstruction(cursor, check_class_id); |
1687 } | 1715 } |
1688 | 1716 |
1689 // The next instruction is the first instruction of the inlined body. | 1717 // The next instruction is the first instruction of the inlined body. |
1690 // Handle the two possible cases (unshared and shared subsequent | 1718 // Handle the two possible cases (unshared and shared subsequent |
1691 // predecessors) separately. | 1719 // predecessors) separately. |
1692 BlockEntryInstr* callee_entry = inlined_entries_[i]; | 1720 BlockEntryInstr* callee_entry = inlined_entries_[i]; |
1693 if (callee_entry->IsGraphEntry()) { | 1721 if (callee_entry->IsGraphEntry()) { |
1694 // Unshared. Graft the normal entry on after the check class | 1722 // Unshared. Graft the normal entry on after the check class |
(...skipping 21 matching lines...) Expand all Loading... | |
1716 goto_join->InheritDeoptTarget(zone(), join); | 1744 goto_join->InheritDeoptTarget(zone(), join); |
1717 cursor->LinkTo(goto_join); | 1745 cursor->LinkTo(goto_join); |
1718 current_block->set_last_instruction(goto_join); | 1746 current_block->set_last_instruction(goto_join); |
1719 } else { | 1747 } else { |
1720 // There is no possibility of a TargetEntry (the first entry to a | 1748 // There is no possibility of a TargetEntry (the first entry to a |
1721 // shared inlined body) because this is the last inlined entry. | 1749 // shared inlined body) because this is the last inlined entry. |
1722 UNREACHABLE(); | 1750 UNREACHABLE(); |
1723 } | 1751 } |
1724 cursor = NULL; | 1752 cursor = NULL; |
1725 } else { | 1753 } else { |
1754 if (is_last_test && test_is_range) follow_with_deopt = true; | |
1726 // For all variants except the last, use a branch on the loaded class | 1755 // For all variants except the last, use a branch on the loaded class |
1727 // id. | 1756 // id. |
1728 const Smi& cid = Smi::ZoneHandle(Smi::New(inlined_variants_[i].cid)); | 1757 const Smi& cid = Smi::ZoneHandle(Smi::New(variant.cid_start)); |
1729 ConstantInstr* cid_constant = new ConstantInstr(cid); | 1758 ConstantInstr* cid_constant = owner_->caller_graph()->GetConstant(cid); |
1730 cid_constant->set_ssa_temp_index( | 1759 BranchInstr* branch; |
1731 owner_->caller_graph()->alloc_ssa_temp_index()); | 1760 BranchInstr* upper_limit_branch = NULL; |
1732 StrictCompareInstr* compare = new StrictCompareInstr( | 1761 BlockEntryInstr* cid_test_entry_block = current_block; |
1733 call_->instance_call()->token_pos(), Token::kEQ_STRICT, | 1762 if (test_is_range) { |
1734 new Value(load_cid), new Value(cid_constant), | 1763 // Double branch for testing a range of Cids. TODO(erikcorry): Make a |
1735 false); // No number check. | 1764 // special instruction that uses subtraction and unsigned comparison to |
1736 BranchInstr* branch = new BranchInstr(compare); | 1765 // do this with a single branch. |
1766 const Smi& cid_end = Smi::ZoneHandle(Smi::New(variant.cid_end)); | |
1767 ConstantInstr* cid_constant_end = | |
1768 owner_->caller_graph()->GetConstant(cid_end); | |
1769 RelationalOpInstr* compare_top = new RelationalOpInstr( | |
Florian Schneider
2017/03/16 20:31:40
dbc: Have you tried using _classRangeCheck from in
| |
1770 call_->instance_call()->token_pos(), Token::kLTE, | |
1771 new Value(load_cid), new Value(cid_constant_end), kSmiCid, | |
1772 call_->deopt_id()); | |
1773 BranchInstr* branch_top = upper_limit_branch = | |
1774 new BranchInstr(compare_top); | |
1775 branch_top->InheritDeoptTarget(zone(), call_); | |
1776 cursor = AppendInstruction(cursor, branch_top); | |
1777 current_block->set_last_instruction(branch_top); | |
1778 | |
1779 TargetEntryInstr* below_target = | |
1780 new TargetEntryInstr(AllocateBlockId(), try_idx); | |
1781 below_target->InheritDeoptTarget(zone(), call_); | |
1782 current_block->AddDominatedBlock(below_target); | |
1783 cursor = current_block = below_target; | |
1784 *branch_top->true_successor_address() = below_target; | |
1785 | |
1786 RelationalOpInstr* compare_bottom = new RelationalOpInstr( | |
1787 call_->instance_call()->token_pos(), Token::kGTE, | |
1788 new Value(load_cid), new Value(cid_constant), kSmiCid, | |
1789 call_->deopt_id()); | |
1790 branch = new BranchInstr(compare_bottom); | |
1791 } else { | |
1792 StrictCompareInstr* compare = new StrictCompareInstr( | |
1793 call_->instance_call()->token_pos(), Token::kEQ_STRICT, | |
1794 new Value(load_cid), new Value(cid_constant), | |
1795 false); // No number check. | |
1796 branch = new BranchInstr(compare); | |
1797 } | |
1798 | |
1737 branch->InheritDeoptTarget(zone(), call_); | 1799 branch->InheritDeoptTarget(zone(), call_); |
1738 AppendInstruction(AppendInstruction(cursor, cid_constant), branch); | 1800 cursor = AppendInstruction(cursor, branch); |
1739 current_block->set_last_instruction(branch); | 1801 current_block->set_last_instruction(branch); |
1740 cursor = NULL; | 1802 cursor = NULL; |
1741 | 1803 |
1742 // 2. Handle a match by linking to the inlined body. There are three | 1804 // 2. Handle a match by linking to the inlined body. There are three |
1743 // cases (unshared, shared first predecessor, and shared subsequent | 1805 // cases (unshared, shared first predecessor, and shared subsequent |
1744 // predecessors). | 1806 // predecessors). |
1745 BlockEntryInstr* callee_entry = inlined_entries_[i]; | 1807 BlockEntryInstr* callee_entry = inlined_entries_[i]; |
1746 TargetEntryInstr* true_target = NULL; | 1808 TargetEntryInstr* true_target = NULL; |
1747 if (callee_entry->IsGraphEntry()) { | 1809 if (callee_entry->IsGraphEntry()) { |
1748 // Unshared. | 1810 // Unshared. |
1749 true_target = callee_entry->AsGraphEntry()->normal_entry(); | 1811 true_target = callee_entry->AsGraphEntry()->normal_entry(); |
1750 // Unuse all inputs of the graph entry. It is not in the graph anymore. | 1812 // Unuse all inputs of the graph entry. It is not in the graph anymore. |
1751 callee_entry->UnuseAllInputs(); | 1813 callee_entry->UnuseAllInputs(); |
1752 } else if (callee_entry->IsTargetEntry()) { | 1814 } else if (callee_entry->IsTargetEntry()) { |
1753 // Shared inlined body and this is the first entry. We have already | 1815 // Shared inlined body and this is the first entry. We have already |
1754 // constructed a join and this target jumps to it. | 1816 // constructed a join and this target jumps to it. |
1755 true_target = callee_entry->AsTargetEntry(); | 1817 true_target = callee_entry->AsTargetEntry(); |
1756 BlockEntryInstr* join = true_target->last_instruction()->SuccessorAt(0); | 1818 BlockEntryInstr* join = true_target->last_instruction()->SuccessorAt(0); |
1757 current_block->AddDominatedBlock(join); | 1819 current_block->AddDominatedBlock(join); |
1758 } else { | 1820 } else { |
1759 // Shared inlined body and this is a subsequent entry. We have | 1821 // Shared inlined body and this is a subsequent entry. We have |
1760 // already constructed a join. We need a fresh target that jumps to | 1822 // already constructed a join. We need a fresh target that jumps to |
1761 // the join. | 1823 // the join. |
1762 JoinEntryInstr* join = callee_entry->AsJoinEntry(); | 1824 JoinEntryInstr* join = callee_entry->AsJoinEntry(); |
1763 ASSERT(join != NULL); | 1825 ASSERT(join != NULL); |
1764 ASSERT(join->dominator() != NULL); | 1826 ASSERT(join->dominator() != NULL); |
1765 true_target = | 1827 true_target = new TargetEntryInstr(AllocateBlockId(), try_idx); |
1766 new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(), | |
1767 call_->GetBlock()->try_index()); | |
1768 true_target->InheritDeoptTarget(zone(), join); | 1828 true_target->InheritDeoptTarget(zone(), join); |
1769 GotoInstr* goto_join = new GotoInstr(join); | 1829 GotoInstr* goto_join = new GotoInstr(join); |
1770 goto_join->InheritDeoptTarget(zone(), join); | 1830 goto_join->InheritDeoptTarget(zone(), join); |
1771 true_target->LinkTo(goto_join); | 1831 true_target->LinkTo(goto_join); |
1772 true_target->set_last_instruction(goto_join); | 1832 true_target->set_last_instruction(goto_join); |
1773 } | 1833 } |
1774 *branch->true_successor_address() = true_target; | 1834 *branch->true_successor_address() = true_target; |
1775 current_block->AddDominatedBlock(true_target); | 1835 current_block->AddDominatedBlock(true_target); |
1776 | 1836 |
1777 // 3. Prepare to handle a match failure on the next iteration or the | 1837 // 3. Prepare to handle a match failure on the next iteration or the |
1778 // fall-through code below for non-inlined variants. | 1838 // fall-through code below for non-inlined variants. |
1839 | |
1779 TargetEntryInstr* false_target = | 1840 TargetEntryInstr* false_target = |
1780 new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(), | 1841 new TargetEntryInstr(AllocateBlockId(), try_idx); |
1781 call_->GetBlock()->try_index()); | |
1782 false_target->InheritDeoptTarget(zone(), call_); | 1842 false_target->InheritDeoptTarget(zone(), call_); |
1783 *branch->false_successor_address() = false_target; | 1843 *branch->false_successor_address() = false_target; |
1784 current_block->AddDominatedBlock(false_target); | 1844 cid_test_entry_block->AddDominatedBlock(false_target); |
1845 | |
1785 cursor = current_block = false_target; | 1846 cursor = current_block = false_target; |
1847 | |
1848 if (test_is_range) { | |
1849 // If we tested against a range of Cids there are two different tests | |
1850 // that can go to the no-cid-match target. | |
1851 JoinEntryInstr* join = new JoinEntryInstr(AllocateBlockId(), try_idx); | |
1852 TargetEntryInstr* false_target2 = | |
1853 new TargetEntryInstr(AllocateBlockId(), try_idx); | |
1854 *upper_limit_branch->false_successor_address() = false_target2; | |
1855 cid_test_entry_block->AddDominatedBlock(false_target2); | |
1856 cid_test_entry_block->AddDominatedBlock(join); | |
1857 GotoInstr* goto_1 = new GotoInstr(join); | |
1858 GotoInstr* goto_2 = new GotoInstr(join); | |
1859 false_target->LinkTo(goto_1); | |
1860 false_target2->LinkTo(goto_2); | |
1861 false_target->set_last_instruction(goto_1); | |
1862 false_target2->set_last_instruction(goto_2); | |
1863 | |
1864 join->InheritDeoptTarget(zone(), call_); | |
1865 false_target2->InheritDeoptTarget(zone(), call_); | |
1866 goto_1->InheritDeoptTarget(zone(), call_); | |
1867 goto_2->InheritDeoptTarget(zone(), call_); | |
1868 | |
1869 cursor = current_block = join; | |
1870 } | |
1786 } | 1871 } |
1787 } | 1872 } |
1788 | 1873 |
1789 // Handle any non-inlined variants. | 1874 // Handle any non-inlined variants. |
1790 if (!non_inlined_variants_.is_empty()) { | 1875 if (!non_inlined_variants_.is_empty()) { |
1791 // Move push arguments of the call. | 1876 // Move push arguments of the call. |
1792 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { | 1877 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { |
1793 PushArgumentInstr* push = call_->PushArgumentAt(i); | 1878 PushArgumentInstr* push = call_->PushArgumentAt(i); |
1794 push->ReplaceUsesWith(push->value()->definition()); | 1879 push->ReplaceUsesWith(push->value()->definition()); |
1795 push->previous()->LinkTo(push->next()); | 1880 push->previous()->LinkTo(push->next()); |
1796 cursor->LinkTo(push); | 1881 cursor->LinkTo(push); |
1797 cursor = push; | 1882 cursor = push; |
1798 } | 1883 } |
1799 const ICData& old_checks = call_->ic_data(); | 1884 const ICData& old_checks = call_->ic_data(); |
1800 const ICData& new_checks = ICData::ZoneHandle(ICData::New( | 1885 const ICData& new_checks = ICData::ZoneHandle(ICData::New( |
1801 Function::Handle(old_checks.Owner()), | 1886 Function::Handle(old_checks.Owner()), |
1802 String::Handle(old_checks.target_name()), | 1887 String::Handle(old_checks.target_name()), |
1803 Array::Handle(old_checks.arguments_descriptor()), old_checks.deopt_id(), | 1888 Array::Handle(old_checks.arguments_descriptor()), old_checks.deopt_id(), |
1804 1, // Number of args tested. | 1889 1, // Number of args tested. |
1805 false)); // is_static_call | 1890 false)); // is_static_call |
1806 for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) { | 1891 for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) { |
1807 new_checks.AddReceiverCheck(non_inlined_variants_[i].cid, | 1892 // We are adding all the cids in each range. They will be joined |
1808 *non_inlined_variants_[i].target, | 1893 // together again by the PolymorphicInstanceCall instruction, which is a |
1809 non_inlined_variants_[i].count); | 1894 // bit messy. |
1895 intptr_t count = non_inlined_variants_[i].count; | |
1896 | |
1897 for (intptr_t j = non_inlined_variants_[i].cid_start; | |
1898 j <= non_inlined_variants_[i].cid_end; j++) { | |
1899 new_checks.AddReceiverCheck(j, *non_inlined_variants_[i].target, count); | |
1900 count = 0; | |
1901 } | |
1810 } | 1902 } |
1811 PolymorphicInstanceCallInstr* fallback_call = | 1903 PolymorphicInstanceCallInstr* fallback_call = |
1812 new PolymorphicInstanceCallInstr(call_->instance_call(), new_checks, | 1904 new PolymorphicInstanceCallInstr(call_->instance_call(), new_checks, |
1813 /* with_checks = */ true, | 1905 /* with_checks = */ true, |
1814 call_->complete()); | 1906 call_->complete()); |
1815 fallback_call->set_ssa_temp_index( | 1907 fallback_call->set_ssa_temp_index( |
1816 owner_->caller_graph()->alloc_ssa_temp_index()); | 1908 owner_->caller_graph()->alloc_ssa_temp_index()); |
1817 fallback_call->InheritDeoptTarget(zone(), call_); | 1909 fallback_call->InheritDeoptTarget(zone(), call_); |
1910 fallback_call->set_total_call_count(call_->CallCount()); | |
1818 ReturnInstr* fallback_return = new ReturnInstr( | 1911 ReturnInstr* fallback_return = new ReturnInstr( |
1819 call_->instance_call()->token_pos(), new Value(fallback_call)); | 1912 call_->instance_call()->token_pos(), new Value(fallback_call)); |
1820 fallback_return->InheritDeoptTargetAfter(owner_->caller_graph(), call_, | 1913 fallback_return->InheritDeoptTargetAfter(owner_->caller_graph(), call_, |
1821 fallback_call); | 1914 fallback_call); |
1822 AppendInstruction(AppendInstruction(cursor, fallback_call), | 1915 AppendInstruction(AppendInstruction(cursor, fallback_call), |
1823 fallback_return); | 1916 fallback_return); |
1824 exit_collector_->AddExit(fallback_return); | 1917 exit_collector_->AddExit(fallback_return); |
1825 cursor = NULL; | 1918 cursor = NULL; |
1826 } else { | 1919 } else { |
1920 if (follow_with_deopt) { | |
1921 DeoptimizeInstr* deopt = new DeoptimizeInstr( | |
1922 ICData::kDeoptPolymorphicInstanceCallTestFail, call_->deopt_id()); | |
1923 deopt->InheritDeoptTarget(zone(), call_); | |
1924 cursor = AppendInstruction(cursor, deopt); | |
1925 cursor = NULL; | |
1926 } | |
1927 | |
1827 // Remove push arguments of the call. | 1928 // Remove push arguments of the call. |
1828 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { | 1929 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { |
1829 PushArgumentInstr* push = call_->PushArgumentAt(i); | 1930 PushArgumentInstr* push = call_->PushArgumentAt(i); |
1830 push->ReplaceUsesWith(push->value()->definition()); | 1931 push->ReplaceUsesWith(push->value()->definition()); |
1831 push->RemoveFromGraph(); | 1932 push->RemoveFromGraph(); |
1832 } | 1933 } |
1833 } | 1934 } |
1834 return entry; | 1935 return entry; |
1835 } | 1936 } |
1836 | 1937 |
1837 | 1938 |
1939 static void TracePolyInlining(const CidRangeTarget& crt, | |
1940 intptr_t total, | |
1941 const char* message) { | |
1942 String& name = String::Handle(crt.target->QualifiedUserVisibleName()); | |
1943 int percent = total == 0 ? 0 : (100 * crt.count) / total; | |
1944 THR_Print("%s cid %" Pd "-%" Pd ": %" Pd "/%" Pd " %d%% %s\n", | |
1945 name.ToCString(), crt.cid_start, crt.cid_end, crt.count, total, | |
1946 percent, message); | |
1947 } | |
1948 | |
1949 | |
1950 bool PolymorphicInliner::trace_inlining() const { | |
1951 return owner_->trace_inlining(); | |
1952 } | |
1953 | |
1954 | |
1838 void PolymorphicInliner::Inline() { | 1955 void PolymorphicInliner::Inline() { |
1839 // Consider the polymorphic variants in order by frequency. | 1956 // Consider the polymorphic variants in order by frequency. |
1840 FlowGraphCompiler::SortICDataByCount(call_->ic_data(), &variants_, | 1957 FlowGraphCompiler::SortICDataByCount(call_->ic_data(), &variants_, |
1841 /* drop_smi = */ false); | 1958 /* drop_smi = */ false); |
1959 intptr_t total = call_->total_call_count(); | |
1842 for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) { | 1960 for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) { |
1961 // We we almost inlined all the cases then try a little harder to inline | |
1962 // the last two, because it's a big win if we inline all of them (compiler | |
1963 // can see all side effects). | |
1964 bool try_harder = (var_idx >= variants_.length() - 2) && | |
1965 non_inlined_variants_.length() == 0; | |
1843 const Function& target = *variants_[var_idx].target; | 1966 const Function& target = *variants_[var_idx].target; |
1844 const intptr_t receiver_cid = variants_[var_idx].cid; | 1967 const intptr_t count = variants_[var_idx].count; |
1968 | |
1969 intptr_t size = target.optimized_instruction_count(); | |
1970 bool small = (size != 0 && size < FLAG_inlining_size_threshold); | |
1971 | |
1972 // If it's less than 3% of the dispatches, we won't even consider | |
1973 // checking for the class ID and branching to another already-inlined | |
1974 // version. | |
1975 if (!try_harder && count < (total >> 5)) { | |
1976 TRACE_INLINING( | |
1977 TracePolyInlining(variants_[var_idx], total, "way too infrequent")); | |
1978 non_inlined_variants_.Add(variants_[var_idx]); | |
1979 continue; | |
1980 } | |
1845 | 1981 |
1846 // First check if this is the same target as an earlier inlined variant. | 1982 // First check if this is the same target as an earlier inlined variant. |
1847 if (CheckInlinedDuplicate(target)) { | 1983 if (CheckInlinedDuplicate(target)) { |
1984 TRACE_INLINING(TracePolyInlining(variants_[var_idx], total, | |
1985 "duplicate already inlined")); | |
1848 inlined_variants_.Add(variants_[var_idx]); | 1986 inlined_variants_.Add(variants_[var_idx]); |
1849 continue; | 1987 continue; |
1850 } | 1988 } |
1851 | 1989 |
1990 // If it's less than 12% of the dispatches and it's not already inlined, we | |
1991 // don't consider inlining. For very small functions we are willing to | |
1992 // consider inlining for 6% of the cases. | |
1993 if (!try_harder && count < (total >> (small ? 4 : 3))) { | |
1994 TRACE_INLINING( | |
1995 TracePolyInlining(variants_[var_idx], total, "too infrequent")); | |
1996 non_inlined_variants_.Add(variants_[var_idx]); | |
1997 continue; | |
1998 } | |
1999 | |
1852 // Also check if this is the same target as an earlier non-inlined | 2000 // Also check if this is the same target as an earlier non-inlined |
1853 // variant. If so and since inlining decisions are costly, do not try | 2001 // variant. If so and since inlining decisions are costly, do not try |
1854 // to inline this variant. | 2002 // to inline this variant. |
1855 if (CheckNonInlinedDuplicate(target)) { | 2003 if (CheckNonInlinedDuplicate(target)) { |
2004 TRACE_INLINING( | |
2005 TracePolyInlining(variants_[var_idx], total, "already not inlined")); | |
1856 non_inlined_variants_.Add(variants_[var_idx]); | 2006 non_inlined_variants_.Add(variants_[var_idx]); |
1857 continue; | 2007 continue; |
1858 } | 2008 } |
1859 | 2009 |
1860 // Make an inlining decision. | 2010 // Make an inlining decision. |
1861 if (TryInliningPoly(receiver_cid, target)) { | 2011 if (TryInliningPoly(variants_[var_idx])) { |
2012 TRACE_INLINING(TracePolyInlining(variants_[var_idx], total, "inlined")); | |
1862 inlined_variants_.Add(variants_[var_idx]); | 2013 inlined_variants_.Add(variants_[var_idx]); |
1863 } else { | 2014 } else { |
2015 TRACE_INLINING( | |
2016 TracePolyInlining(variants_[var_idx], total, "not inlined")); | |
1864 non_inlined_variants_.Add(variants_[var_idx]); | 2017 non_inlined_variants_.Add(variants_[var_idx]); |
1865 } | 2018 } |
1866 } | 2019 } |
1867 | 2020 |
1868 // If there are no inlined variants, leave the call in place. | 2021 // If there are no inlined variants, leave the call in place. |
1869 if (inlined_variants_.is_empty()) return; | 2022 if (inlined_variants_.is_empty()) return; |
1870 | 2023 |
1871 // Now build a decision tree (a DAG because of shared inline variants) and | 2024 // Now build a decision tree (a DAG because of shared inline variants) and |
1872 // inline it at the call site. | 2025 // inline it at the call site. |
1873 TargetEntryInstr* entry = BuildDecisionGraph(); | 2026 TargetEntryInstr* entry = BuildDecisionGraph(); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1984 // Collect graph info and store it on the function. | 2137 // Collect graph info and store it on the function. |
1985 // We might later use it for an early bailout from the inlining. | 2138 // We might later use it for an early bailout from the inlining. |
1986 CollectGraphInfo(flow_graph_); | 2139 CollectGraphInfo(flow_graph_); |
1987 | 2140 |
1988 const Function& top = flow_graph_->function(); | 2141 const Function& top = flow_graph_->function(); |
1989 if ((FLAG_inlining_filter != NULL) && | 2142 if ((FLAG_inlining_filter != NULL) && |
1990 (strstr(top.ToFullyQualifiedCString(), FLAG_inlining_filter) == NULL)) { | 2143 (strstr(top.ToFullyQualifiedCString(), FLAG_inlining_filter) == NULL)) { |
1991 return; | 2144 return; |
1992 } | 2145 } |
1993 | 2146 |
1994 TRACE_INLINING(THR_Print("Inlining calls in %s\n", top.ToCString())); | 2147 if (trace_inlining()) { |
2148 String& name = String::Handle(top.QualifiedUserVisibleName()); | |
2149 THR_Print("Inlining calls in %s\n", name.ToCString()); | |
2150 } | |
1995 | 2151 |
1996 if (FLAG_support_il_printer && trace_inlining() && | 2152 if (FLAG_support_il_printer && trace_inlining() && |
1997 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { | 2153 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { |
1998 THR_Print("Before Inlining of %s\n", | 2154 THR_Print("Before Inlining of %s\n", |
1999 flow_graph_->function().ToFullyQualifiedCString()); | 2155 flow_graph_->function().ToFullyQualifiedCString()); |
2000 FlowGraphPrinter printer(*flow_graph_); | 2156 FlowGraphPrinter printer(*flow_graph_); |
2001 printer.PrintBlocks(); | 2157 printer.PrintBlocks(); |
2002 } | 2158 } |
2003 | 2159 |
2004 intptr_t inlining_depth_threshold = FLAG_inlining_depth_threshold; | 2160 intptr_t inlining_depth_threshold = FLAG_inlining_depth_threshold; |
(...skipping 735 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2740 *entry = new (Z) TargetEntryInstr(flow_graph->allocate_block_id(), | 2896 *entry = new (Z) TargetEntryInstr(flow_graph->allocate_block_id(), |
2741 call->GetBlock()->try_index()); | 2897 call->GetBlock()->try_index()); |
2742 (*entry)->InheritDeoptTarget(Z, call); | 2898 (*entry)->InheritDeoptTarget(Z, call); |
2743 | 2899 |
2744 *last = PrepareInlineStringIndexOp(flow_graph, call, cid, str, index, *entry); | 2900 *last = PrepareInlineStringIndexOp(flow_graph, call, cid, str, index, *entry); |
2745 | 2901 |
2746 return true; | 2902 return true; |
2747 } | 2903 } |
2748 | 2904 |
2749 | 2905 |
2906 // Only used for monomorphic calls. | |
2750 bool FlowGraphInliner::TryReplaceInstanceCallWithInline( | 2907 bool FlowGraphInliner::TryReplaceInstanceCallWithInline( |
2751 FlowGraph* flow_graph, | 2908 FlowGraph* flow_graph, |
2752 ForwardInstructionIterator* iterator, | 2909 ForwardInstructionIterator* iterator, |
2753 InstanceCallInstr* call) { | 2910 InstanceCallInstr* call) { |
2754 Function& target = Function::Handle(Z); | 2911 Function& target = Function::Handle(Z); |
2755 GrowableArray<intptr_t> class_ids; | 2912 GrowableArray<intptr_t> class_ids; |
2756 call->ic_data()->GetCheckAt(0, &class_ids, &target); | 2913 call->ic_data()->GetCheckAt(0, &class_ids, &target); |
2757 const intptr_t receiver_cid = class_ids[0]; | 2914 const intptr_t receiver_cid = class_ids[0]; |
2758 | 2915 |
2759 TargetEntryInstr* entry; | 2916 TargetEntryInstr* entry; |
(...skipping 897 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3657 } | 3814 } |
3658 | 3815 |
3659 default: | 3816 default: |
3660 return false; | 3817 return false; |
3661 } | 3818 } |
3662 } | 3819 } |
3663 | 3820 |
3664 | 3821 |
3665 } // namespace dart | 3822 } // namespace dart |
3666 #endif // !defined(DART_PRECOMPILED_RUNTIME) | 3823 #endif // !defined(DART_PRECOMPILED_RUNTIME) |
OLD | NEW |