diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -114,6 +114,7 @@ namespace llvm { +struct Attributor; struct AbstractAttribute; struct InformationCache; struct AAIsDead; @@ -397,7 +398,8 @@ /// e.g., the function position if this is an /// argument position, should be ignored. bool hasAttr(ArrayRef AKs, - bool IgnoreSubsumingPositions = false) const; + bool IgnoreSubsumingPositions = false, + Attributor *A = nullptr) const; /// Return the attributes of any kind in \p AKs existing in the IR at a /// position that will affect this one. While each position can only have a @@ -409,7 +411,8 @@ /// argument position, should be ignored. void getAttrs(ArrayRef AKs, SmallVectorImpl &Attrs, - bool IgnoreSubsumingPositions = false) const; + bool IgnoreSubsumingPositions = false, + Attributor *A = nullptr) const; /// Remove the attribute of kind \p AKs existing in the IR at this position. void removeAttrs(ArrayRef AKs) const { @@ -469,6 +472,12 @@ bool getAttrsFromIRAttr(Attribute::AttrKind AK, SmallVectorImpl &Attrs) const; + /// Return the attributes of kind \p AK existing in the IR as operand bundles + /// of an llvm.assume. + bool getAttrsFromAssumes(Attribute::AttrKind AK, + SmallVectorImpl &Attrs, + Attributor &A) const; + protected: /// The value this position is anchored at. Value *AnchorVal; @@ -1696,7 +1705,8 @@ /// See AbstractAttribute::initialize(...). virtual void initialize(Attributor &A) override { const IRPosition &IRP = this->getIRPosition(); - if (isa(IRP.getAssociatedValue()) || hasAttr(getAttrKind())) { + if (isa(IRP.getAssociatedValue()) || + hasAttr(getAttrKind(), /* IgnoreSubsumingPositions */ false, &A)) { this->getState().indicateOptimisticFixpoint(); return; } diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -36,14 +36,15 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/NoFolder.h" #include "llvm/IR/Verifier.h" #include "llvm/InitializePasses.h" -#include "llvm/IR/NoFolder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/KnowledgeRetention.h" #include "llvm/Transforms/Utils/Local.h" #include @@ -677,32 +678,42 @@ } bool IRPosition::hasAttr(ArrayRef AKs, - bool IgnoreSubsumingPositions) const { + bool IgnoreSubsumingPositions, Attributor *A) const { SmallVector Attrs; for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) { - for (Attribute::AttrKind AK : AKs) + for (Attribute::AttrKind AK : AKs) { if (EquivIRP.getAttrsFromIRAttr(AK, Attrs)) return true; + if (A && EquivIRP.getAttrsFromAssumes(AK, Attrs, *A)) + return true; + } // 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; + // Do not perform an search for assumes on subsuming positions. + A = nullptr; } return false; } void IRPosition::getAttrs(ArrayRef AKs, SmallVectorImpl &Attrs, - bool IgnoreSubsumingPositions) const { + bool IgnoreSubsumingPositions, Attributor *A) const { for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) { - for (Attribute::AttrKind AK : AKs) + for (Attribute::AttrKind AK : AKs) { EquivIRP.getAttrsFromIRAttr(AK, Attrs); + if (A) + EquivIRP.getAttrsFromAssumes(AK, Attrs, *A); + } // 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; + // Do not perform an search for assumes on subsuming positions. + A = nullptr; } } @@ -723,6 +734,38 @@ return HasAttr; } +bool IRPosition::getAttrsFromAssumes(Attribute::AttrKind AK, + SmallVectorImpl &Attrs, + Attributor &A) const { + assert(getPositionKind() != IRP_INVALID && "Did expect a valid position!"); + Value &AssociatedValue = getAssociatedValue(); + LLVMContext &Ctx = AssociatedValue.getContext(); + uint64_t ArgVal = 0; + uint64_t *ArgValPtr = + Attribute::doesAttrKindHaveArgument(AK) ? &ArgVal : nullptr; + StringRef AttrName = Attribute::getNameFromAttrKind(AK); + DenseMap AssumeAttrMap; + for (const Use &U : AssociatedValue.uses()) + if (IntrinsicInst *II = dyn_cast_or_null(U.getUser())) + if (II->getIntrinsicID() == Intrinsic::assume && !II->isArgOperand(&U)) + if (hasAttributeInAssume(*II, &AssociatedValue, AttrName, ArgValPtr)) + if (!ArgValPtr || ArgVal > 0) + AssumeAttrMap[II] = Attribute::get(Ctx, AK, ArgVal); + + // Check if we found any potential assume use, if not we don't need to create + // explorer iterators. + if (AssumeAttrMap.empty()) + return false; + + unsigned AttrsSize = Attrs.size(); + MustBeExecutedContextExplorer &Explorer = + A.getInfoCache().getMustBeExecutedContextExplorer(); + auto EIt = Explorer.begin(getCtxI()), EEnd = Explorer.end(getCtxI()); + for (auto &It : AssumeAttrMap) + if (Explorer.findInContextOf(It.first, EIt, EEnd)) + Attrs.push_back(It.second); + return AttrsSize != Attrs.size(); +} void IRPosition::verify() { switch (KindOrArgNo) { @@ -1982,7 +2025,8 @@ /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { if (!NullIsDefined && - hasAttr({Attribute::NonNull, Attribute::Dereferenceable})) + hasAttr({Attribute::NonNull, Attribute::Dereferenceable}, + /* IgnoreSubsumingPositions */ false, &A)) indicateOptimisticFixpoint(); else if (isa(getAssociatedValue())) indicatePessimisticFixpoint(); @@ -3525,7 +3569,7 @@ void initialize(Attributor &A) override { SmallVector Attrs; getAttrs({Attribute::Dereferenceable, Attribute::DereferenceableOrNull}, - Attrs); + Attrs, /* IgnoreSubsumingPositions */ false, &A); for (const Attribute &Attr : Attrs) takeKnownDerefBytesMaximum(Attr.getValueAsInt()); diff --git a/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp b/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp --- a/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp +++ b/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp @@ -186,6 +186,8 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn, StringRef AttrName, uint64_t *ArgVal, AssumeQuery AQR) { + assert(isa(AssumeCI) && + "this function is intended to be used on llvm.assume"); IntrinsicInst &Assume = cast(AssumeCI); assert(Assume.getIntrinsicID() == Intrinsic::assume && "this function is intended to be used on llvm.assume"); @@ -202,7 +204,7 @@ /// right WasOn needs to be done via linear search. /// Element have been ordered by argument value so the first we find is the /// one we need. - if (AQR == AssumeQuery::Lowest) + if (AQR == AssumeQuery::Lowest) { Lookup = llvm::lower_bound(Assume.bundle_op_infos(), AttrName, [](const CallBase::BundleOpInfo &BOI, StringRef RHS) { @@ -210,14 +212,22 @@ "this attribute doesn't exist"); return BOI.Tag->getKey() < RHS; }); - else - Lookup = std::prev( + } else { + Lookup = llvm::upper_bound(Assume.bundle_op_infos(), AttrName, [](StringRef LHS, const CallBase::BundleOpInfo &BOI) { assert(isExistingAttribute(BOI.Tag->getKey()) && "this attribute doesn't exist"); return LHS < BOI.Tag->getKey(); - })); + }); + // If we are at the very beginning the tag was not found. If we are not at + // the very beginning we don't know if the tag was found but we can go back + // to find out. + if (Lookup == Assume.bundle_op_info_begin()) + Lookup = Assume.bundle_op_info_end(); + else + Lookup = std::prev(Lookup); + } auto getValueFromBundleOpInfo = [&Assume](const CallBase::BundleOpInfo &BOI, unsigned Idx) { diff --git a/llvm/test/Transforms/Attributor/dereferenceable-1.ll b/llvm/test/Transforms/Attributor/dereferenceable-1.ll --- a/llvm/test/Transforms/Attributor/dereferenceable-1.ll +++ b/llvm/test/Transforms/Attributor/dereferenceable-1.ll @@ -312,5 +312,28 @@ ret void } +declare void @unknown() +define void @nonnull_assume_pos(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos +; ATTRIBUTOR-SAME: (i8* nocapture nofree nonnull readnone dereferenceable(101) [[ARG1:%.*]], i8* nocapture nofree readnone dereferenceable_or_null(31) [[ARG2:%.*]], i8* nocapture nofree readnone [[ARG3:%.*]], i8* nocapture nofree readnone dereferenceable_or_null(42) [[ARG4:%.*]]) +; ATTRIBUTOR-NEXT: call void @unknown() +; ATTRIBUTOR-NEXT: ret void +; + call void @llvm.assume(i1 true) ["dereferenceable"(i8* %arg1, i64 1), "dereferenceable"(i8* %arg1, i64 2), "dereferenceable"(i8* %arg1, i64 101), "dereferenceable"(i8* %arg3, i64 0), "dereferenceable_or_null"(i8* %arg2, i64 31), "dereferenceable_or_null"(i8* %arg4, i64 42)] + call void @unknown() + ret void +} +define void @nonnull_assume_neg(i8* %arg1, i8* %arg2, i8* %arg3) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg +; ATTRIBUTOR-SAME: (i8* nocapture nofree readnone [[ARG1:%.*]], i8* nocapture nofree readnone [[ARG2:%.*]], i8* nocapture nofree readnone [[ARG3:%.*]]) +; ATTRIBUTOR-NEXT: call void @unknown() +; ATTRIBUTOR-NEXT: ret void +; + call void @unknown() + call void @llvm.assume(i1 true) ["dereferenceable"(i8* %arg1, i64 101), "dereferenceable"(i8* %arg2, i64 -2), "dereferenceable_or_null"(i8* %arg3, i64 31)] + ret void +} +declare void @llvm.assume(i1) + !0 = !{i64 10, i64 100} diff --git a/llvm/test/Transforms/Attributor/nofree.ll b/llvm/test/Transforms/Attributor/nofree.ll --- a/llvm/test/Transforms/Attributor/nofree.ll +++ b/llvm/test/Transforms/Attributor/nofree.ll @@ -247,6 +247,31 @@ ret void } +; UTC_ARGS: --enable + +declare void @unknown(i8*, i8*, i8*, i8*) +define void @nonnull_assume_pos(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos +; ATTRIBUTOR-SAME: (i8* nofree [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* nofree [[ARG3:%.*]], i8* [[ARG4:%.*]]) +; ATTRIBUTOR-NEXT: call void @unknown(i8* [[ARG1]], i8* [[ARG2]], i8* [[ARG3]], i8* [[ARG4]]) +; ATTRIBUTOR-NEXT: ret void +; + call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg3)] + call void @unknown(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) + ret void +} +define void @nonnull_assume_neg(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg +; ATTRIBUTOR-SAME: (i8* [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* [[ARG3:%.*]], i8* [[ARG4:%.*]]) +; ATTRIBUTOR-NEXT: call void @unknown(i8* [[ARG1]], i8* [[ARG2]], i8* [[ARG3]], i8* [[ARG4]]) +; ATTRIBUTOR-NEXT: ret void +; + call void @unknown(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) + call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg3)] + ret void +} +declare void @llvm.assume(i1) + declare noalias i8* @malloc(i64) attributes #0 = { nounwind uwtable noinline } diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll --- a/llvm/test/Transforms/Attributor/nonnull.ll +++ b/llvm/test/Transforms/Attributor/nonnull.ll @@ -845,5 +845,26 @@ ret i8* %cond } +define void @nonnull_assume_pos(i8* %arg) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos +; ATTRIBUTOR-SAME: (i8* nocapture nofree nonnull readnone [[ARG:%.*]]) +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i8* @unknown() +; ATTRIBUTOR-NEXT: ret void +; + call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)] + call i8* @unknown() + ret void +} +define void @nonnull_assume_neg(i8* %arg) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg +; ATTRIBUTOR-SAME: (i8* nocapture nofree readnone [[ARG:%.*]]) +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i8* @unknown() +; ATTRIBUTOR-NEXT: ret void +; + call i8* @unknown() + call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)] + ret void +} + attributes #0 = { "null-pointer-is-valid"="true" } attributes #1 = { nounwind willreturn}