aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp2811
1 files changed, 2316 insertions, 495 deletions
diff --git a/contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp b/contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp
index 95f47345d8fd..f2995817eaf8 100644
--- a/contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/contrib/llvm-project/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -23,14 +23,18 @@
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/LazyValueInfo.h"
#include "llvm/Analysis/Loads.h"
#include "llvm/Analysis/MemoryBuiltins.h"
+#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/InitializePasses.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -53,6 +57,8 @@ 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");
// Some helper macros to deal with statistics tracking.
//
@@ -98,6 +104,36 @@ STATISTIC(NumAttributesManifested,
STATS_DECLTRACK(NAME, Floating, \
("Number of floating values known to be '" #NAME "'"))
+// Specialization of the operator<< for abstract attributes subclasses. This
+// disambiguates situations where multiple operators are applicable.
+namespace llvm {
+#define PIPE_OPERATOR(CLASS) \
+ raw_ostream &operator<<(raw_ostream &OS, const CLASS &AA) { \
+ return OS << static_cast<const AbstractAttribute &>(AA); \
+ }
+
+PIPE_OPERATOR(AAIsDead)
+PIPE_OPERATOR(AANoUnwind)
+PIPE_OPERATOR(AANoSync)
+PIPE_OPERATOR(AANoRecurse)
+PIPE_OPERATOR(AAWillReturn)
+PIPE_OPERATOR(AANoReturn)
+PIPE_OPERATOR(AAReturnedValues)
+PIPE_OPERATOR(AANonNull)
+PIPE_OPERATOR(AANoAlias)
+PIPE_OPERATOR(AADereferenceable)
+PIPE_OPERATOR(AAAlign)
+PIPE_OPERATOR(AANoCapture)
+PIPE_OPERATOR(AAValueSimplify)
+PIPE_OPERATOR(AANoFree)
+PIPE_OPERATOR(AAHeapToStack)
+PIPE_OPERATOR(AAReachability)
+PIPE_OPERATOR(AAMemoryBehavior)
+PIPE_OPERATOR(AAValueConstantRange)
+
+#undef PIPE_OPERATOR
+} // namespace llvm
+
// TODO: Determine a good default value.
//
// In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads
@@ -120,6 +156,10 @@ static cl::opt<bool> DisableAttributor(
cl::desc("Disable the attributor inter-procedural deduction pass."),
cl::init(true));
+static cl::opt<bool> AnnotateDeclarationCallSites(
+ "attributor-annotate-decl-cs", cl::Hidden,
+ cl::desc("Annotate call sites of function declarations."), cl::init(false));
+
static cl::opt<bool> ManifestInternal(
"attributor-manifest-internal", cl::Hidden,
cl::desc("Manifest Attributor internal string attributes."),
@@ -147,6 +187,74 @@ ChangeStatus llvm::operator&(ChangeStatus l, ChangeStatus r) {
}
///}
+Argument *IRPosition::getAssociatedArgument() const {
+ if (getPositionKind() == IRP_ARGUMENT)
+ return cast<Argument>(&getAnchorValue());
+
+ // Not an Argument and no argument number means this is not a call site
+ // argument, thus we cannot find a callback argument to return.
+ int ArgNo = getArgNo();
+ if (ArgNo < 0)
+ return nullptr;
+
+ // Use abstract call sites to make the connection between the call site
+ // values and the ones in callbacks. If a callback was found that makes use
+ // of the underlying call site operand, we want the corresponding callback
+ // callee argument and not the direct callee argument.
+ Optional<Argument *> CBCandidateArg;
+ SmallVector<const Use *, 4> CBUses;
+ ImmutableCallSite ICS(&getAnchorValue());
+ AbstractCallSite::getCallbackUses(ICS, CBUses);
+ for (const Use *U : CBUses) {
+ AbstractCallSite ACS(U);
+ assert(ACS && ACS.isCallbackCall());
+ if (!ACS.getCalledFunction())
+ continue;
+
+ for (unsigned u = 0, e = ACS.getNumArgOperands(); u < e; u++) {
+
+ // Test if the underlying call site operand is argument number u of the
+ // callback callee.
+ if (ACS.getCallArgOperandNo(u) != ArgNo)
+ continue;
+
+ assert(ACS.getCalledFunction()->arg_size() > u &&
+ "ACS mapped into var-args arguments!");
+ if (CBCandidateArg.hasValue()) {
+ CBCandidateArg = nullptr;
+ break;
+ }
+ CBCandidateArg = ACS.getCalledFunction()->getArg(u);
+ }
+ }
+
+ // If we found a unique callback candidate argument, return it.
+ if (CBCandidateArg.hasValue() && CBCandidateArg.getValue())
+ return CBCandidateArg.getValue();
+
+ // If no callbacks were found, or none used the underlying call site operand
+ // exclusively, use the direct callee argument if available.
+ const Function *Callee = ICS.getCalledFunction();
+ if (Callee && Callee->arg_size() > unsigned(ArgNo))
+ return Callee->getArg(ArgNo);
+
+ return nullptr;
+}
+
+/// For calls (and invokes) we will only replace instruction uses to not disturb
+/// the old style call graph.
+/// TODO: Remove this once we get rid of the old PM.
+static void replaceAllInstructionUsesWith(Value &Old, Value &New) {
+ if (!isa<CallBase>(Old))
+ return Old.replaceAllUsesWith(&New);
+ SmallVector<Use *, 8> Uses;
+ for (Use &U : Old.uses())
+ if (isa<Instruction>(U.getUser()))
+ Uses.push_back(&U);
+ for (Use *U : Uses)
+ U->set(&New);
+}
+
/// Recursively visit all values that might become \p IRP at some point. This
/// will be done by looking through cast instructions, selects, phis, and calls
/// with the "returned" attribute. Once we cannot look through the value any
@@ -234,7 +342,7 @@ static bool genericValueTraversal(
// If we actually used liveness information so we have to record a dependence.
if (AnyDead)
- A.recordDependence(*LivenessAA, QueryingAA);
+ A.recordDependence(*LivenessAA, QueryingAA, DepClassTy::OPTIONAL);
// All values have been visited.
return true;
@@ -282,34 +390,18 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
llvm_unreachable("Expected enum or string attribute!");
}
-static const Value *getPointerOperand(const Instruction *I) {
- if (auto *LI = dyn_cast<LoadInst>(I))
- if (!LI->isVolatile())
- return LI->getPointerOperand();
- if (auto *SI = dyn_cast<StoreInst>(I))
- if (!SI->isVolatile())
- return SI->getPointerOperand();
-
- if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(I))
- if (!CXI->isVolatile())
- return CXI->getPointerOperand();
-
- if (auto *RMWI = dyn_cast<AtomicRMWInst>(I))
- if (!RMWI->isVolatile())
- return RMWI->getPointerOperand();
-
- return nullptr;
-}
-static const Value *getBasePointerOfAccessPointerOperand(const Instruction *I,
- int64_t &BytesOffset,
- const DataLayout &DL) {
- const Value *Ptr = getPointerOperand(I);
+static const Value *
+getBasePointerOfAccessPointerOperand(const Instruction *I, int64_t &BytesOffset,
+ const DataLayout &DL,
+ bool AllowNonInbounds = false) {
+ const Value *Ptr =
+ Attributor::getPointerOperand(I, /* AllowVolatile */ false);
if (!Ptr)
return nullptr;
return GetPointerBaseWithConstantOffset(Ptr, BytesOffset, DL,
- /*AllowNonInbounds*/ false);
+ AllowNonInbounds);
}
ChangeStatus AbstractAttribute::update(Attributor &A) {
@@ -328,7 +420,7 @@ ChangeStatus AbstractAttribute::update(Attributor &A) {
}
ChangeStatus
-IRAttributeManifest::manifestAttrs(Attributor &A, IRPosition &IRP,
+IRAttributeManifest::manifestAttrs(Attributor &A, const IRPosition &IRP,
const ArrayRef<Attribute> &DeducedAttrs) {
Function *ScopeFn = IRP.getAssociatedFunction();
IRPosition::Kind PK = IRP.getPositionKind();
@@ -457,13 +549,20 @@ bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs,
}
void IRPosition::getAttrs(ArrayRef<Attribute::AttrKind> AKs,
- SmallVectorImpl<Attribute> &Attrs) const {
- for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this))
+ SmallVectorImpl<Attribute> &Attrs,
+ bool IgnoreSubsumingPositions) const {
+ for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) {
for (Attribute::AttrKind AK : AKs) {
const Attribute &Attr = EquivIRP.getAttr(AK);
if (Attr.getKindAsEnum() == AK)
Attrs.push_back(Attr);
}
+ // The first position returned by the SubsumingPositionIterator is
+ // always the position itself. If we ignore subsuming positions we
+ // are done after the first iteration.
+ if (IgnoreSubsumingPositions)
+ break;
+ }
}
void IRPosition::verify() {
@@ -517,38 +616,24 @@ void IRPosition::verify() {
}
namespace {
-/// Helper functions to clamp a state \p S of type \p StateType with the
+/// Helper function to clamp a state \p S of type \p StateType with the
/// information in \p R and indicate/return if \p S did change (as-in update is
/// required to be run again).
-///
-///{
template <typename StateType>
-ChangeStatus clampStateAndIndicateChange(StateType &S, const StateType &R);
-
-template <>
-ChangeStatus clampStateAndIndicateChange<IntegerState>(IntegerState &S,
- const IntegerState &R) {
+ChangeStatus clampStateAndIndicateChange(StateType &S, const StateType &R) {
auto Assumed = S.getAssumed();
S ^= R;
return Assumed == S.getAssumed() ? ChangeStatus::UNCHANGED
: ChangeStatus::CHANGED;
}
-template <>
-ChangeStatus clampStateAndIndicateChange<BooleanState>(BooleanState &S,
- const BooleanState &R) {
- return clampStateAndIndicateChange<IntegerState>(S, R);
-}
-///}
-
/// Clamp the information known for all returned values of a function
/// (identified by \p QueryingAA) into \p S.
template <typename AAType, typename StateType = typename AAType::StateType>
static void clampReturnedValueStates(Attributor &A, const AAType &QueryingAA,
StateType &S) {
LLVM_DEBUG(dbgs() << "[Attributor] Clamp return value states for "
- << static_cast<const AbstractAttribute &>(QueryingAA)
- << " into " << S << "\n");
+ << QueryingAA << " into " << S << "\n");
assert((QueryingAA.getIRPosition().getPositionKind() ==
IRPosition::IRP_RETURNED ||
@@ -593,7 +678,8 @@ struct AAComposeTwoGenericDeduction
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
- ChangeStatus ChangedF = F<AAType, G<AAType, Base, StateType>, StateType>::updateImpl(A);
+ ChangeStatus ChangedF =
+ F<AAType, G<AAType, Base, StateType>, StateType>::updateImpl(A);
ChangeStatus ChangedG = G<AAType, Base, StateType>::updateImpl(A);
return ChangedF | ChangedG;
}
@@ -621,8 +707,7 @@ template <typename AAType, typename StateType = typename AAType::StateType>
static void clampCallSiteArgumentStates(Attributor &A, const AAType &QueryingAA,
StateType &S) {
LLVM_DEBUG(dbgs() << "[Attributor] Clamp call site argument states for "
- << static_cast<const AbstractAttribute &>(QueryingAA)
- << " into " << S << "\n");
+ << QueryingAA << " into " << S << "\n");
assert(QueryingAA.getIRPosition().getPositionKind() ==
IRPosition::IRP_ARGUMENT &&
@@ -718,7 +803,7 @@ struct AAFromMustBeExecutedContext : public Base {
void initialize(Attributor &A) override {
Base::initialize(A);
- IRPosition &IRP = this->getIRPosition();
+ const IRPosition &IRP = this->getIRPosition();
Instruction *CtxI = IRP.getCtxI();
if (!CtxI)
@@ -739,21 +824,16 @@ struct AAFromMustBeExecutedContext : public Base {
MustBeExecutedContextExplorer &Explorer =
A.getInfoCache().getMustBeExecutedContextExplorer();
- SetVector<const Use *> NextUses;
-
- for (const Use *U : Uses) {
+ auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI);
+ for (unsigned u = 0; u < Uses.size(); ++u) {
+ const Use *U = Uses[u];
if (const Instruction *UserI = dyn_cast<Instruction>(U->getUser())) {
- auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI);
- bool Found = EIt.count(UserI);
- while (!Found && ++EIt != EEnd)
- Found = EIt.getCurrentInst() == UserI;
+ bool Found = Explorer.findInContextOf(UserI, EIt, EEnd);
if (Found && Base::followUse(A, U, UserI))
for (const Use &Us : UserI->uses())
- NextUses.insert(&Us);
+ Uses.insert(&Us);
}
}
- for (const Use *U : NextUses)
- Uses.insert(U);
return BeforeState == S ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED;
}
@@ -994,13 +1074,15 @@ ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) {
auto ReplaceCallSiteUsersWith = [](CallBase &CB, Constant &C) {
if (CB.getNumUses() == 0 || CB.isMustTailCall())
return ChangeStatus::UNCHANGED;
- CB.replaceAllUsesWith(&C);
+ replaceAllInstructionUsesWith(CB, C);
return ChangeStatus::CHANGED;
};
// If the assumed unique return value is an argument, annotate it.
if (auto *UniqueRVArg = dyn_cast<Argument>(UniqueRV.getValue())) {
- getIRPosition() = IRPosition::argument(*UniqueRVArg);
+ // TODO: This should be handled differently!
+ this->AnchorVal = UniqueRVArg;
+ this->KindOrArgNo = UniqueRVArg->getArgNo();
Changed = IRAttribute::manifest(A);
} else if (auto *RVC = dyn_cast<Constant>(UniqueRV.getValue())) {
// We can replace the returned value with the unique returned constant.
@@ -1010,14 +1092,18 @@ ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) {
if (CallBase *CB = dyn_cast<CallBase>(U.getUser()))
if (CB->isCallee(&U)) {
Constant *RVCCast =
- ConstantExpr::getTruncOrBitCast(RVC, CB->getType());
+ CB->getType() == RVC->getType()
+ ? RVC
+ : ConstantExpr::getTruncOrBitCast(RVC, CB->getType());
Changed = ReplaceCallSiteUsersWith(*CB, *RVCCast) | Changed;
}
} else {
assert(isa<CallBase>(AnchorValue) &&
"Expcected a function or call base anchor!");
Constant *RVCCast =
- ConstantExpr::getTruncOrBitCast(RVC, AnchorValue.getType());
+ AnchorValue.getType() == RVC->getType()
+ ? RVC
+ : ConstantExpr::getTruncOrBitCast(RVC, AnchorValue.getType());
Changed = ReplaceCallSiteUsersWith(cast<CallBase>(AnchorValue), *RVCCast);
}
if (Changed == ChangeStatus::CHANGED)
@@ -1157,8 +1243,7 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
const auto &RetValAA = A.getAAFor<AAReturnedValues>(
*this, IRPosition::function(*CB->getCalledFunction()));
LLVM_DEBUG(dbgs() << "[AAReturnedValues] Found another AAReturnedValues: "
- << static_cast<const AbstractAttribute &>(RetValAA)
- << "\n");
+ << RetValAA << "\n");
// Skip dead ends, thus if we do not know anything about the returned
// call we mark it as unresolved and it will stay that way.
@@ -1393,7 +1478,7 @@ ChangeStatus AANoSyncImpl::updateImpl(Attributor &A) {
auto CheckRWInstForNoSync = [&](Instruction &I) {
/// We are looking for volatile instructions or Non-Relaxed atomics.
- /// FIXME: We should ipmrove the handling of intrinsics.
+ /// FIXME: We should improve the handling of intrinsics.
if (isa<IntrinsicInst>(&I) && isNoSyncIntrinsic(&I))
return true;
@@ -1532,6 +1617,115 @@ struct AANoFreeCallSite final : AANoFreeImpl {
void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(nofree); }
};
+/// NoFree attribute for floating values.
+struct AANoFreeFloating : AANoFreeImpl {
+ AANoFreeFloating(const IRPosition &IRP) : AANoFreeImpl(IRP) {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override{STATS_DECLTRACK_FLOATING_ATTR(nofree)}
+
+ /// See Abstract Attribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ const IRPosition &IRP = getIRPosition();
+
+ const auto &NoFreeAA =
+ A.getAAFor<AANoFree>(*this, IRPosition::function_scope(IRP));
+ if (NoFreeAA.isAssumedNoFree())
+ return ChangeStatus::UNCHANGED;
+
+ Value &AssociatedValue = getIRPosition().getAssociatedValue();
+ auto Pred = [&](const Use &U, bool &Follow) -> bool {
+ Instruction *UserI = cast<Instruction>(U.getUser());
+ if (auto *CB = dyn_cast<CallBase>(UserI)) {
+ if (CB->isBundleOperand(&U))
+ return false;
+ if (!CB->isArgOperand(&U))
+ return true;
+ unsigned ArgNo = CB->getArgOperandNo(&U);
+
+ const auto &NoFreeArg = A.getAAFor<AANoFree>(
+ *this, IRPosition::callsite_argument(*CB, ArgNo));
+ return NoFreeArg.isAssumedNoFree();
+ }
+
+ if (isa<GetElementPtrInst>(UserI) || isa<BitCastInst>(UserI) ||
+ isa<PHINode>(UserI) || isa<SelectInst>(UserI)) {
+ Follow = true;
+ return true;
+ }
+
+ // Unknown user.
+ return false;
+ };
+ if (!A.checkForAllUses(Pred, *this, AssociatedValue))
+ return indicatePessimisticFixpoint();
+
+ return ChangeStatus::UNCHANGED;
+ }
+};
+
+/// NoFree attribute for a call site argument.
+struct AANoFreeArgument final : AANoFreeFloating {
+ AANoFreeArgument(const IRPosition &IRP) : AANoFreeFloating(IRP) {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nofree) }
+};
+
+/// NoFree attribute for call site arguments.
+struct AANoFreeCallSiteArgument final : AANoFreeFloating {
+ AANoFreeCallSiteArgument(const IRPosition &IRP) : AANoFreeFloating(IRP) {}
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ // TODO: Once we have call site specific value information we can provide
+ // call site specific liveness information and then it makes
+ // sense to specialize attributes for call sites arguments instead of
+ // redirecting requests to the callee argument.
+ Argument *Arg = getAssociatedArgument();
+ if (!Arg)
+ return indicatePessimisticFixpoint();
+ const IRPosition &ArgPos = IRPosition::argument(*Arg);
+ auto &ArgAA = A.getAAFor<AANoFree>(*this, ArgPos);
+ return clampStateAndIndicateChange(
+ getState(), static_cast<const AANoFree::StateType &>(ArgAA.getState()));
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override{STATS_DECLTRACK_CSARG_ATTR(nofree)};
+};
+
+/// NoFree attribute for function return value.
+struct AANoFreeReturned final : AANoFreeFloating {
+ AANoFreeReturned(const IRPosition &IRP) : AANoFreeFloating(IRP) {
+ llvm_unreachable("NoFree is not applicable to function returns!");
+ }
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ llvm_unreachable("NoFree is not applicable to function returns!");
+ }
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ llvm_unreachable("NoFree is not applicable to function returns!");
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {}
+};
+
+/// NoFree attribute deduction for a call site return value.
+struct AANoFreeCallSiteReturned final : AANoFreeFloating {
+ AANoFreeCallSiteReturned(const IRPosition &IRP) : AANoFreeFloating(IRP) {}
+
+ ChangeStatus manifest(Attributor &A) override {
+ return ChangeStatus::UNCHANGED;
+ }
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(nofree) }
+};
+
/// ------------------------ NonNull Argument Attribute ------------------------
static int64_t getKnownNonNullAndDerefBytesForUse(
Attributor &A, AbstractAttribute &QueryingAA, Value &AssociatedValue,
@@ -1558,30 +1752,49 @@ static int64_t getKnownNonNullAndDerefBytesForUse(
unsigned ArgNo = ICS.getArgumentNo(U);
IRPosition IRP = IRPosition::callsite_argument(ICS, ArgNo);
- auto &DerefAA = A.getAAFor<AADereferenceable>(QueryingAA, IRP);
+ // As long as we only use known information there is no need to track
+ // dependences here.
+ auto &DerefAA = A.getAAFor<AADereferenceable>(QueryingAA, IRP,
+ /* TrackDependence */ false);
IsNonNull |= DerefAA.isKnownNonNull();
return DerefAA.getKnownDereferenceableBytes();
}
+ // We need to follow common pointer manipulation uses to the accesses they
+ // feed into. We can try to be smart to avoid looking through things we do not
+ // like for now, e.g., non-inbounds GEPs.
+ if (isa<CastInst>(I)) {
+ TrackUse = true;
+ return 0;
+ }
+ if (auto *GEP = dyn_cast<GetElementPtrInst>(I))
+ if (GEP->hasAllConstantIndices()) {
+ TrackUse = true;
+ return 0;
+ }
+
int64_t Offset;
if (const Value *Base = getBasePointerOfAccessPointerOperand(I, Offset, DL)) {
- if (Base == &AssociatedValue && getPointerOperand(I) == UseV) {
+ if (Base == &AssociatedValue &&
+ Attributor::getPointerOperand(I, /* AllowVolatile */ false) == UseV) {
int64_t DerefBytes =
- Offset + (int64_t)DL.getTypeStoreSize(PtrTy->getPointerElementType());
+ (int64_t)DL.getTypeStoreSize(PtrTy->getPointerElementType()) + Offset;
IsNonNull |= !NullPointerIsDefined;
- return DerefBytes;
+ return std::max(int64_t(0), DerefBytes);
}
}
- if (const Value *Base =
- GetPointerBaseWithConstantOffset(UseV, Offset, DL,
- /*AllowNonInbounds*/ false)) {
- auto &DerefAA =
- A.getAAFor<AADereferenceable>(QueryingAA, IRPosition::value(*Base));
- IsNonNull |= (!NullPointerIsDefined && DerefAA.isKnownNonNull());
- IsNonNull |= (!NullPointerIsDefined && (Offset != 0));
- int64_t DerefBytes = DerefAA.getKnownDereferenceableBytes();
- return std::max(int64_t(0), DerefBytes - Offset);
+
+ /// Corner case when an offset is 0.
+ if (const Value *Base = getBasePointerOfAccessPointerOperand(
+ I, Offset, DL, /*AllowNonInbounds*/ true)) {
+ if (Offset == 0 && Base == &AssociatedValue &&
+ Attributor::getPointerOperand(I, /* AllowVolatile */ false) == UseV) {
+ int64_t DerefBytes =
+ (int64_t)DL.getTypeStoreSize(PtrTy->getPointerElementType());
+ IsNonNull |= !NullPointerIsDefined;
+ return std::max(int64_t(0), DerefBytes);
+ }
}
return 0;
@@ -1599,6 +1812,8 @@ struct AANonNullImpl : AANonNull {
if (!NullIsDefined &&
hasAttr({Attribute::NonNull, Attribute::Dereferenceable}))
indicateOptimisticFixpoint();
+ else if (isa<ConstantPointerNull>(getAssociatedValue()))
+ indicatePessimisticFixpoint();
else
AANonNull::initialize(A);
}
@@ -1609,7 +1824,7 @@ struct AANonNullImpl : AANonNull {
bool TrackUse = false;
getKnownNonNullAndDerefBytesForUse(A, *this, getAssociatedValue(), U, I,
IsNonNull, TrackUse);
- takeKnownMaximum(IsNonNull);
+ setKnown(IsNonNull);
return TrackUse;
}
@@ -1629,24 +1844,6 @@ struct AANonNullFloating
using Base = AAFromMustBeExecutedContext<AANonNull, AANonNullImpl>;
AANonNullFloating(const IRPosition &IRP) : Base(IRP) {}
- /// See AbstractAttribute::initialize(...).
- void initialize(Attributor &A) override {
- Base::initialize(A);
-
- if (isAtFixpoint())
- return;
-
- const IRPosition &IRP = getIRPosition();
- const Value &V = IRP.getAssociatedValue();
- const DataLayout &DL = A.getDataLayout();
-
- // TODO: This context sensitive query should be removed once we can do
- // context sensitive queries in the genericValueTraversal below.
- if (isKnownNonZero(&V, DL, 0, /* TODO: AC */ nullptr, IRP.getCtxI(),
- /* TODO: DT */ nullptr))
- indicateOptimisticFixpoint();
- }
-
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
ChangeStatus Change = Base::updateImpl(A);
@@ -1654,20 +1851,24 @@ struct AANonNullFloating
return Change;
if (!NullIsDefined) {
- const auto &DerefAA = A.getAAFor<AADereferenceable>(*this, getIRPosition());
+ const auto &DerefAA =
+ A.getAAFor<AADereferenceable>(*this, getIRPosition());
if (DerefAA.getAssumedDereferenceableBytes())
return Change;
}
const DataLayout &DL = A.getDataLayout();
- auto VisitValueCB = [&](Value &V, AAAlign::StateType &T,
+ DominatorTree *DT = nullptr;
+ InformationCache &InfoCache = A.getInfoCache();
+ if (const Function *Fn = getAnchorScope())
+ DT = InfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(*Fn);
+
+ auto VisitValueCB = [&](Value &V, AANonNull::StateType &T,
bool Stripped) -> bool {
const auto &AA = A.getAAFor<AANonNull>(*this, IRPosition::value(V));
if (!Stripped && this == &AA) {
- if (!isKnownNonZero(&V, DL, 0, /* TODO: AC */ nullptr,
- /* CtxI */ getCtxI(),
- /* TODO: DT */ nullptr))
+ if (!isKnownNonZero(&V, DL, 0, /* TODO: AC */ nullptr, getCtxI(), DT))
T.indicatePessimisticFixpoint();
} else {
// Use abstract attribute information.
@@ -1814,6 +2015,208 @@ struct AANoRecurseCallSite final : AANoRecurseImpl {
void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(norecurse); }
};
+/// -------------------- Undefined-Behavior Attributes ------------------------
+
+struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior {
+ AAUndefinedBehaviorImpl(const IRPosition &IRP) : AAUndefinedBehavior(IRP) {}
+
+ /// See AbstractAttribute::updateImpl(...).
+ // through a pointer (i.e. also branches etc.)
+ ChangeStatus updateImpl(Attributor &A) override {
+ const size_t UBPrevSize = KnownUBInsts.size();
+ const size_t NoUBPrevSize = AssumedNoUBInsts.size();
+
+ auto InspectMemAccessInstForUB = [&](Instruction &I) {
+ // Skip instructions that are already saved.
+ if (AssumedNoUBInsts.count(&I) || KnownUBInsts.count(&I))
+ return true;
+
+ // If we reach here, we know we have an instruction
+ // that accesses memory through a pointer operand,
+ // for which getPointerOperand() should give it to us.
+ const Value *PtrOp =
+ Attributor::getPointerOperand(&I, /* AllowVolatile */ true);
+ assert(PtrOp &&
+ "Expected pointer operand of memory accessing instruction");
+
+ // A memory access through a pointer is considered UB
+ // only if the pointer has constant null value.
+ // TODO: Expand it to not only check constant values.
+ if (!isa<ConstantPointerNull>(PtrOp)) {
+ AssumedNoUBInsts.insert(&I);
+ return true;
+ }
+ const Type *PtrTy = PtrOp->getType();
+
+ // Because we only consider instructions inside functions,
+ // assume that a parent function exists.
+ const Function *F = I.getFunction();
+
+ // A memory access using constant null pointer is only considered UB
+ // if null pointer is _not_ defined for the target platform.
+ if (llvm::NullPointerIsDefined(F, PtrTy->getPointerAddressSpace()))
+ AssumedNoUBInsts.insert(&I);
+ else
+ KnownUBInsts.insert(&I);
+ return true;
+ };
+
+ auto InspectBrInstForUB = [&](Instruction &I) {
+ // A conditional branch instruction is considered UB if it has `undef`
+ // condition.
+
+ // Skip instructions that are already saved.
+ if (AssumedNoUBInsts.count(&I) || KnownUBInsts.count(&I))
+ return true;
+
+ // We know we have a branch instruction.
+ auto BrInst = cast<BranchInst>(&I);
+
+ // Unconditional branches are never considered UB.
+ if (BrInst->isUnconditional())
+ return true;
+
+ // Either we stopped and the appropriate action was taken,
+ // or we got back a simplified value to continue.
+ Optional<Value *> SimplifiedCond =
+ stopOnUndefOrAssumed(A, BrInst->getCondition(), BrInst);
+ if (!SimplifiedCond.hasValue())
+ return true;
+ AssumedNoUBInsts.insert(&I);
+ return true;
+ };
+
+ A.checkForAllInstructions(InspectMemAccessInstForUB, *this,
+ {Instruction::Load, Instruction::Store,
+ Instruction::AtomicCmpXchg,
+ Instruction::AtomicRMW});
+ A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br});
+ if (NoUBPrevSize != AssumedNoUBInsts.size() ||
+ UBPrevSize != KnownUBInsts.size())
+ return ChangeStatus::CHANGED;
+ return ChangeStatus::UNCHANGED;
+ }
+
+ bool isKnownToCauseUB(Instruction *I) const override {
+ return KnownUBInsts.count(I);
+ }
+
+ bool isAssumedToCauseUB(Instruction *I) const override {
+ // In simple words, if an instruction is not in the assumed to _not_
+ // cause UB, then it is assumed UB (that includes those
+ // in the KnownUBInsts set). The rest is boilerplate
+ // is to ensure that it is one of the instructions we test
+ // for UB.
+
+ switch (I->getOpcode()) {
+ case Instruction::Load:
+ case Instruction::Store:
+ case Instruction::AtomicCmpXchg:
+ case Instruction::AtomicRMW:
+ return !AssumedNoUBInsts.count(I);
+ case Instruction::Br: {
+ auto BrInst = cast<BranchInst>(I);
+ if (BrInst->isUnconditional())
+ return false;
+ return !AssumedNoUBInsts.count(I);
+ } break;
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ ChangeStatus manifest(Attributor &A) override {
+ if (KnownUBInsts.empty())
+ return ChangeStatus::UNCHANGED;
+ for (Instruction *I : KnownUBInsts)
+ A.changeToUnreachableAfterManifest(I);
+ return ChangeStatus::CHANGED;
+ }
+
+ /// See AbstractAttribute::getAsStr()
+ const std::string getAsStr() const override {
+ return getAssumed() ? "undefined-behavior" : "no-ub";
+ }
+
+ /// Note: The correctness of this analysis depends on the fact that the
+ /// following 2 sets will stop changing after some point.
+ /// "Change" here means that their size changes.
+ /// The size of each set is monotonically increasing
+ /// (we only add items to them) and it is upper bounded by the number of
+ /// instructions in the processed function (we can never save more
+ /// elements in either set than this number). Hence, at some point,
+ /// they will stop increasing.
+ /// Consequently, at some point, both sets will have stopped
+ /// changing, effectively making the analysis reach a fixpoint.
+
+ /// Note: These 2 sets are disjoint and an instruction can be considered
+ /// one of 3 things:
+ /// 1) Known to cause UB (AAUndefinedBehavior could prove it) and put it in
+ /// the KnownUBInsts set.
+ /// 2) Assumed to cause UB (in every updateImpl, AAUndefinedBehavior
+ /// has a reason to assume it).
+ /// 3) Assumed to not cause UB. very other instruction - AAUndefinedBehavior
+ /// could not find a reason to assume or prove that it can cause UB,
+ /// hence it assumes it doesn't. We have a set for these instructions
+ /// so that we don't reprocess them in every update.
+ /// Note however that instructions in this set may cause UB.
+
+protected:
+ /// A set of all live instructions _known_ to cause UB.
+ SmallPtrSet<Instruction *, 8> KnownUBInsts;
+
+private:
+ /// A set of all the (live) instructions that are assumed to _not_ cause UB.
+ SmallPtrSet<Instruction *, 8> AssumedNoUBInsts;
+
+ // Should be called on updates in which if we're processing an instruction
+ // \p I that depends on a value \p V, one of the following has to happen:
+ // - If the value is assumed, then stop.
+ // - If the value is known but undef, then consider it UB.
+ // - Otherwise, do specific processing with the simplified value.
+ // We return None in the first 2 cases to signify that an appropriate
+ // action was taken and the caller should stop.
+ // Otherwise, we return the simplified value that the caller should
+ // use for specific processing.
+ Optional<Value *> stopOnUndefOrAssumed(Attributor &A, const Value *V,
+ Instruction *I) {
+ const auto &ValueSimplifyAA =
+ A.getAAFor<AAValueSimplify>(*this, IRPosition::value(*V));
+ Optional<Value *> SimplifiedV =
+ ValueSimplifyAA.getAssumedSimplifiedValue(A);
+ if (!ValueSimplifyAA.isKnown()) {
+ // Don't depend on assumed values.
+ return llvm::None;
+ }
+ if (!SimplifiedV.hasValue()) {
+ // If it is known (which we tested above) but it doesn't have a value,
+ // then we can assume `undef` and hence the instruction is UB.
+ KnownUBInsts.insert(I);
+ return llvm::None;
+ }
+ Value *Val = SimplifiedV.getValue();
+ if (isa<UndefValue>(Val)) {
+ KnownUBInsts.insert(I);
+ return llvm::None;
+ }
+ return Val;
+ }
+};
+
+struct AAUndefinedBehaviorFunction final : AAUndefinedBehaviorImpl {
+ AAUndefinedBehaviorFunction(const IRPosition &IRP)
+ : AAUndefinedBehaviorImpl(IRP) {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECL(UndefinedBehaviorInstruction, Instruction,
+ "Number of instructions known to have UB");
+ BUILD_STAT_NAME(UndefinedBehaviorInstruction, Instruction) +=
+ KnownUBInsts.size();
+ }
+};
+
/// ------------------------ Will-Return Attributes ----------------------------
// Helper function that checks whether a function has any cycle.
@@ -1914,6 +2317,32 @@ struct AAWillReturnCallSite final : AAWillReturnImpl {
void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(willreturn); }
};
+/// -------------------AAReachability Attribute--------------------------
+
+struct AAReachabilityImpl : AAReachability {
+ AAReachabilityImpl(const IRPosition &IRP) : AAReachability(IRP) {}
+
+ const std::string getAsStr() const override {
+ // TODO: Return the number of reachable queries.
+ return "reachable";
+ }
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override { indicatePessimisticFixpoint(); }
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ return indicatePessimisticFixpoint();
+ }
+};
+
+struct AAReachabilityFunction final : public AAReachabilityImpl {
+ AAReachabilityFunction(const IRPosition &IRP) : AAReachabilityImpl(IRP) {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_FN_ATTR(reachable); }
+};
+
/// ------------------------ NoAlias Argument Attribute ------------------------
struct AANoAliasImpl : AANoAlias {
@@ -1954,8 +2383,43 @@ struct AANoAliasFloating final : AANoAliasImpl {
/// NoAlias attribute for an argument.
struct AANoAliasArgument final
: AAArgumentFromCallSiteArguments<AANoAlias, AANoAliasImpl> {
- AANoAliasArgument(const IRPosition &IRP)
- : AAArgumentFromCallSiteArguments<AANoAlias, AANoAliasImpl>(IRP) {}
+ using Base = AAArgumentFromCallSiteArguments<AANoAlias, AANoAliasImpl>;
+ AANoAliasArgument(const IRPosition &IRP) : Base(IRP) {}
+
+ /// See AbstractAttribute::update(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ // We have to make sure no-alias on the argument does not break
+ // synchronization when this is a callback argument, see also [1] below.
+ // If synchronization cannot be affected, we delegate to the base updateImpl
+ // function, otherwise we give up for now.
+
+ // If the function is no-sync, no-alias cannot break synchronization.
+ const auto &NoSyncAA = A.getAAFor<AANoSync>(
+ *this, IRPosition::function_scope(getIRPosition()));
+ if (NoSyncAA.isAssumedNoSync())
+ return Base::updateImpl(A);
+
+ // If the argument is read-only, no-alias cannot break synchronization.
+ const auto &MemBehaviorAA =
+ A.getAAFor<AAMemoryBehavior>(*this, getIRPosition());
+ if (MemBehaviorAA.isAssumedReadOnly())
+ return Base::updateImpl(A);
+
+ // If the argument is never passed through callbacks, no-alias cannot break
+ // synchronization.
+ if (A.checkForAllCallSites(
+ [](AbstractCallSite ACS) { return !ACS.isCallbackCall(); }, *this,
+ true))
+ return Base::updateImpl(A);
+
+ // TODO: add no-alias but make sure it doesn't break synchronization by
+ // introducing fake uses. See:
+ // [1] Compiler Optimizations for OpenMP, J. Doerfert and H. Finkel,
+ // International Workshop on OpenMP 2018,
+ // http://compilers.cs.uni-saarland.de/people/doerfert/par_opt18.pdf
+
+ return indicatePessimisticFixpoint();
+ }
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(noalias) }
@@ -1987,6 +2451,8 @@ struct AANoAliasCallSiteArgument final : AANoAliasImpl {
// (i) Check whether noalias holds in the definition.
auto &NoAliasAA = A.getAAFor<AANoAlias>(*this, IRP);
+ LLVM_DEBUG(dbgs() << "[Attributor][AANoAliasCSArg] check definition: " << V
+ << " :: " << NoAliasAA << "\n");
if (!NoAliasAA.isAssumedNoAlias())
return indicatePessimisticFixpoint();
@@ -2008,6 +2474,7 @@ struct AANoAliasCallSiteArgument final : AANoAliasImpl {
// (iii) Check there is no other pointer argument which could alias with the
// value.
+ // TODO: AbstractCallSite
ImmutableCallSite ICS(&getAnchorValue());
for (unsigned i = 0; i < ICS.getNumArgOperands(); i++) {
if (getArgNo() == (int)i)
@@ -2018,7 +2485,7 @@ struct AANoAliasCallSiteArgument final : AANoAliasImpl {
if (const Function *F = getAnchorScope()) {
if (AAResults *AAR = A.getInfoCache().getAAResultsForFunction(*F)) {
- bool IsAliasing = AAR->isNoAlias(&getAssociatedValue(), ArgOp);
+ bool IsAliasing = !AAR->isNoAlias(&getAssociatedValue(), ArgOp);
LLVM_DEBUG(dbgs()
<< "[Attributor][NoAliasCSArg] Check alias between "
"callsite arguments "
@@ -2026,7 +2493,7 @@ struct AANoAliasCallSiteArgument final : AANoAliasImpl {
<< getAssociatedValue() << " " << *ArgOp << " => "
<< (IsAliasing ? "" : "no-") << "alias \n");
- if (IsAliasing)
+ if (!IsAliasing)
continue;
}
}
@@ -2108,42 +2575,229 @@ struct AANoAliasCallSiteReturned final : AANoAliasImpl {
/// -------------------AAIsDead Function Attribute-----------------------
-struct AAIsDeadImpl : public AAIsDead {
- AAIsDeadImpl(const IRPosition &IRP) : AAIsDead(IRP) {}
+struct AAIsDeadValueImpl : public AAIsDead {
+ AAIsDeadValueImpl(const IRPosition &IRP) : AAIsDead(IRP) {}
+
+ /// See AAIsDead::isAssumedDead().
+ bool isAssumedDead() const override { return getAssumed(); }
+
+ /// See AAIsDead::isAssumedDead(BasicBlock *).
+ bool isAssumedDead(const BasicBlock *BB) const override { return false; }
+
+ /// See AAIsDead::isKnownDead(BasicBlock *).
+ bool isKnownDead(const BasicBlock *BB) const override { return false; }
+
+ /// See AAIsDead::isAssumedDead(Instruction *I).
+ bool isAssumedDead(const Instruction *I) const override {
+ return I == getCtxI() && isAssumedDead();
+ }
+
+ /// See AAIsDead::isKnownDead(Instruction *I).
+ bool isKnownDead(const Instruction *I) const override {
+ return I == getCtxI() && getKnown();
+ }
+ /// See AbstractAttribute::getAsStr().
+ const std::string getAsStr() const override {
+ return isAssumedDead() ? "assumed-dead" : "assumed-live";
+ }
+};
+
+struct AAIsDeadFloating : public AAIsDeadValueImpl {
+ AAIsDeadFloating(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- const Function *F = getAssociatedFunction();
- if (F && !F->isDeclaration())
- exploreFromEntry(A, F);
+ if (Instruction *I = dyn_cast<Instruction>(&getAssociatedValue()))
+ if (!wouldInstructionBeTriviallyDead(I))
+ indicatePessimisticFixpoint();
+ if (isa<UndefValue>(getAssociatedValue()))
+ indicatePessimisticFixpoint();
+ }
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ auto UsePred = [&](const Use &U, bool &Follow) {
+ Instruction *UserI = cast<Instruction>(U.getUser());
+ if (CallSite CS = CallSite(UserI)) {
+ if (!CS.isArgOperand(&U))
+ return false;
+ const IRPosition &CSArgPos =
+ IRPosition::callsite_argument(CS, CS.getArgumentNo(&U));
+ const auto &CSArgIsDead = A.getAAFor<AAIsDead>(*this, CSArgPos);
+ return CSArgIsDead.isAssumedDead();
+ }
+ if (ReturnInst *RI = dyn_cast<ReturnInst>(UserI)) {
+ const IRPosition &RetPos = IRPosition::returned(*RI->getFunction());
+ const auto &RetIsDeadAA = A.getAAFor<AAIsDead>(*this, RetPos);
+ return RetIsDeadAA.isAssumedDead();
+ }
+ Follow = true;
+ return wouldInstructionBeTriviallyDead(UserI);
+ };
+
+ if (!A.checkForAllUses(UsePred, *this, getAssociatedValue()))
+ return indicatePessimisticFixpoint();
+ return ChangeStatus::UNCHANGED;
+ }
+
+ /// See AbstractAttribute::manifest(...).
+ ChangeStatus manifest(Attributor &A) override {
+ Value &V = getAssociatedValue();
+ if (auto *I = dyn_cast<Instruction>(&V))
+ if (wouldInstructionBeTriviallyDead(I)) {
+ A.deleteAfterManifest(*I);
+ return ChangeStatus::CHANGED;
+ }
+
+ if (V.use_empty())
+ return ChangeStatus::UNCHANGED;
+
+ UndefValue &UV = *UndefValue::get(V.getType());
+ bool AnyChange = A.changeValueAfterManifest(V, UV);
+ return AnyChange ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
}
- void exploreFromEntry(Attributor &A, const Function *F) {
- ToBeExploredPaths.insert(&(F->getEntryBlock().front()));
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECLTRACK_FLOATING_ATTR(IsDead)
+ }
+};
- for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)
- if (const Instruction *NextNoReturnI =
- findNextNoReturn(A, ToBeExploredPaths[i]))
- NoReturnCalls.insert(NextNoReturnI);
+struct AAIsDeadArgument : public AAIsDeadFloating {
+ AAIsDeadArgument(const IRPosition &IRP) : AAIsDeadFloating(IRP) {}
- // Mark the block live after we looked for no-return instructions.
- assumeLive(A, F->getEntryBlock());
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ if (!getAssociatedFunction()->hasExactDefinition())
+ indicatePessimisticFixpoint();
}
- /// Find the next assumed noreturn instruction in the block of \p I starting
- /// from, thus including, \p I.
- ///
- /// The caller is responsible to monitor the ToBeExploredPaths set as new
- /// instructions discovered in other basic block will be placed in there.
- ///
- /// \returns The next assumed noreturn instructions in the block of \p I
- /// starting from, thus including, \p I.
- const Instruction *findNextNoReturn(Attributor &A, const Instruction *I);
+ /// See AbstractAttribute::manifest(...).
+ ChangeStatus manifest(Attributor &A) override {
+ ChangeStatus Changed = AAIsDeadFloating::manifest(A);
+ Argument &Arg = *getAssociatedArgument();
+ if (Arg.getParent()->hasLocalLinkage())
+ if (A.registerFunctionSignatureRewrite(
+ Arg, /* ReplacementTypes */ {},
+ Attributor::ArgumentReplacementInfo::CalleeRepairCBTy{},
+ Attributor::ArgumentReplacementInfo::ACSRepairCBTy{}))
+ return ChangeStatus::CHANGED;
+ return Changed;
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(IsDead) }
+};
+
+struct AAIsDeadCallSiteArgument : public AAIsDeadValueImpl {
+ AAIsDeadCallSiteArgument(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ if (isa<UndefValue>(getAssociatedValue()))
+ indicatePessimisticFixpoint();
+ }
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ // TODO: Once we have call site specific value information we can provide
+ // call site specific liveness information and then it makes
+ // sense to specialize attributes for call sites arguments instead of
+ // redirecting requests to the callee argument.
+ Argument *Arg = getAssociatedArgument();
+ if (!Arg)
+ return indicatePessimisticFixpoint();
+ const IRPosition &ArgPos = IRPosition::argument(*Arg);
+ auto &ArgAA = A.getAAFor<AAIsDead>(*this, ArgPos);
+ return clampStateAndIndicateChange(
+ getState(), static_cast<const AAIsDead::StateType &>(ArgAA.getState()));
+ }
+
+ /// See AbstractAttribute::manifest(...).
+ ChangeStatus manifest(Attributor &A) override {
+ CallBase &CB = cast<CallBase>(getAnchorValue());
+ Use &U = CB.getArgOperandUse(getArgNo());
+ assert(!isa<UndefValue>(U.get()) &&
+ "Expected undef values to be filtered out!");
+ UndefValue &UV = *UndefValue::get(U->getType());
+ if (A.changeUseAfterManifest(U, UV))
+ return ChangeStatus::CHANGED;
+ return ChangeStatus::UNCHANGED;
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_CSARG_ATTR(IsDead) }
+};
+
+struct AAIsDeadReturned : public AAIsDeadValueImpl {
+ AAIsDeadReturned(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {}
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+
+ auto PredForCallSite = [&](AbstractCallSite ACS) {
+ if (ACS.isCallbackCall())
+ return false;
+ const IRPosition &CSRetPos =
+ IRPosition::callsite_returned(ACS.getCallSite());
+ const auto &RetIsDeadAA = A.getAAFor<AAIsDead>(*this, CSRetPos);
+ return RetIsDeadAA.isAssumedDead();
+ };
+
+ if (!A.checkForAllCallSites(PredForCallSite, *this, true))
+ return indicatePessimisticFixpoint();
+
+ return ChangeStatus::UNCHANGED;
+ }
+
+ /// See AbstractAttribute::manifest(...).
+ ChangeStatus manifest(Attributor &A) override {
+ // TODO: Rewrite the signature to return void?
+ bool AnyChange = false;
+ UndefValue &UV = *UndefValue::get(getAssociatedFunction()->getReturnType());
+ auto RetInstPred = [&](Instruction &I) {
+ ReturnInst &RI = cast<ReturnInst>(I);
+ if (!isa<UndefValue>(RI.getReturnValue()))
+ AnyChange |= A.changeUseAfterManifest(RI.getOperandUse(0), UV);
+ return true;
+ };
+ A.checkForAllInstructions(RetInstPred, *this, {Instruction::Ret});
+ return AnyChange ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_FNRET_ATTR(IsDead) }
+};
+
+struct AAIsDeadCallSiteReturned : public AAIsDeadFloating {
+ AAIsDeadCallSiteReturned(const IRPosition &IRP) : AAIsDeadFloating(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(IsDead) }
+};
+
+struct AAIsDeadFunction : public AAIsDead {
+ AAIsDeadFunction(const IRPosition &IRP) : AAIsDead(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ const Function *F = getAssociatedFunction();
+ if (F && !F->isDeclaration()) {
+ ToBeExploredFrom.insert(&F->getEntryBlock().front());
+ assumeLive(A, F->getEntryBlock());
+ }
+ }
/// See AbstractAttribute::getAsStr().
const std::string getAsStr() const override {
return "Live[#BB " + std::to_string(AssumedLiveBlocks.size()) + "/" +
- std::to_string(getAssociatedFunction()->size()) + "][#NRI " +
- std::to_string(NoReturnCalls.size()) + "]";
+ std::to_string(getAssociatedFunction()->size()) + "][#TBEP " +
+ std::to_string(ToBeExploredFrom.size()) + "][#KDE " +
+ std::to_string(KnownDeadEnds.size()) + "]";
}
/// See AbstractAttribute::manifest(...).
@@ -2164,73 +2818,22 @@ struct AAIsDeadImpl : public AAIsDead {
// function allows to catch asynchronous exceptions.
bool Invoke2CallAllowed = !mayCatchAsynchronousExceptions(F);
- for (const Instruction *NRC : NoReturnCalls) {
- Instruction *I = const_cast<Instruction *>(NRC);
- BasicBlock *BB = I->getParent();
- Instruction *SplitPos = I->getNextNode();
- // TODO: mark stuff before unreachable instructions as dead.
-
- if (auto *II = dyn_cast<InvokeInst>(I)) {
- // If we keep the invoke the split position is at the beginning of the
- // normal desitination block (it invokes a noreturn function after all).
- BasicBlock *NormalDestBB = II->getNormalDest();
- SplitPos = &NormalDestBB->front();
-
- /// Invoke is replaced with a call and unreachable is placed after it if
- /// the callee is nounwind and noreturn. Otherwise, we keep the invoke
- /// and only place an unreachable in the normal successor.
- if (Invoke2CallAllowed) {
- if (II->getCalledFunction()) {
- const IRPosition &IPos = IRPosition::callsite_function(*II);
- const auto &AANoUnw = A.getAAFor<AANoUnwind>(*this, IPos);
- if (AANoUnw.isAssumedNoUnwind()) {
- LLVM_DEBUG(dbgs()
- << "[AAIsDead] Replace invoke with call inst\n");
- // We do not need an invoke (II) but instead want a call followed
- // by an unreachable. However, we do not remove II as other
- // abstract attributes might have it cached as part of their
- // results. Given that we modify the CFG anyway, we simply keep II
- // around but in a new dead block. To avoid II being live through
- // a different edge we have to ensure the block we place it in is
- // only reached from the current block of II and then not reached
- // at all when we insert the unreachable.
- SplitBlockPredecessors(NormalDestBB, {BB}, ".i2c");
- CallInst *CI = createCallMatchingInvoke(II);
- CI->insertBefore(II);
- CI->takeName(II);
- II->replaceAllUsesWith(CI);
- SplitPos = CI->getNextNode();
- }
- }
- }
-
- if (SplitPos == &NormalDestBB->front()) {
- // If this is an invoke of a noreturn function the edge to the normal
- // destination block is dead but not necessarily the block itself.
- // TODO: We need to move to an edge based system during deduction and
- // also manifest.
- assert(!NormalDestBB->isLandingPad() &&
- "Expected the normal destination not to be a landingpad!");
- if (NormalDestBB->getUniquePredecessor() == BB) {
- assumeLive(A, *NormalDestBB);
- } else {
- BasicBlock *SplitBB =
- SplitBlockPredecessors(NormalDestBB, {BB}, ".dead");
- // The split block is live even if it contains only an unreachable
- // instruction at the end.
- assumeLive(A, *SplitBB);
- SplitPos = SplitBB->getTerminator();
- HasChanged = ChangeStatus::CHANGED;
- }
- }
- }
-
- if (isa_and_nonnull<UnreachableInst>(SplitPos))
+ KnownDeadEnds.set_union(ToBeExploredFrom);
+ for (const Instruction *DeadEndI : KnownDeadEnds) {
+ auto *CB = dyn_cast<CallBase>(DeadEndI);
+ if (!CB)
+ continue;
+ const auto &NoReturnAA =
+ A.getAAFor<AANoReturn>(*this, IRPosition::callsite_function(*CB));
+ bool MayReturn = !NoReturnAA.isAssumedNoReturn();
+ if (MayReturn && (!Invoke2CallAllowed || !isa<InvokeInst>(CB)))
continue;
- BB = SplitPos->getParent();
- SplitBlock(BB, SplitPos);
- changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
+ if (auto *II = dyn_cast<InvokeInst>(DeadEndI))
+ A.registerInvokeWithDeadSuccessor(const_cast<InvokeInst &>(*II));
+ else
+ A.changeToUnreachableAfterManifest(
+ const_cast<Instruction *>(DeadEndI->getNextNode()));
HasChanged = ChangeStatus::CHANGED;
}
@@ -2244,6 +2847,12 @@ struct AAIsDeadImpl : public AAIsDead {
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override;
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {}
+
+ /// Returns true if the function is assumed dead.
+ bool isAssumedDead() const override { return false; }
+
/// See AAIsDead::isAssumedDead(BasicBlock *).
bool isAssumedDead(const BasicBlock *BB) const override {
assert(BB->getParent() == getAssociatedFunction() &&
@@ -2272,8 +2881,14 @@ struct AAIsDeadImpl : public AAIsDead {
if (!AssumedLiveBlocks.count(I->getParent()))
return true;
- // If it is not after a noreturn call, than it is live.
- return isAfterNoReturn(I);
+ // If it is not after a liveness barrier it is live.
+ const Instruction *PrevI = I->getPrevNode();
+ while (PrevI) {
+ if (KnownDeadEnds.count(PrevI) || ToBeExploredFrom.count(PrevI))
+ return true;
+ PrevI = PrevI->getPrevNode();
+ }
+ return false;
}
/// See AAIsDead::isKnownDead(Instruction *I).
@@ -2281,9 +2896,6 @@ struct AAIsDeadImpl : public AAIsDead {
return getKnown() && isAssumedDead(I);
}
- /// Check if instruction is after noreturn call, in other words, assumed dead.
- bool isAfterNoReturn(const Instruction *I) const;
-
/// Determine if \p F might catch asynchronous exceptions.
static bool mayCatchAsynchronousExceptions(const Function &F) {
return F.hasPersonalityFn() && !canSimplifyInvokeNoUnwind(&F);
@@ -2291,9 +2903,9 @@ struct AAIsDeadImpl : public AAIsDead {
/// Assume \p BB is (partially) live now and indicate to the Attributor \p A
/// that internal function called from \p BB should now be looked at.
- void assumeLive(Attributor &A, const BasicBlock &BB) {
+ bool assumeLive(Attributor &A, const BasicBlock &BB) {
if (!AssumedLiveBlocks.insert(&BB).second)
- return;
+ return false;
// We assume that all of BB is (probably) live now and if there are calls to
// internal functions we will assume that those are now live as well. This
@@ -2304,140 +2916,219 @@ struct AAIsDeadImpl : public AAIsDead {
if (const Function *F = ICS.getCalledFunction())
if (F->hasLocalLinkage())
A.markLiveInternalFunction(*F);
+ return true;
}
- /// Collection of to be explored paths.
- SmallSetVector<const Instruction *, 8> ToBeExploredPaths;
+ /// Collection of instructions that need to be explored again, e.g., we
+ /// did assume they do not transfer control to (one of their) successors.
+ SmallSetVector<const Instruction *, 8> ToBeExploredFrom;
+
+ /// Collection of instructions that are known to not transfer control.
+ SmallSetVector<const Instruction *, 8> KnownDeadEnds;
/// Collection of all assumed live BasicBlocks.
DenseSet<const BasicBlock *> AssumedLiveBlocks;
-
- /// Collection of calls with noreturn attribute, assumed or knwon.
- SmallSetVector<const Instruction *, 4> NoReturnCalls;
};
-struct AAIsDeadFunction final : public AAIsDeadImpl {
- AAIsDeadFunction(const IRPosition &IRP) : AAIsDeadImpl(IRP) {}
+static bool
+identifyAliveSuccessors(Attributor &A, const CallBase &CB,
+ AbstractAttribute &AA,
+ SmallVectorImpl<const Instruction *> &AliveSuccessors) {
+ const IRPosition &IPos = IRPosition::callsite_function(CB);
+
+ const auto &NoReturnAA = A.getAAFor<AANoReturn>(AA, IPos);
+ if (NoReturnAA.isAssumedNoReturn())
+ return !NoReturnAA.isKnownNoReturn();
+ if (CB.isTerminator())
+ AliveSuccessors.push_back(&CB.getSuccessor(0)->front());
+ else
+ AliveSuccessors.push_back(CB.getNextNode());
+ return false;
+}
- /// See AbstractAttribute::trackStatistics()
- void trackStatistics() const override {
- STATS_DECL(PartiallyDeadBlocks, Function,
- "Number of basic blocks classified as partially dead");
- BUILD_STAT_NAME(PartiallyDeadBlocks, Function) += NoReturnCalls.size();
+static bool
+identifyAliveSuccessors(Attributor &A, const InvokeInst &II,
+ AbstractAttribute &AA,
+ SmallVectorImpl<const Instruction *> &AliveSuccessors) {
+ bool UsedAssumedInformation =
+ identifyAliveSuccessors(A, cast<CallBase>(II), AA, AliveSuccessors);
+
+ // First, determine if we can change an invoke to a call assuming the
+ // callee is nounwind. This is not possible if the personality of the
+ // function allows to catch asynchronous exceptions.
+ if (AAIsDeadFunction::mayCatchAsynchronousExceptions(*II.getFunction())) {
+ AliveSuccessors.push_back(&II.getUnwindDest()->front());
+ } else {
+ const IRPosition &IPos = IRPosition::callsite_function(II);
+ const auto &AANoUnw = A.getAAFor<AANoUnwind>(AA, IPos);
+ if (AANoUnw.isAssumedNoUnwind()) {
+ UsedAssumedInformation |= !AANoUnw.isKnownNoUnwind();
+ } else {
+ AliveSuccessors.push_back(&II.getUnwindDest()->front());
+ }
}
-};
+ return UsedAssumedInformation;
+}
-bool AAIsDeadImpl::isAfterNoReturn(const Instruction *I) const {
- const Instruction *PrevI = I->getPrevNode();
- while (PrevI) {
- if (NoReturnCalls.count(PrevI))
- return true;
- PrevI = PrevI->getPrevNode();
+static Optional<ConstantInt *>
+getAssumedConstant(Attributor &A, const Value &V, AbstractAttribute &AA,
+ bool &UsedAssumedInformation) {
+ const auto &ValueSimplifyAA =
+ A.getAAFor<AAValueSimplify>(AA, IRPosition::value(V));
+ Optional<Value *> SimplifiedV = ValueSimplifyAA.getAssumedSimplifiedValue(A);
+ UsedAssumedInformation |= !ValueSimplifyAA.isKnown();
+ if (!SimplifiedV.hasValue())
+ return llvm::None;
+ if (isa_and_nonnull<UndefValue>(SimplifiedV.getValue()))
+ return llvm::None;
+ return dyn_cast_or_null<ConstantInt>(SimplifiedV.getValue());
+}
+
+static bool
+identifyAliveSuccessors(Attributor &A, const BranchInst &BI,
+ AbstractAttribute &AA,
+ SmallVectorImpl<const Instruction *> &AliveSuccessors) {
+ bool UsedAssumedInformation = false;
+ if (BI.getNumSuccessors() == 1) {
+ AliveSuccessors.push_back(&BI.getSuccessor(0)->front());
+ } else {
+ Optional<ConstantInt *> CI =
+ getAssumedConstant(A, *BI.getCondition(), AA, UsedAssumedInformation);
+ if (!CI.hasValue()) {
+ // No value yet, assume both edges are dead.
+ } else if (CI.getValue()) {
+ const BasicBlock *SuccBB =
+ BI.getSuccessor(1 - CI.getValue()->getZExtValue());
+ AliveSuccessors.push_back(&SuccBB->front());
+ } else {
+ AliveSuccessors.push_back(&BI.getSuccessor(0)->front());
+ AliveSuccessors.push_back(&BI.getSuccessor(1)->front());
+ UsedAssumedInformation = false;
+ }
}
- return false;
+ return UsedAssumedInformation;
}
-const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A,
- const Instruction *I) {
- const BasicBlock *BB = I->getParent();
- const Function &F = *BB->getParent();
-
- // Flag to determine if we can change an invoke to a call assuming the callee
- // is nounwind. This is not possible if the personality of the function allows
- // to catch asynchronous exceptions.
- bool Invoke2CallAllowed = !mayCatchAsynchronousExceptions(F);
-
- // TODO: We should have a function that determines if an "edge" is dead.
- // Edges could be from an instruction to the next or from a terminator
- // to the successor. For now, we need to special case the unwind block
- // of InvokeInst below.
-
- while (I) {
- ImmutableCallSite ICS(I);
-
- if (ICS) {
- const IRPosition &IPos = IRPosition::callsite_function(ICS);
- // Regarless of the no-return property of an invoke instruction we only
- // learn that the regular successor is not reachable through this
- // instruction but the unwind block might still be.
- if (auto *Invoke = dyn_cast<InvokeInst>(I)) {
- // Use nounwind to justify the unwind block is dead as well.
- const auto &AANoUnw = A.getAAFor<AANoUnwind>(*this, IPos);
- if (!Invoke2CallAllowed || !AANoUnw.isAssumedNoUnwind()) {
- assumeLive(A, *Invoke->getUnwindDest());
- ToBeExploredPaths.insert(&Invoke->getUnwindDest()->front());
- }
+static bool
+identifyAliveSuccessors(Attributor &A, const SwitchInst &SI,
+ AbstractAttribute &AA,
+ SmallVectorImpl<const Instruction *> &AliveSuccessors) {
+ bool UsedAssumedInformation = false;
+ Optional<ConstantInt *> CI =
+ getAssumedConstant(A, *SI.getCondition(), AA, UsedAssumedInformation);
+ if (!CI.hasValue()) {
+ // No value yet, assume all edges are dead.
+ } else if (CI.getValue()) {
+ for (auto &CaseIt : SI.cases()) {
+ if (CaseIt.getCaseValue() == CI.getValue()) {
+ AliveSuccessors.push_back(&CaseIt.getCaseSuccessor()->front());
+ return UsedAssumedInformation;
}
-
- const auto &NoReturnAA = A.getAAFor<AANoReturn>(*this, IPos);
- if (NoReturnAA.isAssumedNoReturn())
- return I;
}
-
- I = I->getNextNode();
+ AliveSuccessors.push_back(&SI.getDefaultDest()->front());
+ return UsedAssumedInformation;
+ } else {
+ for (const BasicBlock *SuccBB : successors(SI.getParent()))
+ AliveSuccessors.push_back(&SuccBB->front());
}
+ return UsedAssumedInformation;
+}
- // get new paths (reachable blocks).
- for (const BasicBlock *SuccBB : successors(BB)) {
- assumeLive(A, *SuccBB);
- ToBeExploredPaths.insert(&SuccBB->front());
- }
+ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {
+ ChangeStatus Change = ChangeStatus::UNCHANGED;
- // No noreturn instruction found.
- return nullptr;
-}
+ LLVM_DEBUG(dbgs() << "[AAIsDead] Live [" << AssumedLiveBlocks.size() << "/"
+ << getAssociatedFunction()->size() << "] BBs and "
+ << ToBeExploredFrom.size() << " exploration points and "
+ << KnownDeadEnds.size() << " known dead ends\n");
-ChangeStatus AAIsDeadImpl::updateImpl(Attributor &A) {
- ChangeStatus Status = ChangeStatus::UNCHANGED;
+ // Copy and clear the list of instructions we need to explore from. It is
+ // refilled with instructions the next update has to look at.
+ SmallVector<const Instruction *, 8> Worklist(ToBeExploredFrom.begin(),
+ ToBeExploredFrom.end());
+ decltype(ToBeExploredFrom) NewToBeExploredFrom;
- // Temporary collection to iterate over existing noreturn instructions. This
- // will alow easier modification of NoReturnCalls collection
- SmallVector<const Instruction *, 8> NoReturnChanged;
+ SmallVector<const Instruction *, 8> AliveSuccessors;
+ while (!Worklist.empty()) {
+ const Instruction *I = Worklist.pop_back_val();
+ LLVM_DEBUG(dbgs() << "[AAIsDead] Exploration inst: " << *I << "\n");
- for (const Instruction *I : NoReturnCalls)
- NoReturnChanged.push_back(I);
+ AliveSuccessors.clear();
- for (const Instruction *I : NoReturnChanged) {
- size_t Size = ToBeExploredPaths.size();
+ bool UsedAssumedInformation = false;
+ switch (I->getOpcode()) {
+ // TODO: look for (assumed) UB to backwards propagate "deadness".
+ default:
+ if (I->isTerminator()) {
+ for (const BasicBlock *SuccBB : successors(I->getParent()))
+ AliveSuccessors.push_back(&SuccBB->front());
+ } else {
+ AliveSuccessors.push_back(I->getNextNode());
+ }
+ break;
+ case Instruction::Call:
+ UsedAssumedInformation = identifyAliveSuccessors(A, cast<CallInst>(*I),
+ *this, AliveSuccessors);
+ break;
+ case Instruction::Invoke:
+ UsedAssumedInformation = identifyAliveSuccessors(A, cast<InvokeInst>(*I),
+ *this, AliveSuccessors);
+ break;
+ case Instruction::Br:
+ UsedAssumedInformation = identifyAliveSuccessors(A, cast<BranchInst>(*I),
+ *this, AliveSuccessors);
+ break;
+ case Instruction::Switch:
+ UsedAssumedInformation = identifyAliveSuccessors(A, cast<SwitchInst>(*I),
+ *this, AliveSuccessors);
+ break;
+ }
- const Instruction *NextNoReturnI = findNextNoReturn(A, I);
- if (NextNoReturnI != I) {
- Status = ChangeStatus::CHANGED;
- NoReturnCalls.remove(I);
- if (NextNoReturnI)
- NoReturnCalls.insert(NextNoReturnI);
+ if (UsedAssumedInformation) {
+ NewToBeExploredFrom.insert(I);
+ } else {
+ Change = ChangeStatus::CHANGED;
+ if (AliveSuccessors.empty() ||
+ (I->isTerminator() && AliveSuccessors.size() < I->getNumSuccessors()))
+ KnownDeadEnds.insert(I);
}
- // Explore new paths.
- while (Size != ToBeExploredPaths.size()) {
- Status = ChangeStatus::CHANGED;
- if (const Instruction *NextNoReturnI =
- findNextNoReturn(A, ToBeExploredPaths[Size++]))
- NoReturnCalls.insert(NextNoReturnI);
+ LLVM_DEBUG(dbgs() << "[AAIsDead] #AliveSuccessors: "
+ << AliveSuccessors.size() << " UsedAssumedInformation: "
+ << UsedAssumedInformation << "\n");
+
+ for (const Instruction *AliveSuccessor : AliveSuccessors) {
+ if (!I->isTerminator()) {
+ assert(AliveSuccessors.size() == 1 &&
+ "Non-terminator expected to have a single successor!");
+ Worklist.push_back(AliveSuccessor);
+ } else {
+ if (assumeLive(A, *AliveSuccessor->getParent()))
+ Worklist.push_back(AliveSuccessor);
+ }
}
}
- LLVM_DEBUG(dbgs() << "[AAIsDead] AssumedLiveBlocks: "
- << AssumedLiveBlocks.size() << " Total number of blocks: "
- << getAssociatedFunction()->size() << "\n");
+ ToBeExploredFrom = std::move(NewToBeExploredFrom);
// If we know everything is live there is no need to query for liveness.
- if (NoReturnCalls.empty() &&
- getAssociatedFunction()->size() == AssumedLiveBlocks.size()) {
- // Indicating a pessimistic fixpoint will cause the state to be "invalid"
- // which will cause the Attributor to not return the AAIsDead on request,
- // which will prevent us from querying isAssumedDead().
- indicatePessimisticFixpoint();
- assert(!isValidState() && "Expected an invalid state!");
- Status = ChangeStatus::CHANGED;
- }
-
- return Status;
+ // Instead, indicating a pessimistic fixpoint will cause the state to be
+ // "invalid" and all queries to be answered conservatively without lookups.
+ // To be in this state we have to (1) finished the exploration and (3) not
+ // discovered any non-trivial dead end and (2) not ruled unreachable code
+ // dead.
+ if (ToBeExploredFrom.empty() &&
+ getAssociatedFunction()->size() == AssumedLiveBlocks.size() &&
+ llvm::all_of(KnownDeadEnds, [](const Instruction *DeadEndI) {
+ return DeadEndI->isTerminator() && DeadEndI->getNumSuccessors() == 0;
+ }))
+ return indicatePessimisticFixpoint();
+ return Change;
}
/// Liveness information for a call sites.
-struct AAIsDeadCallSite final : AAIsDeadImpl {
- AAIsDeadCallSite(const IRPosition &IRP) : AAIsDeadImpl(IRP) {}
+struct AAIsDeadCallSite final : AAIsDeadFunction {
+ AAIsDeadCallSite(const IRPosition &IRP) : AAIsDeadFunction(IRP) {}
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
@@ -2463,10 +3154,9 @@ struct AAIsDeadCallSite final : AAIsDeadImpl {
template <>
ChangeStatus clampStateAndIndicateChange<DerefState>(DerefState &S,
const DerefState &R) {
- ChangeStatus CS0 = clampStateAndIndicateChange<IntegerState>(
- S.DerefBytesState, R.DerefBytesState);
- ChangeStatus CS1 =
- clampStateAndIndicateChange<IntegerState>(S.GlobalState, R.GlobalState);
+ ChangeStatus CS0 =
+ clampStateAndIndicateChange(S.DerefBytesState, R.DerefBytesState);
+ ChangeStatus CS1 = clampStateAndIndicateChange(S.GlobalState, R.GlobalState);
return CS0 | CS1;
}
@@ -2496,16 +3186,49 @@ struct AADereferenceableImpl : AADereferenceable {
const StateType &getState() const override { return *this; }
/// }
+ /// Helper function for collecting accessed bytes in must-be-executed-context
+ void addAccessedBytesForUse(Attributor &A, const Use *U,
+ const Instruction *I) {
+ const Value *UseV = U->get();
+ if (!UseV->getType()->isPointerTy())
+ return;
+
+ Type *PtrTy = UseV->getType();
+ const DataLayout &DL = A.getDataLayout();
+ int64_t Offset;
+ if (const Value *Base = getBasePointerOfAccessPointerOperand(
+ I, Offset, DL, /*AllowNonInbounds*/ true)) {
+ if (Base == &getAssociatedValue() &&
+ Attributor::getPointerOperand(I, /* AllowVolatile */ false) == UseV) {
+ uint64_t Size = DL.getTypeStoreSize(PtrTy->getPointerElementType());
+ addAccessedBytes(Offset, Size);
+ }
+ }
+ return;
+ }
+
/// See AAFromMustBeExecutedContext
bool followUse(Attributor &A, const Use *U, const Instruction *I) {
bool IsNonNull = false;
bool TrackUse = false;
int64_t DerefBytes = getKnownNonNullAndDerefBytesForUse(
A, *this, getAssociatedValue(), U, I, IsNonNull, TrackUse);
+
+ addAccessedBytesForUse(A, U, I);
takeKnownDerefBytesMaximum(DerefBytes);
return TrackUse;
}
+ /// See AbstractAttribute::manifest(...).
+ ChangeStatus manifest(Attributor &A) override {
+ ChangeStatus Change = AADereferenceable::manifest(A);
+ if (isAssumedNonNull() && hasAttr(Attribute::DereferenceableOrNull)) {
+ removeAttrs({Attribute::DereferenceableOrNull});
+ return ChangeStatus::CHANGED;
+ }
+ return Change;
+ }
+
void getDeducedAttributes(LLVMContext &Ctx,
SmallVectorImpl<Attribute> &Attrs) const override {
// TODO: Add *_globally support
@@ -2564,6 +3287,8 @@ struct AADereferenceableFloating
T.GlobalState &= DS.GlobalState;
}
+ // TODO: Use `AAConstantRange` to infer dereferenceable bytes.
+
// For now we do not try to "increase" dereferenceability due to negative
// indices as we first have to come up with code to deal with loops and
// for overflows of the dereferenceable bytes.
@@ -2654,30 +3379,6 @@ struct AADereferenceableCallSiteReturned final
AADereferenceable, AADereferenceableImpl>;
AADereferenceableCallSiteReturned(const IRPosition &IRP) : Base(IRP) {}
- /// See AbstractAttribute::initialize(...).
- void initialize(Attributor &A) override {
- Base::initialize(A);
- Function *F = getAssociatedFunction();
- if (!F)
- indicatePessimisticFixpoint();
- }
-
- /// See AbstractAttribute::updateImpl(...).
- ChangeStatus updateImpl(Attributor &A) override {
- // TODO: Once we have call site specific value information we can provide
- // call site specific liveness information and then it makes
- // sense to specialize attributes for call sites arguments instead of
- // redirecting requests to the callee argument.
-
- ChangeStatus Change = Base::updateImpl(A);
- Function *F = getAssociatedFunction();
- const IRPosition &FnPos = IRPosition::returned(*F);
- auto &FnAA = A.getAAFor<AADereferenceable>(*this, FnPos);
- return Change |
- clampStateAndIndicateChange(
- getState(), static_cast<const DerefState &>(FnAA.getState()));
- }
-
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
STATS_DECLTRACK_CS_ATTR(dereferenceable);
@@ -2686,16 +3387,69 @@ struct AADereferenceableCallSiteReturned final
// ------------------------ Align Argument Attribute ------------------------
+static unsigned int getKnownAlignForUse(Attributor &A,
+ AbstractAttribute &QueryingAA,
+ Value &AssociatedValue, const Use *U,
+ const Instruction *I, bool &TrackUse) {
+ // We need to follow common pointer manipulation uses to the accesses they
+ // feed into.
+ if (isa<CastInst>(I)) {
+ // Follow all but ptr2int casts.
+ TrackUse = !isa<PtrToIntInst>(I);
+ return 0;
+ }
+ if (auto *GEP = dyn_cast<GetElementPtrInst>(I)) {
+ if (GEP->hasAllConstantIndices()) {
+ TrackUse = true;
+ return 0;
+ }
+ }
+
+ unsigned Alignment = 0;
+ if (ImmutableCallSite ICS = ImmutableCallSite(I)) {
+ if (ICS.isBundleOperand(U) || ICS.isCallee(U))
+ return 0;
+
+ unsigned ArgNo = ICS.getArgumentNo(U);
+ IRPosition IRP = IRPosition::callsite_argument(ICS, ArgNo);
+ // As long as we only use known information there is no need to track
+ // dependences here.
+ auto &AlignAA = A.getAAFor<AAAlign>(QueryingAA, IRP,
+ /* TrackDependence */ false);
+ Alignment = AlignAA.getKnownAlign();
+ }
+
+ const Value *UseV = U->get();
+ if (auto *SI = dyn_cast<StoreInst>(I))
+ Alignment = SI->getAlignment();
+ else if (auto *LI = dyn_cast<LoadInst>(I))
+ Alignment = LI->getAlignment();
+
+ if (Alignment <= 1)
+ return 0;
+
+ auto &DL = A.getDataLayout();
+ int64_t Offset;
+
+ if (const Value *Base = GetPointerBaseWithConstantOffset(UseV, Offset, DL)) {
+ if (Base == &AssociatedValue) {
+ // BasePointerAddr + Offset = Alignment * Q for some integer Q.
+ // So we can say that the maximum power of two which is a divisor of
+ // gcd(Offset, Alignment) is an alignment.
+
+ uint32_t gcd =
+ greatestCommonDivisor(uint32_t(abs((int32_t)Offset)), Alignment);
+ Alignment = llvm::PowerOf2Floor(gcd);
+ }
+ }
+
+ return Alignment;
+}
struct AAAlignImpl : AAAlign {
AAAlignImpl(const IRPosition &IRP) : AAAlign(IRP) {}
- // Max alignemnt value allowed in IR
- static const unsigned MAX_ALIGN = 1U << 29;
-
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- takeAssumedMinimum(MAX_ALIGN);
-
SmallVector<Attribute, 4> Attrs;
getAttrs({Attribute::Alignment}, Attrs);
for (const Attribute &Attr : Attrs)
@@ -2718,7 +3472,7 @@ struct AAAlignImpl : AAAlign {
if (SI->getPointerOperand() == &AnchorVal)
if (SI->getAlignment() < getAssumedAlign()) {
STATS_DECLTRACK(AAAlign, Store,
- "Number of times alignemnt added to a store");
+ "Number of times alignment added to a store");
SI->setAlignment(Align(getAssumedAlign()));
Changed = ChangeStatus::CHANGED;
}
@@ -2727,7 +3481,7 @@ struct AAAlignImpl : AAAlign {
if (LI->getAlignment() < getAssumedAlign()) {
LI->setAlignment(Align(getAssumedAlign()));
STATS_DECLTRACK(AAAlign, Load,
- "Number of times alignemnt added to a load");
+ "Number of times alignment added to a load");
Changed = ChangeStatus::CHANGED;
}
}
@@ -2748,6 +3502,16 @@ struct AAAlignImpl : AAAlign {
Attrs.emplace_back(
Attribute::getWithAlignment(Ctx, Align(getAssumedAlign())));
}
+ /// See AAFromMustBeExecutedContext
+ bool followUse(Attributor &A, const Use *U, const Instruction *I) {
+ bool TrackUse = false;
+
+ unsigned int KnownAlign =
+ getKnownAlignForUse(A, *this, getAssociatedValue(), U, I, TrackUse);
+ takeKnownMaximum(KnownAlign);
+
+ return TrackUse;
+ }
/// See AbstractAttribute::getAsStr().
const std::string getAsStr() const override {
@@ -2758,11 +3522,14 @@ struct AAAlignImpl : AAAlign {
};
/// Align attribute for a floating value.
-struct AAAlignFloating : AAAlignImpl {
- AAAlignFloating(const IRPosition &IRP) : AAAlignImpl(IRP) {}
+struct AAAlignFloating : AAFromMustBeExecutedContext<AAAlign, AAAlignImpl> {
+ using Base = AAFromMustBeExecutedContext<AAAlign, AAAlignImpl>;
+ AAAlignFloating(const IRPosition &IRP) : Base(IRP) {}
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
+ Base::updateImpl(A);
+
const DataLayout &DL = A.getDataLayout();
auto VisitValueCB = [&](Value &V, AAAlign::StateType &T,
@@ -2808,9 +3575,12 @@ struct AAAlignReturned final
/// Align attribute for function argument.
struct AAAlignArgument final
- : AAArgumentFromCallSiteArguments<AAAlign, AAAlignImpl> {
+ : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<AAAlign,
+ AAAlignImpl> {
AAAlignArgument(const IRPosition &IRP)
- : AAArgumentFromCallSiteArguments<AAAlign, AAAlignImpl>(IRP) {}
+ : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<AAAlign,
+ AAAlignImpl>(
+ IRP) {}
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(aligned) }
@@ -2824,35 +3594,39 @@ struct AAAlignCallSiteArgument final : AAAlignFloating {
return AAAlignImpl::manifest(A);
}
+ /// See AbstractAttribute::updateImpl(Attributor &A).
+ ChangeStatus updateImpl(Attributor &A) override {
+ ChangeStatus Changed = AAAlignFloating::updateImpl(A);
+ if (Argument *Arg = getAssociatedArgument()) {
+ const auto &ArgAlignAA = A.getAAFor<AAAlign>(
+ *this, IRPosition::argument(*Arg), /* TrackDependence */ false,
+ DepClassTy::OPTIONAL);
+ takeKnownMaximum(ArgAlignAA.getKnownAlign());
+ }
+ return Changed;
+ }
+
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override { STATS_DECLTRACK_CSARG_ATTR(aligned) }
};
/// Align attribute deduction for a call site return value.
-struct AAAlignCallSiteReturned final : AAAlignImpl {
- AAAlignCallSiteReturned(const IRPosition &IRP) : AAAlignImpl(IRP) {}
+struct AAAlignCallSiteReturned final
+ : AACallSiteReturnedFromReturnedAndMustBeExecutedContext<AAAlign,
+ AAAlignImpl> {
+ using Base =
+ AACallSiteReturnedFromReturnedAndMustBeExecutedContext<AAAlign,
+ AAAlignImpl>;
+ AAAlignCallSiteReturned(const IRPosition &IRP) : Base(IRP) {}
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- AAAlignImpl::initialize(A);
+ Base::initialize(A);
Function *F = getAssociatedFunction();
if (!F)
indicatePessimisticFixpoint();
}
- /// See AbstractAttribute::updateImpl(...).
- ChangeStatus updateImpl(Attributor &A) override {
- // TODO: Once we have call site specific value information we can provide
- // call site specific liveness information and then it makes
- // sense to specialize attributes for call sites arguments instead of
- // redirecting requests to the callee argument.
- Function *F = getAssociatedFunction();
- const IRPosition &FnPos = IRPosition::returned(*F);
- auto &FnAA = A.getAAFor<AAAlign>(*this, FnPos);
- return clampStateAndIndicateChange(
- getState(), static_cast<const AAAlign::StateType &>(FnAA.getState()));
- }
-
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(align); }
};
@@ -2865,7 +3639,7 @@ struct AANoReturnImpl : public AANoReturn {
void initialize(Attributor &A) override {
AANoReturn::initialize(A);
Function *F = getAssociatedFunction();
- if (!F || F->hasFnAttribute(Attribute::WillReturn))
+ if (!F)
indicatePessimisticFixpoint();
}
@@ -2876,9 +3650,6 @@ struct AANoReturnImpl : public AANoReturn {
/// See AbstractAttribute::updateImpl(Attributor &A).
virtual ChangeStatus updateImpl(Attributor &A) override {
- const auto &WillReturnAA = A.getAAFor<AAWillReturn>(*this, getIRPosition());
- if (WillReturnAA.isKnownWillReturn())
- return indicatePessimisticFixpoint();
auto CheckForNoReturn = [](Instruction &) { return false; };
if (!A.checkForAllInstructions(CheckForNoReturn, *this,
{(unsigned)Instruction::Ret}))
@@ -2924,7 +3695,16 @@ struct AANoCaptureImpl : public AANoCapture {
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- AANoCapture::initialize(A);
+ if (hasAttr(getAttrKind(), /* IgnoreSubsumingPositions */ true)) {
+ indicateOptimisticFixpoint();
+ return;
+ }
+ Function *AnchorScope = getAnchorScope();
+ if (isFnInterfaceKind() &&
+ (!AnchorScope || !AnchorScope->hasExactDefinition())) {
+ indicatePessimisticFixpoint();
+ return;
+ }
// You cannot "capture" null in the default address space.
if (isa<ConstantPointerNull>(getAssociatedValue()) &&
@@ -2933,13 +3713,11 @@ struct AANoCaptureImpl : public AANoCapture {
return;
}
- const IRPosition &IRP = getIRPosition();
- const Function *F =
- getArgNo() >= 0 ? IRP.getAssociatedFunction() : IRP.getAnchorScope();
+ const Function *F = getArgNo() >= 0 ? getAssociatedFunction() : AnchorScope;
// Check what state the associated function can actually capture.
if (F)
- determineFunctionCaptureCapabilities(IRP, *F, *this);
+ determineFunctionCaptureCapabilities(getIRPosition(), *F, *this);
else
indicatePessimisticFixpoint();
}
@@ -2967,7 +3745,7 @@ struct AANoCaptureImpl : public AANoCapture {
/// state in memory and through "returning/throwing", respectively.
static void determineFunctionCaptureCapabilities(const IRPosition &IRP,
const Function &F,
- IntegerState &State) {
+ BitIntegerState &State) {
// TODO: Once we have memory behavior attributes we should use them here.
// If we know we cannot communicate or write to memory, we do not care about
@@ -2992,7 +3770,7 @@ struct AANoCaptureImpl : public AANoCapture {
// Check existing "returned" attributes.
int ArgNo = IRP.getArgNo();
if (F.doesNotThrow() && ArgNo >= 0) {
- for (unsigned u = 0, e = F.arg_size(); u< e; ++u)
+ for (unsigned u = 0, e = F.arg_size(); u < e; ++u)
if (F.hasParamAttribute(u, Attribute::Returned)) {
if (u == unsigned(ArgNo))
State.removeAssumedBits(NOT_CAPTURED_IN_RET);
@@ -3036,7 +3814,7 @@ struct AACaptureUseTracker final : public CaptureTracker {
/// the search is stopped with \p CapturedInMemory and \p CapturedInInteger
/// conservatively set to true.
AACaptureUseTracker(Attributor &A, AANoCapture &NoCaptureAA,
- const AAIsDead &IsDeadAA, IntegerState &State,
+ const AAIsDead &IsDeadAA, AANoCapture::StateType &State,
SmallVectorImpl<const Value *> &PotentialCopies,
unsigned &RemainingUsesToExplore)
: A(A), NoCaptureAA(NoCaptureAA), IsDeadAA(IsDeadAA), State(State),
@@ -3155,7 +3933,7 @@ private:
const AAIsDead &IsDeadAA;
/// The state currently updated.
- IntegerState &State;
+ AANoCapture::StateType &State;
/// Set of potential copies of the tracked value.
SmallVectorImpl<const Value *> &PotentialCopies;
@@ -3238,9 +4016,11 @@ ChangeStatus AANoCaptureImpl::updateImpl(Attributor &A) {
while (T.isAssumed(NO_CAPTURE_MAYBE_RETURNED) && Idx < PotentialCopies.size())
Tracker.valueMayBeCaptured(PotentialCopies[Idx++]);
- AAAlign::StateType &S = getState();
+ AANoCapture::StateType &S = getState();
auto Assumed = S.getAssumed();
S.intersectAssumedBits(T.getAssumed());
+ if (!isAssumedNoCaptureMaybeReturned())
+ return indicatePessimisticFixpoint();
return Assumed == S.getAssumed() ? ChangeStatus::UNCHANGED
: ChangeStatus::CHANGED;
}
@@ -3257,6 +4037,14 @@ struct AANoCaptureArgument final : AANoCaptureImpl {
struct AANoCaptureCallSiteArgument final : AANoCaptureImpl {
AANoCaptureCallSiteArgument(const IRPosition &IRP) : AANoCaptureImpl(IRP) {}
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ if (Argument *Arg = getAssociatedArgument())
+ if (Arg->hasByValAttr())
+ indicateOptimisticFixpoint();
+ AANoCaptureImpl::initialize(A);
+ }
+
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
// TODO: Once we have call site specific value information we can provide
@@ -3375,6 +4163,28 @@ struct AAValueSimplifyImpl : AAValueSimplify {
return true;
}
+ bool askSimplifiedValueForAAValueConstantRange(Attributor &A) {
+ if (!getAssociatedValue().getType()->isIntegerTy())
+ return false;
+
+ const auto &ValueConstantRangeAA =
+ A.getAAFor<AAValueConstantRange>(*this, getIRPosition());
+
+ Optional<ConstantInt *> COpt =
+ ValueConstantRangeAA.getAssumedConstantInt(A);
+ if (COpt.hasValue()) {
+ if (auto *C = COpt.getValue())
+ SimplifiedAssociatedValue = C;
+ else
+ return false;
+ } else {
+ // FIXME: It should be llvm::None but if you set llvm::None,
+ // values are mistakenly infered as `undef` now.
+ SimplifiedAssociatedValue = &getAssociatedValue();
+ }
+ return true;
+ }
+
/// See AbstractAttribute::manifest(...).
ChangeStatus manifest(Attributor &A) override {
ChangeStatus Changed = ChangeStatus::UNCHANGED;
@@ -3389,7 +4199,7 @@ struct AAValueSimplifyImpl : AAValueSimplify {
if (!V.user_empty() && &V != C && V.getType() == C->getType()) {
LLVM_DEBUG(dbgs() << "[Attributor][ValueSimplify] " << V << " -> " << *C
<< "\n");
- V.replaceAllUsesWith(C);
+ A.changeValueAfterManifest(V, *C);
Changed = ChangeStatus::CHANGED;
}
}
@@ -3397,6 +4207,15 @@ struct AAValueSimplifyImpl : AAValueSimplify {
return Changed | AAValueSimplify::manifest(A);
}
+ /// See AbstractState::indicatePessimisticFixpoint(...).
+ ChangeStatus indicatePessimisticFixpoint() override {
+ // NOTE: Associated value will be returned in a pessimistic fixpoint and is
+ // regarded as known. That's why`indicateOptimisticFixpoint` is called.
+ SimplifiedAssociatedValue = &getAssociatedValue();
+ indicateOptimisticFixpoint();
+ return ChangeStatus::CHANGED;
+ }
+
protected:
// An assumed simplified value. Initially, it is set to Optional::None, which
// means that the value is not clear under current assumption. If in the
@@ -3408,20 +4227,49 @@ protected:
struct AAValueSimplifyArgument final : AAValueSimplifyImpl {
AAValueSimplifyArgument(const IRPosition &IRP) : AAValueSimplifyImpl(IRP) {}
+ void initialize(Attributor &A) override {
+ AAValueSimplifyImpl::initialize(A);
+ if (!getAssociatedFunction() || getAssociatedFunction()->isDeclaration())
+ indicatePessimisticFixpoint();
+ if (hasAttr({Attribute::InAlloca, Attribute::StructRet, Attribute::Nest},
+ /* IgnoreSubsumingPositions */ true))
+ indicatePessimisticFixpoint();
+ }
+
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
+ // Byval is only replacable if it is readonly otherwise we would write into
+ // the replaced value and not the copy that byval creates implicitly.
+ Argument *Arg = getAssociatedArgument();
+ if (Arg->hasByValAttr()) {
+ const auto &MemAA = A.getAAFor<AAMemoryBehavior>(*this, getIRPosition());
+ if (!MemAA.isAssumedReadOnly())
+ return indicatePessimisticFixpoint();
+ }
+
bool HasValueBefore = SimplifiedAssociatedValue.hasValue();
auto PredForCallSite = [&](AbstractCallSite ACS) {
// Check if we have an associated argument or not (which can happen for
// callback calls).
- if (Value *ArgOp = ACS.getCallArgOperand(getArgNo()))
- return checkAndUpdate(A, *this, *ArgOp, SimplifiedAssociatedValue);
- return false;
+ Value *ArgOp = ACS.getCallArgOperand(getArgNo());
+ if (!ArgOp)
+ return false;
+ // We can only propagate thread independent values through callbacks.
+ // This is different to direct/indirect call sites because for them we
+ // know the thread executing the caller and callee is the same. For
+ // callbacks this is not guaranteed, thus a thread dependent value could
+ // be different for the caller and callee, making it invalid to propagate.
+ if (ACS.isCallbackCall())
+ if (auto *C = dyn_cast<Constant>(ArgOp))
+ if (C->isThreadDependent())
+ return false;
+ return checkAndUpdate(A, *this, *ArgOp, SimplifiedAssociatedValue);
};
if (!A.checkForAllCallSites(PredForCallSite, *this, true))
- return indicatePessimisticFixpoint();
+ if (!askSimplifiedValueForAAValueConstantRange(A))
+ return indicatePessimisticFixpoint();
// If a candicate was found in this update, return CHANGED.
return HasValueBefore == SimplifiedAssociatedValue.hasValue()
@@ -3447,7 +4295,8 @@ struct AAValueSimplifyReturned : AAValueSimplifyImpl {
};
if (!A.checkForAllReturnedValues(PredForReturned, *this))
- return indicatePessimisticFixpoint();
+ if (!askSimplifiedValueForAAValueConstantRange(A))
+ return indicatePessimisticFixpoint();
// If a candicate was found in this update, return CHANGED.
return HasValueBefore == SimplifiedAssociatedValue.hasValue()
@@ -3468,7 +4317,7 @@ struct AAValueSimplifyFloating : AAValueSimplifyImpl {
Value &V = getAnchorValue();
// TODO: add other stuffs
- if (isa<Constant>(V) || isa<UndefValue>(V))
+ if (isa<Constant>(V))
indicatePessimisticFixpoint();
}
@@ -3480,10 +4329,10 @@ struct AAValueSimplifyFloating : AAValueSimplifyImpl {
auto &AA = A.getAAFor<AAValueSimplify>(*this, IRPosition::value(V));
if (!Stripped && this == &AA) {
// TODO: Look the instruction and check recursively.
+
LLVM_DEBUG(
dbgs() << "[Attributor][ValueSimplify] Can't be stripped more : "
<< V << "\n");
- indicatePessimisticFixpoint();
return false;
}
return checkAndUpdate(A, *this, V, SimplifiedAssociatedValue);
@@ -3492,7 +4341,8 @@ struct AAValueSimplifyFloating : AAValueSimplifyImpl {
if (!genericValueTraversal<AAValueSimplify, BooleanState>(
A, getIRPosition(), *this, static_cast<BooleanState &>(*this),
VisitValueCB))
- return indicatePessimisticFixpoint();
+ if (!askSimplifiedValueForAAValueConstantRange(A))
+ return indicatePessimisticFixpoint();
// If a candicate was found in this update, return CHANGED.
@@ -3601,7 +4451,7 @@ struct AAHeapToStackImpl : public AAHeapToStack {
AI = new BitCastInst(AI, MallocCall->getType(), "malloc_bc",
AI->getNextNode());
- MallocCall->replaceAllUsesWith(AI);
+ replaceAllInstructionUsesWith(*MallocCall, *AI);
if (auto *II = dyn_cast<InvokeInst>(MallocCall)) {
auto *NBB = II->getNormalDest();
@@ -3645,76 +4495,80 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) {
const Function *F = getAssociatedFunction();
const auto *TLI = A.getInfoCache().getTargetLibraryInfoForFunction(*F);
- auto UsesCheck = [&](Instruction &I) {
- SmallPtrSet<const Use *, 8> Visited;
- SmallVector<const Use *, 8> Worklist;
-
- for (Use &U : I.uses())
- Worklist.push_back(&U);
+ MustBeExecutedContextExplorer &Explorer =
+ A.getInfoCache().getMustBeExecutedContextExplorer();
- while (!Worklist.empty()) {
- const Use *U = Worklist.pop_back_val();
- if (!Visited.insert(U).second)
- continue;
-
- auto *UserI = U->getUser();
+ auto FreeCheck = [&](Instruction &I) {
+ const auto &Frees = FreesForMalloc.lookup(&I);
+ if (Frees.size() != 1)
+ return false;
+ Instruction *UniqueFree = *Frees.begin();
+ return Explorer.findInContextOf(UniqueFree, I.getNextNode());
+ };
+ auto UsesCheck = [&](Instruction &I) {
+ bool ValidUsesOnly = true;
+ bool MustUse = true;
+ auto Pred = [&](const Use &U, bool &Follow) -> bool {
+ Instruction *UserI = cast<Instruction>(U.getUser());
if (isa<LoadInst>(UserI))
- continue;
+ return true;
if (auto *SI = dyn_cast<StoreInst>(UserI)) {
- if (SI->getValueOperand() == U->get()) {
- LLVM_DEBUG(dbgs() << "[H2S] escaping store to memory: " << *UserI << "\n");
- return false;
+ if (SI->getValueOperand() == U.get()) {
+ LLVM_DEBUG(dbgs()
+ << "[H2S] escaping store to memory: " << *UserI << "\n");
+ ValidUsesOnly = false;
+ } else {
+ // A store into the malloc'ed memory is fine.
}
- // A store into the malloc'ed memory is fine.
- continue;
+ return true;
}
-
- // NOTE: Right now, if a function that has malloc pointer as an argument
- // frees memory, we assume that the malloc pointer is freed.
-
- // TODO: Add nofree callsite argument attribute to indicate that pointer
- // argument is not freed.
if (auto *CB = dyn_cast<CallBase>(UserI)) {
- if (!CB->isArgOperand(U))
- continue;
-
- if (CB->isLifetimeStartOrEnd())
- continue;
-
+ if (!CB->isArgOperand(&U) || CB->isLifetimeStartOrEnd())
+ return true;
// Record malloc.
if (isFreeCall(UserI, TLI)) {
- FreesForMalloc[&I].insert(
- cast<Instruction>(const_cast<User *>(UserI)));
- continue;
+ if (MustUse) {
+ FreesForMalloc[&I].insert(UserI);
+ } else {
+ LLVM_DEBUG(dbgs() << "[H2S] free potentially on different mallocs: "
+ << *UserI << "\n");
+ ValidUsesOnly = false;
+ }
+ return true;
}
- // If a function does not free memory we are fine
- const auto &NoFreeAA =
- A.getAAFor<AANoFree>(*this, IRPosition::callsite_function(*CB));
+ unsigned ArgNo = CB->getArgOperandNo(&U);
- unsigned ArgNo = U - CB->arg_begin();
const auto &NoCaptureAA = A.getAAFor<AANoCapture>(
*this, IRPosition::callsite_argument(*CB, ArgNo));
- if (!NoCaptureAA.isAssumedNoCapture() || !NoFreeAA.isAssumedNoFree()) {
+ // If a callsite argument use is nofree, we are fine.
+ const auto &ArgNoFreeAA = A.getAAFor<AANoFree>(
+ *this, IRPosition::callsite_argument(*CB, ArgNo));
+
+ if (!NoCaptureAA.isAssumedNoCapture() ||
+ !ArgNoFreeAA.isAssumedNoFree()) {
LLVM_DEBUG(dbgs() << "[H2S] Bad user: " << *UserI << "\n");
- return false;
+ ValidUsesOnly = false;
}
- continue;
+ return true;
}
- if (isa<GetElementPtrInst>(UserI) || isa<BitCastInst>(UserI)) {
- for (Use &U : UserI->uses())
- Worklist.push_back(&U);
- continue;
+ if (isa<GetElementPtrInst>(UserI) || isa<BitCastInst>(UserI) ||
+ isa<PHINode>(UserI) || isa<SelectInst>(UserI)) {
+ MustUse &= !(isa<PHINode>(UserI) || isa<SelectInst>(UserI));
+ Follow = true;
+ return true;
}
-
- // Unknown user.
+ // Unknown user for which we can not track uses further (in a way that
+ // makes sense).
LLVM_DEBUG(dbgs() << "[H2S] Unknown user: " << *UserI << "\n");
- return false;
- }
- return true;
+ ValidUsesOnly = false;
+ return true;
+ };
+ A.checkForAllUses(Pred, *this, I);
+ return ValidUsesOnly;
};
auto MallocCallocCheck = [&](Instruction &I) {
@@ -3730,8 +4584,8 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) {
if (IsMalloc) {
if (auto *Size = dyn_cast<ConstantInt>(I.getOperand(0)))
- if (Size->getValue().sle(MaxHeapToStackSize))
- if (UsesCheck(I)) {
+ if (Size->getValue().ule(MaxHeapToStackSize))
+ if (UsesCheck(I) || FreeCheck(I)) {
MallocCalls.insert(&I);
return true;
}
@@ -3740,8 +4594,8 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) {
if (auto *Num = dyn_cast<ConstantInt>(I.getOperand(0)))
if (auto *Size = dyn_cast<ConstantInt>(I.getOperand(1)))
if ((Size->getValue().umul_ov(Num->getValue(), Overflow))
- .sle(MaxHeapToStackSize))
- if (!Overflow && UsesCheck(I)) {
+ .ule(MaxHeapToStackSize))
+ if (!Overflow && (UsesCheck(I) || FreeCheck(I))) {
MallocCalls.insert(&I);
return true;
}
@@ -3767,8 +4621,10 @@ struct AAHeapToStackFunction final : public AAHeapToStackImpl {
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
STATS_DECL(MallocCalls, Function,
- "Number of MallocCalls converted to allocas");
- BUILD_STAT_NAME(MallocCalls, Function) += MallocCalls.size();
+ "Number of malloc calls converted to allocas");
+ for (auto *C : MallocCalls)
+ if (!BadMallocCalls.count(C))
+ ++BUILD_STAT_NAME(MallocCalls, Function);
}
};
@@ -3787,9 +4643,10 @@ struct AAMemoryBehaviorImpl : public AAMemoryBehavior {
/// Return the memory behavior information encoded in the IR for \p IRP.
static void getKnownStateFromValue(const IRPosition &IRP,
- IntegerState &State) {
+ BitIntegerState &State,
+ bool IgnoreSubsumingPositions = false) {
SmallVector<Attribute, 2> Attrs;
- IRP.getAttrs(AttrKinds, Attrs);
+ IRP.getAttrs(AttrKinds, Attrs, IgnoreSubsumingPositions);
for (const Attribute &Attr : Attrs) {
switch (Attr.getKindAsEnum()) {
case Attribute::ReadNone:
@@ -3829,7 +4686,7 @@ struct AAMemoryBehaviorImpl : public AAMemoryBehavior {
/// See AbstractAttribute::manifest(...).
ChangeStatus manifest(Attributor &A) override {
- IRPosition &IRP = getIRPosition();
+ const IRPosition &IRP = getIRPosition();
// Check if we would improve the existing attributes first.
SmallVector<Attribute, 4> DeducedAttrs;
@@ -3911,12 +4768,25 @@ struct AAMemoryBehaviorArgument : AAMemoryBehaviorFloating {
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- AAMemoryBehaviorFloating::initialize(A);
+ intersectAssumedBits(BEST_STATE);
+ const IRPosition &IRP = getIRPosition();
+ // TODO: Make IgnoreSubsumingPositions a property of an IRAttribute so we
+ // can query it when we use has/getAttr. That would allow us to reuse the
+ // initialize of the base class here.
+ bool HasByVal =
+ IRP.hasAttr({Attribute::ByVal}, /* IgnoreSubsumingPositions */ true);
+ getKnownStateFromValue(IRP, getState(),
+ /* IgnoreSubsumingPositions */ HasByVal);
// Initialize the use vector with all direct uses of the associated value.
Argument *Arg = getAssociatedArgument();
- if (!Arg || !Arg->getParent()->hasExactDefinition())
+ if (!Arg || !Arg->getParent()->hasExactDefinition()) {
indicatePessimisticFixpoint();
+ } else {
+ // Initialize the use vector with all direct uses of the associated value.
+ for (const Use &U : Arg->uses())
+ Uses.insert(&U);
+ }
}
ChangeStatus manifest(Attributor &A) override {
@@ -3929,7 +4799,6 @@ struct AAMemoryBehaviorArgument : AAMemoryBehaviorFloating {
return AAMemoryBehaviorFloating::manifest(A);
}
-
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
if (isAssumedReadNone())
@@ -3945,6 +4814,19 @@ struct AAMemoryBehaviorCallSiteArgument final : AAMemoryBehaviorArgument {
AAMemoryBehaviorCallSiteArgument(const IRPosition &IRP)
: AAMemoryBehaviorArgument(IRP) {}
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ if (Argument *Arg = getAssociatedArgument()) {
+ if (Arg->hasByValAttr()) {
+ addKnownBits(NO_WRITES);
+ removeKnownBits(NO_READS);
+ removeAssumedBits(NO_READS);
+ }
+ } else {
+ }
+ AAMemoryBehaviorArgument::initialize(A);
+ }
+
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
// TODO: Once we have call site specific value information we can provide
@@ -3956,7 +4838,7 @@ struct AAMemoryBehaviorCallSiteArgument final : AAMemoryBehaviorArgument {
auto &ArgAA = A.getAAFor<AAMemoryBehavior>(*this, ArgPos);
return clampStateAndIndicateChange(
getState(),
- static_cast<const AANoCapture::StateType &>(ArgAA.getState()));
+ static_cast<const AAMemoryBehavior::StateType &>(ArgAA.getState()));
}
/// See AbstractAttribute::trackStatistics()
@@ -4036,7 +4918,8 @@ struct AAMemoryBehaviorCallSite final : AAMemoryBehaviorImpl {
const IRPosition &FnPos = IRPosition::function(*F);
auto &FnAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
return clampStateAndIndicateChange(
- getState(), static_cast<const AAAlign::StateType &>(FnAA.getState()));
+ getState(),
+ static_cast<const AAMemoryBehavior::StateType &>(FnAA.getState()));
}
/// See AbstractAttribute::trackStatistics()
@@ -4090,19 +4973,26 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) {
// First, check the function scope. We take the known information and we avoid
// work if the assumed information implies the current assumed information for
- // this attribute.
- const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
- S.addKnownBits(FnMemAA.getKnown());
- if ((S.getAssumed() & FnMemAA.getAssumed()) == S.getAssumed())
- return ChangeStatus::UNCHANGED;
+ // this attribute. This is a valid for all but byval arguments.
+ Argument *Arg = IRP.getAssociatedArgument();
+ AAMemoryBehavior::base_t FnMemAssumedState =
+ AAMemoryBehavior::StateType::getWorstState();
+ if (!Arg || !Arg->hasByValAttr()) {
+ const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
+ FnMemAssumedState = FnMemAA.getAssumed();
+ S.addKnownBits(FnMemAA.getKnown());
+ if ((S.getAssumed() & FnMemAA.getAssumed()) == S.getAssumed())
+ return ChangeStatus::UNCHANGED;
+ }
// Make sure the value is not captured (except through "return"), if
// it is, any information derived would be irrelevant anyway as we cannot
// check the potential aliases introduced by the capture. However, no need
// to fall back to anythign less optimistic than the function state.
- const auto &ArgNoCaptureAA = A.getAAFor<AANoCapture>(*this, IRP);
+ const auto &ArgNoCaptureAA = A.getAAFor<AANoCapture>(
+ *this, IRP, /* TrackDependence */ true, DepClassTy::OPTIONAL);
if (!ArgNoCaptureAA.isAssumedNoCaptureMaybeReturned()) {
- S.intersectAssumedBits(FnMemAA.getAssumed());
+ S.intersectAssumedBits(FnMemAssumedState);
return ChangeStatus::CHANGED;
}
@@ -4223,7 +5113,451 @@ void AAMemoryBehaviorFloating::analyzeUseIn(Attributor &A, const Use *U,
if (UserI->mayWriteToMemory())
removeAssumedBits(NO_WRITES);
}
+/// ------------------ Value Constant Range Attribute -------------------------
+
+struct AAValueConstantRangeImpl : AAValueConstantRange {
+ using StateType = IntegerRangeState;
+ AAValueConstantRangeImpl(const IRPosition &IRP) : AAValueConstantRange(IRP) {}
+
+ /// See AbstractAttribute::getAsStr().
+ const std::string getAsStr() const override {
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ OS << "range(" << getBitWidth() << ")<";
+ getKnown().print(OS);
+ OS << " / ";
+ getAssumed().print(OS);
+ OS << ">";
+ return OS.str();
+ }
+
+ /// Helper function to get a SCEV expr for the associated value at program
+ /// point \p I.
+ const SCEV *getSCEV(Attributor &A, const Instruction *I = nullptr) const {
+ if (!getAnchorScope())
+ return nullptr;
+
+ ScalarEvolution *SE =
+ A.getInfoCache().getAnalysisResultForFunction<ScalarEvolutionAnalysis>(
+ *getAnchorScope());
+
+ LoopInfo *LI = A.getInfoCache().getAnalysisResultForFunction<LoopAnalysis>(
+ *getAnchorScope());
+
+ if (!SE || !LI)
+ return nullptr;
+
+ const SCEV *S = SE->getSCEV(&getAssociatedValue());
+ if (!I)
+ return S;
+
+ return SE->getSCEVAtScope(S, LI->getLoopFor(I->getParent()));
+ }
+
+ /// Helper function to get a range from SCEV for the associated value at
+ /// program point \p I.
+ ConstantRange getConstantRangeFromSCEV(Attributor &A,
+ const Instruction *I = nullptr) const {
+ if (!getAnchorScope())
+ return getWorstState(getBitWidth());
+
+ ScalarEvolution *SE =
+ A.getInfoCache().getAnalysisResultForFunction<ScalarEvolutionAnalysis>(
+ *getAnchorScope());
+
+ const SCEV *S = getSCEV(A, I);
+ if (!SE || !S)
+ return getWorstState(getBitWidth());
+
+ return SE->getUnsignedRange(S);
+ }
+
+ /// Helper function to get a range from LVI for the associated value at
+ /// program point \p I.
+ ConstantRange
+ getConstantRangeFromLVI(Attributor &A,
+ const Instruction *CtxI = nullptr) const {
+ if (!getAnchorScope())
+ return getWorstState(getBitWidth());
+
+ LazyValueInfo *LVI =
+ A.getInfoCache().getAnalysisResultForFunction<LazyValueAnalysis>(
+ *getAnchorScope());
+
+ if (!LVI || !CtxI)
+ return getWorstState(getBitWidth());
+ return LVI->getConstantRange(&getAssociatedValue(),
+ const_cast<BasicBlock *>(CtxI->getParent()),
+ const_cast<Instruction *>(CtxI));
+ }
+
+ /// See AAValueConstantRange::getKnownConstantRange(..).
+ ConstantRange
+ getKnownConstantRange(Attributor &A,
+ const Instruction *CtxI = nullptr) const override {
+ if (!CtxI || CtxI == getCtxI())
+ return getKnown();
+
+ ConstantRange LVIR = getConstantRangeFromLVI(A, CtxI);
+ ConstantRange SCEVR = getConstantRangeFromSCEV(A, CtxI);
+ return getKnown().intersectWith(SCEVR).intersectWith(LVIR);
+ }
+
+ /// See AAValueConstantRange::getAssumedConstantRange(..).
+ ConstantRange
+ getAssumedConstantRange(Attributor &A,
+ const Instruction *CtxI = nullptr) const override {
+ // TODO: Make SCEV use Attributor assumption.
+ // We may be able to bound a variable range via assumptions in
+ // Attributor. ex.) If x is assumed to be in [1, 3] and y is known to
+ // evolve to x^2 + x, then we can say that y is in [2, 12].
+
+ if (!CtxI || CtxI == getCtxI())
+ return getAssumed();
+
+ ConstantRange LVIR = getConstantRangeFromLVI(A, CtxI);
+ ConstantRange SCEVR = getConstantRangeFromSCEV(A, CtxI);
+ return getAssumed().intersectWith(SCEVR).intersectWith(LVIR);
+ }
+
+ /// See AbstractAttribute::initialize(..).
+ void initialize(Attributor &A) override {
+ // Intersect a range given by SCEV.
+ intersectKnown(getConstantRangeFromSCEV(A, getCtxI()));
+
+ // Intersect a range given by LVI.
+ intersectKnown(getConstantRangeFromLVI(A, getCtxI()));
+ }
+
+ /// Helper function to create MDNode for range metadata.
+ static MDNode *
+ getMDNodeForConstantRange(Type *Ty, LLVMContext &Ctx,
+ const ConstantRange &AssumedConstantRange) {
+ Metadata *LowAndHigh[] = {ConstantAsMetadata::get(ConstantInt::get(
+ Ty, AssumedConstantRange.getLower())),
+ ConstantAsMetadata::get(ConstantInt::get(
+ Ty, AssumedConstantRange.getUpper()))};
+ return MDNode::get(Ctx, LowAndHigh);
+ }
+
+ /// Return true if \p Assumed is included in \p KnownRanges.
+ static bool isBetterRange(const ConstantRange &Assumed, MDNode *KnownRanges) {
+
+ if (Assumed.isFullSet())
+ return false;
+
+ if (!KnownRanges)
+ return true;
+
+ // If multiple ranges are annotated in IR, we give up to annotate assumed
+ // range for now.
+
+ // TODO: If there exists a known range which containts assumed range, we
+ // can say assumed range is better.
+ if (KnownRanges->getNumOperands() > 2)
+ return false;
+
+ ConstantInt *Lower =
+ mdconst::extract<ConstantInt>(KnownRanges->getOperand(0));
+ ConstantInt *Upper =
+ mdconst::extract<ConstantInt>(KnownRanges->getOperand(1));
+
+ ConstantRange Known(Lower->getValue(), Upper->getValue());
+ return Known.contains(Assumed) && Known != Assumed;
+ }
+
+ /// Helper function to set range metadata.
+ static bool
+ setRangeMetadataIfisBetterRange(Instruction *I,
+ const ConstantRange &AssumedConstantRange) {
+ auto *OldRangeMD = I->getMetadata(LLVMContext::MD_range);
+ if (isBetterRange(AssumedConstantRange, OldRangeMD)) {
+ if (!AssumedConstantRange.isEmptySet()) {
+ I->setMetadata(LLVMContext::MD_range,
+ getMDNodeForConstantRange(I->getType(), I->getContext(),
+ AssumedConstantRange));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// See AbstractAttribute::manifest()
+ ChangeStatus manifest(Attributor &A) override {
+ ChangeStatus Changed = ChangeStatus::UNCHANGED;
+ ConstantRange AssumedConstantRange = getAssumedConstantRange(A);
+ assert(!AssumedConstantRange.isFullSet() && "Invalid state");
+
+ auto &V = getAssociatedValue();
+ if (!AssumedConstantRange.isEmptySet() &&
+ !AssumedConstantRange.isSingleElement()) {
+ if (Instruction *I = dyn_cast<Instruction>(&V))
+ if (isa<CallInst>(I) || isa<LoadInst>(I))
+ if (setRangeMetadataIfisBetterRange(I, AssumedConstantRange))
+ Changed = ChangeStatus::CHANGED;
+ }
+
+ return Changed;
+ }
+};
+
+struct AAValueConstantRangeArgument final : public AAValueConstantRangeImpl {
+
+ AAValueConstantRangeArgument(const IRPosition &IRP)
+ : AAValueConstantRangeImpl(IRP) {}
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ // TODO: Use AAArgumentFromCallSiteArguments
+
+ IntegerRangeState S(getBitWidth());
+ clampCallSiteArgumentStates<AAValueConstantRange, IntegerRangeState>(
+ A, *this, S);
+
+ // TODO: If we know we visited all incoming values, thus no are assumed
+ // dead, we can take the known information from the state T.
+ return clampStateAndIndicateChange<IntegerRangeState>(this->getState(), S);
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECLTRACK_ARG_ATTR(value_range)
+ }
+};
+
+struct AAValueConstantRangeReturned : AAValueConstantRangeImpl {
+ AAValueConstantRangeReturned(const IRPosition &IRP)
+ : AAValueConstantRangeImpl(IRP) {}
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ // TODO: Use AAReturnedFromReturnedValues
+
+ // TODO: If we know we visited all returned values, thus no are assumed
+ // dead, we can take the known information from the state T.
+
+ IntegerRangeState S(getBitWidth());
+
+ clampReturnedValueStates<AAValueConstantRange, IntegerRangeState>(A, *this,
+ S);
+ return clampStateAndIndicateChange<StateType>(this->getState(), S);
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECLTRACK_FNRET_ATTR(value_range)
+ }
+};
+
+struct AAValueConstantRangeFloating : AAValueConstantRangeImpl {
+ AAValueConstantRangeFloating(const IRPosition &IRP)
+ : AAValueConstantRangeImpl(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ AAValueConstantRange::initialize(A);
+ Value &V = getAssociatedValue();
+
+ if (auto *C = dyn_cast<ConstantInt>(&V)) {
+ unionAssumed(ConstantRange(C->getValue()));
+ indicateOptimisticFixpoint();
+ return;
+ }
+
+ if (isa<UndefValue>(&V)) {
+ indicateOptimisticFixpoint();
+ return;
+ }
+
+ if (auto *I = dyn_cast<Instruction>(&V))
+ if (isa<BinaryOperator>(I) || isa<CmpInst>(I)) {
+ Value *LHS = I->getOperand(0);
+ Value *RHS = I->getOperand(1);
+
+ if (LHS->getType()->isIntegerTy() && RHS->getType()->isIntegerTy())
+ return;
+ }
+
+ // If it is a load instruction with range metadata, use it.
+ if (LoadInst *LI = dyn_cast<LoadInst>(&V))
+ if (auto *RangeMD = LI->getMetadata(LLVMContext::MD_range)) {
+ intersectKnown(getConstantRangeFromMetadata(*RangeMD));
+ return;
+ }
+
+ // Otherwise we give up.
+ indicatePessimisticFixpoint();
+
+ LLVM_DEBUG(dbgs() << "[Attributor][AAValueConstantRange] We give up: "
+ << getAssociatedValue());
+ }
+
+ bool calculateBinaryOperator(Attributor &A, BinaryOperator *BinOp,
+ IntegerRangeState &T, Instruction *CtxI) {
+ Value *LHS = BinOp->getOperand(0);
+ Value *RHS = BinOp->getOperand(1);
+
+ auto &LHSAA =
+ A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*LHS));
+ auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI);
+
+ auto &RHSAA =
+ A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*RHS));
+ auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI);
+
+ auto AssumedRange = LHSAARange.binaryOp(BinOp->getOpcode(), RHSAARange);
+
+ T.unionAssumed(AssumedRange);
+
+ // TODO: Track a known state too.
+
+ return T.isValidState();
+ }
+
+ bool calculateCmpInst(Attributor &A, CmpInst *CmpI, IntegerRangeState &T,
+ Instruction *CtxI) {
+ Value *LHS = CmpI->getOperand(0);
+ Value *RHS = CmpI->getOperand(1);
+
+ auto &LHSAA =
+ A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*LHS));
+ auto &RHSAA =
+ A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*RHS));
+
+ auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI);
+ auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI);
+
+ // If one of them is empty set, we can't decide.
+ if (LHSAARange.isEmptySet() || RHSAARange.isEmptySet())
+ return true;
+
+ bool MustTrue = false, MustFalse = false;
+
+ auto AllowedRegion =
+ ConstantRange::makeAllowedICmpRegion(CmpI->getPredicate(), RHSAARange);
+
+ auto SatisfyingRegion = ConstantRange::makeSatisfyingICmpRegion(
+ CmpI->getPredicate(), RHSAARange);
+
+ if (AllowedRegion.intersectWith(LHSAARange).isEmptySet())
+ MustFalse = true;
+
+ if (SatisfyingRegion.contains(LHSAARange))
+ MustTrue = true;
+
+ assert((!MustTrue || !MustFalse) &&
+ "Either MustTrue or MustFalse should be false!");
+
+ if (MustTrue)
+ T.unionAssumed(ConstantRange(APInt(/* numBits */ 1, /* val */ 1)));
+ else if (MustFalse)
+ T.unionAssumed(ConstantRange(APInt(/* numBits */ 1, /* val */ 0)));
+ else
+ T.unionAssumed(ConstantRange(/* BitWidth */ 1, /* isFullSet */ true));
+
+ LLVM_DEBUG(dbgs() << "[AAValueConstantRange] " << *CmpI << " " << LHSAA
+ << " " << RHSAA << "\n");
+
+ // TODO: Track a known state too.
+ return T.isValidState();
+ }
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ Instruction *CtxI = getCtxI();
+ auto VisitValueCB = [&](Value &V, IntegerRangeState &T,
+ bool Stripped) -> bool {
+ Instruction *I = dyn_cast<Instruction>(&V);
+ if (!I) {
+
+ // If the value is not instruction, we query AA to Attributor.
+ const auto &AA =
+ A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(V));
+
+ // Clamp operator is not used to utilize a program point CtxI.
+ T.unionAssumed(AA.getAssumedConstantRange(A, CtxI));
+
+ return T.isValidState();
+ }
+
+ if (auto *BinOp = dyn_cast<BinaryOperator>(I))
+ return calculateBinaryOperator(A, BinOp, T, CtxI);
+ else if (auto *CmpI = dyn_cast<CmpInst>(I))
+ return calculateCmpInst(A, CmpI, T, CtxI);
+ else {
+ // Give up with other instructions.
+ // TODO: Add other instructions
+ T.indicatePessimisticFixpoint();
+ return false;
+ }
+ };
+
+ IntegerRangeState T(getBitWidth());
+
+ if (!genericValueTraversal<AAValueConstantRange, IntegerRangeState>(
+ A, getIRPosition(), *this, T, VisitValueCB))
+ return indicatePessimisticFixpoint();
+
+ return clampStateAndIndicateChange(getState(), T);
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECLTRACK_FLOATING_ATTR(value_range)
+ }
+};
+
+struct AAValueConstantRangeFunction : AAValueConstantRangeImpl {
+ AAValueConstantRangeFunction(const IRPosition &IRP)
+ : AAValueConstantRangeImpl(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ llvm_unreachable("AAValueConstantRange(Function|CallSite)::updateImpl will "
+ "not be called");
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_FN_ATTR(value_range) }
+};
+
+struct AAValueConstantRangeCallSite : AAValueConstantRangeFunction {
+ AAValueConstantRangeCallSite(const IRPosition &IRP)
+ : AAValueConstantRangeFunction(IRP) {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(value_range) }
+};
+
+struct AAValueConstantRangeCallSiteReturned : AAValueConstantRangeReturned {
+ AAValueConstantRangeCallSiteReturned(const IRPosition &IRP)
+ : AAValueConstantRangeReturned(IRP) {}
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ // If it is a load instruction with range metadata, use the metadata.
+ if (CallInst *CI = dyn_cast<CallInst>(&getAssociatedValue()))
+ if (auto *RangeMD = CI->getMetadata(LLVMContext::MD_range))
+ intersectKnown(getConstantRangeFromMetadata(*RangeMD));
+
+ AAValueConstantRangeReturned::initialize(A);
+ }
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECLTRACK_CSRET_ATTR(value_range)
+ }
+};
+struct AAValueConstantRangeCallSiteArgument : AAValueConstantRangeFloating {
+ AAValueConstantRangeCallSiteArgument(const IRPosition &IRP)
+ : AAValueConstantRangeFloating(IRP) {}
+
+ /// See AbstractAttribute::trackStatistics()
+ void trackStatistics() const override {
+ STATS_DECLTRACK_CSARG_ATTR(value_range)
+ }
+};
/// ----------------------------------------------------------------------------
/// Attributor
/// ----------------------------------------------------------------------------
@@ -4234,6 +5568,8 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA,
if (!CtxI)
return false;
+ // TODO: Find a good way to utilize fine and coarse grained liveness
+ // information.
if (!LivenessAA)
LivenessAA =
&getAAFor<AAIsDead>(AA, IRPosition::function(*CtxI->getFunction()),
@@ -4247,7 +5583,58 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA,
return false;
// We actually used liveness information so we have to record a dependence.
- recordDependence(*LivenessAA, AA);
+ recordDependence(*LivenessAA, AA, DepClassTy::OPTIONAL);
+
+ return true;
+}
+
+bool Attributor::checkForAllUses(
+ const function_ref<bool(const Use &, bool &)> &Pred,
+ const AbstractAttribute &QueryingAA, const Value &V) {
+ const IRPosition &IRP = QueryingAA.getIRPosition();
+ SmallVector<const Use *, 16> Worklist;
+ SmallPtrSet<const Use *, 16> Visited;
+
+ for (const Use &U : V.uses())
+ Worklist.push_back(&U);
+
+ LLVM_DEBUG(dbgs() << "[Attributor] Got " << Worklist.size()
+ << " initial uses to check\n");
+
+ if (Worklist.empty())
+ return true;
+
+ bool AnyDead = false;
+ const Function *ScopeFn = IRP.getAnchorScope();
+ const auto *LivenessAA =
+ ScopeFn ? &getAAFor<AAIsDead>(QueryingAA, IRPosition::function(*ScopeFn),
+ /* TrackDependence */ false)
+ : nullptr;
+
+ while (!Worklist.empty()) {
+ const Use *U = Worklist.pop_back_val();
+ if (!Visited.insert(U).second)
+ continue;
+ LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << "\n");
+ if (Instruction *UserI = dyn_cast<Instruction>(U->getUser()))
+ if (LivenessAA && LivenessAA->isAssumedDead(UserI)) {
+ LLVM_DEBUG(dbgs() << "[Attributor] Dead user: " << *UserI << ": "
+ << *LivenessAA << "\n");
+ AnyDead = true;
+ continue;
+ }
+
+ bool Follow = false;
+ if (!Pred(*U, Follow))
+ return false;
+ if (!Follow)
+ continue;
+ for (const Use &UU : U->getUser()->uses())
+ Worklist.push_back(&UU);
+ }
+
+ if (AnyDead)
+ recordDependence(*LivenessAA, QueryingAA, DepClassTy::OPTIONAL);
return true;
}
@@ -4284,10 +5671,12 @@ bool Attributor::checkForAllCallSites(
for (const Use &U : Fn.uses()) {
AbstractCallSite ACS(&U);
if (!ACS) {
- LLVM_DEBUG(dbgs() << "[Attributor] Function "
- << Fn.getName()
+ LLVM_DEBUG(dbgs() << "[Attributor] Function " << Fn.getName()
<< " has non call site use " << *U.get() << " in "
<< *U.getUser() << "\n");
+ // BlockAddress users are allowed.
+ if (isa<BlockAddress>(U.getUser()))
+ continue;
return false;
}
@@ -4296,14 +5685,14 @@ bool Attributor::checkForAllCallSites(
const auto *LivenessAA =
lookupAAFor<AAIsDead>(IRPosition::function(*Caller), QueryingAA,
- /* TrackDependence */ false);
+ /* TrackDependence */ false);
// Skip dead calls.
if (LivenessAA && LivenessAA->isAssumedDead(I)) {
// We actually used liveness information so we have to record a
// dependence.
if (QueryingAA)
- recordDependence(*LivenessAA, *QueryingAA);
+ recordDependence(*LivenessAA, *QueryingAA, DepClassTy::OPTIONAL);
continue;
}
@@ -4313,8 +5702,7 @@ bool Attributor::checkForAllCallSites(
if (!RequireAllCallSites)
continue;
LLVM_DEBUG(dbgs() << "[Attributor] User " << EffectiveUse->getUser()
- << " is an invalid use of "
- << Fn.getName() << "\n");
+ << " is an invalid use of " << Fn.getName() << "\n");
return false;
}
@@ -4417,7 +5805,7 @@ bool Attributor::checkForAllInstructions(
// If we actually used liveness information so we have to record a dependence.
if (AnyDead)
- recordDependence(LivenessAA, QueryingAA);
+ recordDependence(LivenessAA, QueryingAA, DepClassTy::OPTIONAL);
return true;
}
@@ -4451,7 +5839,7 @@ bool Attributor::checkForAllReadWriteInstructions(
// If we actually used liveness information so we have to record a dependence.
if (AnyDead)
- recordDependence(LivenessAA, QueryingAA);
+ recordDependence(LivenessAA, QueryingAA, DepClassTy::OPTIONAL);
return true;
}
@@ -4467,7 +5855,7 @@ ChangeStatus Attributor::run(Module &M) {
unsigned IterationCounter = 1;
SmallVector<AbstractAttribute *, 64> ChangedAAs;
- SetVector<AbstractAttribute *> Worklist;
+ SetVector<AbstractAttribute *> Worklist, InvalidAAs;
Worklist.insert(AllAbstractAttributes.begin(), AllAbstractAttributes.end());
bool RecomputeDependences = false;
@@ -4478,6 +5866,29 @@ ChangeStatus Attributor::run(Module &M) {
LLVM_DEBUG(dbgs() << "\n\n[Attributor] #Iteration: " << IterationCounter
<< ", Worklist size: " << Worklist.size() << "\n");
+ // For invalid AAs we can fix dependent AAs that have a required dependence,
+ // thereby folding long dependence chains in a single step without the need
+ // to run updates.
+ for (unsigned u = 0; u < InvalidAAs.size(); ++u) {
+ AbstractAttribute *InvalidAA = InvalidAAs[u];
+ auto &QuerriedAAs = QueryMap[InvalidAA];
+ LLVM_DEBUG(dbgs() << "[Attributor] InvalidAA: " << *InvalidAA << " has "
+ << QuerriedAAs.RequiredAAs.size() << "/"
+ << QuerriedAAs.OptionalAAs.size()
+ << " required/optional dependences\n");
+ for (AbstractAttribute *DepOnInvalidAA : QuerriedAAs.RequiredAAs) {
+ AbstractState &DOIAAState = DepOnInvalidAA->getState();
+ DOIAAState.indicatePessimisticFixpoint();
+ ++NumAttributesFixedDueToRequiredDependences;
+ assert(DOIAAState.isAtFixpoint() && "Expected fixpoint state!");
+ if (!DOIAAState.isValidState())
+ InvalidAAs.insert(DepOnInvalidAA);
+ }
+ if (!RecomputeDependences)
+ Worklist.insert(QuerriedAAs.OptionalAAs.begin(),
+ QuerriedAAs.OptionalAAs.end());
+ }
+
// If dependences (=QueryMap) are recomputed we have to look at all abstract
// attributes again, regardless of what changed in the last iteration.
if (RecomputeDependences) {
@@ -4493,22 +5904,35 @@ ChangeStatus Attributor::run(Module &M) {
// changed to the work list.
for (AbstractAttribute *ChangedAA : ChangedAAs) {
auto &QuerriedAAs = QueryMap[ChangedAA];
- Worklist.insert(QuerriedAAs.begin(), QuerriedAAs.end());
+ Worklist.insert(QuerriedAAs.OptionalAAs.begin(),
+ QuerriedAAs.OptionalAAs.end());
+ Worklist.insert(QuerriedAAs.RequiredAAs.begin(),
+ QuerriedAAs.RequiredAAs.end());
}
LLVM_DEBUG(dbgs() << "[Attributor] #Iteration: " << IterationCounter
<< ", Worklist+Dependent size: " << Worklist.size()
<< "\n");
- // Reset the changed set.
+ // Reset the changed and invalid set.
ChangedAAs.clear();
+ InvalidAAs.clear();
// Update all abstract attribute in the work list and record the ones that
// changed.
for (AbstractAttribute *AA : Worklist)
- if (!isAssumedDead(*AA, nullptr))
- if (AA->update(*this) == ChangeStatus::CHANGED)
+ if (!AA->getState().isAtFixpoint() && !isAssumedDead(*AA, nullptr)) {
+ QueriedNonFixAA = false;
+ if (AA->update(*this) == ChangeStatus::CHANGED) {
ChangedAAs.push_back(AA);
+ if (!AA->getState().isValidState())
+ InvalidAAs.insert(AA);
+ } else if (!QueriedNonFixAA) {
+ // If the attribute did not query any non-fix information, the state
+ // will not change and we can indicate that right away.
+ AA->getState().indicateOptimisticFixpoint();
+ }
+ }
// Check if we recompute the dependences in the next iteration.
RecomputeDependences = (DepRecomputeInterval > 0 &&
@@ -4552,7 +5976,10 @@ ChangeStatus Attributor::run(Module &M) {
}
auto &QuerriedAAs = QueryMap[ChangedAA];
- ChangedAAs.append(QuerriedAAs.begin(), QuerriedAAs.end());
+ ChangedAAs.append(QuerriedAAs.OptionalAAs.begin(),
+ QuerriedAAs.OptionalAAs.end());
+ ChangedAAs.append(QuerriedAAs.RequiredAAs.begin(),
+ QuerriedAAs.RequiredAAs.end());
}
LLVM_DEBUG({
@@ -4611,27 +6038,85 @@ ChangeStatus Attributor::run(Module &M) {
LLVM_DEBUG(dbgs() << "\n[Attributor] Delete at least "
<< ToBeDeletedFunctions.size() << " functions and "
<< ToBeDeletedBlocks.size() << " blocks and "
- << ToBeDeletedInsts.size() << " instructions\n");
+ << ToBeDeletedInsts.size() << " instructions and "
+ << ToBeChangedUses.size() << " uses\n");
+
+ SmallVector<Instruction *, 32> DeadInsts;
+ SmallVector<Instruction *, 32> TerminatorsToFold;
+
+ for (auto &It : ToBeChangedUses) {
+ Use *U = It.first;
+ Value *NewV = It.second;
+ Value *OldV = U->get();
+ LLVM_DEBUG(dbgs() << "Use " << *NewV << " in " << *U->getUser()
+ << " instead of " << *OldV << "\n");
+ U->set(NewV);
+ if (Instruction *I = dyn_cast<Instruction>(OldV))
+ if (!isa<PHINode>(I) && !ToBeDeletedInsts.count(I) &&
+ isInstructionTriviallyDead(I)) {
+ DeadInsts.push_back(I);
+ }
+ if (isa<Constant>(NewV) && isa<BranchInst>(U->getUser())) {
+ Instruction *UserI = cast<Instruction>(U->getUser());
+ if (isa<UndefValue>(NewV)) {
+ ToBeChangedToUnreachableInsts.insert(UserI);
+ } else {
+ TerminatorsToFold.push_back(UserI);
+ }
+ }
+ }
+ for (auto &V : InvokeWithDeadSuccessor)
+ if (InvokeInst *II = dyn_cast_or_null<InvokeInst>(V)) {
+ bool UnwindBBIsDead = II->hasFnAttr(Attribute::NoUnwind);
+ bool NormalBBIsDead = II->hasFnAttr(Attribute::NoReturn);
+ bool Invoke2CallAllowed =
+ !AAIsDeadFunction::mayCatchAsynchronousExceptions(
+ *II->getFunction());
+ assert((UnwindBBIsDead || NormalBBIsDead) &&
+ "Invoke does not have dead successors!");
+ BasicBlock *BB = II->getParent();
+ BasicBlock *NormalDestBB = II->getNormalDest();
+ if (UnwindBBIsDead) {
+ Instruction *NormalNextIP = &NormalDestBB->front();
+ if (Invoke2CallAllowed) {
+ changeToCall(II);
+ NormalNextIP = BB->getTerminator();
+ }
+ if (NormalBBIsDead)
+ ToBeChangedToUnreachableInsts.insert(NormalNextIP);
+ } else {
+ assert(NormalBBIsDead && "Broken invariant!");
+ if (!NormalDestBB->getUniquePredecessor())
+ NormalDestBB = SplitBlockPredecessors(NormalDestBB, {BB}, ".dead");
+ ToBeChangedToUnreachableInsts.insert(&NormalDestBB->front());
+ }
+ }
+ for (auto &V : ToBeChangedToUnreachableInsts)
+ if (Instruction *I = dyn_cast_or_null<Instruction>(V))
+ changeToUnreachable(I, /* UseLLVMTrap */ false);
+ for (Instruction *I : TerminatorsToFold)
+ ConstantFoldTerminator(I->getParent());
+
for (Instruction *I : ToBeDeletedInsts) {
- if (!I->use_empty())
- I->replaceAllUsesWith(UndefValue::get(I->getType()));
- I->eraseFromParent();
+ I->replaceAllUsesWith(UndefValue::get(I->getType()));
+ if (!isa<PHINode>(I) && isInstructionTriviallyDead(I))
+ DeadInsts.push_back(I);
+ else
+ I->eraseFromParent();
}
+ RecursivelyDeleteTriviallyDeadInstructions(DeadInsts);
+
if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) {
SmallVector<BasicBlock *, 8> ToBeDeletedBBs;
ToBeDeletedBBs.reserve(NumDeadBlocks);
ToBeDeletedBBs.append(ToBeDeletedBlocks.begin(), ToBeDeletedBlocks.end());
- DeleteDeadBlocks(ToBeDeletedBBs);
- STATS_DECLTRACK(AAIsDead, BasicBlock,
- "Number of dead basic blocks deleted.");
- }
-
- STATS_DECL(AAIsDead, Function, "Number of dead functions deleted.");
- for (Function *Fn : ToBeDeletedFunctions) {
- Fn->replaceAllUsesWith(UndefValue::get(Fn->getType()));
- Fn->eraseFromParent();
- STATS_TRACK(AAIsDead, Function);
+ // Actually we do not delete the blocks but squash them into a single
+ // unreachable but untangling branches that jump here is something we need
+ // to do in a more generic way.
+ DetatchDeadBlocks(ToBeDeletedBBs, nullptr);
+ STATS_DECL(AAIsDead, BasicBlock, "Number of dead basic blocks deleted.");
+ BUILD_STAT_NAME(AAIsDead, BasicBlock) += ToBeDeletedBlocks.size();
}
// Identify dead internal functions and delete them. This happens outside
@@ -4651,22 +6136,33 @@ ChangeStatus Attributor::run(Module &M) {
if (!F)
continue;
- const auto *LivenessAA =
- lookupAAFor<AAIsDead>(IRPosition::function(*F));
- if (LivenessAA &&
- !checkForAllCallSites([](AbstractCallSite ACS) { return false; },
- *LivenessAA, true))
+ if (!checkForAllCallSites(
+ [this](AbstractCallSite ACS) {
+ return ToBeDeletedFunctions.count(
+ ACS.getInstruction()->getFunction());
+ },
+ *F, true, nullptr))
continue;
- STATS_TRACK(AAIsDead, Function);
- F->replaceAllUsesWith(UndefValue::get(F->getType()));
- F->eraseFromParent();
+ ToBeDeletedFunctions.insert(F);
InternalFns[u] = nullptr;
FoundDeadFn = true;
}
}
}
+ STATS_DECL(AAIsDead, Function, "Number of dead functions deleted.");
+ BUILD_STAT_NAME(AAIsDead, Function) += ToBeDeletedFunctions.size();
+
+ // Rewrite the functions as requested during manifest.
+ ManifestChange = ManifestChange | rewriteFunctionSignatures();
+
+ for (Function *Fn : ToBeDeletedFunctions) {
+ Fn->deleteBody();
+ Fn->replaceAllUsesWith(UndefValue::get(Fn->getType()));
+ Fn->eraseFromParent();
+ }
+
if (VerifyMaxFixpointIterations &&
IterationCounter != MaxFixpointIterations) {
errs() << "\n[Attributor] Fixpoint iteration done after: "
@@ -4679,6 +6175,252 @@ ChangeStatus Attributor::run(Module &M) {
return ManifestChange;
}
+bool Attributor::registerFunctionSignatureRewrite(
+ Argument &Arg, ArrayRef<Type *> ReplacementTypes,
+ ArgumentReplacementInfo::CalleeRepairCBTy &&CalleeRepairCB,
+ ArgumentReplacementInfo::ACSRepairCBTy &&ACSRepairCB) {
+
+ auto CallSiteCanBeChanged = [](AbstractCallSite ACS) {
+ // Forbid must-tail calls for now.
+ return !ACS.isCallbackCall() && !ACS.getCallSite().isMustTailCall();
+ };
+
+ Function *Fn = Arg.getParent();
+ // Avoid var-arg functions for now.
+ if (Fn->isVarArg()) {
+ LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite var-args functions\n");
+ return false;
+ }
+
+ // Avoid functions with complicated argument passing semantics.
+ AttributeList FnAttributeList = Fn->getAttributes();
+ if (FnAttributeList.hasAttrSomewhere(Attribute::Nest) ||
+ FnAttributeList.hasAttrSomewhere(Attribute::StructRet) ||
+ FnAttributeList.hasAttrSomewhere(Attribute::InAlloca)) {
+ LLVM_DEBUG(
+ dbgs() << "[Attributor] Cannot rewrite due to complex attribute\n");
+ return false;
+ }
+
+ // Avoid callbacks for now.
+ if (!checkForAllCallSites(CallSiteCanBeChanged, *Fn, true, nullptr)) {
+ LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite all call sites\n");
+ return false;
+ }
+
+ auto InstPred = [](Instruction &I) {
+ if (auto *CI = dyn_cast<CallInst>(&I))
+ return !CI->isMustTailCall();
+ return true;
+ };
+
+ // Forbid must-tail calls for now.
+ // TODO:
+ bool AnyDead;
+ auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*Fn);
+ if (!checkForAllInstructionsImpl(OpcodeInstMap, InstPred, nullptr, AnyDead,
+ {Instruction::Call})) {
+ LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite due to instructions\n");
+ return false;
+ }
+
+ SmallVectorImpl<ArgumentReplacementInfo *> &ARIs = ArgumentReplacementMap[Fn];
+ if (ARIs.size() == 0)
+ ARIs.resize(Fn->arg_size());
+
+ // If we have a replacement already with less than or equal new arguments,
+ // ignore this request.
+ ArgumentReplacementInfo *&ARI = ARIs[Arg.getArgNo()];
+ if (ARI && ARI->getNumReplacementArgs() <= ReplacementTypes.size()) {
+ LLVM_DEBUG(dbgs() << "[Attributor] Existing rewrite is preferred\n");
+ return false;
+ }
+
+ // If we have a replacement already but we like the new one better, delete
+ // the old.
+ if (ARI)
+ delete ARI;
+
+ // Remember the replacement.
+ ARI = new ArgumentReplacementInfo(*this, Arg, ReplacementTypes,
+ std::move(CalleeRepairCB),
+ std::move(ACSRepairCB));
+
+ return true;
+}
+
+ChangeStatus Attributor::rewriteFunctionSignatures() {
+ ChangeStatus Changed = ChangeStatus::UNCHANGED;
+
+ for (auto &It : ArgumentReplacementMap) {
+ Function *OldFn = It.getFirst();
+
+ // Deleted functions do not require rewrites.
+ if (ToBeDeletedFunctions.count(OldFn))
+ continue;
+
+ const SmallVectorImpl<ArgumentReplacementInfo *> &ARIs = It.getSecond();
+ assert(ARIs.size() == OldFn->arg_size() && "Inconsistent state!");
+
+ SmallVector<Type *, 16> NewArgumentTypes;
+ SmallVector<AttributeSet, 16> NewArgumentAttributes;
+
+ // Collect replacement argument types and copy over existing attributes.
+ AttributeList OldFnAttributeList = OldFn->getAttributes();
+ for (Argument &Arg : OldFn->args()) {
+ if (ArgumentReplacementInfo *ARI = ARIs[Arg.getArgNo()]) {
+ NewArgumentTypes.append(ARI->ReplacementTypes.begin(),
+ ARI->ReplacementTypes.end());
+ NewArgumentAttributes.append(ARI->getNumReplacementArgs(),
+ AttributeSet());
+ } else {
+ NewArgumentTypes.push_back(Arg.getType());
+ NewArgumentAttributes.push_back(
+ OldFnAttributeList.getParamAttributes(Arg.getArgNo()));
+ }
+ }
+
+ FunctionType *OldFnTy = OldFn->getFunctionType();
+ Type *RetTy = OldFnTy->getReturnType();
+
+ // Construct the new function type using the new arguments types.
+ FunctionType *NewFnTy =
+ FunctionType::get(RetTy, NewArgumentTypes, OldFnTy->isVarArg());
+
+ LLVM_DEBUG(dbgs() << "[Attributor] Function rewrite '" << OldFn->getName()
+ << "' from " << *OldFn->getFunctionType() << " to "
+ << *NewFnTy << "\n");
+
+ // Create the new function body and insert it into the module.
+ Function *NewFn = Function::Create(NewFnTy, OldFn->getLinkage(),
+ OldFn->getAddressSpace(), "");
+ OldFn->getParent()->getFunctionList().insert(OldFn->getIterator(), NewFn);
+ NewFn->takeName(OldFn);
+ NewFn->copyAttributesFrom(OldFn);
+
+ // Patch the pointer to LLVM function in debug info descriptor.
+ NewFn->setSubprogram(OldFn->getSubprogram());
+ OldFn->setSubprogram(nullptr);
+
+ // Recompute the parameter attributes list based on the new arguments for
+ // the function.
+ LLVMContext &Ctx = OldFn->getContext();
+ NewFn->setAttributes(AttributeList::get(
+ Ctx, OldFnAttributeList.getFnAttributes(),
+ OldFnAttributeList.getRetAttributes(), NewArgumentAttributes));
+
+ // Since we have now created the new function, splice the body of the old
+ // function right into the new function, leaving the old rotting hulk of the
+ // function empty.
+ NewFn->getBasicBlockList().splice(NewFn->begin(),
+ OldFn->getBasicBlockList());
+
+ // Set of all "call-like" instructions that invoke the old function mapped
+ // to their new replacements.
+ SmallVector<std::pair<CallBase *, CallBase *>, 8> CallSitePairs;
+
+ // Callback to create a new "call-like" instruction for a given one.
+ auto CallSiteReplacementCreator = [&](AbstractCallSite ACS) {
+ CallBase *OldCB = cast<CallBase>(ACS.getInstruction());
+ const AttributeList &OldCallAttributeList = OldCB->getAttributes();
+
+ // Collect the new argument operands for the replacement call site.
+ SmallVector<Value *, 16> NewArgOperands;
+ SmallVector<AttributeSet, 16> NewArgOperandAttributes;
+ for (unsigned OldArgNum = 0; OldArgNum < ARIs.size(); ++OldArgNum) {
+ unsigned NewFirstArgNum = NewArgOperands.size();
+ (void)NewFirstArgNum; // only used inside assert.
+ if (ArgumentReplacementInfo *ARI = ARIs[OldArgNum]) {
+ if (ARI->ACSRepairCB)
+ ARI->ACSRepairCB(*ARI, ACS, NewArgOperands);
+ assert(ARI->getNumReplacementArgs() + NewFirstArgNum ==
+ NewArgOperands.size() &&
+ "ACS repair callback did not provide as many operand as new "
+ "types were registered!");
+ // TODO: Exose the attribute set to the ACS repair callback
+ NewArgOperandAttributes.append(ARI->ReplacementTypes.size(),
+ AttributeSet());
+ } else {
+ NewArgOperands.push_back(ACS.getCallArgOperand(OldArgNum));
+ NewArgOperandAttributes.push_back(
+ OldCallAttributeList.getParamAttributes(OldArgNum));
+ }
+ }
+
+ assert(NewArgOperands.size() == NewArgOperandAttributes.size() &&
+ "Mismatch # argument operands vs. # argument operand attributes!");
+ assert(NewArgOperands.size() == NewFn->arg_size() &&
+ "Mismatch # argument operands vs. # function arguments!");
+
+ SmallVector<OperandBundleDef, 4> OperandBundleDefs;
+ OldCB->getOperandBundlesAsDefs(OperandBundleDefs);
+
+ // Create a new call or invoke instruction to replace the old one.
+ CallBase *NewCB;
+ if (InvokeInst *II = dyn_cast<InvokeInst>(OldCB)) {
+ NewCB =
+ InvokeInst::Create(NewFn, II->getNormalDest(), II->getUnwindDest(),
+ NewArgOperands, OperandBundleDefs, "", OldCB);
+ } else {
+ auto *NewCI = CallInst::Create(NewFn, NewArgOperands, OperandBundleDefs,
+ "", OldCB);
+ NewCI->setTailCallKind(cast<CallInst>(OldCB)->getTailCallKind());
+ NewCB = NewCI;
+ }
+
+ // Copy over various properties and the new attributes.
+ uint64_t W;
+ if (OldCB->extractProfTotalWeight(W))
+ NewCB->setProfWeight(W);
+ NewCB->setCallingConv(OldCB->getCallingConv());
+ NewCB->setDebugLoc(OldCB->getDebugLoc());
+ NewCB->takeName(OldCB);
+ NewCB->setAttributes(AttributeList::get(
+ Ctx, OldCallAttributeList.getFnAttributes(),
+ OldCallAttributeList.getRetAttributes(), NewArgOperandAttributes));
+
+ CallSitePairs.push_back({OldCB, NewCB});
+ return true;
+ };
+
+ // Use the CallSiteReplacementCreator to create replacement call sites.
+ bool Success =
+ checkForAllCallSites(CallSiteReplacementCreator, *OldFn, true, nullptr);
+ (void)Success;
+ assert(Success && "Assumed call site replacement to succeed!");
+
+ // Rewire the arguments.
+ auto OldFnArgIt = OldFn->arg_begin();
+ auto NewFnArgIt = NewFn->arg_begin();
+ for (unsigned OldArgNum = 0; OldArgNum < ARIs.size();
+ ++OldArgNum, ++OldFnArgIt) {
+ if (ArgumentReplacementInfo *ARI = ARIs[OldArgNum]) {
+ if (ARI->CalleeRepairCB)
+ ARI->CalleeRepairCB(*ARI, *NewFn, NewFnArgIt);
+ NewFnArgIt += ARI->ReplacementTypes.size();
+ } else {
+ NewFnArgIt->takeName(&*OldFnArgIt);
+ OldFnArgIt->replaceAllUsesWith(&*NewFnArgIt);
+ ++NewFnArgIt;
+ }
+ }
+
+ // Eliminate the instructions *after* we visited all of them.
+ for (auto &CallSitePair : CallSitePairs) {
+ CallBase &OldCB = *CallSitePair.first;
+ CallBase &NewCB = *CallSitePair.second;
+ OldCB.replaceAllUsesWith(&NewCB);
+ OldCB.eraseFromParent();
+ }
+
+ ToBeDeletedFunctions.insert(OldFn);
+
+ Changed = ChangeStatus::CHANGED;
+ }
+
+ return Changed;
+}
+
void Attributor::initializeInformationCache(Function &F) {
// Walk all instructions to find interesting instructions that might be
@@ -4710,6 +6452,9 @@ void Attributor::initializeInformationCache(Function &F) {
case Instruction::Invoke:
case Instruction::CleanupRet:
case Instruction::CatchSwitch:
+ case Instruction::AtomicRMW:
+ case Instruction::AtomicCmpXchg:
+ case Instruction::Br:
case Instruction::Resume:
case Instruction::Ret:
IsInterestingOpcode = true;
@@ -4721,9 +6466,26 @@ void Attributor::initializeInformationCache(Function &F) {
}
}
+void Attributor::recordDependence(const AbstractAttribute &FromAA,
+ const AbstractAttribute &ToAA,
+ DepClassTy DepClass) {
+ if (FromAA.getState().isAtFixpoint())
+ return;
+
+ if (DepClass == DepClassTy::REQUIRED)
+ QueryMap[&FromAA].RequiredAAs.insert(
+ const_cast<AbstractAttribute *>(&ToAA));
+ else
+ QueryMap[&FromAA].OptionalAAs.insert(
+ const_cast<AbstractAttribute *>(&ToAA));
+ QueriedNonFixAA = true;
+}
+
void Attributor::identifyDefaultAbstractAttributes(Function &F) {
if (!VisitedFunctions.insert(&F).second)
return;
+ if (F.isDeclaration())
+ return;
IRPosition FPos = IRPosition::function(F);
@@ -4735,6 +6497,9 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
// Every function might be "will-return".
getOrCreateAAFor<AAWillReturn>(FPos);
+ // Every function might contain instructions that cause "undefined behavior".
+ getOrCreateAAFor<AAUndefinedBehavior>(FPos);
+
// Every function can be nounwind.
getOrCreateAAFor<AANoUnwind>(FPos);
@@ -4766,6 +6531,9 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
IRPosition RetPos = IRPosition::returned(F);
+ // Every returned value might be dead.
+ getOrCreateAAFor<AAIsDead>(RetPos);
+
// Every function might be simplified.
getOrCreateAAFor<AAValueSimplify>(RetPos);
@@ -4811,16 +6579,41 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
// Every argument with pointer type might be marked
// "readnone/readonly/writeonly/..."
getOrCreateAAFor<AAMemoryBehavior>(ArgPos);
+
+ // Every argument with pointer type might be marked nofree.
+ getOrCreateAAFor<AANoFree>(ArgPos);
}
}
auto CallSitePred = [&](Instruction &I) -> bool {
CallSite CS(&I);
- if (CS.getCalledFunction()) {
- for (int i = 0, e = CS.getCalledFunction()->arg_size(); i < e; i++) {
+ if (Function *Callee = CS.getCalledFunction()) {
+ // Skip declerations except if annotations on their call sites were
+ // explicitly requested.
+ if (!AnnotateDeclarationCallSites && Callee->isDeclaration() &&
+ !Callee->hasMetadata(LLVMContext::MD_callback))
+ return true;
+
+ if (!Callee->getReturnType()->isVoidTy() && !CS->use_empty()) {
+
+ IRPosition CSRetPos = IRPosition::callsite_returned(CS);
+
+ // Call site return values might be dead.
+ getOrCreateAAFor<AAIsDead>(CSRetPos);
+
+ // Call site return integer values might be limited by a constant range.
+ if (Callee->getReturnType()->isIntegerTy()) {
+ getOrCreateAAFor<AAValueConstantRange>(CSRetPos);
+ }
+ }
+
+ for (int i = 0, e = CS.getNumArgOperands(); i < e; i++) {
IRPosition CSArgPos = IRPosition::callsite_argument(CS, i);
+ // Every call site argument might be dead.
+ getOrCreateAAFor<AAIsDead>(CSArgPos);
+
// Call site argument might be simplified.
getOrCreateAAFor<AAValueSimplify>(CSArgPos);
@@ -4838,6 +6631,13 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
// Call site argument attribute "align".
getOrCreateAAFor<AAAlign>(CSArgPos);
+
+ // Call site argument attribute
+ // "readnone/readonly/writeonly/..."
+ getOrCreateAAFor<AAMemoryBehavior>(CSArgPos);
+
+ // Call site argument attribute "nofree".
+ getOrCreateAAFor<AANoFree>(CSArgPos);
}
}
return true;
@@ -4903,11 +6703,24 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const IRPosition &Pos) {
<< Pos.getAnchorValue().getName() << "@" << Pos.getArgNo() << "]}";
}
-raw_ostream &llvm::operator<<(raw_ostream &OS, const IntegerState &S) {
+template <typename base_ty, base_ty BestState, base_ty WorstState>
+raw_ostream &
+llvm::operator<<(raw_ostream &OS,
+ const IntegerStateBase<base_ty, BestState, WorstState> &S) {
return OS << "(" << S.getKnown() << "-" << S.getAssumed() << ")"
<< static_cast<const AbstractState &>(S);
}
+raw_ostream &llvm::operator<<(raw_ostream &OS, const IntegerRangeState &S) {
+ OS << "range-state(" << S.getBitWidth() << ")<";
+ S.getKnown().print(OS);
+ OS << " / ";
+ S.getAssumed().print(OS);
+ OS << ">";
+
+ return OS << static_cast<const AbstractState &>(S);
+}
+
raw_ostream &llvm::operator<<(raw_ostream &OS, const AbstractState &S) {
return OS << (!S.isValidState() ? "top" : (S.isAtFixpoint() ? "fix" : ""));
}
@@ -4963,7 +6776,9 @@ static bool runAttributorOnModule(Module &M, AnalysisGetter &AG) {
A.identifyDefaultAbstractAttributes(F);
}
- return A.run(M) == ChangeStatus::CHANGED;
+ bool Changed = A.run(M) == ChangeStatus::CHANGED;
+ assert(!verifyModule(M, &errs()) && "Module verification failed!");
+ return Changed;
}
PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) {
@@ -5011,7 +6826,9 @@ const char AANoFree::ID = 0;
const char AANonNull::ID = 0;
const char AANoRecurse::ID = 0;
const char AAWillReturn::ID = 0;
+const char AAUndefinedBehavior::ID = 0;
const char AANoAlias::ID = 0;
+const char AAReachability::ID = 0;
const char AANoReturn::ID = 0;
const char AAIsDead::ID = 0;
const char AADereferenceable::ID = 0;
@@ -5020,6 +6837,7 @@ const char AANoCapture::ID = 0;
const char AAValueSimplify::ID = 0;
const char AAHeapToStack::ID = 0;
const char AAMemoryBehavior::ID = 0;
+const char AAValueConstantRange::ID = 0;
// Macro magic to create the static generator function for attributes that
// follow the naming scheme.
@@ -5115,11 +6933,9 @@ const char AAMemoryBehavior::ID = 0;
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUnwind)
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoSync)
-CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree)
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoRecurse)
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAWillReturn)
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoReturn)
-CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead)
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAReturnedValues)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANonNull)
@@ -5127,10 +6943,15 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoAlias)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AADereferenceable)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAlign)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoCapture)
+CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueConstantRange)
CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify)
+CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead)
+CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree)
CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack)
+CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAReachability)
+CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUndefinedBehavior)
CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryBehavior)