[llvm] [BasicAA] Preserve constant offset correlations through select indices (PR #228778)
Dávid Bolvanský via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 3 15:33:11 PDT 2026
https://github.com/davidbolvansky updated https://github.com/llvm/llvm-project/pull/228778
>From 9fcc3f729efe6dbe6183bf24d2d6c9c1c3d9eb48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1vid=20Bolvansk=C3=BD?= <david.bolvansky at gmail.com>
Date: Sat, 3 Oct 2026 23:06:24 +0200
Subject: [PATCH 1/3] [BasicAA] Check constant offsets in select index
alternatives
---
llvm/lib/Analysis/BasicAliasAnalysis.cpp | 32 +++++++++++++++++++
.../test/Analysis/BasicAA/non-equal-select.ll | 3 +-
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 3aece3ca7d76953..d6eaa2aa4dda765 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -2107,6 +2107,38 @@ bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
Var0.Val.V->getType() != Var1.Val.V->getType())
return false;
+ // Without extensions, matching linear expressions have an exact modular
+ // difference. Check each select alternative before losing that correlation.
+ if (!AAQI.MayBeCrossIteration && !Var0.Val.ZExtBits && !Var0.Val.SExtBits &&
+ Var0.Val.getBitWidth() == GEP.Offset.getBitWidth() &&
+ (isa<SelectInst>(Var0.Val.V) || isa<SelectInst>(Var1.Val.V))) {
+ unsigned Budget = 8;
+ auto Check = [&](auto &&Check, const Value *V0, const Value *V1) -> bool {
+ if (!Budget)
+ return false;
+ --Budget;
+ LinearExpression E0 = GetLinearExpression(CastedValue(V0), DL, 0, AC, DT);
+ LinearExpression E1 = GetLinearExpression(CastedValue(V1), DL, 0, AC, DT);
+ if (E0.Scale == E1.Scale && E0.Val.hasSameCastsAs(E1.Val) &&
+ isValueEqualInPotentialCycles(E0.Val.V, E1.Val.V, AAQI)) {
+ APInt Offset = (E0.Offset - E1.Offset) * Var0.Scale;
+ if (Var0.IsNegated)
+ Offset = -Offset;
+ Offset += GEP.Offset;
+ return Offset.uge(V2Size) && (-Offset).uge(V1Size);
+ }
+ if (const auto *SI = dyn_cast<SelectInst>(V0))
+ return Check(Check, SI->getTrueValue(), V1) &&
+ Check(Check, SI->getFalseValue(), V1);
+ if (const auto *SI = dyn_cast<SelectInst>(V1))
+ return Check(Check, V0, SI->getTrueValue()) &&
+ Check(Check, V0, SI->getFalseValue());
+ return false;
+ };
+ if (Check(Check, Var0.Val.V, Var1.Val.V))
+ return true;
+ }
+
// We'll strip off the Extensions of Var0 and Var1 and do another round
// of GetLinearExpression decomposition. In the example above, if Var0
// is zext(%x + 1) we should get V1 == %x and V1Offset == 1.
diff --git a/llvm/test/Analysis/BasicAA/non-equal-select.ll b/llvm/test/Analysis/BasicAA/non-equal-select.ll
index fe38a57eeb1b477..76986fbf4e0e149 100644
--- a/llvm/test/Analysis/BasicAA/non-equal-select.ll
+++ b/llvm/test/Analysis/BasicAA/non-equal-select.ll
@@ -17,9 +17,8 @@ entry:
define void @select_in_gep2(i1 %c, i64 %x) {
entry:
- ; TODO: should be "NoAlias" here as well.
; CHECK-LABEL: Function: select_in_gep2
-; CHECK: MayAlias: i32* %arrayidx1, i32* %arrayidx2
+; CHECK: NoAlias: i32* %arrayidx1, i32* %arrayidx2
%add1_ = add nsw i64 %x, 1
%add2_ = add nsw i64 %x, 2
%add3_ = add nsw i64 %x, 3
>From c5609df0551134c4cdb5a0f8ce2b2cc62b0e46c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1vid=20Bolvansk=C3=BD?= <david.bolvansky at gmail.com>
Date: Sun, 4 Oct 2026 00:07:17 +0200
Subject: [PATCH 2/3] [BasicAA] Extend select offset proofs through integer
extensions
---
llvm/lib/Analysis/BasicAliasAnalysis.cpp | 28 +++++++----
.../test/Analysis/BasicAA/non-equal-select.ll | 48 +++++++++++++++++++
2 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index d6eaa2aa4dda765..aaca4e8d36eae63 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -2107,10 +2107,8 @@ bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
Var0.Val.V->getType() != Var1.Val.V->getType())
return false;
- // Without extensions, matching linear expressions have an exact modular
- // difference. Check each select alternative before losing that correlation.
- if (!AAQI.MayBeCrossIteration && !Var0.Val.ZExtBits && !Var0.Val.SExtBits &&
- Var0.Val.getBitWidth() == GEP.Offset.getBitWidth() &&
+ // Check each select alternative before losing its offset correlation.
+ if (!AAQI.MayBeCrossIteration &&
(isa<SelectInst>(Var0.Val.V) || isa<SelectInst>(Var1.Val.V))) {
unsigned Budget = 8;
auto Check = [&](auto &&Check, const Value *V0, const Value *V1) -> bool {
@@ -2121,11 +2119,23 @@ bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
LinearExpression E1 = GetLinearExpression(CastedValue(V1), DL, 0, AC, DT);
if (E0.Scale == E1.Scale && E0.Val.hasSameCastsAs(E1.Val) &&
isValueEqualInPotentialCycles(E0.Val.V, E1.Val.V, AAQI)) {
- APInt Offset = (E0.Offset - E1.Offset) * Var0.Scale;
- if (Var0.IsNegated)
- Offset = -Offset;
- Offset += GEP.Offset;
- return Offset.uge(V2Size) && (-Offset).uge(V1Size);
+ if (!Var0.Val.ZExtBits && !Var0.Val.SExtBits &&
+ Var0.Val.getBitWidth() == GEP.Offset.getBitWidth()) {
+ APInt Offset = (E0.Offset - E1.Offset) * Var0.Scale;
+ if (Var0.IsNegated)
+ Offset = -Offset;
+ Offset += GEP.Offset;
+ return Offset.uge(V2Size) && (-Offset).uge(V1Size);
+ }
+
+ // Extensions may change the direction of the difference on wrapping,
+ // but preserve its minimum absolute distance in the source type.
+ APInt Diff = E0.Offset - E1.Offset;
+ APInt MinDiff = APIntOps::umin(Diff, -Diff);
+ APInt MinDiffBytes =
+ MinDiff.zextOrTrunc(Var0.Scale.getBitWidth()) * Var0.Scale.abs();
+ return MinDiffBytes.uge(V1Size + GEP.Offset.abs()) &&
+ MinDiffBytes.uge(V2Size + GEP.Offset.abs());
}
if (const auto *SI = dyn_cast<SelectInst>(V0))
return Check(Check, SI->getTrueValue(), V1) &&
diff --git a/llvm/test/Analysis/BasicAA/non-equal-select.ll b/llvm/test/Analysis/BasicAA/non-equal-select.ll
index 76986fbf4e0e149..b747d926234502a 100644
--- a/llvm/test/Analysis/BasicAA/non-equal-select.ll
+++ b/llvm/test/Analysis/BasicAA/non-equal-select.ll
@@ -76,3 +76,51 @@ entry:
store i32 43, ptr %arrayidx2, align 4
ret void
}
+
+; CHECK-LABEL: Function: select_offsets_zext
+; CHECK: NoAlias: i32* %p, i32* %q
+define void @select_offsets_zext(ptr %base, i1 %c, i32 %x) {
+ %a = add i32 %x, 1
+ %b = add i32 %x, 2
+ %d = add i32 %x, 3
+ %s = select i1 %c, i32 %a, i32 %b
+ %i = zext i32 %s to i64
+ %j = zext i32 %d to i64
+ %p = getelementptr i32, ptr %base, i64 %i
+ %q = getelementptr i32, ptr %base, i64 %j
+ store i32 0, ptr %p
+ store i32 0, ptr %q
+ ret void
+}
+
+; CHECK-LABEL: Function: select_offsets_sext
+; CHECK: NoAlias: i32* %p, i32* %q
+define void @select_offsets_sext(ptr %base, i1 %c, i32 %x) {
+ %a = add i32 %x, 1
+ %b = add i32 %x, 2
+ %d = add i32 %x, 3
+ %s = select i1 %c, i32 %a, i32 %b
+ %i = sext i32 %s to i64
+ %j = sext i32 %d to i64
+ %p = getelementptr i32, ptr %base, i64 %i
+ %q = getelementptr i32, ptr %base, i64 %j
+ store i32 0, ptr %p
+ store i32 0, ptr %q
+ ret void
+}
+
+; CHECK-LABEL: Function: select_offsets_zext_overlap
+; CHECK: MayAlias: i32* %p, i32* %q
+define void @select_offsets_zext_overlap(ptr %base, i1 %c, i32 %x) {
+ %a = add i32 %x, 1
+ %b = add i32 %x, 3
+ %d = add i32 %x, 3
+ %s = select i1 %c, i32 %a, i32 %b
+ %i = zext i32 %s to i64
+ %j = zext i32 %d to i64
+ %p = getelementptr i32, ptr %base, i64 %i
+ %q = getelementptr i32, ptr %base, i64 %j
+ store i32 0, ptr %p
+ store i32 0, ptr %q
+ ret void
+}
>From 5b9c43f90cb3951b5eaa921486f03f0292ca079e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1vid=20Bolvansk=C3=BD?= <david.bolvansky at gmail.com>
Date: Sun, 4 Oct 2026 00:17:00 +0200
Subject: [PATCH 3/3] [BasicAA] Exercise extended select offsets with a
residual byte offset
---
.../test/Analysis/BasicAA/non-equal-select.ll | 36 ++++++++++---------
1 file changed, 20 insertions(+), 16 deletions(-)
diff --git a/llvm/test/Analysis/BasicAA/non-equal-select.ll b/llvm/test/Analysis/BasicAA/non-equal-select.ll
index b747d926234502a..fa763170ad62416 100644
--- a/llvm/test/Analysis/BasicAA/non-equal-select.ll
+++ b/llvm/test/Analysis/BasicAA/non-equal-select.ll
@@ -77,35 +77,38 @@ entry:
ret void
}
-; CHECK-LABEL: Function: select_offsets_zext
+
+; CHECK-LABEL: Function: select_offsets_zext_gap
; CHECK: NoAlias: i32* %p, i32* %q
-define void @select_offsets_zext(ptr %base, i1 %c, i32 %x) {
+define void @select_offsets_zext_gap(ptr %base, i1 %c, i32 %x) {
%a = add i32 %x, 1
%b = add i32 %x, 2
- %d = add i32 %x, 3
+ %d = add i32 %x, 5
%s = select i1 %c, i32 %a, i32 %b
%i = zext i32 %s to i64
%j = zext i32 %d to i64
%p = getelementptr i32, ptr %base, i64 %i
- %q = getelementptr i32, ptr %base, i64 %j
- store i32 0, ptr %p
- store i32 0, ptr %q
+ %qbase = getelementptr i32, ptr %base, i64 %j
+ %q = getelementptr i8, ptr %qbase, i64 1
+ store i32 0, ptr %p, align 1
+ store i32 0, ptr %q, align 1
ret void
}
-; CHECK-LABEL: Function: select_offsets_sext
+; CHECK-LABEL: Function: select_offsets_sext_gap
; CHECK: NoAlias: i32* %p, i32* %q
-define void @select_offsets_sext(ptr %base, i1 %c, i32 %x) {
+define void @select_offsets_sext_gap(ptr %base, i1 %c, i32 %x) {
%a = add i32 %x, 1
%b = add i32 %x, 2
- %d = add i32 %x, 3
+ %d = add i32 %x, 5
%s = select i1 %c, i32 %a, i32 %b
%i = sext i32 %s to i64
%j = sext i32 %d to i64
%p = getelementptr i32, ptr %base, i64 %i
- %q = getelementptr i32, ptr %base, i64 %j
- store i32 0, ptr %p
- store i32 0, ptr %q
+ %qbase = getelementptr i32, ptr %base, i64 %j
+ %q = getelementptr i8, ptr %qbase, i64 1
+ store i32 0, ptr %p, align 1
+ store i32 0, ptr %q, align 1
ret void
}
@@ -113,14 +116,15 @@ define void @select_offsets_sext(ptr %base, i1 %c, i32 %x) {
; CHECK: MayAlias: i32* %p, i32* %q
define void @select_offsets_zext_overlap(ptr %base, i1 %c, i32 %x) {
%a = add i32 %x, 1
- %b = add i32 %x, 3
+ %b = add i32 %x, 2
%d = add i32 %x, 3
%s = select i1 %c, i32 %a, i32 %b
%i = zext i32 %s to i64
%j = zext i32 %d to i64
%p = getelementptr i32, ptr %base, i64 %i
- %q = getelementptr i32, ptr %base, i64 %j
- store i32 0, ptr %p
- store i32 0, ptr %q
+ %qbase = getelementptr i32, ptr %base, i64 %j
+ %q = getelementptr i8, ptr %qbase, i64 -1
+ store i32 0, ptr %p, align 1
+ store i32 0, ptr %q, align 1
ret void
}
More information about the llvm-commits
mailing list