[llvm] 4d4beeb - [SimplifyCFG] Supporting hoisting/sinking callbases with differing attrs

Noah Goldstein via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 1 16:27:21 PDT 2024


Author: Noah Goldstein
Date: 2024-10-01T18:27:08-05:00
New Revision: 4d4beeb43cfdf5c858ced68b541a1e9ef79f4907

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

LOG: [SimplifyCFG] Supporting hoisting/sinking callbases with differing attrs

Some (many) attributes can safely be dropped to enable sinking. For
example removing `nonnull` on a return/param can't affect correctness.

Closes #109472

Added: 
    

Modified: 
    llvm/include/llvm/IR/InstrTypes.h
    llvm/include/llvm/IR/Instruction.h
    llvm/lib/IR/Instruction.cpp
    llvm/lib/Transforms/Utils/SimplifyCFG.cpp
    llvm/test/Transforms/SimplifyCFG/hoist-cb-diff-attrs.ll
    llvm/test/Transforms/SimplifyCFG/sink-cb-diff-attrs.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 4852f64d0977fb..86d88da3d9460e 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1461,6 +1461,22 @@ class CallBase : public Instruction {
   ///
   void setAttributes(AttributeList A) { Attrs = A; }
 
+  /// Try to intersect the attributes from 'this' CallBase and the
+  /// 'Other' CallBase. Sets the intersected attributes to 'this' and
+  /// return true if successful. Doesn't modify 'this' and returns
+  /// false if unsuccessful.
+  bool tryIntersectAttributes(const CallBase *Other) {
+    if (this == Other)
+      return true;
+    AttributeList AL = getAttributes();
+    AttributeList ALOther = Other->getAttributes();
+    auto Intersected = AL.intersectWith(getContext(), ALOther);
+    if (!Intersected)
+      return false;
+    setAttributes(*Intersected);
+    return true;
+  }
+
   /// Determine whether this call has the given attribute. If it does not
   /// then determine if the called function has the attribute, but only if
   /// the attribute is allowed for the call.

diff  --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h
index 4a1e66a90831c8..61dba265dc948b 100644
--- a/llvm/include/llvm/IR/Instruction.h
+++ b/llvm/include/llvm/IR/Instruction.h
@@ -881,16 +881,20 @@ class Instruction : public User,
   /// This is like isIdenticalTo, except that it ignores the
   /// SubclassOptionalData flags, which may specify conditions under which the
   /// instruction's result is undefined.
-  bool isIdenticalToWhenDefined(const Instruction *I) const LLVM_READONLY;
+  bool
+  isIdenticalToWhenDefined(const Instruction *I,
+                           bool IntersectAttrs = false) const LLVM_READONLY;
 
   /// When checking for operation equivalence (using isSameOperationAs) it is
   /// sometimes useful to ignore certain attributes.
   enum OperationEquivalenceFlags {
     /// Check for equivalence ignoring load/store alignment.
-    CompareIgnoringAlignment = 1<<0,
+    CompareIgnoringAlignment = 1 << 0,
     /// Check for equivalence treating a type and a vector of that type
     /// as equivalent.
-    CompareUsingScalarTypes = 1<<1
+    CompareUsingScalarTypes = 1 << 1,
+    /// Check for equivalence with intersected callbase attrs.
+    CompareUsingIntersectedAttrs = 1 << 2,
   };
 
   /// This function determines if the specified instruction executes the same
@@ -911,8 +915,8 @@ class Instruction : public User,
   /// @returns true if the specific instruction has the same opcde specific
   /// characteristics as the current one. Determine if one instruction has the
   /// same state as another.
-  bool hasSameSpecialState(const Instruction *I2,
-                           bool IgnoreAlignment = false) const LLVM_READONLY;
+  bool hasSameSpecialState(const Instruction *I2, bool IgnoreAlignment = false,
+                           bool IntersectAttrs = false) const LLVM_READONLY;
 
   /// Return true if there are any uses of this instruction in blocks other than
   /// the specified block. Note that PHI nodes are considered to evaluate their

diff  --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index b1c2b0200c8269..b84e62d2e43b7e 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -785,11 +785,21 @@ const char *Instruction::getOpcodeName(unsigned OpCode) {
 /// This must be kept in sync with FunctionComparator::cmpOperations in
 /// lib/Transforms/IPO/MergeFunctions.cpp.
 bool Instruction::hasSameSpecialState(const Instruction *I2,
-                                      bool IgnoreAlignment) const {
+                                      bool IgnoreAlignment,
+                                      bool IntersectAttrs) const {
   auto I1 = this;
   assert(I1->getOpcode() == I2->getOpcode() &&
          "Can not compare special state of 
diff erent instructions");
 
+  auto CheckAttrsSame = [IntersectAttrs](const CallBase *CB0,
+                                         const CallBase *CB1) {
+    return IntersectAttrs
+               ? CB0->getAttributes()
+                     .intersectWith(CB0->getContext(), CB1->getAttributes())
+                     .has_value()
+               : CB0->getAttributes() == CB1->getAttributes();
+  };
+
   if (const AllocaInst *AI = dyn_cast<AllocaInst>(I1))
     return AI->getAllocatedType() == cast<AllocaInst>(I2)->getAllocatedType() &&
            (AI->getAlign() == cast<AllocaInst>(I2)->getAlign() ||
@@ -811,15 +821,15 @@ bool Instruction::hasSameSpecialState(const Instruction *I2,
   if (const CallInst *CI = dyn_cast<CallInst>(I1))
     return CI->isTailCall() == cast<CallInst>(I2)->isTailCall() &&
            CI->getCallingConv() == cast<CallInst>(I2)->getCallingConv() &&
-           CI->getAttributes() == cast<CallInst>(I2)->getAttributes() &&
+           CheckAttrsSame(CI, cast<CallInst>(I2)) &&
            CI->hasIdenticalOperandBundleSchema(*cast<CallInst>(I2));
   if (const InvokeInst *CI = dyn_cast<InvokeInst>(I1))
     return CI->getCallingConv() == cast<InvokeInst>(I2)->getCallingConv() &&
-           CI->getAttributes() == cast<InvokeInst>(I2)->getAttributes() &&
+           CheckAttrsSame(CI, cast<InvokeInst>(I2)) &&
            CI->hasIdenticalOperandBundleSchema(*cast<InvokeInst>(I2));
   if (const CallBrInst *CI = dyn_cast<CallBrInst>(I1))
     return CI->getCallingConv() == cast<CallBrInst>(I2)->getCallingConv() &&
-           CI->getAttributes() == cast<CallBrInst>(I2)->getAttributes() &&
+           CheckAttrsSame(CI, cast<CallBrInst>(I2)) &&
            CI->hasIdenticalOperandBundleSchema(*cast<CallBrInst>(I2));
   if (const InsertValueInst *IVI = dyn_cast<InsertValueInst>(I1))
     return IVI->getIndices() == cast<InsertValueInst>(I2)->getIndices();
@@ -857,10 +867,10 @@ bool Instruction::isIdenticalTo(const Instruction *I) const {
          SubclassOptionalData == I->SubclassOptionalData;
 }
 
-bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
+bool Instruction::isIdenticalToWhenDefined(const Instruction *I,
+                                           bool IntersectAttrs) const {
   if (getOpcode() != I->getOpcode() ||
-      getNumOperands() != I->getNumOperands() ||
-      getType() != I->getType())
+      getNumOperands() != I->getNumOperands() || getType() != I->getType())
     return false;
 
   // If both instructions have no operands, they are identical.
@@ -879,7 +889,8 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
                       otherPHI->block_begin());
   }
 
-  return this->hasSameSpecialState(I);
+  return this->hasSameSpecialState(I, /*IgnoreAlignment=*/false,
+                                   IntersectAttrs);
 }
 
 // Keep this in sync with FunctionComparator::cmpOperations in
@@ -887,7 +898,8 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
 bool Instruction::isSameOperationAs(const Instruction *I,
                                     unsigned flags) const {
   bool IgnoreAlignment = flags & CompareIgnoringAlignment;
-  bool UseScalarTypes  = flags & CompareUsingScalarTypes;
+  bool UseScalarTypes = flags & CompareUsingScalarTypes;
+  bool IntersectAttrs = flags & CompareUsingIntersectedAttrs;
 
   if (getOpcode() != I->getOpcode() ||
       getNumOperands() != I->getNumOperands() ||
@@ -905,7 +917,7 @@ bool Instruction::isSameOperationAs(const Instruction *I,
         getOperand(i)->getType() != I->getOperand(i)->getType())
       return false;
 
-  return this->hasSameSpecialState(I, IgnoreAlignment);
+  return this->hasSameSpecialState(I, IgnoreAlignment, IntersectAttrs);
 }
 
 bool Instruction::isUsedOutsideOfBlock(const BasicBlock *BB) const {

diff  --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index ffd4037f23ffae..18d26aaf460662 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -1594,7 +1594,7 @@ static void hoistLockstepIdenticalDbgVariableRecords(
 
 static bool areIdenticalUpToCommutativity(const Instruction *I1,
                                           const Instruction *I2) {
-  if (I1->isIdenticalToWhenDefined(I2))
+  if (I1->isIdenticalToWhenDefined(I2, /*IntersectAttrs=*/true))
     return true;
 
   if (auto *Cmp1 = dyn_cast<CmpInst>(I1))
@@ -1910,6 +1910,14 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
           if (!I2->use_empty())
             I2->replaceAllUsesWith(I1);
           I1->andIRFlags(I2);
+          if (auto *CB = dyn_cast<CallBase>(I1)) {
+            bool Success = CB->tryIntersectAttributes(cast<CallBase>(I2));
+            assert(Success && "We should not be trying to hoist callbases "
+                              "with non-intersectable attributes");
+            // For NDEBUG Compile.
+            (void)Success;
+          }
+
           combineMetadataForCSE(I1, I2, true);
           // I1 and I2 are being combined into a single instruction.  Its debug
           // location is the merged locations of the original instructions.
@@ -2130,7 +2138,7 @@ static bool canSinkInstructions(
   const Instruction *I0 = Insts.front();
   const auto I0MMRA = MMRAMetadata(*I0);
   for (auto *I : Insts) {
-    if (!I->isSameOperationAs(I0))
+    if (!I->isSameOperationAs(I0, Instruction::CompareUsingIntersectedAttrs))
       return false;
 
     // swifterror pointers can only be used by a load or store; sinking a load
@@ -2287,6 +2295,13 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
       I0->applyMergedLocation(I0->getDebugLoc(), I->getDebugLoc());
       combineMetadataForCSE(I0, I, true);
       I0->andIRFlags(I);
+      if (auto *CB = dyn_cast<CallBase>(I0)) {
+        bool Success = CB->tryIntersectAttributes(cast<CallBase>(I));
+        assert(Success && "We should not be trying to sink callbases "
+                          "with non-intersectable attributes");
+        // For NDEBUG Compile.
+        (void)Success;
+      }
     }
 
   for (User *U : make_early_inc_range(I0->users())) {

diff  --git a/llvm/test/Transforms/SimplifyCFG/hoist-cb-
diff -attrs.ll b/llvm/test/Transforms/SimplifyCFG/hoist-cb-
diff -attrs.ll
index 5d9399fa70dfab..ee6c0cfd43b253 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-cb-
diff -attrs.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-cb-
diff -attrs.ll
@@ -8,15 +8,11 @@ declare void @side.effect()
 define ptr @test_hoist_int_attrs(i1 %c, ptr %p, ptr %p2, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(100) [[P]], ptr align 32 dereferenceable(50) [[P2]], i64 range(i64 10, 100000) [[X]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
 ; CHECK:       common.ret:
-; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
-; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call ptr @foo2(ptr align 64 dereferenceable_or_null(100) [[P]], ptr align 64 dereferenceable(50) [[P2]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK-NEXT:    ret ptr [[R]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(200) [[P]], ptr align 32 dereferenceable(100) [[P2]], i64 range(i64 10000, 100000) [[X]]) #[[ATTR1:[0-9]+]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -34,15 +30,11 @@ else:
 define ptr @test_hoist_int_attrs2(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs2
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT:    br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
 ; CHECK:       common.ret:
-; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
-; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0]]
-; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK-NEXT:    ret ptr [[R]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call ptr @foo(ptr align 32 dereferenceable(100) dereferenceable_or_null(200) [[P]], i64 range(i64 11, 100) [[X]]) #[[ATTR2:[0-9]+]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -60,15 +52,11 @@ else:
 define ptr @test_hoist_bool_attrs2(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs2
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:    br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
 ; CHECK:       common.ret:
-; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
-; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call noundef ptr @foo(ptr noundef nonnull readnone [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
-; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK-NEXT:    ret ptr [[R]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -86,15 +74,11 @@ else:
 define ptr @test_hoist_bool_attrs3(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs3
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
+; CHECK-NEXT:    br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
 ; CHECK:       common.ret:
-; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
-; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
-; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK-NEXT:    ret ptr [[R]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -117,10 +101,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable(i1 %c, ptr %p, i64 %x) {
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
 ; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
 ; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5]]
+; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
 ; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
+; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -143,10 +127,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable2(i1 %c, ptr %p, i64 %x) {
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
 ; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
 ; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]]
+; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
 ; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
+; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -169,10 +153,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable3(i1 %c, ptr %p, i64 %x) {
 ; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
 ; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
 ; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR8]]
+; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR6]]
 ; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
+; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -190,15 +174,11 @@ else:
 define ptr @test_hoist_bool_attrs4(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs4
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
+; CHECK-NEXT:    br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
 ; CHECK:       common.ret:
-; CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
-; CHECK:       if:
-; CHECK-NEXT:    [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]]
-; CHECK-NEXT:    br label [[COMMON_RET:%.*]]
+; CHECK-NEXT:    ret ptr [[R]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
 ; CHECK-NEXT:    call void @side.effect()
 ; CHECK-NEXT:    br label [[COMMON_RET]]
 ;
@@ -214,13 +194,11 @@ else:
 }
 
 ;.
-; CHECK: attributes #[[ATTR0]] = { memory(read) }
-; CHECK: attributes #[[ATTR1]] = { memory(write) }
-; CHECK: attributes #[[ATTR2]] = { memory(none) }
-; CHECK: attributes #[[ATTR3]] = { cold mustprogress nocallback nofree nosync willreturn }
-; CHECK: attributes #[[ATTR4]] = { mustprogress nocallback nofree willreturn }
-; CHECK: attributes #[[ATTR5]] = { alwaysinline cold nocallback nofree nosync willreturn }
-; CHECK: attributes #[[ATTR6]] = { alwaysinline nosync willreturn }
-; CHECK: attributes #[[ATTR7]] = { nosync willreturn }
-; CHECK: attributes #[[ATTR8]] = { cold nocallback nofree nosync willreturn }
+; CHECK: attributes #[[ATTR0]] = { memory(readwrite) }
+; CHECK: attributes #[[ATTR1]] = { memory(read) }
+; CHECK: attributes #[[ATTR2]] = { mustprogress nocallback nofree willreturn }
+; CHECK: attributes #[[ATTR3]] = { alwaysinline nosync willreturn }
+; CHECK: attributes #[[ATTR4]] = { alwaysinline cold nocallback nofree nosync willreturn }
+; CHECK: attributes #[[ATTR5]] = { nosync willreturn }
+; CHECK: attributes #[[ATTR6]] = { cold nocallback nofree nosync willreturn }
 ;.

diff  --git a/llvm/test/Transforms/SimplifyCFG/sink-cb-
diff -attrs.ll b/llvm/test/Transforms/SimplifyCFG/sink-cb-
diff -attrs.ll
index 65d99d9780b553..1998055ca75f2e 100644
--- a/llvm/test/Transforms/SimplifyCFG/sink-cb-
diff -attrs.ll
+++ b/llvm/test/Transforms/SimplifyCFG/sink-cb-
diff -attrs.ll
@@ -8,17 +8,13 @@ declare void @side.effect()
 define ptr @test_sink_int_attrs(i1 %c, ptr %p, ptr %p2, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_sink_int_attrs
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call ptr @foo2(ptr align 64 dereferenceable_or_null(100) [[P]], ptr align 64 dereferenceable(50) [[P2]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    br label [[END:%.*]]
-; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(200) [[P]], ptr align 32 dereferenceable(100) [[P2]], i64 range(i64 10000, 100000) [[X]]) #[[ATTR1:[0-9]+]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[PR]]
+; CHECK-NEXT:    [[R2:%.*]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(100) [[P]], ptr align 32 dereferenceable(50) [[P2]], i64 range(i64 10, 100000) [[X]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    ret ptr [[R2]]
 ;
   br i1 %c, label %if, label %else
 if:
@@ -37,17 +33,13 @@ end:
 define ptr @test_sink_int_attrs2(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_sink_int_attrs2
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0]]
-; CHECK-NEXT:    br label [[END:%.*]]
-; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call ptr @foo(ptr align 32 dereferenceable(100) dereferenceable_or_null(200) [[P]], i64 range(i64 11, 100) [[X]]) #[[ATTR2:[0-9]+]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[PR]]
+; CHECK-NEXT:    [[R2:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT:    ret ptr [[R2]]
 ;
   br i1 %c, label %if, label %else
 if:
@@ -66,17 +58,13 @@ end:
 define ptr @test_sink_bool_attrs2(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_sink_bool_attrs2
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call noundef ptr @foo(ptr noundef nonnull readnone [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
-; CHECK-NEXT:    br label [[END:%.*]]
-; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[PR]]
+; CHECK-NEXT:    [[R2:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:    ret ptr [[R2]]
 ;
   br i1 %c, label %if, label %else
 if:
@@ -95,17 +83,13 @@ end:
 define ptr @test_sink_bool_attrs3(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_sink_bool_attrs3
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
-; CHECK-NEXT:    br label [[END:%.*]]
-; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[PR]]
+; CHECK-NEXT:    [[R2:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
+; CHECK-NEXT:    ret ptr [[R2]]
 ;
   br i1 %c, label %if, label %else
 if:
@@ -127,10 +111,10 @@ define ptr @test_sink_bool_attrs_fail_non_droppable(i1 %c, ptr %p, i64 %x) {
 ; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5]]
+; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
 ; CHECK-NEXT:    br label [[END:%.*]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
+; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
 ; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
@@ -156,10 +140,10 @@ define ptr @test_sink_bool_attrs_fail_non_droppable2(i1 %c, ptr %p, i64 %x) {
 ; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]]
+; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
 ; CHECK-NEXT:    br label [[END:%.*]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
+; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
 ; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
@@ -185,10 +169,10 @@ define ptr @test_sink_bool_attrs_fail_non_droppable3(i1 %c, ptr %p, i64 %x) {
 ; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR8]]
+; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR6]]
 ; CHECK-NEXT:    br label [[END:%.*]]
 ; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR9:[0-9]+]]
+; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
 ; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
@@ -211,17 +195,13 @@ end:
 define ptr @test_sink_bool_attrs4(i1 %c, ptr %p, i64 %x) {
 ; CHECK-LABEL: define {{[^@]+}}@test_sink_bool_attrs4
 ; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
-; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
 ; CHECK:       if:
 ; CHECK-NEXT:    call void @side.effect()
-; CHECK-NEXT:    [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]]
-; CHECK-NEXT:    br label [[END:%.*]]
-; CHECK:       else:
-; CHECK-NEXT:    [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR9]]
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:
-; CHECK-NEXT:    [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
-; CHECK-NEXT:    ret ptr [[PR]]
+; CHECK-NEXT:    [[R2:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
+; CHECK-NEXT:    ret ptr [[R2]]
 ;
   br i1 %c, label %if, label %else
 if:
@@ -238,14 +218,12 @@ end:
 }
 
 ;.
-; CHECK: attributes #[[ATTR0]] = { memory(read) }
-; CHECK: attributes #[[ATTR1]] = { memory(write) }
-; CHECK: attributes #[[ATTR2]] = { memory(none) }
-; CHECK: attributes #[[ATTR3]] = { cold mustprogress nocallback nofree nosync willreturn }
-; CHECK: attributes #[[ATTR4]] = { mustprogress nocallback nofree willreturn }
-; CHECK: attributes #[[ATTR5]] = { alwaysinline cold nocallback nofree nosync willreturn }
-; CHECK: attributes #[[ATTR6]] = { alwaysinline nosync willreturn }
-; CHECK: attributes #[[ATTR7]] = { nosync willreturn }
-; CHECK: attributes #[[ATTR8]] = { cold nocallback nofree nosync willreturn }
-; CHECK: attributes #[[ATTR9]] = { nosync }
+; CHECK: attributes #[[ATTR0]] = { memory(readwrite) }
+; CHECK: attributes #[[ATTR1]] = { memory(read) }
+; CHECK: attributes #[[ATTR2]] = { mustprogress nocallback nofree willreturn }
+; CHECK: attributes #[[ATTR3]] = { alwaysinline nosync willreturn }
+; CHECK: attributes #[[ATTR4]] = { alwaysinline cold nocallback nofree nosync willreturn }
+; CHECK: attributes #[[ATTR5]] = { nosync willreturn }
+; CHECK: attributes #[[ATTR6]] = { cold nocallback nofree nosync willreturn }
+; CHECK: attributes #[[ATTR7]] = { nosync }
 ;.


        


More information about the llvm-commits mailing list