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

Classes

struct  CallInfo
 

Public Member Functions

 CallSites (intptr_t threshold, GrowableArray< CallInfo< InstanceCallInstr > > *calls)
 
const GrowableArray< CallInfo< PolymorphicInstanceCallInstr > > & instance_calls () const
 
const GrowableArray< CallInfo< StaticCallInstr > > & static_calls () const
 
const GrowableArray< CallInfo< ClosureCallInstr > > & closure_calls () const
 
bool HasCalls () const
 
intptr_t NumCalls () const
 
void Clear ()
 
void ComputeCallSiteRatio (intptr_t static_calls_start_ix, intptr_t instance_calls_start_ix, intptr_t calls_start_ix)
 
void TryDevirtualize (FlowGraph *graph)
 
void FindCallSites (FlowGraph *graph, intptr_t depth, GrowableArray< InlinedInfo > *inlined_info)
 
- Public Member Functions inherited from dart::ValueObject
 ValueObject ()
 
 ~ValueObject ()
 

Static Public Member Functions

template<typename CallType >
static intptr_t ComputeMaxCallCount (const GrowableArray< CallInfo< CallType > > &calls, intptr_t start_index)
 
template<typename CallType >
static void ComputeCallRatio (GrowableArray< CallInfo< CallType > > &calls, intptr_t start_index, intptr_t max_count)
 
static void RecordAllNotInlinedFunction (FlowGraph *graph, intptr_t depth, GrowableArray< InlinedInfo > *inlined_info)
 
template<typename CallType >
static void PruneRemovedCallsIn (GrowableArray< CallInfo< CallType > > *arr)
 

Detailed Description

Definition at line 261 of file inliner.cc.

Constructor & Destructor Documentation

◆ CallSites()

dart::CallSites::CallSites ( intptr_t  threshold,
GrowableArray< CallInfo< InstanceCallInstr > > *  calls 
)
inlineexplicit

Definition at line 290 of file inliner.cc.

292 : inlining_depth_threshold_(threshold),
293 static_calls_(),
294 closure_calls_(),
295 instance_calls_(),
296 calls_(calls) {}

Member Function Documentation

◆ Clear()

void dart::CallSites::Clear ( )
inline

Definition at line 321 of file inliner.cc.

321 {
322 static_calls_.Clear();
323 closure_calls_.Clear();
324 instance_calls_.Clear();
325 }

◆ closure_calls()

const GrowableArray< CallInfo< ClosureCallInstr > > & dart::CallSites::closure_calls ( ) const
inline

Definition at line 307 of file inliner.cc.

307 {
308 return closure_calls_;
309 }

◆ ComputeCallRatio()

template<typename CallType >
static void dart::CallSites::ComputeCallRatio ( GrowableArray< CallInfo< CallType > > &  calls,
intptr_t  start_index,
intptr_t  max_count 
)
inlinestatic

Definition at line 342 of file inliner.cc.

344 {
345 for (intptr_t i = start_index; i < calls.length(); ++i) {
346 calls[i].ratio = static_cast<double>(calls[i].call_count) / max_count;
347 }
348 }

◆ ComputeCallSiteRatio()

void dart::CallSites::ComputeCallSiteRatio ( intptr_t  static_calls_start_ix,
intptr_t  instance_calls_start_ix,
intptr_t  calls_start_ix 
)
inline

Definition at line 354 of file inliner.cc.

356 {
357 intptr_t max_count = 0;
358 max_count = Utils::Maximum(
359 max_count,
360 ComputeMaxCallCount(instance_calls_, instance_calls_start_ix));
361 max_count = Utils::Maximum(
362 max_count, ComputeMaxCallCount(static_calls_, static_calls_start_ix));
363 max_count =
364 Utils::Maximum(max_count, ComputeMaxCallCount(*calls_, calls_start_ix));
365
366 if (max_count == 0) {
367 return;
368 }
369
370 ComputeCallRatio(instance_calls_, instance_calls_start_ix, max_count);
371 ComputeCallRatio(static_calls_, static_calls_start_ix, max_count);
372 ComputeCallRatio(*calls_, calls_start_ix, max_count);
373 }
static void ComputeCallRatio(GrowableArray< CallInfo< CallType > > &calls, intptr_t start_index, intptr_t max_count)
Definition: inliner.cc:342
static intptr_t ComputeMaxCallCount(const GrowableArray< CallInfo< CallType > > &calls, intptr_t start_index)
Definition: inliner.cc:328
static constexpr T Maximum(T x, T y)
Definition: utils.h:41

