From 31d276a1c1f0aee944b62cbbee731f7428b791a7 Mon Sep 17 00:00:00 2001 From: Johannes Doerfert Date: Thu, 20 Feb 2020 02:06:48 -0600 Subject: [PATCH] [Attributor] Use knowledge retained in llvm.assume (operand bundles) This patch integrates operand bundle llvm.assumes [0] with the Attributor. Most IRAttributes will now look at uses of the associated value and if there are llvm.assume operand bundle uses with the right tag we will check if they are in the must-be-executed-context (around the context instruction). Droppable users, which is currently only llvm::assume, are handled special in some places now as well. [0] http://lists.llvm.org/pipermail/llvm-dev/2019-December/137632.html Reviewed By: uenoku Differential Revision: https://reviews.llvm.org/D74888 --- include/llvm/IR/KnowledgeRetention.h | 10 ++- include/llvm/Transforms/IPO/Attributor.h | 23 +++++- lib/IR/KnowledgeRetention.cpp | 12 +-- lib/Transforms/IPO/Attributor.cpp | 63 +++++++++++++-- .../Attributor/dereferenceable-1.ll | 62 +++++++++++++++ test/Transforms/Attributor/nofree.ll | 58 ++++++++++++++ test/Transforms/Attributor/nonnull.ll | 40 +++++++++- unittests/IR/KnowledgeRetentionTest.cpp | 78 ++++++++++--------- 8 files changed, 292 insertions(+), 54 deletions(-) diff --git a/include/llvm/IR/KnowledgeRetention.h b/include/llvm/IR/KnowledgeRetention.h index f5c2d554c23..a7de7cd16b6 100644 --- a/include/llvm/IR/KnowledgeRetention.h +++ b/include/llvm/IR/KnowledgeRetention.h @@ -22,6 +22,7 @@ #include "llvm/ADT/DenseMap.h" namespace llvm { +class IntrinsicInst; /// Build a call to llvm.assume to preserve informations that can be derived /// from the given instruction. @@ -84,7 +85,14 @@ struct MinMax { unsigned Max; }; -using RetainedKnowledgeMap = DenseMap; +/// A mapping from intrinsics (=`llvm.assume` calls) to a value range +/// (=knowledge) that is encoded in them. How the value range is interpreted +/// depends on the RetainedKnowledgeKey that was used to get this out of the +/// RetainedKnowledgeMap. +using Assume2KnowledgeMap = DenseMap; + +using RetainedKnowledgeMap = + DenseMap; /// Insert into the map all the informations contained in the operand bundles of /// the llvm.assume. This should be used instead of hasAttributeInAssume when diff --git a/include/llvm/Transforms/IPO/Attributor.h b/include/llvm/Transforms/IPO/Attributor.h index a229f55e1a8..2113197c067 100644 --- a/include/llvm/Transforms/IPO/Attributor.h +++ b/include/llvm/Transforms/IPO/Attributor.h @@ -110,11 +110,13 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/ConstantRange.h" +#include "llvm/IR/KnowledgeRetention.h" #include "llvm/IR/PassManager.h" #include "llvm/Transforms/Utils/CallGraphUpdater.h" namespace llvm { +struct Attributor; struct AbstractAttribute; struct InformationCache; struct AAIsDead; @@ -391,7 +393,8 @@ struct IRPosition { /// 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 @@ -403,7 +406,8 @@ struct IRPosition { /// 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 { @@ -463,6 +467,12 @@ private: 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; @@ -607,6 +617,9 @@ struct InformationCache { /// Return datalayout used in the module. const DataLayout &getDL() { return DL; } + /// Return the map conaining all the knowledge we have from `llvm.assume`s. + const RetainedKnowledgeMap &getKnowledgeMap() const { return KnowledgeMap; } + private: /// A map type from functions to opcode to instruction maps. using FuncInstOpcodeMapTy = DenseMap; @@ -627,6 +640,9 @@ private: /// MustBeExecutedContextExplorer MustBeExecutedContextExplorer Explorer; + /// A map with knowledge retained in `llvm.assume` instructions. + RetainedKnowledgeMap KnowledgeMap; + /// Getters for analysis. AnalysisGetter &AG; @@ -1710,7 +1726,8 @@ struct IRAttribute : public IRPosition, public Base { /// 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/lib/IR/KnowledgeRetention.cpp b/lib/IR/KnowledgeRetention.cpp index ec6f5712cee..aa83d0fd6df 100644 --- a/lib/IR/KnowledgeRetention.cpp +++ b/lib/IR/KnowledgeRetention.cpp @@ -206,6 +206,8 @@ static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume, 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"); @@ -253,19 +255,19 @@ void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) { if (Key.first == nullptr && Key.second == Attribute::None) continue; if (!BundleHasArguement(Bundles, BOIE_Argument)) { - Result[Key] = {0, 0}; + Result[Key][&Assume] = {0, 0}; continue; } unsigned Val = cast( getValueFromBundleOpInfo(Assume, Bundles, BOIE_Argument)) ->getZExtValue(); auto Lookup = Result.find(Key); - if (Lookup == Result.end()) { - Result[Key] = {Val, Val}; + if (Lookup == Result.end() || !Lookup->second.count(&Assume)) { + Result[Key][&Assume] = {Val, Val}; continue; } - Lookup->second.Min = std::min(Val, Lookup->second.Min); - Lookup->second.Max = std::max(Val, Lookup->second.Max); + Lookup->second[&Assume].Min = std::min(Val, Lookup->second[&Assume].Min); + Lookup->second[&Assume].Max = std::max(Val, Lookup->second[&Assume].Max); } } diff --git a/lib/Transforms/IPO/Attributor.cpp b/lib/Transforms/IPO/Attributor.cpp index dff83243e11..053255ae460 100644 --- a/lib/Transforms/IPO/Attributor.cpp +++ b/lib/Transforms/IPO/Attributor.cpp @@ -677,7 +677,7 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) { } 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) @@ -689,12 +689,16 @@ bool IRPosition::hasAttr(ArrayRef AKs, if (IgnoreSubsumingPositions) break; } + if (A) + for (Attribute::AttrKind AK : AKs) + if (getAttrsFromAssumes(AK, Attrs, *A)) + return true; 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) EquivIRP.getAttrsFromIRAttr(AK, Attrs); @@ -704,6 +708,9 @@ void IRPosition::getAttrs(ArrayRef AKs, if (IgnoreSubsumingPositions) break; } + if (A) + for (Attribute::AttrKind AK : AKs) + getAttrsFromAssumes(AK, Attrs, *A); } bool IRPosition::getAttrsFromIRAttr(Attribute::AttrKind AK, @@ -723,6 +730,31 @@ bool IRPosition::getAttrsFromIRAttr(Attribute::AttrKind AK, 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(); + + const Assume2KnowledgeMap &A2K = + A.getInfoCache().getKnowledgeMap().lookup({&AssociatedValue, AK}); + + // Check if we found any potential assume use, if not we don't need to create + // explorer iterators. + if (A2K.empty()) + return false; + + LLVMContext &Ctx = AssociatedValue.getContext(); + unsigned AttrsSize = Attrs.size(); + MustBeExecutedContextExplorer &Explorer = + A.getInfoCache().getMustBeExecutedContextExplorer(); + auto EIt = Explorer.begin(getCtxI()), EEnd = Explorer.end(getCtxI()); + for (auto &It : A2K) + if (Explorer.findInContextOf(It.first, EIt, EEnd)) + Attrs.push_back(Attribute::get(Ctx, AK, It.second.Max)); + return AttrsSize != Attrs.size(); +} + void IRPosition::verify() { switch (KindOrArgNo) { default: @@ -2057,7 +2089,8 @@ struct AANonNullImpl : AANonNull { /// 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(); @@ -3643,7 +3676,7 @@ struct AADereferenceableImpl : AADereferenceable { 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()); @@ -4394,8 +4427,9 @@ struct AACaptureUseTracker final : public CaptureTracker { /// See CaptureTracker::shouldExplore(...). bool shouldExplore(const Use *U) override { - // Check liveness. - return !A.isAssumedDead(*U, &NoCaptureAA, &IsDeadAA); + // Check liveness and ignore droppable users. + return !U->getUser()->isDroppable() && + !A.isAssumedDead(*U, &NoCaptureAA, &IsDeadAA); } /// Update the state according to \p CapturedInMem, \p CapturedInInt, and @@ -6157,6 +6191,10 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) { if (A.isAssumedDead(*U, this, &LivenessAA)) continue; + // Droppable users, e.g., llvm::assume does not actually perform any action. + if (UserI->isDroppable()) + continue; + // Check if the users of UserI should also be visited. if (followUsersOfUseIn(A, U, UserI)) for (const Use &UserIUse : UserI->uses()) @@ -7414,6 +7452,10 @@ bool Attributor::checkForAllUses(function_ref Pred, LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); continue; } + if (U->getUser()->isDroppable()) { + LLVM_DEBUG(dbgs() << "[Attributor] Droppable user, skip!\n"); + continue; + } bool Follow = false; if (!Pred(*U, Follow)) @@ -8308,11 +8350,18 @@ void Attributor::initializeInformationCache(Function &F) { "New call site/base instruction type needs to be known in the " "Attributor."); break; + case Instruction::Call: + // Calls are interesting but for `llvm.assume` calls we also fill the + // KnowledgeMap as we find them. + if (IntrinsicInst *Assume = dyn_cast(&I)) { + if (Assume->getIntrinsicID() == Intrinsic::assume) + fillMapFromAssume(*Assume, InfoCache.KnowledgeMap); + } + LLVM_FALLTHROUGH; case Instruction::Load: // The alignment of a pointer is interesting for loads. case Instruction::Store: // The alignment of a pointer is interesting for stores. - case Instruction::Call: case Instruction::CallBr: case Instruction::Invoke: case Instruction::CleanupRet: diff --git a/test/Transforms/Attributor/dereferenceable-1.ll b/test/Transforms/Attributor/dereferenceable-1.ll index 87fb7867eab..459e57e5c2e 100644 --- a/test/Transforms/Attributor/dereferenceable-1.ll +++ b/test/Transforms/Attributor/dereferenceable-1.ll @@ -455,5 +455,67 @@ if.end8: ; preds = %if.then5, %if.else6 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 nonnull readnone [[ARG3:%.*]], i8* nocapture nofree readnone dereferenceable_or_null(42) [[ARG4:%.*]]) +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) #6 [ "nonnull"(i8* undef), "dereferenceable"(i8* undef, i64 1), "dereferenceable"(i8* undef, i64 2), "dereferenceable"(i8* undef, i64 101), "dereferenceable_or_null"(i8* undef, i64 31), "dereferenceable_or_null"(i8* undef, i64 42) ] +; ATTRIBUTOR-NEXT: call void @unknown() +; ATTRIBUTOR-NEXT: ret void +; + call void @llvm.assume(i1 true) [ "nonnull"(i8* %arg3), "dereferenceable"(i8* %arg1, i64 1), "dereferenceable"(i8* %arg1, i64 2), "dereferenceable"(i8* %arg1, i64 101), "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: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* undef, i64 101), "dereferenceable"(i8* undef, i64 -2), "dereferenceable_or_null"(i8* undef, i64 31) ] +; 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 +} +define void @nonnull_assume_call(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_call +; ATTRIBUTOR-SAME: (i8* [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* [[ARG3:%.*]], i8* [[ARG4:%.*]]) +; ATTRIBUTOR-NEXT: call void @unknown() +; ATTRIBUTOR-NEXT: [[P:%.*]] = call nonnull dereferenceable(101) i32* @unkown_ptr() +; ATTRIBUTOR-NEXT: call void @unknown_use32(i32* nonnull dereferenceable(101) [[P]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(42) [[ARG4]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull [[ARG3]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(31) [[ARG2]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(2) [[ARG1]]) +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i8* [[ARG3]]), "dereferenceable"(i8* [[ARG1]], i64 1), "dereferenceable"(i8* [[ARG1]], i64 2), "dereferenceable"(i32* [[P]], i64 101), "dereferenceable_or_null"(i8* [[ARG2]], i64 31), "dereferenceable_or_null"(i8* [[ARG4]], i64 42) ] +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(2) [[ARG1]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(31) [[ARG2]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull [[ARG3]]) +; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(42) [[ARG4]]) +; ATTRIBUTOR-NEXT: call void @unknown_use32(i32* nonnull dereferenceable(101) [[P]]) +; ATTRIBUTOR-NEXT: call void @unknown() +; ATTRIBUTOR-NEXT: ret void +; + call void @unknown() + %p = call i32* @unkown_ptr() + call void @unknown_use32(i32* %p) + call void @unknown_use8(i8* %arg4) + call void @unknown_use8(i8* %arg3) + call void @unknown_use8(i8* %arg2) + call void @unknown_use8(i8* %arg1) + call void @llvm.assume(i1 true) [ "nonnull"(i8* %arg3), "dereferenceable"(i8* %arg1, i64 1), "dereferenceable"(i8* %arg1, i64 2), "dereferenceable"(i32* %p, i64 101), "dereferenceable_or_null"(i8* %arg2, i64 31), "dereferenceable_or_null"(i8* %arg4, i64 42)] + call void @unknown_use8(i8* %arg1) + call void @unknown_use8(i8* %arg2) + call void @unknown_use8(i8* %arg3) + call void @unknown_use8(i8* %arg4) + call void @unknown_use32(i32* %p) + call void @unknown() + ret void +} +declare void @unknown_use8(i8*) willreturn nounwind +declare void @unknown_use32(i32*) willreturn nounwind +declare void @llvm.assume(i1) + !0 = !{i64 10, i64 100} diff --git a/test/Transforms/Attributor/nofree.ll b/test/Transforms/Attributor/nofree.ll index 07f0a4eb0ef..a5af2435016 100644 --- a/test/Transforms/Attributor/nofree.ll +++ b/test/Transforms/Attributor/nofree.ll @@ -247,6 +247,64 @@ define void @test14(i8* nocapture %0, i8* nocapture %1) { ret void } +; UTC_ARGS: --enable + +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 @llvm.assume(i1 true) #11 [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG3]]) ] +; ATTRIBUTOR-NEXT: call void @unknown(i8* nofree [[ARG1]], i8* [[ARG2]], i8* nofree [[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: call void @llvm.assume(i1 true) [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG3]]) ] +; 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 +} +define void @nonnull_assume_call(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) { +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_call +; 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: call void @use_i8_ptr(i8* noalias readnone [[ARG1]]) +; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias readnone [[ARG2]]) +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG3]]) ] +; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias nofree readnone [[ARG3]]) +; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias readnone [[ARG4]]) +; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nofree readnone [[ARG1]]) +; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias readnone [[ARG2]]) +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG4]]) ] +; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nofree readnone [[ARG3]]) +; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nofree readnone [[ARG4]]) +; ATTRIBUTOR-NEXT: ret void +; + call void @unknown(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) + call void @use_i8_ptr(i8* %arg1) + call void @use_i8_ptr(i8* %arg2) + call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg3)] + call void @use_i8_ptr(i8* %arg3) + call void @use_i8_ptr(i8* %arg4) + call void @use_i8_ptr_ret(i8* %arg1) + call void @use_i8_ptr_ret(i8* %arg2) + call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg4)] + call void @use_i8_ptr_ret(i8* %arg3) + call void @use_i8_ptr_ret(i8* %arg4) + ret void +} +declare void @llvm.assume(i1) +declare void @unknown(i8*, i8*, i8*, i8*) +declare void @use_i8_ptr(i8* nocapture readnone) nounwind +declare void @use_i8_ptr_ret(i8* nocapture readnone) nounwind willreturn + declare noalias i8* @malloc(i64) attributes #0 = { nounwind uwtable noinline } diff --git a/test/Transforms/Attributor/nonnull.ll b/test/Transforms/Attributor/nonnull.ll index 2aae7aa2770..06ec6c2c585 100644 --- a/test/Transforms/Attributor/nonnull.ll +++ b/test/Transforms/Attributor/nonnull.ll @@ -167,7 +167,6 @@ define void @test13_helper() { tail call void @test13(i8* %nonnullptr, i8* %maybenullptr, i8* %nonnullptr) ret void } -declare void @use_i8_ptr(i8* nofree nocapture readnone) nounwind define internal void @test13(i8* %a, i8* %b, i8* %c) { ; ATTRIBUTOR: define internal void @test13(i8* noalias nocapture nofree nonnull readnone %a, i8* noalias nocapture nofree readnone %b, i8* noalias nocapture nofree readnone %c) call void @use_i8_ptr(i8* %a) @@ -839,5 +838,44 @@ define i8* @mybasename(i8* nofree readonly %str) { 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: call void @llvm.assume(i1 true) #11 [ "nonnull"(i8* [[ARG]]) ] +; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias 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 void @use_i8_ptr(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: call void @use_i8_ptr(i8* noalias nocapture nofree readnone [[ARG]]) +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i8* [[ARG]]) ] +; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias nocapture nofree nonnull readnone [[ARG]]) +; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = call i8* @unknown() +; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nocapture nofree nonnull readnone [[ARG]]) +; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i8* [[ARG]]) ] +; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nocapture nofree nonnull readnone [[ARG]]) +; ATTRIBUTOR-NEXT: ret void +; + call i8* @unknown() + call void @use_i8_ptr(i8* %arg) + call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)] + call void @use_i8_ptr(i8* %arg) + call i8* @unknown() + call void @use_i8_ptr_ret(i8* %arg) + call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)] + call void @use_i8_ptr_ret(i8* %arg) + ret void +} +declare void @use_i8_ptr(i8* nofree nocapture readnone) nounwind +declare void @use_i8_ptr_ret(i8* nofree nocapture readnone) nounwind willreturn + attributes #0 = { "null-pointer-is-valid"="true" } attributes #1 = { nounwind willreturn} diff --git a/unittests/IR/KnowledgeRetentionTest.cpp b/unittests/IR/KnowledgeRetentionTest.cpp index 62caeebba89..aca801d2b29 100644 --- a/unittests/IR/KnowledgeRetentionTest.cpp +++ b/unittests/IR/KnowledgeRetentionTest.cpp @@ -22,6 +22,10 @@ using namespace llvm; extern cl::opt ShouldPreserveAllAttributes; extern cl::opt EnableKnowledgeRetention; +static IntrinsicInst *buildAssumeFromInst(Instruction *I) { + return cast_or_null(BuildAssumeFromInst(I)); +} + static void RunTest( StringRef Head, StringRef Tail, std::vector>> @@ -40,7 +44,7 @@ static void RunTest( } } -bool hasMatchesExactlyAttributes(CallInst *Assume, Value *WasOn, +bool hasMatchesExactlyAttributes(IntrinsicInst *Assume, Value *WasOn, StringRef AttrToMatch) { Regex Reg(AttrToMatch); SmallVector Matches; @@ -56,7 +60,7 @@ bool hasMatchesExactlyAttributes(CallInst *Assume, Value *WasOn, return true; } -bool hasTheRightValue(CallInst *Assume, Value *WasOn, +bool hasTheRightValue(IntrinsicInst *Assume, Value *WasOn, Attribute::AttrKind Kind, unsigned Value, bool Both, AssumeQuery AQ = AssumeQuery::Highest) { if (!Both) { @@ -97,7 +101,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align " "8 noalias %P1)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0), "(nonnull|align|dereferenceable)")); @@ -117,7 +121,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { "dereferenceable(4) " "%P, i32* nonnull align 16 dereferenceable(12) %P)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0), "(nonnull|align|dereferenceable)")); @@ -149,7 +153,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { Tests.push_back(std::make_pair( "call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) { ShouldPreserveAllAttributes.setValue(true); - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); ASSERT_TRUE(hasMatchesExactlyAttributes( Assume, nullptr, @@ -159,7 +163,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { })); Tests.push_back( std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) { - CallInst *Assume = cast(I); + IntrinsicInst *Assume = cast(I); ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, nullptr, "")); })); Tests.push_back(std::make_pair( @@ -169,7 +173,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { "dereferenceable(4) " "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); ASSERT_TRUE(hasMatchesExactlyAttributes( Assume, I->getOperand(0), @@ -205,7 +209,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { "dereferenceable(4) " "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); I->getOperand(1)->dropDroppableUses(); I->getOperand(2)->dropDroppableUses(); @@ -228,7 +232,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) { "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align " "8 noalias %P1)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); Value *New = I->getFunction()->getArg(3); Value *Old = I->getOperand(0); @@ -260,11 +264,11 @@ static bool FindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn, return true; } -static bool MapHasRightValue(RetainedKnowledgeMap &Map, - RetainedKnowledgeKey Key, MinMax MM) { +static bool MapHasRightValue(RetainedKnowledgeMap &Map, IntrinsicInst *II, + RetainedKnowledgeKey Key, MinMax MM) { auto LookupIt = Map.find(Key); - return (LookupIt != Map.end()) && (LookupIt->second.Min == MM.Min) && - (LookupIt->second.Max == MM.Max); + return (LookupIt != Map.end()) && (LookupIt->second[II].Min == MM.Min) && + (LookupIt->second[II].Max == MM.Max); } TEST(AssumeQueryAPI, fillMapFromAssume) { @@ -284,7 +288,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align " "8 noalias %P1)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); RetainedKnowledgeMap Map; @@ -294,10 +298,10 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1), "(align)")); ASSERT_TRUE(MapHasRightValue( - Map, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16})); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment}, + Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16})); + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment}, {4, 4})); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment}, + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment}, {4, 4})); })); Tests.push_back(std::make_pair( @@ -307,7 +311,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { "dereferenceable(4) " "%P, i32* nonnull align 16 dereferenceable(12) %P)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); RetainedKnowledgeMap Map; @@ -322,14 +326,14 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3), "(nonnull|align|dereferenceable)")); ASSERT_TRUE(MapHasRightValue( - Map, {I->getOperand(0), Attribute::Dereferenceable}, {4, 48})); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment}, + Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {4, 48})); + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment}, {8, 64})); })); Tests.push_back(std::make_pair( "call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) { ShouldPreserveAllAttributes.setValue(true); - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); RetainedKnowledgeMap Map; @@ -342,7 +346,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { Tests.push_back( std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) { RetainedKnowledgeMap Map; - fillMapFromAssume(*cast(I), Map); + fillMapFromAssume(*cast(I), Map); ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, "")); ASSERT_TRUE(Map.empty()); @@ -354,7 +358,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { "dereferenceable(4) " "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); RetainedKnowledgeMap Map; @@ -368,22 +372,22 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { "(align|dereferenceable)")); ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3), "(nonnull|align|dereferenceable)")); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment}, + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment}, {32, 32})); ASSERT_TRUE(MapHasRightValue( - Map, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48})); + Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48})); ASSERT_TRUE(MapHasRightValue( - Map, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28})); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(1), Attribute::Alignment}, + Map, Assume, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28})); + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(1), Attribute::Alignment}, {8, 8})); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(2), Attribute::Alignment}, + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(2), Attribute::Alignment}, {64, 64})); ASSERT_TRUE(MapHasRightValue( - Map, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4})); - ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(3), Attribute::Alignment}, + Map, Assume, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4})); + ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(3), Attribute::Alignment}, {16, 16})); ASSERT_TRUE(MapHasRightValue( - Map, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12})); + Map, Assume, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12})); })); /// Keep this test last as it modifies the function. @@ -391,7 +395,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) { "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align " "8 noalias %P1)\n", [](Instruction *I) { - CallInst *Assume = BuildAssumeFromInst(I); + IntrinsicInst *Assume = buildAssumeFromInst(I); Assume->insertBefore(I); RetainedKnowledgeMap Map; @@ -475,12 +479,12 @@ static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount, OpBundle.push_back(OperandBundleDef{ss.str().c_str(), std::move(Args)}); } - Instruction *Assume = - CallInst::Create(FnAssume, ArrayRef({ConstantInt::getTrue(C)}), - std::move(OpBundle)); + auto *Assume = cast(IntrinsicInst::Create( + FnAssume, ArrayRef({ConstantInt::getTrue(C)}), + std::move(OpBundle))); Assume->insertBefore(&F->begin()->front()); RetainedKnowledgeMap Map; - fillMapFromAssume(*cast(Assume), Map); + fillMapFromAssume(*Assume, Map); for (int i = 0; i < (Size * 2); i++) { if (!HasArg[i]) continue; @@ -488,7 +492,7 @@ static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount, getKnowledgeFromUseInAssume(&*ShuffledArgs[i]->use_begin()); auto LookupIt = Map.find(RetainedKnowledgeKey{K.WasOn, K.AttrKind}); ASSERT_TRUE(LookupIt != Map.end()); - MinMax MM = LookupIt->second; + MinMax MM = LookupIt->second[Assume]; ASSERT_TRUE(MM.Min == MM.Max); ASSERT_TRUE(MM.Min == K.ArgValue); }