#include "llvm/Analysis/BasicAliasAnalysis.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/Analysis/PhiValues.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/KnownBits.h"
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <utility>
#define DEBUG_TYPE "basicaa"
using namespace llvm;
static cl::opt<bool> EnableRecPhiAnalysis("basic-aa-recphi", cl::Hidden,
cl::init(true));
STATISTIC(SearchLimitReached, "Number of times the limit to "
"decompose GEPs is reached");
STATISTIC(SearchTimes, "Number of times a GEP is decomposed");
const unsigned MaxNumPhiBBsValueReachabilityCheck = 20;
static const unsigned MaxLookupSearchDepth = 6;
bool BasicAAResult::invalidate(Function &Fn, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv) {
if (Inv.invalidate<AssumptionAnalysis>(Fn, PA) ||
(DT && Inv.invalidate<DominatorTreeAnalysis>(Fn, PA)) ||
(PV && Inv.invalidate<PhiValuesAnalysis>(Fn, PA)))
return true;
return false;
}
static uint64_t getObjectSize(const Value *V, const DataLayout &DL,
const TargetLibraryInfo &TLI,
bool NullIsValidLoc,
bool RoundToAlign = false) {
uint64_t Size;
ObjectSizeOpts Opts;
Opts.RoundToAlign = RoundToAlign;
Opts.NullIsUnknownSize = NullIsValidLoc;
if (getObjectSize(V, Size, DL, &TLI, Opts))
return Size;
return MemoryLocation::UnknownSize;
}
static bool isObjectSmallerThan(const Value *V, uint64_t Size,
const DataLayout &DL,
const TargetLibraryInfo &TLI,
bool NullIsValidLoc) {
if (!isIdentifiedObject(V))
return false;
uint64_t ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc,
true);
return ObjectSize != MemoryLocation::UnknownSize && ObjectSize < Size;
}
static uint64_t getMinimalExtentFrom(const Value &V,
const LocationSize &LocSize,
const DataLayout &DL,
bool NullIsValidLoc) {
bool CanBeNull, CanBeFreed;
uint64_t DerefBytes =
V.getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed);
DerefBytes = (CanBeNull && NullIsValidLoc) ? 0 : DerefBytes;
if (LocSize.isPrecise())
DerefBytes = std::max(DerefBytes, LocSize.getValue());
return DerefBytes;
}
static bool isObjectSize(const Value *V, uint64_t Size, const DataLayout &DL,
const TargetLibraryInfo &TLI, bool NullIsValidLoc) {
uint64_t ObjectSize = getObjectSize(V, DL, TLI, NullIsValidLoc);
return ObjectSize != MemoryLocation::UnknownSize && ObjectSize == Size;
}
CaptureInfo::~CaptureInfo() = default;
bool SimpleCaptureInfo::isNotCapturedBeforeOrAt(const Value *Object,
const Instruction *I) {
return isNonEscapingLocalObject(Object, &IsCapturedCache);
}
bool EarliestEscapeInfo::isNotCapturedBeforeOrAt(const Value *Object,
const Instruction *I) {
if (!isIdentifiedFunctionLocal(Object))
return false;
auto Iter = EarliestEscapes.insert({Object, nullptr});
if (Iter.second) {
Instruction *EarliestCapture = FindEarliestCapture(
Object, *const_cast<Function *>(I->getFunction()),
false, true, DT, EphValues);
if (EarliestCapture) {
auto Ins = Inst2Obj.insert({EarliestCapture, {}});
Ins.first->second.push_back(Object);
}
Iter.first->second = EarliestCapture;
}
if (!Iter.first->second)
return true;
return I != Iter.first->second &&
!isPotentiallyReachable(Iter.first->second, I, nullptr, &DT, &LI);
}
void EarliestEscapeInfo::removeInstruction(Instruction *I) {
auto Iter = Inst2Obj.find(I);
if (Iter != Inst2Obj.end()) {
for (const Value *Obj : Iter->second)
EarliestEscapes.erase(Obj);
Inst2Obj.erase(I);
}
}
namespace {
struct CastedValue {
const Value *V;
unsigned ZExtBits = 0;
unsigned SExtBits = 0;
unsigned TruncBits = 0;
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 getBitWidth() const {
return V->getType()->getPrimitiveSizeInBits() - TruncBits + ZExtBits +
SExtBits;
}
CastedValue withValue(const Value *NewV) const {
return CastedValue(NewV, ZExtBits, SExtBits, TruncBits);
}
CastedValue withZExtOfValue(const Value *NewV) const {
unsigned ExtendBy = V->getType()->getPrimitiveSizeInBits() -
NewV->getType()->getPrimitiveSizeInBits();
if (ExtendBy <= TruncBits)
return CastedValue(NewV, ZExtBits, SExtBits, TruncBits - ExtendBy);
ExtendBy -= TruncBits;
return CastedValue(NewV, ZExtBits + SExtBits + ExtendBy, 0, 0);
}
CastedValue withSExtOfValue(const Value *NewV) const {
unsigned ExtendBy = V->getType()->getPrimitiveSizeInBits() -
NewV->getType()->getPrimitiveSizeInBits();
if (ExtendBy <= TruncBits)
return CastedValue(NewV, ZExtBits, SExtBits, TruncBits - ExtendBy);
ExtendBy -= TruncBits;
return CastedValue(NewV, ZExtBits, SExtBits + ExtendBy, 0);
}
APInt evaluateWith(APInt N) const {
assert(N.getBitWidth() == V->getType()->getPrimitiveSizeInBits() &&
"Incompatible bit width");
if (TruncBits) N = N.trunc(N.getBitWidth() - TruncBits);
if (SExtBits) N = N.sext(N.getBitWidth() + SExtBits);
if (ZExtBits) N = N.zext(N.getBitWidth() + ZExtBits);
return N;
}
ConstantRange evaluateWith(ConstantRange N) const {
assert(N.getBitWidth() == V->getType()->getPrimitiveSizeInBits() &&
"Incompatible bit width");
if (TruncBits) N = N.truncate(N.getBitWidth() - TruncBits);
if (SExtBits) N = N.signExtend(N.getBitWidth() + SExtBits);
if (ZExtBits) N = N.zeroExtend(N.getBitWidth() + ZExtBits);
return N;
}
bool canDistributeOver(bool NUW, bool NSW) const {
return (!ZExtBits || NUW) && (!SExtBits || NSW);
}
bool hasSameCastsAs(const CastedValue &Other) const {
return ZExtBits == Other.ZExtBits && SExtBits == Other.SExtBits &&
TruncBits == Other.TruncBits;
}
};
struct LinearExpression {
CastedValue Val;
APInt Scale;
APInt Offset;
bool IsNSW;
LinearExpression(const CastedValue &Val, const APInt &Scale,
const APInt &Offset, bool IsNSW)
: Val(Val), Scale(Scale), Offset(Offset), IsNSW(IsNSW) {}
LinearExpression(const CastedValue &Val) : Val(Val), IsNSW(true) {
unsigned BitWidth = Val.getBitWidth();
Scale = APInt(BitWidth, 1);
Offset = APInt(BitWidth, 0);
}
LinearExpression mul(const APInt &Other, bool MulIsNSW) const {
bool NSW = IsNSW && (Other.isOne() || (MulIsNSW && Offset.isZero()));
return LinearExpression(Val, Scale * Other, Offset * Other, NSW);
}
};
}
static LinearExpression GetLinearExpression(
const CastedValue &Val, const DataLayout &DL, unsigned Depth,
AssumptionCache *AC, DominatorTree *DT) {
if (Depth == 6)
return Val;
if (const ConstantInt *Const = dyn_cast<ConstantInt>(Val.V))
return LinearExpression(Val, APInt(Val.getBitWidth(), 0),
Val.evaluateWith(Const->getValue()), true);
if (const BinaryOperator *BOp = dyn_cast<BinaryOperator>(Val.V)) {
if (ConstantInt *RHSC = dyn_cast<ConstantInt>(BOp->getOperand(1))) {
APInt RHS = Val.evaluateWith(RHSC->getValue());
bool NUW = true, NSW = true;
if (isa<OverflowingBinaryOperator>(BOp)) {
NUW &= BOp->hasNoUnsignedWrap();
NSW &= BOp->hasNoSignedWrap();
}
if (!Val.canDistributeOver(NUW, NSW))
return Val;
if (Val.TruncBits)
NUW = NSW = false;
LinearExpression E(Val);
switch (BOp->getOpcode()) {
default:
return Val;
case Instruction::Or:
if (!MaskedValueIsZero(BOp->getOperand(0), RHSC->getValue(), DL, 0, AC,
BOp, DT))
return Val;
LLVM_FALLTHROUGH;
case Instruction::Add: {
E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL,
Depth + 1, AC, DT);
E.Offset += RHS;
E.IsNSW &= NSW;
break;
}
case Instruction::Sub: {
E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL,
Depth + 1, AC, DT);
E.Offset -= RHS;
E.IsNSW &= NSW;
break;
}
case Instruction::Mul:
E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL,
Depth + 1, AC, DT)
.mul(RHS, NSW);
break;
case Instruction::Shl:
if (RHS.getLimitedValue() > Val.getBitWidth())
return Val;
E = GetLinearExpression(Val.withValue(BOp->getOperand(0)), DL,
Depth + 1, AC, DT);
E.Offset <<= RHS.getLimitedValue();
E.Scale <<= RHS.getLimitedValue();
E.IsNSW &= NSW;
break;
}
return E;
}
}
if (isa<ZExtInst>(Val.V))
return GetLinearExpression(
Val.withZExtOfValue(cast<CastInst>(Val.V)->getOperand(0)),
DL, Depth + 1, AC, DT);
if (isa<SExtInst>(Val.V))
return GetLinearExpression(
Val.withSExtOfValue(cast<CastInst>(Val.V)->getOperand(0)),
DL, Depth + 1, AC, DT);
return Val;
}
static APInt adjustToIndexSize(const APInt &Offset, unsigned IndexSize) {
assert(IndexSize <= Offset.getBitWidth() && "Invalid IndexSize!");
unsigned ShiftBits = Offset.getBitWidth() - IndexSize;
return (Offset << ShiftBits).ashr(ShiftBits);
}
namespace {
struct VariableGEPIndex {
CastedValue Val;
APInt Scale;
const Instruction *CxtI;
bool IsNSW;
void dump() const {
print(dbgs());
dbgs() << "\n";
}
void print(raw_ostream &OS) const {
OS << "(V=" << Val.V->getName()
<< ", zextbits=" << Val.ZExtBits
<< ", sextbits=" << Val.SExtBits
<< ", truncbits=" << Val.TruncBits
<< ", scale=" << Scale << ")";
}
};
}
struct BasicAAResult::DecomposedGEP {
const Value *Base;
APInt Offset;
SmallVector<VariableGEPIndex, 4> VarIndices;
Optional<bool> InBounds;
void dump() const {
print(dbgs());
dbgs() << "\n";
}
void print(raw_ostream &OS) const {
OS << "(DecomposedGEP Base=" << Base->getName()
<< ", Offset=" << Offset
<< ", VarIndices=[";
for (size_t i = 0; i < VarIndices.size(); i++) {
if (i != 0)
OS << ", ";
VarIndices[i].print(OS);
}
OS << "])";
}
};
BasicAAResult::DecomposedGEP
BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
AssumptionCache *AC, DominatorTree *DT) {
unsigned MaxLookup = MaxLookupSearchDepth;
SearchTimes++;
const Instruction *CxtI = dyn_cast<Instruction>(V);
unsigned MaxIndexSize = DL.getMaxIndexSizeInBits();
DecomposedGEP Decomposed;
Decomposed.Offset = APInt(MaxIndexSize, 0);
do {
const Operator *Op = dyn_cast<Operator>(V);
if (!Op) {
if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
if (!GA->isInterposable()) {
V = GA->getAliasee();
continue;
}
}
Decomposed.Base = V;
return Decomposed;
}
if (Op->getOpcode() == Instruction::BitCast ||
Op->getOpcode() == Instruction::AddrSpaceCast) {
V = Op->getOperand(0);
continue;
}
const GEPOperator *GEPOp = dyn_cast<GEPOperator>(Op);
if (!GEPOp) {
if (const auto *PHI = dyn_cast<PHINode>(V)) {
if (PHI->getNumIncomingValues() == 1) {
V = PHI->getIncomingValue(0);
continue;
}
} else if (const auto *Call = dyn_cast<CallBase>(V)) {
if (auto *RP = getArgumentAliasingToReturnedPointer(Call, false)) {
V = RP;
continue;
}
}
Decomposed.Base = V;
return Decomposed;
}
if (Decomposed.InBounds == None)
Decomposed.InBounds = GEPOp->isInBounds();
else if (!GEPOp->isInBounds())
Decomposed.InBounds = false;
assert(GEPOp->getSourceElementType()->isSized() && "GEP must be sized");
if (isa<ScalableVectorType>(GEPOp->getSourceElementType())) {
Decomposed.Base = V;
return Decomposed;
}
unsigned AS = GEPOp->getPointerAddressSpace();
gep_type_iterator GTI = gep_type_begin(GEPOp);
unsigned IndexSize = DL.getIndexSizeInBits(AS);
bool GepHasConstantOffset = true;
for (User::const_op_iterator I = GEPOp->op_begin() + 1, E = GEPOp->op_end();
I != E; ++I, ++GTI) {
const Value *Index = *I;
if (StructType *STy = GTI.getStructTypeOrNull()) {
unsigned FieldNo = cast<ConstantInt>(Index)->getZExtValue();
if (FieldNo == 0)
continue;
Decomposed.Offset += DL.getStructLayout(STy)->getElementOffset(FieldNo);
continue;
}
if (const ConstantInt *CIdx = dyn_cast<ConstantInt>(Index)) {
if (CIdx->isZero())
continue;
Decomposed.Offset +=
DL.getTypeAllocSize(GTI.getIndexedType()).getFixedSize() *
CIdx->getValue().sextOrTrunc(MaxIndexSize);
continue;
}
GepHasConstantOffset = false;
unsigned Width = Index->getType()->getIntegerBitWidth();
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);
unsigned TypeSize =
DL.getTypeAllocSize(GTI.getIndexedType()).getFixedSize();
LE = LE.mul(APInt(IndexSize, TypeSize), GEPOp->isInBounds());
Decomposed.Offset += LE.Offset.sext(MaxIndexSize);
APInt Scale = LE.Scale.sext(MaxIndexSize);
for (unsigned i = 0, e = Decomposed.VarIndices.size(); i != e; ++i) {
if (Decomposed.VarIndices[i].Val.V == LE.Val.V &&
Decomposed.VarIndices[i].Val.hasSameCastsAs(LE.Val)) {
Scale += Decomposed.VarIndices[i].Scale;
Decomposed.VarIndices.erase(Decomposed.VarIndices.begin() + i);
break;
}
}
Scale = adjustToIndexSize(Scale, IndexSize);
if (!!Scale) {
VariableGEPIndex Entry = {LE.Val, Scale, CxtI, LE.IsNSW};
Decomposed.VarIndices.push_back(Entry);
}
}
if (GepHasConstantOffset)
Decomposed.Offset = adjustToIndexSize(Decomposed.Offset, IndexSize);
V = GEPOp->getOperand(0);
} while (--MaxLookup);
Decomposed.Base = V;
SearchLimitReached++;
return Decomposed;
}
bool BasicAAResult::pointsToConstantMemory(const MemoryLocation &Loc,
AAQueryInfo &AAQI, bool OrLocal) {
assert(Visited.empty() && "Visited must be cleared after use!");
unsigned MaxLookup = 8;
SmallVector<const Value *, 16> Worklist;
Worklist.push_back(Loc.Ptr);
do {
const Value *V = getUnderlyingObject(Worklist.pop_back_val());
if (!Visited.insert(V).second) {
Visited.clear();
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
}
if (OrLocal && isa<AllocaInst>(V))
continue;
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) {
if (!GV->isConstant()) {
Visited.clear();
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
}
continue;
}
if (const SelectInst *SI = dyn_cast<SelectInst>(V)) {
Worklist.push_back(SI->getTrueValue());
Worklist.push_back(SI->getFalseValue());
continue;
}
if (const PHINode *PN = dyn_cast<PHINode>(V)) {
if (PN->getNumIncomingValues() > MaxLookup) {
Visited.clear();
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
}
append_range(Worklist, PN->incoming_values());
continue;
}
Visited.clear();
return AAResultBase::pointsToConstantMemory(Loc, AAQI, OrLocal);
} while (!Worklist.empty() && --MaxLookup);
Visited.clear();
return Worklist.empty();
}
static bool isIntrinsicCall(const CallBase *Call, Intrinsic::ID IID) {
const IntrinsicInst *II = dyn_cast<IntrinsicInst>(Call);
return II && II->getIntrinsicID() == IID;
}
FunctionModRefBehavior BasicAAResult::getModRefBehavior(const CallBase *Call) {
if (Call->doesNotAccessMemory())
return FMRB_DoesNotAccessMemory;
FunctionModRefBehavior Min = FMRB_UnknownModRefBehavior;
if (Call->onlyReadsMemory())
Min = FMRB_OnlyReadsMemory;
else if (Call->onlyWritesMemory())
Min = FMRB_OnlyWritesMemory;
if (Call->onlyAccessesArgMemory())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees);
else if (Call->onlyAccessesInaccessibleMemory())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleMem);
else if (Call->onlyAccessesInaccessibleMemOrArgMem())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleOrArgMem);
if (!Call->hasOperandBundles())
if (const Function *F = Call->getCalledFunction())
Min =
FunctionModRefBehavior(Min & getBestAAResults().getModRefBehavior(F));
return Min;
}
FunctionModRefBehavior BasicAAResult::getModRefBehavior(const Function *F) {
if (F->doesNotAccessMemory())
return FMRB_DoesNotAccessMemory;
FunctionModRefBehavior Min = FMRB_UnknownModRefBehavior;
if (F->onlyReadsMemory())
Min = FMRB_OnlyReadsMemory;
else if (F->onlyWritesMemory())
Min = FMRB_OnlyWritesMemory;
if (F->onlyAccessesArgMemory())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees);
else if (F->onlyAccessesInaccessibleMemory())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleMem);
else if (F->onlyAccessesInaccessibleMemOrArgMem())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleOrArgMem);
return Min;
}
static bool isWriteOnlyParam(const CallBase *Call, unsigned ArgIdx,
const TargetLibraryInfo &TLI) {
if (Call->paramHasAttr(ArgIdx, Attribute::WriteOnly))
return true;
LibFunc F;
if (Call->getCalledFunction() &&
TLI.getLibFunc(*Call->getCalledFunction(), F) &&
F == LibFunc_memset_pattern16 && TLI.has(F))
if (ArgIdx == 0)
return true;
return false;
}
ModRefInfo BasicAAResult::getArgModRefInfo(const CallBase *Call,
unsigned ArgIdx) {
if (isWriteOnlyParam(Call, ArgIdx, TLI))
return ModRefInfo::Mod;
if (Call->paramHasAttr(ArgIdx, Attribute::ReadOnly))
return ModRefInfo::Ref;
if (Call->paramHasAttr(ArgIdx, Attribute::ReadNone))
return ModRefInfo::NoModRef;
return AAResultBase::getArgModRefInfo(Call, ArgIdx);
}
#ifndef NDEBUG
static const Function *getParent(const Value *V) {
if (const Instruction *inst = dyn_cast<Instruction>(V)) {
if (!inst->getParent())
return nullptr;
return inst->getParent()->getParent();
}
if (const Argument *arg = dyn_cast<Argument>(V))
return arg->getParent();
return nullptr;
}
static bool notDifferentParent(const Value *O1, const Value *O2) {
const Function *F1 = getParent(O1);
const Function *F2 = getParent(O2);
return !F1 || !F2 || F1 == F2;
}
#endif
AliasResult BasicAAResult::alias(const MemoryLocation &LocA,
const MemoryLocation &LocB,
AAQueryInfo &AAQI) {
assert(notDifferentParent(LocA.Ptr, LocB.Ptr) &&
"BasicAliasAnalysis doesn't support interprocedural queries.");
return aliasCheck(LocA.Ptr, LocA.Size, LocB.Ptr, LocB.Size, AAQI);
}
ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
const MemoryLocation &Loc,
AAQueryInfo &AAQI) {
assert(notDifferentParent(Call, Loc.Ptr) &&
"AliasAnalysis query involving multiple functions!");
const Value *Object = getUnderlyingObject(Loc.Ptr);
if (isa<AllocaInst>(Object))
if (const CallInst *CI = dyn_cast<CallInst>(Call))
if (CI->isTailCall() &&
!CI->getAttributes().hasAttrSomewhere(Attribute::ByVal))
return ModRefInfo::NoModRef;
if (auto *AI = dyn_cast<AllocaInst>(Object))
if (!AI->isStaticAlloca() && isIntrinsicCall(Call, Intrinsic::stackrestore))
return ModRefInfo::Mod;
if (!isa<Constant>(Object) && Call != Object &&
AAQI.CI->isNotCapturedBeforeOrAt(Object, Call)) {
ModRefInfo Result = ModRefInfo::NoModRef;
bool IsMustAlias = true;
unsigned OperandNo = 0;
for (auto CI = Call->data_operands_begin(), CE = Call->data_operands_end();
CI != CE; ++CI, ++OperandNo) {
if (!(*CI)->getType()->isPointerTy() ||
(!Call->doesNotCapture(OperandNo) && OperandNo < Call->arg_size() &&
!Call->isByValArgument(OperandNo)))
continue;
if (Call->doesNotAccessMemory(OperandNo))
continue;
AliasResult AR = getBestAAResults().alias(
MemoryLocation::getBeforeOrAfter(*CI),
MemoryLocation::getBeforeOrAfter(Object), AAQI);
if (AR != AliasResult::MustAlias)
IsMustAlias = false;
if (AR == AliasResult::NoAlias)
continue;
if (Call->onlyReadsMemory(OperandNo)) {
Result = setRef(Result);
continue;
}
if (Call->onlyWritesMemory(OperandNo)) {
Result = setMod(Result);
continue;
}
Result = ModRefInfo::ModRef;
break;
}
if (isNoModRef(Result))
IsMustAlias = false;
if (!isModAndRefSet(Result)) {
if (isNoModRef(Result))
return ModRefInfo::NoModRef;
return IsMustAlias ? setMust(Result) : clearMust(Result);
}
}
if (isMallocOrCallocLikeFn(Call, &TLI)) {
if (getBestAAResults().alias(MemoryLocation::getBeforeOrAfter(Call), Loc,
AAQI) == AliasResult::NoAlias)
return ModRefInfo::NoModRef;
}
if (auto *Inst = dyn_cast<AnyMemTransferInst>(Call)) {
AliasResult SrcAA =
getBestAAResults().alias(MemoryLocation::getForSource(Inst), Loc, AAQI);
AliasResult DestAA =
getBestAAResults().alias(MemoryLocation::getForDest(Inst), Loc, AAQI);
ModRefInfo rv = ModRefInfo::NoModRef;
if (SrcAA != AliasResult::NoAlias || Call->hasReadingOperandBundles())
rv = setRef(rv);
if (DestAA != AliasResult::NoAlias || Call->hasClobberingOperandBundles())
rv = setMod(rv);
return rv;
}
if (isIntrinsicCall(Call, Intrinsic::experimental_guard))
return ModRefInfo::Ref;
if (isIntrinsicCall(Call, Intrinsic::experimental_deoptimize))
return ModRefInfo::Ref;
if (isIntrinsicCall(Call, Intrinsic::invariant_start))
return ModRefInfo::Ref;
return AAResultBase::getModRefInfo(Call, Loc, AAQI);
}
ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call1,
const CallBase *Call2,
AAQueryInfo &AAQI) {
if (isIntrinsicCall(Call1, Intrinsic::experimental_guard))
return isModSet(createModRefInfo(getModRefBehavior(Call2)))
? ModRefInfo::Ref
: ModRefInfo::NoModRef;
if (isIntrinsicCall(Call2, Intrinsic::experimental_guard))
return isModSet(createModRefInfo(getModRefBehavior(Call1)))
? ModRefInfo::Mod
: ModRefInfo::NoModRef;
return AAResultBase::getModRefInfo(Call1, Call2, AAQI);
}
static bool isBaseOfObject(const Value *V) {
return (isa<AllocaInst>(V) || isa<GlobalVariable>(V));
}
AliasResult BasicAAResult::aliasGEP(
const GEPOperator *GEP1, LocationSize V1Size,
const Value *V2, LocationSize V2Size,
const Value *UnderlyingV1, const Value *UnderlyingV2, AAQueryInfo &AAQI) {
if (!V1Size.hasValue() && !V2Size.hasValue()) {
if (!isa<GEPOperator>(V2))
return AliasResult::MayAlias;
AliasResult BaseAlias = getBestAAResults().alias(
MemoryLocation::getBeforeOrAfter(UnderlyingV1),
MemoryLocation::getBeforeOrAfter(UnderlyingV2), AAQI);
return BaseAlias == AliasResult::NoAlias ? AliasResult::NoAlias
: AliasResult::MayAlias;
}
DecomposedGEP DecompGEP1 = DecomposeGEPExpression(GEP1, DL, &AC, DT);
DecomposedGEP DecompGEP2 = DecomposeGEPExpression(V2, DL, &AC, DT);
if (DecompGEP1.Base == GEP1 && DecompGEP2.Base == V2)
return AliasResult::MayAlias;
subtractDecomposedGEPs(DecompGEP1, DecompGEP2);
if (*DecompGEP1.InBounds && DecompGEP1.VarIndices.empty() &&
V2Size.hasValue() && DecompGEP1.Offset.sge(V2Size.getValue()) &&
isBaseOfObject(DecompGEP2.Base))
return AliasResult::NoAlias;
if (isa<GEPOperator>(V2)) {
if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
V1Size.hasValue() && DecompGEP1.Offset.sle(-V1Size.getValue()) &&
isBaseOfObject(DecompGEP1.Base))
return AliasResult::NoAlias;
}
if (DecompGEP1.Offset == 0 && DecompGEP1.VarIndices.empty())
return getBestAAResults().alias(MemoryLocation(DecompGEP1.Base, V1Size),
MemoryLocation(DecompGEP2.Base, V2Size),
AAQI);
AliasResult BaseAlias = getBestAAResults().alias(
MemoryLocation::getBeforeOrAfter(DecompGEP1.Base),
MemoryLocation::getBeforeOrAfter(DecompGEP2.Base), AAQI);
if (BaseAlias != AliasResult::MustAlias) {
assert(BaseAlias == AliasResult::NoAlias ||
BaseAlias == AliasResult::MayAlias);
return BaseAlias;
}
if (DecompGEP1.VarIndices.empty()) {
APInt &Off = DecompGEP1.Offset;
const Value *LeftPtr = V2;
const Value *RightPtr = GEP1;
LocationSize VLeftSize = V2Size;
LocationSize VRightSize = V1Size;
const bool Swapped = Off.isNegative();
if (Swapped) {
std::swap(LeftPtr, RightPtr);
std::swap(VLeftSize, VRightSize);
Off = -Off;
}
if (!VLeftSize.hasValue())
return AliasResult::MayAlias;
const uint64_t LSize = VLeftSize.getValue();
if (Off.ult(LSize)) {
AliasResult AR = AliasResult::PartialAlias;
if (VRightSize.hasValue() && Off.ule(INT32_MAX) &&
(Off + VRightSize.getValue()).ule(LSize)) {
AR.setOffset(-Off.getSExtValue());
AR.swap(Swapped);
}
return AR;
}
return AliasResult::NoAlias;
}
if (!V1Size.hasValue() || !V2Size.hasValue())
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;
APInt ScaleForGCD = Scale;
if (!Index.IsNSW)
ScaleForGCD = APInt::getOneBitSet(Scale.getBitWidth(),
Scale.countTrailingZeros());
if (i == 0)
GCD = ScaleForGCD.abs();
else
GCD = APIntOps::GreatestCommonDivisor(GCD, ScaleForGCD.abs());
ConstantRange CR = computeConstantRange(Index.Val.V, false,
true, &AC, Index.CxtI);
KnownBits Known =
computeKnownBits(Index.Val.V, DL, 0, &AC, Index.CxtI, DT);
CR = CR.intersectWith(
ConstantRange::fromKnownBits(Known, 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)
OffsetRange = OffsetRange.add(CR.smul_sat(ConstantRange(Scale)));
else
OffsetRange = OffsetRange.add(CR.smul_fast(ConstantRange(Scale)));
}
APInt ModOffset = DecompGEP1.Offset.srem(GCD);
if (ModOffset.isNegative())
ModOffset += GCD; if (ModOffset.uge(V2Size.getValue()) &&
(GCD - ModOffset).uge(V1Size.getValue()))
return AliasResult::NoAlias;
unsigned BW = OffsetRange.getBitWidth();
ConstantRange Range1 = OffsetRange.add(
ConstantRange(APInt(BW, 0), APInt(BW, V1Size.getValue())));
ConstantRange Range2 =
ConstantRange(APInt(BW, 0), APInt(BW, V2Size.getValue()));
if (Range1.intersectWith(Range2).isEmptySet())
return AliasResult::NoAlias;
Optional<APInt> MinAbsVarIndex;
if (DecompGEP1.VarIndices.size() == 1) {
const VariableGEPIndex &Var = DecompGEP1.VarIndices[0];
if (Var.Val.TruncBits == 0 &&
isKnownNonZero(Var.Val.V, DL, 0, &AC, Var.CxtI, DT)) {
MinAbsVarIndex = APInt(Var.Scale.getBitWidth(), 1);
auto MultiplyByScaleNoWrap = [](const VariableGEPIndex &Var) {
if (Var.IsNSW)
return true;
int ValOrigBW = Var.Val.V->getType()->getPrimitiveSizeInBits();
int MaxScaleValueBW = Var.Val.getBitWidth() - ValOrigBW;
if (MaxScaleValueBW <= 0)
return false;
return Var.Scale.ule(
APInt::getMaxValue(MaxScaleValueBW).zext(Var.Scale.getBitWidth()));
};
if (MultiplyByScaleNoWrap(Var)) {
MinAbsVarIndex = Var.Scale.abs();
}
}
} else if (DecompGEP1.VarIndices.size() == 2) {
const VariableGEPIndex &Var0 = DecompGEP1.VarIndices[0];
const VariableGEPIndex &Var1 = DecompGEP1.VarIndices[1];
if (Var0.Scale == -Var1.Scale && Var0.Val.TruncBits == 0 &&
Var0.Val.hasSameCastsAs(Var1.Val) && VisitedPhiBBs.empty() &&
isKnownNonEqual(Var0.Val.V, Var1.Val.V, DL, &AC, nullptr,
DT))
MinAbsVarIndex = Var0.Scale.abs();
}
if (MinAbsVarIndex) {
APInt OffsetLo = DecompGEP1.Offset - *MinAbsVarIndex;
APInt OffsetHi = DecompGEP1.Offset + *MinAbsVarIndex;
if (OffsetLo.isNegative() && (-OffsetLo).uge(V1Size.getValue()) &&
OffsetHi.isNonNegative() && OffsetHi.uge(V2Size.getValue()))
return AliasResult::NoAlias;
}
if (constantOffsetHeuristic(DecompGEP1, V1Size, V2Size, &AC, DT))
return AliasResult::NoAlias;
return AliasResult::MayAlias;
}
static AliasResult MergeAliasResults(AliasResult A, AliasResult B) {
if (A == B)
return A;
if ((A == AliasResult::PartialAlias && B == AliasResult::MustAlias) ||
(B == AliasResult::PartialAlias && A == AliasResult::MustAlias))
return AliasResult::PartialAlias;
return AliasResult::MayAlias;
}
AliasResult
BasicAAResult::aliasSelect(const SelectInst *SI, LocationSize SISize,
const Value *V2, LocationSize V2Size,
AAQueryInfo &AAQI) {
if (const SelectInst *SI2 = dyn_cast<SelectInst>(V2))
if (SI->getCondition() == SI2->getCondition()) {
AliasResult Alias = getBestAAResults().alias(
MemoryLocation(SI->getTrueValue(), SISize),
MemoryLocation(SI2->getTrueValue(), V2Size), AAQI);
if (Alias == AliasResult::MayAlias)
return AliasResult::MayAlias;
AliasResult ThisAlias = getBestAAResults().alias(
MemoryLocation(SI->getFalseValue(), SISize),
MemoryLocation(SI2->getFalseValue(), V2Size), AAQI);
return MergeAliasResults(ThisAlias, Alias);
}
AliasResult Alias =
getBestAAResults().alias(MemoryLocation(SI->getTrueValue(), SISize),
MemoryLocation(V2, V2Size), AAQI);
if (Alias == AliasResult::MayAlias)
return AliasResult::MayAlias;
AliasResult ThisAlias =
getBestAAResults().alias(MemoryLocation(SI->getFalseValue(), SISize),
MemoryLocation(V2, V2Size), AAQI);
return MergeAliasResults(ThisAlias, Alias);
}
AliasResult BasicAAResult::aliasPHI(const PHINode *PN, LocationSize PNSize,
const Value *V2, LocationSize V2Size,
AAQueryInfo &AAQI) {
if (!PN->getNumIncomingValues())
return AliasResult::NoAlias;
if (const PHINode *PN2 = dyn_cast<PHINode>(V2))
if (PN2->getParent() == PN->getParent()) {
Optional<AliasResult> Alias;
for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) {
AliasResult ThisAlias = getBestAAResults().alias(
MemoryLocation(PN->getIncomingValue(i), PNSize),
MemoryLocation(
PN2->getIncomingValueForBlock(PN->getIncomingBlock(i)), V2Size),
AAQI);
if (Alias)
*Alias = MergeAliasResults(*Alias, ThisAlias);
else
Alias = ThisAlias;
if (*Alias == AliasResult::MayAlias)
break;
}
return *Alias;
}
SmallVector<Value *, 4> V1Srcs;
bool isRecursive = false;
auto CheckForRecPhi = [&](Value *PV) {
if (!EnableRecPhiAnalysis)
return false;
if (getUnderlyingObject(PV) == PN) {
isRecursive = true;
return true;
}
return false;
};
if (PV) {
const PhiValues::ValueSet &PhiValueSet = PV->getValuesForPhi(PN);
if (PhiValueSet.size() > MaxLookupSearchDepth)
return AliasResult::MayAlias;
for (Value *PV1 : PhiValueSet) {
if (CheckForRecPhi(PV1))
continue;
V1Srcs.push_back(PV1);
}
} else {
SmallPtrSet<Value *, 4> UniqueSrc;
Value *OnePhi = nullptr;
for (Value *PV1 : PN->incoming_values()) {
if (isa<PHINode>(PV1)) {
if (OnePhi && OnePhi != PV1) {
return AliasResult::MayAlias;
}
OnePhi = PV1;
}
if (CheckForRecPhi(PV1))
continue;
if (UniqueSrc.insert(PV1).second)
V1Srcs.push_back(PV1);
}
if (OnePhi && UniqueSrc.size() > 1)
return AliasResult::MayAlias;
}
if (V1Srcs.empty())
return AliasResult::MayAlias;
if (isRecursive)
PNSize = LocationSize::beforeOrAfterPointer();
bool BlockInserted = VisitedPhiBBs.insert(PN->getParent()).second;
auto _ = make_scope_exit([&]() {
if (BlockInserted)
VisitedPhiBBs.erase(PN->getParent());
});
AAQueryInfo NewAAQI = AAQI.withEmptyCache();
AAQueryInfo *UseAAQI = BlockInserted ? &NewAAQI : &AAQI;
AliasResult Alias = getBestAAResults().alias(
MemoryLocation(V1Srcs[0], PNSize), MemoryLocation(V2, V2Size), *UseAAQI);
if (Alias == AliasResult::MayAlias)
return AliasResult::MayAlias;
if (isRecursive && Alias != AliasResult::NoAlias)
return AliasResult::MayAlias;
for (unsigned i = 1, e = V1Srcs.size(); i != e; ++i) {
Value *V = V1Srcs[i];
AliasResult ThisAlias = getBestAAResults().alias(
MemoryLocation(V, PNSize), MemoryLocation(V2, V2Size), *UseAAQI);
Alias = MergeAliasResults(ThisAlias, Alias);
if (Alias == AliasResult::MayAlias)
break;
}
return Alias;
}
AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,
const Value *V2, LocationSize V2Size,
AAQueryInfo &AAQI) {
if (V1Size.isZero() || V2Size.isZero())
return AliasResult::NoAlias;
V1 = V1->stripPointerCastsForAliasAnalysis();
V2 = V2->stripPointerCastsForAliasAnalysis();
if (isa<UndefValue>(V1) || isa<UndefValue>(V2))
return AliasResult::NoAlias;
if (isValueEqualInPotentialCycles(V1, V2))
return AliasResult::MustAlias;
if (!V1->getType()->isPointerTy() || !V2->getType()->isPointerTy())
return AliasResult::NoAlias;
const Value *O1 = getUnderlyingObject(V1, MaxLookupSearchDepth);
const Value *O2 = getUnderlyingObject(V2, MaxLookupSearchDepth);
if (const ConstantPointerNull *CPN = dyn_cast<ConstantPointerNull>(O1))
if (!NullPointerIsDefined(&F, CPN->getType()->getAddressSpace()))
return AliasResult::NoAlias;
if (const ConstantPointerNull *CPN = dyn_cast<ConstantPointerNull>(O2))
if (!NullPointerIsDefined(&F, CPN->getType()->getAddressSpace()))
return AliasResult::NoAlias;
if (O1 != O2) {
if (isIdentifiedObject(O1) && isIdentifiedObject(O2))
return AliasResult::NoAlias;
if ((isa<Constant>(O1) && isIdentifiedObject(O2) && !isa<Constant>(O2)) ||
(isa<Constant>(O2) && isIdentifiedObject(O1) && !isa<Constant>(O1)))
return AliasResult::NoAlias;
if ((isa<Argument>(O1) && isIdentifiedFunctionLocal(O2)) ||
(isa<Argument>(O2) && isIdentifiedFunctionLocal(O1)))
return AliasResult::NoAlias;
if (isEscapeSource(O1) &&
AAQI.CI->isNotCapturedBeforeOrAt(O2, cast<Instruction>(O1)))
return AliasResult::NoAlias;
if (isEscapeSource(O2) &&
AAQI.CI->isNotCapturedBeforeOrAt(O1, cast<Instruction>(O2)))
return AliasResult::NoAlias;
}
bool NullIsValidLocation = NullPointerIsDefined(&F);
if ((isObjectSmallerThan(
O2, getMinimalExtentFrom(*V1, V1Size, DL, NullIsValidLocation), DL,
TLI, NullIsValidLocation)) ||
(isObjectSmallerThan(
O1, getMinimalExtentFrom(*V2, V2Size, DL, NullIsValidLocation), DL,
TLI, NullIsValidLocation)))
return AliasResult::NoAlias;
if (V1Size.mayBeBeforePointer() || V2Size.mayBeBeforePointer()) {
V1Size = LocationSize::afterPointer();
V2Size = LocationSize::afterPointer();
}
if (AAQI.Depth >= 512)
return AliasResult::MayAlias;
AAQueryInfo::LocPair Locs({V1, V1Size}, {V2, V2Size});
const bool Swapped = V1 > V2;
if (Swapped)
std::swap(Locs.first, Locs.second);
const auto &Pair = AAQI.AliasCache.try_emplace(
Locs, AAQueryInfo::CacheEntry{AliasResult::NoAlias, 0});
if (!Pair.second) {
auto &Entry = Pair.first->second;
if (!Entry.isDefinitive()) {
++Entry.NumAssumptionUses;
++AAQI.NumAssumptionUses;
}
auto Result = Entry.Result;
Result.swap(Swapped);
return Result;
}
int OrigNumAssumptionUses = AAQI.NumAssumptionUses;
unsigned OrigNumAssumptionBasedResults = AAQI.AssumptionBasedResults.size();
AliasResult Result =
aliasCheckRecursive(V1, V1Size, V2, V2Size, AAQI, O1, O2);
auto It = AAQI.AliasCache.find(Locs);
assert(It != AAQI.AliasCache.end() && "Must be in cache");
auto &Entry = It->second;
bool AssumptionDisproven =
Entry.NumAssumptionUses > 0 && Result != AliasResult::NoAlias;
if (AssumptionDisproven)
Result = AliasResult::MayAlias;
AAQI.NumAssumptionUses -= Entry.NumAssumptionUses;
Entry.Result = Result;
Entry.Result.swap(Swapped);
Entry.NumAssumptionUses = -1;
if (AssumptionDisproven)
while (AAQI.AssumptionBasedResults.size() > OrigNumAssumptionBasedResults)
AAQI.AliasCache.erase(AAQI.AssumptionBasedResults.pop_back_val());
if (OrigNumAssumptionUses != AAQI.NumAssumptionUses &&
Result != AliasResult::MayAlias)
AAQI.AssumptionBasedResults.push_back(Locs);
return Result;
}
AliasResult BasicAAResult::aliasCheckRecursive(
const Value *V1, LocationSize V1Size,
const Value *V2, LocationSize V2Size,
AAQueryInfo &AAQI, const Value *O1, const Value *O2) {
if (const GEPOperator *GV1 = dyn_cast<GEPOperator>(V1)) {
AliasResult Result = aliasGEP(GV1, V1Size, V2, V2Size, O1, O2, AAQI);
if (Result != AliasResult::MayAlias)
return Result;
} else if (const GEPOperator *GV2 = dyn_cast<GEPOperator>(V2)) {
AliasResult Result = aliasGEP(GV2, V2Size, V1, V1Size, O2, O1, AAQI);
Result.swap();
if (Result != AliasResult::MayAlias)
return Result;
}
if (const PHINode *PN = dyn_cast<PHINode>(V1)) {
AliasResult Result = aliasPHI(PN, V1Size, V2, V2Size, AAQI);
if (Result != AliasResult::MayAlias)
return Result;
} else if (const PHINode *PN = dyn_cast<PHINode>(V2)) {
AliasResult Result = aliasPHI(PN, V2Size, V1, V1Size, AAQI);
Result.swap();
if (Result != AliasResult::MayAlias)
return Result;
}
if (const SelectInst *S1 = dyn_cast<SelectInst>(V1)) {
AliasResult Result = aliasSelect(S1, V1Size, V2, V2Size, AAQI);
if (Result != AliasResult::MayAlias)
return Result;
} else if (const SelectInst *S2 = dyn_cast<SelectInst>(V2)) {
AliasResult Result = aliasSelect(S2, V2Size, V1, V1Size, AAQI);
Result.swap();
if (Result != AliasResult::MayAlias)
return Result;
}
if (O1 == O2) {
bool NullIsValidLocation = NullPointerIsDefined(&F);
if (V1Size.isPrecise() && V2Size.isPrecise() &&
(isObjectSize(O1, V1Size.getValue(), DL, TLI, NullIsValidLocation) ||
isObjectSize(O2, V2Size.getValue(), DL, TLI, NullIsValidLocation)))
return AliasResult::PartialAlias;
}
return AliasResult::MayAlias;
}
bool BasicAAResult::isValueEqualInPotentialCycles(const Value *V,
const Value *V2) {
if (V != V2)
return false;
const Instruction *Inst = dyn_cast<Instruction>(V);
if (!Inst)
return true;
if (VisitedPhiBBs.empty())
return true;
if (VisitedPhiBBs.size() > MaxNumPhiBBsValueReachabilityCheck)
return false;
for (const auto *P : VisitedPhiBBs)
if (isPotentiallyReachable(&P->front(), Inst, nullptr, DT))
return false;
return true;
}
void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP,
const DecomposedGEP &SrcGEP) {
DestGEP.Offset -= SrcGEP.Offset;
for (const VariableGEPIndex &Src : SrcGEP.VarIndices) {
bool Found = false;
for (auto I : enumerate(DestGEP.VarIndices)) {
VariableGEPIndex &Dest = I.value();
if (!isValueEqualInPotentialCycles(Dest.Val.V, Src.Val.V) ||
!Dest.Val.hasSameCastsAs(Src.Val))
continue;
if (Dest.Scale != Src.Scale) {
Dest.Scale -= Src.Scale;
Dest.IsNSW = false;
} else {
DestGEP.VarIndices.erase(DestGEP.VarIndices.begin() + I.index());
}
Found = true;
break;
}
if (!Found) {
VariableGEPIndex Entry = {Src.Val, -Src.Scale, Src.CxtI, Src.IsNSW};
DestGEP.VarIndices.push_back(Entry);
}
}
}
bool BasicAAResult::constantOffsetHeuristic(
const DecomposedGEP &GEP, LocationSize MaybeV1Size,
LocationSize MaybeV2Size, AssumptionCache *AC, DominatorTree *DT) {
if (GEP.VarIndices.size() != 2 || !MaybeV1Size.hasValue() ||
!MaybeV2Size.hasValue())
return false;
const uint64_t V1Size = MaybeV1Size.getValue();
const uint64_t V2Size = MaybeV2Size.getValue();
const VariableGEPIndex &Var0 = GEP.VarIndices[0], &Var1 = GEP.VarIndices[1];
if (Var0.Val.TruncBits != 0 || !Var0.Val.hasSameCastsAs(Var1.Val) ||
Var0.Scale != -Var1.Scale ||
Var0.Val.V->getType() != Var1.Val.V->getType())
return false;
LinearExpression E0 =
GetLinearExpression(CastedValue(Var0.Val.V), DL, 0, AC, DT);
LinearExpression E1 =
GetLinearExpression(CastedValue(Var1.Val.V), DL, 0, AC, DT);
if (E0.Scale != E1.Scale || !E0.Val.hasSameCastsAs(E1.Val) ||
!isValueEqualInPotentialCycles(E0.Val.V, E1.Val.V))
return false;
APInt MinDiff = E0.Offset - E1.Offset, Wrapped = -MinDiff;
MinDiff = APIntOps::umin(MinDiff, Wrapped);
APInt MinDiffBytes =
MinDiff.zextOrTrunc(Var0.Scale.getBitWidth()) * Var0.Scale.abs();
return MinDiffBytes.uge(V1Size + GEP.Offset.abs()) &&
MinDiffBytes.uge(V2Size + GEP.Offset.abs());
}
AnalysisKey BasicAA::Key;
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);
auto *PV = AM.getCachedResult<PhiValuesAnalysis>(F);
return BasicAAResult(F.getParent()->getDataLayout(), F, TLI, AC, DT, PV);
}
BasicAAWrapperPass::BasicAAWrapperPass() : FunctionPass(ID) {
initializeBasicAAWrapperPassPass(*PassRegistry::getPassRegistry());
}
char BasicAAWrapperPass::ID = 0;
void BasicAAWrapperPass::anchor() {}
INITIALIZE_PASS_BEGIN(BasicAAWrapperPass, "basic-aa",
"Basic Alias Analysis (stateless AA impl)", true, true)
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(PhiValuesWrapperPass)
INITIALIZE_PASS_END(BasicAAWrapperPass, "basic-aa",
"Basic Alias Analysis (stateless AA impl)", true, true)
FunctionPass *llvm::createBasicAAWrapperPass() {
return new BasicAAWrapperPass();
}
bool BasicAAWrapperPass::runOnFunction(Function &F) {
auto &ACT = getAnalysis<AssumptionCacheTracker>();
auto &TLIWP = getAnalysis<TargetLibraryInfoWrapperPass>();
auto &DTWP = getAnalysis<DominatorTreeWrapperPass>();
auto *PVWP = getAnalysisIfAvailable<PhiValuesWrapperPass>();
Result.reset(new BasicAAResult(F.getParent()->getDataLayout(), F,
TLIWP.getTLI(F), ACT.getAssumptionCache(F),
&DTWP.getDomTree(),
PVWP ? &PVWP->getResult() : nullptr));
return false;
}
void BasicAAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequiredTransitive<AssumptionCacheTracker>();
AU.addRequiredTransitive<DominatorTreeWrapperPass>();
AU.addRequiredTransitive<TargetLibraryInfoWrapperPass>();
AU.addUsedIfAvailable<PhiValuesWrapperPass>();
}
BasicAAResult llvm::createLegacyPMBasicAAResult(Pass &P, Function &F) {
return BasicAAResult(
F.getParent()->getDataLayout(), F,
P.getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F),
P.getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F));
}