[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