[llvm] [BasicAA] Refactor offset-based heuristics in `aliasGEP`, unify style (NFC) (PR #218687)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 06:43:33 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Antonio Frighetto (antoniofrighetto)

<details>
<summary>Changes</summary>

Part of the offset-based reasoning in `aliasGEP`, including GCD and minimum absolute heuristics, has been abstracted out into `BasicAAResult` private methods.

Minor opportunity to modernize code style where possible.

---
Full diff: https://github.com/llvm/llvm-project/pull/218687.diff


2 Files Affected:

- (modified) llvm/include/llvm/Analysis/BasicAliasAnalysis.h (+23-3) 
- (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+141-119) 


``````````diff
diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
index e42792548e5eb..da81bdec88f45 100644
--- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
@@ -109,6 +109,12 @@ class BasicAAResult : public AAResultBase {
 private:
   struct DecomposedGEP;
 
+  /// Results of analyzing variable GEP indices for offset-based disambiguation.
+  struct VariableGEPOffsetInfo {
+    APInt GCD;
+    ConstantRange OffsetRange;
+  };
+
   /// Tracks instructions visited by pointsToConstantMemory.
   SmallPtrSet<const Value *, 16> Visited;
 
@@ -116,6 +122,19 @@ class BasicAAResult : public AAResultBase {
   DecomposeGEPExpression(const Value *V, const DataLayout &DL,
                          AssumptionCache *AC, DominatorTree *DT);
 
+  /// Analyze the variable indices of a decomposed GEP, computing the GCD
+  /// that each Scale*V term is a multiple of, and an approximate range of
+  /// possible total offsets.
+  VariableGEPOffsetInfo analyzeVariableOffsets(const DecomposedGEP &GEP,
+                                               DominatorTree *DT);
+
+  /// Try to determine the range of values for VarIndex such that
+  /// VarIndex <= -MinAbsVarIndex || MinAbsVarIndex <= VarIndex, thus
+  /// establishing a minimum absolute value of the variable offset.
+  std::optional<APInt> computeMinAbsVarIndexHeuristic(const DecomposedGEP &GEP,
+                                                      DominatorTree *DT,
+                                                      const AAQueryInfo &AAQI);
+
   /// A Heuristic for aliasGEP that searches for a constant offset
   /// between the variables.
   ///
@@ -124,9 +143,10 @@ class BasicAAResult : public AAResultBase {
   /// will therefore conservatively refuse to decompose these expressions.
   /// However, we know that, for all %x, zext(%x) != zext(%x + 1), even if
   /// the addition overflows.
-  bool constantOffsetHeuristic(const DecomposedGEP &GEP, LocationSize V1Size,
-                               LocationSize V2Size, AssumptionCache *AC,
-                               DominatorTree *DT, const AAQueryInfo &AAQI);
+  bool computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
+                                      LocationSize V1Size, LocationSize V2Size,
+                                      AssumptionCache *AC, DominatorTree *DT,
+                                      const AAQueryInfo &AAQI);
 
   bool isValueEqualInPotentialCycles(const Value *V1, const Value *V2,
                                      const AAQueryInfo &AAQI);
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index e33fde77cf365..95255b4045ee3 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -1152,8 +1152,7 @@ AliasResult BasicAAResult::aliasGEP(
   // If an inbounds GEP would have to start from an out of bounds address
   // for the two to alias, then we can assume noalias.
   // TODO: Remove !isScalable() once BasicAA fully support scalable location
-  // size
-
+  // size.
   if (DecompGEP1.NWFlags.isInBounds() && DecompGEP1.VarIndices.empty() &&
       V2Size.hasValue() && !V2Size.isScalable() &&
       DecompGEP1.Offset.sge(V2Size.getValue()) &&
@@ -1229,15 +1228,15 @@ AliasResult BasicAAResult::aliasGEP(
         return AR;
       }
       return AliasResult::NoAlias;
-    } else {
-      // We can use the getVScaleRange to prove that Off >= (CR.upper * LSize).
-      ConstantRange CR = getVScaleRange(&F, Off.getBitWidth());
-      bool Overflow;
-      APInt UpperRange = CR.getUnsignedMax().umul_ov(
-          APInt(Off.getBitWidth(), LSize.getKnownMinValue()), Overflow);
-      if (!Overflow && Off.uge(UpperRange))
-        return AliasResult::NoAlias;
     }
+
+    // We can use the getVScaleRange to prove that Off >= (CR.upper * LSize).
+    ConstantRange CR = getVScaleRange(&F, Off.getBitWidth());
+    bool Overflow;
+    APInt UpperRange = CR.getUnsignedMax().umul_ov(
+        APInt(Off.getBitWidth(), LSize.getKnownMinValue()), Overflow);
+    if (!Overflow && Off.uge(UpperRange))
+      return AliasResult::NoAlias;
   }
 
   // VScale Alias Analysis - Given one scalable offset between accesses and a
@@ -1285,7 +1284,7 @@ AliasResult BasicAAResult::aliasGEP(
       !V2Size.isScalable() && DecompGEP1.Offset.uge(V2Size.getValue()))
     return AliasResult::NoAlias;
 
-  // Bail on analysing scalable LocationSize
+  // Bail on analyzing scalable LocationSize.
   if (V1Size.isScalable() || V2Size.isScalable())
     return AliasResult::MayAlias;
 
@@ -1296,55 +1295,10 @@ AliasResult BasicAAResult::aliasGEP(
       !isUIntN(BW, V1Size.getValue()) || !isUIntN(BW, V2Size.getValue()))
     return AliasResult::MayAlias;
 
-  APInt GCD;
-  ConstantRange OffsetRange = ConstantRange(DecompGEP1.Offset);
-  for (unsigned i = 0, e = DecompGEP1.VarIndices.size(); i != e; ++i) {
-    const VariableGEPIndex &Index = DecompGEP1.VarIndices[i];
-    const APInt &Scale = Index.Scale;
-
-    SimplifyQuery SQ(DL, DT, &AC, Index.CxtI, /*UseInstrInfo=*/true);
-    KnownBits Known = computeKnownBits(Index.Val.V, SQ);
-
-    APInt ScaleForGCD = Scale;
-    if (!Index.IsNSW)
-      ScaleForGCD =
-          APInt::getOneBitSet(Scale.getBitWidth(), Scale.countr_zero());
-
-    // If V has known trailing zeros, V is a multiple of 2^VarTZ, so
-    // V*Scale is a multiple of ScaleForGCD * 2^VarTZ. Shift ScaleForGCD
-    // left to account for this (trailing zeros compose additively through
-    // multiplication, even in Z/2^n).
-    unsigned VarTZ = Known.countMinTrailingZeros();
-    if (VarTZ > 0) {
-      unsigned MaxShift =
-          Scale.getBitWidth() - ScaleForGCD.getSignificantBits();
-      ScaleForGCD <<= std::min(VarTZ, MaxShift);
-    }
-
-    if (i == 0)
-      GCD = ScaleForGCD.abs();
-    else
-      GCD = APIntOps::GreatestCommonDivisor(GCD, ScaleForGCD.abs());
-
-    ConstantRange CR =
-        computeConstantRange(Index.Val.V, /*ForSigned=*/false, SQ);
-    CR = CR.intersectWith(
-        ConstantRange::fromKnownBits(Known, /* Signed */ true),
-        ConstantRange::Signed);
-    CR = Index.Val.evaluateWith(CR).sextOrTrunc(OffsetRange.getBitWidth());
-
-    assert(OffsetRange.getBitWidth() == Scale.getBitWidth() &&
-           "Bit widths are normalized to MaxIndexSize");
-    if (Index.IsNSW)
-      CR = CR.smul_sat(ConstantRange(Scale));
-    else
-      CR = CR.smul_fast(ConstantRange(Scale));
-
-    if (Index.IsNegated)
-      OffsetRange = OffsetRange.sub(CR);
-    else
-      OffsetRange = OffsetRange.add(CR);
-  }
+  // Analyze the variable indices, and compute the GCD that the total
+  // variable offset is guaranteed to be a multiple of, and its approximate
+  // range.
+  auto [GCD, OffsetRange] = analyzeVariableOffsets(DecompGEP1, DT);
 
   // We now have accesses at two offsets from the same base:
   //  1. (...)*GCD + DecompGEP1.Offset with size V1Size
@@ -1359,8 +1313,8 @@ AliasResult BasicAAResult::aliasGEP(
       (GCD - ModOffset).uge(V1Size.getValue()))
     return AliasResult::NoAlias;
 
-  // Compute ranges of potentially accessed bytes for both accesses. If the
-  // interseciton is empty, there can be no overlap.
+  // If the ranges of potentially accessed bytes are disjoint, there cannot be
+  // any overlap.
   ConstantRange Range1 = OffsetRange.add(
       ConstantRange(APInt(BW, 0), APInt(BW, V1Size.getValue())));
   ConstantRange Range2 =
@@ -1368,56 +1322,10 @@ AliasResult BasicAAResult::aliasGEP(
   if (Range1.intersectWith(Range2).isEmptySet())
     return AliasResult::NoAlias;
 
-  // Check if abs(V*Scale) >= abs(Scale) holds in the presence of
-  // potentially wrapping math.
-  auto MultiplyByScaleNoWrap = [](const VariableGEPIndex &Var) {
-    if (Var.IsNSW)
-      return true;
-
-    int ValOrigBW = Var.Val.V->getType()->getPrimitiveSizeInBits();
-    // If Scale is small enough so that abs(V*Scale) >= abs(Scale) holds.
-    // The max value of abs(V) is 2^ValOrigBW - 1. Multiplying with a
-    // constant smaller than 2^(bitwidth(Val) - ValOrigBW) won't wrap.
-    int MaxScaleValueBW = Var.Val.getBitWidth() - ValOrigBW;
-    if (MaxScaleValueBW <= 0)
-      return false;
-    return Var.Scale.ule(
-        APInt::getMaxValue(MaxScaleValueBW).zext(Var.Scale.getBitWidth()));
-  };
-
-  // Try to determine the range of values for VarIndex such that
-  // VarIndex <= -MinAbsVarIndex || MinAbsVarIndex <= VarIndex.
-  std::optional<APInt> MinAbsVarIndex;
-  if (DecompGEP1.VarIndices.size() == 1) {
-    // VarIndex = Scale*V.
-    const VariableGEPIndex &Var = DecompGEP1.VarIndices[0];
-    if (Var.Val.TruncBits == 0 &&
-        isKnownNonZero(Var.Val.V, SimplifyQuery(DL, DT, &AC, Var.CxtI))) {
-      // Refine MinAbsVarIndex, if abs(Scale*V) >= abs(Scale) holds in the
-      // presence of potentially wrapping math.
-      if (MultiplyByScaleNoWrap(Var)) {
-        // If V != 0 then abs(VarIndex) >= abs(Scale).
-        MinAbsVarIndex = Var.Scale.abs();
-      }
-    }
-  } else if (DecompGEP1.VarIndices.size() == 2) {
-    // VarIndex = Scale*V0 + (-Scale)*V1.
-    // If V0 != V1 then abs(VarIndex) >= abs(Scale).
-    // Check that MayBeCrossIteration is false, to avoid reasoning about
-    // inequality of values across loop iterations.
-    const VariableGEPIndex &Var0 = DecompGEP1.VarIndices[0];
-    const VariableGEPIndex &Var1 = DecompGEP1.VarIndices[1];
-    if (Var0.hasNegatedScaleOf(Var1) && Var0.Val.TruncBits == 0 &&
-        Var0.Val.hasSameCastsAs(Var1.Val) && !AAQI.MayBeCrossIteration &&
-        MultiplyByScaleNoWrap(Var0) && MultiplyByScaleNoWrap(Var1) &&
-        isKnownNonEqual(Var0.Val.V, Var1.Val.V,
-                        SimplifyQuery(DL, DT, &AC, /*CxtI=*/Var0.CxtI
-                                                       ? Var0.CxtI
-                                                       : Var1.CxtI)))
-      MinAbsVarIndex = Var0.Scale.abs();
-  }
-
-  if (MinAbsVarIndex) {
+  // If a minimum absolute variable offset can be established, employ it to
+  // prove that the two accesses are far enough apart.
+  if (auto MinAbsVarIndex =
+          computeMinAbsVarIndexHeuristic(DecompGEP1, DT, AAQI)) {
     // The constant offset will have added at least +/-MinAbsVarIndex to it.
     APInt OffsetLo = DecompGEP1.Offset - *MinAbsVarIndex;
     APInt OffsetHi = DecompGEP1.Offset + *MinAbsVarIndex;
@@ -1427,7 +1335,9 @@ AliasResult BasicAAResult::aliasGEP(
       return AliasResult::NoAlias;
   }
 
-  if (constantOffsetHeuristic(DecompGEP1, V1Size, V2Size, &AC, DT, AAQI))
+  // As a last attempt, search for a constant offset between the variable
+  // indices that GetLinearExpression could not extract through casts.
+  if (computeConstantOffsetHeuristic(DecompGEP1, V1Size, V2Size, &AC, DT, AAQI))
     return AliasResult::NoAlias;
 
   // Statically, we can see that the base objects are the same, but the
@@ -2005,12 +1915,124 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
   }
 }
 
-bool BasicAAResult::constantOffsetHeuristic(const DecomposedGEP &GEP,
-                                            LocationSize MaybeV1Size,
-                                            LocationSize MaybeV2Size,
-                                            AssumptionCache *AC,
-                                            DominatorTree *DT,
-                                            const AAQueryInfo &AAQI) {
+BasicAAResult::VariableGEPOffsetInfo
+BasicAAResult::analyzeVariableOffsets(const DecomposedGEP &GEP,
+                                      DominatorTree *DT) {
+  APInt GCD;
+  ConstantRange OffsetRange(GEP.Offset);
+
+  for (unsigned I = 0, E = GEP.VarIndices.size(); I != E; ++I) {
+    const VariableGEPIndex &Index = GEP.VarIndices[I];
+    const APInt &Scale = Index.Scale;
+
+    SimplifyQuery SQ(DL, DT, &AC, Index.CxtI, /*UseInstrInfo=*/true);
+    KnownBits Known = computeKnownBits(Index.Val.V, SQ);
+
+    APInt ScaleForGCD = Scale;
+    if (!Index.IsNSW)
+      ScaleForGCD =
+          APInt::getOneBitSet(Scale.getBitWidth(), Scale.countr_zero());
+
+    // If V has known trailing zeros, V is a multiple of 2^VarTZ, so
+    // V*Scale is a multiple of ScaleForGCD * 2^VarTZ. Shift ScaleForGCD
+    // left to account for this (trailing zeros compose additively through
+    // multiplication, even in Z/2^n).
+    unsigned VarTZ = Known.countMinTrailingZeros();
+    if (VarTZ > 0) {
+      unsigned MaxShift =
+          Scale.getBitWidth() - ScaleForGCD.getSignificantBits();
+      ScaleForGCD <<= std::min(VarTZ, MaxShift);
+    }
+
+    if (I == 0)
+      GCD = ScaleForGCD.abs();
+    else
+      GCD = APIntOps::GreatestCommonDivisor(GCD, ScaleForGCD.abs());
+
+    ConstantRange CR =
+        computeConstantRange(Index.Val.V, /*ForSigned=*/false, SQ);
+    CR =
+        CR.intersectWith(ConstantRange::fromKnownBits(Known, /*IsSigned=*/true),
+                         ConstantRange::Signed);
+    CR = Index.Val.evaluateWith(CR).sextOrTrunc(OffsetRange.getBitWidth());
+
+    assert(OffsetRange.getBitWidth() == Scale.getBitWidth() &&
+           "Bit widths are normalized to MaxIndexSize");
+    if (Index.IsNSW)
+      CR = CR.smul_sat(ConstantRange(Scale));
+    else
+      CR = CR.smul_fast(ConstantRange(Scale));
+
+    if (Index.IsNegated)
+      OffsetRange = OffsetRange.sub(CR);
+    else
+      OffsetRange = OffsetRange.add(CR);
+  }
+
+  return {GCD, OffsetRange};
+}
+
+std::optional<APInt> BasicAAResult::computeMinAbsVarIndexHeuristic(
+    const DecomposedGEP &GEP, DominatorTree *DT, const AAQueryInfo &AAQI) {
+  // Check if abs(V*Scale) >= abs(Scale) holds in the presence of
+  // potentially wrapping math.
+  auto MultiplyByScaleNoWrap = [](const VariableGEPIndex &Var) {
+    if (Var.IsNSW)
+      return true;
+
+    int ValOrigBW = Var.Val.V->getType()->getPrimitiveSizeInBits();
+    // If Scale is small enough so that abs(V*Scale) >= abs(Scale) holds.
+    // The max value of abs(V) is 2^ValOrigBW - 1. Multiplying with a
+    // constant smaller than 2^(bitwidth(Val) - ValOrigBW) won't wrap.
+    int MaxScaleValueBW = Var.Val.getBitWidth() - ValOrigBW;
+    if (MaxScaleValueBW <= 0)
+      return false;
+    return Var.Scale.ule(
+        APInt::getMaxValue(MaxScaleValueBW).zext(Var.Scale.getBitWidth()));
+  };
+
+  const auto &VarIndices = GEP.VarIndices;
+  if (VarIndices.size() == 1) {
+    // VarIndex = Scale*V.
+    const VariableGEPIndex &Var = VarIndices[0];
+    if (Var.Val.TruncBits == 0 &&
+        isKnownNonZero(Var.Val.V, SimplifyQuery(DL, DT, &AC, Var.CxtI))) {
+      // Refine MinAbsVarIndex, if abs(Scale*V) >= abs(Scale) holds in the
+      // presence of potentially wrapping math.
+      if (MultiplyByScaleNoWrap(Var)) {
+        // If V != 0 then abs(VarIndex) >= abs(Scale).
+        return Var.Scale.abs();
+      }
+    }
+    return std::nullopt;
+  }
+
+  if (VarIndices.size() == 2) {
+    // VarIndex = Scale*V0 + (-Scale)*V1.
+    // If V0 != V1 then abs(VarIndex) >= abs(Scale).
+    // Check that MayBeCrossIteration is false, to avoid reasoning about
+    // inequality of values across loop iterations.
+    const VariableGEPIndex &Var0 = VarIndices[0];
+    const VariableGEPIndex &Var1 = VarIndices[1];
+    if (Var0.hasNegatedScaleOf(Var1) && Var0.Val.TruncBits == 0 &&
+        Var0.Val.hasSameCastsAs(Var1.Val) && !AAQI.MayBeCrossIteration &&
+        MultiplyByScaleNoWrap(Var0) && MultiplyByScaleNoWrap(Var1) &&
+        isKnownNonEqual(Var0.Val.V, Var1.Val.V,
+                        SimplifyQuery(DL, DT, &AC, /*CxtI=*/Var0.CxtI
+                                                       ? Var0.CxtI
+                                                       : Var1.CxtI)))
+      return Var0.Scale.abs();
+  }
+
+  return std::nullopt;
+}
+
+bool BasicAAResult::computeConstantOffsetHeuristic(const DecomposedGEP &GEP,
+                                                   LocationSize MaybeV1Size,
+                                                   LocationSize MaybeV2Size,
+                                                   AssumptionCache *AC,
+                                                   DominatorTree *DT,
+                                                   const AAQueryInfo &AAQI) {
   if (GEP.VarIndices.size() != 2 || !MaybeV1Size.hasValue() ||
       !MaybeV2Size.hasValue())
     return false;

``````````

</details>


https://github.com/llvm/llvm-project/pull/218687


More information about the llvm-commits mailing list