Flutter Engine
The Flutter Engine
Classes | Public Member Functions | Friends | List of all members
dart::CallSiteInliner Class Reference
Inheritance diagram for dart::CallSiteInliner:
dart::ValueObject

Classes

struct  InliningDecision
 

Public Member Functions

 CallSiteInliner (FlowGraphInliner *inliner, intptr_t threshold)
 
FlowGraphcaller_graph () const
 
Threadthread () const
 
Zonezone () const
 
bool trace_inlining () const
 
int inlining_depth ()
 
InliningDecision ShouldWeInline (const Function &callee, intptr_t instr_count, intptr_t call_site_count)
 
void InlineCalls ()
 
bool inlined () const
 
double GrowthFactor () const
 
DefinitionCreateParameterStub (intptr_t i, Value *argument, FlowGraph *graph)
 
bool TryInlining (const Function &function, const Array &argument_names, InlinedCallData *call_data, bool stricter_heuristic)
 
bool TryInliningImpl (const Function &function, const Array &argument_names, InlinedCallData *call_data, bool stricter_heuristic)
 
void PrintInlinedInfo (const Function &top)
 
- Public Member Functions inherited from dart::ValueObject
 ValueObject ()
 
 ~ValueObject ()
 

Friends

class PolymorphicInliner
 

Detailed Description

Definition at line 979 of file inliner.cc.

Constructor & Destructor Documentation

◆ CallSiteInliner()

dart::CallSiteInliner::CallSiteInliner ( FlowGraphInliner inliner,
intptr_t  threshold 
)
inlineexplicit

Definition at line 981 of file inliner.cc.

982 : inliner_(inliner),
983 caller_graph_(inliner->flow_graph()),
984 inlined_(false),
985 initial_size_(inliner->flow_graph()->InstructionCount()),
986 inlined_size_(0),
987 inlined_recursive_call_(false),
988 inlining_depth_(1),
989 inlining_recursion_depth_(0),
990 inlining_depth_threshold_(threshold),
991 collected_call_sites_(nullptr),
992 inlining_call_sites_(nullptr),
993 function_cache_(),
994 inlined_info_() {}

Member Function Documentation

◆ caller_graph()

FlowGraph * dart::CallSiteInliner::caller_graph ( ) const
inline

Definition at line 996 of file inliner.cc.

996{ return caller_graph_; }

◆ CreateParameterStub()

Definition * dart::CallSiteInliner::CreateParameterStub ( intptr_t  i,
Value argument,
FlowGraph graph 
)
inline

Definition at line 1113 of file inliner.cc.

1115 {
1116 ConstantInstr* constant = argument->definition()->AsConstant();
1117 if (constant != nullptr) {
1118 return graph->GetConstant(constant->value());
1119 }
1120 ParameterInstr* param = new (Z) ParameterInstr(
1121 graph->graph_entry(),
1122 /*env_index=*/i, /*param_index=*/i, Location(), kNoRepresentation);
1123 if (i >= 0) {
1124 // Compute initial parameter type using static and inferred types
1125 // and combine it with an argument type from the caller.
1126 param->UpdateType(
1127 *CompileType::ComputeRefinedType(param->Type(), argument->Type()));
1128 } else {
1129 // Parameter stub for function type arguments.
1130 // It doesn't correspond to a real parameter, so don't try to
1131 // query its static/inferred type.
1132 param->UpdateType(*argument->Type());
1133 }
1134 return param;
1135 }
static CompileType * ComputeRefinedType(CompileType *old_type, CompileType *new_type)
#define Z
Definition: inliner.cc:88

◆ GrowthFactor()

double dart::CallSiteInliner::GrowthFactor ( ) const
inline

Definition at line 1107 of file inliner.cc.

1107 {
1108 return static_cast<double>(inlined_size_) /
1109 static_cast<double>(initial_size_);
1110 }

◆ InlineCalls()

void dart::CallSiteInliner::InlineCalls ( )
inline

Definition at line 1051 of file inliner.cc.

