[llvm] [InstCombine] Try to freely invert phi nodes (PR #80804)

via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 23:16:51 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

This patch tries to invert phi nodes if all incoming values are either constants or nots.


---
Full diff: https://github.com/llvm/llvm-project/pull/80804.diff


3 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+27) 
- (modified) llvm/test/Transforms/InstCombine/div-i1.ll (+3-1) 
- (modified) llvm/test/Transforms/InstCombine/free-inversion.ll (+190-2) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index dd168917f4dc9c..9a148636aebe35 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2375,6 +2375,33 @@ Value *InstCombiner::getFreelyInvertedImpl(Value *V, bool WillInvertAllUses,
     }
   }
 
+  if (PHINode *PN = dyn_cast<PHINode>(V)) {
+    SmallVector<std::pair<Value *, BasicBlock *>, 8> IncomingValues;
+    for (Use &U : PN->operands()) {
+      BasicBlock *IncomingBlock = PN->getIncomingBlock(U);
+      if (isa<PHINode>(U.get()))
+        return nullptr;
+      Value *NewIncomingVal =
+          getFreelyInvertedImpl(U.get(), /*WillInvertAllUses=*/false,
+                                /*Builder=*/nullptr, DoesConsume, Depth);
+      if (NewIncomingVal == nullptr)
+        return nullptr;
+      // Make sure that we can safely erase the original PHI node.
+      if (NewIncomingVal == V)
+        return nullptr;
+      if (Builder != nullptr)
+        IncomingValues.emplace_back(NewIncomingVal, IncomingBlock);
+    }
+    if (Builder != nullptr) {
+      PHINode *NewPN =
+          PHINode::Create(PN->getType(), PN->getNumIncomingValues(), "", PN);
+      for (auto [Val, Pred] : IncomingValues)
+        NewPN->addIncoming(Val, Pred);
+      return NewPN;
+    }
+    return NonNull;
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/div-i1.ll b/llvm/test/Transforms/InstCombine/div-i1.ll
index a14ef3588e611f..ad8aa71c8f1c88 100644
--- a/llvm/test/Transforms/InstCombine/div-i1.ll
+++ b/llvm/test/Transforms/InstCombine/div-i1.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+; RUN: opt -S -passes='instcombine<no-verify-fixpoint>' < %s | FileCheck %s
 
 define i1 @sdiv_by_zero_indirect_is_poison(i1 %c, i1 %x, i1 %y) {
 ; CHECK-LABEL: @sdiv_by_zero_indirect_is_poison(
@@ -132,6 +132,8 @@ define i1 @pt62607() {
 ; CHECK-NEXT:  entry_1:
 ; CHECK-NEXT:    br label [[LOOP_5:%.*]]
 ; CHECK:       loop_5:
+; CHECK-NEXT:    [[VAL_I1_55:%.*]] = phi i1 [ true, [[ENTRY_1:%.*]] ], [ poison, [[LOOP_5]] ]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[VAL_I1_55]])
 ; CHECK-NEXT:    br i1 poison, label [[LOOP_5]], label [[LOOP_EXIT_8:%.*]]
 ; CHECK:       loop_exit_8:
 ; CHECK-NEXT:    ret i1 false
diff --git a/llvm/test/Transforms/InstCombine/free-inversion.ll b/llvm/test/Transforms/InstCombine/free-inversion.ll
index be9bedbf798598..a89887a586b582 100644
--- a/llvm/test/Transforms/InstCombine/free-inversion.ll
+++ b/llvm/test/Transforms/InstCombine/free-inversion.ll
@@ -9,6 +9,7 @@ declare i8 @llvm.umax.i8(i8, i8)
 declare void @llvm.assume(i1)
 
 declare void @use.i8(i8)
+declare void @use.i1(i1)
 
 define i8 @xor_1(i8 %a, i1 %c, i8 %x, i8 %y) {
 ; CHECK-LABEL: @xor_1(
@@ -544,11 +545,198 @@ define i8 @lshr_not_nneg(i8 %x, i8 %y) {
 define i8 @lshr_not_nneg2(i8 %x) {
 ; CHECK-LABEL: @lshr_not_nneg2(
 ; CHECK-NEXT:    [[SHR:%.*]] = lshr i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[SHR_NOT1:%.*]] = or disjoint i8 [[SHR]], -128
-; CHECK-NEXT:    ret i8 [[SHR_NOT1]]
+; CHECK-NEXT:    [[SHR_NOT:%.*]] = or disjoint i8 [[SHR]], -128
+; CHECK-NEXT:    ret i8 [[SHR_NOT]]
 ;
   %x.not = xor i8 %x, -1
   %shr = lshr i8 %x.not, 1
   %shr.not = xor i8 %shr, -1
   ret i8 %shr.not
 }
+
+define i1 @test_inv_free(i1 %c1, i1 %c2, i1 %c3, i1 %c4) {
+; CHECK-LABEL: @test_inv_free(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C1:%.*]], label [[B1:%.*]], label [[B2:%.*]]
+; CHECK:       b1:
+; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[B3:%.*]]
+; CHECK:       b2:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       b3:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[VAL_NOT:%.*]] = phi i1 [ false, [[B1]] ], [ true, [[B2]] ], [ [[C3:%.*]], [[B3]] ]
+; CHECK-NEXT:    [[COND_NOT:%.*]] = and i1 [[VAL_NOT]], [[C4:%.*]]
+; CHECK-NEXT:    br i1 [[COND_NOT]], label [[B5:%.*]], label [[B4:%.*]]
+; CHECK:       b4:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       b5:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  br i1 %c1, label %b1, label %b2
+b1:
+  br i1 %c2, label %exit, label %b3
+b2:
+  br label %exit
+b3:
+  %invc3 = xor i1 %c3, true
+  br label %exit
+exit:
+  %val = phi i1 [ true, %b1 ], [ false, %b2 ], [ %invc3, %b3 ]
+  %inv = xor i1 %c4, true
+  %cond = or i1 %val, %inv
+  br i1 %cond, label %b4, label %b5
+b4:
+  ret i1 true
+b5:
+  ret i1 false
+}
+
+define i32 @test_inv_free_i32(i1 %c1, i1 %c2, i32 %c3, i32 %c4) {
+; CHECK-LABEL: @test_inv_free_i32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C1:%.*]], label [[B1:%.*]], label [[B2:%.*]]
+; CHECK:       b1:
+; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[B3:%.*]]
+; CHECK:       b2:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       b3:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[TMP0:%.*]] = phi i32 [ 0, [[B1]] ], [ -1, [[B2]] ], [ [[C3:%.*]], [[B3]] ]
+; CHECK-NEXT:    [[COND:%.*]] = xor i32 [[TMP0]], [[C4:%.*]]
+; CHECK-NEXT:    ret i32 [[COND]]
+;
+entry:
+  br i1 %c1, label %b1, label %b2
+b1:
+  br i1 %c2, label %exit, label %b3
+b2:
+  br label %exit
+b3:
+  %invc3 = xor i32 %c3, -1
+  br label %exit
+exit:
+  %val = phi i32 [ -1, %b1 ], [ 0, %b2 ], [ %invc3, %b3 ]
+  %inv = xor i32 %c4, -1
+  %cond = xor i32 %val, %inv
+  ret i32 %cond
+}
+
+; Negative tests
+
+define i1 @test_inv_free_multiuse(i1 %c1, i1 %c2, i1 %c3, i1 %c4) {
+; CHECK-LABEL: @test_inv_free_multiuse(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C1:%.*]], label [[B1:%.*]], label [[B2:%.*]]
+; CHECK:       b1:
+; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[B3:%.*]]
+; CHECK:       b2:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       b3:
+; CHECK-NEXT:    [[INVC3:%.*]] = xor i1 [[C3:%.*]], true
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[VAL:%.*]] = phi i1 [ true, [[B1]] ], [ false, [[B2]] ], [ [[INVC3]], [[B3]] ]
+; CHECK-NEXT:    call void @use.i1(i1 [[VAL]])
+; CHECK-NEXT:    [[INV:%.*]] = xor i1 [[C4:%.*]], true
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[VAL]], [[INV]]
+; CHECK-NEXT:    br i1 [[COND]], label [[B4:%.*]], label [[B5:%.*]]
+; CHECK:       b4:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       b5:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  br i1 %c1, label %b1, label %b2
+b1:
+  br i1 %c2, label %exit, label %b3
+b2:
+  br label %exit
+b3:
+  %invc3 = xor i1 %c3, true
+  br label %exit
+exit:
+  %val = phi i1 [ true, %b1 ], [ false, %b2 ], [ %invc3, %b3 ]
+  call void @use.i1(i1 %val)
+  %inv = xor i1 %c4, true
+  %cond = or i1 %val, %inv
+  br i1 %cond, label %b4, label %b5
+b4:
+  ret i1 true
+b5:
+  ret i1 false
+}
+
+define i32 @test_inv_free_i32_newinst(i1 %c1, i1 %c2, i32 %c3, i32 %c4) {
+; CHECK-LABEL: @test_inv_free_i32_newinst(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C1:%.*]], label [[B1:%.*]], label [[B2:%.*]]
+; CHECK:       b1:
+; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[B3:%.*]]
+; CHECK:       b2:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       b3:
+; CHECK-NEXT:    [[ASHR:%.*]] = ashr i32 -8, [[C3:%.*]]
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[VAL:%.*]] = phi i32 [ -1, [[B1]] ], [ 0, [[B2]] ], [ [[ASHR]], [[B3]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = xor i32 [[VAL]], [[C4:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = xor i32 [[TMP0]], -1
+; CHECK-NEXT:    ret i32 [[COND]]
+;
+entry:
+  br i1 %c1, label %b1, label %b2
+b1:
+  br i1 %c2, label %exit, label %b3
+b2:
+  br label %exit
+b3:
+  %ashr = ashr i32 -8, %c3
+  br label %exit
+exit:
+  %val = phi i32 [ -1, %b1 ], [ 0, %b2 ], [ %ashr, %b3 ]
+  %inv = xor i32 %c4, -1
+  %cond = xor i32 %val, %inv
+  ret i32 %cond
+}
+
+define i1 @test_inv_free_loop(i1 %c1, i1 %c2, i1 %c3, i1 %c4) {
+; CHECK-LABEL: @test_inv_free_loop(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C1:%.*]], label [[B1:%.*]], label [[B2:%.*]]
+; CHECK:       b1:
+; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[B3:%.*]]
+; CHECK:       b2:
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       b3:
+; CHECK-NEXT:    [[INVC3:%.*]] = xor i1 [[C3:%.*]], true
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[VAL:%.*]] = phi i1 [ true, [[B1]] ], [ false, [[B2]] ], [ [[INVC3]], [[B3]] ], [ [[NOT:%.*]], [[EXIT]] ]
+; CHECK-NEXT:    [[INV:%.*]] = xor i1 [[C4:%.*]], true
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[VAL]], [[INV]]
+; CHECK-NEXT:    [[NOT]] = xor i1 [[VAL]], true
+; CHECK-NEXT:    br i1 [[COND]], label [[EXIT]], label [[B4:%.*]]
+; CHECK:       b4:
+; CHECK-NEXT:    ret i1 true
+;
+entry:
+  br i1 %c1, label %b1, label %b2
+b1:
+  br i1 %c2, label %exit, label %b3
+b2:
+  br label %exit
+b3:
+  %invc3 = xor i1 %c3, true
+  br label %exit
+exit:
+  %val = phi i1 [ true, %b1 ], [ false, %b2 ], [ %invc3, %b3 ], [ %not, %exit ]
+  %inv = xor i1 %c4, true
+  %cond = or i1 %val, %inv
+  %not = xor i1 %val, true
+  br i1 %cond, label %exit, label %b4
+b4:
+  ret i1 true
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/80804


More information about the llvm-commits mailing list