[llvm] db86f45 - [Attributor] Introduce assumption accesses in AAPointerInfo

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 7 15:39:48 PST 2022


Author: Johannes Doerfert
Date: 2022-12-07T15:39:07-08:00
New Revision: db86f45478785c373caaf35890f8aa0ae8fb3dee

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

LOG: [Attributor] Introduce assumption accesses in AAPointerInfo

Assumptions can help us reason about memory content. This patch teaches
AAPointerInfo to reason about memory assumptions of the following form:

```
%x = load %ptr
... code not writing memory, may include branches ...
%c = %x == %val
... code not writing memory, may include branches ...
llvm.assume(%c)
```

Assumption accesses are recognized from the involved load (%x above).

Assumption accesses are treated special and neither as ordinary read or
write. We use read encoding with an extra flag. Reads are not impacting
other reads or writes. Writes could do that. We don't want assumptions
to impact other writes as they themselves only confirm a value, not
write it. So the "other" write might be required as the assumption only
confirms the effect of that write.

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/lib/Transforms/IPO/AttributorAttributes.cpp
    llvm/test/Transforms/Attributor/value-simplify-assume.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index 10450157452bc..54a4a430bc8b7 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -4986,7 +4986,7 @@ struct AAPointerInfo : public AbstractAttribute {
   AAPointerInfo(const IRPosition &IRP) : AbstractAttribute(IRP) {}
 
   enum AccessKind {
-    // First two bits to distinguish may and must accesses
+    // First two bits to distinguish may and must accesses.
     AK_MUST = 1 << 0,
     AK_MAY = 1 << 1,
 
@@ -4995,6 +4995,11 @@ struct AAPointerInfo : public AbstractAttribute {
     AK_W = 1 << 3,
     AK_RW = AK_R | AK_W,
 
+    // One special case for assumptions about memory content. These
+    // are neither reads nor writes. They are however always modeled
+    // as read to avoid using them for write removal.
+    AK_ASSUMPTION = (1 << 4) | AK_MUST,
+
     // Helper for easy access.
     AK_MAY_READ = AK_MAY | AK_R,
     AK_MAY_WRITE = AK_MAY | AK_W,
@@ -5054,6 +5059,8 @@ struct AAPointerInfo : public AbstractAttribute {
     void verify() {
       assert(isMustAccess() + isMayAccess() == 1 &&
              "Expect must or may access, not both.");
+      assert(isAssumption() + isWrite() <= 1 &&
+             "Expect assumption access or write access, never both.");
     }
 
     /// Return the access kind.
@@ -5065,6 +5072,12 @@ struct AAPointerInfo : public AbstractAttribute {
     /// Return true if this is a write access.
     bool isWrite() const { return Kind & AK_W; }
 
+    /// Return true if this is a write access.
+    bool isWriteOrAssumption() const { return isWrite() | isAssumption(); }
+
+    /// Return true if this is an assumption access.
+    bool isAssumption() const { return Kind == AK_ASSUMPTION; }
+
     bool isMustAccess() const { return Kind & AK_MUST; }
     bool isMayAccess() const { return Kind & AK_MAY; }
 

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 00be64e6ebbbe..cac974ad19a17 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -405,7 +405,7 @@ static bool getPotentialCopiesOfMemoryValue(
     };
 
     auto CheckAccess = [&](const AAPointerInfo::Access &Acc, bool IsExact) {
-      if ((IsLoad && !Acc.isWrite()) || (!IsLoad && !Acc.isRead()))
+      if ((IsLoad && !Acc.isWriteOrAssumption()) || (!IsLoad && !Acc.isRead()))
         return true;
       if (IsLoad && Acc.isWrittenValueYetUndetermined())
         return true;

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 5b0f97fff6777..531b287550b1a 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -1072,7 +1072,7 @@ struct AAPointerInfoImpl
     }
 
     auto AccessCB = [&](const Access &Acc, bool Exact) {
-      if ((!FindInterferingWrites || !Acc.isWrite()) &&
+      if ((!FindInterferingWrites || !Acc.isWriteOrAssumption()) &&
           (!FindInterferingReads || !Acc.isRead()))
         return true;
 
@@ -1105,7 +1105,7 @@ struct AAPointerInfoImpl
     // the worst case quadratic as we are looking for another write that will
     // hide the effect of this one.
     auto CanSkipAccess = [&](const Access &Acc, bool Exact) {
-      if ((!Acc.isWrite() ||
+      if ((!Acc.isWriteOrAssumption() ||
            !AA::isPotentiallyReachable(A, *Acc.getLocalInst(), I, QueryingAA,
                                        IsLiveInCalleeCB)) &&
           (!Acc.isRead() ||
@@ -1375,7 +1375,87 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl {
           AK = AccessKind(AK | AccessKind::AK_MUST);
         else
           AK = AccessKind(AK | AccessKind::AK_MAY);
-        return handleAccess(A, *LoadI, *CurPtr, /* Content */ nullptr, AK,
+        if (!handleAccess(A, *LoadI, *CurPtr, /* Content */ nullptr, AK,
+                          OffsetInfoMap[CurPtr].Offset, Changed,
+                          LoadI->getType()))
+          return false;
+
+        auto IsAssumption = [](Instruction &I) {
+          if (auto *II = dyn_cast<IntrinsicInst>(&I))
+            return II->isAssumeLikeIntrinsic();
+          return false;
+        };
+
+        auto IsImpactedInRange = [&](Instruction *FromI, Instruction *ToI) {
+          // Check if the assumption and the load are executed together without
+          // memory modification.
+          do {
+            if (FromI->mayWriteToMemory() && !IsAssumption(*FromI))
+              return true;
+            FromI = FromI->getNextNonDebugInstruction();
+          } while (FromI && FromI != ToI);
+          return false;
+        };
+
+        BasicBlock *BB = LoadI->getParent();
+        auto IsValidAssume = [&](IntrinsicInst &IntrI) {
+          if (IntrI.getIntrinsicID() != Intrinsic::assume)
+            return false;
+          BasicBlock *IntrBB = IntrI.getParent();
+          if (IntrI.getParent() == BB) {
+            if (IsImpactedInRange(LoadI->getNextNonDebugInstruction(), &IntrI))
+              return false;
+          } else {
+            auto PredIt = pred_begin(IntrBB);
+            if ((*PredIt) != BB)
+              return false;
+            if (++PredIt != pred_end(IntrBB))
+              return false;
+            for (auto *SuccBB : successors(BB)) {
+              if (SuccBB == IntrBB)
+                continue;
+              if (isa<UnreachableInst>(SuccBB->getTerminator()))
+                continue;
+              return false;
+            }
+            if (IsImpactedInRange(LoadI->getNextNonDebugInstruction(),
+                                  BB->getTerminator()))
+              return false;
+            if (IsImpactedInRange(&IntrBB->front(), &IntrI))
+              return false;
+          }
+          return true;
+        };
+
+        std::pair<Value *, IntrinsicInst *> Assumption;
+        for (const Use &LoadU : LoadI->uses()) {
+          if (auto *CmpI = dyn_cast<CmpInst>(LoadU.getUser())) {
+            if (!CmpI->isEquality() || !CmpI->isTrueWhenEqual())
+              continue;
+            for (const Use &CmpU : CmpI->uses()) {
+              if (auto *IntrI = dyn_cast<IntrinsicInst>(CmpU.getUser())) {
+                if (!IsValidAssume(*IntrI))
+                  continue;
+                int Idx = CmpI->getOperandUse(0) == LoadU;
+                Assumption = {CmpI->getOperand(Idx), IntrI};
+                break;
+              }
+            }
+          }
+          if (Assumption.first)
+            break;
+        }
+
+        // Check if we found an assumption associated with this load.
+        if (!Assumption.first || !Assumption.second)
+          return true;
+
+        LLVM_DEBUG(dbgs() << "[AAPointerInfo] Assumption found "
+                          << *Assumption.second << ": " << *LoadI
+                          << " == " << *Assumption.first << "\n");
+
+        return handleAccess(A, *Assumption.second, *CurPtr, Assumption.first,
+                            AccessKind::AK_ASSUMPTION,
                             OffsetInfoMap[CurPtr].Offset, Changed,
                             LoadI->getType());
       }
@@ -1446,6 +1526,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl {
       return false;
     };
     auto EquivalentUseCB = [&](const Use &OldU, const Use &NewU) {
+      assert(OffsetInfoMap.count(OldU) && "Old use should be known already!");
       if (OffsetInfoMap.count(NewU)) {
         LLVM_DEBUG({
           if (!(OffsetInfoMap[NewU] == OffsetInfoMap[OldU])) {

diff  --git a/llvm/test/Transforms/Attributor/value-simplify-assume.ll b/llvm/test/Transforms/Attributor/value-simplify-assume.ll
index a44a5f618d4c8..37c61bc1c5975 100644
--- a/llvm/test/Transforms/Attributor/value-simplify-assume.ll
+++ b/llvm/test/Transforms/Attributor/value-simplify-assume.ll
@@ -2,10 +2,17 @@
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
+ at Gstatic_int1 = internal global i32 zeroinitializer, align 4
+ at Gstatic_int2 = internal global i32 zeroinitializer, align 4
+
 declare void @llvm.assume(i1)
 declare void @useI1p(i1*)
 declare void @unknown()
 
+;.
+; CHECK: @[[GSTATIC_INT1:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0, align 4
+; CHECK: @[[GSTATIC_INT2:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0, align 4
+;.
 define i1 @readI1p(i1* %p) {
 ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: read)
 ; CHECK-LABEL: define {{[^@]+}}@readI1p
@@ -40,13 +47,13 @@ define i1 @drop_assume_1c_nr() norecurse {
 ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
 ; TUNIT-LABEL: define {{[^@]+}}@drop_assume_1c_nr
 ; TUNIT-SAME: () #[[ATTR3:[0-9]+]] {
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR4:[0-9]+]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR6:[0-9]+]]
 ; TUNIT-NEXT:    ret i1 true
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
 ; CGSCC-LABEL: define {{[^@]+}}@drop_assume_1c_nr
 ; CGSCC-SAME: () #[[ATTR3:[0-9]+]] {
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR5:[0-9]+]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR7:[0-9]+]]
 ; CGSCC-NEXT:    ret i1 true
 ;
   %stack = alloca i1
@@ -80,13 +87,14 @@ define i1 @keep_assume_2c_nr() norecurse {
 }
 
 define i1 @keep_assume_3c_nr() norecurse {
+;
 ; TUNIT: Function Attrs: norecurse
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_3c_nr
 ; TUNIT-SAME: () #[[ATTR2]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
@@ -96,7 +104,7 @@ define i1 @keep_assume_3c_nr() norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
@@ -108,13 +116,14 @@ define i1 @keep_assume_3c_nr() norecurse {
   ret i1 %l
 }
 define i1 @keep_assume_4c_nr() norecurse {
+;
 ; TUNIT: Function Attrs: norecurse
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_4c_nr
 ; TUNIT-SAME: () #[[ATTR2]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L4]]
 ;
@@ -124,7 +133,7 @@ define i1 @keep_assume_4c_nr() norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L4]]
 ;
@@ -161,7 +170,7 @@ define i1 @drop_assume_1_nr(i1 %arg) norecurse {
 ; TUNIT-SAME: (i1 returned [[ARG:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR6]]
 ; TUNIT-NEXT:    ret i1 [[ARG]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -169,7 +178,7 @@ define i1 @drop_assume_1_nr(i1 %arg) norecurse {
 ; CGSCC-SAME: (i1 returned [[ARG:%.*]]) #[[ATTR3]] {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[ARG]]
 ;
   %stack = alloca i1
@@ -203,13 +212,14 @@ define i1 @keep_assume_2_nr(i1 %arg) norecurse {
 }
 
 define i1 @keep_assume_3_nr(i1 %arg) norecurse {
+;
 ; TUNIT: Function Attrs: norecurse
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_3_nr
 ; TUNIT-SAME: (i1 [[ARG:%.*]]) #[[ATTR2]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
@@ -219,7 +229,7 @@ define i1 @keep_assume_3_nr(i1 %arg) norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
@@ -232,13 +242,14 @@ define i1 @keep_assume_3_nr(i1 %arg) norecurse {
 }
 
 define i1 @keep_assume_4_nr(i1 %arg) norecurse {
+;
 ; TUNIT: Function Attrs: norecurse
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_4_nr
 ; TUNIT-SAME: (i1 [[ARG:%.*]]) #[[ATTR2]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
@@ -248,7 +259,7 @@ define i1 @keep_assume_4_nr(i1 %arg) norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
@@ -266,7 +277,7 @@ define i1 @assume_1_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-SAME: (i1 returned [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    br label [[M:%.*]]
@@ -280,7 +291,7 @@ define i1 @assume_1_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-SAME: (i1 returned [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    br label [[M:%.*]]
@@ -311,7 +322,7 @@ define void @assume_1b_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    br label [[M:%.*]]
@@ -326,7 +337,7 @@ define void @assume_1b_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    br label [[M:%.*]]
@@ -365,7 +376,7 @@ define i1 @assume_2_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -382,7 +393,7 @@ define i1 @assume_2_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
   %stack = alloca i1
@@ -415,7 +426,7 @@ define void @assume_2b_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    ret void
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -432,7 +443,7 @@ define void @assume_2b_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret void
 ;
   %stack = alloca i1
@@ -457,7 +468,7 @@ define i1 @assume_3_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -466,7 +477,7 @@ define i1 @assume_3_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5:[0-9]+]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7:[0-9]+]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -475,7 +486,7 @@ define i1 @assume_3_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -484,7 +495,7 @@ define i1 @assume_3_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -518,8 +529,8 @@ define i1 @assume_4_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -536,8 +547,8 @@ define i1 @assume_4_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -563,22 +574,22 @@ define i1 @assume_5_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M:%.*]]
 ; TUNIT:       f:
 ; TUNIT-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -587,22 +598,22 @@ define i1 @assume_5_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M:%.*]]
 ; CGSCC:       f:
 ; CGSCC-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -634,22 +645,22 @@ define i1 @assume_5c_nr(i1 %cond) norecurse {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M:%.*]]
 ; TUNIT:       f:
 ; TUNIT-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -658,22 +669,22 @@ define i1 @assume_5c_nr(i1 %cond) norecurse {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M:%.*]]
 ; CGSCC:       f:
 ; CGSCC-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -720,13 +731,13 @@ define i1 @drop_assume_1c() {
 ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
 ; TUNIT-LABEL: define {{[^@]+}}@drop_assume_1c
 ; TUNIT-SAME: () #[[ATTR3]] {
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR6]]
 ; TUNIT-NEXT:    ret i1 true
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
 ; CGSCC-LABEL: define {{[^@]+}}@drop_assume_1c
 ; CGSCC-SAME: () #[[ATTR3]] {
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 true
 ;
   %stack = alloca i1
@@ -758,11 +769,12 @@ define i1 @keep_assume_2c() {
 }
 
 define i1 @keep_assume_3c() {
+;
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_3c() {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
@@ -770,7 +782,7 @@ define i1 @keep_assume_3c() {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
@@ -782,11 +794,12 @@ define i1 @keep_assume_3c() {
   ret i1 %l
 }
 define i1 @keep_assume_4c() {
+;
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_4c() {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L4]]
 ;
@@ -794,7 +807,7 @@ define i1 @keep_assume_4c() {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L4]]
 ;
@@ -830,7 +843,7 @@ define i1 @drop_assume_1(i1 %arg) {
 ; TUNIT-SAME: (i1 returned [[ARG:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR6]]
 ; TUNIT-NEXT:    ret i1 [[ARG]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -838,7 +851,7 @@ define i1 @drop_assume_1(i1 %arg) {
 ; CGSCC-SAME: (i1 returned [[ARG:%.*]]) #[[ATTR3]] {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[ARG]]
 ;
   %stack = alloca i1
@@ -871,12 +884,13 @@ define i1 @keep_assume_2(i1 %arg) {
 }
 
 define i1 @keep_assume_3(i1 %arg) {
+;
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_3
 ; TUNIT-SAME: (i1 [[ARG:%.*]]) {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
@@ -885,7 +899,7 @@ define i1 @keep_assume_3(i1 %arg) {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
@@ -898,12 +912,13 @@ define i1 @keep_assume_3(i1 %arg) {
 }
 
 define i1 @keep_assume_4(i1 %arg) {
+;
 ; TUNIT-LABEL: define {{[^@]+}}@keep_assume_4
 ; TUNIT-SAME: (i1 [[ARG:%.*]]) {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
@@ -912,7 +927,7 @@ define i1 @keep_assume_4(i1 %arg) {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    call void @useI1p(i1* noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
@@ -930,7 +945,7 @@ define i1 @assume_1(i1 %arg, i1 %cond) {
 ; TUNIT-SAME: (i1 returned [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    br label [[M:%.*]]
@@ -944,7 +959,7 @@ define i1 @assume_1(i1 %arg, i1 %cond) {
 ; CGSCC-SAME: (i1 returned [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    br label [[M:%.*]]
@@ -975,7 +990,7 @@ define void @assume_1b(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    br label [[M:%.*]]
@@ -990,7 +1005,7 @@ define void @assume_1b(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    br label [[M:%.*]]
@@ -1029,7 +1044,7 @@ define i1 @assume_2(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    ret i1 [[L]]
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -1046,7 +1061,7 @@ define i1 @assume_2(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[L]]
 ;
   %stack = alloca i1
@@ -1079,7 +1094,7 @@ define void @assume_2b(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    ret void
 ;
 ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -1096,7 +1111,7 @@ define void @assume_2b(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret void
 ;
   %stack = alloca i1
@@ -1121,7 +1136,7 @@ define i1 @assume_3(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1130,7 +1145,7 @@ define i1 @assume_3(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -1139,7 +1154,7 @@ define i1 @assume_3(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1148,7 +1163,7 @@ define i1 @assume_3(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -1182,8 +1197,8 @@ define i1 @assume_4(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -1200,8 +1215,8 @@ define i1 @assume_4(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -1227,22 +1242,22 @@ define i1 @assume_5(i1 %arg, i1 %cond) {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M:%.*]]
 ; TUNIT:       f:
 ; TUNIT-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -1251,22 +1266,22 @@ define i1 @assume_5(i1 %arg, i1 %cond) {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M:%.*]]
 ; CGSCC:       f:
 ; CGSCC-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -1298,22 +1313,22 @@ define i1 @assume_5c(i1 %cond) {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M:%.*]]
 ; TUNIT:       f:
 ; TUNIT-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; TUNIT-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR4]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br label [[M]]
 ; TUNIT:       m:
 ; TUNIT-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; TUNIT-NEXT:    ret i1 [[R]]
 ;
 ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
@@ -1322,22 +1337,22 @@ define i1 @assume_5c(i1 %cond) {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L2:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L2]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M:%.*]]
 ; CGSCC:       f:
 ; CGSCC-NEXT:    store i1 false, i1* [[STACK]], align 1
 ; CGSCC-NEXT:    [[L3:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L3]]) #[[ATTR7]]
 ; CGSCC-NEXT:    br label [[M]]
 ; CGSCC:       m:
 ; CGSCC-NEXT:    [[L4:%.*]] = load i1, i1* [[STACK]], align 1
-; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR5]]
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[R:%.*]] = call i1 @readI1p(i1* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[STACK]]) #[[ATTR7]]
 ; CGSCC-NEXT:    ret i1 [[R]]
 ;
   %stack = alloca i1
@@ -1362,18 +1377,115 @@ m:
   ret i1 %r
 }
 
+define i32 @assume_read_global_good() {
+;
+;
+; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn
+; TUNIT-LABEL: define {{[^@]+}}@assume_read_global_good
+; TUNIT-SAME: () #[[ATTR4:[0-9]+]] {
+; TUNIT-NEXT:    [[LGS1:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; TUNIT-NEXT:    [[C:%.*]] = icmp eq i32 [[LGS1]], 42
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[C]]) #[[ATTR6]]
+; TUNIT-NEXT:    store i32 13, i32* @Gstatic_int1, align 4
+; TUNIT-NEXT:    store i32 17, i32* @Gstatic_int1, align 4
+; TUNIT-NEXT:    [[LGS3:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; TUNIT-NEXT:    [[ADD:%.*]] = add i32 42, [[LGS3]]
+; TUNIT-NEXT:    ret i32 [[ADD]]
+;
+; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn
+; CGSCC-LABEL: define {{[^@]+}}@assume_read_global_good
+; CGSCC-SAME: () #[[ATTR5:[0-9]+]] {
+; CGSCC-NEXT:    [[LGS1:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; CGSCC-NEXT:    [[C:%.*]] = icmp eq i32 [[LGS1]], 42
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[C]]) #[[ATTR7]]
+; CGSCC-NEXT:    store i32 13, i32* @Gstatic_int1, align 4
+; CGSCC-NEXT:    store i32 17, i32* @Gstatic_int1, align 4
+; CGSCC-NEXT:    [[LGS3:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; CGSCC-NEXT:    [[ADD:%.*]] = add i32 42, [[LGS3]]
+; CGSCC-NEXT:    ret i32 [[ADD]]
+;
+  %lgs1 = load i32, i32* @Gstatic_int1
+  %c = icmp eq i32 %lgs1, 42
+  call void @llvm.assume(i1 %c)
+  %lgs2 = load i32, i32* @Gstatic_int1
+  store i32 13, i32* @Gstatic_int1, align 4
+  store i32 17, i32* @Gstatic_int1, align 4
+  %lgs3 = load i32, i32* @Gstatic_int1
+  %add = add i32 %lgs2, %lgs3
+  ret i32 %add
+}
+
+; TODO: Technically we could still utilize the assumption if we employ AA.
+define i32 @assume_read_global_bad(i32* %p) {
+;
+; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn
+; TUNIT-LABEL: define {{[^@]+}}@assume_read_global_bad
+; TUNIT-SAME: (i32* nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR4]] {
+; TUNIT-NEXT:    [[LGS1:%.*]] = load i32, i32* @Gstatic_int2, align 4
+; TUNIT-NEXT:    [[C:%.*]] = icmp eq i32 [[LGS1]], 42
+; TUNIT-NEXT:    store i32 13, i32* [[P]], align 4
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[C]]) #[[ATTR6]]
+; TUNIT-NEXT:    [[LGS2:%.*]] = load i32, i32* @Gstatic_int2, align 4
+; TUNIT-NEXT:    store i32 17, i32* @Gstatic_int2, align 4
+; TUNIT-NEXT:    ret i32 [[LGS2]]
+;
+; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn
+; CGSCC-LABEL: define {{[^@]+}}@assume_read_global_bad
+; CGSCC-SAME: (i32* nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR5]] {
+; CGSCC-NEXT:    [[LGS1:%.*]] = load i32, i32* @Gstatic_int2, align 4
+; CGSCC-NEXT:    [[C:%.*]] = icmp eq i32 [[LGS1]], 42
+; CGSCC-NEXT:    store i32 13, i32* [[P]], align 4
+; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[C]]) #[[ATTR7]]
+; CGSCC-NEXT:    [[LGS2:%.*]] = load i32, i32* @Gstatic_int2, align 4
+; CGSCC-NEXT:    store i32 17, i32* @Gstatic_int2, align 4
+; CGSCC-NEXT:    ret i32 [[LGS2]]
+;
+  %lgs1 = load i32, i32* @Gstatic_int2
+  %c = icmp eq i32 %lgs1, 42
+  store i32 13, i32* %p, align 4
+  call void @llvm.assume(i1 %c)
+  %lgs2 = load i32, i32* @Gstatic_int2
+  store i32 17, i32* @Gstatic_int2, align 4
+  ret i32 %lgs2
+}
+
+define void @assume_write_globals() {
+;
+; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(write)
+; TUNIT-LABEL: define {{[^@]+}}@assume_write_globals
+; TUNIT-SAME: () #[[ATTR5:[0-9]+]] {
+; TUNIT-NEXT:    store i32 42, i32* @Gstatic_int1, align 4
+; TUNIT-NEXT:    store i32 42, i32* @Gstatic_int2, align 4
+; TUNIT-NEXT:    ret void
+;
+; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(write)
+; CGSCC-LABEL: define {{[^@]+}}@assume_write_globals
+; CGSCC-SAME: () #[[ATTR6:[0-9]+]] {
+; CGSCC-NEXT:    store i32 42, i32* @Gstatic_int1, align 4
+; CGSCC-NEXT:    store i32 42, i32* @Gstatic_int2, align 4
+; CGSCC-NEXT:    ret void
+;
+  store i32 42, i32* @Gstatic_int1, align 4
+  store i32 42, i32* @Gstatic_int2, align 4
+  ret void
+}
+
 ;.
 ; TUNIT: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
 ; TUNIT: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind willreturn memory(argmem: read) }
 ; TUNIT: attributes #[[ATTR2]] = { norecurse }
 ; TUNIT: attributes #[[ATTR3]] = { nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
-; TUNIT: attributes #[[ATTR4]] = { willreturn }
-; TUNIT: attributes #[[ATTR5]] = { nofree nosync nounwind willreturn }
+; TUNIT: attributes #[[ATTR4]] = { nofree norecurse nosync nounwind willreturn }
+; TUNIT: attributes #[[ATTR5]] = { nofree norecurse nosync nounwind willreturn memory(write) }
+; TUNIT: attributes #[[ATTR6]] = { willreturn }
+; TUNIT: attributes #[[ATTR7]] = { nofree nosync nounwind willreturn }
 ;.
 ; CGSCC: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
 ; CGSCC: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind willreturn memory(argmem: read) }
 ; CGSCC: attributes #[[ATTR2]] = { norecurse }
 ; CGSCC: attributes #[[ATTR3]] = { nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
 ; CGSCC: attributes #[[ATTR4]] = { nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
-; CGSCC: attributes #[[ATTR5]] = { willreturn }
+; CGSCC: attributes #[[ATTR5]] = { nofree norecurse nosync nounwind willreturn }
+; CGSCC: attributes #[[ATTR6]] = { nofree norecurse nosync nounwind willreturn memory(write) }
+; CGSCC: attributes #[[ATTR7]] = { willreturn }
 ;.


        


More information about the llvm-commits mailing list