diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2024-07-26 22:04:10 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2024-07-26 22:04:10 +0000 |
| commit | ac9a064cb179f3425b310fa2847f8764ac970a4d (patch) | |
| tree | 6f945cdaa68c2b4c688dcf9fec4f922d35f4d1a4 /llvm/lib/Analysis/BasicAliasAnalysis.cpp | |
| parent | 4df029cc74e5ec124f14a5682e44999ce4f086df (diff) | |
Diffstat (limited to 'llvm/lib/Analysis/BasicAliasAnalysis.cpp')
| -rw-r--r-- | llvm/lib/Analysis/BasicAliasAnalysis.cpp | 164 |
1 files changed, 120 insertions, 44 deletions
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index 3178e2d27816..161a3034e482 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -44,6 +44,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Operator.h" +#include "llvm/IR/PatternMatch.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" @@ -89,7 +90,7 @@ bool BasicAAResult::invalidate(Function &Fn, const PreservedAnalyses &PA, // may be created without handles to some analyses and in that case don't // depend on them. if (Inv.invalidate<AssumptionAnalysis>(Fn, PA) || - (DT && Inv.invalidate<DominatorTreeAnalysis>(Fn, PA))) + (DT_ && Inv.invalidate<DominatorTreeAnalysis>(Fn, PA))) return true; // Otherwise this analysis result remains valid. @@ -188,6 +189,12 @@ static bool isObjectSize(const Value *V, TypeSize Size, const DataLayout &DL, return ObjectSize && *ObjectSize == Size; } +/// Return true if both V1 and V2 are VScale +static bool areBothVScale(const Value *V1, const Value *V2) { + return PatternMatch::match(V1, PatternMatch::m_VScale()) && + PatternMatch::match(V2, PatternMatch::m_VScale()); +} + //===----------------------------------------------------------------------===// // CaptureInfo implementations //===----------------------------------------------------------------------===// @@ -261,31 +268,43 @@ struct CastedValue { unsigned ZExtBits = 0; unsigned SExtBits = 0; unsigned TruncBits = 0; + /// Whether trunc(V) is non-negative. + bool IsNonNegative = false; explicit CastedValue(const Value *V) : V(V) {} explicit CastedValue(const Value *V, unsigned ZExtBits, unsigned SExtBits, - unsigned TruncBits) - : V(V), ZExtBits(ZExtBits), SExtBits(SExtBits), TruncBits(TruncBits) {} + unsigned TruncBits, bool IsNonNegative) + : V(V), ZExtBits(ZExtBits), SExtBits(SExtBits), TruncBits(TruncBits), + IsNonNegative(IsNonNegative) {} unsigned getBitWidth() const { return V->getType()->getPrimitiveSizeInBits() - TruncBits + ZExtBits + SExtBits; } - CastedValue withValue(const Value *NewV) const { - return CastedValue(NewV, ZExtBits, SExtBits, TruncBits); + CastedValue withValue(const Value *NewV, bool PreserveNonNeg) const { + return CastedValue(NewV, ZExtBits, SExtBits, TruncBits, + IsNonNegative && PreserveNonNeg); } /// Replace V with zext(NewV) - CastedValue withZExtOfValue(const Value *NewV) const { + CastedValue withZExtOfValue(const Value *NewV, bool ZExtNonNegative) const { unsigned ExtendBy = V->getType()->getPrimitiveSizeInBits() - NewV->getType()->getPrimitiveSizeInBits(); if (ExtendBy <= TruncBits) - return CastedValue(NewV, ZExtBits, SExtBits, TruncBits - ExtendBy); + // zext<nneg>(trunc(zext(NewV))) == zext<nneg>(trunc(NewV)) + // The nneg can be preserved on the outer zext here. + return CastedValue(NewV, ZExtBits, SExtBits, TruncBits - ExtendBy, + IsNonNegative); // zext(sext(zext(NewV))) == zext(zext(zext(NewV))) ExtendBy -= TruncBits; - return CastedValue(NewV, ZExtBits + SExtBits + ExtendBy, 0, 0); + // zext<nneg>(zext(NewV)) == zext(NewV) + // zext(zext<nneg>(NewV)) == zext<nneg>(NewV) + // The nneg can be preserved from the inner zext here but must be dropped + // from the outer. + return CastedValue(NewV, ZExtBits + SExtBits + ExtendBy, 0, 0, + ZExtNonNegative); } /// Replace V with sext(NewV) @@ -293,11 +312,16 @@ struct CastedValue { unsigned ExtendBy = V->getType()->getPrimitiveSizeInBits() - NewV->getType()->getPrimitiveSizeInBits(); if (ExtendBy <= TruncBits) - return CastedValue(NewV, ZExtBits, SExtBits, TruncBits - ExtendBy); + // zext<nneg>(trunc(sext(NewV))) == zext<nneg>(trunc(NewV)) + // The nneg can be preserved on the outer zext here + return CastedValue(NewV, ZExtBits, SExtBits, TruncBits - ExtendBy, + IsNonNegative); // zext(sext(sext(NewV))) ExtendBy -= TruncBits; - return CastedValue(NewV, ZExtBits, SExtBits + ExtendBy, 0); + // zext<nneg>(sext(sext(NewV))) = zext<nneg>(sext(NewV)) + // The nneg can be preserved on the outer zext here + return CastedValue(NewV, ZExtBits, SExtBits + ExtendBy, 0, IsNonNegative); } APInt evaluateWith(APInt N) const { @@ -326,8 +350,15 @@ struct CastedValue { } bool hasSameCastsAs(const CastedValue &Other) const { - return ZExtBits == Other.ZExtBits && SExtBits == Other.SExtBits && - TruncBits == Other.TruncBits; + if (ZExtBits == Other.ZExtBits && SExtBits == Other.SExtBits && + TruncBits == Other.TruncBits) + return true; + // If either CastedValue has a nneg zext then the sext/zext bits are + // interchangable for that value. + if (IsNonNegative || Other.IsNonNegative) + return (ZExtBits + SExtBits == Other.ZExtBits + Other.SExtBits && + TruncBits == Other.TruncBits); + return false; } }; @@ -403,21 +434,21 @@ static LinearExpression GetLinearExpression( [[fallthrough]]; case Instruction::Add: { - E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL, + E = GetLinearExpression(Val.withValue(BOp->getOperand(0), false), DL, Depth + 1, AC, DT); E.Offset += RHS; E.IsNSW &= NSW; break; } case Instruction::Sub: { - E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL, + E = GetLinearExpression(Val.withValue(BOp->getOperand(0), false), DL, Depth + 1, AC, DT); E.Offset -= RHS; E.IsNSW &= NSW; break; } case Instruction::Mul: - E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL, + E = GetLinearExpression(Val.withValue(BOp->getOperand(0), false), DL, Depth + 1, AC, DT) .mul(RHS, NSW); break; @@ -430,7 +461,7 @@ static LinearExpression GetLinearExpression( if (RHS.getLimitedValue() > Val.getBitWidth()) return Val; - E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL, + E = GetLinearExpression(Val.withValue(BOp->getOperand(0), NSW), DL, Depth + 1, AC, DT); E.Offset <<= RHS.getLimitedValue(); E.Scale <<= RHS.getLimitedValue(); @@ -441,10 +472,10 @@ static LinearExpression GetLinearExpression( } } - if (isa<ZExtInst>(Val.V)) + if (const auto *ZExt = dyn_cast<ZExtInst>(Val.V)) return GetLinearExpression( - Val.withZExtOfValue(cast<CastInst>(Val.V)->getOperand(0)), - DL, Depth + 1, AC, DT); + Val.withZExtOfValue(ZExt->getOperand(0), ZExt->hasNonNeg()), DL, + Depth + 1, AC, DT); if (isa<SExtInst>(Val.V)) return GetLinearExpression( @@ -666,7 +697,7 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL, unsigned SExtBits = IndexSize > Width ? IndexSize - Width : 0; unsigned TruncBits = IndexSize < Width ? Width - IndexSize : 0; LinearExpression LE = GetLinearExpression( - CastedValue(Index, 0, SExtBits, TruncBits), DL, 0, AC, DT); + CastedValue(Index, 0, SExtBits, TruncBits, false), DL, 0, AC, DT); // Scale by the type size. unsigned TypeSize = AllocTypeSize.getFixedValue(); @@ -679,7 +710,8 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL, // A[x][x] -> x*16 + x*4 -> x*20 // This also ensures that 'x' only appears in the index list once. for (unsigned i = 0, e = Decomposed.VarIndices.size(); i != e; ++i) { - if (Decomposed.VarIndices[i].Val.V == LE.Val.V && + if ((Decomposed.VarIndices[i].Val.V == LE.Val.V || + areBothVScale(Decomposed.VarIndices[i].Val.V, LE.Val.V)) && Decomposed.VarIndices[i].Val.hasSameCastsAs(LE.Val)) { Scale += Decomposed.VarIndices[i].Scale; LE.IsNSW = false; // We cannot guarantee nsw for the merge. @@ -1063,6 +1095,7 @@ AliasResult BasicAAResult::aliasGEP( : AliasResult::MayAlias; } + DominatorTree *DT = getDT(AAQI); DecomposedGEP DecompGEP1 = DecomposeGEPExpression(GEP1, DL, &AC, DT); DecomposedGEP DecompGEP2 = DecomposeGEPExpression(V2, DL, &AC, DT); @@ -1112,10 +1145,6 @@ AliasResult BasicAAResult::aliasGEP( return BaseAlias; } - // Bail on analysing scalable LocationSize - if (V1Size.isScalable() || V2Size.isScalable()) - return AliasResult::MayAlias; - // If there is a constant difference between the pointers, but the difference // is less than the size of the associated memory object, then we know // that the objects are partially overlapping. If the difference is @@ -1145,24 +1174,69 @@ AliasResult BasicAAResult::aliasGEP( if (!VLeftSize.hasValue()) return AliasResult::MayAlias; - const uint64_t LSize = VLeftSize.getValue(); - if (Off.ult(LSize)) { - // Conservatively drop processing if a phi was visited and/or offset is - // too big. - AliasResult AR = AliasResult::PartialAlias; - if (VRightSize.hasValue() && Off.ule(INT32_MAX) && - (Off + VRightSize.getValue()).ule(LSize)) { - // Memory referenced by right pointer is nested. Save the offset in - // cache. Note that originally offset estimated as GEP1-V2, but - // AliasResult contains the shift that represents GEP1+Offset=V2. - AR.setOffset(-Off.getSExtValue()); - AR.swap(Swapped); + const TypeSize LSize = VLeftSize.getValue(); + if (!LSize.isScalable()) { + if (Off.ult(LSize)) { + // Conservatively drop processing if a phi was visited and/or offset is + // too big. + AliasResult AR = AliasResult::PartialAlias; + if (VRightSize.hasValue() && !VRightSize.isScalable() && + Off.ule(INT32_MAX) && (Off + VRightSize.getValue()).ule(LSize)) { + // Memory referenced by right pointer is nested. Save the offset in + // cache. Note that originally offset estimated as GEP1-V2, but + // AliasResult contains the shift that represents GEP1+Offset=V2. + AR.setOffset(-Off.getSExtValue()); + AR.swap(Swapped); + } + return AR; } - 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; } - return AliasResult::NoAlias; } + // VScale Alias Analysis - Given one scalable offset between accesses and a + // scalable typesize, we can divide each side by vscale, treating both values + // as a constant. We prove that Offset/vscale >= TypeSize/vscale. + if (DecompGEP1.VarIndices.size() == 1 && + DecompGEP1.VarIndices[0].Val.TruncBits == 0 && + DecompGEP1.Offset.isZero() && + PatternMatch::match(DecompGEP1.VarIndices[0].Val.V, + PatternMatch::m_VScale())) { + const VariableGEPIndex &ScalableVar = DecompGEP1.VarIndices[0]; + APInt Scale = + ScalableVar.IsNegated ? -ScalableVar.Scale : ScalableVar.Scale; + LocationSize VLeftSize = Scale.isNegative() ? V1Size : V2Size; + + // Check if the offset is known to not overflow, if it does then attempt to + // prove it with the known values of vscale_range. + bool Overflows = !DecompGEP1.VarIndices[0].IsNSW; + if (Overflows) { + ConstantRange CR = getVScaleRange(&F, Scale.getBitWidth()); + (void)CR.getSignedMax().smul_ov(Scale, Overflows); + } + + if (!Overflows) { + // Note that we do not check that the typesize is scalable, as vscale >= 1 + // so noalias still holds so long as the dependency distance is at least + // as big as the typesize. + if (VLeftSize.hasValue() && + Scale.abs().uge(VLeftSize.getValue().getKnownMinValue())) + return AliasResult::NoAlias; + } + } + + // Bail on analysing scalable LocationSize + if (V1Size.isScalable() || V2Size.isScalable()) + return AliasResult::MayAlias; + // We need to know both acess sizes for all the following heuristics. if (!V1Size.hasValue() || !V2Size.hasValue()) return AliasResult::MayAlias; @@ -1234,7 +1308,7 @@ AliasResult BasicAAResult::aliasGEP( // VarIndex = Scale*V. const VariableGEPIndex &Var = DecompGEP1.VarIndices[0]; if (Var.Val.TruncBits == 0 && - isKnownNonZero(Var.Val.V, DL, 0, &AC, Var.CxtI, DT)) { + isKnownNonZero(Var.Val.V, SimplifyQuery(DL, DT, &AC, Var.CxtI))) { // Check if abs(V*Scale) >= abs(Scale) holds in the presence of // potentially wrapping math. auto MultiplyByScaleNoWrap = [](const VariableGEPIndex &Var) { @@ -1556,6 +1630,7 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size, const Value *HintO1 = getUnderlyingObject(Hint1); const Value *HintO2 = getUnderlyingObject(Hint2); + DominatorTree *DT = getDT(AAQI); auto ValidAssumeForPtrContext = [&](const Value *Ptr) { if (const Instruction *PtrI = dyn_cast<Instruction>(Ptr)) { return isValidAssumeForContext(Assume, PtrI, DT, @@ -1735,7 +1810,7 @@ bool BasicAAResult::isValueEqualInPotentialCycles(const Value *V, if (!Inst || Inst->getParent()->isEntryBlock()) return true; - return isNotInCycle(Inst, DT, /*LI*/ nullptr); + return isNotInCycle(Inst, getDT(AAQI), /*LI*/ nullptr); } /// Computes the symbolic difference between two de-composed GEPs. @@ -1749,7 +1824,8 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP, bool Found = false; for (auto I : enumerate(DestGEP.VarIndices)) { VariableGEPIndex &Dest = I.value(); - if (!isValueEqualInPotentialCycles(Dest.Val.V, Src.Val.V, AAQI) || + if ((!isValueEqualInPotentialCycles(Dest.Val.V, Src.Val.V, AAQI) && + !areBothVScale(Dest.Val.V, Src.Val.V)) || !Dest.Val.hasSameCastsAs(Src.Val)) continue; @@ -1843,7 +1919,7 @@ BasicAAResult BasicAA::run(Function &F, FunctionAnalysisManager &AM) { auto &TLI = AM.getResult<TargetLibraryAnalysis>(F); auto &AC = AM.getResult<AssumptionAnalysis>(F); auto *DT = &AM.getResult<DominatorTreeAnalysis>(F); - return BasicAAResult(F.getParent()->getDataLayout(), F, TLI, AC, DT); + return BasicAAResult(F.getDataLayout(), F, TLI, AC, DT); } BasicAAWrapperPass::BasicAAWrapperPass() : FunctionPass(ID) { @@ -1871,7 +1947,7 @@ bool BasicAAWrapperPass::runOnFunction(Function &F) { auto &TLIWP = getAnalysis<TargetLibraryInfoWrapperPass>(); auto &DTWP = getAnalysis<DominatorTreeWrapperPass>(); - Result.reset(new BasicAAResult(F.getParent()->getDataLayout(), F, + Result.reset(new BasicAAResult(F.getDataLayout(), F, TLIWP.getTLI(F), ACT.getAssumptionCache(F), &DTWP.getDomTree())); |
