mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[erel-matcher] Do not throw away the list of releases if we do not have a joint post-dominating release set. Instead, use a flag.
I am tuning a new argument explosion heuristic to reduce code-size. One part of the heuristic I am playing with is the part of the algorithm that attempts to figure out if we could eliminate additonal arguments after performing owned->guaranteed an additional release when we run FSO a second time. Today we do this unconditionally. I am trying to do it in a more conservative way where we only do it if we know that we aren't going to increase the number of arguments too much. rdar://41146023
This commit is contained in:
@@ -752,35 +752,36 @@ bool ConsumedArgToEpilogueReleaseMatcher::releaseArgument(
|
||||
void
|
||||
ConsumedArgToEpilogueReleaseMatcher::
|
||||
processMatchingReleases() {
|
||||
llvm::DenseSet<SILArgument *> ArgToRemove;
|
||||
// If we can not find a release for all parts with reference semantics
|
||||
// that means we did not find all releases for the base.
|
||||
for (auto Arg : ArgInstMap) {
|
||||
for (auto &pair : ArgInstMap) {
|
||||
// We do not know if we have a fully post dominating release set
|
||||
// so all release sets should be considered partially post
|
||||
// dominated.
|
||||
auto releaseSet = pair.second.getPartiallyPostDomReleases();
|
||||
if (!releaseSet)
|
||||
continue;
|
||||
|
||||
// If an argument has a single release and it is rc-identical to the
|
||||
// SILArgument. Then we do not need to use projection to check for whether
|
||||
// all non-trivial fields are covered.
|
||||
if (Arg.second.size() == 1) {
|
||||
SILInstruction *I = *Arg.second.begin();
|
||||
SILValue RV = I->getOperand(0);
|
||||
if (Arg.first == RCFI->getRCIdentityRoot(RV))
|
||||
if (releaseSet->size() == 1) {
|
||||
SILInstruction *inst = *releaseSet->begin();
|
||||
SILValue rv = inst->getOperand(0);
|
||||
if (pair.first == RCFI->getRCIdentityRoot(rv)) {
|
||||
pair.second.setHasJointPostDominatingReleaseSet();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// OK. we have multiple epilogue releases for this argument, check whether
|
||||
// it has covered all fields with reference semantic in the argument.
|
||||
if (releaseArgument(Arg.second, Arg.first))
|
||||
if (!releaseArgument(*releaseSet, pair.first))
|
||||
continue;
|
||||
|
||||
// OK. we did find some epilogue releases, just not all.
|
||||
if (!Arg.second.empty())
|
||||
FoundSomeReleases.insert(Arg.first);
|
||||
|
||||
ArgToRemove.insert(Arg.first);
|
||||
}
|
||||
|
||||
// Clear any releases found for this argument.
|
||||
for (auto &X : ArgToRemove) {
|
||||
ArgInstMap.erase(ArgInstMap.find(X));
|
||||
// OK. At this point we know that we found a joint post dominating
|
||||
// set of releases. Mark our argument as such.
|
||||
pair.second.setHasJointPostDominatingReleaseSet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,43 +797,50 @@ isOneOfConventions(SILArgumentConvention Convention,
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ConsumedArgToEpilogueReleaseMatcher::
|
||||
collectMatchingDestroyAddresses(SILBasicBlock *BB) {
|
||||
void ConsumedArgToEpilogueReleaseMatcher::collectMatchingDestroyAddresses(
|
||||
SILBasicBlock *block) {
|
||||
// Check if we can find destroy_addr for each @in argument.
|
||||
SILFunction::iterator AnotherEpilogueBB =
|
||||
SILFunction::iterator anotherEpilogueBB =
|
||||
(Kind == ExitKind::Return) ? F->findThrowBB() : F->findReturnBB();
|
||||
for (auto Arg : F->begin()->getFunctionArguments()) {
|
||||
if (Arg->isIndirectResult())
|
||||
|
||||
for (auto *arg : F->begin()->getFunctionArguments()) {
|
||||
if (arg->isIndirectResult())
|
||||
continue;
|
||||
if (Arg->getArgumentConvention() != SILArgumentConvention::Indirect_In)
|
||||
if (arg->getArgumentConvention() != SILArgumentConvention::Indirect_In)
|
||||
continue;
|
||||
bool HasDestroyAddrOutsideEpilogueBB = false;
|
||||
bool hasDestroyAddrOutsideEpilogueBB = false;
|
||||
// This is an @in argument. Check if there are any destroy_addr
|
||||
// instructions for it.
|
||||
for (auto Use : getNonDebugUses(Arg)) {
|
||||
auto User = Use->getUser();
|
||||
if (!isa<DestroyAddrInst>(User))
|
||||
for (Operand *op : getNonDebugUses(arg)) {
|
||||
auto *user = op->getUser();
|
||||
if (!isa<DestroyAddrInst>(user))
|
||||
continue;
|
||||
// Do not take into account any uses in the other
|
||||
// epilogue BB.
|
||||
if (AnotherEpilogueBB != F->end() &&
|
||||
User->getParent() == &*AnotherEpilogueBB)
|
||||
if (anotherEpilogueBB != F->end() &&
|
||||
user->getParent() == &*anotherEpilogueBB)
|
||||
continue;
|
||||
if (User->getParent() != BB )
|
||||
HasDestroyAddrOutsideEpilogueBB = true;
|
||||
ArgInstMap[Arg].push_back(dyn_cast<SILInstruction>(User));
|
||||
if (user->getParent() != block)
|
||||
hasDestroyAddrOutsideEpilogueBB = true;
|
||||
|
||||
// Since ArgumentState uses a TinyPtrVector, creating
|
||||
// temporaries containing one element is cheap.
|
||||
auto iter = ArgInstMap.insert({arg, ArgumentState(user)});
|
||||
// We inserted the value.
|
||||
if (iter.second)
|
||||
continue;
|
||||
// Otherwise, add this release to the set.
|
||||
iter.first->second.addRelease(user);
|
||||
}
|
||||
|
||||
// Don't know how to handle destroy_addr outside of the epilogue.
|
||||
if (HasDestroyAddrOutsideEpilogueBB)
|
||||
ArgInstMap.erase(Arg);
|
||||
if (hasDestroyAddrOutsideEpilogueBB)
|
||||
ArgInstMap.erase(arg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConsumedArgToEpilogueReleaseMatcher::
|
||||
collectMatchingReleases(SILBasicBlock *BB) {
|
||||
void ConsumedArgToEpilogueReleaseMatcher::collectMatchingReleases(
|
||||
SILBasicBlock *block) {
|
||||
// Iterate over the instructions post-order and find final releases
|
||||
// associated with each arguments.
|
||||
//
|
||||
@@ -849,19 +857,17 @@ collectMatchingReleases(SILBasicBlock *BB) {
|
||||
// 3. A release that is mapped to an argument which already has a release
|
||||
// that overlaps with this release. This release for sure is not the final
|
||||
// release.
|
||||
bool IsTrackingInArgs = isOneOfConventions(SILArgumentConvention::Indirect_In,
|
||||
bool isTrackingInArgs = isOneOfConventions(SILArgumentConvention::Indirect_In,
|
||||
ArgumentConventions);
|
||||
|
||||
for (auto II = std::next(BB->rbegin()), IE = BB->rend(); II != IE; ++II) {
|
||||
if (IsTrackingInArgs && isa<DestroyAddrInst>(*II)) {
|
||||
for (auto &inst : reversed(*block)) {
|
||||
if (isTrackingInArgs && isa<DestroyAddrInst>(inst)) {
|
||||
// It is probably a destroy addr for an @in argument.
|
||||
continue;
|
||||
}
|
||||
// If we do not have a release_value or strong_release. We can continue
|
||||
if (!isa<ReleaseValueInst>(*II) && !isa<StrongReleaseInst>(*II)) {
|
||||
|
||||
if (!isa<ReleaseValueInst>(inst) && !isa<StrongReleaseInst>(inst)) {
|
||||
// We cannot match a final release if it is followed by a dealloc_ref.
|
||||
if (isa<DeallocRefInst>(*II))
|
||||
if (isa<DeallocRefInst>(inst))
|
||||
break;
|
||||
|
||||
// We do not know what this instruction is, do a simple check to make sure
|
||||
@@ -869,7 +875,7 @@ collectMatchingReleases(SILBasicBlock *BB) {
|
||||
//
|
||||
// TODO: we could make the logic here more complicated to handle each type
|
||||
// of instructions in a more precise manner.
|
||||
if (!II->mayRelease())
|
||||
if (!inst.mayRelease())
|
||||
continue;
|
||||
// This instruction may release something, bail out conservatively.
|
||||
break;
|
||||
@@ -877,14 +883,13 @@ collectMatchingReleases(SILBasicBlock *BB) {
|
||||
|
||||
// Ok, we have a release_value or strong_release. Grab Target and find the
|
||||
// RC identity root of its operand.
|
||||
SILInstruction *Target = &*II;
|
||||
SILValue OrigOp = Target->getOperand(0);
|
||||
SILValue Op = RCFI->getRCIdentityRoot(OrigOp);
|
||||
SILValue origOp = inst.getOperand(0);
|
||||
SILValue op = RCFI->getRCIdentityRoot(origOp);
|
||||
|
||||
// Check whether this is a SILArgument or a part of a SILArgument. This is
|
||||
// possible after we expand release instructions in SILLowerAgg pass.
|
||||
auto *Arg = dyn_cast<SILFunctionArgument>(stripValueProjections(Op));
|
||||
if (!Arg)
|
||||
auto *arg = dyn_cast<SILFunctionArgument>(stripValueProjections(op));
|
||||
if (!arg)
|
||||
break;
|
||||
|
||||
// If Op is not a consumed argument, we must break since this is not an Op
|
||||
@@ -892,17 +897,17 @@ collectMatchingReleases(SILBasicBlock *BB) {
|
||||
// we could make this more general by allowing for intervening non-arg
|
||||
// releases in the sense that we do not allow for race conditions in between
|
||||
// destructors.
|
||||
if (!Arg ||
|
||||
!isOneOfConventions(Arg->getArgumentConvention(), ArgumentConventions))
|
||||
if (!arg ||
|
||||
!isOneOfConventions(arg->getArgumentConvention(), ArgumentConventions))
|
||||
break;
|
||||
|
||||
// Ok, we have a release on a SILArgument that has a consuming convention.
|
||||
// Attempt to put it into our arc opts map. If we already have it, we have
|
||||
// exited the return value sequence so break. Otherwise, continue looking
|
||||
// for more arc operations.
|
||||
auto Iter = ArgInstMap.find(Arg);
|
||||
if (Iter == ArgInstMap.end()) {
|
||||
ArgInstMap[Arg].push_back(Target);
|
||||
auto iter = ArgInstMap.find(arg);
|
||||
if (iter == ArgInstMap.end()) {
|
||||
ArgInstMap.insert({arg, {&inst}});
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -911,18 +916,24 @@ collectMatchingReleases(SILBasicBlock *BB) {
|
||||
//
|
||||
// If we are seeing a redundant release we have exited the return value
|
||||
// sequence, so break.
|
||||
if (!isa<DestroyAddrInst>(Target))
|
||||
if (isRedundantRelease(Iter->second, Arg, OrigOp))
|
||||
break;
|
||||
|
||||
if (!isa<DestroyAddrInst>(inst)) {
|
||||
// We do not know if we have a fully post dominating release
|
||||
// set, so we use the partial post dom entry point.
|
||||
if (auto partialReleases = iter->second.getPartiallyPostDomReleases()) {
|
||||
if (isRedundantRelease(*partialReleases, arg, origOp)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We've seen part of this base, but this is a part we've have not seen.
|
||||
// Record it.
|
||||
Iter->second.push_back(Target);
|
||||
// Record it.
|
||||
iter->second.addRelease(&inst);
|
||||
}
|
||||
|
||||
if (IsTrackingInArgs) {
|
||||
if (isTrackingInArgs) {
|
||||
// Find destroy_addr for each @in argument.
|
||||
collectMatchingDestroyAddresses(BB);
|
||||
collectMatchingDestroyAddresses(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user