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

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 7 05:00:18 PST 2024


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

>From e6a615691cea528c3149c75adde63aae10c1bb9b Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 6 Feb 2024 15:01:23 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 .../Transforms/InstCombine/free-inversion.ll  | 196 +++++++++++++++++-
 1 file changed, 194 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/free-inversion.ll b/llvm/test/Transforms/InstCombine/free-inversion.ll
index be9bedbf79859..712be30c4618f 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,202 @@ 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:    [[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:    [[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 ]
+  %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:    [[INVC3:%.*]] = xor i32 [[C3:%.*]], -1
+; CHECK-NEXT:    br label [[EXIT]]
+; CHECK:       exit:
+; CHECK-NEXT:    [[VAL:%.*]] = phi i32 [ -1, [[B1]] ], [ 0, [[B2]] ], [ [[INVC3]], [[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:
+  %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
+}

>From 32b29f7f457110149a134d1ab869f3ae1ebe78a1 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 6 Feb 2024 15:02:59 +0800
Subject: [PATCH 2/2] [InstCombine] Try to freely invert phi nodes

---
 .../InstCombine/InstructionCombining.cpp      | 27 +++++++++++++++++++
 .../Transforms/InstCombine/free-inversion.ll  | 14 ++++------
 2 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 4e88a5cc535b1..e392414027633 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);
+      Value *NewIncomingVal = getFreelyInvertedImpl(
+          U.get(), /*WillInvertAllUses=*/false,
+          /*Builder=*/nullptr, DoesConsume, MaxAnalysisRecursionDepth - 1);
+      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) {
+      IRBuilderBase::InsertPointGuard Guard(*Builder);
+      Builder->SetInsertPoint(PN);
+      PHINode *NewPN =
+          Builder->CreatePHI(PN->getType(), PN->getNumIncomingValues());
+      for (auto [Val, Pred] : IncomingValues)
+        NewPN->addIncoming(Val, Pred);
+      return NewPN;
+    }
+    return NonNull;
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/free-inversion.ll b/llvm/test/Transforms/InstCombine/free-inversion.ll
index 712be30c4618f..a89887a586b58 100644
--- a/llvm/test/Transforms/InstCombine/free-inversion.ll
+++ b/llvm/test/Transforms/InstCombine/free-inversion.ll
@@ -563,13 +563,11 @@ define i1 @test_inv_free(i1 %c1, i1 %c2, i1 %c3, i1 %c4) {
 ; 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:    [[INV:%.*]] = xor i1 [[C4:%.*]], true
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[VAL]], [[INV]]
-; CHECK-NEXT:    br i1 [[COND]], label [[B4:%.*]], label [[B5:%.*]]
+; 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:
@@ -604,12 +602,10 @@ define i32 @test_inv_free_i32(i1 %c1, i1 %c2, i32 %c3, i32 %c4) {
 ; CHECK:       b2:
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       b3:
-; CHECK-NEXT:    [[INVC3:%.*]] = xor i32 [[C3:%.*]], -1
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       exit:
-; CHECK-NEXT:    [[VAL:%.*]] = phi i32 [ -1, [[B1]] ], [ 0, [[B2]] ], [ [[INVC3]], [[B3]] ]
-; CHECK-NEXT:    [[TMP0:%.*]] = xor i32 [[VAL]], [[C4:%.*]]
-; CHECK-NEXT:    [[COND:%.*]] = xor i32 [[TMP0]], -1
+; CHECK-NEXT:    [[TMP0:%.*]] = phi i32 [ 0, [[B1]] ], [ -1, [[B2]] ], [ [[C3:%.*]], [[B3]] ]
+; CHECK-NEXT:    [[COND:%.*]] = xor i32 [[TMP0]], [[C4:%.*]]
 ; CHECK-NEXT:    ret i32 [[COND]]
 ;
 entry:



More information about the llvm-commits mailing list