◆ ComputeMaxCallCount()

template<typename CallType >
static intptr_t dart::CallSites::ComputeMaxCallCount ( const GrowableArray< CallInfo< CallType > > &  calls,
intptr_t  start_index 
)
inlinestatic

Definition at line 328 of file inliner.cc.

330 {
331 intptr_t max_count = 0;
332 for (intptr_t i = start_index; i < calls.length(); ++i) {
333 const auto count = calls[i].call_count;
334 if (count > max_count) {
335 max_count = count;
336 }
337 }
338 return max_count;
339 }
int count
Definition: FontMgrTest.cpp:50

◆ FindCallSites()

void dart::CallSites::FindCallSites ( FlowGraph graph,
intptr_t  depth,
GrowableArray< InlinedInfo > *  inlined_info 
)
inline

Definition at line 543 of file inliner.cc.

545 {
547 ASSERT(graph != nullptr);
548 if (depth > inlining_depth_threshold_) {
549 if (FLAG_print_inlining_tree) {
550 RecordAllNotInlinedFunction(graph, depth, inlined_info);
551 }
552 return;
553 }
554
555 // At the maximum inlining depth, only profitable methods
556 // are further considered for inlining.
557 const bool inline_only_profitable_methods =
558 (depth >= inlining_depth_threshold_);
559
560 // In AOT, compute loop hierarchy.
561 const bool is_aot = CompilerState::Current().is_aot();
562 if (is_aot) {
563 graph->GetLoopHierarchy();
564 }
565
566 const intptr_t instance_calls_start_ix = instance_calls_.length();
567 const intptr_t static_calls_start_ix = static_calls_.length();
568 const intptr_t calls_start_ix = calls_->length();
569 for (BlockIterator block_it = graph->postorder_iterator(); !block_it.Done();
570 block_it.Advance()) {
571 BlockEntryInstr* entry = block_it.Current();
572 const intptr_t nesting_depth = entry->NestingDepth();
573 for (auto current : entry->instructions()) {
574 if (auto instance_call = current->AsPolymorphicInstanceCall()) {
575 if (!inline_only_profitable_methods ||
576 instance_call->IsSureToCallSingleRecognizedTarget() ||
577 instance_call->HasOnlyDispatcherOrImplicitAccessorTargets()) {
578 // Consider instance call for further inlining. Note that it will
579 // still be subject to all the inlining heuristics.
580 instance_calls_.Add({graph, instance_call, depth, nesting_depth});
581 } else {
582 // No longer consider the instance call because inlining is too
583 // deep and the method is not deemed profitable by other criteria.
584 if (FLAG_print_inlining_tree) {
585 const Function* caller = &graph->function();
586 const Function* target = &instance_call->targets().FirstTarget();
587 inlined_info->Add(InlinedInfo(caller, target, depth + 1,
588 instance_call, "Too deep"));
589 }
590 }
591 } else if (auto call = current->AsInstanceCall()) {
592 calls_->Add({graph, call, depth, nesting_depth});
593 } else if (auto static_call = current->AsStaticCall()) {
594 HandleStaticCall(static_call, inline_only_profitable_methods, graph,
595 depth, nesting_depth, inlined_info);
596 } else if (auto closure_call = current->AsClosureCall()) {
597 if (!inline_only_profitable_methods) {
598 // Consider closure for further inlining. Note that it will
599 // still be subject to all the inlining heuristics.
600 closure_calls_.Add({graph, closure_call, depth, nesting_depth});
601 } else {
602 // No longer consider the closure because inlining is too deep.
603 }
604 }
605 }
606 }
607 ComputeCallSiteRatio(static_calls_start_ix, instance_calls_start_ix,
608 calls_start_ix);
609 }
void FindCallSites(FlowGraph *graph, intptr_t depth, GrowableArray< InlinedInfo > *inlined_info)
Definition: inliner.cc:543
void ComputeCallSiteRatio(intptr_t static_calls_start_ix, intptr_t instance_calls_start_ix, intptr_t calls_start_ix)
Definition: inliner.cc:354
static void RecordAllNotInlinedFunction(FlowGraph *graph, intptr_t depth, GrowableArray< InlinedInfo > *inlined_info)
Definition: inliner.cc:375
bool is_aot() const
static CompilerState & Current()
#define COMPILER_TIMINGS_TIMER_SCOPE(thread, timer_id)
#define ASSERT(E)
uint32_t * target
def call(args)
Definition: dom.py:159

