Skip to content

Commit cf5cd98

Browse files
authored
[InstCombine] Support and/or in getFreelyInvertedImpl using DeMorgan's Law (#85193)
This patch adds the support for and/or in `getFreelyInvertedImpl` using DeMorgan's Law: ``` (~(A | B)) -> (~A & ~B) (~(A & B)) -> (~A | ~B) ``` Alive2: https://alive2.llvm.org/ce/z/Uig8-j
1 parent 6d30223 commit cf5cd98

File tree

4 files changed

+250
-10
lines changed

4 files changed

+250
-10
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2601,6 +2601,45 @@ Value *InstCombiner::getFreelyInvertedImpl(Value *V, bool WillInvertAllUses,
26012601
return nullptr;
26022602
}
26032603

2604+
// De Morgan's Laws:
2605+
// (~(A | B)) -> (~A & ~B)
2606+
// (~(A & B)) -> (~A | ~B)
2607+
auto TryInvertAndOrUsingDeMorgan = [&](Instruction::BinaryOps Opcode,
2608+
bool IsLogical, Value *A,
2609+
Value *B) -> Value * {
2610+
bool LocalDoesConsume = DoesConsume;
2611+
if (!getFreelyInvertedImpl(B, B->hasOneUse(), /*Builder=*/nullptr,
2612+
LocalDoesConsume, Depth))
2613+
return nullptr;
2614+
if (auto *NotA = getFreelyInvertedImpl(A, A->hasOneUse(), Builder,
2615+
LocalDoesConsume, Depth)) {
2616+
auto *NotB = getFreelyInvertedImpl(B, B->hasOneUse(), Builder,
2617+
LocalDoesConsume, Depth);
2618+
DoesConsume = LocalDoesConsume;
2619+
if (IsLogical)
2620+
return Builder ? Builder->CreateLogicalOp(Opcode, NotA, NotB) : NonNull;
2621+
return Builder ? Builder->CreateBinOp(Opcode, NotA, NotB) : NonNull;
2622+
}
2623+
2624+
return nullptr;
2625+
};
2626+
2627+
if (match(V, m_Or(m_Value(A), m_Value(B))))
2628+
return TryInvertAndOrUsingDeMorgan(Instruction::And, /*IsLogical=*/false, A,
2629+
B);
2630+
2631+
if (match(V, m_And(m_Value(A), m_Value(B))))
2632+
return TryInvertAndOrUsingDeMorgan(Instruction::Or, /*IsLogical=*/false, A,
2633+
B);
2634+
2635+
if (match(V, m_LogicalOr(m_Value(A), m_Value(B))))
2636+
return TryInvertAndOrUsingDeMorgan(Instruction::And, /*IsLogical=*/true, A,
2637+
B);
2638+
2639+
if (match(V, m_LogicalAnd(m_Value(A), m_Value(B))))
2640+
return TryInvertAndOrUsingDeMorgan(Instruction::Or, /*IsLogical=*/true, A,
2641+
B);
2642+
26042643
return nullptr;
26052644
}
26062645

llvm/test/Transforms/InstCombine/icmp-and-lowbit-mask.ll

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,11 @@ define i1 @src_is_mask_shl_lshr(i8 %x_in, i8 %y, i1 %cond) {
226226

227227
define i1 @src_is_mask_shl_lshr_fail_not_allones(i8 %x_in, i8 %y, i1 %cond) {
228228
; CHECK-LABEL: @src_is_mask_shl_lshr_fail_not_allones(
229-
; CHECK-NEXT: [[X:%.*]] = xor i8 [[X_IN:%.*]], 123
230229
; CHECK-NEXT: [[TMP1:%.*]] = lshr i8 -1, [[Y:%.*]]
231230
; CHECK-NEXT: [[MASK:%.*]] = and i8 [[TMP1]], -2
232-
; CHECK-NEXT: [[NOTMASK:%.*]] = xor i8 [[MASK]], -1
233-
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], [[NOTMASK]]
234-
; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[AND]], 0
231+
; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[X_IN:%.*]], -124
232+
; CHECK-NEXT: [[TMP3:%.*]] = or i8 [[TMP2]], [[MASK]]
233+
; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[TMP3]], -1
235234
; CHECK-NEXT: ret i1 [[R]]
236235
;
237236
%x = xor i8 %x_in, 123
@@ -572,11 +571,10 @@ define i1 @src_is_notmask_neg_p2(i8 %x_in, i8 %y) {
572571

573572
define i1 @src_is_notmask_neg_p2_fail_not_invertable(i8 %x_in, i8 %y) {
574573
; CHECK-LABEL: @src_is_notmask_neg_p2_fail_not_invertable(
575-
; CHECK-NEXT: [[X:%.*]] = xor i8 [[X_IN:%.*]], 123
576-
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[Y:%.*]], -1
577-
; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y]], -1
578-
; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[TMP1]], [[TMP2]]
579-
; CHECK-NEXT: [[R:%.*]] = icmp ule i8 [[X]], [[TMP3]]
574+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X_IN:%.*]], -124
575+
; CHECK-NEXT: [[TMP2:%.*]] = sub i8 0, [[Y:%.*]]
576+
; CHECK-NEXT: [[TMP3:%.*]] = or i8 [[TMP2]], [[Y]]
577+
; CHECK-NEXT: [[R:%.*]] = icmp uge i8 [[TMP1]], [[TMP3]]
580578
; CHECK-NEXT: ret i1 [[R]]
581579
;
582580
%x = xor i8 %x_in, 123

