Skip to content

Commit a13c113

Browse files
committed
[caller-analysis] Add a new SIL utility primitive: findLocalApplySites.
This utility works by taking in a function_ref and then traverses the transitive uses of the function_ref until it finds either a use it does not understand "escape" or an "apply" instruction. It returns a result structure that contains the final found applications and more importantly a bool telling the caller if we found any "escaping" uses. This is intended to be an inverse operation to ApplySite::getCalleeOrigin(). As such it has a bunch of assertions in it that check that the two stay in sync. rdar://41146023
1 parent 1dce4aa commit a13c113

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,52 @@ class StaticInitCloner : public SILCloner<StaticInitCloner> {
643643
}
644644
};
645645

646+
/// Move only data structure that is the result of findLocalApplySite.
647+
///
648+
/// NOTE: Generally it is not suggested to have move only types that contain
649+
/// small vectors. Since our small vectors contain one element or a std::vector
650+
/// like data structure , this is ok since we will either just copy the single
651+
/// element when we do the move or perform a move of the vector type.
652+
struct LLVM_LIBRARY_VISIBILITY FindLocalApplySitesResult {
653+
/// Contains the list of local non fully applied partial apply sites that we
654+
/// found.
655+
SmallVector<ApplySite, 1> partialApplySites;
656+
657+
/// Contains the list of full apply sites that we found.
658+
SmallVector<FullApplySite, 1> fullApplySites;
659+
660+
/// Set to true if the function_ref escapes into a use that our analysis does
661+
/// not understand. Set to false if we found a use that had an actual
662+
/// escape. Set to None if we did not find any call sites, but also didn't
663+
/// find any "escaping uses" as well.
664+
///
665+
/// The none case is so that we can distinguish in between saying that a value
666+
/// did escape and saying that we did not find any conservative information.
667+
bool escapes;
668+
669+
FindLocalApplySitesResult() = default;
670+
FindLocalApplySitesResult(const FindLocalApplySitesResult &) = delete;
671+
FindLocalApplySitesResult &
672+
operator=(const FindLocalApplySitesResult &) = delete;
673+
FindLocalApplySitesResult(FindLocalApplySitesResult &&) = default;
674+
FindLocalApplySitesResult &operator=(FindLocalApplySitesResult &&) = default;
675+
~FindLocalApplySitesResult() = default;
676+
677+
/// Treat this function ref as escaping only if we found an actual user we
678+
/// didn't understand. Do not treat it as escaping if we did not find any
679+
/// users at all.
680+
bool isEscaping() const { return escapes; }
681+
};
682+
683+
/// Returns .some(FindLocalApplySitesResult) if we found any interesting
684+
/// information for the given function_ref. Otherwise, returns None.
685+
///
686+
/// We consider "interesting information" to mean inclusively that:
687+
///
688+
/// 1. We discovered that the function_ref never escapes.
689+
/// 2. We were able to find either a partial apply or a full apply site.
690+
Optional<FindLocalApplySitesResult> findLocalApplySites(FunctionRefInst *FRI);
691+
646692
} // end namespace swift
647693

648694
#endif

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,4 +1586,72 @@ StaticInitCloner::clone(SingleValueInstruction *InitVal) {
15861586
return cast<SingleValueInstruction>(ValueMap[InitVal]);
15871587
}
15881588

1589+
Optional<FindLocalApplySitesResult>
1590+
swift::findLocalApplySites(FunctionRefInst *FRI) {
1591+
SmallVector<Operand *, 32> worklist(FRI->use_begin(), FRI->use_end());
15891592

1593+
FindLocalApplySitesResult f;
1594+
1595+
// Optimistically state that we have no escapes before our def-use dataflow.
1596+
f.escapes = false;
1597+
1598+
while (!worklist.empty()) {
1599+
auto *op = worklist.pop_back_val();
1600+
auto *user = op->getUser();
1601+
1602+
// If we have a full apply site as our user.
1603+
if (auto apply = FullApplySite::isa(user)) {
1604+
if (apply.getCallee() == op->get()) {
1605+
f.fullApplySites.push_back(apply);
1606+
continue;
1607+
}
1608+
}
1609+
1610+
// If we have a partial apply as a user, start tracking it, but also look at
1611+
// its users.
1612+
if (auto *pai = dyn_cast<PartialApplyInst>(user)) {
1613+
if (pai->getCallee() == op->get()) {
1614+
// Track the partial apply that we saw so we can potentially eliminate
1615+
// dead closure arguments.
1616+
f.partialApplySites.push_back(pai);
1617+
// Look to see if we can find a full application of this partial apply
1618+
// as well.
1619+
copy(pai->getUses(), std::back_inserter(worklist));
1620+
continue;
1621+
}
1622+
}
1623+
1624+
// Otherwise, see if we have any function casts to look through...
1625+
switch (user->getKind()) {
1626+
case SILInstructionKind::ThinToThickFunctionInst:
1627+
case SILInstructionKind::ConvertFunctionInst:
1628+
case SILInstructionKind::ConvertEscapeToNoEscapeInst:
1629+
copy(cast<SingleValueInstruction>(user)->getUses(),
1630+
std::back_inserter(worklist));
1631+
continue;
1632+
1633+
// Look through any reference count instructions since these are not
1634+
// escapes:
1635+
case SILInstructionKind::CopyValueInst:
1636+
copy(cast<CopyValueInst>(user)->getUses(), std::back_inserter(worklist));
1637+
continue;
1638+
case SILInstructionKind::StrongRetainInst:
1639+
case SILInstructionKind::StrongReleaseInst:
1640+
case SILInstructionKind::RetainValueInst:
1641+
case SILInstructionKind::ReleaseValueInst:
1642+
case SILInstructionKind::DestroyValueInst:
1643+
continue;
1644+
default:
1645+
break;
1646+
}
1647+
1648+
// But everything else is considered an escape.
1649+
f.escapes = true;
1650+
}
1651+
1652+
// If we did escape and didn't find any apply sites, then we have no
1653+
// information for our users that is interesting.
1654+
if (f.escapes && f.partialApplySites.empty() && f.fullApplySites.empty())
1655+
return None;
1656+
return f;
1657+
}

0 commit comments

Comments
 (0)