diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -532,12 +532,15 @@ bool VisitBottomUp(BasicBlock *BB, DenseMap &BBStates, BlotMapVector &Retains); - bool VisitInstructionTopDown(Instruction *Inst, - DenseMap &Releases, - BBState &MyStates); + bool VisitInstructionTopDown( + Instruction *Inst, DenseMap &Releases, BBState &MyStates, + const DenseMap + &ReleaseInsertPtToRCIdentityRoot); bool VisitTopDown(BasicBlock *BB, DenseMap &BBStates, - DenseMap &Releases); + DenseMap &Releases, + const DenseMap + &ReleaseInsertPtToRCIdentityRoot); bool Visit(Function &F, DenseMap &BBStates, BlotMapVector &Retains, DenseMap &Releases); @@ -1495,14 +1498,56 @@ return NestingDetected; } -bool -ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst, - DenseMap &Releases, - BBState &MyStates) { +// Fill ReleaseInsertPtToRCIdentityRoot. +static void +collectReleaseInsertPts(const BlotMapVector &Retains, + DenseMap + &ReleaseInsertPtToRCIdentityRoot) { + for (auto &P : Retains) { + // Get the RC identity root of the objc_retain call. + Instruction *Retain = cast(P.first); + Value *Root = GetRCIdentityRoot(Retain->getOperand(0)); + // Collect all the insertion points of the objc_release calls corresponding + // to the objc_retain call. + for (const Instruction *InsertPt : P.second.ReverseInsertPts) + ReleaseInsertPtToRCIdentityRoot[InsertPt] = Root; + } +} + +// Get the RC identity root from an insertion point of an objc_release call. +// Return nullptr if the passed instruction isn't an insertion point. +static const Value *getRCIdentityRootFromReleaseInsertPt( + const Instruction *InsertPt, + const DenseMap + &ReleaseInsertPtToRCIdentityRoot) { + auto I = ReleaseInsertPtToRCIdentityRoot.find(InsertPt); + if (I == ReleaseInsertPtToRCIdentityRoot.end()) + return nullptr; + return I->second; +} + +bool ObjCARCOpt::VisitInstructionTopDown( + Instruction *Inst, DenseMap &Releases, BBState &MyStates, + const DenseMap + &ReleaseInsertPtToRCIdentityRoot) { bool NestingDetected = false; ARCInstKind Class = GetARCInstKind(Inst); const Value *Arg = nullptr; + // Make sure a call to objc_retain isn't moved past insertion points of calls + // to objc_release. + if ((Arg = getRCIdentityRootFromReleaseInsertPt( + Inst, ReleaseInsertPtToRCIdentityRoot))) { + TopDownPtrState &S = MyStates.getPtrTopDownState(Arg); + // Disable code motion if the current position is S_Retain to prevent moving + // the objc_retain call past objc_release calls. If it's S_CanRelease or + // larger, it's not necessary to disable code motion as the insertion points + // that prevent the objc_retain call from moving down should have been set + // already. + if (S.GetSeq() == S_Retain) + S.SetCFGHazardAfflicted(true); + } + LLVM_DEBUG(dbgs() << " Class: " << Class << "\n"); switch (Class) { @@ -1565,10 +1610,11 @@ return NestingDetected; } -bool -ObjCARCOpt::VisitTopDown(BasicBlock *BB, - DenseMap &BBStates, - DenseMap &Releases) { +bool ObjCARCOpt::VisitTopDown(BasicBlock *BB, + DenseMap &BBStates, + DenseMap &Releases, + const DenseMap + &ReleaseInsertPtToRCIdentityRoot) { LLVM_DEBUG(dbgs() << "\n== ObjCARCOpt::VisitTopDown ==\n"); bool NestingDetected = false; BBState &MyStates = BBStates[BB]; @@ -1608,7 +1654,8 @@ for (Instruction &Inst : *BB) { LLVM_DEBUG(dbgs() << " Visiting " << Inst << "\n"); - NestingDetected |= VisitInstructionTopDown(&Inst, Releases, MyStates); + NestingDetected |= VisitInstructionTopDown(&Inst, Releases, MyStates, + ReleaseInsertPtToRCIdentityRoot); // Bail out if the number of pointers being tracked becomes too large so // that this pass can complete in a reasonable amount of time. @@ -1728,10 +1775,14 @@ return false; } + DenseMap ReleaseInsertPtToRCIdentityRoot; + collectReleaseInsertPts(Retains, ReleaseInsertPtToRCIdentityRoot); + // Use reverse-postorder for top-down. bool TopDownNestingDetected = false; for (BasicBlock *BB : llvm::reverse(PostOrder)) { - TopDownNestingDetected |= VisitTopDown(BB, BBStates, Releases); + TopDownNestingDetected |= + VisitTopDown(BB, BBStates, Releases, ReleaseInsertPtToRCIdentityRoot); if (DisableRetainReleasePairing) return false; } diff --git a/llvm/test/Transforms/ObjCARC/code-motion.ll b/llvm/test/Transforms/ObjCARC/code-motion.ll --- a/llvm/test/Transforms/ObjCARC/code-motion.ll +++ b/llvm/test/Transforms/ObjCARC/code-motion.ll @@ -38,6 +38,43 @@ ret void } +; Check that code motion is disabled. +; Previously, ARC optimizer would move the release past the retain. + +; if.then: +; %p0 = getelementptr inbounds i8, i8* %obj, i64 8 +; %1 = load i8, i8* %p0, align 1 +; call void @llvm.objc.release(i8* %obj) #1, !clang.imprecise_release !2 +; %2 = load i8, i8* %p1, align 1 +; %3 = tail call i8* @llvm.objc.retain(i8* %obj) + +; CHECK: define void @test3(i8* %[[OBJ:.*]], i1 +; CHECK-NEXT: call i8* @llvm.objc.retain(i8* %[[OBJ]]) +; CHECK-NOT: call {{.*}} @llvm.objc +; CHECK: call void @llvm.objc.release(i8* %[[OBJ]]) +; CHECK-NEXT: ret void + +define void @test3(i8* %obj, i1 %cond, i8* %p1) { + %v0 = call i8* @llvm.objc.retain(i8* %obj) + br i1 %cond, label %if.then, label %if.else + +if.then: + %p0 = getelementptr inbounds i8, i8* %obj, i64 8 + load i8, i8* %p0 + load i8, i8* %p1 + call void @alterRefCount() + br label %join + +if.else: + call void @alterRefCount() + call void @use(i8* %obj) + br label %join + +join: + call void @llvm.objc.release(i8* %obj), !clang.imprecise_release !9 + ret void +} + declare void @llvm.dbg.declare(metadata, metadata, metadata) declare i8* @llvm.objc.retain(i8*) local_unnamed_addr declare void @llvm.objc.release(i8*) local_unnamed_addr