[llvm] b98c405 - [EarlyCSE] De-Duplicate callsites with differing attrs

Noah Goldstein via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 4 08:36:42 PDT 2024


Author: Noah Goldstein
Date: 2024-10-04T10:36:28-05:00
New Revision: b98c405f954f9ef6150c655f6ffc68e377d2ac84

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

LOG: [EarlyCSE] De-Duplicate callsites with differing attrs

We only do this if the attributes of the two callsites are compatible
(intersectable) which is probably not in fact necessary.

Closes #110929

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/EarlyCSE.cpp
    llvm/test/Transforms/EarlyCSE/replace-calls-def-attrs.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index cf11f5bc885a75..a1dbb4e1d5e75f 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -362,7 +362,7 @@ static bool isEqualImpl(SimpleValue LHS, SimpleValue RHS) {
 
   if (LHSI->getOpcode() != RHSI->getOpcode())
     return false;
-  if (LHSI->isIdenticalToWhenDefined(RHSI)) {
+  if (LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true)) {
     // Convergent calls implicitly depend on the set of threads that is
     // currently executing, so conservatively return false if they are in
     // 
diff erent basic blocks.
@@ -551,7 +551,7 @@ bool DenseMapInfo<CallValue>::isEqual(CallValue LHS, CallValue RHS) {
   if (LHSI->isConvergent() && LHSI->getParent() != RHSI->getParent())
     return false;
 
-  return LHSI->isIdenticalTo(RHSI);
+  return LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true);
 }
 
 //===----------------------------------------------------------------------===//
@@ -1307,6 +1307,23 @@ static void combineIRFlags(Instruction &From, Value *To) {
         (I->hasPoisonGeneratingFlags() && !programUndefinedIfPoison(I)))
       I->andIRFlags(&From);
   }
+  if (isa<CallBase>(&From) && isa<CallBase>(To)) {
+    // NB: Intersection of attrs between InVal.first and Inst is overly
+    // conservative. Since we only CSE readonly functions that have the same
+    // memory state, we can preserve (or possibly in some cases combine)
+    // more attributes. Likewise this implies when checking equality of
+    // callsite for CSEing, we can probably ignore more attributes.
+    // Generally poison generating attributes need to be handled with more
+    // care as they can create *new* UB if preserved/combined and violated.
+    // Attributes that imply immediate UB on the other hand would have been
+    // violated either way.
+    bool Success =
+        cast<CallBase>(To)->tryIntersectAttributes(cast<CallBase>(&From));
+    assert(Success && "Failed to intersect attributes in callsites that "
+                      "passed identical check");
+    // For NDEBUG Compile.
+    (void)Success;
+  }
 }
 
 bool EarlyCSE::overridingStores(const ParseMemoryInst &Earlier,
@@ -1632,6 +1649,7 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
           LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n");
           continue;
         }
+        combineIRFlags(Inst, InVal.first);
         if (!Inst.use_empty())
           Inst.replaceAllUsesWith(InVal.first);
         salvageKnowledge(&Inst, &AC);

diff  --git a/llvm/test/Transforms/EarlyCSE/replace-calls-def-attrs.ll b/llvm/test/Transforms/EarlyCSE/replace-calls-def-attrs.ll
index 19560080744a1f..2adaf1c7b67a04 100644
--- a/llvm/test/Transforms/EarlyCSE/replace-calls-def-attrs.ll
+++ b/llvm/test/Transforms/EarlyCSE/replace-calls-def-attrs.ll
@@ -13,9 +13,8 @@ declare i8 @buz.fp(float, float)
 define i8 @same_parent_combine_
