[llvm] [LICM] Ignore AA tags on conditional stores in scalar promotion. (PR #218031)
Eli Friedman via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 14:04:41 PDT 2026
https://github.com/efriedma-quic created https://github.com/llvm/llvm-project/pull/218031
One of the key aspects of scalar promotion, as done in LICM, is that the store is always unconditional: it executes even if the store in the loop didn't execute. This makes the aliasing check more complicated: we need to ensure that it's okay to both sink the store out of the loop, and speculatively execute the store.
For the sinking, it's fine to use AA tags, but for speculatively executing the store, it's a problem: AA tags are only meaningful for operations that actually execute at runtime. Existing code will strip the AA tags from the generated store, but still used them for the aliasing checks.
The new isStrongerThanMonotonic() checks don't affect correctness; this change is just moving the checks earlier because of the way the AliasSetTracker data structure works.
Fixes #60860. Fixes #217052.
>From a74e37a7c0e5b1627d2b4e87a50b016e5eb5a8b9 Mon Sep 17 00:00:00 2001
From: Eli Friedman <efriedma at qti.qualcomm.com>
Date: Fri, 21 Aug 2026 13:41:47 -0700
Subject: [PATCH] [LICM] Ignore AA tags on conditional stores in scalar
promotion.
One of the key aspects of scalar promotion, as done in LICM, is that the
store is always unconditional: it executes even if the store in the loop
didn't execute. This makes the aliasing check more complicated: we need
to ensure that it's okay to both sink the store out of the loop, and
speculatively execute the store.
For the sinking, it's fine to use AA tags, but for speculatively
executing the store, it's a problem: AA tags are only meaningful for
operations that actually execute at runtime. Existing code will strip
the AA tags from the generated store, but still used them for the
aliasing checks.
The new isStrongerThanMonotonic() checks don't affect correctness;
this change is just moving the checks earlier because of the way the
AliasSetTracker data structure works.
Fixes #60860. Fixes #217052.
---
llvm/include/llvm/Analysis/AliasSetTracker.h | 1 +
llvm/lib/Analysis/AliasSetTracker.cpp | 7 +++
llvm/lib/Transforms/Scalar/LICM.cpp | 31 +++++++++--
llvm/test/Transforms/LICM/scalar-promote.ll | 57 ++++++++++++++++++++
4 files changed, 92 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/Analysis/AliasSetTracker.h b/llvm/include/llvm/Analysis/AliasSetTracker.h
index 2f3418217e1b4..066245a75f51b 100644
--- a/llvm/include/llvm/Analysis/AliasSetTracker.h
+++ b/llvm/include/llvm/Analysis/AliasSetTracker.h
@@ -188,6 +188,7 @@ class AliasSetTracker {
LLVM_ABI void add(const MemoryLocation &Loc);
LLVM_ABI void add(LoadInst *LI);
LLVM_ABI void add(StoreInst *SI);
+ LLVM_ABI void addWithoutAATags(StoreInst *SI);
LLVM_ABI void add(VAArgInst *VAAI);
LLVM_ABI void add(AnyMemSetInst *MSI);
LLVM_ABI void add(AnyMemTransferInst *MTI);
diff --git a/llvm/lib/Analysis/AliasSetTracker.cpp b/llvm/lib/Analysis/AliasSetTracker.cpp
index 1e2f05b60a9a3..311d5485aa25e 100644
--- a/llvm/lib/Analysis/AliasSetTracker.cpp
+++ b/llvm/lib/Analysis/AliasSetTracker.cpp
@@ -329,6 +329,13 @@ void AliasSetTracker::add(StoreInst *SI) {
addMemoryLocation(MemoryLocation::get(SI), AliasSet::ModAccess);
}
+void AliasSetTracker::addWithoutAATags(StoreInst *SI) {
+ assert(!isStrongerThanMonotonic(SI->getOrdering()) &&
+ "Can't handle release stores here");
+ addMemoryLocation(MemoryLocation::get(SI).getWithoutAATags(),
+ AliasSet::ModAccess);
+}
+
void AliasSetTracker::add(VAArgInst *VAAI) {
addMemoryLocation(MemoryLocation::get(VAAI), AliasSet::ModRefAccess);
}
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index fed0b96ddc089..ee4c7603fdd2d 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -226,7 +226,9 @@ static void foreachMemoryAccess(MemorySSA *MSSA, Loop *L,
using PointersAndHasReadsOutsideSet =
std::pair<SmallSetVector<Value *, 8>, bool>;
static SmallVector<PointersAndHasReadsOutsideSet, 0>
-collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L);
+collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA,
+ DominatorTree *DT, ICFLoopSafetyInfo *SafetyInfo,
+ Loop *L);
namespace {
struct LoopInvariantCodeMotion {
@@ -520,7 +522,7 @@ bool LoopInvariantCodeMotion::runOnLoop(Loop *L, AAResults *AA, LoopInfo *LI,
do {
LocalPromoted = false;
for (auto [PointerMustAliases, HasReadsOutsideSet] :
- collectPromotionCandidates(MSSA, AA, L)) {
+ collectPromotionCandidates(MSSA, AA, DT, &SafetyInfo, L)) {
LocalPromoted |= promoteLoopAccessesToScalars(
PointerMustAliases, ExitBlocks, InsertPts, MSSAInsertPts, PIC, LI,
DT, AC, TLI, TTI, L, MSSAU, &SafetyInfo, ORE,
@@ -2335,17 +2337,23 @@ static void foreachMemoryAccess(MemorySSA *MSSA, Loop *L,
// The bool indicates whether there might be reads outside the set, in which
// case only loads may be promoted.
static SmallVector<PointersAndHasReadsOutsideSet, 0>
-collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L) {
+collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA,
+ DominatorTree *DT, ICFLoopSafetyInfo *SafetyInfo,
+ Loop *L) {
BatchAAResults BatchAA(*AA);
AliasSetTracker AST(BatchAA);
auto IsPotentiallyPromotable = [L](const Instruction *I) {
if (const auto *SI = dyn_cast<StoreInst>(I)) {
const Value *PtrOp = SI->getPointerOperand();
+ if (isStrongerThanMonotonic(SI->getOrdering()))
+ return false;
return !isa<ConstantData>(PtrOp) && L->isLoopInvariant(PtrOp);
}
if (const auto *LI = dyn_cast<LoadInst>(I)) {
const Value *PtrOp = LI->getPointerOperand();
+ if (isStrongerThanMonotonic(LI->getOrdering()))
+ return false;
return !isa<ConstantData>(PtrOp) && L->isLoopInvariant(PtrOp);
}
return false;
@@ -2356,7 +2364,22 @@ collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L) {
foreachMemoryAccess(MSSA, L, [&](Instruction *I) {
if (IsPotentiallyPromotable(I)) {
AttemptingPromotion.insert(I);
- AST.add(I);
+ if (StoreInst *SI = dyn_cast<StoreInst>(I);
+ SI && !SafetyInfo->isGuaranteedToExecute(*SI, DT, L)) {
+ // Promotion requires inserting a new store at the loop exits; we need
+ // to prove that store doesn't alias anything, in addition to proving
+ // aliasing for the stores we're removing. The new store is executed
+ // unconditionally, so when we're proving aliasing for that store, we
+ // can't rely on AA tags for stores which are conditionally executed.
+ //
+ // As a future improvement, we could avoid stripping AA tags in more
+ // cases. isGuaranteedToExecute() is stronger than what we need.
+ // We only need to prove that every exit from the loop is dominated
+ // by a store to the same location with the same AA tag.
+ AST.addWithoutAATags(SI);
+ } else {
+ AST.add(I);
+ }
}
});
diff --git a/llvm/test/Transforms/LICM/scalar-promote.ll b/llvm/test/Transforms/LICM/scalar-promote.ll
index ee25ba90c214e..be57f3072c6b9 100644
--- a/llvm/test/Transforms/LICM/scalar-promote.ll
+++ b/llvm/test/Transforms/LICM/scalar-promote.ll
@@ -968,12 +968,66 @@ exit:
ret void
}
+define i32 @noalias_metadata(i1 %c, i1 %c2, i64 %idx) {
+; CHECK-LABEL: define i32 @noalias_metadata(
+; CHECK-SAME: i1 [[C:%.*]], i1 [[C2:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[PTR:%.*]] = alloca [2 x i32], align 4
+; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[IDX]]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[ELSE:.*]]
+; CHECK: [[IF]]:
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[PTR]], align 4, !noalias [[META6:![0-9]+]]
+; CHECK-NEXT: [[V_INC:%.*]] = add i32 [[V]], 1
+; CHECK-NEXT: store i32 [[V_INC]], ptr [[PTR]], align 4, !noalias [[META6]]
+; CHECK-NEXT: br label %[[LATCH:.*]]
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: store i32 42, ptr [[PTR2]], align 4, !alias.scope [[META6]]
+; CHECK-NEXT: br label %[[LATCH]]
+; CHECK: [[LATCH]]:
+; CHECK-NEXT: br i1 [[C2]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[RES:%.*]] = load i32, ptr [[PTR]], align 4
+; CHECK-NEXT: ret i32 [[RES]]
+;
+entry:
+ %ptr = alloca [2 x i32]
+ %ptr2 = getelementptr i32, ptr %ptr, i64 %idx
+ br label %loop
+
+loop:
+ br i1 %c, label %if, label %else
+
+if:
+ %v = load i32, ptr %ptr, !noalias !8
+ %v.inc = add i32 %v, 1
+ store i32 %v.inc, ptr %ptr, !noalias !8
+ br label %latch
+
+else:
+ store i32 42, ptr %ptr2, !alias.scope !8
+ br label %latch
+
+latch:
+ br i1 %c2, label %exit, label %loop
+
+exit:
+ %res = load i32, ptr %ptr
+ ret i32 %res
+}
+
!0 = !{!4, !4, i64 0}
!1 = !{!"omnipotent char", !2}
!2 = !{!"Simple C/C++ TBAA"}
!3 = !{!5, !5, i64 0}
!4 = !{!"int", !1}
!5 = !{!"float", !1}
+!6 = !{!6}
+!7 = !{!7, !6}
+!8 = !{!7}
+
+;
;.
; CHECK: [[INT_TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
; CHECK: [[META1]] = !{!"int", [[META2:![0-9]+]]}
@@ -981,4 +1035,7 @@ exit:
; CHECK: [[META3]] = !{!"Simple C/C++ TBAA"}
; CHECK: [[FLOAT_TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0}
; CHECK: [[META5]] = !{!"float", [[META2]]}
+; CHECK: [[META6]] = !{[[META7:![0-9]+]]}
+; CHECK: [[META7]] = distinct !{[[META7]], [[META8:![0-9]+]]}
+; CHECK: [[META8]] = distinct !{[[META8]]}
;.
More information about the llvm-commits
mailing list