Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: runtime/vm/flow_graph_inliner.cc

Issue 2737303003: Allow dispatch to use a range of Class-ids in tests (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698