[llvm] 5699d08 - [Attributor] Use knowledge retained in llvm.assume (operand bundles)

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 24 13:34:48 PDT 2020


Author: Johannes Doerfert
Date: 2020-03-24T15:33:40-05:00
New Revision: 5699d08b79afdaed167fb202a4e22d73d6f1bf48

URL: https://github.com/llvm/llvm-project/commit/5699d08b79afdaed167fb202a4e22d73d6f1bf48
DIFF: https://github.com/llvm/llvm-project/commit/5699d08b79afdaed167fb202a4e22d73d6f1bf48.diff

LOG: [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

Added: 
    

Modified: 
    llvm/include/llvm/IR/KnowledgeRetention.h
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/IR/KnowledgeRetention.cpp
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/test/Transforms/Attributor/dereferenceable-1.ll
    llvm/test/Transforms/Attributor/nofree.ll
    llvm/test/Transforms/Attributor/nonnull.ll
    llvm/unittests/IR/KnowledgeRetentionTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/KnowledgeRetention.h b/llvm/include/llvm/IR/KnowledgeRetention.h
index f5c2d554c230..a7de7cd16b66 100644
--- a/llvm/include/llvm/IR/KnowledgeRetention.h
+++ b/llvm/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<RetainedKnowledgeKey, MinMax>;
+/// 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<IntrinsicInst *, MinMax>;
+
+using RetainedKnowledgeMap =
+    DenseMap<RetainedKnowledgeKey, Assume2KnowledgeMap>;
 
 /// 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/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index a229f55e1a84..2113197c067d 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/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<Attribute::AttrKind> 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<Attribute::AttrKind> AKs,
                 SmallVectorImpl<Attribute> &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<Attribute::AttrKind> AKs) const {
@@ -463,6 +467,12 @@ struct IRPosition {
   bool getAttrsFromIRAttr(Attribute::AttrKind AK,
                           SmallVectorImpl<Attribute> &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<Attribute> &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<const Function *, OpcodeInstMapTy>;
@@ -627,6 +640,9 @@ struct InformationCache {
   /// 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<UndefValue>(IRP.getAssociatedValue()) || hasAttr(getAttrKind())) {
+    if (isa<UndefValue>(IRP.getAssociatedValue()) ||
+        hasAttr(getAttrKind(), /* IgnoreSubsumingPositions */ false, &A)) {
       this->getState().indicateOptimisticFixpoint();
       return;
     }

diff  --git a/llvm/lib/IR/KnowledgeRetention.cpp b/llvm/lib/IR/KnowledgeRetention.cpp
index ec6f5712cee5..aa83d0fd6df8 100644
--- a/llvm/lib/IR/KnowledgeRetention.cpp
+++ b/llvm/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<IntrinsicInst>(AssumeCI) &&
+         "this function is intended to be used on llvm.assume");
   IntrinsicInst &Assume = cast<IntrinsicInst>(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<ConstantInt>(
                        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/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index dff83243e11c..053255ae4602 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -677,7 +677,7 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
 }
 
 bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs,
-                         bool IgnoreSubsumingPositions) const {
+                         bool IgnoreSubsumingPositions, Attributor *A) const {
   SmallVector<Attribute, 4> Attrs;
   for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) {
     for (Attribute::AttrKind AK : AKs)
@@ -689,12 +689,16 @@ bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs,
     if (IgnoreSubsumingPositions)
       break;
   }
+  if (A)
+    for (Attribute::AttrKind AK : AKs)
+      if (getAttrsFromAssumes(AK, Attrs, *A))
+        return true;
   return false;
 }
 
 void IRPosition::getAttrs(ArrayRef<Attribute::AttrKind> AKs,
                           SmallVectorImpl<Attribute> &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<Attribute::AttrKind> 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<Attribute> &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<ConstantPointerNull>(getAssociatedValue()))
       indicatePessimisticFixpoint();
@@ -3643,7 +3676,7 @@ struct AADereferenceableImpl : AADereferenceable {
   void initialize(Attributor &A) override {
     SmallVector<Attribute, 4> 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<bool(const Use &, bool &)> 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<IntrinsicInst>(&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/llvm/test/Transforms/Attributor/dereferenceable-1.ll b/llvm/test/Transforms/Attributor/dereferenceable-1.ll
index 87fb7867eabd..459e57e5c2ef 100644
--- a/llvm/test/Transforms/Attributor/dereferenceable-1.ll
+++ b/llvm/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/llvm/test/Transforms/Attributor/nofree.ll b/llvm/test/Transforms/Attributor/nofree.ll
index 07f0a4eb0ef2..a5af24350164 100644
--- a/llvm/test/Transforms/Attributor/nofree.ll
+++ b/llvm/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/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll
index 2aae7aa27706..06ec6c2c585f 100644
--- a/llvm/test/Transforms/Attributor/nonnull.ll
+++ b/llvm/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/llvm/unittests/IR/KnowledgeRetentionTest.cpp b/llvm/unittests/IR/KnowledgeRetentionTest.cpp
index 62caeebba895..aca801d2b296 100644
--- a/llvm/unittests/IR/KnowledgeRetentionTest.cpp
+++ b/llvm/unittests/IR/KnowledgeRetentionTest.cpp
@@ -22,6 +22,10 @@ using namespace llvm;
 extern cl::opt<bool> ShouldPreserveAllAttributes;
 extern cl::opt<bool> EnableKnowledgeRetention;
 
+static IntrinsicInst *buildAssumeFromInst(Instruction *I) {
+  return cast_or_null<IntrinsicInst>(BuildAssumeFromInst(I));
+}
+
 static void RunTest(
     StringRef Head, StringRef Tail,
     std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
@@ -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<StringRef, 1> 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<CallInst>(I);
+        IntrinsicInst *Assume = cast<IntrinsicInst>(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<CallInst>(I), Map);
+        fillMapFromAssume(*cast<IntrinsicInst>(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<Value *>({ConstantInt::getTrue(C)}),
-                       std::move(OpBundle));
+  auto *Assume = cast<IntrinsicInst>(IntrinsicInst::Create(
+      FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}),
+      std::move(OpBundle)));
   Assume->insertBefore(&F->begin()->front());
   RetainedKnowledgeMap Map;
-  fillMapFromAssume(*cast<CallInst>(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);
   }


        


More information about the llvm-commits mailing list