aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/IPO/Attributor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Transforms/IPO/Attributor.cpp')
-rw-r--r--llvm/lib/Transforms/IPO/Attributor.cpp739
1 files changed, 609 insertions, 130 deletions
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 03ad45135001..762317425026 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Analysis/InlineCost.h"
@@ -24,9 +25,16 @@
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/MustExecute.h"
#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constant.h"
+#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/NoFolder.h"
+#include "llvm/IR/ValueHandle.h"
#include "llvm/IR/Verifier.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/Casting.h"
@@ -62,8 +70,6 @@ STATISTIC(NumAttributesValidFixpoint,
"Number of abstract attributes in a valid fixpoint state");
STATISTIC(NumAttributesManifested,
"Number of abstract attributes manifested in IR");
-STATISTIC(NumAttributesFixedDueToRequiredDependences,
- "Number of abstract attributes fixed due to required dependences");
// TODO: Determine a good default value.
//
@@ -74,7 +80,7 @@ STATISTIC(NumAttributesFixedDueToRequiredDependences,
// This will become more evolved once we perform two interleaved fixpoint
// iterations: bottom-up and top-down.
static cl::opt<unsigned>
- MaxFixpointIterations("attributor-max-iterations", cl::Hidden,
+ SetFixpointIterations("attributor-max-iterations", cl::Hidden,
cl::desc("Maximal number of fixpoint iterations."),
cl::init(32));
@@ -141,17 +147,221 @@ static cl::opt<bool> PrintDependencies("attributor-print-dep", cl::Hidden,
cl::desc("Print attribute dependencies"),
cl::init(false));
+static cl::opt<bool> EnableCallSiteSpecific(
+ "attributor-enable-call-site-specific-deduction", cl::Hidden,
+ cl::desc("Allow the Attributor to do call site specific analysis"),
+ cl::init(false));
+
+static cl::opt<bool>
+ PrintCallGraph("attributor-print-call-graph", cl::Hidden,
+ cl::desc("Print Attributor's internal call graph"),
+ cl::init(false));
+
+static cl::opt<bool> SimplifyAllLoads("attributor-simplify-all-loads",
+ cl::Hidden,
+ cl::desc("Try to simplify all loads."),
+ cl::init(true));
+
/// Logic operators for the change status enum class.
///
///{
ChangeStatus llvm::operator|(ChangeStatus L, ChangeStatus R) {
return L == ChangeStatus::CHANGED ? L : R;
}
+ChangeStatus &llvm::operator|=(ChangeStatus &L, ChangeStatus R) {
+ L = L | R;
+ return L;
+}
ChangeStatus llvm::operator&(ChangeStatus L, ChangeStatus R) {
return L == ChangeStatus::UNCHANGED ? L : R;
}
+ChangeStatus &llvm::operator&=(ChangeStatus &L, ChangeStatus R) {
+ L = L & R;
+ return L;
+}
///}
+bool AA::isDynamicallyUnique(Attributor &A, const AbstractAttribute &QueryingAA,
+ const Value &V) {
+ if (auto *C = dyn_cast<Constant>(&V))
+ return !C->isThreadDependent();
+ // TODO: Inspect and cache more complex instructions.
+ if (auto *CB = dyn_cast<CallBase>(&V))
+ return CB->getNumOperands() == 0 && !CB->mayHaveSideEffects() &&
+ !CB->mayReadFromMemory();
+ const Function *Scope = nullptr;
+ if (auto *I = dyn_cast<Instruction>(&V))
+ Scope = I->getFunction();
+ if (auto *A = dyn_cast<Argument>(&V))
+ Scope = A->getParent();
+ if (!Scope)
+ return false;
+ auto &NoRecurseAA = A.getAAFor<AANoRecurse>(
+ QueryingAA, IRPosition::function(*Scope), DepClassTy::OPTIONAL);
+ return NoRecurseAA.isAssumedNoRecurse();
+}
+
+Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty) {
+ if (isa<AllocaInst>(Obj))
+ return UndefValue::get(&Ty);
+ auto *GV = dyn_cast<GlobalVariable>(&Obj);
+ if (!GV || !GV->hasLocalLinkage())
+ return nullptr;
+ if (!GV->hasInitializer())
+ return UndefValue::get(&Ty);
+ return dyn_cast_or_null<Constant>(getWithType(*GV->getInitializer(), Ty));
+}
+
+bool AA::isValidInScope(const Value &V, const Function *Scope) {
+ if (isa<Constant>(V))
+ return true;
+ if (auto *I = dyn_cast<Instruction>(&V))
+ return I->getFunction() == Scope;
+ if (auto *A = dyn_cast<Argument>(&V))
+ return A->getParent() == Scope;
+ return false;
+}
+
+bool AA::isValidAtPosition(const Value &V, const Instruction &CtxI,
+ InformationCache &InfoCache) {
+ if (isa<Constant>(V))
+ return true;
+ const Function *Scope = CtxI.getFunction();
+ if (auto *A = dyn_cast<Argument>(&V))
+ return A->getParent() == Scope;
+ if (auto *I = dyn_cast<Instruction>(&V))
+ if (I->getFunction() == Scope) {
+ const DominatorTree *DT =
+ InfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(*Scope);
+ return DT && DT->dominates(I, &CtxI);
+ }
+ return false;
+}
+
+Value *AA::getWithType(Value &V, Type &Ty) {
+ if (V.getType() == &Ty)
+ return &V;
+ if (isa<PoisonValue>(V))
+ return PoisonValue::get(&Ty);
+ if (isa<UndefValue>(V))
+ return UndefValue::get(&Ty);
+ if (auto *C = dyn_cast<Constant>(&V)) {
+ if (C->isNullValue())
+ return Constant::getNullValue(&Ty);
+ if (C->getType()->isPointerTy() && Ty.isPointerTy())
+ return ConstantExpr::getPointerCast(C, &Ty);
+ if (C->getType()->isIntegerTy() && Ty.isIntegerTy())
+ return ConstantExpr::getTrunc(C, &Ty, /* OnlyIfReduced */ true);
+ if (C->getType()->isFloatingPointTy() && Ty.isFloatingPointTy())
+ return ConstantExpr::getFPTrunc(C, &Ty, /* OnlyIfReduced */ true);
+ }
+ return nullptr;
+}
+
+Optional<Value *>
+AA::combineOptionalValuesInAAValueLatice(const Optional<Value *> &A,
+ const Optional<Value *> &B, Type *Ty) {
+ if (A == B)
+ return A;
+ if (!B.hasValue())
+ return A;
+ if (*B == nullptr)
+ return nullptr;
+ if (!A.hasValue())
+ return Ty ? getWithType(**B, *Ty) : nullptr;
+ if (*A == nullptr)
+ return nullptr;
+ if (!Ty)
+ Ty = (*A)->getType();
+ if (isa_and_nonnull<UndefValue>(*A))
+ return getWithType(**B, *Ty);
+ if (isa<UndefValue>(*B))
+ return A;
+ if (*A && *B && *A == getWithType(**B, *Ty))
+ return A;
+ return nullptr;
+}
+
+bool AA::getPotentialCopiesOfStoredValue(
+ Attributor &A, StoreInst &SI, SmallSetVector<Value *, 4> &PotentialCopies,
+ const AbstractAttribute &QueryingAA, bool &UsedAssumedInformation) {
+
+ Value &Ptr = *SI.getPointerOperand();
+ SmallVector<Value *, 8> Objects;
+ if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, QueryingAA, &SI)) {
+ LLVM_DEBUG(
+ dbgs() << "Underlying objects stored into could not be determined\n";);
+ return false;
+ }
+
+ SmallVector<const AAPointerInfo *> PIs;
+ SmallVector<Value *> NewCopies;
+
+ for (Value *Obj : Objects) {
+ LLVM_DEBUG(dbgs() << "Visit underlying object " << *Obj << "\n");
+ if (isa<UndefValue>(Obj))
+ continue;
+ if (isa<ConstantPointerNull>(Obj)) {
+ // A null pointer access can be undefined but any offset from null may
+ // be OK. We do not try to optimize the latter.
+ if (!NullPointerIsDefined(SI.getFunction(),
+ Ptr.getType()->getPointerAddressSpace()) &&
+ A.getAssumedSimplified(Ptr, QueryingAA, UsedAssumedInformation) ==
+ Obj)
+ continue;
+ LLVM_DEBUG(
+ dbgs() << "Underlying object is a valid nullptr, giving up.\n";);
+ return false;
+ }
+ if (!isa<AllocaInst>(Obj) && !isa<GlobalVariable>(Obj)) {
+ LLVM_DEBUG(dbgs() << "Underlying object is not supported yet: " << *Obj
+ << "\n";);
+ return false;
+ }
+ if (auto *GV = dyn_cast<GlobalVariable>(Obj))
+ if (!GV->hasLocalLinkage()) {
+ LLVM_DEBUG(dbgs() << "Underlying object is global with external "
+ "linkage, not supported yet: "
+ << *Obj << "\n";);
+ return false;
+ }
+
+ auto CheckAccess = [&](const AAPointerInfo::Access &Acc, bool IsExact) {
+ if (!Acc.isRead())
+ return true;
+ auto *LI = dyn_cast<LoadInst>(Acc.getRemoteInst());
+ if (!LI) {
+ LLVM_DEBUG(dbgs() << "Underlying object read through a non-load "
+ "instruction not supported yet: "
+ << *Acc.getRemoteInst() << "\n";);
+ return false;
+ }
+ NewCopies.push_back(LI);
+ return true;
+ };
+
+ auto &PI = A.getAAFor<AAPointerInfo>(QueryingAA, IRPosition::value(*Obj),
+ DepClassTy::NONE);
+ if (!PI.forallInterferingAccesses(SI, CheckAccess)) {
+ LLVM_DEBUG(
+ dbgs()
+ << "Failed to verify all interfering accesses for underlying object: "
+ << *Obj << "\n");
+ return false;
+ }
+ PIs.push_back(&PI);
+ }
+
+ for (auto *PI : PIs) {
+ if (!PI->getState().isAtFixpoint())
+ UsedAssumedInformation = true;
+ A.recordDependence(*PI, QueryingAA, DepClassTy::OPTIONAL);
+ }
+ PotentialCopies.insert(NewCopies.begin(), NewCopies.end());
+
+ return true;
+}
+
/// Return true if \p New is equal or worse than \p Old.
static bool isEqualOrWorse(const Attribute &New, const Attribute &Old) {
if (!Old.isIntAttribute())
@@ -164,12 +374,14 @@ static bool isEqualOrWorse(const Attribute &New, const Attribute &Old) {
/// attribute list \p Attrs. This is only the case if it was not already present
/// in \p Attrs at the position describe by \p PK and \p AttrIdx.
static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
- AttributeList &Attrs, int AttrIdx) {
+ AttributeList &Attrs, int AttrIdx,
+ bool ForceReplace = false) {
if (Attr.isEnumAttribute()) {
Attribute::AttrKind Kind = Attr.getKindAsEnum();
if (Attrs.hasAttribute(AttrIdx, Kind))
- if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
+ if (!ForceReplace &&
+ isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
return false;
Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr);
return true;
@@ -177,7 +389,8 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
if (Attr.isStringAttribute()) {
StringRef Kind = Attr.getKindAsString();
if (Attrs.hasAttribute(AttrIdx, Kind))
- if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
+ if (!ForceReplace &&
+ isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
return false;
Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr);
return true;
@@ -185,7 +398,8 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
if (Attr.isIntAttribute()) {
Attribute::AttrKind Kind = Attr.getKindAsEnum();
if (Attrs.hasAttribute(AttrIdx, Kind))
- if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
+ if (!ForceReplace &&
+ isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind)))
return false;
Attrs = Attrs.removeAttribute(Ctx, AttrIdx, Kind);
Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr);
@@ -266,7 +480,8 @@ ChangeStatus AbstractAttribute::update(Attributor &A) {
ChangeStatus
IRAttributeManifest::manifestAttrs(Attributor &A, const IRPosition &IRP,
- const ArrayRef<Attribute> &DeducedAttrs) {
+ const ArrayRef<Attribute> &DeducedAttrs,
+ bool ForceReplace) {
Function *ScopeFn = IRP.getAnchorScope();
IRPosition::Kind PK = IRP.getPositionKind();
@@ -294,7 +509,7 @@ IRAttributeManifest::manifestAttrs(Attributor &A, const IRPosition &IRP,
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
LLVMContext &Ctx = IRP.getAnchorValue().getContext();
for (const Attribute &Attr : DeducedAttrs) {
- if (!addIfNotExistent(Ctx, Attr, Attrs, IRP.getAttrIdx()))
+ if (!addIfNotExistent(Ctx, Attr, Attrs, IRP.getAttrIdx(), ForceReplace))
continue;
HasChanged = ChangeStatus::CHANGED;
@@ -475,6 +690,8 @@ void IRPosition::verify() {
#ifdef EXPENSIVE_CHECKS
switch (getPositionKind()) {
case IRP_INVALID:
+ assert((CBContext == nullptr) &&
+ "Invalid position must not have CallBaseContext!");
assert(!Enc.getOpaqueValue() &&
"Expected a nullptr for an invalid position!");
return;
@@ -490,12 +707,16 @@ void IRPosition::verify() {
"Associated value mismatch!");
return;
case IRP_CALL_SITE_RETURNED:
+ assert((CBContext == nullptr) &&
+ "'call site returned' position must not have CallBaseContext!");
assert((isa<CallBase>(getAsValuePtr())) &&
"Expected call base for 'call site returned' position!");
assert(getAsValuePtr() == &getAssociatedValue() &&
"Associated value mismatch!");
return;
case IRP_CALL_SITE:
+ assert((CBContext == nullptr) &&
+ "'call site function' position must not have CallBaseContext!");
assert((isa<CallBase>(getAsValuePtr())) &&
"Expected call base for 'call site function' position!");
assert(getAsValuePtr() == &getAssociatedValue() &&
@@ -514,6 +735,8 @@ void IRPosition::verify() {
"Associated value mismatch!");
return;
case IRP_CALL_SITE_ARGUMENT: {
+ assert((CBContext == nullptr) &&
+ "'call site argument' position must not have CallBaseContext!");
Use *U = getAsUsePtr();
assert(U && "Expected use for a 'call site argument' position!");
assert(isa<CallBase>(U->getUser()) &&
@@ -532,13 +755,25 @@ void IRPosition::verify() {
}
Optional<Constant *>
-Attributor::getAssumedConstant(const Value &V, const AbstractAttribute &AA,
+Attributor::getAssumedConstant(const IRPosition &IRP,
+ const AbstractAttribute &AA,
bool &UsedAssumedInformation) {
- const auto &ValueSimplifyAA = getAAFor<AAValueSimplify>(
- AA, IRPosition::value(V), /* TrackDependence */ false);
+ // First check all callbacks provided by outside AAs. If any of them returns
+ // a non-null value that is different from the associated value, or None, we
+ // assume it's simpliied.
+ for (auto &CB : SimplificationCallbacks.lookup(IRP)) {
+ Optional<Value *> SimplifiedV = CB(IRP, &AA, UsedAssumedInformation);
+ if (!SimplifiedV.hasValue())
+ return llvm::None;
+ if (isa_and_nonnull<Constant>(*SimplifiedV))
+ return cast<Constant>(*SimplifiedV);
+ return nullptr;
+ }
+ const auto &ValueSimplifyAA =
+ getAAFor<AAValueSimplify>(AA, IRP, DepClassTy::NONE);
Optional<Value *> SimplifiedV =
ValueSimplifyAA.getAssumedSimplifiedValue(*this);
- bool IsKnown = ValueSimplifyAA.isKnown();
+ bool IsKnown = ValueSimplifyAA.isAtFixpoint();
UsedAssumedInformation |= !IsKnown;
if (!SimplifiedV.hasValue()) {
recordDependence(ValueSimplifyAA, AA, DepClassTy::OPTIONAL);
@@ -546,18 +781,66 @@ Attributor::getAssumedConstant(const Value &V, const AbstractAttribute &AA,
}
if (isa_and_nonnull<UndefValue>(SimplifiedV.getValue())) {
recordDependence(ValueSimplifyAA, AA, DepClassTy::OPTIONAL);
- return llvm::None;
+ return UndefValue::get(IRP.getAssociatedType());
}
Constant *CI = dyn_cast_or_null<Constant>(SimplifiedV.getValue());
- if (CI && CI->getType() != V.getType()) {
- // TODO: Check for a save conversion.
- return nullptr;
- }
+ if (CI)
+ CI = dyn_cast_or_null<Constant>(
+ AA::getWithType(*CI, *IRP.getAssociatedType()));
if (CI)
recordDependence(ValueSimplifyAA, AA, DepClassTy::OPTIONAL);
return CI;
}
+Optional<Value *>
+Attributor::getAssumedSimplified(const IRPosition &IRP,
+ const AbstractAttribute *AA,
+ bool &UsedAssumedInformation) {
+ // First check all callbacks provided by outside AAs. If any of them returns
+ // a non-null value that is different from the associated value, or None, we
+ // assume it's simpliied.
+ for (auto &CB : SimplificationCallbacks.lookup(IRP))
+ return CB(IRP, AA, UsedAssumedInformation);
+
+ // If no high-level/outside simplification occured, use AAValueSimplify.
+ const auto &ValueSimplifyAA =
+ getOrCreateAAFor<AAValueSimplify>(IRP, AA, DepClassTy::NONE);
+ Optional<Value *> SimplifiedV =
+ ValueSimplifyAA.getAssumedSimplifiedValue(*this);
+ bool IsKnown = ValueSimplifyAA.isAtFixpoint();
+ UsedAssumedInformation |= !IsKnown;
+ if (!SimplifiedV.hasValue()) {
+ if (AA)
+ recordDependence(ValueSimplifyAA, *AA, DepClassTy::OPTIONAL);
+ return llvm::None;
+ }
+ if (*SimplifiedV == nullptr)
+ return const_cast<Value *>(&IRP.getAssociatedValue());
+ if (Value *SimpleV =
+ AA::getWithType(**SimplifiedV, *IRP.getAssociatedType())) {
+ if (AA)
+ recordDependence(ValueSimplifyAA, *AA, DepClassTy::OPTIONAL);
+ return SimpleV;
+ }
+ return const_cast<Value *>(&IRP.getAssociatedValue());
+}
+
+Optional<Value *> Attributor::translateArgumentToCallSiteContent(
+ Optional<Value *> V, CallBase &CB, const AbstractAttribute &AA,
+ bool &UsedAssumedInformation) {
+ if (!V.hasValue())
+ return V;
+ if (*V == nullptr || isa<Constant>(*V))
+ return V;
+ if (auto *Arg = dyn_cast<Argument>(*V))
+ if (CB.getCalledFunction() == Arg->getParent())
+ if (!Arg->hasPointeeInMemoryValueAttr())
+ return getAssumedSimplified(
+ IRPosition::callsite_argument(CB, Arg->getArgNo()), AA,
+ UsedAssumedInformation);
+ return nullptr;
+}
+
Attributor::~Attributor() {
// The abstract attributes are allocated via the BumpPtrAllocator Allocator,
// thus we cannot delete them. We can, and want to, destruct them though.
@@ -569,21 +852,24 @@ Attributor::~Attributor() {
bool Attributor::isAssumedDead(const AbstractAttribute &AA,
const AAIsDead *FnLivenessAA,
+ bool &UsedAssumedInformation,
bool CheckBBLivenessOnly, DepClassTy DepClass) {
const IRPosition &IRP = AA.getIRPosition();
if (!Functions.count(IRP.getAnchorScope()))
return false;
- return isAssumedDead(IRP, &AA, FnLivenessAA, CheckBBLivenessOnly, DepClass);
+ return isAssumedDead(IRP, &AA, FnLivenessAA, UsedAssumedInformation,
+ CheckBBLivenessOnly, DepClass);
}
bool Attributor::isAssumedDead(const Use &U,
const AbstractAttribute *QueryingAA,
const AAIsDead *FnLivenessAA,
+ bool &UsedAssumedInformation,
bool CheckBBLivenessOnly, DepClassTy DepClass) {
Instruction *UserI = dyn_cast<Instruction>(U.getUser());
if (!UserI)
return isAssumedDead(IRPosition::value(*U.get()), QueryingAA, FnLivenessAA,
- CheckBBLivenessOnly, DepClass);
+ UsedAssumedInformation, CheckBBLivenessOnly, DepClass);
if (auto *CB = dyn_cast<CallBase>(UserI)) {
// For call site argument uses we can check if the argument is
@@ -592,30 +878,38 @@ bool Attributor::isAssumedDead(const Use &U,
const IRPosition &CSArgPos =
IRPosition::callsite_argument(*CB, CB->getArgOperandNo(&U));
return isAssumedDead(CSArgPos, QueryingAA, FnLivenessAA,
- CheckBBLivenessOnly, DepClass);
+ UsedAssumedInformation, CheckBBLivenessOnly,
+ DepClass);
}
} else if (ReturnInst *RI = dyn_cast<ReturnInst>(UserI)) {
const IRPosition &RetPos = IRPosition::returned(*RI->getFunction());
- return isAssumedDead(RetPos, QueryingAA, FnLivenessAA, CheckBBLivenessOnly,
- DepClass);
+ return isAssumedDead(RetPos, QueryingAA, FnLivenessAA,
+ UsedAssumedInformation, CheckBBLivenessOnly, DepClass);
} else if (PHINode *PHI = dyn_cast<PHINode>(UserI)) {
BasicBlock *IncomingBB = PHI->getIncomingBlock(U);
return isAssumedDead(*IncomingBB->getTerminator(), QueryingAA, FnLivenessAA,
- CheckBBLivenessOnly, DepClass);
+ UsedAssumedInformation, CheckBBLivenessOnly, DepClass);
}
return isAssumedDead(IRPosition::value(*UserI), QueryingAA, FnLivenessAA,
- CheckBBLivenessOnly, DepClass);
+ UsedAssumedInformation, CheckBBLivenessOnly, DepClass);
}
bool Attributor::isAssumedDead(const Instruction &I,
const AbstractAttribute *QueryingAA,
const AAIsDead *FnLivenessAA,
+ bool &UsedAssumedInformation,
bool CheckBBLivenessOnly, DepClassTy DepClass) {
+ const IRPosition::CallBaseContext *CBCtx =
+ QueryingAA ? QueryingAA->getCallBaseContext() : nullptr;
+
+ if (ManifestAddedBlocks.contains(I.getParent()))
+ return false;
+
if (!FnLivenessAA)
- FnLivenessAA = lookupAAFor<AAIsDead>(IRPosition::function(*I.getFunction()),
- QueryingAA,
- /* TrackDependence */ false);
+ FnLivenessAA =
+ lookupAAFor<AAIsDead>(IRPosition::function(*I.getFunction(), CBCtx),
+ QueryingAA, DepClassTy::NONE);
// If we have a context instruction and a liveness AA we use it.
if (FnLivenessAA &&
@@ -623,6 +917,8 @@ bool Attributor::isAssumedDead(const Instruction &I,
FnLivenessAA->isAssumedDead(&I)) {
if (QueryingAA)
recordDependence(*FnLivenessAA, *QueryingAA, DepClass);
+ if (!FnLivenessAA->isKnownDead(&I))
+ UsedAssumedInformation = true;
return true;
}
@@ -630,7 +926,7 @@ bool Attributor::isAssumedDead(const Instruction &I,
return false;
const AAIsDead &IsDeadAA = getOrCreateAAFor<AAIsDead>(
- IRPosition::value(I), QueryingAA, /* TrackDependence */ false);
+ IRPosition::value(I, CBCtx), QueryingAA, DepClassTy::NONE);
// Don't check liveness for AAIsDead.
if (QueryingAA == &IsDeadAA)
return false;
@@ -638,6 +934,8 @@ bool Attributor::isAssumedDead(const Instruction &I,
if (IsDeadAA.isAssumedDead()) {
if (QueryingAA)
recordDependence(IsDeadAA, *QueryingAA, DepClass);
+ if (!IsDeadAA.isKnownDead())
+ UsedAssumedInformation = true;
return true;
}
@@ -647,10 +945,11 @@ bool Attributor::isAssumedDead(const Instruction &I,
bool Attributor::isAssumedDead(const IRPosition &IRP,
const AbstractAttribute *QueryingAA,
const AAIsDead *FnLivenessAA,
+ bool &UsedAssumedInformation,
bool CheckBBLivenessOnly, DepClassTy DepClass) {
Instruction *CtxI = IRP.getCtxI();
if (CtxI &&
- isAssumedDead(*CtxI, QueryingAA, FnLivenessAA,
+ isAssumedDead(*CtxI, QueryingAA, FnLivenessAA, UsedAssumedInformation,
/* CheckBBLivenessOnly */ true,
CheckBBLivenessOnly ? DepClass : DepClassTy::OPTIONAL))
return true;
@@ -663,10 +962,9 @@ bool Attributor::isAssumedDead(const IRPosition &IRP,
if (IRP.getPositionKind() == IRPosition::IRP_CALL_SITE)
IsDeadAA = &getOrCreateAAFor<AAIsDead>(
IRPosition::callsite_returned(cast<CallBase>(IRP.getAssociatedValue())),
- QueryingAA, /* TrackDependence */ false);
+ QueryingAA, DepClassTy::NONE);
else
- IsDeadAA = &getOrCreateAAFor<AAIsDead>(IRP, QueryingAA,
- /* TrackDependence */ false);
+ IsDeadAA = &getOrCreateAAFor<AAIsDead>(IRP, QueryingAA, DepClassTy::NONE);
// Don't check liveness for AAIsDead.
if (QueryingAA == IsDeadAA)
return false;
@@ -674,6 +972,24 @@ bool Attributor::isAssumedDead(const IRPosition &IRP,
if (IsDeadAA->isAssumedDead()) {
if (QueryingAA)
recordDependence(*IsDeadAA, *QueryingAA, DepClass);
+ if (!IsDeadAA->isKnownDead())
+ UsedAssumedInformation = true;
+ return true;
+ }
+
+ return false;
+}
+
+bool Attributor::isAssumedDead(const BasicBlock &BB,
+ const AbstractAttribute *QueryingAA,
+ const AAIsDead *FnLivenessAA,
+ DepClassTy DepClass) {
+ if (!FnLivenessAA)
+ FnLivenessAA = lookupAAFor<AAIsDead>(IRPosition::function(*BB.getParent()),
+ QueryingAA, DepClassTy::NONE);
+ if (FnLivenessAA->isAssumedDead(&BB)) {
+ if (QueryingAA)
+ recordDependence(*FnLivenessAA, *QueryingAA, DepClass);
return true;
}
@@ -682,25 +998,13 @@ bool Attributor::isAssumedDead(const IRPosition &IRP,
bool Attributor::checkForAllUses(function_ref<bool(const Use &, bool &)> Pred,
const AbstractAttribute &QueryingAA,
- const Value &V, DepClassTy LivenessDepClass) {
+ const Value &V, bool CheckBBLivenessOnly,
+ DepClassTy LivenessDepClass) {
// Check the trivial case first as it catches void values.
if (V.use_empty())
return true;
- // If the value is replaced by another one, for now a constant, we do not have
- // uses. Note that this requires users of `checkForAllUses` to not recurse but
- // instead use the `follow` callback argument to look at transitive users,
- // however, that should be clear from the presence of the argument.
- bool UsedAssumedInformation = false;
- Optional<Constant *> C =
- getAssumedConstant(V, QueryingAA, UsedAssumedInformation);
- if (C.hasValue() && C.getValue()) {
- LLVM_DEBUG(dbgs() << "[Attributor] Value is simplified, uses skipped: " << V
- << " -> " << *C.getValue() << "\n");
- return true;
- }
-
const IRPosition &IRP = QueryingAA.getIRPosition();
SmallVector<const Use *, 16> Worklist;
SmallPtrSet<const Use *, 16> Visited;
@@ -714,7 +1018,7 @@ bool Attributor::checkForAllUses(function_ref<bool(const Use &, bool &)> Pred,
const Function *ScopeFn = IRP.getAnchorScope();
const auto *LivenessAA =
ScopeFn ? &getAAFor<AAIsDead>(QueryingAA, IRPosition::function(*ScopeFn),
- /* TrackDependence */ false)
+ DepClassTy::NONE)
: nullptr;
while (!Worklist.empty()) {
@@ -723,8 +1027,9 @@ bool Attributor::checkForAllUses(function_ref<bool(const Use &, bool &)> Pred,
continue;
LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << " in "
<< *U->getUser() << "\n");
- if (isAssumedDead(*U, &QueryingAA, LivenessAA,
- /* CheckBBLivenessOnly */ false, LivenessDepClass)) {
+ bool UsedAssumedInformation = false;
+ if (isAssumedDead(*U, &QueryingAA, LivenessAA, UsedAssumedInformation,
+ CheckBBLivenessOnly, LivenessDepClass)) {
LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n");
continue;
}
@@ -733,6 +1038,23 @@ bool Attributor::checkForAllUses(function_ref<bool(const Use &, bool &)> Pred,
continue;
}
+ if (auto *SI = dyn_cast<StoreInst>(U->getUser())) {
+ if (&SI->getOperandUse(0) == U) {
+ SmallSetVector<Value *, 4> PotentialCopies;
+ if (AA::getPotentialCopiesOfStoredValue(*this, *SI, PotentialCopies,
+ QueryingAA,
+ UsedAssumedInformation)) {
+ LLVM_DEBUG(dbgs() << "[Attributor] Value is stored, continue with "
+ << PotentialCopies.size()
+ << " potential copies instead!\n");
+ for (Value *PotentialCopy : PotentialCopies)
+ for (const Use &U : PotentialCopy->uses())
+ Worklist.push_back(&U);
+ continue;
+ }
+ }
+ }
+
bool Follow = false;
if (!Pred(*U, Follow))
return false;
@@ -787,7 +1109,9 @@ bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
const Use &U = *Uses[u];
LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << *U << " in "
<< *U.getUser() << "\n");
- if (isAssumedDead(U, QueryingAA, nullptr, /* CheckBBLivenessOnly */ true)) {
+ bool UsedAssumedInformation = false;
+ if (isAssumedDead(U, QueryingAA, nullptr, UsedAssumedInformation,
+ /* CheckBBLivenessOnly */ true)) {
LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n");
continue;
}
@@ -850,6 +1174,13 @@ bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
return true;
}
+bool Attributor::shouldPropagateCallBaseContext(const IRPosition &IRP) {
+ // TODO: Maintain a cache of Values that are
+ // on the pathway from a Argument to a Instruction that would effect the
+ // liveness/return state etc.
+ return EnableCallSiteSpecific;
+}
+
bool Attributor::checkForAllReturnedValuesAndReturnInsts(
function_ref<bool(Value &, const SmallSetVector<ReturnInst *, 4> &)> Pred,
const AbstractAttribute &QueryingAA) {
@@ -865,7 +1196,8 @@ bool Attributor::checkForAllReturnedValuesAndReturnInsts(
// and liveness information.
// TODO: use the function scope once we have call site AAReturnedValues.
const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction);
- const auto &AARetVal = getAAFor<AAReturnedValues>(QueryingAA, QueryIRP);
+ const auto &AARetVal =
+ getAAFor<AAReturnedValues>(QueryingAA, QueryIRP, DepClassTy::REQUIRED);
if (!AARetVal.getState().isValidState())
return false;
@@ -881,8 +1213,10 @@ bool Attributor::checkForAllReturnedValues(
return false;
// TODO: use the function scope once we have call site AAReturnedValues.
- const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction);
- const auto &AARetVal = getAAFor<AAReturnedValues>(QueryingAA, QueryIRP);
+ const IRPosition &QueryIRP = IRPosition::function(
+ *AssociatedFunction, QueryingAA.getCallBaseContext());
+ const auto &AARetVal =
+ getAAFor<AAReturnedValues>(QueryingAA, QueryIRP, DepClassTy::REQUIRED);
if (!AARetVal.getState().isValidState())
return false;
@@ -896,7 +1230,8 @@ static bool checkForAllInstructionsImpl(
Attributor *A, InformationCache::OpcodeInstMapTy &OpcodeInstMap,
function_ref<bool(Instruction &)> Pred, const AbstractAttribute *QueryingAA,
const AAIsDead *LivenessAA, const ArrayRef<unsigned> &Opcodes,
- bool CheckBBLivenessOnly = false) {
+ bool &UsedAssumedInformation, bool CheckBBLivenessOnly = false,
+ bool CheckPotentiallyDead = false) {
for (unsigned Opcode : Opcodes) {
// Check if we have instructions with this opcode at all first.
auto *Insts = OpcodeInstMap.lookup(Opcode);
@@ -905,8 +1240,9 @@ static bool checkForAllInstructionsImpl(
for (Instruction *I : *Insts) {
// Skip dead instructions.
- if (A && A->isAssumedDead(IRPosition::value(*I), QueryingAA, LivenessAA,
- CheckBBLivenessOnly))
+ if (A && !CheckPotentiallyDead &&
+ A->isAssumedDead(IRPosition::value(*I), QueryingAA, LivenessAA,
+ UsedAssumedInformation, CheckBBLivenessOnly))
continue;
if (!Pred(*I))
@@ -919,7 +1255,9 @@ static bool checkForAllInstructionsImpl(
bool Attributor::checkForAllInstructions(function_ref<bool(Instruction &)> Pred,
const AbstractAttribute &QueryingAA,
const ArrayRef<unsigned> &Opcodes,
- bool CheckBBLivenessOnly) {
+ bool &UsedAssumedInformation,
+ bool CheckBBLivenessOnly,
+ bool CheckPotentiallyDead) {
const IRPosition &IRP = QueryingAA.getIRPosition();
// Since we need to provide instructions we have to have an exact definition.
@@ -927,24 +1265,29 @@ bool Attributor::checkForAllInstructions(function_ref<bool(Instruction &)> Pred,
if (!AssociatedFunction)
return false;
+ if (AssociatedFunction->isDeclaration())
+ return false;
+
// TODO: use the function scope once we have call site AAReturnedValues.
const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction);
const auto *LivenessAA =
- CheckBBLivenessOnly ? nullptr
- : &(getAAFor<AAIsDead>(QueryingAA, QueryIRP,
- /* TrackDependence */ false));
+ (CheckBBLivenessOnly || CheckPotentiallyDead)
+ ? nullptr
+ : &(getAAFor<AAIsDead>(QueryingAA, QueryIRP, DepClassTy::NONE));
auto &OpcodeInstMap =
InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction);
if (!checkForAllInstructionsImpl(this, OpcodeInstMap, Pred, &QueryingAA,
- LivenessAA, Opcodes, CheckBBLivenessOnly))
+ LivenessAA, Opcodes, UsedAssumedInformation,
+ CheckBBLivenessOnly, CheckPotentiallyDead))
return false;
return true;
}
bool Attributor::checkForAllReadWriteInstructions(
- function_ref<bool(Instruction &)> Pred, AbstractAttribute &QueryingAA) {
+ function_ref<bool(Instruction &)> Pred, AbstractAttribute &QueryingAA,
+ bool &UsedAssumedInformation) {
const Function *AssociatedFunction =
QueryingAA.getIRPosition().getAssociatedFunction();
@@ -954,12 +1297,13 @@ bool Attributor::checkForAllReadWriteInstructions(
// TODO: use the function scope once we have call site AAReturnedValues.
const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction);
const auto &LivenessAA =
- getAAFor<AAIsDead>(QueryingAA, QueryIRP, /* TrackDependence */ false);
+ getAAFor<AAIsDead>(QueryingAA, QueryIRP, DepClassTy::NONE);
for (Instruction *I :
InfoCache.getReadOrWriteInstsForFunction(*AssociatedFunction)) {
// Skip dead instructions.
- if (isAssumedDead(IRPosition::value(*I), &QueryingAA, &LivenessAA))
+ if (isAssumedDead(IRPosition::value(*I), &QueryingAA, &LivenessAA,
+ UsedAssumedInformation))
continue;
if (!Pred(*I))
@@ -979,6 +1323,11 @@ void Attributor::runTillFixpoint() {
// the abstract analysis.
unsigned IterationCounter = 1;
+ unsigned MaxFixedPointIterations;
+ if (MaxFixpointIterations)
+ MaxFixedPointIterations = MaxFixpointIterations.getValue();
+ else
+ MaxFixedPointIterations = SetFixpointIterations;
SmallVector<AbstractAttribute *, 32> ChangedAAs;
SetVector<AbstractAttribute *> Worklist, InvalidAAs;
@@ -1058,7 +1407,7 @@ void Attributor::runTillFixpoint() {
Worklist.clear();
Worklist.insert(ChangedAAs.begin(), ChangedAAs.end());
- } while (!Worklist.empty() && (IterationCounter++ < MaxFixpointIterations ||
+ } while (!Worklist.empty() && (IterationCounter++ < MaxFixedPointIterations ||
VerifyMaxFixpointIterations));
LLVM_DEBUG(dbgs() << "\n[Attributor] Fixpoint iteration done after: "
@@ -1097,9 +1446,9 @@ void Attributor::runTillFixpoint() {
});
if (VerifyMaxFixpointIterations &&
- IterationCounter != MaxFixpointIterations) {
+ IterationCounter != MaxFixedPointIterations) {
errs() << "\n[Attributor] Fixpoint iteration done after: "
- << IterationCounter << "/" << MaxFixpointIterations
+ << IterationCounter << "/" << MaxFixedPointIterations
<< " iterations\n";
llvm_unreachable("The fixpoint was not reached with exactly the number of "
"specified iterations!");
@@ -1124,12 +1473,17 @@ ChangeStatus Attributor::manifestAttributes() {
if (!State.isAtFixpoint())
State.indicateOptimisticFixpoint();
+ // We must not manifest Attributes that use Callbase info.
+ if (AA->hasCallBaseContext())
+ continue;
// If the state is invalid, we do not try to manifest it.
if (!State.isValidState())
continue;
// Skip dead code.
- if (isAssumedDead(*AA, nullptr, /* CheckBBLivenessOnly */ true))
+ bool UsedAssumedInformation = false;
+ if (isAssumedDead(*AA, nullptr, UsedAssumedInformation,
+ /* CheckBBLivenessOnly */ true))
continue;
// Check if the manifest debug counter that allows skipping manifestation of
// AAs
@@ -1174,6 +1528,10 @@ ChangeStatus Attributor::manifestAttributes() {
}
void Attributor::identifyDeadInternalFunctions() {
+ // Early exit if we don't intend to delete functions.
+ if (!DeleteFns)
+ return;
+
// Identify dead internal functions and delete them. This happens outside
// the other fixpoint analysis as we might treat potentially dead functions
// as live to lower the number of iterations. If they happen to be dead, the
@@ -1218,40 +1576,68 @@ void Attributor::identifyDeadInternalFunctions() {
ChangeStatus Attributor::cleanupIR() {
TimeTraceScope TimeScope("Attributor::cleanupIR");
// Delete stuff at the end to avoid invalid references and a nice order.
- LLVM_DEBUG(dbgs() << "\n[Attributor] Delete at least "
+ LLVM_DEBUG(dbgs() << "\n[Attributor] Delete/replace at least "
<< ToBeDeletedFunctions.size() << " functions and "
<< ToBeDeletedBlocks.size() << " blocks and "
<< ToBeDeletedInsts.size() << " instructions and "
- << ToBeChangedUses.size() << " uses\n");
+ << ToBeChangedValues.size() << " values and "
+ << ToBeChangedUses.size() << " uses. "
+ << "Preserve manifest added " << ManifestAddedBlocks.size()
+ << " blocks\n");
SmallVector<WeakTrackingVH, 32> DeadInsts;
SmallVector<Instruction *, 32> TerminatorsToFold;
- for (auto &It : ToBeChangedUses) {
- Use *U = It.first;
- Value *NewV = It.second;
+ auto ReplaceUse = [&](Use *U, Value *NewV) {
Value *OldV = U->get();
+ // If we plan to replace NewV we need to update it at this point.
+ do {
+ const auto &Entry = ToBeChangedValues.lookup(NewV);
+ if (!Entry.first)
+ break;
+ NewV = Entry.first;
+ } while (true);
+
// Do not replace uses in returns if the value is a must-tail call we will
// not delete.
- if (isa<ReturnInst>(U->getUser()))
+ if (auto *RI = dyn_cast<ReturnInst>(U->getUser())) {
if (auto *CI = dyn_cast<CallInst>(OldV->stripPointerCasts()))
- if (CI->isMustTailCall() && !ToBeDeletedInsts.count(CI))
- continue;
+ if (CI->isMustTailCall() &&
+ (!ToBeDeletedInsts.count(CI) || !isRunOn(*CI->getCaller())))
+ return;
+ // If we rewrite a return and the new value is not an argument, strip the
+ // `returned` attribute as it is wrong now.
+ if (!isa<Argument>(NewV))
+ for (auto &Arg : RI->getFunction()->args())
+ Arg.removeAttr(Attribute::Returned);
+ }
+
+ // Do not perform call graph altering changes outside the SCC.
+ if (auto *CB = dyn_cast<CallBase>(U->getUser()))
+ if (CB->isCallee(U) && !isRunOn(*CB->getCaller()))
+ return;
LLVM_DEBUG(dbgs() << "Use " << *NewV << " in " << *U->getUser()
<< " instead of " << *OldV << "\n");
U->set(NewV);
- // Do not modify call instructions outside the SCC.
- if (auto *CB = dyn_cast<CallBase>(OldV))
- if (!Functions.count(CB->getCaller()))
- continue;
+
if (Instruction *I = dyn_cast<Instruction>(OldV)) {
CGModifiedFunctions.insert(I->getFunction());
if (!isa<PHINode>(I) && !ToBeDeletedInsts.count(I) &&
isInstructionTriviallyDead(I))
DeadInsts.push_back(I);
}
+ if (isa<UndefValue>(NewV) && isa<CallBase>(U->getUser())) {
+ auto *CB = cast<CallBase>(U->getUser());
+ if (CB->isArgOperand(U)) {
+ unsigned Idx = CB->getArgOperandNo(U);
+ CB->removeParamAttr(Idx, Attribute::NoUndef);
+ Function *Fn = CB->getCalledFunction();
+ if (Fn && Fn->arg_size() > Idx)
+ Fn->removeParamAttr(Idx, Attribute::NoUndef);
+ }
+ }
if (isa<Constant>(NewV) && isa<BranchInst>(U->getUser())) {
Instruction *UserI = cast<Instruction>(U->getUser());
if (isa<UndefValue>(NewV)) {
@@ -1260,9 +1646,31 @@ ChangeStatus Attributor::cleanupIR() {
TerminatorsToFold.push_back(UserI);
}
}
+ };
+
+ for (auto &It : ToBeChangedUses) {
+ Use *U = It.first;
+ Value *NewV = It.second;
+ ReplaceUse(U, NewV);
+ }
+
+ SmallVector<Use *, 4> Uses;
+ for (auto &It : ToBeChangedValues) {
+ Value *OldV = It.first;
+ auto &Entry = It.second;
+ Value *NewV = Entry.first;
+ Uses.clear();
+ for (auto &U : OldV->uses())
+ if (Entry.second || !U.getUser()->isDroppable())
+ Uses.push_back(&U);
+ for (Use *U : Uses)
+ ReplaceUse(U, NewV);
}
+
for (auto &V : InvokeWithDeadSuccessor)
if (InvokeInst *II = dyn_cast_or_null<InvokeInst>(V)) {
+ assert(isRunOn(*II->getFunction()) &&
+ "Cannot replace an invoke outside the current SCC!");
bool UnwindBBIsDead = II->hasFnAttr(Attribute::NoUnwind);
bool NormalBBIsDead = II->hasFnAttr(Attribute::NoReturn);
bool Invoke2CallAllowed =
@@ -1287,17 +1695,27 @@ ChangeStatus Attributor::cleanupIR() {
}
}
for (Instruction *I : TerminatorsToFold) {
+ if (!isRunOn(*I->getFunction()))
+ continue;
CGModifiedFunctions.insert(I->getFunction());
ConstantFoldTerminator(I->getParent());
}
for (auto &V : ToBeChangedToUnreachableInsts)
if (Instruction *I = dyn_cast_or_null<Instruction>(V)) {
+ if (!isRunOn(*I->getFunction()))
+ continue;
CGModifiedFunctions.insert(I->getFunction());
- changeToUnreachable(I, /* UseLLVMTrap */ false);
+ changeToUnreachable(I);
}
for (auto &V : ToBeDeletedInsts) {
if (Instruction *I = dyn_cast_or_null<Instruction>(V)) {
+ if (auto *CB = dyn_cast<CallBase>(I)) {
+ if (!isRunOn(*I->getFunction()))
+ continue;
+ if (!isa<IntrinsicInst>(CB))
+ CGUpdater.removeCallSite(*CB);
+ }
I->dropDroppableUses();
CGModifiedFunctions.insert(I->getFunction());
if (!I->getType()->isVoidTy())
@@ -1309,8 +1727,16 @@ ChangeStatus Attributor::cleanupIR() {
}
}
- LLVM_DEBUG(dbgs() << "[Attributor] DeadInsts size: " << DeadInsts.size()
- << "\n");
+ llvm::erase_if(DeadInsts, [&](WeakTrackingVH I) {
+ return !I || !isRunOn(*cast<Instruction>(I)->getFunction());
+ });
+
+ LLVM_DEBUG({
+ dbgs() << "[Attributor] DeadInsts size: " << DeadInsts.size() << "\n";
+ for (auto &I : DeadInsts)
+ if (I)
+ dbgs() << " - " << *I << "\n";
+ });
RecursivelyDeleteTriviallyDeadInstructions(DeadInsts);
@@ -1318,7 +1744,12 @@ ChangeStatus Attributor::cleanupIR() {
SmallVector<BasicBlock *, 8> ToBeDeletedBBs;
ToBeDeletedBBs.reserve(NumDeadBlocks);
for (BasicBlock *BB : ToBeDeletedBlocks) {
+ assert(isRunOn(*BB->getParent()) &&
+ "Cannot delete a block outside the current SCC!");
CGModifiedFunctions.insert(BB->getParent());
+ // Do not delete BBs added during manifests of AAs.
+ if (ManifestAddedBlocks.contains(BB))
+ continue;
ToBeDeletedBBs.push_back(BB);
}
// Actually we do not delete the blocks but squash them into a single
@@ -1333,7 +1764,7 @@ ChangeStatus Attributor::cleanupIR() {
ChangeStatus ManifestChange = rewriteFunctionSignatures(CGModifiedFunctions);
for (Function *Fn : CGModifiedFunctions)
- if (!ToBeDeletedFunctions.count(Fn))
+ if (!ToBeDeletedFunctions.count(Fn) && Functions.count(Fn))
CGUpdater.reanalyzeFunction(*Fn);
for (Function *Fn : ToBeDeletedFunctions) {
@@ -1381,6 +1812,10 @@ ChangeStatus Attributor::cleanupIR() {
ChangeStatus Attributor::run() {
TimeTraceScope TimeScope("Attributor::run");
+ AttributorCallGraph ACallGraph(*this);
+
+ if (PrintCallGraph)
+ ACallGraph.populateAll();
Phase = AttributorPhase::UPDATE;
runTillFixpoint();
@@ -1401,6 +1836,9 @@ ChangeStatus Attributor::run() {
Phase = AttributorPhase::CLEANUP;
ChangeStatus CleanupChange = cleanupIR();
+ if (PrintCallGraph)
+ ACallGraph.print();
+
return ManifestChange | CleanupChange;
}
@@ -1417,7 +1855,9 @@ ChangeStatus Attributor::updateAA(AbstractAttribute &AA) {
auto &AAState = AA.getState();
ChangeStatus CS = ChangeStatus::UNCHANGED;
- if (!isAssumedDead(AA, nullptr, /* CheckBBLivenessOnly */ true))
+ bool UsedAssumedInformation = false;
+ if (!isAssumedDead(AA, nullptr, UsedAssumedInformation,
+ /* CheckBBLivenessOnly */ true))
CS = AA.update(*this);
if (DV.empty()) {
@@ -1485,19 +1925,12 @@ void Attributor::createShallowWrapper(Function &F) {
NumFnShallowWrappersCreated++;
}
-/// Make another copy of the function \p F such that the copied version has
-/// internal linkage afterwards and can be analysed. Then we replace all uses
-/// of the original function to the copied one
-///
-/// Only non-exactly defined functions that have `linkonce_odr` or `weak_odr`
-/// linkage can be internalized because these linkages guarantee that other
-/// definitions with the same name have the same semantics as this one
-///
-static Function *internalizeFunction(Function &F) {
- assert(AllowDeepWrapper && "Cannot create a copy if not allowed.");
- assert(!F.isDeclaration() && !F.hasExactDefinition() &&
- !GlobalValue::isInterposableLinkage(F.getLinkage()) &&
- "Trying to internalize function which cannot be internalized.");
+Function *Attributor::internalizeFunction(Function &F, bool Force) {
+ if (!AllowDeepWrapper && !Force)
+ return nullptr;
+ if (F.isDeclaration() || F.hasLocalLinkage() ||
+ GlobalValue::isInterposableLinkage(F.getLinkage()))
+ return nullptr;
Module &M = *F.getParent();
FunctionType *FnTy = F.getFunctionType();
@@ -1515,7 +1948,8 @@ static Function *internalizeFunction(Function &F) {
SmallVector<ReturnInst *, 8> Returns;
// Copy the body of the original function to the new one
- CloneFunctionInto(Copied, &F, VMap, /* ModuleLevelChanges */ false, Returns);
+ CloneFunctionInto(Copied, &F, VMap, CloneFunctionChangeType::LocalChangesOnly,
+ Returns);
// Set the linakage and visibility late as CloneFunctionInto has some implicit
// requirements.
@@ -1526,7 +1960,8 @@ static Function *internalizeFunction(Function &F) {
SmallVector<std::pair<unsigned, MDNode *>, 1> MDs;
F.getAllMetadata(MDs);
for (auto MDIt : MDs)
- Copied->addMetadata(MDIt.first, *MDIt.second);
+ if (!Copied->hasMetadata())
+ Copied->addMetadata(MDIt.first, *MDIt.second);
M.getFunctionList().insert(F.getIterator(), Copied);
F.replaceAllUsesWith(Copied);
@@ -1538,6 +1973,9 @@ static Function *internalizeFunction(Function &F) {
bool Attributor::isValidFunctionSignatureRewrite(
Argument &Arg, ArrayRef<Type *> ReplacementTypes) {
+ if (!RewriteSignatures)
+ return false;
+
auto CallSiteCanBeChanged = [](AbstractCallSite ACS) {
// Forbid the call site to cast the function return type. If we need to
// rewrite these functions we need to re-create a cast for the new call site
@@ -1584,9 +2022,11 @@ bool Attributor::isValidFunctionSignatureRewrite(
// Forbid must-tail calls for now.
// TODO:
+ bool UsedAssumedInformation = false;
auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*Fn);
if (!checkForAllInstructionsImpl(nullptr, OpcodeInstMap, InstPred, nullptr,
- nullptr, {Instruction::Call})) {
+ nullptr, {Instruction::Call},
+ UsedAssumedInformation)) {
LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite due to instructions\n");
return false;
}
@@ -1696,6 +2136,7 @@ ChangeStatus Attributor::rewriteFunctionSignatures(
// Create the new function body and insert it into the module.
Function *NewFn = Function::Create(NewFnTy, OldFn->getLinkage(),
OldFn->getAddressSpace(), "");
+ Functions.insert(NewFn);
OldFn->getParent()->getFunctionList().insert(OldFn->getIterator(), NewFn);
NewFn->takeName(OldFn);
NewFn->copyAttributesFrom(OldFn);
@@ -1870,9 +2311,8 @@ void InformationCache::initializeInformationCache(const Function &CF,
// Calls are interesting on their own, additionally:
// For `llvm.assume` calls we also fill the KnowledgeMap as we find them.
// For `must-tail` calls we remember the caller and callee.
- if (IntrinsicInst *Assume = dyn_cast<IntrinsicInst>(&I)) {
- if (Assume->getIntrinsicID() == Intrinsic::assume)
- fillMapFromAssume(*Assume, KnowledgeMap);
+ if (auto *Assume = dyn_cast<AssumeInst>(&I)) {
+ fillMapFromAssume(*Assume, KnowledgeMap);
} else if (cast<CallInst>(I).isMustTailCall()) {
FI.ContainsMustTailCall = true;
if (const Function *Callee = cast<CallInst>(I).getCalledFunction())
@@ -1892,6 +2332,8 @@ void InformationCache::initializeInformationCache(const Function &CF,
// The alignment of a pointer is interesting for loads.
case Instruction::Store:
// The alignment of a pointer is interesting for stores.
+ case Instruction::Alloca:
+ case Instruction::AddrSpaceCast:
IsInterestingOpcode = true;
}
if (IsInterestingOpcode) {
@@ -1923,6 +2365,8 @@ InformationCache::FunctionInfo::~FunctionInfo() {
void Attributor::recordDependence(const AbstractAttribute &FromAA,
const AbstractAttribute &ToAA,
DepClassTy DepClass) {
+ if (DepClass == DepClassTy::NONE)
+ return;
// If we are outside of an update, thus before the actual fixpoint iteration
// started (= when we create AAs), we do not track dependences because we will
// put all AAs into the initial worklist anyway.
@@ -1937,6 +2381,9 @@ void Attributor::rememberDependences() {
assert(!DependenceStack.empty() && "No dependences to remember!");
for (DepInfo &DI : *DependenceStack.back()) {
+ assert((DI.DepClass == DepClassTy::REQUIRED ||
+ DI.DepClass == DepClassTy::OPTIONAL) &&
+ "Expected required or optional dependence (1 bit)!");
auto &DepAAs = const_cast<AbstractAttribute &>(*DI.FromAA).Deps;
DepAAs.push_back(AbstractAttribute::DepTy(
const_cast<AbstractAttribute *>(DI.ToAA), unsigned(DI.DepClass)));
@@ -2036,8 +2483,11 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
for (Argument &Arg : F.args()) {
IRPosition ArgPos = IRPosition::argument(Arg);
- // Every argument might be simplified.
- getOrCreateAAFor<AAValueSimplify>(ArgPos);
+ // Every argument might be simplified. We have to go through the Attributor
+ // interface though as outside AAs can register custom simplification
+ // callbacks.
+ bool UsedAssumedInformation = false;
+ getAssumedSimplified(ArgPos, /* AA */ nullptr, UsedAssumedInformation);
// Every argument might be dead.
getOrCreateAAFor<AAIsDead>(ArgPos);
@@ -2096,10 +2546,7 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
if (!Callee->getReturnType()->isVoidTy() && !CB.use_empty()) {
IRPosition CBRetPos = IRPosition::callsite_returned(CB);
-
- // Call site return integer values might be limited by a constant range.
- if (Callee->getReturnType()->isIntegerTy())
- getOrCreateAAFor<AAValueConstantRange>(CBRetPos);
+ getOrCreateAAFor<AAValueSimplify>(CBRetPos);
}
for (int I = 0, E = CB.getNumArgOperands(); I < E; ++I) {
@@ -2109,8 +2556,11 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
// Every call site argument might be dead.
getOrCreateAAFor<AAIsDead>(CBArgPos);
- // Call site argument might be simplified.
- getOrCreateAAFor<AAValueSimplify>(CBArgPos);
+ // Call site argument might be simplified. We have to go through the
+ // Attributor interface though as outside AAs can register custom
+ // simplification callbacks.
+ bool UsedAssumedInformation = false;
+ getAssumedSimplified(CBArgPos, /* AA */ nullptr, UsedAssumedInformation);
// Every call site argument might be marked "noundef".
getOrCreateAAFor<AANoUndef>(CBArgPos);
@@ -2145,25 +2595,30 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
bool Success;
+ bool UsedAssumedInformation = false;
Success = checkForAllInstructionsImpl(
nullptr, OpcodeInstMap, CallSitePred, nullptr, nullptr,
{(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr,
- (unsigned)Instruction::Call});
+ (unsigned)Instruction::Call},
+ UsedAssumedInformation);
(void)Success;
assert(Success && "Expected the check call to be successful!");
auto LoadStorePred = [&](Instruction &I) -> bool {
- if (isa<LoadInst>(I))
+ if (isa<LoadInst>(I)) {
getOrCreateAAFor<AAAlign>(
IRPosition::value(*cast<LoadInst>(I).getPointerOperand()));
- else
+ if (SimplifyAllLoads)
+ getOrCreateAAFor<AAValueSimplify>(IRPosition::value(I));
+ } else
getOrCreateAAFor<AAAlign>(
IRPosition::value(*cast<StoreInst>(I).getPointerOperand()));
return true;
};
Success = checkForAllInstructionsImpl(
nullptr, OpcodeInstMap, LoadStorePred, nullptr, nullptr,
- {(unsigned)Instruction::Load, (unsigned)Instruction::Store});
+ {(unsigned)Instruction::Load, (unsigned)Instruction::Store},
+ UsedAssumedInformation);
(void)Success;
assert(Success && "Expected the check call to be successful!");
}
@@ -2199,9 +2654,12 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, IRPosition::Kind AP) {
raw_ostream &llvm::operator<<(raw_ostream &OS, const IRPosition &Pos) {
const Value &AV = Pos.getAssociatedValue();
- return OS << "{" << Pos.getPositionKind() << ":" << AV.getName() << " ["
- << Pos.getAnchorValue().getName() << "@" << Pos.getCallSiteArgNo()
- << "]}";
+ OS << "{" << Pos.getPositionKind() << ":" << AV.getName() << " ["
+ << Pos.getAnchorValue().getName() << "@" << Pos.getCallSiteArgNo() << "]";
+
+ if (Pos.hasCallBaseContext())
+ OS << "[cb_context:" << *Pos.getCallBaseContext() << "]";
+ return OS << "}";
}
raw_ostream &llvm::operator<<(raw_ostream &OS, const IntegerRangeState &S) {
@@ -2266,6 +2724,16 @@ void AbstractAttribute::printWithDeps(raw_ostream &OS) const {
OS << '\n';
}
+
+raw_ostream &llvm::operator<<(raw_ostream &OS,
+ const AAPointerInfo::Access &Acc) {
+ OS << " [" << Acc.getKind() << "] " << *Acc.getRemoteInst();
+ if (Acc.getLocalInst() != Acc.getRemoteInst())
+ OS << " via " << *Acc.getLocalInst();
+ if (Acc.getContent().hasValue())
+ OS << " [" << *Acc.getContent() << "]";
+ return OS;
+}
///}
/// ----------------------------------------------------------------------------
@@ -2275,16 +2743,22 @@ void AbstractAttribute::printWithDeps(raw_ostream &OS) const {
static bool runAttributorOnFunctions(InformationCache &InfoCache,
SetVector<Function *> &Functions,
AnalysisGetter &AG,
- CallGraphUpdater &CGUpdater) {
+ CallGraphUpdater &CGUpdater,
+ bool DeleteFns) {
if (Functions.empty())
return false;
- LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << Functions.size()
- << " functions.\n");
+ LLVM_DEBUG({
+ dbgs() << "[Attributor] Run on module with " << Functions.size()
+ << " functions:\n";
+ for (Function *Fn : Functions)
+ dbgs() << " - " << Fn->getName() << "\n";
+ });
// Create an Attributor and initially empty information cache that is filled
// while we identify default attribute opportunities.
- Attributor A(Functions, InfoCache, CGUpdater);
+ Attributor A(Functions, InfoCache, CGUpdater, /* Allowed */ nullptr,
+ DeleteFns);
// Create shallow wrappers for all functions that are not IPO amendable
if (AllowShallowWrappers)
@@ -2302,7 +2776,8 @@ static bool runAttributorOnFunctions(InformationCache &InfoCache,
Function *F = Functions[u];
if (!F->isDeclaration() && !F->isDefinitionExact() && F->getNumUses() &&
!GlobalValue::isInterposableLinkage(F->getLinkage())) {
- Function *NewF = internalizeFunction(*F);
+ Function *NewF = Attributor::internalizeFunction(*F);
+ assert(NewF && "Could not internalize function.");
Functions.insert(NewF);
// Update call graph
@@ -2363,7 +2838,7 @@ void AADepGraph::dumpGraph() {
std::error_code EC;
- raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);
+ raw_fd_ostream File(Filename, EC, sys::fs::OF_TextWithCRLF);
if (!EC)
llvm::WriteGraph(File, this);
@@ -2387,7 +2862,8 @@ PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) {
CallGraphUpdater CGUpdater;
BumpPtrAllocator Allocator;
InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr);
- if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) {
+ if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater,
+ /* DeleteFns */ true)) {
// FIXME: Think about passes we will preserve and add them here.
return PreservedAnalyses::none();
}
@@ -2414,7 +2890,8 @@ PreservedAnalyses AttributorCGSCCPass::run(LazyCallGraph::SCC &C,
CGUpdater.initialize(CG, C, AM, UR);
BumpPtrAllocator Allocator;
InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions);
- if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) {
+ if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater,
+ /* DeleteFns */ false)) {
// FIXME: Think about passes we will preserve and add them here.
PreservedAnalyses PA;
PA.preserve<FunctionAnalysisManagerCGSCCProxy>();
@@ -2489,7 +2966,8 @@ struct AttributorLegacyPass : public ModulePass {
CallGraphUpdater CGUpdater;
BumpPtrAllocator Allocator;
InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr);
- return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater);
+ return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater,
+ /* DeleteFns*/ true);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
@@ -2525,7 +3003,8 @@ struct AttributorCGSCCLegacyPass : public CallGraphSCCPass {
Module &M = *Functions.back()->getParent();
BumpPtrAllocator Allocator;
InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions);
- return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater);
+ return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater,
+ /* DeleteFns */ false);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {