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

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

Powered by Google App Engine
This is Rietveld 408576698