[llvm] 6e96783 - [AA] Cache (optionally) estimated PartialAlias offsets.

via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 2 08:04:31 PST 2021


Author: dfukalov
Date: 2021-03-02T19:04:15+03:00
New Revision: 6e967834b9fa17ae5361651c15c969a9c7331eb2

URL: https://github.com/llvm/llvm-project/commit/6e967834b9fa17ae5361651c15c969a9c7331eb2
DIFF: https://github.com/llvm/llvm-project/commit/6e967834b9fa17ae5361651c15c969a9c7331eb2.diff

LOG: [AA] Cache (optionally) estimated PartialAlias offsets.

For the cases of two clobbering loads and one loaded object is fully contained
in the second `BasicAAResult::aliasGEP` returns just `PartialAlias` that
is actually more common case of partial overlap, it doesn't say anything about
actual overlapping sizes.

AA users such as GVN and DSE have no functionality to estimate aliasing of GEPs
with non-constant offsets. The change stores estimated relative offsets so they
can be used further.

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D93529

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/AliasAnalysis.h
    llvm/lib/Analysis/AliasAnalysis.cpp
    llvm/lib/Analysis/BasicAliasAnalysis.cpp
    llvm/unittests/Analysis/AliasAnalysisTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index 59963bcde6f9..3910966fda7b 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -344,6 +344,16 @@ createModRefInfo(const FunctionModRefBehavior FMRB) {
 /// The information stored in an `AAQueryInfo` is currently limitted to the
 /// caches used by BasicAA, but can further be extended to fit other AA needs.
 class AAQueryInfo {
+  /// Storage for estimated relative offsets between two partially aliased
+  /// values. Used to optimize out redundant parts of loads/stores (in GVN/DSE).
+  /// These users cannot process quite complicated addresses (e.g. GEPs with
+  /// non-constant offsets). Used by BatchAAResults only.
+  bool CacheOffsets = false;
+  SmallDenseMap<std::pair<std::pair<const Value *, const Value *>,
+                          std::pair<uint64_t, uint64_t>>,
+                int64_t, 4>
+      ClobberOffsets;
+
 public:
   using LocPair = std::pair<MemoryLocation, MemoryLocation>;
   struct CacheEntry {
@@ -371,7 +381,9 @@ class AAQueryInfo {
   /// assumption is disproven.
   SmallVector<AAQueryInfo::LocPair, 4> AssumptionBasedResults;
 
-  AAQueryInfo() : AliasCache(), IsCapturedCache() {}
+  AAQueryInfo(bool CacheOffsets = false)
+      : CacheOffsets(CacheOffsets), ClobberOffsets(), AliasCache(),
+        IsCapturedCache() {}
 
   /// Create a new AAQueryInfo based on this one, but with the cache cleared.
   /// This is used for recursive queries across phis, where cache results may
@@ -381,6 +393,33 @@ class AAQueryInfo {
     NewAAQI.Depth = Depth;
     return NewAAQI;
   }
+
+  Optional<int64_t> getClobberOffset(const Value *Ptr1, const Value *Ptr2,
+                                     uint64_t Size1, uint64_t Size2) const {
+    assert(CacheOffsets && "Clobber offset cached in batch mode only!");
+    const bool Swapped = Ptr1 > Ptr2;
+    if (Swapped) {
+      std::swap(Ptr1, Ptr2);
+      std::swap(Size1, Size2);
+    }
+    const auto IOff = ClobberOffsets.find({{Ptr1, Ptr2}, {Size1, Size2}});
+    if (IOff != ClobberOffsets.end())
+      return Swapped ? -IOff->second : IOff->second;
+    return None;
+  }
+
+  void setClobberOffset(const Value *Ptr1, const Value *Ptr2, uint64_t Size1,
+                        uint64_t Size2, int64_t Offset) {
+    // Cache offset for batch mode only.
+    if (!CacheOffsets)
+      return;
+    if (Ptr1 > Ptr2) {
+      std::swap(Ptr1, Ptr2);
+      std::swap(Size1, Size2);
+      Offset = -Offset;
+    }
+    ClobberOffsets[{{Ptr1, Ptr2}, {Size1, Size2}}] = Offset;
+  }
 };
 
 class BatchAAResults;
@@ -823,7 +862,8 @@ class BatchAAResults {
   AAQueryInfo AAQI;
 
 public:
-  BatchAAResults(AAResults &AAR) : AA(AAR), AAQI() {}
+  BatchAAResults(AAResults &AAR, bool CacheOffsets = false)
+      : AA(AAR), AAQI(CacheOffsets) {}
   AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB) {
     return AA.alias(LocA, LocB, AAQI);
   }
@@ -856,6 +896,8 @@ class BatchAAResults {
     return alias(MemoryLocation(V1, LocationSize::precise(1)),
                  MemoryLocation(V2, LocationSize::precise(1))) == MustAlias;
   }
+  Optional<int64_t> getClobberOffset(const MemoryLocation &LocA,
+                                     const MemoryLocation &LocB) const;
 };
 
 /// Temporary typedef for legacy code that uses a generic \c AliasAnalysis

diff  --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 6af69e823d40..4946a12c5628 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -958,6 +958,17 @@ AAResults llvm::createLegacyPMAAResults(Pass &P, Function &F,
   return AAR;
 }
 
+Optional<int64_t>
+BatchAAResults::getClobberOffset(const MemoryLocation &LocA,
+                                 const MemoryLocation &LocB) const {
+  if (!LocA.Size.hasValue() || !LocB.Size.hasValue())
+    return None;
+  const Value *V1 = LocA.Ptr->stripPointerCastsForAliasAnalysis();
+  const Value *V2 = LocB.Ptr->stripPointerCastsForAliasAnalysis();
+  return AAQI.getClobberOffset(V1, V2, LocA.Size.getValue(),
+                               LocB.Size.getValue());
+}
+
 bool llvm::isNoAliasCall(const Value *V) {
   if (const auto *Call = dyn_cast<CallBase>(V))
     return Call->hasRetAttr(Attribute::NoAlias);

diff  --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 828d9130c78d..d954810e54f6 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -1144,24 +1144,43 @@ AliasResult BasicAAResult::aliasGEP(
   // that the objects are partially overlapping.  If the 
diff erence is
   // greater, we know they do not overlap.
   if (DecompGEP1.Offset != 0 && DecompGEP1.VarIndices.empty()) {
-    if (DecompGEP1.Offset.sge(0)) {
-      if (V2Size.hasValue()) {
-        if (DecompGEP1.Offset.ult(V2Size.getValue()))
-          return PartialAlias;
-        return NoAlias;
-      }
-    } else {
-      // We have the situation where:
+    APInt &Off = DecompGEP1.Offset;
+
+    // Initialize for Off >= 0 (V2 <= GEP1) case.
+    const Value *LeftPtr = V2;
+    const Value *RightPtr = GEP1;
+    LocationSize VLeftSize = V2Size;
+    LocationSize VRightSize = V1Size;
+
+    if (Off.isNegative()) {
+      // Swap if we have the situation where:
       // +                +
       // | BaseOffset     |
       // ---------------->|
       // |-->V1Size       |-------> V2Size
       // GEP1             V2
-      if (V1Size.hasValue()) {
-        if ((-DecompGEP1.Offset).ult(V1Size.getValue()))
-          return PartialAlias;
-        return NoAlias;
+      std::swap(LeftPtr, RightPtr);
+      std::swap(VLeftSize, VRightSize);
+      Off = -Off;
+    }
+
+    if (VLeftSize.hasValue()) {
+      const uint64_t LSize = VLeftSize.getValue();
+      if (Off.ult(LSize)) {
+        // Conservatively drop processing if a phi was visited and/or offset is
+        // too big.
+        if (VisitedPhiBBs.empty() && VRightSize.hasValue() &&
+            Off.ule(INT64_MAX)) {
+          // Memory referenced by right pointer is nested. Save the offset in
+          // cache.
+          const uint64_t RSize = VRightSize.getValue();
+          if ((Off + RSize).ule(LSize))
+            AAQI.setClobberOffset(LeftPtr, RightPtr, LSize, RSize,
+                                  Off.getSExtValue());
+        }
+        return PartialAlias;
       }
+      return NoAlias;
     }
   }
 

diff  --git a/llvm/unittests/Analysis/AliasAnalysisTest.cpp b/llvm/unittests/Analysis/AliasAnalysisTest.cpp
index 472ac9f59f8a..a1aaabc49a3c 100644
--- a/llvm/unittests/Analysis/AliasAnalysisTest.cpp
+++ b/llvm/unittests/Analysis/AliasAnalysisTest.cpp
@@ -298,6 +298,46 @@ TEST_F(AliasAnalysisTest, BatchAAPhiAssumption) {
   EXPECT_EQ(MayAlias, BatchAA.alias(ANextLoc, BNextLoc));
 }
 
+// Check that two aliased GEPs with non-constant offsets are correctly
+// analyzed and their relative offset can be requested from AA.
+TEST_F(AliasAnalysisTest, PartialAliasOffset) {
+  LLVMContext C;
+  SMDiagnostic Err;
+  std::unique_ptr<Module> M = parseAssemblyString(R"(
+    define void @foo(float* %arg, i32 %i) {
+    bb:
+      %i2 = zext i32 %i to i64
+      %i3 = getelementptr inbounds float, float* %arg, i64 %i2
+      %i4 = bitcast float* %i3 to <2 x float>*
+      %L2 = load <2 x float>, <2 x float>* %i4, align 16
+      %i7 = add nuw nsw i32 %i, 1
+      %i8 = zext i32 %i7 to i64
+      %i9 = getelementptr inbounds float, float* %arg, i64 %i8
+      %L1 = load float, float* %i9, align 4
+      ret void
+    }
+  )",
+                                                  Err, C);
+
+  if (!M)
+    Err.print("PartialAliasOffset", errs());
+
+  Function *F = M->getFunction("foo");
+  const auto Loc1 = MemoryLocation::get(getInstructionByName(*F, "L1"));
+  auto Loc2 = MemoryLocation::get(getInstructionByName(*F, "L2"));
+
+  auto &AA = getAAResults(*F);
+
+  BatchAAResults BatchAA(AA, /*CacheOffsets =*/true);
+  EXPECT_EQ(PartialAlias, BatchAA.alias(Loc1, Loc2));
+  EXPECT_EQ(-4, BatchAA.getClobberOffset(Loc1, Loc2).getValueOr(0));
+  EXPECT_EQ(4, BatchAA.getClobberOffset(Loc2, Loc1).getValueOr(0));
+
+  // Check that no offset returned for 
diff erent size.
+  Loc2.Size = LocationSize::precise(42);
+  EXPECT_EQ(0, BatchAA.getClobberOffset(Loc1, Loc2).getValueOr(0));
+}
+
 class AAPassInfraTest : public testing::Test {
 protected:
   LLVMContext C;


        


More information about the llvm-commits mailing list