1051 {
1052 // If inlining depth is less than one abort.
1053 if (inlining_depth_threshold_ < 1) return;
1054 if (caller_graph_->function().deoptimization_counter() >=
1055 FLAG_deoptimization_counter_inlining_threshold) {
1056 return;
1057 }
1058 // Create two call site collections to swap between.
1059 GrowableArray<CallSites::CallInfo<InstanceCallInstr>> calls;
1060 CallSites sites1(inlining_depth_threshold_, &calls);
1061 CallSites sites2(inlining_depth_threshold_, &calls);
1062 CallSites* call_sites_temp = nullptr;
1063 collected_call_sites_ = &sites1;
1064 inlining_call_sites_ = &sites2;
1065 // Collect initial call sites.
1066 collected_call_sites_->FindCallSites(caller_graph_, inlining_depth_,
1067 &inlined_info_);
1068 while (collected_call_sites_->HasCalls()) {
1070 THR_Print(" Depth %" Pd " ----------\n", inlining_depth_));
1071 if (FLAG_print_inlining_tree) {
1072 THR_Print("**Depth % " Pd " calls to inline %" Pd " (threshold % " Pd
1073 ")\n",
1074 inlining_depth_, collected_call_sites_->NumCalls(),
1075 static_cast<intptr_t>(FLAG_max_inlined_per_depth));
1076 }
1077 if (collected_call_sites_->NumCalls() > FLAG_max_inlined_per_depth) {
1078 break;
1079 }
1080 // Swap collected and inlining arrays and clear the new collecting array.
1081 call_sites_temp = collected_call_sites_;
1082 collected_call_sites_ = inlining_call_sites_;
1083 inlining_call_sites_ = call_sites_temp;
1084 collected_call_sites_->Clear();
1085 // Inline call sites at the current depth.
1086 bool inlined_instance = InlineInstanceCalls();
1087 bool inlined_statics = InlineStaticCalls();
1088 bool inlined_closures = InlineClosureCalls();
1089 if (inlined_instance || inlined_statics || inlined_closures) {
1090 collected_call_sites_->TryDevirtualize(caller_graph());
1091 // Increment the inlining depths. Checked before subsequent inlining.
1092 ++inlining_depth_;
1093 if (inlined_recursive_call_) {
1094 ++inlining_recursion_depth_;
1095 inlined_recursive_call_ = false;
1096 }
1098 }
1099 }
1100
1101 collected_call_sites_ = nullptr;
1102 inlining_call_sites_ = nullptr;
1103 }
FlowGraph * caller_graph() const
Definition: inliner.cc:996
Thread * thread() const
Definition: inliner.cc:998
void FindCallSites(FlowGraph *graph, intptr_t depth, GrowableArray< InlinedInfo > *inlined_info)
Definition: inliner.cc:543
void Clear()
Definition: inliner.cc:321
intptr_t NumCalls() const
Definition: inliner.cc:316
bool HasCalls() const
Definition: inliner.cc:311
void TryDevirtualize(FlowGraph *graph)
Definition: inliner.cc:423
const Function & function() const
Definition: flow_graph.h:130
void CheckForSafepoint()
Definition: thread.h:1104
#define THR_Print(format,...)
Definition: log.h:20
#define TRACE_INLINING(statement)
Definition: inliner.cc:90
#define Pd
Definition: globals.h:408

◆ inlined()

bool dart::CallSiteInliner::inlined ( ) const
inline

Definition at line 1105 of file inliner.cc.

1105{ return inlined_; }

◆ inlining_depth()

int dart::CallSiteInliner::inlining_depth ( )
inline

Definition at line 1003 of file inliner.cc.

1003{ return inlining_depth_; }

◆ PrintInlinedInfo()

void dart::CallSiteInliner::PrintInlinedInfo ( const Function top)
inline

Definition at line 1618 of file inliner.cc.

1618 {
1619 if (inlined_info_.length() > 0) {
1620 THR_Print("Inlining into: '%s'\n growth: %f (%" Pd " -> %" Pd ")\n",
1621 top.ToFullyQualifiedCString(), GrowthFactor(), initial_size_,
1622 inlined_size_);
1623 PrintInlinedInfoFor(top, 1);
1624 }
1625 }
double GrowthFactor() const
Definition: inliner.cc:1107

◆ ShouldWeInline()

InliningDecision dart::CallSiteInliner::ShouldWeInline ( const Function callee,
intptr_t  instr_count,
intptr_t  call_site_count 
)
inline

Definition at line 1018 of file inliner.cc.

