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