Chromium Code Reviews| 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 |