1020 {
1021 // Pragma or size heuristics.
1022 if (inliner_->AlwaysInline(callee)) {
1023 return InliningDecision::Yes("AlwaysInline");
1024 } else if (inlined_size_ > FLAG_inlining_caller_size_threshold) {
1025 // Prevent caller methods becoming humongous and thus slow to compile.
1026 return InliningDecision::No("--inlining-caller-size-threshold");
1027 } else if (instr_count > FLAG_inlining_callee_size_threshold) {
1028 // Prevent inlining of callee methods that exceed certain size.
1029 return InliningDecision::No("--inlining-callee-size-threshold");
1030 }
1031 // Inlining depth.
1032 const int callee_inlining_depth = callee.inlining_depth();
1033 if (callee_inlining_depth > 0 &&
1034 ((callee_inlining_depth + inlining_depth_) >
1035 FLAG_inlining_depth_threshold)) {
1036 return InliningDecision::No("--inlining-depth-threshold");
1037 }
1038 // Situation instr_count == 0 denotes no counts have been computed yet.
1039 // In that case, we say ok to the early heuristic and come back with the
1040 // late heuristic.
1041 if (instr_count == 0) {
1042 return InliningDecision::Yes("need to count first");
1043 } else if (instr_count <= FLAG_inlining_size_threshold) {
1044 return InliningDecision::Yes("--inlining-size-threshold");
1045 } else if (call_site_count <= FLAG_inlining_callee_call_sites_threshold) {
1046 return InliningDecision::Yes("--inlining-callee-call-sites-threshold");
1047 }
1048 return InliningDecision::No("default");
1049 }
bool AlwaysInline(const Function &function)
Definition: inliner.cc:2512
static InliningDecision No(const char *reason)
Definition: inliner.cc:1012
static InliningDecision Yes(const char *reason)
Definition: inliner.cc:1009

◆ thread()

Thread * dart::CallSiteInliner::thread ( ) const
inline

Definition at line 998 of file inliner.cc.

998{ return caller_graph_->thread(); }
Thread * thread() const
Definition: flow_graph.h:260

◆ trace_inlining()

bool dart::CallSiteInliner::trace_inlining ( ) const
inline

Definition at line 1001 of file inliner.cc.

1001{ return inliner_->trace_inlining(); }
bool trace_inlining() const
Definition: inliner.h:132

◆ TryInlining()

bool dart::CallSiteInliner::TryInlining ( const Function function,
const Array argument_names,
InlinedCallData call_data,
bool  stricter_heuristic 
)
inline

Definition at line 1137 of file inliner.cc.

1140 {
1141 Timer timer;
1142 if (thread()->compiler_timings() != nullptr) {
1143 timer.Start();
1144 }
1145 const bool success = TryInliningImpl(function, argument_names, call_data,
1146 stricter_heuristic);
1147 if (thread()->compiler_timings() != nullptr) {
1148 timer.Stop();
1150 timer);
1151 }
1152 return success;
1153 }
bool TryInliningImpl(const Function &function, const Array &argument_names, InlinedCallData *call_data, bool stricter_heuristic)
Definition: inliner.cc:1155
void RecordInliningStatsByOutcome(bool success, const Timer &timer)
CompilerTimings * compiler_timings() const
Definition: thread.h:617
Dart_NativeFunction function
Definition: fuchsia.cc:51

◆ TryInliningImpl()

bool dart::CallSiteInliner::TryInliningImpl ( const Function function,
const Array argument_names,
InlinedCallData call_data,
bool  stricter_heuristic 
)
inline

Definition at line 1155 of file inliner.cc.