diff _attrs(i8 %x, i8 %y) {
 ; CHECK-LABEL: define i8 @same_parent_combine_
diff _attrs(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT:    [[C2:%.*]] = call i8 @baz(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR0:[0-9]+]]
-; CHECK-NEXT:    [[C1:%.*]] = call i8 @baz(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz(i8 [[C1]], i8 [[C2]])
+; CHECK-NEXT:    [[C1:%.*]] = call i8 @baz(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz(i8 [[C1]], i8 [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %c1 = call i8 @baz(i8 noundef %x, i8 noundef %y) readnone
@@ -28,9 +27,8 @@ define i8 @same_parent_combine_
diff _attrs(i8 %x, i8 %y) {
 define i8 @same_parent_combine_
diff _attrs_needs_intersect(i8 %x, i8 %y) {
 ; CHECK-LABEL: define i8 @same_parent_combine_
diff _attrs_needs_intersect(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT:    [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR0]]
-; CHECK-NEXT:    [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr [[C1]])
+; CHECK-NEXT:    [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %c1 = call nonnull ptr @baz.ptr(i8 noundef %x, i8 noundef %y) readnone
@@ -43,9 +41,8 @@ define i8 @same_parent_combine_
diff _attrs_needs_intersect(i8 %x, i8 %y) {
 define i8 @same_parent_combine_
diff _attrs_fmf(float %x, float %y) {
 ; CHECK-LABEL: define i8 @same_parent_combine_
diff _attrs_fmf(
 ; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
-; CHECK-NEXT:    [[C1:%.*]] = call nnan nsz float @baz.fp(float noundef [[X]], float noundef [[Y]]) #[[ATTR1:[0-9]+]]
-; CHECK-NEXT:    [[C0:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR1]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.fp(float [[C0]], float [[C1]])
+; CHECK-NEXT:    [[C1:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.fp(float [[C1]], float [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %c1 = call nnan nsz float @baz.fp(float noundef %x, float noundef %y) readonly
@@ -58,9 +55,8 @@ define i8 @same_parent_combine_
diff _attrs_fmf(float %x, float %y) {
 define i8 @same_parent_combine_
diff _attrs_fmf2(float %x, float %y) {
 ; CHECK-LABEL: define i8 @same_parent_combine_
diff _attrs_fmf2(
 ; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
-; CHECK-NEXT:    [[C1:%.*]] = call nnan float @baz.fp(float noundef [[X]], float noundef [[Y]]) #[[ATTR0]]
-; CHECK-NEXT:    [[C0:%.*]] = call nnan nsz float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR0]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.fp(float [[C0]], float [[C1]])
+; CHECK-NEXT:    [[C1:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR0]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.fp(float [[C1]], float [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %c1 = call nnan float @baz.fp(float noundef %x, float noundef %y) readnone
@@ -73,9 +69,8 @@ define i8 @same_parent_combine_
diff _attrs_fmf2(float %x, float %y) {
 define i8 @same_parent_combine_
diff _attrs_needs_intersect2(i8 %x, i8 %y) {
 ; CHECK-LABEL: define i8 @same_parent_combine_
diff _attrs_needs_intersect2(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT:    [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
-; CHECK-NEXT:    [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr [[C1]])
+; CHECK-NEXT:    [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %c1 = call nonnull ptr @baz.ptr(i8 noundef %x, i8 noundef %y) readonly
@@ -88,9 +83,8 @@ define i8 @same_parent_combine_
diff _attrs_needs_intersect2(i8 %x, i8 %y) {
 define i8 @same_parent_combine_
diff _attrs_really_needs_intersect(i8 %x, i8 %y) {
 ; CHECK-LABEL: define i8 @same_parent_combine_
diff _attrs_really_needs_intersect(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT:    [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
-; CHECK-NEXT:    [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr noundef [[C1]])
+; CHECK-NEXT:    [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr noundef [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %c1 = call nonnull ptr @baz.ptr(i8 noundef %x, i8 noundef %y) readonly
@@ -160,8 +154,7 @@ define i8 @
diff _parent_combine_
diff _attrs_preserves_return_attrs(i1 %c, i8 %x, i
 ; CHECK-NEXT:    [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
 ; CHECK-NEXT:    br i1 [[C]], label %[[T:.*]], label %[[F:.*]]
 ; CHECK:       [[T]]:
-; CHECK-NEXT:    [[C0:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
-; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr noundef [[C1]])
+; CHECK-NEXT:    [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr noundef [[C1]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ; CHECK:       [[F]]:
 ; CHECK-NEXT:    ret i8 9


        


More information about the llvm-commits mailing list