[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:40:20 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/4] [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 3aece3ca7d7695..d6eaa2aa4dda76 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 fe38a57eeb1b47..76986fbf4e0e14 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/4] [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 d6eaa2aa4dda76..aaca4e8d36eae6 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 76986fbf4e0e14..b747d926234502 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/4] [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 b747d926234502..fa763170ad6241 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
 }

>From de0f01200e0c661c337e570d37f63d1ef734aa24 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:40:05 +0200
Subject: [PATCH 4/4] [BasicAA] Preserve select offset differences through
 index truncation

---
 llvm/lib/Analysis/BasicAliasAnalysis.cpp      | 12 ++++---
 .../test/Analysis/BasicAA/non-equal-select.ll | 33 ++++++++++++++++++-
 2 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index aaca4e8d36eae6..ab8fe818f6d991 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -2102,8 +2102,7 @@ bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
 
   const VariableGEPIndex &Var0 = GEP.VarIndices[0], &Var1 = GEP.VarIndices[1];
 
-  if (Var0.Val.TruncBits != 0 || !Var0.Val.hasSameCastsAs(Var1.Val) ||
-      !Var0.hasNegatedScaleOf(Var1) ||
+  if (!Var0.Val.hasSameCastsAs(Var1.Val) || !Var0.hasNegatedScaleOf(Var1) ||
       Var0.Val.V->getType() != Var1.Val.V->getType())
     return false;
 
@@ -2119,9 +2118,12 @@ 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 Diff = E0.Offset - E1.Offset;
+        if (Var0.Val.TruncBits)
+          Diff = Diff.trunc(Diff.getBitWidth() - Var0.Val.TruncBits);
         if (!Var0.Val.ZExtBits && !Var0.Val.SExtBits &&
             Var0.Val.getBitWidth() == GEP.Offset.getBitWidth()) {
-          APInt Offset = (E0.Offset - E1.Offset) * Var0.Scale;
+          APInt Offset = Diff * Var0.Scale;
           if (Var0.IsNegated)
             Offset = -Offset;
           Offset += GEP.Offset;
@@ -2130,7 +2132,6 @@ bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
 
         // 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();
@@ -2149,6 +2150,9 @@ bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
       return true;
   }
 
+  if (Var0.Val.TruncBits)
+    return false;
+
   // 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 fa763170ad6241..0642ef159015bd 100644
--- a/llvm/test/Analysis/BasicAA/non-equal-select.ll
+++ b/llvm/test/Analysis/BasicAA/non-equal-select.ll
@@ -1,4 +1,6 @@
 ; RUN: opt < %s -aa-pipeline=basic-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
+target datalayout = "e-p:64:64-p1:64:64:64:32"
+
 @G = global [10 x i32] zeroinitializer, align 4
 
 define void @select_in_gep1(i1 %c, i64 %x) {
@@ -77,7 +79,6 @@ entry:
   ret void
 }
 
-
 ; CHECK-LABEL: Function: select_offsets_zext_gap
 ; CHECK: NoAlias: i32* %p, i32* %q
 define void @select_offsets_zext_gap(ptr %base, i1 %c, i32 %x) {
@@ -128,3 +129,33 @@ define void @select_offsets_zext_overlap(ptr %base, i1 %c, i32 %x) {
   store i32 0, ptr %q, align 1
   ret void
 }
+
+; CHECK-LABEL: Function: select_offsets_trunc_gap
+; CHECK: NoAlias: i32 addrspace(1)* %p, i32 addrspace(1)* %q
+define void @select_offsets_trunc_gap(ptr addrspace(1) %base, i1 %c, i64 %x) {
+  %a = add i64 %x, 1
+  %b = add i64 %x, 2
+  %d = add i64 %x, 5
+  %s = select i1 %c, i64 %a, i64 %b
+  %p = getelementptr i32, ptr addrspace(1) %base, i64 %s
+  %qbase = getelementptr i32, ptr addrspace(1) %base, i64 %d
+  %q = getelementptr i8, ptr addrspace(1) %qbase, i64 1
+  store i32 0, ptr addrspace(1) %p, align 1
+  store i32 0, ptr addrspace(1) %q, align 1
+  ret void
+}
+
+; CHECK-LABEL: Function: select_offsets_trunc_wrap
+; CHECK: MayAlias: i32 addrspace(1)* %p, i32 addrspace(1)* %q
+define void @select_offsets_trunc_wrap(ptr addrspace(1) %base, i1 %c, i64 %x) {
+  %a = add i64 %x, 1
+  %b = add i64 %x, 2
+  %d = add i64 %x, 4294967297
+  %s = select i1 %c, i64 %a, i64 %b
+  %p = getelementptr i32, ptr addrspace(1) %base, i64 %s
+  %qbase = getelementptr i32, ptr addrspace(1) %base, i64 %d
+  %q = getelementptr i8, ptr addrspace(1) %qbase, i64 0
+  store i32 0, ptr addrspace(1) %p, align 1
+  store i32 0, ptr addrspace(1) %q, align 1
+  ret void
+}



More information about the llvm-commits mailing list