[llvm] [ObjCARC] Optimize loadWeak across copyWeak and moveWeak (PR #218417)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 07:32:37 PDT 2026


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/218417

>From 098cb544e5f9c4a05cc97d9ab75bafaff5d825ee Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 24 Aug 2026 10:12:04 -0400
Subject: [PATCH 1/2] [ObjCARC] Precommit tests for weak pointer copy/move
 optimization

---
 llvm/test/Transforms/ObjCARC/weak.ll | 138 +++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/llvm/test/Transforms/ObjCARC/weak.ll b/llvm/test/Transforms/ObjCARC/weak.ll
index db63847c8f6c9..1ce14db64f900 100644
--- a/llvm/test/Transforms/ObjCARC/weak.ll
+++ b/llvm/test/Transforms/ObjCARC/weak.ll
@@ -58,3 +58,141 @@ entry:
 
   ret void
 }
+
+declare void @use_pointer(ptr)
+
+; Test that copyWeak enables eliminating a redundant loadWeak.
+define void @test_copyweak(ptr %src, ptr %dest, ptr %val) {
+; CHECK-LABEL: @test_copyweak(
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[SRC:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.copyWeak(ptr [[DEST:%.*]], ptr [[SRC]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[DEST]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  call ptr @llvm.objc.storeWeak(ptr %src, ptr %val)
+  call void @llvm.objc.copyWeak(ptr %dest, ptr %src)
+  %x = call ptr @llvm.objc.loadWeak(ptr %dest)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+; Test that moveWeak enables eliminating a redundant loadWeak.
+define void @test_moveweak(ptr %src, ptr %dest, ptr %val) {
+; CHECK-LABEL: @test_moveweak(
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[SRC:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.moveWeak(ptr [[DEST:%.*]], ptr [[SRC]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[DEST]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  call ptr @llvm.objc.storeWeak(ptr %src, ptr %val)
+  call void @llvm.objc.moveWeak(ptr %dest, ptr %src)
+  %x = call ptr @llvm.objc.loadWeak(ptr %dest)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+; Test that moveWeak clobbers a load from its source.
+define void @test_moveweak_clobber(ptr %src, ptr %dest, ptr %val) {
+; CHECK-LABEL: @test_moveweak_clobber(
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[SRC:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.moveWeak(ptr [[DEST:%.*]], ptr [[SRC]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[SRC]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  call ptr @llvm.objc.storeWeak(ptr %src, ptr %val)
+  call void @llvm.objc.moveWeak(ptr %dest, ptr %src)
+  %x = call ptr @llvm.objc.loadWeak(ptr %src)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+; Test that copyWeak does NOT clobber a load from its source.
+define void @test_copyweak_no_clobber(ptr %src, ptr %dest, ptr %val) {
+; CHECK-LABEL: @test_copyweak_no_clobber(
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[SRC:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.copyWeak(ptr [[DEST:%.*]], ptr [[SRC]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[SRC]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  call ptr @llvm.objc.storeWeak(ptr %src, ptr %val)
+  call void @llvm.objc.copyWeak(ptr %dest, ptr %src)
+  %x = call ptr @llvm.objc.loadWeak(ptr %src)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+; Test that moveWeak(p, p) clobbers the load. We are conservative here because
+; moveWeak might clear the source pointer even if it is the same as the
+; destination.
+define void @test_moveweak_self_alias(ptr %p, ptr %val) {
+; CHECK-LABEL: @test_moveweak_self_alias(
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[P:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.moveWeak(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[P]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  call ptr @llvm.objc.storeWeak(ptr %p, ptr %val)
+  call void @llvm.objc.moveWeak(ptr %p, ptr %p)
+  %x = call ptr @llvm.objc.loadWeak(ptr %p)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+; Test that copyWeak(p, p) is safely handled.
+define void @test_copyweak_self_alias(ptr %p, ptr %val) {
+; CHECK-LABEL: @test_copyweak_self_alias(
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[P:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.copyWeak(ptr [[P]], ptr [[P]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[P]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  call ptr @llvm.objc.storeWeak(ptr %p, ptr %val)
+  call void @llvm.objc.copyWeak(ptr %p, ptr %p)
+  %x = call ptr @llvm.objc.loadWeak(ptr %p)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+declare ptr @get_may_alias()
+
+; Test that moveWeak with a MayAlias destination clobbers the load.
+define void @test_moveweak_mayalias_dest(ptr %src, ptr %val) {
+; CHECK-LABEL: @test_moveweak_mayalias_dest(
+; CHECK-NEXT:    [[DEST:%.*]] = call ptr @get_may_alias()
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[SRC:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.moveWeak(ptr [[DEST]], ptr [[SRC]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[SRC]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  %dest = call ptr @get_may_alias()
+  call ptr @llvm.objc.storeWeak(ptr %src, ptr %val)
+  call void @llvm.objc.moveWeak(ptr %dest, ptr %src)
+  %x = call ptr @llvm.objc.loadWeak(ptr %src)
+  call void @use_pointer(ptr %x)
+  ret void
+}
+
+; Test that moveWeak with a MayAlias source clobbers the load.
+define void @test_moveweak_mayalias_src(ptr %dest, ptr %val) {
+; CHECK-LABEL: @test_moveweak_mayalias_src(
+; CHECK-NEXT:    [[SRC:%.*]] = call ptr @get_may_alias()
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[DEST:%.*]], ptr [[VAL:%.*]])
+; CHECK-NEXT:    call void @llvm.objc.moveWeak(ptr [[DEST]], ptr [[SRC]])
+; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[DEST]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    ret void
+;
+  %src = call ptr @get_may_alias()
+  call ptr @llvm.objc.storeWeak(ptr %dest, ptr %val)
+  call void @llvm.objc.moveWeak(ptr %dest, ptr %src)
+  %x = call ptr @llvm.objc.loadWeak(ptr %dest)
+  call void @use_pointer(ptr %x)
+  ret void
+}

>From d2cacfa9ab6912a03d417753f4323233430f43c9 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 24 Aug 2026 10:12:15 -0400
Subject: [PATCH 2/2] [ObjCARC] Optimize loadWeak across copyWeak and moveWeak

Previously, the backward scan for redundant weak loads maintained its tracked pointer locally within the instruction-visitation loop. If it encountered a CopyWeak or MoveWeak operating on the tracked pointer, it conservatively aborted the scan (goto clobbered).
---
 llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 30 +++++++++++++++++----
 llvm/test/Transforms/ObjCARC/weak.ll        |  5 ++--
 2 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
index f92e5bbdf0111..190c0cbf01a77 100644
--- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
+++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
@@ -2096,6 +2096,7 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
     // within the same block. Theoretically, we could do memdep-style non-local
     // analysis too, but that would want caching. A better approach would be to
     // use the technique that EarlyCSE uses.
+    Value *Arg = cast<CallInst>(Inst)->getArgOperand(0);
     inst_iterator Current = std::prev(I);
     BasicBlock *CurrentBB = &*Current.getBasicBlockIterator();
     for (BasicBlock::iterator B = CurrentBB->begin(),
@@ -2110,7 +2111,6 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
         // with that one.
         CallInst *Call = cast<CallInst>(Inst);
         CallInst *EarlierCall = cast<CallInst>(EarlierInst);
-        Value *Arg = Call->getArgOperand(0);
         Value *EarlierArg = EarlierCall->getArgOperand(0);
         switch (PA.getAA()->alias(Arg, EarlierArg)) {
         case AliasResult::MustAlias:
@@ -2140,7 +2140,6 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
         // replace this load's value with the stored value.
         CallInst *Call = cast<CallInst>(Inst);
         CallInst *EarlierCall = cast<CallInst>(EarlierInst);
-        Value *Arg = Call->getArgOperand(0);
         Value *EarlierArg = EarlierCall->getArgOperand(0);
         switch (PA.getAA()->alias(Arg, EarlierArg)) {
         case AliasResult::MustAlias:
@@ -2165,9 +2164,30 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
         break;
       }
       case ARCInstKind::MoveWeak:
-      case ARCInstKind::CopyWeak:
-        // TOOD: Grab the copied value.
-        goto clobbered;
+      case ARCInstKind::CopyWeak: {
+        CallInst *EarlierCall = cast<CallInst>(EarlierInst);
+        Value *EarlierDest = EarlierCall->getArgOperand(0);
+        Value *EarlierSrc = EarlierCall->getArgOperand(1);
+
+        // If the source and destination might alias, don't try to trace.
+        if (PA.getAA()->alias(EarlierDest, EarlierSrc) != AliasResult::NoAlias)
+          goto clobbered;
+
+        switch (PA.getAA()->alias(Arg, EarlierDest)) {
+        case AliasResult::MustAlias:
+          Arg = EarlierSrc;
+          break;
+        case AliasResult::MayAlias:
+        case AliasResult::PartialAlias:
+          goto clobbered;
+        case AliasResult::NoAlias:
+          if (EarlierClass == ARCInstKind::MoveWeak &&
+              PA.getAA()->alias(Arg, EarlierSrc) != AliasResult::NoAlias)
+            goto clobbered;
+          break;
+        }
+        break;
+      }
       case ARCInstKind::AutoreleasepoolPush:
       case ARCInstKind::None:
       case ARCInstKind::IntrinsicUser:
diff --git a/llvm/test/Transforms/ObjCARC/weak.ll b/llvm/test/Transforms/ObjCARC/weak.ll
index 1ce14db64f900..0759f401fbdd1 100644
--- a/llvm/test/Transforms/ObjCARC/weak.ll
+++ b/llvm/test/Transforms/ObjCARC/weak.ll
@@ -110,12 +110,11 @@ define void @test_moveweak_clobber(ptr %src, ptr %dest, ptr %val) {
 }
 
 ; Test that copyWeak does NOT clobber a load from its source.
-define void @test_copyweak_no_clobber(ptr %src, ptr %dest, ptr %val) {
+define void @test_copyweak_no_clobber(ptr noalias %src, ptr noalias %dest, ptr %val) {
 ; CHECK-LABEL: @test_copyweak_no_clobber(
 ; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[SRC:%.*]], ptr [[VAL:%.*]])
 ; CHECK-NEXT:    call void @llvm.objc.copyWeak(ptr [[DEST:%.*]], ptr [[SRC]])
-; CHECK-NEXT:    [[X:%.*]] = call ptr @llvm.objc.loadWeak(ptr [[SRC]])
-; CHECK-NEXT:    call void @use_pointer(ptr [[X]])
+; CHECK-NEXT:    call void @use_pointer(ptr [[VAL]])
 ; CHECK-NEXT:    ret void
 ;
   call ptr @llvm.objc.storeWeak(ptr %src, ptr %val)



More information about the llvm-commits mailing list