diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index 9d7f5e64f9868..60bd238476f03 100644 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -138,12 +138,6 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) { // calls followed by objc_autoreleasePoolPop calls (perhaps in ObjC++ code // after inlining) can be turned into plain release calls. -// TODO: Critical-edge splitting. If the optimial insertion point is -// a critical edge, the current algorithm has to fail, because it doesn't -// know how to split edges. It should be possible to make the optimizer -// think in terms of edges, rather than blocks, and then split critical -// edges on demand. - // TODO: OptimizeSequences could generalized to be Interprocedural. // TODO: Recognize that a bunch of other objc runtime calls have @@ -599,6 +593,7 @@ class ObjCARCOpt { void init(Function &F); bool run(Function &F, AAResults &AA); bool hasCFGChanged() const { return CFGChanged; } + BasicBlock *SplitCriticalEdge(BasicBlock *Pred, BasicBlock *Succ); }; } // end anonymous namespace @@ -1765,8 +1760,50 @@ void ObjCARCOpt::MoveCalls(Value *Arg, RRInfo &RetainsToMove, Module *M) { LLVM_DEBUG(dbgs() << "== ObjCARCOpt::MoveCalls ==\n"); - // Insert the new retain and release calls. + // First, handle critical edges for retain insertion points + SmallVector NewRetainInsertPts; + for (Instruction *InsertPt : RetainsToMove.ReverseInsertPts) { + BasicBlock *BB = InsertPt->getParent(); + BasicBlock *Pred = BB->getUniquePredecessor(); + + // If this is a critical edge, split it + if (!Pred && BB->hasNPredecessors(1)) { + for (BasicBlock *PredBB : predecessors(BB)) { + if (PredBB->getTerminator()->getNumSuccessors() > 1) { + if (BasicBlock *NewBB = SplitCriticalEdge(PredBB, BB)) { + // Add the new block as an insertion point + NewRetainInsertPts.push_back(NewBB->getTerminator()); + } + } + } + } else { + NewRetainInsertPts.push_back(InsertPt); + } + } + + // Then handle critical edges for release insertion points + SmallVector NewReleaseInsertPts; for (Instruction *InsertPt : ReleasesToMove.ReverseInsertPts) { + BasicBlock *BB = InsertPt->getParent(); + BasicBlock *Pred = BB->getUniquePredecessor(); + + // If this is a critical edge, split it + if (!Pred && BB->hasNPredecessors(1)) { + for (BasicBlock *PredBB : predecessors(BB)) { + if (PredBB->getTerminator()->getNumSuccessors() > 1) { + if (BasicBlock *NewBB = SplitCriticalEdge(PredBB, BB)) { + // Add the new block as an insertion point + NewReleaseInsertPts.push_back(NewBB->getTerminator()); + } + } + } + } else { + NewReleaseInsertPts.push_back(InsertPt); + } + } + + // Now insert the new retain calls at the split points + for (Instruction *InsertPt : NewRetainInsertPts) { Function *Decl = EP.get(ARCRuntimeEntryPointKind::Retain); SmallVector BundleList; addOpBundleForFunclet(InsertPt->getParent(), BundleList); @@ -1780,7 +1817,9 @@ void ObjCARCOpt::MoveCalls(Value *Arg, RRInfo &RetainsToMove, "At insertion point: " << *InsertPt << "\n"); } - for (Instruction *InsertPt : RetainsToMove.ReverseInsertPts) { + + // And insert the new release calls at the split points + for (Instruction *InsertPt : NewReleaseInsertPts) { Function *Decl = EP.get(ARCRuntimeEntryPointKind::Release); SmallVector BundleList; addOpBundleForFunclet(InsertPt->getParent(), BundleList); @@ -2488,6 +2527,53 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) { return Changed; } +/// Split critical edges where we need to insert retain/release calls +BasicBlock *ObjCARCOpt::SplitCriticalEdge(BasicBlock *Pred, BasicBlock *Succ) { + // Don't split if either block is a landing pad - we want to maintain + // the property that landing pads have exactly one predecessor + if (Succ->isLandingPad() || isa(Pred->getTerminator())) + return nullptr; + + // We need both multiple successors in predecessor and + // multiple predecessors in successor + if (Pred->getTerminator()->getNumSuccessors() <= 1 || + Succ->getUniquePredecessor()) + return nullptr; + + // Create a new basic block for the split edge + BasicBlock *NewBB = BasicBlock::Create( + Pred->getContext(), Pred->getName() + "." + Succ->getName() + ".arc", + Pred->getParent()); + + // Update the terminator of Pred to branch to NewBB instead of Succ + BranchInst *Term = cast(Pred->getTerminator()); + for (unsigned i = 0, e = Term->getNumSuccessors(); i != e; ++i) { + if (Term->getSuccessor(i) == Succ) { + Term->setSuccessor(i, NewBB); + break; + } + } + + // Create an unconditional branch from NewBB to Succ + BranchInst::Create(Succ, NewBB); + + // Update PHI nodes in Succ + for (PHINode &PHI : Succ->phis()) { + Value *V = PHI.getIncomingValueForBlock(Pred); + PHI.setIncomingBlock(PHI.getBasicBlockIndex(Pred), NewBB); + PHI.addIncoming(V, Pred); + } + + // Update any funclet bundles + if (!BlockEHColors.empty()) { + const ColorVector &CV = BlockEHColors.find(Pred)->second; + BlockEHColors[NewBB] = CV; + } + + CFGChanged = true; + return NewBB; +} + /// @} /// diff --git a/llvm/test/Transforms/ObjCARC/allocas.ll b/llvm/test/Transforms/ObjCARC/allocas.ll index 6fe2edf3e2dd4..03f428e96683e 100644 --- a/llvm/test/Transforms/ObjCARC/allocas.ll +++ b/llvm/test/Transforms/ObjCARC/allocas.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=objc-arc -S < %s | FileCheck %s declare ptr @llvm.objc.retain(ptr) @@ -31,7 +32,7 @@ declare ptr @objc_msgSend(ptr, ptr, ...) ; In the presence of allocas, unconditionally remove retain/release pairs only ; if they are known safe in both directions. This prevents matching up an inner ; retain with the boundary guarding release in the following situation: -; +; ; %A = alloca ; retain(%x) ; retain(%x) <--- Inner Retain @@ -43,14 +44,21 @@ declare ptr @objc_msgSend(ptr, ptr, ...) ; ; rdar://13750319 -; CHECK: define void @test1a(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test1a(ptr %x) { +; CHECK-LABEL: define void @test1a( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: store ptr [[X]], ptr [[A]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[A]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0:![0-9]+]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %A = alloca ptr tail call ptr @llvm.objc.retain(ptr %x) @@ -64,14 +72,21 @@ entry: ret void } -; CHECK: define void @test1b(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test1b(ptr %x) { +; CHECK-LABEL: define void @test1b( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[A]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[A]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %A = alloca ptr tail call ptr @llvm.objc.retain(ptr %x) @@ -86,14 +101,22 @@ entry: } -; CHECK: define void @test1c(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test1c(ptr %x) { +; CHECK-LABEL: define void @test1c( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[A:%.*]] = alloca ptr, i32 3, align 8 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr ptr, ptr [[A]], i32 2 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[GEP]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[GEP]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %A = alloca ptr, i32 3 %gep = getelementptr ptr, ptr %A, i32 2 @@ -109,14 +132,29 @@ entry: } -; CHECK: define void @test1d(ptr %x, i1 %arg) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test1d(ptr %x, i1 %arg) { +; CHECK-LABEL: define void @test1d( +; CHECK-SAME: ptr [[X:%.*]], i1 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[ARG]], label %[[USE_ALLOCAA:.*]], label %[[USE_ALLOCAB:.*]] +; CHECK: [[USE_ALLOCAA]]: +; CHECK-NEXT: [[ALLOCAA:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: br label %[[EXIT:.*]] +; CHECK: [[USE_ALLOCAB]]: +; CHECK-NEXT: [[ALLOCAB:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[A:%.*]] = phi ptr [ [[ALLOCAA]], %[[USE_ALLOCAA]] ], [ [[ALLOCAB]], %[[USE_ALLOCAB]] ] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[A]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[A]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %arg, label %use_allocaA, label %use_allocaB @@ -141,14 +179,30 @@ exit: ret void } -; CHECK: define void @test1e(ptr %x, i1 %arg) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test1e(ptr %x, i1 %arg) { +; CHECK-LABEL: define void @test1e( +; CHECK-SAME: ptr [[X:%.*]], i1 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[ARG]], label %[[USE_ALLOCAA:.*]], label %[[USE_ALLOCAB:.*]] +; CHECK: [[USE_ALLOCAA]]: +; CHECK-NEXT: [[ALLOCAA:%.*]] = alloca ptr, i32 4, align 8 +; CHECK-NEXT: br label %[[EXIT:.*]] +; CHECK: [[USE_ALLOCAB]]: +; CHECK-NEXT: [[ALLOCAB:%.*]] = alloca ptr, i32 4, align 8 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[A:%.*]] = phi ptr [ [[ALLOCAA]], %[[USE_ALLOCAA]] ], [ [[ALLOCAB]], %[[USE_ALLOCAB]] ] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr ptr, ptr [[A]], i32 2 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[GEP]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[GEP]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %arg, label %use_allocaA, label %use_allocaB @@ -174,14 +228,23 @@ exit: ret void } -; CHECK: define void @test1f(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test1f(ptr %x) { +; CHECK-LABEL: define void @test1f( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[ALLOCAONE:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[ALLOCATWO:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[A:%.*]] = select i1 undef, ptr [[ALLOCAONE]], ptr [[ALLOCATWO]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[A]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[A]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %allocaOne = alloca ptr %allocaTwo = alloca ptr @@ -201,14 +264,27 @@ entry: ; conservatively. -; CHECK: define void @test2a(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test2a(ptr %x) { +; CHECK-LABEL: define void @test2a( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: store ptr [[X]], ptr [[A]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[A]], align 8 +; CHECK-NEXT: br label %[[BB1:.*]] +; CHECK: [[BB1]]: +; CHECK-NEXT: br label %[[BB2:.*]] +; CHECK: [[BB2]]: +; CHECK-NEXT: br label %[[BB3:.*]] +; CHECK: [[BB3]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %A = alloca ptr store ptr %x, ptr %A, align 8 @@ -231,14 +307,27 @@ bb3: ret void } -; CHECK: define void @test2b(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test2b(ptr %x) { +; CHECK-LABEL: define void @test2b( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: store ptr [[X]], ptr [[A]], align 8 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[A]], align 8 +; CHECK-NEXT: br label %[[BB1:.*]] +; CHECK: [[BB1]]: +; CHECK-NEXT: br label %[[BB2:.*]] +; CHECK: [[BB2]]: +; CHECK-NEXT: br label %[[BB3:.*]] +; CHECK: [[BB3]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %A = alloca ptr store ptr %x, ptr %A, align 8 @@ -261,14 +350,29 @@ bb3: ret void } -; CHECK: define void @test2c(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test2c(ptr %x) { +; CHECK-LABEL: define void @test2c( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[A:%.*]] = alloca ptr, i32 3, align 8 +; CHECK-NEXT: [[GEP1:%.*]] = getelementptr ptr, ptr [[A]], i32 2 +; CHECK-NEXT: store ptr [[X]], ptr [[GEP1]], align 8 +; CHECK-NEXT: [[GEP2:%.*]] = getelementptr ptr, ptr [[A]], i32 2 +; CHECK-NEXT: [[Y:%.*]] = load ptr, ptr [[GEP2]], align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[BB1:.*]] +; CHECK: [[BB1]]: +; CHECK-NEXT: br label %[[BB2:.*]] +; CHECK: [[BB2]]: +; CHECK-NEXT: br label %[[BB3:.*]] +; CHECK: [[BB3]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %A = alloca ptr, i32 3 %gep1 = getelementptr ptr, ptr %A, i32 2 @@ -293,14 +397,36 @@ bb3: ret void } -; CHECK: define void @test2d(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %y) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: ret void -; CHECK: } define void @test2d(ptr %x) { +; CHECK-LABEL: define void @test2d( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[BB1:.*]] +; CHECK: [[BB1]]: +; CHECK-NEXT: [[ABB1:%.*]] = alloca ptr, i32 3, align 8 +; CHECK-NEXT: [[GEPBB11:%.*]] = getelementptr ptr, ptr [[ABB1]], i32 2 +; CHECK-NEXT: store ptr [[X]], ptr [[GEPBB11]], align 8 +; CHECK-NEXT: [[GEPBB12:%.*]] = getelementptr ptr, ptr [[ABB1]], i32 2 +; CHECK-NEXT: [[YBB1:%.*]] = load ptr, ptr [[GEPBB12]], align 8 +; CHECK-NEXT: br label %[[BB3:.*]] +; CHECK: [[BB2:.*]]: +; CHECK-NEXT: [[ABB2:%.*]] = alloca ptr, i32 4, align 8 +; CHECK-NEXT: [[GEPBB21:%.*]] = getelementptr ptr, ptr [[ABB2]], i32 2 +; CHECK-NEXT: store ptr [[X]], ptr [[GEPBB21]], align 8 +; CHECK-NEXT: [[GEPBB22:%.*]] = getelementptr ptr, ptr [[ABB2]], i32 2 +; CHECK-NEXT: [[YBB2:%.*]] = load ptr, ptr [[GEPBB22]], align 8 +; CHECK-NEXT: br label %[[BB3]] +; CHECK: [[BB3]]: +; CHECK-NEXT: [[A:%.*]] = phi ptr [ [[ABB1]], %[[BB1]] ], [ [[ABB2]], %[[BB2]] ] +; CHECK-NEXT: [[Y:%.*]] = phi ptr [ [[YBB1]], %[[BB1]] ], [ [[YBB2]], %[[BB2]] ] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_alloca(ptr [[A]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: tail call ptr @llvm.objc.retain(ptr %x) br label %bb1 @@ -338,30 +464,52 @@ bb3: ; ; rdar://13949644 -; CHECK: define void @test3a() { -; CHECK: entry: -; CHECK: @llvm.objc.retainAutoreleasedReturnValue -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.retain -; CHECK: arraydestroy.body: -; CHECK: @llvm.objc.release -; CHECK-NOT: @llvm.objc.release -; CHECK: arraydestroy.done: -; CHECK-NOT: @llvm.objc.release -; CHECK: arraydestroy.body1: -; CHECK: @llvm.objc.release -; CHECK-NOT: @llvm.objc.release -; CHECK: arraydestroy.done1: -; CHECK: @llvm.objc.release -; CHECK: ret void -; CHECK: } define void @test3a() { +; CHECK-LABEL: define void @test3a() { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[KEYS:%.*]] = alloca [2 x ptr], align 16 +; CHECK-NEXT: [[OBJS:%.*]] = alloca [2 x ptr], align 16 +; CHECK-NEXT: [[CALL1:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL1]], ptr [[OBJS]], align 8 +; CHECK-NEXT: [[OBJS_ELT:%.*]] = getelementptr inbounds [2 x ptr], ptr [[OBJS]], i64 0, i64 1 +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL1]], ptr [[OBJS_ELT]], align 8 +; CHECK-NEXT: [[CALL2:%.*]] = call ptr @returner1() +; CHECK-NEXT: [[CALL3:%.*]] = call ptr @returner2() +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL2]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL2]], ptr [[KEYS]], align 8 +; CHECK-NEXT: [[KEYS_ELT:%.*]] = getelementptr inbounds [2 x ptr], ptr [[KEYS]], i64 0, i64 1 +; CHECK-NEXT: [[TMP3:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL3]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL3]], ptr [[KEYS_ELT]], align 8 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr [[OBJS]], i64 0, i64 2 +; CHECK-NEXT: br label %[[ARRAYDESTROY_BODY:.*]] +; CHECK: [[ARRAYDESTROY_BODY]]: +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENTPAST:%.*]] = phi ptr [ [[GEP]], %[[ENTRY]] ], [ [[ARRAYDESTROY_ELEMENT:%.*]], %[[ARRAYDESTROY_BODY]] ] +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds ptr, ptr [[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +; CHECK-NEXT: [[DESTROY_TMP:%.*]] = load ptr, ptr [[ARRAYDESTROY_ELEMENT]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[DESTROY_TMP]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[ARRAYDESTROY_CMP:%.*]] = icmp eq ptr [[ARRAYDESTROY_ELEMENT]], [[OBJS]] +; CHECK-NEXT: br i1 [[ARRAYDESTROY_CMP]], label %[[ARRAYDESTROY_DONE:.*]], label %[[ARRAYDESTROY_BODY]] +; CHECK: [[ARRAYDESTROY_DONE]]: +; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds [2 x ptr], ptr [[KEYS]], i64 0, i64 2 +; CHECK-NEXT: br label %[[ARRAYDESTROY_BODY1:.*]] +; CHECK: [[ARRAYDESTROY_BODY1]]: +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENTPAST1:%.*]] = phi ptr [ [[GEP1]], %[[ARRAYDESTROY_DONE]] ], [ [[ARRAYDESTROY_ELEMENT1:%.*]], %[[ARRAYDESTROY_BODY1]] ] +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENT1]] = getelementptr inbounds ptr, ptr [[ARRAYDESTROY_ELEMENTPAST1]], i64 -1 +; CHECK-NEXT: [[DESTROY_TMP1:%.*]] = load ptr, ptr [[ARRAYDESTROY_ELEMENT1]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[DESTROY_TMP1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[ARRAYDESTROY_CMP1:%.*]] = icmp eq ptr [[ARRAYDESTROY_ELEMENT1]], [[KEYS]] +; CHECK-NEXT: br i1 [[ARRAYDESTROY_CMP1]], label %[[ARRAYDESTROY_DONE1:.*]], label %[[ARRAYDESTROY_BODY1]] +; CHECK: [[ARRAYDESTROY_DONE1]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %keys = alloca [2 x ptr], align 16 %objs = alloca [2 x ptr], align 16 - + %call1 = call ptr @returner() %tmp0 = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %call1) @@ -377,8 +525,8 @@ entry: store ptr %call2, ptr %keys, align 8 %keys.elt = getelementptr inbounds [2 x ptr], ptr %keys, i64 0, i64 1 tail call ptr @llvm.objc.retain(ptr %call3) - store ptr %call3, ptr %keys.elt - + store ptr %call3, ptr %keys.elt + %gep = getelementptr inbounds [2 x ptr], ptr %objs, i64 0, i64 2 br label %arraydestroy.body @@ -412,30 +560,52 @@ arraydestroy.done1: ; ; rdar://13949644 -; CHECK: define void @test3b() { -; CHECK: entry: -; CHECK: @llvm.objc.retainAutoreleasedReturnValue -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.retain -; CHECK: arraydestroy.body: -; CHECK: @llvm.objc.release -; CHECK-NOT: @llvm.objc.release -; CHECK: arraydestroy.done: -; CHECK-NOT: @llvm.objc.release -; CHECK: arraydestroy.body1: -; CHECK: @llvm.objc.release -; CHECK-NOT: @llvm.objc.release -; CHECK: arraydestroy.done1: -; CHECK: @llvm.objc.release -; CHECK: ret void -; CHECK: } define void @test3b() { +; CHECK-LABEL: define void @test3b() { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[KEYS:%.*]] = alloca [2 x ptr], align 16 +; CHECK-NEXT: [[OBJS:%.*]] = alloca [2 x ptr], align 16 +; CHECK-NEXT: [[CALL1:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL1]], ptr [[OBJS]], align 8 +; CHECK-NEXT: [[OBJS_ELT:%.*]] = getelementptr inbounds [2 x ptr], ptr [[OBJS]], i64 0, i64 1 +; CHECK-NEXT: store ptr [[CALL1]], ptr [[OBJS_ELT]], align 8 +; CHECK-NEXT: [[CALL2:%.*]] = call ptr @returner1() +; CHECK-NEXT: [[CALL3:%.*]] = call ptr @returner2() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL2]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL2]], ptr [[KEYS]], align 8 +; CHECK-NEXT: [[KEYS_ELT:%.*]] = getelementptr inbounds [2 x ptr], ptr [[KEYS]], i64 0, i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL3]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[CALL3]], ptr [[KEYS_ELT]], align 8 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr [[OBJS]], i64 0, i64 2 +; CHECK-NEXT: br label %[[ARRAYDESTROY_BODY:.*]] +; CHECK: [[ARRAYDESTROY_BODY]]: +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENTPAST:%.*]] = phi ptr [ [[GEP]], %[[ENTRY]] ], [ [[ARRAYDESTROY_ELEMENT:%.*]], %[[ARRAYDESTROY_BODY]] ] +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds ptr, ptr [[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +; CHECK-NEXT: [[DESTROY_TMP:%.*]] = load ptr, ptr [[ARRAYDESTROY_ELEMENT]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[DESTROY_TMP]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[ARRAYDESTROY_CMP:%.*]] = icmp eq ptr [[ARRAYDESTROY_ELEMENT]], [[OBJS]] +; CHECK-NEXT: br i1 [[ARRAYDESTROY_CMP]], label %[[ARRAYDESTROY_DONE:.*]], label %[[ARRAYDESTROY_BODY]] +; CHECK: [[ARRAYDESTROY_DONE]]: +; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds [2 x ptr], ptr [[KEYS]], i64 0, i64 2 +; CHECK-NEXT: br label %[[ARRAYDESTROY_BODY1:.*]] +; CHECK: [[ARRAYDESTROY_BODY1]]: +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENTPAST1:%.*]] = phi ptr [ [[GEP1]], %[[ARRAYDESTROY_DONE]] ], [ [[ARRAYDESTROY_ELEMENT1:%.*]], %[[ARRAYDESTROY_BODY1]] ] +; CHECK-NEXT: [[ARRAYDESTROY_ELEMENT1]] = getelementptr inbounds ptr, ptr [[ARRAYDESTROY_ELEMENTPAST1]], i64 -1 +; CHECK-NEXT: [[DESTROY_TMP1:%.*]] = load ptr, ptr [[ARRAYDESTROY_ELEMENT1]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[DESTROY_TMP1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[ARRAYDESTROY_CMP1:%.*]] = icmp eq ptr [[ARRAYDESTROY_ELEMENT1]], [[KEYS]] +; CHECK-NEXT: br i1 [[ARRAYDESTROY_CMP1]], label %[[ARRAYDESTROY_DONE1:.*]], label %[[ARRAYDESTROY_BODY1]] +; CHECK: [[ARRAYDESTROY_DONE1]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %keys = alloca [2 x ptr], align 16 %objs = alloca [2 x ptr], align 16 - + %call1 = call ptr @returner() %tmp0 = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %call1) %tmp1 = tail call ptr @llvm.objc.retain(ptr %call1) @@ -452,8 +622,8 @@ entry: store ptr %call2, ptr %keys, align 8 %keys.elt = getelementptr inbounds [2 x ptr], ptr %keys, i64 0, i64 1 tail call ptr @llvm.objc.retain(ptr %call3) - store ptr %call3, ptr %keys.elt - + store ptr %call3, ptr %keys.elt + %gep = getelementptr inbounds [2 x ptr], ptr %objs, i64 0, i64 2 br label %arraydestroy.body @@ -486,3 +656,6 @@ arraydestroy.done1: !0 = !{} declare i32 @__gxx_personality_v0(...) +;. +; CHECK: [[META0]] = !{} +;. diff --git a/llvm/test/Transforms/ObjCARC/basic.ll b/llvm/test/Transforms/ObjCARC/basic.ll index aa0c53b4f4851..e92ac86cce77f 100644 --- a/llvm/test/Transforms/ObjCARC/basic.ll +++ b/llvm/test/Transforms/ObjCARC/basic.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -aa-pipeline=basic-aa -passes=objc-arc -S < %s | FileCheck %s target datalayout = "e-p:64:64:64" @@ -31,11 +32,23 @@ declare ptr @objc_msgSend(ptr, ptr, ...) ; Simple retain+release pair deletion, with some intervening control ; flow and harmless instructions. -; CHECK: define void @test0_precise(ptr %x, i1 %p) [[NUW:#[0-9]+]] { -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test0_precise(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test0_precise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -54,10 +67,21 @@ return: ret void } -; CHECK: define void @test0_imprecise(ptr %x, i1 %p) [[NUW]] { -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test0_imprecise(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test0_imprecise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -81,11 +105,26 @@ return: ; TODO: Make the llvm.objc.release's argument be %0. -; CHECK: define void @test1_precise(ptr %x, i1 %p, i1 %q) [[NUW]] { -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %x) -; CHECK: {{^}}} define void @test1_precise(ptr %x, i1 %p, i1 %q) nounwind { +; CHECK-LABEL: define void @test1_precise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br i1 [[Q]], label %[[RETURN]], label %[[ALT_RETURN:.*]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; CHECK: [[ALT_RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -108,11 +147,26 @@ alt_return: ret void } -; CHECK: define void @test1_imprecise(ptr %x, i1 %p, i1 %q) [[NUW]] { -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test1_imprecise(ptr %x, i1 %p, i1 %q) nounwind { +; CHECK-LABEL: define void @test1_imprecise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br i1 [[Q]], label %[[RETURN]], label %[[ALT_RETURN:.*]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3:![0-9]+]] +; CHECK-NEXT: ret void +; CHECK: [[ALT_RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -138,15 +192,24 @@ alt_return: ; Don't do partial elimination into two different CFG diamonds. -; CHECK: define void @test1b_precise(ptr %x, i1 %p, i1 %q) { -; CHECK: entry: -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK-NOT: @llvm.objc. -; CHECK: if.end5: -; CHECK: tail call void @llvm.objc.release(ptr %x) [[NUW]] -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test1b_precise(ptr %x, i1 %p, i1 %q) { +; CHECK-LABEL: define void @test1b_precise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[IF_THEN:.*]], label %[[IF_END:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: tail call void @callee() +; CHECK-NEXT: br label %[[IF_END]] +; CHECK: [[IF_END]]: +; CHECK-NEXT: br i1 [[Q]], label %[[IF_THEN3:.*]], label %[[IF_END5:.*]] +; CHECK: [[IF_THEN3]]: +; CHECK-NEXT: tail call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: br label %[[IF_END5]] +; CHECK: [[IF_END5]]: +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: tail call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %if.then, label %if.end @@ -167,15 +230,24 @@ if.end5: ; preds = %if.then3, %if.end ret void } -; CHECK-LABEL: define void @test1b_imprecise( -; CHECK: entry: -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) [[NUW:#[0-9]+]] -; CHECK-NOT: @llvm.objc. -; CHECK: if.end5: -; CHECK: tail call void @llvm.objc.release(ptr %x) [[NUW]], !clang.imprecise_release ![[RELEASE:[0-9]+]] -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test1b_imprecise(ptr %x, i1 %p, i1 %q) { +; CHECK-LABEL: define void @test1b_imprecise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[IF_THEN:.*]], label %[[IF_END:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: tail call void @callee() +; CHECK-NEXT: br label %[[IF_END]] +; CHECK: [[IF_END]]: +; CHECK-NEXT: br i1 [[Q]], label %[[IF_THEN3:.*]], label %[[IF_END5:.*]] +; CHECK: [[IF_THEN3]]: +; CHECK-NEXT: tail call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: br label %[[IF_END5]] +; CHECK: [[IF_END5]]: +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; entry: tail call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %if.then, label %if.end @@ -200,11 +272,25 @@ if.end5: ; preds = %if.then3, %if.end ; Like test0 but the pointer is passed to an intervening call, ; so the optimization is not safe. -; CHECK-LABEL: define void @test2_precise( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test2_precise(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test2_precise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @use_pointer(ptr [[TMP0]]) +; CHECK-NEXT: store float 3.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -225,11 +311,25 @@ return: ret void } -; CHECK-LABEL: define void @test2_imprecise( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test2_imprecise(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test2_imprecise( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @use_pointer(ptr [[TMP0]]) +; CHECK-NEXT: store float 3.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -255,11 +355,20 @@ return: ; TODO: For now, assume this can't happen. -; CHECK-LABEL: define void @test3_precise( ; TODO: @llvm.objc.retain(ptr %a) ; TODO: @llvm.objc.release ; CHECK: {{^}}} define void @test3_precise(ptr %x, ptr %q) nounwind { +; CHECK-LABEL: define void @test3_precise( +; CHECK-SAME: ptr [[X:%.*]], ptr [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[J:%.*]] = load volatile i1, ptr [[Q]], align 1 +; CHECK-NEXT: br i1 [[J]], label %[[LOOP]], label %[[RETURN:.*]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br label %loop @@ -273,11 +382,20 @@ return: ret void } -; CHECK-LABEL: define void @test3_imprecise( ; TODO: @llvm.objc.retain(ptr %a) ; TODO: @llvm.objc.release ; CHECK: {{^}}} define void @test3_imprecise(ptr %x, ptr %q) nounwind { +; CHECK-LABEL: define void @test3_imprecise( +; CHECK-SAME: ptr [[X:%.*]], ptr [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[J:%.*]] = load volatile i1, ptr [[Q]], align 1 +; CHECK-NEXT: br i1 [[J]], label %[[LOOP]], label %[[RETURN:.*]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br label %loop @@ -297,11 +415,22 @@ return: ; Like test0 but the retain is in a loop, ; so the optimization is not safe. -; CHECK-LABEL: define void @test4_precise( ; TODO: @llvm.objc.retain(ptr %a) ; TODO: @llvm.objc.release ; CHECK: {{^}}} define void @test4_precise(ptr %x, ptr %q) nounwind { +; CHECK-LABEL: define void @test4_precise( +; CHECK-SAME: ptr [[X:%.*]], ptr [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[J:%.*]] = load volatile i1, ptr [[Q]], align 1 +; CHECK-NEXT: br i1 [[J]], label %[[LOOP]], label %[[RETURN:.*]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br label %loop @@ -315,11 +444,22 @@ return: ret void } -; CHECK-LABEL: define void @test4_imprecise( ; TODO: @llvm.objc.retain(ptr %a) ; TODO: @llvm.objc.release ; CHECK: {{^}}} define void @test4_imprecise(ptr %x, ptr %q) nounwind { +; CHECK-LABEL: define void @test4_imprecise( +; CHECK-SAME: ptr [[X:%.*]], ptr [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[J:%.*]] = load volatile i1, ptr [[Q]], align 1 +; CHECK-NEXT: br i1 [[J]], label %[[LOOP]], label %[[RETURN:.*]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; entry: br label %loop @@ -337,11 +477,17 @@ return: ; Like test0 but the pointer is conditionally passed to an intervening call, ; so the optimization is not safe. -; CHECK-LABEL: define void @test5a( -; CHECK: @llvm.objc.retain(ptr -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test5a(ptr %x, i1 %q, ptr %y) nounwind { +; CHECK-LABEL: define void @test5a( +; CHECK-SAME: ptr [[X:%.*]], i1 [[Q:%.*]], ptr [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[S:%.*]] = select i1 [[Q]], ptr [[Y]], ptr [[X]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[S]]) +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind %s = select i1 %q, ptr %y, ptr %0 @@ -351,11 +497,17 @@ entry: ret void } -; CHECK-LABEL: define void @test5b( -; CHECK: @llvm.objc.retain(ptr -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test5b(ptr %x, i1 %q, ptr %y) nounwind { +; CHECK-LABEL: define void @test5b( +; CHECK-SAME: ptr [[X:%.*]], i1 [[Q:%.*]], ptr [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[S:%.*]] = select i1 [[Q]], ptr [[Y]], ptr [[X]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @use_pointer(ptr [[S]]) +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind %s = select i1 %q, ptr %y, ptr %0 @@ -369,16 +521,25 @@ entry: ; retain+release pair deletion, where the release happens on two different ; flow paths. -; CHECK-LABEL: define void @test6a( -; CHECK: entry: -; CHECK: tail call ptr @llvm.objc.retain -; CHECK: t: -; CHECK: call void @llvm.objc.release -; CHECK: f: -; CHECK: call void @llvm.objc.release -; CHECK: return: -; CHECK: {{^}}} define void @test6a(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test6a( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -399,10 +560,22 @@ return: ret void } -; CHECK-LABEL: define void @test6b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test6b(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test6b( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -423,16 +596,25 @@ return: ret void } -; CHECK-LABEL: define void @test6c( -; CHECK: entry: -; CHECK: tail call ptr @llvm.objc.retain -; CHECK: t: -; CHECK: call void @llvm.objc.release -; CHECK: f: -; CHECK: call void @llvm.objc.release -; CHECK: return: -; CHECK: {{^}}} define void @test6c(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test6c( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -453,16 +635,25 @@ return: ret void } -; CHECK-LABEL: define void @test6d( -; CHECK: entry: -; CHECK: tail call ptr @llvm.objc.retain -; CHECK: t: -; CHECK: call void @llvm.objc.release -; CHECK: f: -; CHECK: call void @llvm.objc.release -; CHECK: return: -; CHECK: {{^}}} define void @test6d(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test6d( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %p, label %t, label %f @@ -487,17 +678,25 @@ return: ; retain+release pair deletion, where the retain happens on two different ; flow paths. -; CHECK-LABEL: define void @test7( -; CHECK: entry: -; CHECK-NOT: llvm.objc. -; CHECK: t: -; CHECK: call ptr @llvm.objc.retain -; CHECK: f: -; CHECK: call ptr @llvm.objc.retain -; CHECK: return: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test7(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test7( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -518,10 +717,22 @@ return: ret void } -; CHECK-LABEL: define void @test7b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test7b(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test7b( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -544,15 +755,25 @@ return: ; Like test7, but there's a retain/retainBlock mismatch. Don't delete! -; CHECK-LABEL: define void @test7c( -; CHECK: t: -; CHECK: call ptr @llvm.objc.retainBlock -; CHECK: f: -; CHECK: call ptr @llvm.objc.retain -; CHECK: return: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test7c(ptr %x, i1 %p) nounwind { +; CHECK-LABEL: define void @test7c( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @llvm.objc.retainBlock(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -576,20 +797,32 @@ return: ; retain+release pair deletion, where the retain and release both happen on ; different flow paths. Wild! -; CHECK-LABEL: define void @test8a( -; CHECK: entry: -; CHECK: t: -; CHECK: @llvm.objc.retain -; CHECK: f: -; CHECK: @llvm.objc.retain -; CHECK: mid: -; CHECK: u: -; CHECK: @llvm.objc.release -; CHECK: g: -; CHECK: @llvm.objc.release -; CHECK: return: -; CHECK: {{^}}} define void @test8a(ptr %x, i1 %p, i1 %q) nounwind { +; CHECK-LABEL: define void @test8a( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID]] +; CHECK: [[MID]]: +; CHECK-NEXT: br i1 [[Q]], label %[[U:.*]], label %[[G:.*]] +; CHECK: [[U]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[G]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -620,10 +853,28 @@ return: ret void } -; CHECK-LABEL: define void @test8b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test8b(ptr %x, i1 %p, i1 %q) nounwind { +; CHECK-LABEL: define void @test8b( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID]] +; CHECK: [[MID]]: +; CHECK-NEXT: br i1 [[Q]], label %[[U:.*]], label %[[G:.*]] +; CHECK: [[U]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[G]]: +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -654,21 +905,30 @@ return: ret void } -; CHECK-LABEL: define void @test8c( -; CHECK: entry: -; CHECK: t: -; CHECK-NOT: @llvm.objc. -; CHECK: f: -; CHECK-NOT: @llvm.objc. -; CHECK: mid: -; CHECK: u: -; CHECK: @llvm.objc.retain -; CHECK: @llvm.objc.release -; CHECK: g: -; CHECK-NOT: @llvm.objc. -; CHECK: return: -; CHECK: {{^}}} define void @test8c(ptr %x, i1 %p, i1 %q) nounwind { +; CHECK-LABEL: define void @test8c( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID]] +; CHECK: [[MID]]: +; CHECK-NEXT: br i1 [[Q]], label %[[U:.*]], label %[[G:.*]] +; CHECK: [[U]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[G]]: +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -699,20 +959,32 @@ return: ret void } -; CHECK-LABEL: define void @test8d( -; CHECK: entry: -; CHECK: t: -; CHECK: @llvm.objc.retain -; CHECK: f: -; CHECK: @llvm.objc.retain -; CHECK: mid: -; CHECK: u: -; CHECK: @llvm.objc.release -; CHECK: g: -; CHECK: @llvm.objc.release -; CHECK: return: -; CHECK: {{^}}} define void @test8d(ptr %x, i1 %p, i1 %q) nounwind { +; CHECK-LABEL: define void @test8d( +; CHECK-SAME: ptr [[X:%.*]], i1 [[P:%.*]], i1 [[Q:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[P]], label %[[T:.*]], label %[[F:.*]] +; CHECK: [[T]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i8 3, ptr [[X]], align 1 +; CHECK-NEXT: store float 2.000000e+00, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store i32 7, ptr [[X]], align 4 +; CHECK-NEXT: br label %[[MID]] +; CHECK: [[MID]]: +; CHECK-NEXT: br i1 [[Q]], label %[[U:.*]], label %[[G:.*]] +; CHECK: [[U]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: br label %[[RETURN:.*]] +; CHECK: [[G]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; entry: br i1 %p, label %t, label %f @@ -745,10 +1017,12 @@ return: ; Trivial retain+release pair deletion. -; CHECK-LABEL: define void @test9( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test9(ptr %x) nounwind { +; CHECK-LABEL: define void @test9( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind call void @llvm.objc.release(ptr %0) nounwind @@ -757,11 +1031,15 @@ entry: ; Retain+release pair, but on an unknown pointer relationship. Don't delete! -; CHECK-LABEL: define void @test9b( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.release(ptr %s) -; CHECK: {{^}}} define void @test9b(ptr %x, i1 %j, ptr %p) nounwind { +; CHECK-LABEL: define void @test9b( +; CHECK-SAME: ptr [[X:%.*]], i1 [[J:%.*]], ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[S:%.*]] = select i1 [[J]], ptr [[X]], ptr [[P]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[S]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind %s = select i1 %j, ptr %x, ptr %p @@ -771,13 +1049,16 @@ entry: ; Trivial retain+release pair with intervening calls - don't delete! -; CHECK-LABEL: define void @test10( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @callee -; CHECK: @use_pointer -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test10(ptr %x) nounwind { +; CHECK-LABEL: define void @test10( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind call void @callee() @@ -790,11 +1071,15 @@ entry: ; Also, add a tail keyword, since llvm.objc.retain can never be passed ; a stack argument. -; CHECK-LABEL: define void @test11( -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK: call ptr @llvm.objc.autorelease(ptr %0) [[NUW]] -; CHECK: {{^}}} define void @test11(ptr %x) nounwind { +; CHECK-LABEL: define void @test11( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.autorelease(ptr %0) nounwind @@ -804,11 +1089,12 @@ entry: ; Same as test11 but with no use_pointer call. Delete the pair! -; CHECK-LABEL: define void @test11a( -; CHECK: entry: -; CHECK-NEXT: ret void -; CHECK: {{^}}} define void @test11a(ptr %x) nounwind { +; CHECK-LABEL: define void @test11a( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.autorelease(ptr %0) nounwind @@ -819,11 +1105,14 @@ entry: ; since if the frontend emitted code for an __autoreleasing variable, we may ; want it to be in the autorelease pool. -; CHECK-LABEL: define ptr @test11b( -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK: call ptr @llvm.objc.autorelease(ptr %0) [[NUW]] -; CHECK: {{^}}} define ptr @test11b(ptr %x) nounwind { +; CHECK-LABEL: define ptr @test11b( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: ret ptr [[X]] +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.autorelease(ptr %0) nounwind @@ -833,13 +1122,17 @@ entry: ; We can not delete this retain, release since we do not have a post-dominating ; use of the release. -; CHECK-LABEL: define void @test12( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: @llvm.objc.retain -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test12(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test12( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.retain(ptr %x) nounwind @@ -851,13 +1144,16 @@ entry: ; Trivial retain,autorelease pair. Don't delete! -; CHECK-LABEL: define void @test13( -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK: @use_pointer(ptr %x) -; CHECK: call ptr @llvm.objc.autorelease(ptr %x) [[NUW]] -; CHECK: {{^}}} define void @test13(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test13( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.objc.autorelease(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.retain(ptr %x) nounwind @@ -868,16 +1164,17 @@ entry: ; Delete the retain+release pair. -; CHECK-LABEL: define void @test13b( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test13b(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test13b( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.retain(ptr %x) nounwind @@ -892,14 +1189,18 @@ entry: ; Don't delete the retain+release pair because there's an ; autoreleasePoolPop in the way. -; CHECK-LABEL: define void @test13c( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc.autoreleasePoolPop -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @use_pointer -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test13c(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test13c( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr undef) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call void @llvm.objc.autoreleasePoolPop(ptr undef) @@ -913,17 +1214,18 @@ entry: ; Like test13c, but there's an autoreleasePoolPush in the way, but that ; doesn't matter. -; CHECK-LABEL: define void @test13d( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: @llvm.objc.autoreleasePoolPush -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test13d(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test13d( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.autoreleasePoolPush() @@ -940,16 +1242,17 @@ entry: ; another release. But it is not known safe in the top down direction. We can ; not eliminate it. -; CHECK-LABEL: define void @test14( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @llvm.objc.release -; CHECK-NEXT: @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test14(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test14( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call void @use_pointer(ptr %x) @@ -962,15 +1265,16 @@ entry: ; Trivial retain,autorelease pair with intervening call, but it's post-dominated ; by another release. Don't delete anything. -; CHECK-LABEL: define void @test15( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @llvm.objc.autorelease(ptr %x) -; CHECK-NEXT: @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test15(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test15( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @llvm.objc.autorelease(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call void @use_pointer(ptr %x) @@ -982,14 +1286,15 @@ entry: ; Trivial retain,autorelease pair, post-dominated ; by another release. Delete the retain and release. -; CHECK-LABEL: define void @test15b( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain -; CHECK-NEXT: @llvm.objc.autorelease -; CHECK-NEXT: @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test15b(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test15b( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.autorelease(ptr %x) nounwind @@ -997,12 +1302,13 @@ entry: ret void } -; CHECK-LABEL: define void @test15c( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.autorelease -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test15c(ptr %x, i64 %n) { +; CHECK-LABEL: define void @test15c( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @llvm.objc.autorelease(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.autorelease(ptr %x) nounwind @@ -1012,14 +1318,29 @@ entry: ; Retain+release pairs in diamonds, all dominated by a retain. -; CHECK-LABEL: define void @test16a( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK-NOT: @objc -; CHECK: purple: -; CHECK: @use_pointer -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test16a(i1 %a, i1 %b, ptr %x) { +; CHECK-LABEL: define void @test16a( +; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[A]], label %[[RED:.*]], label %[[ORANGE:.*]] +; CHECK: [[RED]]: +; CHECK-NEXT: br label %[[YELLOW:.*]] +; CHECK: [[ORANGE]]: +; CHECK-NEXT: br label %[[YELLOW]] +; CHECK: [[YELLOW]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: br i1 [[B]], label %[[GREEN:.*]], label %[[BLUE:.*]] +; CHECK: [[GREEN]]: +; CHECK-NEXT: br label %[[PURPLE:.*]] +; CHECK: [[BLUE]]: +; CHECK-NEXT: br label %[[PURPLE]] +; CHECK: [[PURPLE]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %a, label %red, label %orange @@ -1051,15 +1372,30 @@ purple: ret void } -; CHECK-LABEL: define void @test16b( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK-NOT: @objc -; CHECK: purple: -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @use_pointer -; CHECK-NEXT: @llvm.objc.release -; CHECK: {{^}}} define void @test16b(i1 %a, i1 %b, ptr %x) { +; CHECK-LABEL: define void @test16b( +; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[A]], label %[[RED:.*]], label %[[ORANGE:.*]] +; CHECK: [[RED]]: +; CHECK-NEXT: br label %[[YELLOW:.*]] +; CHECK: [[ORANGE]]: +; CHECK-NEXT: br label %[[YELLOW]] +; CHECK: [[YELLOW]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: br i1 [[B]], label %[[GREEN:.*]], label %[[BLUE:.*]] +; CHECK: [[GREEN]]: +; CHECK-NEXT: br label %[[PURPLE:.*]] +; CHECK: [[BLUE]]: +; CHECK-NEXT: br label %[[PURPLE]] +; CHECK: [[PURPLE]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %a, label %red, label %orange @@ -1092,14 +1428,29 @@ purple: ret void } -; CHECK-LABEL: define void @test16c( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK-NOT: @objc -; CHECK: purple: -; CHECK: @use_pointer -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test16c(i1 %a, i1 %b, ptr %x) { +; CHECK-LABEL: define void @test16c( +; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[A]], label %[[RED:.*]], label %[[ORANGE:.*]] +; CHECK: [[RED]]: +; CHECK-NEXT: br label %[[YELLOW:.*]] +; CHECK: [[ORANGE]]: +; CHECK-NEXT: br label %[[YELLOW]] +; CHECK: [[YELLOW]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: br i1 [[B]], label %[[GREEN:.*]], label %[[BLUE:.*]] +; CHECK: [[GREEN]]: +; CHECK-NEXT: br label %[[PURPLE:.*]] +; CHECK: [[BLUE]]: +; CHECK-NEXT: br label %[[PURPLE]] +; CHECK: [[PURPLE]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %a, label %red, label %orange @@ -1131,11 +1482,31 @@ purple: ret void } -; CHECK-LABEL: define void @test16d( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK: @llvm.objc -; CHECK: {{^}}} define void @test16d(i1 %a, i1 %b, ptr %x) { +; CHECK-LABEL: define void @test16d( +; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[A]], label %[[RED:.*]], label %[[ORANGE:.*]] +; CHECK: [[RED]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[YELLOW:.*]] +; CHECK: [[ORANGE]]: +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[YELLOW]] +; CHECK: [[YELLOW]]: +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: br i1 [[B]], label %[[GREEN:.*]], label %[[BLUE:.*]] +; CHECK: [[GREEN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[PURPLE:.*]] +; CHECK: [[BLUE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: br label %[[PURPLE]] +; CHECK: [[PURPLE]]: +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind br i1 %a, label %red, label %orange @@ -1167,10 +1538,10 @@ purple: ; Delete no-ops. -; CHECK-LABEL: define void @test18( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test18() { +; CHECK-LABEL: define void @test18() { +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr null) call void @llvm.objc.release(ptr null) call ptr @llvm.objc.autorelease(ptr null) @@ -1179,10 +1550,10 @@ define void @test18() { ; Delete no-ops where undef can be assumed to be null. -; CHECK-LABEL: define void @test18b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test18b() { +; CHECK-LABEL: define void @test18b() { +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr undef) call void @llvm.objc.release(ptr undef) call ptr @llvm.objc.autorelease(ptr undef) @@ -1192,14 +1563,16 @@ define void @test18b() { ; Replace uses of arguments with uses of return values, to reduce ; register pressure. -; CHECK: define void @test19(ptr %y) { -; CHECK: %0 = tail call ptr @llvm.objc.retain(ptr %y) -; CHECK: call void @use_pointer(ptr %y) -; CHECK: call void @use_pointer(ptr %y) -; CHECK: call void @llvm.objc.release(ptr %y) -; CHECK: ret void -; CHECK: {{^}}} define void @test19(ptr %y) { +; CHECK-LABEL: define void @test19( +; CHECK-SAME: ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[Y]]) +; CHECK-NEXT: call void @use_pointer(ptr [[Y]]) +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %y) nounwind call void @use_pointer(ptr %y) @@ -1210,24 +1583,37 @@ entry: ; Bitcast insertion -; CHECK-LABEL: define void @test20( -; CHECK: %tmp1 = tail call ptr @llvm.objc.retain(ptr %self) [[NUW]] -; CHECK-NEXT: invoke -; CHECK: {{^}}} define void @test20(ptr %self) personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define void @test20( +; CHECK-SAME: ptr [[SELF:%.*]]) personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[IF_THEN12:.*]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[SELF]]) #[[ATTR0]] +; CHECK-NEXT: invoke void @invokee() +; CHECK-NEXT: to label %[[INVOKE_CONT23:.*]] unwind label %[[LPAD20:.*]] +; CHECK: [[INVOKE_CONT23]]: +; CHECK-NEXT: invoke void @invokee() +; CHECK-NEXT: to label %[[IF_END:.*]] unwind label %[[LPAD20]] +; CHECK: [[LPAD20]]: +; CHECK-NEXT: [[TMP502:%.*]] = phi ptr [ undef, %[[INVOKE_CONT23]] ], [ [[SELF]], %[[IF_THEN12]] ] +; CHECK-NEXT: [[EXN:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: unreachable +; CHECK: [[IF_END]]: +; CHECK-NEXT: ret void +; if.then12: %tmp1 = call ptr @llvm.objc.retain(ptr %self) nounwind invoke void @invokee() - to label %invoke.cont23 unwind label %lpad20 + to label %invoke.cont23 unwind label %lpad20 invoke.cont23: ; preds = %if.then12 invoke void @invokee() - to label %if.end unwind label %lpad20 + to label %if.end unwind label %lpad20 lpad20: ; preds = %invoke.cont23, %if.then12 %tmp502 = phi ptr [ undef, %invoke.cont23 ], [ %self, %if.then12 ] %exn = landingpad {ptr, i32} - cleanup + cleanup unreachable if.end: ; preds = %invoke.cont23 @@ -1237,11 +1623,12 @@ if.end: ; preds = %invoke.cont23 ; Delete a redundant retain,autorelease when forwaring a call result ; directly to a return value. -; CHECK-LABEL: define ptr @test21( -; CHECK: call ptr @returner() -; CHECK-NEXT: ret ptr %call -; CHECK-NEXT: } define ptr @test21() { +; CHECK-LABEL: define ptr @test21() { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: ret ptr [[CALL]] +; entry: %call = call ptr @returner() %0 = call ptr @llvm.objc.retain(ptr %call) nounwind @@ -1251,14 +1638,18 @@ entry: ; Move an objc call up through a phi that has null operands. -; CHECK-LABEL: define void @test22( -; CHECK: B: -; CHECK: call void @llvm.objc.release(ptr %p) -; CHECK: br label %C -; CHECK: C: ; preds = %B, %A -; CHECK-NOT: @llvm.objc.release -; CHECK: {{^}}} define void @test22(ptr %p, i1 %a) { +; CHECK-LABEL: define void @test22( +; CHECK-SAME: ptr [[P:%.*]], i1 [[A:%.*]]) { +; CHECK-NEXT: br i1 [[A]], label %[[A:.*]], label %[[B:.*]] +; CHECK: [[A]]: +; CHECK-NEXT: br label %[[C:.*]] +; CHECK: [[B]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[C]] +; CHECK: [[C]]: +; CHECK-NEXT: ret void +; br i1 %a, label %A, label %B A: br label %C @@ -1272,11 +1663,19 @@ C: ; Do not move an llvm.objc.release that doesn't have the clang.imprecise_release tag. -; CHECK-LABEL: define void @test22_precise( -; CHECK: %[[P0:.*]] = phi ptr -; CHECK: call void @llvm.objc.release(ptr %[[P0]]) -; CHECK: ret void define void @test22_precise(ptr %p, i1 %a) { +; CHECK-LABEL: define void @test22_precise( +; CHECK-SAME: ptr [[P:%.*]], i1 [[A:%.*]]) { +; CHECK-NEXT: br i1 [[A]], label %[[A:.*]], label %[[B:.*]] +; CHECK: [[A]]: +; CHECK-NEXT: br label %[[C:.*]] +; CHECK: [[B]]: +; CHECK-NEXT: br label %[[C]] +; CHECK: [[C]]: +; CHECK-NEXT: [[H:%.*]] = phi ptr [ null, %[[A]] ], [ [[P]], %[[B]] ] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[H]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; br i1 %a, label %A, label %B A: br label %C @@ -1290,11 +1689,15 @@ C: ; Any call can decrement a retain count. -; CHECK-LABEL: define void @test24( -; CHECK: @llvm.objc.retain(ptr %a) -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test24(ptr %r, ptr %a) { +; CHECK-LABEL: define void @test24( +; CHECK-SAME: ptr [[R:%.*]], ptr [[A:%.*]]) { +; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[R]]) +; CHECK-NEXT: [[Q:%.*]] = load i8, ptr [[A]], align 1 +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %a) call void @use_pointer(ptr %r) %q = load i8, ptr %a @@ -1305,14 +1708,20 @@ define void @test24(ptr %r, ptr %a) { ; Don't move a retain/release pair if the release can be moved ; but the retain can't be moved to balance it. -; CHECK-LABEL: define void @test25( -; CHECK: entry: -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: true: -; CHECK: done: -; CHECK: call void @llvm.objc.release(ptr %p) -; CHECK: {{^}}} define void @test25(ptr %p, i1 %x) { +; CHECK-LABEL: define void @test25( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -1330,14 +1739,20 @@ done: ; Don't move a retain/release pair if the retain can be moved ; but the release can't be moved to balance it. -; CHECK-LABEL: define void @test26( -; CHECK: entry: -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: true: -; CHECK: done: -; CHECK: call void @llvm.objc.release(ptr %p) -; CHECK: {{^}}} define void @test26(ptr %p, i1 %x) { +; CHECK-LABEL: define void @test26( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1354,15 +1769,20 @@ done: ; Don't sink the retain,release into the loop. -; CHECK-LABEL: define void @test27( -; CHECK: entry: -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: loop: -; CHECK-NOT: @llvm.objc. -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test27(ptr %p, i1 %x, i1 %y) { +; CHECK-LABEL: define void @test27( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], i1 [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[LOOP:.*]], label %[[DONE:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[Y]], label %[[DONE]], label %[[LOOP]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %loop, label %done @@ -1379,17 +1799,20 @@ done: ; Trivial code motion case: Triangle. -; CHECK-LABEL: define void @test28( -; CHECK-NOT: @llvm.objc. -; CHECK: true: -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void @callee() -; CHECK: store -; CHECK: call void @llvm.objc.release -; CHECK: done: -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test28(ptr %p, i1 %x) { +; CHECK-LABEL: define void @test28( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1407,18 +1830,21 @@ done: ; Trivial code motion case: Triangle, but no metadata. Don't move past ; unrelated memory references! -; CHECK-LABEL: define void @test28b( -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK-NOT: @llvm.objc. -; CHECK: call void @callee() -; CHECK-NOT: @llvm.objc. -; CHECK: store -; CHECK-NOT: @llvm.objc. -; CHECK: done: -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test28b(ptr %p, i1 %x, ptr noalias %t) { +; CHECK-LABEL: define void @test28b( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr noalias [[T:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: store i8 0, ptr [[T]], align 1 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1437,17 +1863,21 @@ done: ; Trivial code motion case: Triangle, with metadata. Do move past ; unrelated memory references! And preserve the metadata. -; CHECK-LABEL: define void @test28c( -; CHECK-NOT: @llvm.objc. -; CHECK: true: -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void @callee() -; CHECK: store -; CHECK: call void @llvm.objc.release(ptr %p) [[NUW]], !clang.imprecise_release -; CHECK: done: -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test28c(ptr %p, i1 %x, ptr noalias %t) { +; CHECK-LABEL: define void @test28c( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr noalias [[T:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: store i8 0, ptr [[T]], align 1 +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1465,17 +1895,23 @@ done: ; Like test28. but with two releases. -; CHECK-LABEL: define void @test29( -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK: call void @callee() -; CHECK: store -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: ohno: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test29(ptr %p, i1 %x, i1 %y) { +; CHECK-LABEL: define void @test29( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], i1 [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[Y]], label %[[DONE]], label %[[OHNO:.*]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; CHECK: [[OHNO]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1497,18 +1933,25 @@ ohno: ; Basic case with the use and call in a diamond ; with an extra release. -; CHECK-LABEL: define void @test30( -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK: call void @callee() -; CHECK: store -; CHECK: false: -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: ohno: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test30(ptr %p, i1 %x, i1 %y, i1 %z) { +; CHECK-LABEL: define void @test30( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], i1 [[Y:%.*]], i1 [[Z:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[FALSE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[Y]], label %[[DONE:.*]], label %[[OHNO:.*]] +; CHECK: [[FALSE]]: +; CHECK-NEXT: br i1 [[Z]], label %[[DONE]], label %[[OHNO]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; CHECK: [[OHNO]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %false @@ -1532,17 +1975,21 @@ ohno: ; Basic case with a mergeable release. -; CHECK-LABEL: define void @test31( -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: call void @callee() -; CHECK: store -; CHECK: true: -; CHECK: call void @llvm.objc.release -; CHECK: false: -; CHECK: call void @llvm.objc.release -; CHECK: ret void -; CHECK: {{^}}} define void @test31(ptr %p, i1 %x) { +; CHECK-LABEL: define void @test31( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[FALSE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; CHECK: [[FALSE]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -1558,15 +2005,20 @@ false: ; Don't consider bitcasts or getelementptrs direct uses. -; CHECK-LABEL: define void @test32( -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK: call void @callee() -; CHECK: store -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test32(ptr %p, i1 %x) { +; CHECK-LABEL: define void @test32( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1583,15 +2035,20 @@ done: ; Do consider icmps to be direct uses. -; CHECK-LABEL: define void @test33( -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK: call void @callee() -; CHECK: icmp -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test33(ptr %p, i1 %x, ptr %y) { +; CHECK-LABEL: define void @test33( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: [[V:%.*]] = icmp eq ptr [[P]], [[Y]] +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1609,13 +2066,19 @@ done: ; Delete retain,release if there's just a possible dec and we have imprecise ; releases. -; CHECK-LABEL: define void @test34a( -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test34a(ptr %p, i1 %x, ptr %y) { +; CHECK-LABEL: define void @test34a( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1629,10 +2092,17 @@ done: ret void } -; CHECK-LABEL: define void @test34b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test34b(ptr %p, i1 %x, ptr %y) { +; CHECK-LABEL: define void @test34b( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1651,14 +2121,19 @@ done: ; release. ; Precise. -; CHECK-LABEL: define void @test35a( -; CHECK: entry: -; CHECK: call ptr @llvm.objc.retain -; CHECK: true: -; CHECK: done: -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test35a(ptr %p, i1 %x, ptr %y) { +; CHECK-LABEL: define void @test35a( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[F0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: [[V:%.*]] = icmp eq ptr [[P]], [[Y]] +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1673,10 +2148,17 @@ done: } ; Imprecise. -; CHECK-LABEL: define void @test35b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test35b(ptr %p, i1 %x, ptr %y) { +; CHECK-LABEL: define void @test35b( +; CHECK-SAME: ptr [[P:%.*]], i1 [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[X]], label %[[TRUE:.*]], label %[[DONE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: [[V:%.*]] = icmp eq ptr [[P]], [[Y]] +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: ret void +; entry: %f0 = call ptr @llvm.objc.retain(ptr %p) br i1 %x, label %true, label %done @@ -1692,14 +2174,16 @@ done: ; Delete a retain,release if there's no actual use and we have precise release. -; CHECK-LABEL: define void @test36a( -; CHECK: @llvm.objc.retain -; CHECK: call void @callee() -; CHECK-NOT: @llvm.objc. -; CHECK: call void @callee() -; CHECK: @llvm.objc.release -; CHECK: {{^}}} define void @test36a(ptr %p) { +; CHECK-LABEL: define void @test36a( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -1710,10 +2194,14 @@ entry: ; Like test36, but with metadata. -; CHECK-LABEL: define void @test36b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test36b(ptr %p) { +; CHECK-LABEL: define void @test36b( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -1724,10 +2212,34 @@ entry: ; Be aggressive about analyzing phis to eliminate possible uses. -; CHECK-LABEL: define void @test38( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test38(ptr %p, i1 %u, i1 %m, ptr %z, ptr %y, ptr %x, ptr %w) { +; CHECK-LABEL: define void @test38( +; CHECK-SAME: ptr [[P:%.*]], i1 [[U:%.*]], i1 [[M:%.*]], ptr [[Z:%.*]], ptr [[Y:%.*]], ptr [[X:%.*]], ptr [[W:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br i1 [[U]], label %[[TRUE:.*]], label %[[FALSE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: br i1 [[M]], label %[[A:.*]], label %[[B:.*]] +; CHECK: [[FALSE]]: +; CHECK-NEXT: br i1 [[M]], label %[[C:.*]], label %[[D:.*]] +; CHECK: [[A]]: +; CHECK-NEXT: br label %[[E:.*]] +; CHECK: [[B]]: +; CHECK-NEXT: br label %[[E]] +; CHECK: [[C]]: +; CHECK-NEXT: br label %[[F:.*]] +; CHECK: [[D]]: +; CHECK-NEXT: br label %[[F]] +; CHECK: [[E]]: +; CHECK-NEXT: [[J:%.*]] = phi ptr [ [[Z]], %[[A]] ], [ [[Y]], %[[B]] ] +; CHECK-NEXT: br label %[[G:.*]] +; CHECK: [[F]]: +; CHECK-NEXT: [[K:%.*]] = phi ptr [ [[W]], %[[C]] ], [ [[X]], %[[D]] ] +; CHECK-NEXT: br label %[[G]] +; CHECK: [[G]]: +; CHECK-NEXT: [[H:%.*]] = phi ptr [ [[J]], %[[E]] ], [ [[K]], %[[F]] ] +; CHECK-NEXT: call void @use_pointer(ptr [[H]]) +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) br i1 %u, label %true, label %false @@ -1758,10 +2270,16 @@ g: ; Delete retain,release pairs around loops. -; CHECK-LABEL: define void @test39( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test39(ptr %p, i1 %arg) { +; CHECK-LABEL: define void @test39( +; CHECK-SAME: ptr [[P:%.*]], i1 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: br i1 [[ARG]], label %[[LOOP]], label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %p) br label %loop @@ -1776,10 +2294,17 @@ exit: ; preds = %loop ; Delete retain,release pairs around loops containing uses. -; CHECK-LABEL: define void @test39b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test39b(ptr %p, i1 %arg) { +; CHECK-LABEL: define void @test39b( +; CHECK-SAME: ptr [[P:%.*]], i1 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[ARG]], label %[[LOOP]], label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %p) br label %loop @@ -1795,10 +2320,17 @@ exit: ; preds = %loop ; Delete retain,release pairs around loops containing potential decrements. -; CHECK-LABEL: define void @test39c( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test39c(ptr %p, i1 %arg) { +; CHECK-LABEL: define void @test39c( +; CHECK-SAME: ptr [[P:%.*]], i1 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: br i1 [[ARG]], label %[[LOOP]], label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %p) br label %loop @@ -1815,10 +2347,17 @@ exit: ; preds = %loop ; Delete retain,release pairs around loops even if ; the successors are in a different order. -; CHECK-LABEL: define void @test40( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test40(ptr %p, i1 %arg) { +; CHECK-LABEL: define void @test40( +; CHECK-SAME: ptr [[P:%.*]], i1 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: br i1 [[ARG]], label %[[EXIT:.*]], label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %p) br label %loop @@ -1835,18 +2374,19 @@ exit: ; preds = %loop ; Do the known-incremented retain+release elimination even if the pointer ; is also autoreleased. -; CHECK-LABEL: define void @test42( -; CHECK-NEXT: entry: -; CHECK-NEXT: call ptr @llvm.objc.retain(ptr %p) -; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @llvm.objc.release(ptr %p) -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test42(ptr %p) { +; CHECK-LABEL: define void @test42( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) call ptr @llvm.objc.autorelease(ptr %p) @@ -1863,18 +2403,19 @@ entry: ; Don't the known-incremented retain+release elimination if the pointer is ; autoreleased and there's an autoreleasePoolPop. -; CHECK-LABEL: define void @test43( -; CHECK-NEXT: entry: -; CHECK-NEXT: call ptr @llvm.objc.retain(ptr %p) -; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %p) -; CHECK-NEXT: call ptr @llvm.objc.retain -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr undef) -; CHECK-NEXT: call void @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test43(ptr %p) { +; CHECK-LABEL: define void @test43( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr undef) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) call ptr @llvm.objc.autorelease(ptr %p) @@ -1889,18 +2430,19 @@ entry: ; Do the known-incremented retain+release elimination if the pointer is ; autoreleased and there's an autoreleasePoolPush. -; CHECK-LABEL: define void @test43b( -; CHECK-NEXT: entry: -; CHECK-NEXT: call ptr @llvm.objc.retain(ptr %p) -; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call ptr @llvm.objc.autoreleasePoolPush() -; CHECK-NEXT: call void @use_pointer(ptr %p) -; CHECK-NEXT: call void @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test43b(ptr %p) { +; CHECK-LABEL: define void @test43b( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) call ptr @llvm.objc.autorelease(ptr %p) @@ -1916,10 +2458,11 @@ entry: ; Do retain+release elimination for non-provenance pointers. -; CHECK-LABEL: define void @test44( -; CHECK-NOT: llvm.objc. -; CHECK: {{^}}} define void @test44(ptr %pp) { +; CHECK-LABEL: define void @test44( +; CHECK-SAME: ptr [[PP:%.*]]) { +; CHECK-NEXT: ret void +; %p = load ptr, ptr %pp %q = call ptr @llvm.objc.retain(ptr %p) call void @llvm.objc.release(ptr %q) @@ -1929,13 +2472,17 @@ define void @test44(ptr %pp) { ; Don't delete retain+release with an unknown-provenance ; may-alias llvm.objc.release between them. -; CHECK-LABEL: define void @test45( -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: call void @llvm.objc.release(ptr %q) -; CHECK: call void @use_pointer(ptr %p) -; CHECK: call void @llvm.objc.release(ptr %p) -; CHECK: {{^}}} define void @test45(ptr %pp, ptr %qq) { +; CHECK-LABEL: define void @test45( +; CHECK-SAME: ptr [[PP:%.*]], ptr [[QQ:%.*]]) { +; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PP]], align 8 +; CHECK-NEXT: [[Q:%.*]] = load ptr, ptr [[QQ]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Q]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %p = load ptr, ptr %pp %q = load ptr, ptr %qq call ptr @llvm.objc.retain(ptr %p) @@ -1947,12 +2494,19 @@ define void @test45(ptr %pp, ptr %qq) { ; Don't delete retain and autorelease here. -; CHECK-LABEL: define void @test46( -; CHECK: tail call ptr @llvm.objc.retain(ptr %p) [[NUW]] -; CHECK: true: -; CHECK: call ptr @llvm.objc.autorelease(ptr %p) [[NUW]] -; CHECK: {{^}}} define void @test46(ptr %p, i1 %a) { +; CHECK-LABEL: define void @test46( +; CHECK-SAME: ptr [[P:%.*]], i1 [[A:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[A]], label %[[TRUE:.*]], label %[[FALSE:.*]] +; CHECK: [[TRUE]]: +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[P]]) +; CHECK-NEXT: ret void +; CHECK: [[FALSE]]: +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %p) br i1 %a, label %true, label %false @@ -1968,33 +2522,33 @@ false: ; Delete no-op cast calls. -; CHECK-LABEL: define ptr @test47( -; CHECK-NOT: call -; CHECK: ret ptr %p -; CHECK: {{^}}} define ptr @test47(ptr %p) nounwind { +; CHECK-LABEL: define ptr @test47( +; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: ret ptr [[P]] +; %x = call ptr @llvm.objc.retainedObject(ptr %p) ret ptr %x } ; Delete no-op cast calls. -; CHECK-LABEL: define ptr @test48( -; CHECK-NOT: call -; CHECK: ret ptr %p -; CHECK: {{^}}} define ptr @test48(ptr %p) nounwind { +; CHECK-LABEL: define ptr @test48( +; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: ret ptr [[P]] +; %x = call ptr @llvm.objc.unretainedObject(ptr %p) ret ptr %x } ; Delete no-op cast calls. -; CHECK-LABEL: define ptr @test49( -; CHECK-NOT: call -; CHECK: ret ptr %p -; CHECK: {{^}}} define ptr @test49(ptr %p) nounwind { +; CHECK-LABEL: define ptr @test49( +; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: ret ptr [[P]] +; %x = call ptr @llvm.objc.unretainedPointer(ptr %p) ret ptr %x } @@ -2002,14 +2556,15 @@ define ptr @test49(ptr %p) nounwind { ; Do delete retain+release with intervening stores of the address value if we ; have imprecise release attached to llvm.objc.release. -; CHECK-LABEL: define void @test50a( -; CHECK-NEXT: call ptr @llvm.objc.retain -; CHECK-NEXT: call void @callee -; CHECK-NEXT: store -; CHECK-NEXT: call void @llvm.objc.release -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test50a(ptr %p, ptr %pp) { +; CHECK-LABEL: define void @test50a( +; CHECK-SAME: ptr [[P:%.*]], ptr [[PP:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store ptr [[P]], ptr [[PP]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %p) call void @callee() store ptr %p, ptr %pp @@ -2017,10 +2572,13 @@ define void @test50a(ptr %p, ptr %pp) { ret void } -; CHECK-LABEL: define void @test50b( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test50b(ptr %p, ptr %pp) { +; CHECK-LABEL: define void @test50b( +; CHECK-SAME: ptr [[P:%.*]], ptr [[PP:%.*]]) { +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store ptr [[P]], ptr [[PP]], align 8 +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %p) call void @callee() store ptr %p, ptr %pp @@ -2032,12 +2590,15 @@ define void @test50b(ptr %p, ptr %pp) { ; Don't delete retain+release with intervening stores through the ; address value. -; CHECK-LABEL: define void @test51a( -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: call void @llvm.objc.release(ptr %p) -; CHECK: ret void -; CHECK: {{^}}} define void @test51a(ptr %p) { +; CHECK-LABEL: define void @test51a( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %p) call void @callee() store i8 0, ptr %p @@ -2045,12 +2606,15 @@ define void @test51a(ptr %p) { ret void } -; CHECK-LABEL: define void @test51b( -; CHECK: call ptr @llvm.objc.retain(ptr %p) -; CHECK: call void @llvm.objc.release(ptr %p) -; CHECK: ret void -; CHECK: {{^}}} define void @test51b(ptr %p) { +; CHECK-LABEL: define void @test51b( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: store i8 0, ptr [[P]], align 1 +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %p) call void @callee() store i8 0, ptr %p @@ -2061,14 +2625,17 @@ define void @test51b(ptr %p) { ; Don't delete retain+release with intervening use of a pointer of ; unknown provenance. -; CHECK-LABEL: define void @test52a( -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void @callee() -; CHECK: call void @use_pointer(ptr %z) -; CHECK: call void @llvm.objc.release -; CHECK: ret void -; CHECK: {{^}}} define void @test52a(ptr %zz, ptr %pp) { +; CHECK-LABEL: define void @test52a( +; CHECK-SAME: ptr [[ZZ:%.*]], ptr [[PP:%.*]]) { +; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PP]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: [[Z:%.*]] = load ptr, ptr [[ZZ]], align 8 +; CHECK-NEXT: call void @use_pointer(ptr [[Z]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %p = load ptr, ptr %pp %1 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -2078,14 +2645,17 @@ define void @test52a(ptr %zz, ptr %pp) { ret void } -; CHECK-LABEL: define void @test52b( -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void @callee() -; CHECK: call void @use_pointer(ptr %z) -; CHECK: call void @llvm.objc.release -; CHECK: ret void -; CHECK: {{^}}} define void @test52b(ptr %zz, ptr %pp) { +; CHECK-LABEL: define void @test52b( +; CHECK-SAME: ptr [[ZZ:%.*]], ptr [[PP:%.*]]) { +; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PP]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: [[Z:%.*]] = load ptr, ptr [[ZZ]], align 8 +; CHECK-NEXT: call void @use_pointer(ptr [[Z]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %p = load ptr, ptr %pp %1 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -2100,10 +2670,17 @@ define void @test52b(ptr %zz, ptr %pp) { ; Oops. That's wrong. Clang sometimes uses function types gratuitously. ; See rdar://10551239. -; CHECK-LABEL: define void @test53( -; CHECK: @llvm.objc. -; CHECK: {{^}}} define void @test53(ptr %zz, ptr %pp) { +; CHECK-LABEL: define void @test53( +; CHECK-SAME: ptr [[ZZ:%.*]], ptr [[PP:%.*]]) { +; CHECK-NEXT: [[P:%.*]] = load ptr, ptr [[PP]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: [[Z:%.*]] = load ptr, ptr [[ZZ]], align 8 +; CHECK-NEXT: call void @callee_fnptr(ptr [[Z]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %p = load ptr, ptr %pp %1 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -2115,12 +2692,12 @@ define void @test53(ptr %zz, ptr %pp) { ; Convert autorelease to release if the value is unused. -; CHECK-LABEL: define void @test54( -; CHECK: call ptr @returner() -; CHECK-NEXT: call void @llvm.objc.release(ptr %t) [[NUW]], !clang.imprecise_release ![[RELEASE]] -; CHECK-NEXT: ret void -; CHECK: {{^}}} define void @test54() { +; CHECK-LABEL: define void @test54() { +; CHECK-NEXT: [[T:%.*]] = call ptr @returner() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[T]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; %t = call ptr @returner() call ptr @llvm.objc.autorelease(ptr %t) ret void @@ -2128,10 +2705,14 @@ define void @test54() { ; Nested retain+release pairs. Delete them both. -; CHECK-LABEL: define void @test55( -; CHECK-NOT: @objc -; CHECK: {{^}}} define void @test55(ptr %x) { +; CHECK-LABEL: define void @test55( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %x) nounwind %1 = call ptr @llvm.objc.retain(ptr %x) nounwind @@ -2145,17 +2726,21 @@ entry: ; can be partially eliminated. Plus an extra outer pair to ; eliminate, for fun. -; CHECK-LABEL: define void @test56( -; CHECK-NOT: @objc -; CHECK: if.then: -; CHECK-NEXT: %0 = tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK-NEXT: tail call void @use_pointer(ptr %x) -; CHECK-NEXT: tail call void @use_pointer(ptr %x) -; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) [[NUW]], !clang.imprecise_release ![[RELEASE]] -; CHECK-NEXT: br label %if.end -; CHECK-NOT: @objc -; CHECK: {{^}}} define void @test56(ptr %x, i32 %n) { +; CHECK-LABEL: define void @test56( +; CHECK-SAME: ptr [[X:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[N]], 0 +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[IF_END:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: tail call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: tail call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[IF_END]] +; CHECK: [[IF_END]]: +; CHECK-NEXT: ret void +; entry: %0 = tail call ptr @llvm.objc.retain(ptr %x) nounwind %1 = tail call ptr @llvm.objc.retain(ptr %0) nounwind @@ -2179,18 +2764,19 @@ if.end: ; preds = %entry, %if.then ; unnecessary because the presence of the second one means that the first one ; won't be deleting the object. -; CHECK-LABEL: define void @test57( -; CHECK-NEXT: entry: -; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: call void @llvm.objc.release(ptr %x) [[NUW]] -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test57(ptr %x) nounwind { +; CHECK-LABEL: define void @test57( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.retain(ptr %x) nounwind @@ -2207,14 +2793,15 @@ entry: ; An adjacent retain+release pair is sufficient even if it will be ; removed itself. -; CHECK-LABEL: define void @test58( -; CHECK-NEXT: entry: -; CHECK-NEXT: @llvm.objc.retain -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test58(ptr %x) nounwind { +; CHECK-LABEL: define void @test58( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %x) nounwind call ptr @llvm.objc.retain(ptr %x) nounwind @@ -2228,15 +2815,16 @@ entry: ; Don't delete the second retain+release pair in an adjacent set. -; CHECK-LABEL: define void @test59( -; CHECK-NEXT: entry: -; CHECK-NEXT: %0 = tail call ptr @llvm.objc.retain(ptr %x) [[NUW]] -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: call void @use_pointer(ptr %x) -; CHECK-NEXT: call void @llvm.objc.release(ptr %x) [[NUW]] -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test59(ptr %x) nounwind { +; CHECK-LABEL: define void @test59( +; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: call void @use_pointer(ptr [[X]]) +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %a = call ptr @llvm.objc.retain(ptr %x) nounwind call void @llvm.objc.release(ptr %x) nounwind @@ -2255,11 +2843,16 @@ entry: ; We have a precise lifetime retain/release here. We can not remove them since ; @something is not constant. -; CHECK-LABEL: define void @test60a( -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void @llvm.objc.release -; CHECK: {{^}}} define void @test60a() { +; CHECK-LABEL: define void @test60a() { +; CHECK-NEXT: [[T:%.*]] = load ptr, ptr @constptr, align 8 +; CHECK-NEXT: [[S:%.*]] = load ptr, ptr @something, align 8 +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[S]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[T]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[S]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %t = load ptr, ptr @constptr %s = load ptr, ptr @something call ptr @llvm.objc.retain(ptr %s) @@ -2269,12 +2862,15 @@ define void @test60a() { ret void } -; CHECK-LABEL: define void @test60b( -; CHECK: call ptr @llvm.objc.retain -; CHECK-NOT: call ptr @llvm.objc.retain -; CHECK-NOT: call ptr @llvm.objc.release -; CHECK: {{^}}} define void @test60b() { +; CHECK-LABEL: define void @test60b() { +; CHECK-NEXT: [[T:%.*]] = load ptr, ptr @constptr, align 8 +; CHECK-NEXT: [[S:%.*]] = load ptr, ptr @something, align 8 +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[T]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[S]]) +; CHECK-NEXT: ret void +; %t = load ptr, ptr @constptr %s = load ptr, ptr @something call ptr @llvm.objc.retain(ptr %t) @@ -2285,10 +2881,13 @@ define void @test60b() { ret void } -; CHECK-LABEL: define void @test60c( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test60c() { +; CHECK-LABEL: define void @test60c() { +; CHECK-NEXT: [[S:%.*]] = load ptr, ptr @something, align 8 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[S]]) +; CHECK-NEXT: ret void +; %t = load ptr, ptr @constptr %s = load ptr, ptr @something call ptr @llvm.objc.retain(ptr %t) @@ -2298,10 +2897,13 @@ define void @test60c() { ret void } -; CHECK-LABEL: define void @test60d( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test60d() { +; CHECK-LABEL: define void @test60d() { +; CHECK-NEXT: [[S:%.*]] = load ptr, ptr @something, align 8 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[S]]) +; CHECK-NEXT: ret void +; %t = load ptr, ptr @constptr %s = load ptr, ptr @something call ptr @llvm.objc.retain(ptr %t) @@ -2311,10 +2913,13 @@ define void @test60d() { ret void } -; CHECK-LABEL: define void @test60e( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test60e() { +; CHECK-LABEL: define void @test60e() { +; CHECK-NEXT: [[S:%.*]] = load ptr, ptr @something, align 8 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[S]]) +; CHECK-NEXT: ret void +; %t = load ptr, ptr @constptr %s = load ptr, ptr @something call ptr @llvm.objc.retain(ptr %t) @@ -2327,10 +2932,13 @@ define void @test60e() { ; Constant pointers to objects don't need to be considered related to other ; pointers. -; CHECK-LABEL: define void @test61( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test61() { +; CHECK-LABEL: define void @test61() { +; CHECK-NEXT: [[T:%.*]] = load ptr, ptr @constptr, align 8 +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @use_pointer(ptr [[T]]) +; CHECK-NEXT: ret void +; %t = load ptr, ptr @constptr call ptr @llvm.objc.retain(ptr %t) call void @callee() @@ -2342,10 +2950,19 @@ define void @test61() { ; Delete a retain matched by releases when one is inside the loop and the ; other is outside the loop. -; CHECK-LABEL: define void @test62( -; CHECK-NOT: @llvm.objc. -; CHECK: {{^}}} define void @test62(ptr %x, ptr %p) nounwind { +; CHECK-LABEL: define void @test62( +; CHECK-SAME: ptr [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[Q:%.*]] = load i1, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[Q]], label %[[LOOP_MORE:.*]], label %[[EXIT:.*]] +; CHECK: [[LOOP_MORE]]: +; CHECK-NEXT: br label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; entry: br label %loop @@ -2366,13 +2983,21 @@ exit: ; Like test62 but with no release in exit. ; Don't delete anything! -; CHECK-LABEL: define void @test63( -; CHECK: loop: -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) -; CHECK: loop.more: -; CHECK: call void @llvm.objc.release(ptr %x) -; CHECK: {{^}}} define void @test63(ptr %x, ptr %p) nounwind { +; CHECK-LABEL: define void @test63( +; CHECK-SAME: ptr [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[Q:%.*]] = load i1, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[Q]], label %[[LOOP_MORE:.*]], label %[[EXIT:.*]] +; CHECK: [[LOOP_MORE]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; entry: br label %loop @@ -2392,13 +3017,21 @@ exit: ; Like test62 but with no release in loop.more. ; Don't delete anything! -; CHECK-LABEL: define void @test64( -; CHECK: loop: -; CHECK: tail call ptr @llvm.objc.retain(ptr %x) -; CHECK: exit: -; CHECK: call void @llvm.objc.release(ptr %x) -; CHECK: {{^}}} define void @test64(ptr %x, ptr %p) nounwind { +; CHECK-LABEL: define void @test64( +; CHECK-SAME: ptr [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[Q:%.*]] = load i1, ptr [[P]], align 1 +; CHECK-NEXT: br i1 [[Q]], label %[[LOOP_MORE:.*]], label %[[EXIT:.*]] +; CHECK: [[LOOP_MORE]]: +; CHECK-NEXT: br label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br label %loop @@ -2417,13 +3050,20 @@ exit: ; Move an autorelease past a phi with a null. -; CHECK-LABEL: define ptr @test65( -; CHECK: if.then: -; CHECK: call ptr @llvm.objc.autorelease( -; CHECK: return: -; CHECK-NOT: @llvm.objc.autorelease -; CHECK: {{^}}} define ptr @test65(i1 %x) { +; CHECK-LABEL: define ptr @test65( +; CHECK-SAME: i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[X]], label %[[RETURN:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: [[C:%.*]] = call ptr @returner() +; CHECK-NEXT: [[S:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[C]]) #[[ATTR0]] +; CHECK-NEXT: [[Q1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[S]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL:%.*]] = phi ptr [ [[S]], %[[IF_THEN]] ], [ null, %[[ENTRY]] ] +; CHECK-NEXT: ret ptr [[RETVAL]] +; entry: br i1 %x, label %return, label %if.then @@ -2440,13 +3080,22 @@ return: ; preds = %if.then, %entry ; Don't move an autorelease past an autorelease pool boundary. -; CHECK-LABEL: define ptr @test65b( -; CHECK: if.then: -; CHECK-NOT: @llvm.objc.autorelease -; CHECK: return: -; CHECK: call ptr @llvm.objc.autorelease( -; CHECK: {{^}}} define ptr @test65b(i1 %x) { +; CHECK-LABEL: define ptr @test65b( +; CHECK-SAME: i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[T:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]] +; CHECK-NEXT: br i1 [[X]], label %[[RETURN:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: [[C:%.*]] = call ptr @returner() +; CHECK-NEXT: [[S:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[C]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL:%.*]] = phi ptr [ [[S]], %[[IF_THEN]] ], [ null, %[[ENTRY]] ] +; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[T]]) #[[ATTR0]] +; CHECK-NEXT: [[Q:%.*]] = call ptr @llvm.objc.autorelease(ptr [[RETVAL]]) #[[ATTR0]] +; CHECK-NEXT: ret ptr [[RETVAL]] +; entry: %t = call ptr @llvm.objc.autoreleasePoolPush() br i1 %x, label %return, label %if.then @@ -2466,13 +3115,20 @@ return: ; preds = %if.then, %entry ; Don't move an autoreleaseReuturnValue, which would break ; the RV optimization. -; CHECK-LABEL: define ptr @test65c( -; CHECK: if.then: -; CHECK-NOT: @llvm.objc.autorelease -; CHECK: return: -; CHECK: call ptr @llvm.objc.autoreleaseReturnValue( -; CHECK: {{^}}} define ptr @test65c(i1 %x) { +; CHECK-LABEL: define ptr @test65c( +; CHECK-SAME: i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[X]], label %[[RETURN:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: [[C:%.*]] = call ptr @returner() +; CHECK-NEXT: [[S:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[C]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL:%.*]] = phi ptr [ [[S]], %[[IF_THEN]] ], [ null, %[[ENTRY]] ] +; CHECK-NEXT: [[Q:%.*]] = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr [[RETVAL]]) #[[ATTR0]] +; CHECK-NEXT: ret ptr [[RETVAL]] +; entry: br i1 %x, label %return, label %if.then @@ -2487,13 +3143,20 @@ return: ; preds = %if.then, %entry ret ptr %retval } -; CHECK-LABEL: define ptr @test65d( -; CHECK: if.then: -; CHECK-NOT: @llvm.objc.autorelease -; CHECK: return: -; CHECK: call ptr @llvm.objc.autoreleaseReturnValue( -; CHECK: {{^}}} define ptr @test65d(i1 %x) { +; CHECK-LABEL: define ptr @test65d( +; CHECK-SAME: i1 [[X:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[X]], label %[[RETURN:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: [[C:%.*]] = call ptr @returner() +; CHECK-NEXT: [[S:%.*]] = tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr [[C]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL:%.*]] = phi ptr [ [[S]], %[[IF_THEN]] ], [ null, %[[ENTRY]] ] +; CHECK-NEXT: [[Q:%.*]] = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr [[RETVAL]]) #[[ATTR0]] +; CHECK-NEXT: ret ptr [[RETVAL]] +; entry: br i1 %x, label %return, label %if.then @@ -2511,13 +3174,22 @@ return: ; preds = %if.then, %entry ; An llvm.objc.retain can serve as a may-use for a different pointer. ; rdar://11931823 -; CHECK-LABEL: define void @test66a( -; CHECK: tail call ptr @llvm.objc.retain(ptr %cond) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %call) [[NUW]] -; CHECK: tail call ptr @llvm.objc.retain(ptr %tmp8) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %cond) [[NUW]] -; CHECK: {{^}}} define void @test66a(ptr %tmp5, ptr %bar, i1 %tobool, i1 %tobool1, ptr %call) { +; CHECK-LABEL: define void @test66a( +; CHECK-SAME: ptr [[TMP5:%.*]], ptr [[BAR:%.*]], i1 [[TOBOOL:%.*]], i1 [[TOBOOL1:%.*]], ptr [[CALL:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[TMP5]], %[[COND_TRUE]] ], [ [[CALL]], %[[ENTRY]] ] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[TOBOOL1]], ptr [[COND]], ptr [[BAR]] +; CHECK-NEXT: [[TMP9:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP8]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %tobool, label %cond.true, label %cond.end @@ -2534,13 +3206,22 @@ cond.end: ; preds = %cond.true, %entry ret void } -; CHECK-LABEL: define void @test66b( -; CHECK: tail call ptr @llvm.objc.retain(ptr %cond) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %call) [[NUW]] -; CHECK: tail call ptr @llvm.objc.retain(ptr %tmp8) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %cond) [[NUW]] -; CHECK: {{^}}} define void @test66b(ptr %tmp5, ptr %bar, i1 %tobool, i1 %tobool1, ptr %call) { +; CHECK-LABEL: define void @test66b( +; CHECK-SAME: ptr [[TMP5:%.*]], ptr [[BAR:%.*]], i1 [[TOBOOL:%.*]], i1 [[TOBOOL1:%.*]], ptr [[CALL:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[TMP5]], %[[COND_TRUE]] ], [ [[CALL]], %[[ENTRY]] ] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[TOBOOL1]], ptr [[COND]], ptr [[BAR]] +; CHECK-NEXT: [[TMP9:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP8]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %tobool, label %cond.true, label %cond.end @@ -2557,13 +3238,22 @@ cond.end: ; preds = %cond.true, %entry ret void } -; CHECK-LABEL: define void @test66c( -; CHECK: tail call ptr @llvm.objc.retain(ptr %cond) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %call) [[NUW]] -; CHECK: tail call ptr @llvm.objc.retain(ptr %tmp8) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %cond) [[NUW]] -; CHECK: {{^}}} define void @test66c(ptr %tmp5, ptr %bar, i1 %tobool, i1 %tobool1, ptr %call) { +; CHECK-LABEL: define void @test66c( +; CHECK-SAME: ptr [[TMP5:%.*]], ptr [[BAR:%.*]], i1 [[TOBOOL:%.*]], i1 [[TOBOOL1:%.*]], ptr [[CALL:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[TMP5]], %[[COND_TRUE]] ], [ [[CALL]], %[[ENTRY]] ] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[TOBOOL1]], ptr [[COND]], ptr [[BAR]] +; CHECK-NEXT: [[TMP9:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP8]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %tobool, label %cond.true, label %cond.end @@ -2580,13 +3270,22 @@ cond.end: ; preds = %cond.true, %entry ret void } -; CHECK-LABEL: define void @test66d( -; CHECK: tail call ptr @llvm.objc.retain(ptr %cond) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %call) [[NUW]] -; CHECK: tail call ptr @llvm.objc.retain(ptr %tmp8) [[NUW]] -; CHECK: tail call void @llvm.objc.release(ptr %cond) [[NUW]] -; CHECK: {{^}}} define void @test66d(ptr %tmp5, ptr %bar, i1 %tobool, i1 %tobool1, ptr %call) { +; CHECK-LABEL: define void @test66d( +; CHECK-SAME: ptr [[TMP5:%.*]], ptr [[BAR:%.*]], i1 [[TOBOOL:%.*]], i1 [[TOBOOL1:%.*]], ptr [[CALL:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[TMP5]], %[[COND_TRUE]] ], [ [[CALL]], %[[ENTRY]] ] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[COND]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[TOBOOL1]], ptr [[COND]], ptr [[BAR]] +; CHECK-NEXT: [[TMP9:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP8]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: br i1 %tobool, label %cond.true, label %cond.end @@ -2612,11 +3311,6 @@ declare i32 @puts(ptr captures(none)) nounwind @str = internal constant [16 x i8] c"-[ Top0 _getX ]\00" ; FIXME: Should be able to eliminate the retain and release -; CHECK-LABEL: define { <2 x float>, <2 x float> } @"\01-[A z]"(ptr %self, ptr captures(none) %_cmd) -; CHECK: tail call ptr @llvm.objc.retain(ptr %self) -; CHECK-NEXT: %call = tail call i32 (ptr, ...) @printf( -; CHECK: tail call void @llvm.objc.release(ptr %self) -; CHECK: {{^}}} define { <2 x float>, <2 x float> } @"\01-[A z]"(ptr %self, ptr captures(none) %_cmd) nounwind { invoke.cont: %i1 = tail call ptr @llvm.objc.retain(ptr %self) nounwind @@ -2654,10 +3348,6 @@ invoke.cont: } ; FIXME: Should be able to eliminate the retain and release -; CHECK-LABEL: @"\01-[Top0 _getX]"(ptr %self, ptr captures(none) %_cmd) -; CHECK: tail call ptr @llvm.objc.retain(ptr %self) -; CHECK: %puts = tail call i32 @puts -; CHECK: tail call void @llvm.objc.release(ptr %self) define i32 @"\01-[Top0 _getX]"(ptr %self, ptr captures(none) %_cmd) nounwind { invoke.cont: %i1 = tail call ptr @llvm.objc.retain(ptr %self) nounwind @@ -2672,14 +3362,24 @@ invoke.cont: ; A simple loop. Eliminate the retain and release inside of it! -; CHECK: define void @loop(ptr %x, i64 %n) { -; CHECK: for.body: -; CHECK-NOT: @llvm.objc. -; CHECK: @objc_msgSend -; CHECK-NOT: @llvm.objc. -; CHECK: for.end: -; CHECK: {{^}}} define void @loop(ptr %x, i64 %n) { +; CHECK-LABEL: define void @loop( +; CHECK-SAME: ptr [[X:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[CMP9:%.*]] = icmp sgt i64 [[N]], 0 +; CHECK-NEXT: br i1 [[CMP9]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]] +; CHECK: [[FOR_BODY]]: +; CHECK-NEXT: [[I_010:%.*]] = phi i64 [ [[INC:%.*]], %[[FOR_BODY]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL:%.*]] = tail call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[X]], ptr [[TMP5]]) +; CHECK-NEXT: [[INC]] = add nsw i64 [[I_010]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[INC]], [[N]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FOR_END]], label %[[FOR_BODY]] +; CHECK: [[FOR_END]]: +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; entry: %0 = tail call ptr @llvm.objc.retain(ptr %x) nounwind %cmp9 = icmp sgt i64 %n, 0 @@ -2702,9 +3402,6 @@ for.end: ; preds = %for.body, %entry ; ObjCARCOpt can delete the retain,release on self. -; CHECK: define void @TextEditTest(ptr %self, ptr %pboard) { -; CHECK-NOT: call ptr @llvm.objc.retain(ptr %tmp7) -; CHECK: {{^}}} %0 = type { ptr, ptr } %1 = type opaque @@ -2759,6 +3456,133 @@ for.end: ; preds = %for.body, %entry declare ptr @truncatedString(ptr, i64) define void @TextEditTest(ptr %self, ptr %pboard) { +; CHECK-LABEL: define void @TextEditTest( +; CHECK-SAME: ptr [[SELF:%.*]], ptr [[PBOARD:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[ERR:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: store ptr null, ptr [[ERR]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_17", align 8 +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @kUTTypePlainText, align 8 +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_19", align 8 +; CHECK-NEXT: [[CALL5:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP1]], ptr [[TMP3]], ptr [[TMP2]]) +; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_21", align 8 +; CHECK-NEXT: [[CALL76:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[PBOARD]], ptr [[TMP5]], ptr [[CALL5]]) +; CHECK-NEXT: [[TMP9:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL76]]) #[[ATTR0]] +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[TMP9]], null +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[END:.*]], label %[[LAND_LHS_TRUE:.*]] +; CHECK: [[LAND_LHS_TRUE]]: +; CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_23", align 8 +; CHECK-NEXT: [[CALL137:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[PBOARD]], ptr [[TMP11]], ptr [[TMP9]]) +; CHECK-NEXT: [[TMP10:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL137]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP12:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL137]]) #[[ATTR0]] +; CHECK-NEXT: [[TOBOOL16:%.*]] = icmp eq ptr [[CALL137]], null +; CHECK-NEXT: br i1 [[TOBOOL16]], label %[[END]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_25", align 8 +; CHECK-NEXT: [[CALL21:%.*]] = call signext i8 @objc_msgSend(ptr [[CALL137]], ptr [[TMP19]]) +; CHECK-NEXT: [[TOBOOL22:%.*]] = icmp eq i8 [[CALL21]], 0 +; CHECK-NEXT: br i1 [[TOBOOL22]], label %[[IF_THEN44:.*]], label %[[LAND_LHS_TRUE23:.*]] +; CHECK: [[LAND_LHS_TRUE23]]: +; CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_26", align 8 +; CHECK-NEXT: [[TMP26:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_28", align 8 +; CHECK-NEXT: [[CALL2822:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP24]], ptr [[TMP26]], ptr [[CALL137]]) +; CHECK-NEXT: [[TMP14:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL2822]]) #[[ATTR0]] +; CHECK-NEXT: [[TOBOOL30:%.*]] = icmp eq ptr [[CALL2822]], null +; CHECK-NEXT: br i1 [[TOBOOL30]], label %[[IF_THEN44]], label %[[IF_END:.*]] +; CHECK: [[IF_END]]: +; CHECK-NEXT: [[TMP32:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_29", align 8 +; CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_31", align 8 +; CHECK-NEXT: [[CALL35:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP32]], ptr [[TMP33]]) +; CHECK-NEXT: [[TMP37:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_33", align 8 +; CHECK-NEXT: [[CALL3923:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[CALL35]], ptr [[TMP37]], ptr [[CALL2822]], i32 signext 1, ptr [[ERR]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[CALL3923]], null +; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN44]], label %[[END]] +; CHECK: [[IF_THEN44]]: +; CHECK-NEXT: [[URL_025:%.*]] = phi ptr [ [[CALL2822]], %[[IF_END]] ], [ [[CALL2822]], %[[LAND_LHS_TRUE23]] ], [ null, %[[IF_THEN]] ] +; CHECK-NEXT: [[TMP49:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_35", align 8 +; CHECK-NEXT: [[CALL51:%.*]] = call [[STRUCT__NSRANGE:%.*]] @[[OBJC_MSGSEND:[a-zA-Z0-9_$\"\\.-]*[a-zA-Z_$\"\\.-][a-zA-Z0-9_$\"\\.-]*]](ptr [[CALL137]], ptr [[TMP49]], i64 0, i64 0) +; CHECK-NEXT: [[CALL513:%.*]] = extractvalue [[STRUCT__NSRANGE]] [[CALL51]], 0 +; CHECK-NEXT: [[CALL514:%.*]] = extractvalue [[STRUCT__NSRANGE]] [[CALL51]], 1 +; CHECK-NEXT: [[TMP52:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_37", align 8 +; CHECK-NEXT: [[CALL548:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[CALL137]], ptr [[TMP52]], i64 [[CALL513]], i64 [[CALL514]]) +; CHECK-NEXT: [[TMP55:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_38", align 8 +; CHECK-NEXT: [[TMP56:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_40", align 8 +; CHECK-NEXT: [[CALL58:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP55]], ptr [[TMP56]]) +; CHECK-NEXT: [[TMP59:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_42", align 8 +; CHECK-NEXT: [[CALL6110:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[CALL548]], ptr [[TMP59]], ptr [[CALL58]]) +; CHECK-NEXT: [[TMP15:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL6110]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL137]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP64:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_46", align 8 +; CHECK-NEXT: [[CALL66:%.*]] = call signext i8 @objc_msgSend(ptr [[CALL6110]], ptr [[TMP64]], ptr @_unnamed_cfstring_44) +; CHECK-NEXT: [[TOBOOL67:%.*]] = icmp eq i8 [[CALL66]], 0 +; CHECK-NEXT: br i1 [[TOBOOL67]], label %[[IF_END74:.*]], label %[[IF_THEN68:.*]] +; CHECK: [[IF_THEN68]]: +; CHECK-NEXT: [[TMP70:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_48", align 8 +; CHECK-NEXT: [[CALL7220:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[CALL6110]], ptr [[TMP70]]) +; CHECK-NEXT: [[TMP16:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL7220]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL6110]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[IF_END74]] +; CHECK: [[IF_END74]]: +; CHECK-NEXT: [[FILENAME_0_IN:%.*]] = phi ptr [ [[CALL7220]], %[[IF_THEN68]] ], [ [[CALL6110]], %[[IF_THEN44]] ] +; CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr @"\01l_objc_msgSend_fixup_isEqual_", align 16 +; CHECK-NEXT: [[CALL78:%.*]] = call signext i8 (ptr, ptr, ptr, ...) [[TMP17]](ptr [[CALL137]], ptr @"\01l_objc_msgSend_fixup_isEqual_", ptr [[FILENAME_0_IN]]) +; CHECK-NEXT: [[TOBOOL79:%.*]] = icmp eq i8 [[CALL78]], 0 +; CHECK-NEXT: br i1 [[TOBOOL79]], label %[[LAND_LHS_TRUE80:.*]], label %[[IF_THEN109:.*]] +; CHECK: [[LAND_LHS_TRUE80]]: +; CHECK-NEXT: [[TMP82:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_25", align 8 +; CHECK-NEXT: [[CALL84:%.*]] = call signext i8 @objc_msgSend(ptr [[FILENAME_0_IN]], ptr [[TMP82]]) +; CHECK-NEXT: [[TOBOOL86:%.*]] = icmp eq i8 [[CALL84]], 0 +; CHECK-NEXT: br i1 [[TOBOOL86]], label %[[IF_THEN109]], label %[[IF_END106:.*]] +; CHECK: [[IF_END106]]: +; CHECK-NEXT: [[TMP88:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_26", align 8 +; CHECK-NEXT: [[TMP90:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_28", align 8 +; CHECK-NEXT: [[CALL9218:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP88]], ptr [[TMP90]], ptr [[FILENAME_0_IN]]) +; CHECK-NEXT: [[TMP21:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL9218]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[URL_025]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP94:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_29", align 8 +; CHECK-NEXT: [[TMP95:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_31", align 8 +; CHECK-NEXT: [[CALL97:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP94]], ptr [[TMP95]]) +; CHECK-NEXT: [[TMP99:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_33", align 8 +; CHECK-NEXT: [[CALL10119:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[CALL97]], ptr [[TMP99]], ptr [[CALL9218]], i32 signext 1, ptr [[ERR]]) +; CHECK-NEXT: [[PHITMP:%.*]] = icmp eq ptr [[CALL10119]], null +; CHECK-NEXT: br i1 [[PHITMP]], label %[[IF_THEN109]], label %[[END]] +; CHECK: [[IF_THEN109]]: +; CHECK-NEXT: [[URL_129:%.*]] = phi ptr [ [[CALL9218]], %[[IF_END106]] ], [ [[URL_025]], %[[IF_END74]] ], [ [[URL_025]], %[[LAND_LHS_TRUE80]] ] +; CHECK-NEXT: [[TMP110:%.*]] = load ptr, ptr [[ERR]], align 8 +; CHECK-NEXT: [[TOBOOL111:%.*]] = icmp eq ptr [[TMP110]], null +; CHECK-NEXT: br i1 [[TOBOOL111]], label %[[IF_THEN112:.*]], label %[[IF_END125:.*]] +; CHECK: [[IF_THEN112]]: +; CHECK-NEXT: [[TMP113:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_50", align 8 +; CHECK-NEXT: [[TMP114:%.*]] = load ptr, ptr @NSCocoaErrorDomain, align 8 +; CHECK-NEXT: [[TMP115:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_51", align 8 +; CHECK-NEXT: [[CALL117:%.*]] = call ptr @truncatedString(ptr [[FILENAME_0_IN]], i64 1034) +; CHECK-NEXT: [[TMP118:%.*]] = load ptr, ptr @NSFilePathErrorKey, align 8 +; CHECK-NEXT: [[TMP119:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_53", align 8 +; CHECK-NEXT: [[CALL12113:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP115]], ptr [[TMP119]], ptr [[CALL117]], ptr [[TMP118]], ptr null) +; CHECK-NEXT: [[TMP122:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_55", align 8 +; CHECK-NEXT: [[CALL12414:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP113]], ptr [[TMP122]], ptr [[TMP114]], i64 258, ptr [[CALL12113]]) +; CHECK-NEXT: [[TMP23:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL12414]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP25:%.*]] = call ptr @llvm.objc.autorelease(ptr [[TMP23]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[TMP25]], ptr [[ERR]], align 8 +; CHECK-NEXT: br label %[[IF_END125]] +; CHECK: [[IF_END125]]: +; CHECK-NEXT: [[TMP127:%.*]] = phi ptr [ [[TMP110]], %[[IF_THEN109]] ], [ [[TMP25]], %[[IF_THEN112]] ] +; CHECK-NEXT: [[TMP126:%.*]] = load ptr, ptr @"\01L_OBJC_CLASSLIST_REFERENCES_$_56", align 8 +; CHECK-NEXT: [[TMP128:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_58", align 8 +; CHECK-NEXT: [[CALL13015:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[TMP126]], ptr [[TMP128]], ptr [[TMP127]]) +; CHECK-NEXT: [[TMP131:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_60", align 8 +; CHECK-NEXT: [[CALL13317:%.*]] = call ptr (ptr, ptr, ...) @objc_msgSend(ptr [[CALL13015]], ptr [[TMP131]]) +; CHECK-NEXT: br label %[[END]] +; CHECK: [[END]]: +; CHECK-NEXT: [[FILENAME_2:%.*]] = phi ptr [ [[FILENAME_0_IN]], %[[IF_END106]] ], [ [[FILENAME_0_IN]], %[[IF_END125]] ], [ [[CALL137]], %[[LAND_LHS_TRUE]] ], [ null, %[[ENTRY]] ], [ [[CALL137]], %[[IF_END]] ] +; CHECK-NEXT: [[ORIGFILENAME_0:%.*]] = phi ptr [ [[CALL137]], %[[IF_END106]] ], [ [[CALL137]], %[[IF_END125]] ], [ [[CALL137]], %[[LAND_LHS_TRUE]] ], [ null, %[[ENTRY]] ], [ [[CALL137]], %[[IF_END]] ] +; CHECK-NEXT: [[URL_2:%.*]] = phi ptr [ [[CALL9218]], %[[IF_END106]] ], [ [[URL_129]], %[[IF_END125]] ], [ null, %[[LAND_LHS_TRUE]] ], [ null, %[[ENTRY]] ], [ [[CALL2822]], %[[IF_END]] ] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP9]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[URL_2]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[ORIGFILENAME_0]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[FILENAME_2]]) #[[ATTR0]], !clang.imprecise_release [[META3]] +; CHECK-NEXT: ret void +; entry: %err = alloca ptr, align 8 %tmp8 = call ptr @llvm.objc.retain(ptr %self) nounwind @@ -2911,12 +3735,13 @@ declare i32 @llvm.objc.sync.exit(ptr) ; Make sure that we understand that objc_sync_{enter,exit} are IC_User not ; IC_Call/IC_CallOrUser. -; CHECK-LABEL: define void @test67( -; CHECK-NEXT: call i32 @llvm.objc.sync.enter(ptr %x) -; CHECK-NEXT: call i32 @llvm.objc.sync.exit(ptr %x) -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test67(ptr %x) { +; CHECK-LABEL: define void @test67( +; CHECK-SAME: ptr [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.objc.sync.enter(ptr [[X]]) +; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.objc.sync.exit(ptr [[X]]) +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %x) call i32 @llvm.objc.sync.enter(ptr %x) call i32 @llvm.objc.sync.exit(ptr %x) @@ -2924,13 +3749,13 @@ define void @test67(ptr %x) { ret void } -; CHECK-LABEL: define void @test68( -; CHECK-NOT: call -; CHECK: call void @callee2( -; CHECK-NOT: call -; CHECK: ret void define void @test68(ptr %a, ptr %b) { +; CHECK-LABEL: define void @test68( +; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) { +; CHECK-NEXT: call void @callee2(ptr [[A]], ptr [[B]]) +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %a) call ptr @llvm.objc.retain(ptr %b) call void @callee2(ptr %a, ptr %b) @@ -2946,11 +3771,12 @@ define void @test68(ptr %a, ptr %b) { !1 = !{i32 1, !"Debug Info Version", i32 3} !2 = distinct !DISubprogram(unit: !3) !3 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang", - file: !4, - isOptimized: true, flags: "-O2", - splitDebugFilename: "abc.debug", emissionKind: 2) + file: !4, + isOptimized: true, flags: "-O2", + splitDebugFilename: "abc.debug", emissionKind: 2) !4 = !DIFile(filename: "path/to/file", directory: "/path/to/dir") !5 = !{i32 2, !"Debug Info Version", i32 3} -; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: ![[RELEASE]] = !{} +;. +; CHECK: [[META3]] = !{} +;. diff --git a/llvm/test/Transforms/ObjCARC/clang-arc-use-barrier.ll b/llvm/test/Transforms/ObjCARC/clang-arc-use-barrier.ll index 2581917a3896b..2bfdd49e35157 100644 --- a/llvm/test/Transforms/ObjCARC/clang-arc-use-barrier.ll +++ b/llvm/test/Transforms/ObjCARC/clang-arc-use-barrier.ll @@ -1,15 +1,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=objc-arc -S %s | FileCheck %s %0 = type opaque ; Make sure ARC optimizer doesn't sink @obj_retain past @llvm.objc.clang.arc.use. -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void (...) @llvm.objc.clang.arc.use( -; CHECK: call ptr @llvm.objc.retain -; CHECK: call void (...) @llvm.objc.clang.arc.use( define void @runTest() local_unnamed_addr { +; CHECK-LABEL: define void @runTest() local_unnamed_addr { +; CHECK-NEXT: [[TMP1:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TMP2:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TMP3:%.*]] = tail call ptr @foo0() +; CHECK-NEXT: [[TMP4:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[TMP3]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP1]], align 8 +; CHECK-NEXT: call void @foo1(ptr nonnull [[TMP1]]) +; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP1]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP5]]) #[[ATTR0]] +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[TMP3]]) +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP3]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP2]], align 8 +; CHECK-NEXT: call void @foo1(ptr nonnull [[TMP2]]) +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP2]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP6]]) #[[ATTR0]] +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[TMP5]]) +; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP2]], align 8 +; CHECK-NEXT: [[TMP7:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP5]]) #[[ATTR0]] +; CHECK-NEXT: call void @foo2(ptr [[TMP6]]) +; CHECK-NEXT: [[TMP8:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP6]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %1 = alloca ptr, align 8 %2 = alloca ptr, align 8 %3 = tail call ptr @foo0() diff --git a/llvm/test/Transforms/ObjCARC/code-motion.ll b/llvm/test/Transforms/ObjCARC/code-motion.ll index 499ee77bc6541..849d4ca163771 100644 --- a/llvm/test/Transforms/ObjCARC/code-motion.ll +++ b/llvm/test/Transforms/ObjCARC/code-motion.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=objc-arc -S < %s | FileCheck %s declare void @alterRefCount() @@ -9,10 +10,18 @@ declare void @readOnlyFunc(ptr, ptr) ; Check that ARC optimizer doesn't reverse the order of the retain call and the ; release call when there are debug instructions. -; CHECK: call ptr @llvm.objc.retain(ptr %x) -; CHECK: call void @llvm.objc.release(ptr %x) define i32 @test(ptr %x, ptr %y, i8 %z, i32 %i) { +; CHECK-LABEL: define i32 @test( +; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]], i8 [[Z:%.*]], i32 [[I:%.*]]) { +; CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 +; CHECK-NEXT: store i8 [[Z]], ptr [[X]], align 1 +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[X]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: call void @alterRefCount() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret i32 [[I]] +; %i.addr = alloca i32, align 4 store i32 %i, ptr %i.addr, align 4 %v1 = tail call ptr @llvm.objc.retain(ptr %x) @@ -26,11 +35,16 @@ define i32 @test(ptr %x, ptr %y, i8 %z, i32 %i) { ; ARC optimizer shouldn't move the release call, which is a precise release call ; past the call to @alterRefCount. -; CHECK-LABEL: define void @test2( -; CHECK: call void @alterRefCount( -; CHECK: call void @llvm.objc.release( define void @test2() { +; CHECK-LABEL: define void @test2() { +; CHECK-NEXT: [[V0:%.*]] = load ptr, ptr @g0, align 8 +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[V0]]) #[[ATTR0]] +; CHECK-NEXT: tail call void @use(ptr [[V0]]) +; CHECK-NEXT: tail call void @alterRefCount() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[V0]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; %v0 = load ptr, ptr @g0, align 8 %v1 = tail call ptr @llvm.objc.retain(ptr %v0) tail call void @use(ptr %v0) @@ -51,20 +65,21 @@ define void @test2() { ; Ideally, the retain/release pairs in BB if.then should be removed. define void @test3(ptr %obj, i1 %cond) { -; CHECK-LABEL: @test3( -; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ:%.*]]) -; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] -; CHECK: if.then: -; CHECK-NEXT: call void @readOnlyFunc(ptr [[OBJ]], ptr null) +; CHECK-LABEL: define void @test3( +; CHECK-SAME: ptr [[OBJ:%.*]], i1 [[COND:%.*]]) { +; CHECK-NEXT: [[V0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: call void @readOnlyFunc(ptr [[OBJ]], ptr null) #[[ATTR1:[0-9]+]] ; CHECK-NEXT: [[TMP1:%.*]] = add i32 1, 2 ; CHECK-NEXT: call void @alterRefCount() -; CHECK-NEXT: br label [[JOIN:%.*]] -; CHECK: if.else: +; CHECK-NEXT: br label %[[JOIN:.*]] +; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: call void @alterRefCount() ; CHECK-NEXT: call void @use(ptr [[OBJ]]) -; CHECK-NEXT: br label [[JOIN]] -; CHECK: join: -; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) {{.*}}, !clang.imprecise_release !2 +; CHECK-NEXT: br label %[[JOIN]] +; CHECK: [[JOIN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META2:![0-9]+]] ; CHECK-NEXT: ret void ; %v0 = call ptr @llvm.objc.retain(ptr %obj) @@ -87,23 +102,24 @@ join: } define void @test4(ptr %obj0, ptr %obj1, i1 %cond) { -; CHECK-LABEL: @test4( -; CHECK-NEXT: [[TMP3:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ0:%.*]]) -; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ1:%.*]]) -; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] -; CHECK: if.then: -; CHECK-NEXT: call void @readOnlyFunc(ptr [[OBJ0]], ptr [[OBJ1]]) +; CHECK-LABEL: define void @test4( +; CHECK-SAME: ptr [[OBJ0:%.*]], ptr [[OBJ1:%.*]], i1 [[COND:%.*]]) { +; CHECK-NEXT: [[V0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ0]]) #[[ATTR0]] +; CHECK-NEXT: [[V1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ1]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: call void @readOnlyFunc(ptr [[OBJ0]], ptr [[OBJ1]]) #[[ATTR1]] ; CHECK-NEXT: [[TMP1:%.*]] = add i32 1, 2 ; CHECK-NEXT: call void @alterRefCount() -; CHECK-NEXT: br label [[JOIN:%.*]] -; CHECK: if.else: +; CHECK-NEXT: br label %[[JOIN:.*]] +; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: call void @alterRefCount() ; CHECK-NEXT: call void @use(ptr [[OBJ0]]) ; CHECK-NEXT: call void @use(ptr [[OBJ1]]) -; CHECK-NEXT: br label [[JOIN]] -; CHECK: join: -; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ0]]) {{.*}}, !clang.imprecise_release !2 -; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) {{.*}}, !clang.imprecise_release !2 +; CHECK-NEXT: br label %[[JOIN]] +; CHECK: [[JOIN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ0]]) #[[ATTR0]], !clang.imprecise_release [[META2]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META2]] ; CHECK-NEXT: ret void ; %v0 = call ptr @llvm.objc.retain(ptr %obj0) @@ -132,26 +148,27 @@ join: ; eliminated are in different blocks (bb1 and if.then). define void @test5(ptr %obj, i1 %cond0, i1 %cond1) { -; CHECK-LABEL: @test5( -; CHECK-NEXT: [[V0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ:%.*]]) -; CHECK-NEXT: br i1 [[COND0:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] -; CHECK: if.then: -; CHECK-NEXT: call void @readOnlyFunc(ptr [[OBJ]], ptr null) -; CHECK-NEXT: br i1 [[COND1:%.*]], label [[IF_THEN2:%.*]], label [[IF_ELSE2:%.*]] -; CHECK: if.then2: -; CHECK-NEXT: br label [[BB1:%.*]] -; CHECK: if.else2: -; CHECK-NEXT: br label [[BB1]] -; CHECK: bb1: +; CHECK-LABEL: define void @test5( +; CHECK-SAME: ptr [[OBJ:%.*]], i1 [[COND0:%.*]], i1 [[COND1:%.*]]) { +; CHECK-NEXT: [[V0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[OBJ]]) #[[ATTR0]] +; CHECK-NEXT: br i1 [[COND0]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: call void @readOnlyFunc(ptr [[OBJ]], ptr null) #[[ATTR1]] +; CHECK-NEXT: br i1 [[COND1]], label %[[IF_THEN2:.*]], label %[[IF_ELSE2:.*]] +; CHECK: [[IF_THEN2]]: +; CHECK-NEXT: br label %[[BB1:.*]] +; CHECK: [[IF_ELSE2]]: +; CHECK-NEXT: br label %[[BB1]] +; CHECK: [[BB1]]: ; CHECK-NEXT: [[TMP1:%.*]] = add i32 1, 2 ; CHECK-NEXT: call void @alterRefCount() -; CHECK-NEXT: br label [[JOIN:%.*]] -; CHECK: if.else: +; CHECK-NEXT: br label %[[JOIN:.*]] +; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: call void @alterRefCount() ; CHECK-NEXT: call void @use(ptr [[OBJ]]) -; CHECK-NEXT: br label [[JOIN]] -; CHECK: join: -; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) +; CHECK-NEXT: br label %[[JOIN]] +; CHECK: [[JOIN]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META2]] ; CHECK-NEXT: ret void ; %v0 = call ptr @llvm.objc.retain(ptr %obj) @@ -201,3 +218,6 @@ attributes #0 = { readonly } !8 = distinct !DICompileUnit(language: DW_LANG_ObjC, file: !4, isOptimized: false, runtimeVersion: 2, emissionKind: FullDebug, enums: !9, nameTableKind: None) !9 = !{} !10 = !DILocation(line: 1, column: 14, scope: !3) +;. +; CHECK: [[META2]] = !{} +;. diff --git a/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll b/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll index 9f047c29bc159..78e75da5837b2 100644 --- a/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll +++ b/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll @@ -1,14 +1,29 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -mtriple=x86_64-windows-msvc -passes=objc-arc -S < %s | FileCheck %s -; Check that funclet tokens are preserved -; -; CHECK-LABEL: catch: -; CHECK: %1 = catchpad within %0 -; CHECK: %2 = tail call ptr @llvm.objc.retain(ptr %exn) #0 [ "funclet"(token %1) ] -; CHECK: call void @llvm.objc.release(ptr %exn) #0 [ "funclet"(token %1) ] -; CHECK: catchret from %1 to label %eh.cont +; Check that funclet tokens are preserved, and that the optimization correctly +; reorders retain and release calls while preserving the funclet operand. define void @try_catch_with_objc_intrinsic() personality ptr @__CxxFrameHandler3 { +; CHECK-LABEL: @try_catch_with_objc_intrinsic( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: invoke void @may_throw(ptr null) +; CHECK-NEXT: to label %eh.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK-NEXT: [[TMP0:%.*]] = catchswitch within none [label %catch] unwind to caller +; CHECK: eh.cont: +; CHECK-NEXT: ret void +; CHECK: catch: +; CHECK-NEXT: [[TMP1:%.*]] = catchpad within [[TMP0]] [ptr null, i32 0, ptr [[EXN_SLOT]]] +; CHECK-NEXT: br label %if.then +; CHECK: if.then: +; CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr null, align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[EXN]]) #[[ATTR0:[0-9]+]] [ "funclet"(token [[TMP1]]) ] +; CHECK-NEXT: call void @may_throw(ptr [[EXN]]) +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[EXN]]) #[[ATTR0]] [ "funclet"(token [[TMP1]]) ] +; CHECK-NEXT: catchret from [[TMP1]] to label %eh.cont +; entry: %exn.slot = alloca ptr, align 8 invoke void @may_throw(ptr null) to label %eh.cont unwind label %catch.dispatch diff --git a/llvm/test/Transforms/ObjCARC/intrinsic-use.ll b/llvm/test/Transforms/ObjCARC/intrinsic-use.ll index 884e2ebea7350..404a7877d0896 100644 --- a/llvm/test/Transforms/ObjCARC/intrinsic-use.ll +++ b/llvm/test/Transforms/ObjCARC/intrinsic-use.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=objc-arc -S < %s | FileCheck %s target datalayout = "e-p:64:64:64" @@ -16,29 +17,33 @@ declare void @can_release(ptr) ; Ensure that we honor clang.arc.use as a use and don't miscompile ; the reduced test case from . ; -; CHECK-LABEL: define void @test0( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: store ptr %y, ptr %temp0 -; CHECK-NEXT: @llvm.objc.retain(ptr %y) -; CHECK-NEXT: call void @test0_helper -; CHECK-NEXT: [[VAL1:%.*]] = load ptr, ptr %temp0 -; CHECK-NEXT: @llvm.objc.retain(ptr [[VAL1]]) -; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr %y) -; CHECK-NEXT: @llvm.objc.release(ptr %y) -; CHECK-NEXT: store ptr [[VAL1]], ptr %temp1 -; CHECK-NEXT: call void @test0_helper -; CHECK-NEXT: [[VAL2:%.*]] = load ptr, ptr %temp1 -; CHECK-NEXT: @llvm.objc.retain(ptr [[VAL2]]) -; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[VAL1]]) -; CHECK-NEXT: @llvm.objc.release(ptr [[VAL1]]) -; CHECK-NEXT: @llvm.objc.autorelease(ptr %x) -; CHECK-NEXT: store ptr %x, ptr %out -; CHECK-NEXT: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: @llvm.objc.release(ptr [[VAL2]]) -; CHECK-NEXT: @llvm.objc.release(ptr %x) -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test0(ptr %out, ptr %x, ptr %y) { +; CHECK-LABEL: define void @test0( +; CHECK-SAME: ptr [[OUT:%.*]], ptr [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TEMP0:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TEMP1:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: store ptr [[Y]], ptr [[TEMP0]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: call void @test0_helper(ptr [[X]], ptr [[TEMP0]]) +; CHECK-NEXT: [[VAL1:%.*]] = load ptr, ptr [[TEMP0]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[VAL1]]) #[[ATTR0]] +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[VAL1]], ptr [[TEMP1]], align 8 +; CHECK-NEXT: call void @test0_helper(ptr [[X]], ptr [[TEMP1]]) +; CHECK-NEXT: [[VAL2:%.*]] = load ptr, ptr [[TEMP1]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[VAL2]]) #[[ATTR0]] +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[VAL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[VAL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP3:%.*]] = call ptr @llvm.objc.autorelease(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[OUT]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP4:%.*]] = tail call ptr @llvm.objc.retain(ptr [[VAL2]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP5:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %temp0 = alloca ptr, align 8 %temp1 = alloca ptr, align 8 @@ -64,27 +69,31 @@ entry: ret void } -; CHECK-LABEL: define void @test0a( -; CHECK: @llvm.objc.retain(ptr %x) -; CHECK-NEXT: store ptr %y, ptr %temp0 -; CHECK-NEXT: @llvm.objc.retain(ptr %y) -; CHECK-NEXT: call void @test0_helper -; CHECK-NEXT: [[VAL1:%.*]] = load ptr, ptr %temp0 -; CHECK-NEXT: @llvm.objc.retain(ptr [[VAL1]]) -; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr %y) -; CHECK-NEXT: @llvm.objc.release(ptr %y) -; CHECK-NEXT: store ptr [[VAL1]], ptr %temp1 -; CHECK-NEXT: call void @test0_helper -; CHECK-NEXT: [[VAL2:%.*]] = load ptr, ptr %temp1 -; CHECK-NEXT: @llvm.objc.retain(ptr [[VAL2]]) -; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[VAL1]]) -; CHECK-NEXT: @llvm.objc.release(ptr [[VAL1]]) -; CHECK-NEXT: @llvm.objc.autorelease(ptr %x) -; CHECK-NEXT: @llvm.objc.release(ptr [[VAL2]]) -; CHECK-NEXT: store ptr %x, ptr %out -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test0a(ptr %out, ptr %x, ptr %y) { +; CHECK-LABEL: define void @test0a( +; CHECK-SAME: ptr [[OUT:%.*]], ptr [[X:%.*]], ptr [[Y:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TEMP0:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TEMP1:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[Y]], ptr [[TEMP0]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[Y]]) #[[ATTR0]], !clang.imprecise_release [[META0:![0-9]+]] +; CHECK-NEXT: call void @test0_helper(ptr [[X]], ptr [[TEMP0]]) +; CHECK-NEXT: [[VAL1:%.*]] = load ptr, ptr [[TEMP0]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[VAL1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[Y]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[VAL1]], ptr [[TEMP1]], align 8 +; CHECK-NEXT: call void @test0_helper(ptr [[X]], ptr [[TEMP1]]) +; CHECK-NEXT: [[VAL2:%.*]] = load ptr, ptr [[TEMP1]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[VAL2]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[VAL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[VAL1]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP3:%.*]] = call ptr @llvm.objc.autorelease(ptr [[X]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP4:%.*]] = tail call ptr @llvm.objc.retain(ptr [[VAL2]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[X]], ptr [[OUT]], align 8 +; CHECK-NEXT: ret void +; entry: %temp0 = alloca ptr, align 8 %temp1 = alloca ptr, align 8 @@ -113,12 +122,14 @@ entry: ; ARC optimizer should be able to safely remove the retain/release pair as the ; call to @llvm.objc.clang.arc.noop.use is a no-op. -; CHECK-LABEL: define void @test_arc_noop_use( -; CHECK-NEXT: call void @can_release(ptr %x) -; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use( -; CHECK-NEXT: ret void define void @test_arc_noop_use(ptr %out, ptr %x) { +; CHECK-LABEL: define void @test_arc_noop_use( +; CHECK-SAME: ptr [[OUT:%.*]], ptr [[X:%.*]]) { +; CHECK-NEXT: call void @can_release(ptr [[X]]) +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[X]]) +; CHECK-NEXT: ret void +; call ptr @llvm.objc.retain(ptr %x) call void @can_release(ptr %x) call void (...) @llvm.objc.clang.arc.noop.use(ptr %x) @@ -128,3 +139,6 @@ define void @test_arc_noop_use(ptr %out, ptr %x) { !0 = !{} +;. +; CHECK: [[META0]] = !{} +;. diff --git a/llvm/test/Transforms/ObjCARC/invoke.ll b/llvm/test/Transforms/ObjCARC/invoke.ll index a792b8e868366..7c1a454daab2c 100644 --- a/llvm/test/Transforms/ObjCARC/invoke.ll +++ b/llvm/test/Transforms/ObjCARC/invoke.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -S -passes=objc-arc < %s | FileCheck %s declare ptr @llvm.objc.retain(ptr) @@ -10,20 +11,28 @@ declare ptr @returner() ; ARCOpt shouldn't try to move the releases to the block containing the invoke. -; CHECK-LABEL: define void @test0( -; CHECK: invoke.cont: -; CHECK: call void @llvm.objc.release(ptr %zipFile) [[NUW:#[0-9]+]], !clang.imprecise_release !0 -; CHECK: ret void -; CHECK: lpad: -; CHECK: call void @llvm.objc.release(ptr %zipFile) [[NUW]], !clang.imprecise_release !0 -; CHECK: ret void -; CHECK-NEXT: } define void @test0(ptr %zipFile) personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define void @test0( +; CHECK-SAME: ptr [[ZIPFILE:%.*]]) personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[ZIPFILE]]) #[[ATTR0:[0-9]+]], !clang.imprecise_release [[META0:![0-9]+]] +; CHECK-NEXT: call void @use_pointer(ptr [[ZIPFILE]]) +; CHECK-NEXT: invoke void @objc_msgSend(ptr [[ZIPFILE]]) +; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] +; CHECK: [[INVOKE_CONT]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[ZIPFILE]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[EXN:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[ZIPFILE]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %zipFile) nounwind call void @use_pointer(ptr %zipFile) - invoke void @objc_msgSend(ptr %zipFile) - to label %invoke.cont unwind label %lpad + invoke void @objc_msgSend(ptr %zipFile) + to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry call void @llvm.objc.release(ptr %zipFile) nounwind, !clang.imprecise_release !0 @@ -31,31 +40,39 @@ invoke.cont: ; preds = %entry lpad: ; preds = %entry %exn = landingpad {ptr, i32} - cleanup + cleanup call void @llvm.objc.release(ptr %zipFile) nounwind, !clang.imprecise_release !0 ret void } ; ARCOpt should move the release before the callee calls. -; CHECK-LABEL: define void @test1( -; CHECK: invoke.cont: -; CHECK: call void @llvm.objc.release(ptr %zipFile) [[NUW]], !clang.imprecise_release !0 -; CHECK: call void @callee() -; CHECK: br label %done -; CHECK: lpad: -; CHECK: call void @llvm.objc.release(ptr %zipFile) [[NUW]], !clang.imprecise_release !0 -; CHECK: call void @callee() -; CHECK: br label %done -; CHECK: done: -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test1(ptr %zipFile) personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define void @test1( +; CHECK-SAME: ptr [[ZIPFILE:%.*]]) personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[ZIPFILE]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @use_pointer(ptr [[ZIPFILE]]) +; CHECK-NEXT: invoke void @objc_msgSend(ptr [[ZIPFILE]]) +; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] +; CHECK: [[INVOKE_CONT]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[ZIPFILE]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[DONE:.*]] +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[EXN:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[ZIPFILE]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[DONE]] +; CHECK: [[DONE]]: +; CHECK-NEXT: ret void +; entry: call ptr @llvm.objc.retain(ptr %zipFile) nounwind call void @use_pointer(ptr %zipFile) invoke void @objc_msgSend(ptr %zipFile) - to label %invoke.cont unwind label %lpad + to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry call void @callee() @@ -63,7 +80,7 @@ invoke.cont: ; preds = %entry lpad: ; preds = %entry %exn = landingpad {ptr, i32} - cleanup + cleanup call void @callee() br label %done @@ -75,26 +92,33 @@ done: ; The optimizer should ignore invoke unwind paths consistently. ; PR12265 -; CHECK: define void @test2() personality ptr @__objc_personality_v0 { -; CHECK: invoke.cont: -; CHECK-NEXT: call ptr @llvm.objc.retain -; CHECK-NOT: @llvm.objc.r -; CHECK: finally.cont: -; CHECK-NEXT: call void @llvm.objc.release -; CHECK-NOT: @objc -; CHECK: finally.rethrow: -; CHECK-NOT: @objc -; CHECK: } define void @test2() personality ptr @__objc_personality_v0 { +; CHECK-LABEL: define void @test2() personality ptr @__objc_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = invoke ptr @objc_msgSend() +; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[FINALLY_RETHROW:.*]], !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK: [[INVOKE_CONT]]: +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @objc_msgSend(), !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK-NEXT: invoke void @use_pointer(ptr [[CALL]]) +; CHECK-NEXT: to label %[[FINALLY_CONT:.*]] unwind label %[[FINALLY_RETHROW]], !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK: [[FINALLY_CONT]]: +; CHECK-NEXT: tail call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; CHECK: [[FINALLY_RETHROW]]: +; CHECK-NEXT: [[TMP2:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: catch ptr null +; CHECK-NEXT: unreachable +; entry: %call = invoke ptr @objc_msgSend() - to label %invoke.cont unwind label %finally.rethrow, !clang.arc.no_objc_arc_exceptions !0 + to label %invoke.cont unwind label %finally.rethrow, !clang.arc.no_objc_arc_exceptions !0 invoke.cont: ; preds = %entry %tmp1 = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %call) nounwind call void @objc_msgSend(), !clang.arc.no_objc_arc_exceptions !0 invoke void @use_pointer(ptr %call) - to label %finally.cont unwind label %finally.rethrow, !clang.arc.no_objc_arc_exceptions !0 + to label %finally.cont unwind label %finally.rethrow, !clang.arc.no_objc_arc_exceptions !0 finally.cont: ; preds = %invoke.cont tail call void @llvm.objc.release(ptr %call) nounwind, !clang.imprecise_release !0 @@ -102,18 +126,33 @@ finally.cont: ; preds = %invoke.cont finally.rethrow: ; preds = %invoke.cont, %entry %tmp2 = landingpad { ptr, i32 } - catch ptr null + catch ptr null unreachable } ; Don't try to place code on invoke critical edges. -; CHECK-LABEL: define void @test3( -; CHECK: if.end: -; CHECK-NEXT: call void @llvm.objc.release(ptr %p) [[NUW]] -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test3(ptr %p, i1 %b) personality ptr @__objc_personality_v0 { +; CHECK-LABEL: define void @test3( +; CHECK-SAME: ptr [[P:%.*]], i1 [[B:%.*]]) personality ptr @__objc_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br i1 [[B]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: invoke void @use_pointer(ptr [[P]]) +; CHECK-NEXT: to label %[[IF_END:.*]] unwind label %[[LPAD:.*]], !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK: [[IF_ELSE]]: +; CHECK-NEXT: invoke void @use_pointer(ptr [[P]]) +; CHECK-NEXT: to label %[[IF_END]] unwind label %[[LPAD]], !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[R:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret void +; CHECK: [[IF_END]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -121,15 +160,15 @@ entry: if.then: invoke void @use_pointer(ptr %p) - to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 if.else: invoke void @use_pointer(ptr %p) - to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 lpad: %r = landingpad { ptr, i32 } - cleanup + cleanup ret void if.end: @@ -139,17 +178,28 @@ if.end: ; Like test3, but with ARC-relevant exception handling. -; CHECK-LABEL: define void @test4( -; CHECK: lpad: -; CHECK-NEXT: %r = landingpad { ptr, i32 } -; CHECK-NEXT: cleanup -; CHECK-NEXT: call void @llvm.objc.release(ptr %p) [[NUW]] -; CHECK-NEXT: ret void -; CHECK: if.end: -; CHECK-NEXT: call void @llvm.objc.release(ptr %p) [[NUW]] -; CHECK-NEXT: ret void -; CHECK-NEXT: } define void @test4(ptr %p, i1 %b) personality ptr @__objc_personality_v0 { +; CHECK-LABEL: define void @test4( +; CHECK-SAME: ptr [[P:%.*]], i1 [[B:%.*]]) personality ptr @__objc_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br i1 [[B]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: invoke void @use_pointer(ptr [[P]]) +; CHECK-NEXT: to label %[[IF_END:.*]] unwind label %[[LPAD:.*]] +; CHECK: [[IF_ELSE]]: +; CHECK-NEXT: invoke void @use_pointer(ptr [[P]]) +; CHECK-NEXT: to label %[[IF_END]] unwind label %[[LPAD]] +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[R:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; CHECK: [[IF_END]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[P]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %0 = call ptr @llvm.objc.retain(ptr %p) call void @callee() @@ -157,15 +207,15 @@ entry: if.then: invoke void @use_pointer(ptr %p) - to label %if.end unwind label %lpad + to label %if.end unwind label %lpad if.else: invoke void @use_pointer(ptr %p) - to label %if.end unwind label %lpad + to label %if.end unwind label %lpad lpad: %r = landingpad { ptr, i32 } - cleanup + cleanup call void @llvm.objc.release(ptr %p) ret void @@ -177,17 +227,26 @@ if.end: ; Don't turn the retainAutoreleaseReturnValue into retain, because it's ; for an invoke which we can assume codegen will put immediately prior. -; CHECK-LABEL: define void @test5( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %z) -; CHECK: } define void @test5() personality ptr @__objc_personality_v0 { +; CHECK-LABEL: define void @test5() personality ptr @__objc_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[Z:%.*]] = invoke ptr @returner() +; CHECK-NEXT: to label %[[IF_END:.*]] unwind label %[[LPAD:.*]], !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[R13:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret void +; CHECK: [[IF_END]]: +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[Z]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %z = invoke ptr @returner() - to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 lpad: %r13 = landingpad { ptr, i32 } - cleanup + cleanup ret void if.end: @@ -197,17 +256,27 @@ if.end: ; Like test5, but there's intervening code. -; CHECK-LABEL: define void @test6( -; CHECK: call ptr @llvm.objc.retain(ptr %z) -; CHECK: } define void @test6() personality ptr @__objc_personality_v0 { +; CHECK-LABEL: define void @test6() personality ptr @__objc_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[Z:%.*]] = invoke ptr @returner() +; CHECK-NEXT: to label %[[IF_END:.*]] unwind label %[[LPAD:.*]], !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[R13:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret void +; CHECK: [[IF_END]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[Z]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %z = invoke ptr @returner() - to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 lpad: %r13 = landingpad { ptr, i32 } - cleanup + cleanup ret void if.end: @@ -219,6 +288,8 @@ if.end: declare i32 @__gxx_personality_v0(...) declare i32 @__objc_personality_v0(...) -; CHECK: attributes [[NUW]] = { nounwind } !0 = !{} +;. +; CHECK: [[META0]] = !{} +;. diff --git a/llvm/test/Transforms/ObjCARC/nested.ll b/llvm/test/Transforms/ObjCARC/nested.ll index d03cbd5f54e71..5ad1f450fa8ab 100644 --- a/llvm/test/Transforms/ObjCARC/nested.ll +++ b/llvm/test/Transforms/ObjCARC/nested.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes=objc-arc -S < %s | FileCheck %s %struct.__objcFastEnumerationState = type { i64, ptr, ptr, [5 x i64] } @@ -25,11 +26,55 @@ declare void @__crasher_block_invoke1(ptr nocapture) ; Delete a nested retain+release pair. -; CHECK-LABEL: define void @test0( -; CHECK: call ptr @llvm.objc.retain -; CHECK-NOT: @llvm.objc.retain -; CHECK: } define void @test0(ptr %a) nounwind { +; CHECK-LABEL: define void @test0( +; CHECK-SAME: ptr [[A:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL:%.*]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP2]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL6:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP7:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP7]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP3:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR3:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR3]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP1]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP0]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: call void @use(ptr [[TMP2]]) +; CHECK-NEXT: [[TMP3]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP3]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL6]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP5]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL6]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0:![0-9]+]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -88,11 +133,56 @@ forcoll.empty: ; Delete a nested retain+release pair. -; CHECK-LABEL: define void @test2( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK-NOT: @llvm.objc.retain -; CHECK: } define void @test2() nounwind { +; CHECK-LABEL: define void @test2( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL3:%.*]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP2]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL3]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL3]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP8:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP8]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP3:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR4:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR4]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP1]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP0]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: call void @use(ptr [[TMP2]]) +; CHECK-NEXT: [[TMP3]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP3]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -152,11 +242,56 @@ forcoll.empty: ; Delete a nested retain+release pair. -; CHECK-LABEL: define void @test4( -; CHECK: call ptr @llvm.objc.retain -; CHECK-NOT: @llvm.objc.retain -; CHECK: } define void @test4() nounwind { +; CHECK-LABEL: define void @test4( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr @g, align 8 +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL:%.*]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP4]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL8:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP9:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP9]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP3:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR5:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR5]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP1]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP0]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: call void @use(ptr [[TMP2]]) +; CHECK-NEXT: [[TMP3]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP3]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL8]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP7]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL8]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -216,11 +351,56 @@ forcoll.empty: ; Delete a nested retain+release pair. -; CHECK-LABEL: define void @test5( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK-NOT: @llvm.objc.retain -; CHECK: } define void @test5() nounwind { +; CHECK-LABEL: define void @test5( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL3:%.*]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP2]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL3]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL3]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP8:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP8]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP3:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR4:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR4]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP1]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP0]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: call void @use(ptr [[TMP2]]) +; CHECK-NEXT: [[TMP3]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP3]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -281,11 +461,57 @@ forcoll.empty: ; We handle this now due to the fact that a release just needs a post dominating ; use. ; -; CHECK-LABEL: define void @test6( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK-NOT: @llvm.objc.retain -; CHECK: } define void @test6() nounwind { +; CHECK-LABEL: define void @test6( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL3:%.*]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP2]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL3]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL3]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP8:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP8]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP3:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR4:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR4]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP1]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP0]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: call void @use(ptr [[TMP2]]) +; CHECK-NEXT: [[TMP3]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP3]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -348,11 +574,60 @@ forcoll.empty: ; The optimizer currently can't do this, because isn't isn't sophisticated enough in ; reasnoning about nesting. -; CHECK-LABEL: define void @test7( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: @llvm.objc.retain -; CHECK: } define void @test7() nounwind { +; CHECK-LABEL: define void @test7( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL3:%.*]] = call i64 @objc_msgSend(ptr [[TMP1]], ptr [[TMP2]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL3]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL3]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP8:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP8]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP4:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR4:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR4]], align 8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP2]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP1]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: call void @use(ptr [[TMP3]]) +; CHECK-NEXT: [[TMP4]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP4]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP1]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP5]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP1]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -414,11 +689,61 @@ forcoll.empty: ; Delete a nested retain+release pair. -; CHECK-LABEL: define void @test8( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK-NOT: @llvm.objc.retain -; CHECK: } define void @test8() nounwind { +; CHECK-LABEL: define void @test8( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL3:%.*]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP2]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL3]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: [[STATEITEMS_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL3]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP8:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP8]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ], [ [[TMP3:%.*]], %[[FORCOLL_NEXT:.*]] ] +; CHECK-NEXT: [[MUTATIONSPTR4:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR4]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP1]], label %[[FORCOLL_NOTMUTATED:.*]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP0]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[STATEITEMS:%.*]] = load ptr, ptr [[STATEITEMS_PTR]], align 8 +; CHECK-NEXT: [[CURRENTITEM_PTR:%.*]] = getelementptr ptr, ptr [[STATEITEMS]], i64 [[FORCOLL_INDEX]] +; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[CURRENTITEM_PTR]], align 8 +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq ptr [[TMP2]], null +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[FORCOLL_NEXT]], label %[[IF_THEN:.*]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: br label %[[FORCOLL_NEXT]] +; CHECK: [[FORCOLL_NEXT]]: +; CHECK-NEXT: [[TMP3]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP3]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP0]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -487,12 +812,58 @@ forcoll.empty: ; The optimizer currently can't do this, because of a split loop backedge. ; See test9b for the same testcase without a split backedge. -; CHECK-LABEL: define void @test9( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: call ptr @llvm.objc.retain -; CHECK: } define void @test9() nounwind { +; CHECK-LABEL: define void @test9( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[CALL1:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL4:%.*]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP3]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL4]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL4]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP9:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP9]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ [[PHITMP:%.*]], %[[FORCOLL_NOTMUTATED_FORCOLL_LOOPBODY_CRIT_EDGE:.*]] ], [ 1, %[[FORCOLL_LOOPBODY_OUTER]] ] +; CHECK-NEXT: [[MUTATIONSPTR5:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR5]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP3]], label %[[FORCOLL_NOTMUTATED:.*]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP2]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[FORCOLL_INDEX]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_NOTMUTATED_FORCOLL_LOOPBODY_CRIT_EDGE]] +; CHECK: [[FORCOLL_NOTMUTATED_FORCOLL_LOOPBODY_CRIT_EDGE]]: +; CHECK-NEXT: [[PHITMP]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP2]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -553,12 +924,56 @@ forcoll.empty: ; Like test9, but without a split backedge. TODO: optimize this. -; CHECK-LABEL: define void @test9b( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: @llvm.objc.retain -; CHECK: } define void @test9b() nounwind { +; CHECK-LABEL: define void @test9b( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[CALL1:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL4:%.*]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP3]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL4]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL4]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP9:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP9]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ [[PHITMP:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ], [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ] +; CHECK-NEXT: [[MUTATIONSPTR5:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR5]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP3]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP2]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[PHITMP]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[PHITMP]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP2]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -618,12 +1033,59 @@ forcoll.empty: ; The optimizer currently can't do this, because of a split loop backedge. ; See test10b for the same testcase without a split backedge. -; CHECK-LABEL: define void @test10( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: call ptr @llvm.objc.retain -; CHECK: } define void @test10() nounwind { +; CHECK-LABEL: define void @test10( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[CALL1:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL4:%.*]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP3]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL4]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL4]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP9:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP9]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ [[PHITMP:%.*]], %[[FORCOLL_NOTMUTATED_FORCOLL_LOOPBODY_CRIT_EDGE:.*]] ], [ 1, %[[FORCOLL_LOOPBODY_OUTER]] ] +; CHECK-NEXT: [[MUTATIONSPTR5:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR5]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP3]], label %[[FORCOLL_NOTMUTATED:.*]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP2]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[FORCOLL_INDEX]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_NOTMUTATED_FORCOLL_LOOPBODY_CRIT_EDGE]] +; CHECK: [[FORCOLL_NOTMUTATED_FORCOLL_LOOPBODY_CRIT_EDGE]]: +; CHECK-NEXT: [[PHITMP]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP2]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -685,12 +1147,57 @@ forcoll.empty: ; Like test10, but without a split backedge. TODO: optimize this. -; CHECK-LABEL: define void @test10b( -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue -; CHECK: @llvm.objc.retain -; CHECK: } define void @test10b() nounwind { +; CHECK-LABEL: define void @test10b( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[STATE_PTR:%.*]] = alloca [[STRUCT___OBJCFASTENUMERATIONSTATE:%.*]], align 8 +; CHECK-NEXT: [[ITEMS_PTR:%.*]] = alloca [16 x ptr], align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[CALL1:%.*]] = call ptr @returner() +; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[CALL1]]) #[[ATTR0]] +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE_PTR]], i8 0, i64 64, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[TMP0]]) #[[ATTR0]] +; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL4:%.*]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP3]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[CALL4]], 0 +; CHECK-NEXT: br i1 [[ISZERO]], label %[[FORCOLL_EMPTY:.*]], label %[[FORCOLL_LOOPINIT:.*]] +; CHECK: [[FORCOLL_LOOPINIT]]: +; CHECK-NEXT: [[MUTATIONSPTR_PTR:%.*]] = getelementptr inbounds [[STRUCT___OBJCFASTENUMERATIONSTATE]], ptr [[STATE_PTR]], i64 0, i32 2 +; CHECK-NEXT: [[MUTATIONSPTR:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[FORCOLL_INITIAL_MUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR]], align 8 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY_OUTER:.*]] +; CHECK: [[FORCOLL_LOOPBODY_OUTER]]: +; CHECK-NEXT: [[FORCOLL_COUNT_PH:%.*]] = phi i64 [ [[CALL4]], %[[FORCOLL_LOOPINIT]] ], [ [[CALL7:%.*]], %[[FORCOLL_REFETCH:.*]] ] +; CHECK-NEXT: [[TMP9:%.*]] = icmp ugt i64 [[FORCOLL_COUNT_PH]], 1 +; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP9]], i64 [[FORCOLL_COUNT_PH]], i64 1 +; CHECK-NEXT: br label %[[FORCOLL_LOOPBODY:.*]] +; CHECK: [[FORCOLL_LOOPBODY]]: +; CHECK-NEXT: [[FORCOLL_INDEX:%.*]] = phi i64 [ [[PHITMP:%.*]], %[[FORCOLL_NOTMUTATED:.*]] ], [ 0, %[[FORCOLL_LOOPBODY_OUTER]] ] +; CHECK-NEXT: [[MUTATIONSPTR5:%.*]] = load ptr, ptr [[MUTATIONSPTR_PTR]], align 8 +; CHECK-NEXT: [[STATEMUTATIONS:%.*]] = load i64, ptr [[MUTATIONSPTR5]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[STATEMUTATIONS]], [[FORCOLL_INITIAL_MUTATIONS]] +; CHECK-NEXT: br i1 [[TMP3]], label %[[FORCOLL_NOTMUTATED]], label %[[FORCOLL_MUTATED:.*]] +; CHECK: [[FORCOLL_MUTATED]]: +; CHECK-NEXT: call void @llvm.objc.enumerationMutation(ptr [[TMP2]]) +; CHECK-NEXT: br label %[[FORCOLL_NOTMUTATED]] +; CHECK: [[FORCOLL_NOTMUTATED]]: +; CHECK-NEXT: [[PHITMP]] = add i64 [[FORCOLL_INDEX]], 1 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[PHITMP]], [[UMAX]] +; CHECK-NEXT: br i1 [[EXITCOND]], label %[[FORCOLL_REFETCH]], label %[[FORCOLL_LOOPBODY]] +; CHECK: [[FORCOLL_REFETCH]]: +; CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr @"\01L_OBJC_SELECTOR_REFERENCES_", align 8 +; CHECK-NEXT: [[CALL7]] = call i64 @objc_msgSend(ptr [[TMP2]], ptr [[TMP6]], ptr [[STATE_PTR]], ptr [[ITEMS_PTR]], i64 16) +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i64 [[CALL7]], 0 +; CHECK-NEXT: br i1 [[TMP4]], label %[[FORCOLL_EMPTY]], label %[[FORCOLL_LOOPBODY_OUTER]] +; CHECK: [[FORCOLL_EMPTY]]: +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP2]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP1]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP0]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: ret void +; entry: %state.ptr = alloca %struct.__objcFastEnumerationState, align 8 %items.ptr = alloca [16 x ptr], align 8 @@ -756,12 +1263,51 @@ forcoll.empty: @__block_d_tmp = external hidden constant { i64, i64, ptr, ptr, ptr, ptr } @__block_d_tmp5 = external hidden constant { i64, i64, ptr, ptr, ptr, ptr } -; CHECK-LABEL: define void @test11( -; CHECK: tail call ptr @llvm.objc.retain(ptr %call) [[NUW:#[0-9]+]] -; CHECK: tail call ptr @llvm.objc.retain(ptr %call) [[NUW]] -; CHECK: call void @llvm.objc.release(ptr %call) [[NUW]], !clang.imprecise_release !0 -; CHECK: } define void @test11() { +; CHECK-LABEL: define void @test11() { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[BLOCK:%.*]] = alloca <{ ptr, i32, i32, ptr, ptr, ptr }>, align 8 +; CHECK-NEXT: [[BLOCK9:%.*]] = alloca <{ ptr, i32, i32, ptr, ptr, ptr }>, align 8 +; CHECK-NEXT: [[CALL:%.*]] = call ptr @def(), !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK-NEXT: [[FOO:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i64 0, i32 5 +; CHECK-NEXT: [[BLOCK_ISA:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i64 0, i32 0 +; CHECK-NEXT: store ptr @_NSConcreteStackBlock, ptr [[BLOCK_ISA]], align 8 +; CHECK-NEXT: [[BLOCK_FLAGS:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i64 0, i32 1 +; CHECK-NEXT: store i32 1107296256, ptr [[BLOCK_FLAGS]], align 8 +; CHECK-NEXT: [[BLOCK_RESERVED:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i64 0, i32 2 +; CHECK-NEXT: store i32 0, ptr [[BLOCK_RESERVED]], align 4 +; CHECK-NEXT: [[BLOCK_INVOKE:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i64 0, i32 3 +; CHECK-NEXT: store ptr @__crasher_block_invoke, ptr [[BLOCK_INVOKE]], align 8 +; CHECK-NEXT: [[BLOCK_D:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i64 0, i32 4 +; CHECK-NEXT: store ptr @__block_d_tmp, ptr [[BLOCK_D]], align 8 +; CHECK-NEXT: [[FOO2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: store ptr [[FOO2]], ptr [[FOO]], align 8 +; CHECK-NEXT: [[FOO5:%.*]] = call ptr @llvm.objc.retainBlock(ptr [[BLOCK]]) #[[ATTR0]] +; CHECK-NEXT: call void @use(ptr [[FOO5]]), !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[FOO5]]) #[[ATTR0]] +; CHECK-NEXT: [[STRONGDESTROY:%.*]] = load ptr, ptr [[FOO]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[STRONGDESTROY]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[FOO10:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK9]], i64 0, i32 5 +; CHECK-NEXT: [[BLOCK_ISA11:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK9]], i64 0, i32 0 +; CHECK-NEXT: store ptr @_NSConcreteStackBlock, ptr [[BLOCK_ISA11]], align 8 +; CHECK-NEXT: [[BLOCK_FLAGS12:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK9]], i64 0, i32 1 +; CHECK-NEXT: store i32 1107296256, ptr [[BLOCK_FLAGS12]], align 8 +; CHECK-NEXT: [[BLOCK_RESERVED13:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK9]], i64 0, i32 2 +; CHECK-NEXT: store i32 0, ptr [[BLOCK_RESERVED13]], align 4 +; CHECK-NEXT: [[BLOCK_INVOKE14:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK9]], i64 0, i32 3 +; CHECK-NEXT: store ptr @__crasher_block_invoke1, ptr [[BLOCK_INVOKE14]], align 8 +; CHECK-NEXT: [[BLOCK_D15:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK9]], i64 0, i32 4 +; CHECK-NEXT: store ptr @__block_d_tmp5, ptr [[BLOCK_D15]], align 8 +; CHECK-NEXT: store ptr [[CALL]], ptr [[FOO10]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[FOO21:%.*]] = call ptr @llvm.objc.retainBlock(ptr [[BLOCK9]]) #[[ATTR0]] +; CHECK-NEXT: call void @use(ptr [[FOO21]]), !clang.arc.no_objc_arc_exceptions [[META0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[FOO21]]) #[[ATTR0]] +; CHECK-NEXT: [[STRONGDESTROY25:%.*]] = load ptr, ptr [[FOO10]], align 8 +; CHECK-NEXT: call void @llvm.objc.release(ptr [[STRONGDESTROY25]]) #[[ATTR0]], !clang.imprecise_release [[META0]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; entry: %block = alloca <{ ptr, i32, i32, ptr, ptr, ptr }>, align 8 %block9 = alloca <{ ptr, i32, i32, ptr, ptr, ptr }>, align 8 @@ -807,6 +1353,6 @@ entry: } -; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } -; CHECK: attributes #2 = { nonlazybind } +;. +; CHECK: [[META0]] = !{} +;. diff --git a/llvm/test/Transforms/ObjCARC/split-backedge.ll b/llvm/test/Transforms/ObjCARC/split-backedge.ll index ed855541cfb54..c28071f5908f5 100644 --- a/llvm/test/Transforms/ObjCARC/split-backedge.ll +++ b/llvm/test/Transforms/ObjCARC/split-backedge.ll @@ -1,28 +1,46 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -S -passes=objc-arc < %s | FileCheck %s ; Handle a retain+release pair entirely contained within a split loop backedge. ; rdar://11256239 -; CHECK-LABEL: define void @test0( -; CHECK: call ptr @llvm.objc.retain(ptr %call) [[NUW:#[0-9]+]] -; CHECK: call ptr @llvm.objc.retain(ptr %call) [[NUW]] -; CHECK: call ptr @llvm.objc.retain(ptr %cond) [[NUW]] -; CHECK: call void @llvm.objc.release(ptr %call) [[NUW]] -; CHECK: call void @llvm.objc.release(ptr %call) [[NUW]] -; CHECK: call void @llvm.objc.release(ptr %cond) [[NUW]] define void @test0() personality ptr @__objc_personality_v0 { +; CHECK-LABEL: define void @test0() personality ptr @__objc_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: br label %[[WHILE_BODY:.*]] +; CHECK: [[WHILE_BODY]]: +; CHECK-NEXT: [[CALL:%.*]] = invoke ptr @returner() +; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]], !clang.arc.no_objc_arc_exceptions [[META0:![0-9]+]] +; CHECK: [[INVOKE_CONT]]: +; CHECK-NEXT: [[T0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: [[T1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: [[CALL_I1:%.*]] = invoke ptr @returner() +; CHECK-NEXT: to label %[[INVOKE_CONT1:.*]] unwind label %[[LPAD]] +; CHECK: [[INVOKE_CONT1]]: +; CHECK-NEXT: [[COND:%.*]] = select i1 undef, ptr null, ptr [[CALL]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @llvm.objc.release(ptr [[CALL]]) #[[ATTR0]] +; CHECK-NEXT: call void @use_pointer(ptr [[COND]]) +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[COND]]) #[[ATTR0]] +; CHECK-NEXT: br label %[[WHILE_BODY]] +; CHECK: [[LPAD]]: +; CHECK-NEXT: [[T4:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: catch ptr null +; CHECK-NEXT: ret void +; entry: br label %while.body while.body: ; preds = %while.cond %call = invoke ptr @returner() - to label %invoke.cont unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + to label %invoke.cont unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 invoke.cont: ; preds = %while.body %t0 = call ptr @llvm.objc.retain(ptr %call) nounwind %t1 = call ptr @llvm.objc.retain(ptr %call) nounwind %call.i1 = invoke ptr @returner() - to label %invoke.cont1 unwind label %lpad + to label %invoke.cont1 unwind label %lpad invoke.cont1: ; preds = %invoke.cont %cond = select i1 undef, ptr null, ptr %call @@ -35,7 +53,7 @@ invoke.cont1: ; preds = %invoke.cont lpad: ; preds = %invoke.cont, %while.body %t4 = landingpad { ptr, i32 } - catch ptr null + catch ptr null ret void } @@ -47,4 +65,6 @@ declare void @use_pointer(ptr) !0 = !{} -; CHECK: attributes [[NUW]] = { nounwind } +;. +; CHECK: [[META0]] = !{} +;.