[llvm] [InstCombine] Support and/or in `getFreelyInvertedImpl` using DeMorgan's Law (PR #85193)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 14 00:54:09 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Yingwei Zheng (dtcxzyw)
<details>
<summary>Changes</summary>
This patch adds the support for and/or in `getFreelyInvertedImpl` using DeMorgan's Law:
```
(~(A | B)) -> (~A & ~B)
(~(A & B)) -> (~A | ~B)
```
---
Full diff: https://github.com/llvm/llvm-project/pull/85193.diff
4 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+26)
- (modified) llvm/test/Transforms/InstCombine/icmp-and-lowbit-mask.ll (+7-9)
- (modified) llvm/test/Transforms/InstCombine/not.ll (+167)
- (modified) llvm/test/Transforms/InstCombine/pr63791.ll (+1-1)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 1688005de2104d..8ee4979fc17892 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2596,6 +2596,32 @@ Value *InstCombiner::getFreelyInvertedImpl(Value *V, bool WillInvertAllUses,
return nullptr;
}
+ // De Morgan's Laws:
+ // (~(A | B)) -> (~A & ~B)
+ // (~(A & B)) -> (~A | ~B)
+ auto TryInvertAndOrUsingDeMorgan = [&](Instruction::BinaryOps Opcode,
+ Value *A, Value *B) -> Value * {
+ bool LocalDoesConsume = DoesConsume;
+ if (!getFreelyInvertedImpl(B, B->hasOneUse(), /*Builder=*/nullptr,
+ LocalDoesConsume, Depth))
+ return nullptr;
+ if (auto *NotA = getFreelyInvertedImpl(A, A->hasOneUse(), Builder,
+ LocalDoesConsume, Depth)) {
+ auto *NotB = getFreelyInvertedImpl(B, B->hasOneUse(), Builder,
+ LocalDoesConsume, Depth);
+ DoesConsume = LocalDoesConsume;
+ return Builder ? Builder->CreateBinOp(Opcode, NotA, NotB) : NonNull;
+ }
+
+ return nullptr;
+ };
+
+ if (match(V, m_Or(m_Value(A), m_Value(B))))
+ return TryInvertAndOrUsingDeMorgan(Instruction::And, A, B);
+
+ if (match(V, m_And(m_Value(A), m_Value(B))))
+ return TryInvertAndOrUsingDeMorgan(Instruction::Or, A, B);
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/icmp-and-lowbit-mask.ll b/llvm/test/Transforms/InstCombine/icmp-and-lowbit-mask.ll
index 07060922895846..410b6c29187b22 100644
--- a/llvm/test/Transforms/InstCombine/icmp-and-lowbit-mask.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-and-lowbit-mask.ll
@@ -226,12 +226,11 @@ define i1 @src_is_mask_shl_lshr(i8 %x_in, i8 %y, i1 %cond) {
define i1 @src_is_mask_shl_lshr_fail_not_allones(i8 %x_in, i8 %y, i1 %cond) {
; CHECK-LABEL: @src_is_mask_shl_lshr_fail_not_allones(
-; CHECK-NEXT: [[X:%.*]] = xor i8 [[X_IN:%.*]], 123
; CHECK-NEXT: [[TMP1:%.*]] = lshr i8 -1, [[Y:%.*]]
; CHECK-NEXT: [[MASK:%.*]] = and i8 [[TMP1]], -2
-; CHECK-NEXT: [[NOTMASK:%.*]] = xor i8 [[MASK]], -1
-; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], [[NOTMASK]]
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[X_IN:%.*]], -124
+; CHECK-NEXT: [[TMP3:%.*]] = or i8 [[TMP2]], [[MASK]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[TMP3]], -1
; CHECK-NEXT: ret i1 [[R]]
;
%x = xor i8 %x_in, 123
@@ -572,11 +571,10 @@ define i1 @src_is_notmask_neg_p2(i8 %x_in, i8 %y) {
define i1 @src_is_notmask_neg_p2_fail_not_invertable(i8 %x_in, i8 %y) {
; CHECK-LABEL: @src_is_notmask_neg_p2_fail_not_invertable(
-; CHECK-NEXT: [[X:%.*]] = xor i8 [[X_IN:%.*]], 123
-; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[Y:%.*]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y]], -1
-; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[TMP1]], [[TMP2]]
-; CHECK-NEXT: [[R:%.*]] = icmp ule i8 [[X]], [[TMP3]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X_IN:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = sub i8 0, [[Y:%.*]]
+; CHECK-NEXT: [[TMP3:%.*]] = or i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[R:%.*]] = icmp uge i8 [[TMP1]], [[TMP3]]
; CHECK-NEXT: ret i1 [[R]]
;
%x = xor i8 %x_in, 123
diff --git a/llvm/test/Transforms/InstCombine/not.ll b/llvm/test/Transforms/InstCombine/not.ll
index f277d13eee930c..0835358682b808 100644
--- a/llvm/test/Transforms/InstCombine/not.ll
+++ b/llvm/test/Transforms/InstCombine/not.ll
@@ -3,6 +3,8 @@
declare void @use1(i1)
declare void @use8(i8)
+declare void @f1()
+declare void @f2()
define i32 @test1(i32 %A) {
; CHECK-LABEL: @test1(
@@ -858,3 +860,168 @@ define i32 @test_zext(i32 %a, i32 %b){
%not = xor i32 %add, -1
ret i32 %not
}
+
+define void @test_invert_demorgan_or(i32 %a, i32 %b, i1 %cond) {
+; CHECK-LABEL: @test_invert_demorgan_or(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[B1:%.*]], 0
+; CHECK-NEXT: [[OR_NOT1:%.*]] = and i1 [[CMP2]], [[CMP3]]
+; CHECK-NEXT: [[MERGE:%.*]] = and i1 [[OR_NOT1]], [[COND:%.*]]
+; CHECK-NEXT: br i1 [[MERGE]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @f1()
+; CHECK-NEXT: unreachable
+; CHECK: if.else:
+; CHECK-NEXT: call void @f2()
+; CHECK-NEXT: unreachable
+;
+entry:
+ %cmp1 = icmp eq i32 %a, 0
+ %cmp2 = icmp ne i32 %b, 0
+ %or = or i1 %cmp1, %cmp2
+ %not = xor i1 %cond, true
+ %merge = or i1 %not, %or
+ br i1 %merge, label %if.then, label %if.else
+if.then:
+ call void @f1()
+ unreachable
+if.else:
+ call void @f2()
+ unreachable
+}
+
+define i1 @test_invert_demorgan_or2(i64 %a, i64 %b, i64 %c) {
+; CHECK-LABEL: @test_invert_demorgan_or2(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[A:%.*]], 24
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i64 [[B:%.*]], 60
+; CHECK-NEXT: [[OR1_NOT1:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT: [[CMP3:%.*]] = icmp ult i64 [[C:%.*]], 60
+; CHECK-NEXT: [[NOT:%.*]] = and i1 [[OR1_NOT1]], [[CMP3]]
+; CHECK-NEXT: ret i1 [[NOT]]
+;
+ %cmp1 = icmp ugt i64 %a, 23
+ %cmp2 = icmp ugt i64 %b, 59
+ %or1 = or i1 %cmp1, %cmp2
+ %cmp3 = icmp ugt i64 %c, 59
+ %or2 = or i1 %or1, %cmp3
+ %not = xor i1 %or2, true
+ ret i1 %not
+}
+
+define i1 @test_invert_demorgan_or3(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_invert_demorgan_or3(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[A:%.*]], 178206
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[B:%.*]], -196608
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[TMP1]], -1506
+; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[B]], -917760
+; CHECK-NEXT: [[CMP3:%.*]] = icmp ult i32 [[TMP2]], -716213
+; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[B]], -1114112
+; CHECK-NEXT: [[CMP4:%.*]] = icmp ult i32 [[TMP3]], -196112
+; CHECK-NEXT: [[OR1_NOT2:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT: [[OR2_NOT1:%.*]] = and i1 [[OR1_NOT2]], [[CMP3]]
+; CHECK-NEXT: [[NOT:%.*]] = and i1 [[OR2_NOT1]], [[CMP4]]
+; CHECK-NEXT: ret i1 [[NOT]]
+;
+ %cmp1 = icmp eq i32 %a, 178206
+ %v1 = add i32 %b, -195102
+ %cmp2 = icmp ult i32 %v1, 1506
+ %v2 = add i32 %b, -201547
+ %cmp3 = icmp ult i32 %v2, 716213
+ %v3 = add i32 %b, -918000
+ %cmp4 = icmp ult i32 %v3, 196112
+ %or1 = or i1 %cmp1, %cmp2
+ %or2 = or i1 %or1, %cmp3
+ %or3 = or i1 %or2, %cmp4
+ %not = xor i1 %or3, true
+ ret i1 %not
+}
+
+define i1 @test_invert_demorgan_and(i32 %a, i32 %b, i1 %cond) {
+; CHECK-LABEL: @test_invert_demorgan_and(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[B1:%.*]], 0
+; CHECK-NEXT: [[AND_NOT1:%.*]] = or i1 [[CMP2]], [[CMP3]]
+; CHECK-NEXT: [[MERGE:%.*]] = or i1 [[AND_NOT1]], [[COND:%.*]]
+; CHECK-NEXT: br i1 [[MERGE]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @f1()
+; CHECK-NEXT: unreachable
+; CHECK: if.else:
+; CHECK-NEXT: call void @f2()
+; CHECK-NEXT: unreachable
+;
+entry:
+ %cmp1 = icmp eq i32 %a, 0
+ %cmp2 = icmp ne i32 %b, 0
+ %and = and i1 %cmp1, %cmp2
+ %not = xor i1 %cond, true
+ %merge = and i1 %not, %and
+ br i1 %merge, label %if.then, label %if.else
+if.then:
+ call void @f1()
+ unreachable
+if.else:
+ call void @f2()
+ unreachable
+}
+
+define i64 @test_invert_demorgan_and2(i64 %x) {
+; CHECK-LABEL: @test_invert_demorgan_and2(
+; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[X:%.*]]
+; CHECK-NEXT: [[SUB:%.*]] = or i64 [[TMP1]], -9223372036854775808
+; CHECK-NEXT: ret i64 [[SUB]]
+;
+ %add = add i64 %x, 9223372036854775807
+ %and = and i64 %add, 9223372036854775807
+ %sub = xor i64 %and, -1
+ ret i64 %sub
+}
+
+define i1 @test_invert_demorgan_and3(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_invert_demorgan_and3(
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[ADD]], 4095
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 4095
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %not = xor i32 %a, -1
+ %add = add i32 %b, %not
+ %and = and i32 %add, 4095
+ %cmp = icmp eq i32 %and, 0
+ ret i1 %cmp
+}
+
+define i1 @test_invert_demorgan_and_multiuse(i32 %a, i32 %b, i1 %cond) {
+; CHECK-LABEL: @test_invert_demorgan_and_multiuse(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT: call void @use1(i1 [[CMP1]])
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[COND:%.*]], true
+; CHECK-NEXT: [[TMP0:%.*]] = and i1 [[CMP2]], [[NOT]]
+; CHECK-NEXT: [[MERGE:%.*]] = and i1 [[TMP0]], [[CMP1]]
+; CHECK-NEXT: br i1 [[MERGE]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @f1()
+; CHECK-NEXT: unreachable
+; CHECK: if.else:
+; CHECK-NEXT: call void @f2()
+; CHECK-NEXT: unreachable
+;
+entry:
+ %cmp1 = icmp eq i32 %a, 0
+ call void @use1(i1 %cmp1)
+ %cmp2 = icmp ne i32 %b, 0
+ %and = and i1 %cmp1, %cmp2
+ %not = xor i1 %cond, true
+ %merge = and i1 %not, %and
+ br i1 %merge, label %if.then, label %if.else
+if.then:
+ call void @f1()
+ unreachable
+if.else:
+ call void @f2()
+ unreachable
+}
diff --git a/llvm/test/Transforms/InstCombine/pr63791.ll b/llvm/test/Transforms/InstCombine/pr63791.ll
index 78cc1130fb33f0..73a559f9892612 100644
--- a/llvm/test/Transforms/InstCombine/pr63791.ll
+++ b/llvm/test/Transforms/InstCombine/pr63791.ll
@@ -15,7 +15,7 @@ define void @y() {
; CHECK-NEXT: store i1 true, ptr poison, align 1
; CHECK-NEXT: br i1 poison, label [[FOR_COND_I]], label [[FOR_COND5_PREHEADER_I]]
; CHECK: for.cond5.preheader.i:
-; CHECK-NEXT: br i1 false, label [[FOR_INC19_I:%.*]], label [[FOR_COND1_LOOPEXIT_I:%.*]]
+; CHECK-NEXT: br i1 true, label [[FOR_COND1_LOOPEXIT_I:%.*]], label [[FOR_INC19_I:%.*]]
; CHECK: for.inc19.i:
; CHECK-NEXT: br i1 poison, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]]
;
``````````
</details>
https://github.com/llvm/llvm-project/pull/85193
More information about the llvm-commits
mailing list