diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -357,6 +357,10 @@ using AliasCacheT = SmallDenseMap; AliasCacheT AliasCache; + using OffsetPair = std::pair; + using OffsetsCacheT = SmallDenseMap; + OffsetsCacheT ClobberOffsets; + using IsCapturedCacheT = SmallDenseMap; IsCapturedCacheT IsCapturedCache; @@ -368,7 +372,27 @@ /// assumption is disproven. SmallVector AssumptionBasedResults; - AAQueryInfo() : AliasCache(), IsCapturedCache() {} + AAQueryInfo() : AliasCache(), ClobberOffsets(), IsCapturedCache() {} + + Optional getClobberOffset(OffsetPair OP) const { + const bool Swapped = OP.first > OP.second; + if (Swapped) { + std::swap(OP.first, OP.second); + } + const auto IOff = ClobberOffsets.find(OP); + if (IOff != ClobberOffsets.end()) { + return Swapped ? -IOff->second : IOff->second; + } + return None; + } + + void setClobberOffset(OffsetPair OP, int64_t Offset) { + if (OP.first > OP.second) { + std::swap(OP.first, OP.second); + Offset = -Offset; + } + ClobberOffsets[OP] = Offset; + } }; class BatchAAResults; @@ -847,6 +871,8 @@ return alias(MemoryLocation(V1, LocationSize::precise(1)), MemoryLocation(V2, LocationSize::precise(1))) == MustAlias; } + Optional 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 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -937,6 +937,14 @@ return AAR; } +Optional +BatchAAResults::getClobberOffset(const MemoryLocation &LocA, + const MemoryLocation &LocB) const { + const Value *V1 = LocA.Ptr->stripPointerCastsAndInvariantGroups(); + const Value *V2 = LocB.Ptr->stripPointerCastsAndInvariantGroups(); + return AAQI.getClobberOffset({V1, V2}); +} + bool llvm::isNoAliasCall(const Value *V) { if (const auto *Call = dyn_cast(V)) return Call->hasRetAttr(Attribute::NoAlias); diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -1176,24 +1176,40 @@ // that the objects are partially overlapping. If the difference 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; + const LocationSize *VLeftSize = &V2Size; + const LocationSize *VRightSize = &V1Size; + + if (!Off.sge(0)) { + // 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()) { + if (Off.ult(VLeftSize->getValue())) { + // Conservatively drop processing if a phi was visited and/or offset is + // too big. + if (VisitedPhiBBs.empty() && VRightSize->hasValue() && + Off.ule(INT64_MAX)) { + // Memory refernced by right pointer is nested. Save the offset in + // cache. + if ((Off + VRightSize->getValue()).ule(VLeftSize->getValue())) + AAQI.setClobberOffset({LeftPtr, RightPtr}, Off.getSExtValue()); + } + return PartialAlias; } + return NoAlias; } } diff --git a/llvm/unittests/Analysis/AliasAnalysisTest.cpp b/llvm/unittests/Analysis/AliasAnalysisTest.cpp --- a/llvm/unittests/Analysis/AliasAnalysisTest.cpp +++ b/llvm/unittests/Analysis/AliasAnalysisTest.cpp @@ -298,6 +298,42 @@ 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 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("CodeMoverUtilsTests", errs()); + + Function *F = M->getFunction("foo"); + const auto Loc1 = MemoryLocation::get(getInstructionByName(*F, "L1")); + const auto Loc2 = MemoryLocation::get(getInstructionByName(*F, "L2")); + + auto &AA = getAAResults(*F); + + BatchAAResults BatchAA(AA); + 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)); +} + class AAPassInfraTest : public testing::Test { protected: LLVMContext C;