Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[InstCombine] Try to freely invert phi nodes #80804

Merged
merged 2 commits into from
Feb 7, 2024

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Feb 6, 2024

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

@llvmbot
Copy link
Collaborator

llvmbot commented Feb 6, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

Changes

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 --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
+}

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Feb 6, 2024
llvm/lib/Transforms/InstCombine/InstructionCombining.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/InstCombine/InstructionCombining.cpp Outdated Show resolved Hide resolved
return nullptr;
// Make sure that we can safely erase the original PHI node.
if (NewIncomingVal == V)
return nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which test checks this case? I'm wondering if this is something that can happen indirectly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the test @test_inv_free_loop. TBH I don't know why it happens. If the PHI node is used twice, WillInvertAllUses should be false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is related to freelyInvertAllUsersOf() used for de-Morgan patterns. I think your check is fine.

llvm/test/Transforms/InstCombine/div-i1.ll Outdated Show resolved Hide resolved
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp Outdated Show resolved Hide resolved
return nullptr;
// Make sure that we can safely erase the original PHI node.
if (NewIncomingVal == V)
return nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is related to freelyInvertAllUsersOf() used for de-Morgan patterns. I think your check is fine.

@dtcxzyw dtcxzyw merged commit 7ab0a87 into llvm:main Feb 7, 2024
3 of 4 checks passed
@dtcxzyw dtcxzyw deleted the freely-invert-phi branch February 7, 2024 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants