-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesThis 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:
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
+}
|
return nullptr; | ||
// Make sure that we can safely erase the original PHI node. | ||
if (NewIncomingVal == V) | ||
return nullptr; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
bfe202f
to
5bade54
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
return nullptr; | ||
// Make sure that we can safely erase the original PHI node. | ||
if (NewIncomingVal == V) | ||
return nullptr; |
There was a problem hiding this comment.
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.
5bade54
to
32b29f7
Compare
This patch tries to invert phi nodes if all incoming values are either constants or nots.