◆ HasCalls()

bool dart::CallSites::HasCalls ( ) const
inline

Definition at line 311 of file inliner.cc.

311 {
312 return !(static_calls_.is_empty() && closure_calls_.is_empty() &&
313 instance_calls_.is_empty());
314 }

◆ instance_calls()

const GrowableArray< CallInfo< PolymorphicInstanceCallInstr > > & dart::CallSites::instance_calls ( ) const
inline

Definition at line 298 of file inliner.cc.

299 {
300 return instance_calls_;
301 }

◆ NumCalls()

intptr_t dart::CallSites::NumCalls ( ) const
inline

Definition at line 316 of file inliner.cc.

316 {
317 return instance_calls_.length() + static_calls_.length() +
318 closure_calls_.length();
319 }

◆ PruneRemovedCallsIn()

template<typename CallType >
static void dart::CallSites::PruneRemovedCallsIn ( GrowableArray< CallInfo< CallType > > *  arr)
inlinestatic

Definition at line 408 of file inliner.cc.

408 {
409 intptr_t j = 0;
410 for (intptr_t i = 0; i < arr->length(); i++) {
411 if ((*arr)[i].call->previous() != nullptr) {
412 if (i != j) {
413 (*arr)[j] = (*arr)[i];
414 }
415 j++;
416 }
417 }
418 arr->TruncateTo(j);
419 }

◆ RecordAllNotInlinedFunction()

static void dart::CallSites::RecordAllNotInlinedFunction ( FlowGraph graph,
intptr_t  depth,
GrowableArray< InlinedInfo > *  inlined_info 
)
inlinestatic

Definition at line 375 of file inliner.cc.

378 {
379 const Function* caller = &graph->function();
380 Function& target = Function::ZoneHandle();
381 for (BlockIterator block_it = graph->postorder_iterator(); !block_it.Done();
382 block_it.Advance()) {
383 for (ForwardInstructionIterator it(block_it.Current()); !it.Done();
384 it.Advance()) {
385 Instruction* current = it.Current();
386 Definition* call = nullptr;
387 if (current->IsPolymorphicInstanceCall()) {
388 PolymorphicInstanceCallInstr* instance_call =
389 current->AsPolymorphicInstanceCall();
390 target = instance_call->targets().FirstTarget().ptr();
391 call = instance_call;
392 } else if (current->IsStaticCall()) {
393 StaticCallInstr* static_call = current->AsStaticCall();
394 target = static_call->function().ptr();
395 call = static_call;
396 } else if (current->IsClosureCall()) {
397 // TODO(srdjan): Add data for closure calls.
398 }
399 if (call != nullptr) {
400 inlined_info->Add(
401 InlinedInfo(caller, &target, depth + 1, call, "Too deep"));
402 }
403 }
404 }
405 }
static Object & ZoneHandle()
Definition: object.h:419

◆ static_calls()

const GrowableArray< CallInfo< StaticCallInstr > > & dart::CallSites::static_calls ( ) const
inline

Definition at line 303 of file inliner.cc.

303 {
304 return static_calls_;
305 }

◆ TryDevirtualize()

void dart::CallSites::TryDevirtualize ( FlowGraph graph)
inline

Definition at line 423 of file inliner.cc.