llvm/test/Transforms/InstCombine/not.ll

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
declare void @use1(i1)
55
declare void @use8(i8)
6+
declare void @f1()
7+
declare void @f2()
68

79
define i32 @test1(i32 %A) {
810
; CHECK-LABEL: @test1(
@@ -858,3 +860,204 @@ define i32 @test_zext(i32 %a, i32 %b){
858860
%not = xor i32 %add, -1
859861
ret i32 %not
860862
}
863+
864+
define void @test_invert_demorgan_or(i32 %a, i32 %b, i1 %cond) {
865+
; CHECK-LABEL: @test_invert_demorgan_or(
866+
; CHECK-NEXT: entry:
867+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 0
868+
; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[B1:%.*]], 0
869+
; CHECK-NEXT: [[OR_NOT1:%.*]] = and i1 [[CMP2]], [[CMP3]]
870+
; CHECK-NEXT: [[MERGE:%.*]] = and i1 [[OR_NOT1]], [[COND:%.*]]
871+
; CHECK-NEXT: br i1 [[MERGE]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
872+
; CHECK: if.then:
873+
; CHECK-NEXT: call void @f1()
874+
; CHECK-NEXT: unreachable
875+
; CHECK: if.else:
876+
; CHECK-NEXT: call void @f2()
877+
; CHECK-NEXT: unreachable
878+
;
879+
entry:
880+
%cmp1 = icmp eq i32 %a, 0
881+
%cmp2 = icmp ne i32 %b, 0
882+
%or = or i1 %cmp1, %cmp2
883+
%not = xor i1 %cond, true
884+
%merge = or i1 %not, %or
885+
br i1 %merge, label %if.then, label %if.else
886+
if.then:
887+
call void @f1()
888+
unreachable
889+
if.else:
890+
call void @f2()
891+
unreachable
892+
}
893+
894+
define i1 @test_invert_demorgan_or2(i64 %a, i64 %b, i64 %c) {
895+
; CHECK-LABEL: @test_invert_demorgan_or2(
896+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[A:%.*]], 24
897+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i64 [[B:%.*]], 60
898+
; CHECK-NEXT: [[OR1_NOT1:%.*]] = and i1 [[CMP1]], [[CMP2]]
899+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ult i64 [[C:%.*]], 60
900+
; CHECK-NEXT: [[NOT:%.*]] = and i1 [[OR1_NOT1]], [[CMP3]]
901+
; CHECK-NEXT: ret i1 [[NOT]]
902+
;
903+
%cmp1 = icmp ugt i64 %a, 23
904+
%cmp2 = icmp ugt i64 %b, 59
905+
%or1 = or i1 %cmp1, %cmp2
906+
%cmp3 = icmp ugt i64 %c, 59
907+
%or2 = or i1 %or1, %cmp3
908+
%not = xor i1 %or2, true
909+
ret i1 %not
910+
}
911+
912+
define i1 @test_invert_demorgan_or3(i32 %a, i32 %b) {
913+
; CHECK-LABEL: @test_invert_demorgan_or3(
914+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[A:%.*]], 178206
915+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[B:%.*]], -196608
916+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[TMP1]], -1506
917+
; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[B]], -917760
918+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ult i32 [[TMP2]], -716213
919+
; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[B]], -1114112
920+
; CHECK-NEXT: [[CMP4:%.*]] = icmp ult i32 [[TMP3]], -196112
921+
; CHECK-NEXT: [[OR1_NOT2:%.*]] = and i1 [[CMP1]], [[CMP2]]
922+
; CHECK-NEXT: [[OR2_NOT1:%.*]] = and i1 [[OR1_NOT2]], [[CMP3]]
923+
; CHECK-NEXT: [[NOT:%.*]] = and i1 [[OR2_NOT1]], [[CMP4]]
924+
; CHECK-NEXT: ret i1 [[NOT]]
925+
;
926+
%cmp1 = icmp eq i32 %a, 178206
927+
%v1 = add i32 %b, -195102
928+
%cmp2 = icmp ult i32 %v1, 1506
929+
%v2 = add i32 %b, -201547
930+
%cmp3 = icmp ult i32 %v2, 716213
931+
%v3 = add i32 %b, -918000
932+
%cmp4 = icmp ult i32 %v3, 196112
933+
%or1 = or i1 %cmp1, %cmp2
934+
%or2 = or i1 %or1, %cmp3
935+
%or3 = or i1 %or2, %cmp4
936+
%not = xor i1 %or3, true
937+
ret i1 %not
938+
}
939+
940+
define i1 @test_invert_demorgan_logical_or(i64 %x, i64 %y) {
941+
; CHECK-LABEL: @test_invert_demorgan_logical_or(
942+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i64 [[X:%.*]], 27
943+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i64 [[Y:%.*]], 0
944+
; CHECK-NEXT: [[SEL_NOT1:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
945+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i64 [[X]], 0
946+
; CHECK-NEXT: [[NOT:%.*]] = and i1 [[CMP3]], [[SEL_NOT1]]
947+
; CHECK-NEXT: ret i1 [[NOT]]
948+
;
949+
%cmp1 = icmp eq i64 %x, 27
950+
%cmp2 = icmp eq i64 %y, 0
951+
%sel = select i1 %cmp1, i1 true, i1 %cmp2
952+
%cmp3 = icmp eq i64 %x, 0
953+
%or = or i1 %cmp3, %sel
954+
%not = xor i1 %or, true
955+
ret i1 %not
956+
}
957+
958+
define i1 @test_invert_demorgan_and(i32 %a, i32 %b, i1 %cond) {
959+
; CHECK-LABEL: @test_invert_demorgan_and(
960+
; CHECK-NEXT: entry:
961+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 0
962+
; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[B1:%.*]], 0
963+
; CHECK-NEXT: [[AND_NOT1:%.*]] = or i1 [[CMP2]], [[CMP3]]
964+
; CHECK-NEXT: [[MERGE:%.*]] = or i1 [[AND_NOT1]], [[COND:%.*]]
965+
; CHECK-NEXT: br i1 [[MERGE]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
966+
; CHECK: if.then:
967+
; CHECK-NEXT: call void @f1()
968+
; CHECK-NEXT: unreachable
969+
; CHECK: if.else:
970+
; CHECK-NEXT: call void @f2()
971+
; CHECK-NEXT: unreachable
972+
;
973+
entry:
974+
%cmp1 = icmp eq i32 %a, 0
975+
%cmp2 = icmp ne i32 %b, 0
976+
%and = and i1 %cmp1, %cmp2
977+
%not = xor i1 %cond, true
978+
%merge = and i1 %not, %and
979+
br i1 %merge, label %if.then, label %if.else
980+
if.then:
981+
call void @f1()
982+
unreachable
983+
if.else:
984+
call void @f2()
985+
unreachable
986+
}
987+
988+
define i64 @test_invert_demorgan_and2(i64 %x) {
989+
; CHECK-LABEL: @test_invert_demorgan_and2(
990+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[X:%.*]]
991+
; CHECK-NEXT: [[SUB:%.*]] = or i64 [[TMP1]], -9223372036854775808
992+
; CHECK-NEXT: ret i64 [[SUB]]
993+
;
994+
%add = add i64 %x, 9223372036854775807
995+
%and = and i64 %add, 9223372036854775807
996+
%sub = xor i64 %and, -1
997+
ret i64 %sub
998+
}
999+
1000+
define i1 @test_invert_demorgan_and3(i32 %a, i32 %b) {
1001+
; CHECK-LABEL: @test_invert_demorgan_and3(
1002+
; CHECK-NEXT: [[ADD:%.*]] = sub i32 [[A:%.*]], [[B:%.*]]
1003+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[ADD]], 4095
1004+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 4095
1005+
; CHECK-NEXT: ret i1 [[CMP]]
1006+
;
1007+
%not = xor i32 %a, -1
1008+
%add = add i32 %b, %not
1009+
%and = and i32 %add, 4095
1010+
%cmp = icmp eq i32 %and, 0
1011+
ret i1 %cmp
1012+
}
1013+
1014+
define i1 @test_invert_demorgan_logical_and(i64 %x, i64 %y) {
1015+
; CHECK-LABEL: @test_invert_demorgan_logical_and(
1016+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i64 [[X:%.*]], 27
1017+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i64 [[Y:%.*]], 0
1018+
; CHECK-NEXT: [[SEL_NOT1:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]]
1019+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i64 [[X]], 0
1020+
; CHECK-NEXT: [[NOT:%.*]] = and i1 [[CMP3]], [[SEL_NOT1]]
1021+
; CHECK-NEXT: ret i1 [[NOT]]
1022+
;
1023+
%cmp1 = icmp eq i64 %x, 27
1024+
%cmp2 = icmp eq i64 %y, 0
1025+
%sel = select i1 %cmp1, i1 %cmp2, i1 false
1026+
%cmp3 = icmp eq i64 %x, 0
1027+
%or = or i1 %cmp3, %sel
1028+
%not = xor i1 %or, true
1029+
ret i1 %not
1030+
}
1031+
1032+
define i1 @test_invert_demorgan_and_multiuse(i32 %a, i32 %b, i1 %cond) {
1033+
; CHECK-LABEL: @test_invert_demorgan_and_multiuse(
1034+
; CHECK-NEXT: entry:
1035+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[A:%.*]], 0
1036+
; CHECK-NEXT: call void @use1(i1 [[CMP1]])
1037+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 0
1038+
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[COND:%.*]], true
1039+
; CHECK-NEXT: [[TMP0:%.*]] = and i1 [[CMP2]], [[NOT]]
1040+
; CHECK-NEXT: [[MERGE:%.*]] = and i1 [[TMP0]], [[CMP1]]
1041+
; CHECK-NEXT: br i1 [[MERGE]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
1042+
; CHECK: if.then:
1043+
; CHECK-NEXT: call void @f1()
1044+
; CHECK-NEXT: unreachable
1045+
; CHECK: if.else:
1046+
; CHECK-NEXT: call void @f2()
1047+
; CHECK-NEXT: unreachable
1048+
;
1049+
entry:
1050+
%cmp1 = icmp eq i32 %a, 0
1051+
call void @use1(i1 %cmp1)
1052+
%cmp2 = icmp ne i32 %b, 0
1053+
%and = and i1 %cmp1, %cmp2
1054+
%not = xor i1 %cond, true
1055+
%merge = and i1 %not, %and
1056+
br i1 %merge, label %if.then, label %if.else
1057+
if.then:
1058+
call void @f1()
1059+
unreachable
1060+
if.else:
1061+
call void @f2()
1062+
unreachable
1063+
}

llvm/test/Transforms/InstCombine/pr63791.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ define void @y() {
1515
; CHECK-NEXT: store i1 true, ptr poison, align 1
1616
; CHECK-NEXT: br i1 poison, label [[FOR_COND_I]], label [[FOR_COND5_PREHEADER_I]]
1717
; CHECK: for.cond5.preheader.i:
18-
; CHECK-NEXT: br i1 false, label [[FOR_INC19_I:%.*]], label [[FOR_COND1_LOOPEXIT_I:%.*]]
18+
; CHECK-NEXT: br i1 true, label [[FOR_COND1_LOOPEXIT_I:%.*]], label [[FOR_INC19_I:%.*]]
1919
; CHECK: for.inc19.i:
2020
; CHECK-NEXT: br i1 poison, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]]
2121
;

0 commit comments

Comments
 (0)