diff options
author | Akira Hatanaka <ahatanaka@apple.com> | 2021-01-25 11:56:33 -0800 |
---|---|---|
committer | Akira Hatanaka <ahatanaka@apple.com> | 2021-01-25 11:57:08 -0800 |
commit | 53176c168061d6f26dcf3ce4fa59288b7d67255e (patch) | |
tree | 293456123ef54447bd19341d86a1afabb1b6304b /llvm/lib/Transforms/Utils/InlineFunction.cpp | |
parent | 9d9ceb37453ffe0186d04f4e9e4ba9fb41200b57 (diff) | |
download | llvm-53176c168061d6f26dcf3ce4fa59288b7d67255e.zip llvm-53176c168061d6f26dcf3ce4fa59288b7d67255e.tar.gz llvm-53176c168061d6f26dcf3ce4fa59288b7d67255e.tar.bz2 |
[ObjC][ARC] Annotate calls with attributes instead of emitting retainRV
or claimRV calls in the IR
Background:
This patch makes changes to the front-end and middle-end that are
needed to fix a longstanding problem where llvm breaks ARC's autorelease
optimization (see the link below) by separating calls from the marker
instructions or retainRV/claimRV calls. The backend changes are in
https://reviews.llvm.org/D92569.
https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-autoreleasereturnvalue
What this patch does to fix the problem:
- The front-end annotates calls with attribute "clang.arc.rv"="retain"
or "clang.arc.rv"="claim", which indicates the call is implicitly
followed by a marker instruction and a retainRV/claimRV call that
consumes the call result. This is currently done only when the target
is arm64 and the optimization level is higher than -O0.
- ARC optimizer temporarily emits retainRV/claimRV calls after the
annotated calls in the IR and removes the inserted calls after
processing the function.
- ARC contract pass emits retainRV/claimRV calls after the annotated
calls. It doesn't remove the attribute on the call since the backend
needs it to emit the marker instruction. The retainRV/claimRV calls
are emitted late in the pipeline to prevent optimization passes from
transforming the IR in a way that makes it harder for the ARC
middle-end passes to figure out the def-use relationship between the
call and the retainRV/claimRV calls (which is the cause of PR31925).
- The function inliner removes the autoreleaseRV call in the callee that
returns the result if nothing in the callee prevents it from being
paired up with the calls annotated with "clang.arc.rv"="retain/claim"
in the caller. If the call is annotated with "claim", a release call
is inserted since autoreleaseRV+claimRV is equivalent to a release. If
it cannot find an autoreleaseRV call, it tries to transfer the
attributes to a function call in the callee. This is important since
ARC optimizer can remove the autoreleaseRV call returning the callee
result, which makes it impossible to pair it up with the retainRV or
claimRV call in the caller. If that fails, it simply emits a retain
call in the IR if the call is annotated with "retain" and does nothing
if it's annotated with "claim".
- This patch teaches dead argument elimination pass not to change the
return type of a function if any of the calls to the function are
annotated with attribute "clang.arc.rv". This is necessary since the
pass can incorrectly determine nothing in the IR uses the function
return, which can happen since the front-end no longer explicitly
emits retainRV/claimRV calls in the IR, and change its return type to
'void'.
Future work:
- Use the attribute on x86-64.
- Fix the auto upgrader to convert call+retainRV/claimRV pairs into
calls annotated with the attributes.
rdar://71443534
Differential Revision: https://reviews.llvm.org/D92808
Diffstat (limited to 'llvm/lib/Transforms/Utils/InlineFunction.cpp')
-rw-r--r-- | llvm/lib/Transforms/Utils/InlineFunction.cpp | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp index 0ac8fa5..8387f68 100644 --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -27,8 +27,9 @@ #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/ObjCARCAnalysisUtils.h" +#include "llvm/Analysis/ObjCARCRVAttr.h" #include "llvm/Analysis/ProfileSummaryInfo.h" -#include "llvm/Transforms/Utils/Local.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/VectorUtils.h" #include "llvm/IR/Argument.h" @@ -61,6 +62,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include <algorithm> #include <cassert> @@ -1644,6 +1646,77 @@ void llvm::updateProfileCallee( } } +static void +insertRetainOrClaimRVCalls(CallBase &CB, + const SmallVectorImpl<ReturnInst *> &Returns) { + Module *Mod = CB.getParent()->getParent()->getParent(); + bool IsRetainRV = objcarc::hasRetainRVAttr(&CB), IsClaimRV = !IsRetainRV; + + for (auto *RI : Returns) { + Value *RetOpnd = llvm::objcarc::GetRCIdentityRoot(RI->getOperand(0)); + BasicBlock::reverse_iterator I = ++(RI->getIterator().getReverse()); + BasicBlock::reverse_iterator EI = RI->getParent()->rend(); + bool InsertRetainCall = IsRetainRV; + IRBuilder<> Builder(RI->getContext()); + + // Walk backwards through the basic block looking for either a matching + // autoreleaseRV call or an unannotated call. + for (; I != EI;) { + auto CurI = I++; + + // Ignore casts. + if (isa<CastInst>(*CurI)) + continue; + + if (auto *II = dyn_cast<IntrinsicInst>(&*CurI)) { + if (II->getIntrinsicID() == Intrinsic::objc_autoreleaseReturnValue && + II->hasNUses(0) && + llvm::objcarc::GetRCIdentityRoot(II->getOperand(0)) == RetOpnd) { + // If we've found a matching authoreleaseRV call: + // - If the call is annotated with claimRV, insert a call to + // objc_release and erase the autoreleaseRV call. + // - If the call is annotated with retainRV, just erase the + // autoreleaseRV call. + if (IsClaimRV) { + Builder.SetInsertPoint(II); + Function *IFn = + Intrinsic::getDeclaration(Mod, Intrinsic::objc_release); + Value *BC = + Builder.CreateBitCast(RetOpnd, IFn->getArg(0)->getType()); + Builder.CreateCall(IFn, BC, ""); + } + II->eraseFromParent(); + InsertRetainCall = false; + } + } else if (auto *CI = dyn_cast<CallInst>(&*CurI)) { + if (llvm::objcarc::GetRCIdentityRoot(CI) == RetOpnd && + !objcarc::hasRetainRVOrClaimRVAttr(CI)) { + // If we've found an unannotated call that defines RetOpnd, annotate + // the call with the attributes. + llvm::AttributeList AL = CI->getAttributes(); + AL = AL.addAttribute(CI->getContext(), AttributeList::ReturnIndex, + objcarc::getRVAttrKeyStr(), + objcarc::getRVAttrValStr(IsRetainRV)); + CI->setAttributes(AL); + InsertRetainCall = false; + } + } + + break; + } + + if (InsertRetainCall) { + // The call is annotated with retainRV and we've failed to find a matching + // autoreleaseRV or an annotated call in the callee. Emit a call to + // objc_retain. + Builder.SetInsertPoint(RI); + Function *IFn = Intrinsic::getDeclaration(Mod, Intrinsic::objc_retain); + Value *BC = Builder.CreateBitCast(RetOpnd, IFn->getArg(0)->getType()); + Builder.CreateCall(IFn, BC, ""); + } + } +} + /// This function inlines the called function into the basic block of the /// caller. This returns false if it is not possible to inline this call. /// The program is still in a well defined state if this occurs though. @@ -1847,6 +1920,10 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI, // Remember the first block that is newly cloned over. FirstNewBlock = LastBlock; ++FirstNewBlock; + // Insert retainRV/clainRV runtime calls. + if (objcarc::hasRetainRVOrClaimRVAttr(&CB)) + insertRetainOrClaimRVCalls(CB, Returns); + if (IFI.CallerBFI != nullptr && IFI.CalleeBFI != nullptr) // Update the BFI of blocks cloned into the caller. updateCallerBFI(OrigBB, VMap, IFI.CallerBFI, IFI.CalleeBFI, |