1158 {
1159 if (trace_inlining()) {
1160 String& name = String::Handle(function.QualifiedUserVisibleName());
1161 THR_Print(" => %s (deopt count %d)\n", name.ToCString(),
1162 function.deoptimization_counter());
1163 }
1164
1165 // Abort if the inlinable bit on the function is low.
1166 if (!function.CanBeInlined()) {
1168 " Bailout: not inlinable due to !function.CanBeInlined()\n"));
1169 PRINT_INLINING_TREE("Not inlinable", &call_data->caller, &function,
1170 call_data->call);
1171 return false;
1172 }
1173
1175 TRACE_INLINING(THR_Print(" Bailout: vm:never-inline pragma\n"));
1176 PRINT_INLINING_TREE("vm:never-inline", &call_data->caller, &function,
1177 call_data->call);
1178 return false;
1179 }
1180
1181 // Don't inline any intrinsified functions in precompiled mode
1182 // to reduce code size and make sure we use the intrinsic code.
1183 if (CompilerState::Current().is_aot() && function.is_intrinsic() &&
1184 !inliner_->AlwaysInline(function)) {
1185 TRACE_INLINING(THR_Print(" Bailout: intrinsic\n"));
1186 PRINT_INLINING_TREE("intrinsic", &call_data->caller, &function,
1187 call_data->call);
1188 return false;
1189 }
1190
1191 // Do not rely on function type feedback or presence of code to determine
1192 // if a function was compiled.
1193 if (!CompilerState::Current().is_aot() && !function.WasCompiled()) {
1194 TRACE_INLINING(THR_Print(" Bailout: not compiled yet\n"));
1195 PRINT_INLINING_TREE("Not compiled", &call_data->caller, &function,
1196 call_data->call);
1197 return false;
1198 }
1199
1200 // Type feedback may have been cleared for this function (ClearICDataArray),
1201 // but we need it for inlining.
1202 if (!CompilerState::Current().is_aot() && !function.ForceOptimize() &&
1203 function.ic_data_array() == Array::null()) {
1204 TRACE_INLINING(THR_Print(" Bailout: type feedback cleared\n"));
1205 PRINT_INLINING_TREE("No ICData", &call_data->caller, &function,
1206 call_data->call);
1207 return false;
1208 }
1209
1210 // Abort if this function has deoptimized too much.
1211 if (function.deoptimization_counter() >=
1212 FLAG_max_deoptimization_counter_threshold) {
1213 function.set_is_inlinable(false);
1214 TRACE_INLINING(THR_Print(" Bailout: deoptimization threshold\n"));
1215 PRINT_INLINING_TREE("Deoptimization threshold exceeded",
1216 &call_data->caller, &function, call_data->call);
1217 return false;
1218 }
1219
1220 // Apply early heuristics. For a specialized case
1221 // (constants_arg_counts > 0), don't use a previously
1222 // estimate of the call site and instruction counts.
1223 // Note that at this point, optional constant parameters
1224 // are not counted yet, which makes this decision approximate.
1225 GrowableArray<Value*>* arguments = call_data->arguments;
1226 const intptr_t constant_arg_count = CountConstants(*arguments);
1227 const intptr_t instruction_count =
1228 constant_arg_count == 0 ? function.optimized_instruction_count() : 0;
1229 const intptr_t call_site_count =
1230 constant_arg_count == 0 ? function.optimized_call_site_count() : 0;
1231 InliningDecision decision =
1232 ShouldWeInline(function, instruction_count, call_site_count);
1233 if (!decision.value) {
1235 THR_Print(" Bailout: early heuristics (%s) with "
1236 "code size: %" Pd ", "
1237 "call sites: %" Pd ", "
1238 "inlining depth of callee: %d, "
1239 "const args: %" Pd "\n",
1240 decision.reason, instruction_count, call_site_count,
1241 function.inlining_depth(), constant_arg_count));
1242 PRINT_INLINING_TREE("Early heuristic", &call_data->caller, &function,
1243 call_data->call);
1244 return false;
1245 }
1246
1247 if ((function.HasOptionalPositionalParameters() ||
1248 function.HasOptionalNamedParameters()) &&
1249 !function.AreValidArguments(function.NumTypeParameters(),
1250 arguments->length(), argument_names,
1251 nullptr)) {
1252 TRACE_INLINING(THR_Print(" Bailout: optional arg mismatch\n"));
1253 PRINT_INLINING_TREE("Optional arg mismatch", &call_data->caller,
1254 &function, call_data->call);
1255 return false;
1256 }
1257
1258 // Abort if this is a recursive occurrence.
1259 Definition* call = call_data->call;
1260 // Added 'volatile' works around a possible GCC 4.9 compiler bug.
1261 volatile bool is_recursive_call = IsCallRecursive(function, call);
1262 if (is_recursive_call &&
1263 inlining_recursion_depth_ >= FLAG_inlining_recursion_depth_threshold) {
1264 TRACE_INLINING(THR_Print(" Bailout: recursive function\n"));
1265 PRINT_INLINING_TREE("Recursive function", &call_data->caller, &function,
1266 call_data->call);
1267 return false;
1268 }
1269
1270 Error& error = Error::Handle();
1271 {
1272 // Save and clear deopt id.
1273 DeoptIdScope deopt_id_scope(thread(), 0);
1274
1275 // Install bailout jump.
1276 LongJumpScope jump;
1277 if (setjmp(*jump.Set()) == 0) {
1278 // Load IC data for the callee.
1279 ZoneGrowableArray<const ICData*>* ic_data_array =
1280 new (Z) ZoneGrowableArray<const ICData*>();
1281 const bool clone_ic_data = Compiler::IsBackgroundCompilation();
1282 ASSERT(CompilerState::Current().is_aot() || function.ForceOptimize() ||
1283 function.ic_data_array() != Array::null());
1284 function.RestoreICDataMap(ic_data_array, clone_ic_data);
1285
1286 // Parse the callee function.
1287 bool in_cache;
1288 ParsedFunction* parsed_function =
1289 GetParsedFunction(function, &in_cache);
1290
1291 // Build the callee graph.
1292 InlineExitCollector* exit_collector =
1293 new (Z) InlineExitCollector(caller_graph_, call);
1294 FlowGraph* callee_graph;
1296 if (StaticCallInstr* instr = call_data->call->AsStaticCall()) {
1297 entry_kind = instr->entry_kind();
1298 } else if (InstanceCallInstr* instr =
1299 call_data->call->AsInstanceCall()) {
1300 entry_kind = instr->entry_kind();
1301 } else if (PolymorphicInstanceCallInstr* instr =
1302 call_data->call->AsPolymorphicInstanceCall()) {
1303 entry_kind = instr->entry_kind();
1304 } else if (call_data->call->IsClosureCall()) {
1305 // Closure functions only have one entry point.
1306 }
1307 // context_level_array=nullptr below means we are not building var desc.
1308 kernel::FlowGraphBuilder builder(
1309 parsed_function, ic_data_array, /*context_level_array=*/nullptr,
1310 exit_collector,
1311 /*optimized=*/true, Compiler::kNoOSRDeoptId,
1312 caller_graph_->max_block_id() + 1,
1313 entry_kind == Code::EntryKind::kUnchecked);
1314 {
1315 COMPILER_TIMINGS_TIMER_SCOPE(thread(), BuildGraph);
1316 callee_graph = builder.BuildGraph();
1317 // Make sure SSA temp indices in the callee graph
1318 // do not intersect with SSA temp indices in the caller.
1319 ASSERT(callee_graph->current_ssa_temp_index() == 0);
1320 callee_graph->set_current_ssa_temp_index(
1321 caller_graph_->current_ssa_temp_index());
1322#if defined(DEBUG)
1323 // The inlining IDs of instructions in the callee graph are unset
1324 // until we call SetInliningID later.
1325 GrowableArray<const Function*> callee_inline_id_to_function;
1326 callee_inline_id_to_function.Add(&function);
1327 FlowGraphChecker(callee_graph, callee_inline_id_to_function)
1328 .Check("Builder (callee)");
1329#endif
1330 CalleeGraphValidator::Validate(callee_graph);
1331 }
1332
1333 {
1334 COMPILER_TIMINGS_TIMER_SCOPE(thread(), PopulateWithICData);
1335
1336#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
1337 if (CompilerState::Current().is_aot()) {
1338 callee_graph->PopulateWithICData(parsed_function->function());
1339 }
1340#endif
1341
1342 // If we inline a function which is intrinsified without a
1343 // fall-through to IR code, we will not have any ICData attached, so
1344 // we do it manually here.
1345 if (!CompilerState::Current().is_aot() && function.is_intrinsic()) {
1346 callee_graph->PopulateWithICData(parsed_function->function());
1347 }
1348 }
1349
1350 // The parameter stubs are a copy of the actual arguments providing
1351 // concrete information about the values, for example constant values,
1352 // without linking between the caller and callee graphs.
1353 // TODO(zerny): Put more information in the stubs, eg, type information.
1354 const intptr_t first_actual_param_index = call_data->first_arg_index;
1355 const intptr_t inlined_type_args_param = function.IsGeneric() ? 1 : 0;
1356 const intptr_t num_inlined_params =
1357 inlined_type_args_param + function.NumParameters();
1358 ZoneGrowableArray<Definition*>* param_stubs =
1359 new (Z) ZoneGrowableArray<Definition*>(num_inlined_params);
1360
1361 // Create a ConstantInstr as Definition for the type arguments, if any.
1362 if (first_actual_param_index > 0) {
1363 // A type argument vector is explicitly passed.
1364 param_stubs->Add(
1365 CreateParameterStub(-1, (*arguments)[0], callee_graph));
1366 } else if (inlined_type_args_param > 0) {
1367 // No type argument vector is passed to the generic function,
1368 // pass a null vector, which is the same as a vector of dynamic types.
1369 param_stubs->Add(callee_graph->GetConstant(Object::ZoneHandle()));
1370 }
1371 // Create a parameter stub for each fixed positional parameter.
1372 for (intptr_t i = 0; i < function.num_fixed_parameters(); ++i) {
1373 param_stubs->Add(CreateParameterStub(
1374 i, (*arguments)[first_actual_param_index + i], callee_graph));
1375 }
1376
1377 // If the callee has optional parameters, rebuild the argument and stub
1378 // arrays so that actual arguments are in one-to-one with the formal
1379 // parameters.
1380 if (function.HasOptionalParameters()) {
1381 TRACE_INLINING(THR_Print(" adjusting for optional parameters\n"));
1382 if (!AdjustForOptionalParameters(
1383 *parsed_function, first_actual_param_index, argument_names,
1384 arguments, param_stubs, callee_graph)) {
1385 function.set_is_inlinable(false);
1386 TRACE_INLINING(THR_Print(" Bailout: optional arg mismatch\n"));
1387 PRINT_INLINING_TREE("Optional arg mismatch", &call_data->caller,
1388 &function, call_data->call);
1389 return false;
1390 }
1391 }
1392
1393 // After treating optional parameters the actual/formal count must
1394 // match.
1395 ASSERT(arguments->length() ==
1396 first_actual_param_index + function.NumParameters());
1397
1398 // Update try-index of the callee graph.
1399 BlockEntryInstr* call_block = call_data->call->GetBlock();
1400 if (call_block->InsideTryBlock()) {
1401 intptr_t try_index = call_block->try_index();
1402 for (BlockIterator it = callee_graph->reverse_postorder_iterator();
1403 !it.Done(); it.Advance()) {
1404 BlockEntryInstr* block = it.Current();
1405 block->set_try_index(try_index);
1406 }
1407 }
1408
1410
1411 {
1412 // Compute SSA on the callee graph, catching bailouts.
1413 COMPILER_TIMINGS_TIMER_SCOPE(thread(), ComputeSSA);
1414 callee_graph->ComputeSSA(param_stubs);
1415#if defined(DEBUG)
1416 // The inlining IDs of instructions in the callee graph are unset
1417 // until we call SetInliningID later.
1418 GrowableArray<const Function*> callee_inline_id_to_function;
1419 callee_inline_id_to_function.Add(&function);
1420 FlowGraphChecker(callee_graph, callee_inline_id_to_function)
1421 .Check("SSA (callee)");
1422#endif
1423 }
1424
1426 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) {
1427 THR_Print("Callee graph for inlining %s (unoptimized)\n",
1428 function.ToFullyQualifiedCString());
1429 FlowGraphPrinter printer(*callee_graph);
1430 printer.PrintBlocks();
1431 }
1432
1433 {
1434 // TODO(fschneider): Improve suppression of speculative inlining.
1435 // Deopt-ids overlap between caller and callee.
1436 if (CompilerState::Current().is_aot()) {
1437#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
1438 AotCallSpecializer call_specializer(inliner_->precompiler_,
1439 callee_graph,
1440 inliner_->speculative_policy_);
1441
1442 CompilerPassState state(Thread::Current(), callee_graph,
1443 inliner_->speculative_policy_);
1444 state.call_specializer = &call_specializer;
1446#else
1447 UNREACHABLE();
1448#endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
1449 } else {
1450 JitCallSpecializer call_specializer(callee_graph,
1451 inliner_->speculative_policy_);
1452
1453 CompilerPassState state(Thread::Current(), callee_graph,
1454 inliner_->speculative_policy_);
1455 state.call_specializer = &call_specializer;
1457 }
1458 }
1459
1461 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) {
1462 THR_Print("Callee graph for inlining %s (optimized)\n",
1463 function.ToFullyQualifiedCString());
1464 FlowGraphPrinter printer(*callee_graph);
1465 printer.PrintBlocks();
1466 }
1467
1468 // Collect information about the call site and caller graph. At this
1469 // point, optional constant parameters are counted too, making the
1470 // specialized vs. non-specialized decision accurate.
1471 intptr_t constants_count = 0;
1472 for (intptr_t i = 0, n = param_stubs->length(); i < n; ++i) {
1473 if ((*param_stubs)[i]->IsConstant()) ++constants_count;
1474 }
1475 intptr_t instruction_count = 0;
1476 intptr_t call_site_count = 0;
1477 FlowGraphInliner::CollectGraphInfo(callee_graph, constants_count,
1478 /*force*/ false, &instruction_count,
1479 &call_site_count);
1480
1481 // Use heuristics do decide if this call should be inlined.
1482 {
1483 COMPILER_TIMINGS_TIMER_SCOPE(thread(), MakeInliningDecision);
1484 InliningDecision decision =
1485 ShouldWeInline(function, instruction_count, call_site_count);
1486 if (!decision.value) {
1487 // If size is larger than all thresholds, don't consider it again.
1488
1489 // TODO(dartbug.com/49665): Make compiler smart enough so it itself
1490 // can identify highly-specialized functions that should always
1491 // be considered for inlining, without relying on a pragma.
1492 if ((instruction_count > FLAG_inlining_size_threshold) &&
1493 (call_site_count > FLAG_inlining_callee_call_sites_threshold)) {
1494 // Will keep trying to inline the function if it can be
1495 // specialized based on argument types.
1497 function)) {
1498 function.set_is_inlinable(false);
1499 TRACE_INLINING(THR_Print(" Mark not inlinable\n"));
1500 }
1501 }
1503 THR_Print(" Bailout: heuristics (%s) with "
1504 "code size: %" Pd ", "
1505 "call sites: %" Pd ", "
1506 "inlining depth of callee: %d, "
1507 "const args: %" Pd "\n",
1508 decision.reason, instruction_count, call_site_count,
1509 function.inlining_depth(), constants_count));
1510 PRINT_INLINING_TREE("Heuristic fail", &call_data->caller, &function,
1511 call_data->call);
1512 return false;
1513 }
1514
1515 // If requested, a stricter heuristic is applied to this inlining.
1516 // This heuristic always scans the method (rather than possibly
1517 // reusing cached results) to make sure all specializations are
1518 // accounted for.
1519 // TODO(ajcbik): with the now better bookkeeping, explore removing
1520 // this
1521 if (stricter_heuristic) {
1522 intptr_t call_site_instructions = 0;
1523 if (auto static_call = call->AsStaticCall()) {
1524 // Push all the arguments, do the call, drop arguments.
1525 call_site_instructions = static_call->ArgumentCount() + 1 + 1;
1526 }
1527 if (!IsSmallLeafOrReduction(inlining_depth_, call_site_instructions,
1528 callee_graph)) {
1530 THR_Print(" Bailout: heuristics (no small leaf)\n"));
1531 PRINT_INLINING_TREE("Heuristic fail (no small leaf)",
1532 &call_data->caller, &function,
1533 call_data->call);
1534 return false;
1535 }
1536 }
1537 }
1538
1539 // Inline dispatcher methods regardless of the current depth.
1540 {
1541 const intptr_t depth =
1542 function.IsDispatcherOrImplicitAccessor() ? 0 : inlining_depth_;
1543 collected_call_sites_->FindCallSites(callee_graph, depth,
1544 &inlined_info_);
1545 }
1546
1547 // Add the function to the cache.
1548 if (!in_cache) {
1549 function_cache_.Add(parsed_function);
1550 }
1551
1552 // Build succeeded so we restore the bailout jump.
1553 inlined_ = true;
1554 inlined_size_ += instruction_count;
1555 if (is_recursive_call) {
1556 inlined_recursive_call_ = true;
1557 }
1558
1559 call_data->callee_graph = callee_graph;
1560 call_data->parameter_stubs = param_stubs;
1561 call_data->exit_collector = exit_collector;
1562
1563 // When inlined, we add the guarded fields of the callee to the caller's
1564 // list of guarded fields.
1565 const FieldSet* callee_guarded_fields =
1566 callee_graph->parsed_function().guarded_fields();
1567 FieldSet::Iterator it = callee_guarded_fields->GetIterator();
1568 while (const Field** field = it.Next()) {
1570 }
1571
1572 {
1573 COMPILER_TIMINGS_TIMER_SCOPE(thread(), SetInliningId);
1575 callee_graph, inliner_->NextInlineId(callee_graph->function(),
1576 call_data->call->source()));
1577 }
1578 TRACE_INLINING(THR_Print(" Success\n"));
1580 " with reason %s, code size %" Pd ", call sites: %" Pd "\n",
1581 decision.reason, instruction_count, call_site_count));
1582 PRINT_INLINING_TREE(nullptr, &call_data->caller, &function, call);
1583 return true;
1584 } else {
1586
1587 if (error.IsLanguageError() &&
1588 (LanguageError::Cast(error).kind() == Report::kBailout)) {
1589 if (error.ptr() == Object::background_compilation_error().ptr()) {
1590 // Fall through to exit the compilation, and retry it later.
1591 } else {
1593 THR_Print(" Bailout: %s\n", error.ToErrorCString()));
1594 PRINT_INLINING_TREE("Bailout", &call_data->caller, &function, call);
1595 return false;
1596 }
1597 } else {
1598 // Fall through to exit long jump scope.
1599 }
1600 }
1601 }
1602
1603 // Propagate a compile-time error. In precompilation we attempt to
1604 // inline functions that have never been compiled before; when JITing we
1605 // should only see language errors in unoptimized compilation.
1606 // Otherwise, there can be an out-of-memory error (unhandled exception).
1607 // In background compilation we may abort compilation as the state
1608 // changes while compiling. Propagate that 'error' and retry compilation
1609 // later.
1610 ASSERT(CompilerState::Current().is_aot() ||
1611 (error.ptr() == Object::out_of_memory_error().ptr()) ||
1612 Compiler::IsBackgroundCompilation() || error.IsUnhandledException());
1614 UNREACHABLE();
1615 return false;
1616 }
#define UNREACHABLE()
Definition: assert.h:248
Iterator GetIterator() const
Definition: hash_map.h:87
static void AssignEdgeWeights(FlowGraph *flow_graph)
InliningDecision ShouldWeInline(const Function &callee, intptr_t instr_count, intptr_t call_site_count)
Definition: inliner.cc:1018
Definition * CreateParameterStub(intptr_t i, Value *argument, FlowGraph *graph)
Definition: inliner.cc:1113
bool trace_inlining() const
Definition: inliner.cc:1001
static void Validate(FlowGraph *callee_graph)
Definition: inliner.cc:127
CodeEntryKind EntryKind
Definition: object.h:6788
static void RunInliningPipeline(PipelineMode mode, CompilerPassState *state)
static CompilerState & Current()
static bool IsBackgroundCompilation()
Definition: compiler.cc:298
static constexpr intptr_t kNoOSRDeoptId
Definition: compiler.h:73
intptr_t NextInlineId(const Function &function, const InstructionSource &source)
Definition: inliner.cc:2606
static bool FunctionHasAlwaysConsiderInliningPragma(const Function &function)
Definition: inliner.cc:2499
static void SetInliningId(FlowGraph *flow_graph, intptr_t inlining_id)
Definition: inliner.cc:2449
static bool FunctionHasNeverInlinePragma(const Function &function)
Definition: inliner.cc:2487
static void CollectGraphInfo(FlowGraph *flow_graph, intptr_t num_constant_args, bool force, intptr_t *instruction_count, intptr_t *call_site_count)
Definition: inliner.cc:2415
intptr_t current_ssa_temp_index() const
Definition: flow_graph.h:243
intptr_t max_block_id() const
Definition: flow_graph.h:264
const ParsedFunction & parsed_function() const
Definition: flow_graph.h:129
DART_NORETURN void Jump(int value, const Error &error)
Definition: longjump.cc:22
static ObjectPtr null()
Definition: object.h:433
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
void AddToGuardedFields(const Field *field) const
Definition: parser.cc:88
@ kBailout
Definition: report.h:26
LongJumpScope * long_jump_base() const
Definition: thread_state.h:47
static Thread * Current()
Definition: thread.h:362
DART_WARN_UNUSED_RESULT ErrorPtr StealStickyError()
Definition: thread.cc:245
#define COMPILER_TIMINGS_TIMER_SCOPE(thread, timer_id)
#define ASSERT(E)
AtkStateType state
const uint8_t uint32_t uint32_t GError ** error
constexpr bool FLAG_support_il_printer
Definition: flag_list.h:48
#define PRINT_INLINING_TREE(comment, caller, target, instance_call)
Definition: inliner.cc:95
const char *const name
static bool IsSmallLeafOrReduction(int inlining_depth, intptr_t call_site_instructions, FlowGraph *graph)
Definition: inliner.cc:666
static bool IsCallRecursive(const Function &function, Definition *call)
Definition: inliner.cc:104
DirectChainedHashMap< FieldKeyValueTrait > FieldSet
Definition: parser.h:66
static bool IsConstant(Definition *def, int64_t *val)
Definition: loops.cc:123
def call(args)
Definition: dom.py:159

◆ zone()

Zone * dart::CallSiteInliner::zone ( ) const
inline

Definition at line 999 of file inliner.cc.

999{ return caller_graph_->zone(); }
Zone * zone() const
Definition: flow_graph.h:261

Friends And Related Function Documentation

◆ PolymorphicInliner

friend class PolymorphicInliner
friend

Definition at line 1628 of file inliner.cc.


The documentation for this class was generated from the following file: