AU.setPreservesCFG();
}
+static bool isSafeBetweenRVCalls(const Instruction *I) {
+ if (IsNoopInstruction(I))
+ return true;
+
+ auto *CB = dyn_cast<CallBase>(I);
+ if (!CB)
+ return false;
+
+ Intrinsic::ID IID = CB->getIntrinsicID();
+ if (IID == Intrinsic::not_intrinsic)
+ return false;
+
+ switch (IID) {
+ case Intrinsic::lifetime_start:
+ case Intrinsic::lifetime_end:
+ // The inliner adds new lifetime markers as part of the return sequence,
+ // which should be skipped when looking for paired return RV call.
+ LLVM_FALLTHROUGH;
+ case Intrinsic::stacksave:
+ case Intrinsic::stackrestore:
+ // If the inlined code contains dynamic allocas, the above applies as well.
+ return true;
+ default:
+ return false;
+ }
+}
+
/// Turn objc_retainAutoreleasedReturnValue into objc_retain if the operand is
/// not a return value. Or, if it can be paired with an
/// objc_autoreleaseReturnValue, delete the pair and return true.
if (I != Begin) {
do
--I;
- while (I != Begin && IsNoopInstruction(&*I));
+ while (I != Begin && isSafeBetweenRVCalls(&*I));
if (GetBasicARCInstKind(&*I) == ARCInstKind::AutoreleaseRV &&
EquivalentArgs.count(GetArgRCIdentityRoot(&*I))) {
Changed = true;
; CHECK: entry:
; CHECK-NEXT: %obj = alloca i8
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* %obj)
-; CHECK-NEXT: %0 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call.i)
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* %obj)
-; CHECK-NEXT: %1 = tail call i8* @llvm.objc.retain(i8* %call.i)
; CHECK-NEXT: ret i8* %call.i
; CHECK-NEXT: }
define i8* @testLifetime(i8* %call.i) {
; CHECK: entry:
; CHECK-NEXT: %save = tail call i8* @llvm.stacksave()
; CHECK-NEXT: %obj = alloca i8, i8 %arg
-; CHECK-NEXT: %0 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call.i)
; CHECK-NEXT: call void @llvm.stackrestore(i8* %save)
-; CHECK-NEXT: %1 = tail call i8* @llvm.objc.retain(i8* %call.i)
; CHECK-NEXT: ret i8* %call.i
; CHECK-NEXT: }
define i8* @testStack(i8* %call.i, i8 %arg) {