[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:
Michael Gottesman
2018-06-17 00:12:19 -07:00
parent 50fb3b8496
commit 00d95425be
2 changed files with 178 additions and 129 deletions

View File

@@ -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);
}
}