[llvm] [ObjCARC] Delete adjacent retain+release pairs (PR #218263)

via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 23 10:07:38 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>

Adds a peephole optimization to ObjCARCOpt::OptimizeIndividualCalls that catches trivial retain followed by release pairs on the same RC identity root and deletes them.

Since these balance each other perfectly and are adjacent, we can eliminate both the retain and the release without invoking the more expensive OptimizeSequences dataflow pass, removing redundant reference counting operations immediately.

This unblocks further optimizations, such as removing inner autoreleasePoolPush/Pop pairs that were previously shadowed by the redundant operations, as evidenced by updates required to existing tests.

---

Patch is 127.30 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218263.diff


4 Files Affected:

- (modified) llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp (+20-2) 
- (modified) llvm/test/Transforms/ObjCARC/basic.ll (+1530-704) 
- (added) llvm/test/Transforms/ObjCARC/release-retain.ll (+31) 
- (modified) llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll (+1-3) 


``````````diff
diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
index f92e5bbdf0111..6eec508357039 100644
--- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
+++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
@@ -154,8 +154,6 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
 
 // TODO: The pointer returned from objc_loadWeakRetained is retained.
 
-// TODO: Delete release+retain pairs (rare).
-
 STATISTIC(NumNoops,       "Number of no-op objc calls eliminated");
 STATISTIC(NumPartialNoops, "Number of partially no-op objc calls eliminated");
 STATISTIC(NumAutoreleases,"Number of autoreleases converted to releases");
@@ -848,6 +846,26 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
       break;
     }
 
+    if (Class == ARCInstKind::Release) {
+      Instruction *PrevInst = Inst->getPrevNode();
+      while (PrevInst && PrevInst->isDebugOrPseudoInst())
+        PrevInst = PrevInst->getPrevNode();
+
+      if (PrevInst && GetBasicARCInstKind(PrevInst) == ARCInstKind::Retain &&
+          GetArgRCIdentityRoot(Inst) == GetArgRCIdentityRoot(PrevInst)) {
+        LLVM_DEBUG(dbgs() << "Deleting adjacent retain and release: "
+                          << *PrevInst << " and " << *Inst << "\n");
+        if (!PrevInst->use_empty())
+          PrevInst->replaceAllUsesWith(
+              cast<CallInst>(PrevInst)->getArgOperand(0));
+        EraseInstruction(Inst);
+        EraseInstruction(PrevInst);
+        Changed = true;
+        ++NumNoops;
+        continue;
+      }
+    }
+
     OptimizeIndividualCallImpl(F, Inst, Class, Arg);
   }
 
diff --git a/llvm/test/Transforms/ObjCARC/basic.ll b/llvm/test/Transforms/ObjCARC/basic.ll
index 24f2862ffe5c7..d32a89bff839d 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 6
 ; 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:    [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]]
+; CHECK-NEXT:    call void @use_pointer(ptr [[S]])
+; CHECK-NEXT:    store i32 7, ptr [[X]], align 4
+; CHECK-NEXT:    call void @llvm.objc.release(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:    [[TMP0:%.*]] = tail call ptr @llvm.objc.retain(ptr [[X]]) #[[ATTR0]]
+; CHECK-NEXT:    call void @use_pointer(ptr [[S]])
+; CHECK-NEXT:    store i32 7, ptr [[X]], align 4
+; 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
   %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...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/218263


More information about the llvm-commits mailing list