[llvm] [SimplifyCFG] Preserve AA metadata when speculating stores (PR #227034)

Hari Limaye via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 04:36:25 PDT 2026


https://github.com/hazzlim updated https://github.com/llvm/llvm-project/pull/227034

>From 270da042551c5d9b9e105706ea1d7ba4138854a1 Mon Sep 17 00:00:00 2001
From: Hari Limaye <hari.limaye at arm.com>
Date: Mon, 28 Sep 2026 10:47:34 +0000
Subject: [PATCH 1/3] Precommit test (NFC)

---
 .../SimplifyCFG/speculate-store-metadata.ll   | 119 ++++++++++++++++++
 1 file changed, 119 insertions(+)
 create mode 100644 llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll

diff --git a/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll b/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll
new file mode 100644
index 0000000000000..f9fece90a3b85
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll
@@ -0,0 +1,119 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
+; RUN: opt < %s -passes=simplifycfg -S | FileCheck %s
+
+define void @matching_store_metadata(ptr %p, i32 %a, i32 %b, i1 %cond) {
+; CHECK-LABEL: @matching_store_metadata(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    store i32 [[A:%.*]], ptr [[P:%.*]], align 4, !tbaa [[TBAA0:![0-9]+]], !alias.scope [[META3:![0-9]+]], !noalias [[META6:![0-9]+]]
+; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  store i32 %a, ptr %p, !tbaa !2, !alias.scope !5, !noalias !8
+  br i1 %cond, label %then, label %exit
+
+then:
+  store i32 %b, ptr %p, !tbaa !2, !alias.scope !5, !noalias !8
+  br label %exit
+
+exit:
+  ret void
+}
+
+define void @missing_store_metadata(ptr %p, i32 %a, i32 %b, i1 %cond) {
+; CHECK-LABEL: @missing_store_metadata(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    store i32 [[A:%.*]], ptr [[P:%.*]], align 4
+; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  store i32 %a, ptr %p
+  br i1 %cond, label %then, label %exit
+
+then:
+  store i32 %b, ptr %p, !tbaa !2
+  br label %exit
+
+exit:
+  ret void
+}
+
+define void @different_store_metadata(ptr %p, i32 %a, i32 %b, i1 %cond) {
+; CHECK-LABEL: @different_store_metadata(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    store i32 [[A:%.*]], ptr [[P:%.*]], align 4, !tbaa [[TBAA0]], !alias.scope [[META3]], !noalias [[META6]]
+; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  store i32 %a, ptr %p, !tbaa !2, !alias.scope !5, !noalias !8
+  br i1 %cond, label %then, label %exit
+
+then:
+  store i32 %b, ptr %p, !tbaa !12, !alias.scope !15, !noalias !18
+  br label %exit
+
+exit:
+  ret void
+}
+
+define i32 @matching_load_store_metadata(i32 %a0, i32 %b, i1 %cond) {
+; CHECK-LABEL: @matching_load_store_metadata(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[P:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[P]], align 4
+; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[P]], align 4, !tbaa [[TBAA0]]
+; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    [[R:%.*]] = load i32, ptr [[P]], align 4
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  %p = alloca i32
+  store i32 %a0, ptr %p
+  %a = load i32, ptr %p, !tbaa !2
+  br i1 %cond, label %then, label %exit
+
+then:
+  store i32 %b, ptr %p, !tbaa !2
+  br label %exit
+
+exit:
+  %r = load i32, ptr %p
+  ret i32 %r
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!"int", !0}
+!2 = !{!1, !1, i64 0}
+!3 = distinct !{!3}
+!4 = distinct !{!4, !3, !"scope.a"}
+!5 = !{!4}
+!6 = distinct !{!6}
+!7 = distinct !{!7, !6, !"noalias.a"}
+!8 = !{!7}
+
+!10 = !{!"Other TBAA"}
+!11 = !{!"other", !10}
+!12 = !{!11, !11, i64 0}
+!13 = distinct !{!13}
+!14 = distinct !{!14, !13, !"scope.b"}
+!15 = !{!14}
+!16 = distinct !{!16}
+!17 = distinct !{!17, !16, !"noalias.b"}
+!18 = !{!17}
+;.
+; CHECK: [[TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
+; CHECK: [[META1]] = !{!"int", [[META2:![0-9]+]]}
+; CHECK: [[META2]] = !{!"Simple C/C++ TBAA"}
+; CHECK: [[META3]] = !{[[META4:![0-9]+]]}
+; CHECK: [[META4]] = distinct !{[[META4]], [[META5:![0-9]+]], !"scope.a"}
+; CHECK: [[META5]] = distinct !{[[META5]]}
+; CHECK: [[META6]] = !{[[META7:![0-9]+]]}
+; CHECK: [[META7]] = distinct !{[[META7]], [[META8:![0-9]+]], !"noalias.a"}
+; CHECK: [[META8]] = distinct !{[[META8]]}
+;.

>From c544209693c5c6e5659596238d113d965e515894 Mon Sep 17 00:00:00 2001
From: Hari Limaye <hari.limaye at arm.com>
Date: Mon, 28 Sep 2026 11:49:03 +0000
Subject: [PATCH 2/3] [SimplifyCFG] Preserve AA metadata when speculating
 stores

When speculating a store instruction, we currently conservatively drop
all UB-implying metadata and attributes, inhibiting alias-analysis after
this transformation has been performed.

This patch improves this by computing the intersection of the
alias-analysis metadata from the speculated store and the previous store
which enabled it.

Assisted-by: Codex
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 38 +++++++++++++++----
 .../SimplifyCFG/speculate-store-metadata.ll   |  7 ++--
 2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 7b31b2ec37a66..42a221036a4ff 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3065,10 +3065,12 @@ class EphemeralValueTracker {
 ///     store i32 %add.add5, i32* %arrayidx2
 ///     ...
 ///
-/// \return The pointer to the value of the previous store if the store can be
-///         hoisted into the predecessor block. 0 otherwise.
+/// \return The value from the previous access if the store can be hoisted into
+///         the predecessor block. PreviousAccess is set to that access. Return
+///         null otherwise.
 static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB,
-                                     BasicBlock *StoreBB, BasicBlock *EndBB) {
+                                     BasicBlock *StoreBB, BasicBlock *EndBB,
+                                     Instruction *&PreviousAccess) {
   StoreInst *StoreToHoist = dyn_cast<StoreInst>(I);
   if (!StoreToHoist)
     return nullptr;
@@ -3102,9 +3104,11 @@ static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB,
       // atomic write.
       if (SI->getPointerOperand() == StorePtr &&
           SI->getValueOperand()->getType() == StoreTy && SI->isSimple() &&
-          SI->getAlign() >= StoreToHoist->getAlign())
+          SI->getAlign() >= StoreToHoist->getAlign()) {
         // Found the previous store, return its value operand.
+        PreviousAccess = SI;
         return SI->getValueOperand();
+      }
       return nullptr; // Unknown store.
     }
 
@@ -3124,6 +3128,7 @@ static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB,
              isDereferenceablePointer(StorePtr, StoreTy, LI->getDataLayout(),
                                       /*IgnoreFree=*/true))) {
           // Found a previous load, return it.
+          PreviousAccess = LI;
           return LI;
         }
       }
@@ -3285,6 +3290,7 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(CondBrInst *BI,
   SmallVector<Instruction *, 2> SpeculatedConditionalLoadsStores;
   Value *SpeculatedStoreValue = nullptr;
   StoreInst *SpeculatedStore = nullptr;
+  Instruction *PreviousStoreAccess = nullptr;
   EphemeralValueTracker EphTracker;
   for (Instruction &I : reverse(drop_end(*ThenBB))) {
     // Skip pseudo probes. The consequence is we lose track of the branch
@@ -3323,8 +3329,8 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(CondBrInst *BI,
     if (!IsSafeCheapLoadStore &&
         !isSafeToSpeculativelyExecute(&I, BI, Options.AC) &&
         !(HoistCondStores && !SpeculatedStoreValue &&
-          (SpeculatedStoreValue =
-               isSafeToSpeculateStore(&I, BB, ThenBB, EndBB))))
+          (SpeculatedStoreValue = isSafeToSpeculateStore(&I, BB, ThenBB, EndBB,
+                                                         PreviousStoreAccess))))
       return false;
     if (!IsSafeCheapLoadStore && !SpeculatedStoreValue &&
         computeSpeculationCost(&I, TTI) >
@@ -3374,6 +3380,11 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(CondBrInst *BI,
   Value *BrCond = BI->getCondition();
   // Insert a select of the value of the speculated store.
   if (SpeculatedStoreValue) {
+    assert(PreviousStoreAccess && "Missing previous store access");
+    // The store will execute on both paths, so retain only AA metadata that is
+    // valid for both the original store and the access on the other path.
+    combineAAMetadata(SpeculatedStore, PreviousStoreAccess);
+
     IRBuilder<NoFolder> Builder(BI);
     Value *OrigV = SpeculatedStore->getValueOperand();
     Value *TrueV = SpeculatedStore->getValueOperand();
@@ -3426,8 +3437,21 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(CondBrInst *BI,
   for (auto &I : make_early_inc_range(*ThenBB)) {
     if (!SpeculatedStoreValue || &I != SpeculatedStore) {
       I.dropLocation();
+      I.dropUBImplyingAttrsAndMetadata();
+    } else {
+      // combineAAMetadata() made these safe on both paths above. Keep the
+      // resulting intersection while dropping all other UB-implying metadata.
+      static constexpr unsigned AAMetadata[] = {
+          LLVMContext::MD_tbaa,
+          LLVMContext::MD_tbaa_struct,
+          LLVMContext::MD_alias_scope,
+          LLVMContext::MD_noalias,
+          LLVMContext::MD_mem_parallel_loop_access,
+          LLVMContext::MD_access_group,
+          LLVMContext::MD_noalias_addrspace,
+      };
+      I.dropUBImplyingAttrsAndMetadata(AAMetadata);
     }
-    I.dropUBImplyingAttrsAndMetadata();
 
     // Drop ephemeral values.
     if (EphTracker.contains(&I)) {
diff --git a/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll b/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll
index f9fece90a3b85..56c9697170a6c 100644
--- a/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll
+++ b/llvm/test/Transforms/SimplifyCFG/speculate-store-metadata.ll
@@ -6,7 +6,7 @@ define void @matching_store_metadata(ptr %p, i32 %a, i32 %b, i1 %cond) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    store i32 [[A:%.*]], ptr [[P:%.*]], align 4, !tbaa [[TBAA0:![0-9]+]], !alias.scope [[META3:![0-9]+]], !noalias [[META6:![0-9]+]]
 ; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
-; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4, !tbaa [[TBAA0]], !alias.scope [[META3]], !noalias [[META6]]
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -46,7 +46,7 @@ define void @different_store_metadata(ptr %p, i32 %a, i32 %b, i1 %cond) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    store i32 [[A:%.*]], ptr [[P:%.*]], align 4, !tbaa [[TBAA0]], !alias.scope [[META3]], !noalias [[META6]]
 ; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
-; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4, !noalias [[META9:![0-9]+]]
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -68,7 +68,7 @@ define i32 @matching_load_store_metadata(i32 %a0, i32 %b, i1 %cond) {
 ; CHECK-NEXT:    store i32 [[A0:%.*]], ptr [[P]], align 4
 ; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[P]], align 4, !tbaa [[TBAA0]]
 ; CHECK-NEXT:    [[SPEC_STORE_SELECT:%.*]] = select i1 [[COND:%.*]], i32 [[B:%.*]], i32 [[A]]
-; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4
+; CHECK-NEXT:    store i32 [[SPEC_STORE_SELECT]], ptr [[P]], align 4, !tbaa [[TBAA0]]
 ; CHECK-NEXT:    [[R:%.*]] = load i32, ptr [[P]], align 4
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
@@ -116,4 +116,5 @@ exit:
 ; CHECK: [[META6]] = !{[[META7:![0-9]+]]}
 ; CHECK: [[META7]] = distinct !{[[META7]], [[META8:![0-9]+]], !"noalias.a"}
 ; CHECK: [[META8]] = distinct !{[[META8]]}
+; CHECK: [[META9]] = !{}
 ;.

>From cbc22113347d8e183f58ab681503174238bee2ee Mon Sep 17 00:00:00 2001
From: Hari Limaye <hari.limaye at arm.com>
Date: Tue, 29 Sep 2026 14:09:20 +0000
Subject: [PATCH 3/3] Use `AAMDNodes::merge()`

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 42a221036a4ff..848183595ec19 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3380,11 +3380,6 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(CondBrInst *BI,
   Value *BrCond = BI->getCondition();
   // Insert a select of the value of the speculated store.
   if (SpeculatedStoreValue) {
-    assert(PreviousStoreAccess && "Missing previous store access");
-    // The store will execute on both paths, so retain only AA metadata that is
-    // valid for both the original store and the access on the other path.
-    combineAAMetadata(SpeculatedStore, PreviousStoreAccess);
-
     IRBuilder<NoFolder> Builder(BI);
     Value *OrigV = SpeculatedStore->getValueOperand();
     Value *TrueV = SpeculatedStore->getValueOperand();
@@ -3439,18 +3434,11 @@ bool SimplifyCFGOpt::speculativelyExecuteBB(CondBrInst *BI,
       I.dropLocation();
       I.dropUBImplyingAttrsAndMetadata();
     } else {
-      // combineAAMetadata() made these safe on both paths above. Keep the
-      // resulting intersection while dropping all other UB-implying metadata.
-      static constexpr unsigned AAMetadata[] = {
-          LLVMContext::MD_tbaa,
-          LLVMContext::MD_tbaa_struct,
-          LLVMContext::MD_alias_scope,
-          LLVMContext::MD_noalias,
-          LLVMContext::MD_mem_parallel_loop_access,
-          LLVMContext::MD_access_group,
-          LLVMContext::MD_noalias_addrspace,
-      };
-      I.dropUBImplyingAttrsAndMetadata(AAMetadata);
+      assert(PreviousStoreAccess && "Missing previous store access");
+      AAMDNodes MergedAA = SpeculatedStore->getAAMetadata().merge(
+          PreviousStoreAccess->getAAMetadata());
+      I.dropUBImplyingAttrsAndMetadata();
+      I.setAAMetadata(MergedAA);
     }
 
     // Drop ephemeral values.



More information about the llvm-commits mailing list