423 {
424 GrowableArray<Definition*> worklist(calls_->length());
425 BitVector processed(graph->zone(), graph->current_ssa_temp_index());
426
427 auto add_to_worklist = [&](Definition* defn) {
428 ASSERT(defn->HasSSATemp());
429 const auto ssa_index = defn->ssa_temp_index();
430 if (ssa_index < processed.length() && !processed.Contains(ssa_index)) {
431 processed.Add(ssa_index);
432 worklist.Add(defn);
433 return true;
434 }
435 return false;
436 };
437
438 auto add_transitive_dependencies_to_worklist = [&](intptr_t from_index) {
439 // Caveat: worklist is growing as we are iterating over it. This loop
440 // goes up to |worklist.length()| and thus is going to visit all newly
441 // added definitions and add their dependencies to the worklist
442 // transitively.
443 for (intptr_t i = from_index; i < worklist.length(); i++) {
444 auto defn = worklist[i];
445 for (auto input : defn->inputs()) {
446 add_to_worklist(input);
447 }
448 // For instructions with arguments we don't expect push arguments to
449 // be inserted yet.
450 ASSERT(defn->ArgumentCount() == 0 || !defn->HasMoveArguments());
451 }
452 };
453
454 // Step 1: add all calls to worklist and then transitively add all
455 // their dependencies (values that flow into inputs). Calls will
456 // form the prefix of the worklist followed by their inputs.
457 for (auto& call_info : *calls_) {
458 // Call might not have an SSA temp assigned because its result is
459 // not used. We still want to add such call to worklist but we
460 // should not try to update the bitvector.
461 if (call_info.call->HasSSATemp()) {
462 add_to_worklist(call_info.call);
463 } else {
464 worklist.Add(call_info.call);
465 }
466 }
467 RELEASE_ASSERT(worklist.length() == calls_->length());
468 add_transitive_dependencies_to_worklist(0);
469
470 // Step 2: canonicalize each definition from the worklist. We process
471 // worklist backwards which means we will usually canonicalize inputs before
472 // we canonicalize the instruction that uses them.
473 // Note: worklist is not topologically sorted, so we might end up
474 // processing some uses before the defs.
475 bool changed = false;
476 intptr_t last_unhandled_call_index = calls_->length() - 1;
477 while (!worklist.is_empty()) {
478 auto defn = worklist.RemoveLast();
479
480 // Once we reach the prefix of the worklist we know that we are processing
481 // calls we are interested in.
482 CallInfo<InstanceCallInstr>* call_info = nullptr;
483 if (worklist.length() == last_unhandled_call_index) {
484 call_info = &(*calls_)[last_unhandled_call_index];
485 RELEASE_ASSERT(call_info->call == defn);
486 last_unhandled_call_index--;
487 }
488
489 // Can't apply canonicalization rule to this definition.
490 if (defn->HasUnmatchedInputRepresentations() &&
491 defn->SpeculativeModeOfInputs() == Instruction::kGuardInputs) {
492 continue;
493 }
494
495 auto replacement = defn->Canonicalize(graph);
496 if (replacement != defn) {
497 changed = true;
498 if (replacement != nullptr) {
499 defn->ReplaceUsesWith(replacement);
500 if (replacement->ssa_temp_index() == -1) {
501 graph->EnsureSSATempIndex(defn, replacement);
502 }
503
504 // Add the replacement with all of its dependencies to the worklist.
505 if (add_to_worklist(replacement)) {
506 add_transitive_dependencies_to_worklist(worklist.length() - 1);
507 }
508
509 // We have devirtualized |InstanceCall| into |StaticCall| check
510 // inlining heuristics and add the |StaticCall| into |static_calls_|
511 // if heuristics suggest inlining.
512 //
513 // Note: currently |InstanceCallInstr::Canonicalize| can only return
514 // a newly constructed |StaticCallInstr|, so the check below is
515 // redundant (it will always succeed). Nevertheless we add it to
516 // catch situations in the future when canonicalization rule is
517 // strengthened.
518 const bool newly_inserted =
519 replacement->ssa_temp_index() >= processed.length();
520 if (call_info != nullptr && replacement->IsStaticCall() &&
521 newly_inserted) {
522 HandleDevirtualization(call_info,
523 replacement->Cast<StaticCallInstr>());
524 }
525 }
526 if (auto phi = defn->AsPhi()) {
527 phi->UnuseAllInputs();
528 phi->block()->RemovePhi(phi);
529 } else {
530 defn->RemoveFromGraph();
531 }
532 }
533 }
534
535 if (changed) {
536 PruneRemovedCallsIn(&instance_calls_);
537 PruneRemovedCallsIn(&static_calls_);
538 PruneRemovedCallsIn(&closure_calls_);
539 PruneRemovedCallsIn(calls_);
540 }
541 }
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
static void PruneRemovedCallsIn(GrowableArray< CallInfo< CallType > > *arr)
Definition: inliner.cc:408
@ kGuardInputs
Definition: il.h:972

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