Skip to content

Commit ec06b38

Browse files
committed
[InstCombine] canonicalize 'not' ops before logical shifts
This reverses the existing transform that would uniformly canonicalize any 'xor' after any shift. In the case of logical shifts, that turns a 'not' into an arbitrary 'xor' with constant, and that's probably not as good for analysis, SCEV, or codegen. The SCEV motivating case is discussed in: http://bugs.llvm.org/PR47136 There's an analysis motivating case at: http://bugs.llvm.org/PR38781 I did draft a patch that would do the same for 'ashr' but that's questionable because it's just swapping the position of a 'not' and uncovers at least 2 missing folds that we would probably need to deal with as preliminary steps. Alive proofs: https://rise4fun.com/Alive/BBV Name: shift right of 'not' Pre: C2 == (-1 u>> C1) %a = lshr i8 %x, C1 %r = xor i8 %a, C2 => %n = xor i8 %x, -1 %r = lshr i8 %n, C1 Name: shift left of 'not' Pre: C2 == (-1 << C1) %a = shl i8 %x, C1 %r = xor i8 %a, C2 => %n = xor i8 %x, -1 %r = shl i8 %n, C1 Name: ashr of 'not' %a = ashr i8 %x, C1 %r = xor i8 %a, -1 => %n = xor i8 %x, -1 %r = ashr i8 %n, C1 Differential Revision: https://reviews.llvm.org/D86243
1 parent 2fc7c85 commit ec06b38

File tree

7 files changed

+56
-27
lines changed

7 files changed

+56
-27
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,6 +3267,24 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
32673267
if (match(Op0, m_Or(m_Value(X), m_APInt(C))) &&
32683268
MaskedValueIsZero(X, *C, 0, &I))
32693269
return BinaryOperator::CreateXor(X, ConstantInt::get(Ty, *C ^ *RHSC));
3270+
3271+
// If RHSC is inverting the remaining bits of shifted X,
3272+
// canonicalize to a 'not' before the shift to help SCEV and codegen:
3273+
// (X << C) ^ RHSC --> ~X << C
3274+
if (match(Op0, m_OneUse(m_Shl(m_Value(X), m_APInt(C)))) &&
3275+
*RHSC == APInt::getAllOnesValue(Ty->getScalarSizeInBits()).shl(*C)) {
3276+
Value *NotX = Builder.CreateNot(X);
3277+
return BinaryOperator::CreateShl(NotX, ConstantInt::get(Ty, *C));
3278+
}
3279+
// (X >>u C) ^ RHSC --> ~X >>u C
3280+
if (match(Op0, m_OneUse(m_LShr(m_Value(X), m_APInt(C)))) &&
3281+
*RHSC == APInt::getAllOnesValue(Ty->getScalarSizeInBits()).lshr(*C)) {
3282+
Value *NotX = Builder.CreateNot(X);
3283+
return BinaryOperator::CreateLShr(NotX, ConstantInt::get(Ty, *C));
3284+
}
3285+
// TODO: We could handle 'ashr' here as well. That would be matching
3286+
// a 'not' op and moving it before the shift. Doing that requires
3287+
// preventing the inverse fold in canShiftBinOpWithConstantRHS().
32703288
}
32713289
}
32723290

llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,12 @@ static bool canShiftBinOpWithConstantRHS(BinaryOperator &Shift,
667667
case Instruction::Add:
668668
return Shift.getOpcode() == Instruction::Shl;
669669
case Instruction::Or:
670-
case Instruction::Xor:
671670
case Instruction::And:
672671
return true;
672+
case Instruction::Xor:
673+
// Do not change a 'not' of logical shift because that would create a normal
674+
// 'xor'. The 'not' is likely better for analysis, SCEV, and codegen.
675+
return !(Shift.isLogicalShift() && match(BO, m_Not(m_Value())));
673676
}
674677
}
675678

llvm/test/Transforms/InstCombine/2010-11-01-lshr-mask.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
define i32 @main(i32 %argc) {
66
; CHECK-LABEL: @main(
77
; CHECK-NEXT: [[T3151:%.*]] = trunc i32 [[ARGC:%.*]] to i8
8-
; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[T3151]], 5
9-
; CHECK-NEXT: [[T4126:%.*]] = and i8 [[TMP1]], 64
10-
; CHECK-NEXT: [[T4127:%.*]] = xor i8 [[T4126]], 64
8+
; CHECK-NEXT: [[T3163:%.*]] = xor i8 [[T3151]], -1
9+
; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[T3163]], 5
10+
; CHECK-NEXT: [[T4127:%.*]] = and i8 [[TMP1]], 64
1111
; CHECK-NEXT: [[T4086:%.*]] = zext i8 [[T4127]] to i32
1212
; CHECK-NEXT: ret i32 [[T4086]]
1313
;

llvm/test/Transforms/InstCombine/and-xor-merge.ll

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,9 @@ define i32 @test2(i32 %x, i32 %y, i32 %z) {
2828

2929
define i32 @PR38761(i32 %a, i32 %b) {
3030
; CHECK-LABEL: @PR38761(
31-
; CHECK-NEXT: [[A_LOBIT:%.*]] = lshr i32 [[A:%.*]], 31
32-
; CHECK-NEXT: [[A_LOBIT_NOT:%.*]] = xor i32 [[A_LOBIT]], 1
33-
; CHECK-NEXT: [[B_LOBIT:%.*]] = lshr i32 [[B:%.*]], 31
34-
; CHECK-NEXT: [[B_LOBIT_NOT:%.*]] = xor i32 [[B_LOBIT]], -1
35-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[A_LOBIT_NOT]], [[B_LOBIT_NOT]]
31+
; CHECK-NEXT: [[B_LOBIT_NOT1_DEMORGAN:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
32+
; CHECK-NEXT: [[B_LOBIT_NOT1:%.*]] = xor i32 [[B_LOBIT_NOT1_DEMORGAN]], -1
33+
; CHECK-NEXT: [[AND:%.*]] = lshr i32 [[B_LOBIT_NOT1]], 31
3634
; CHECK-NEXT: ret i32 [[AND]]
3735
;
3836
%a.lobit = lshr i32 %a, 31

llvm/test/Transforms/InstCombine/compare-signs.ll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
define i32 @test1(i32 %a, i32 %b) nounwind readnone {
77
; CHECK-LABEL: @test1(
88
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
9-
; CHECK-NEXT: [[DOTLOBIT:%.*]] = lshr i32 [[TMP1]], 31
10-
; CHECK-NEXT: [[DOTLOBIT_NOT:%.*]] = xor i32 [[DOTLOBIT]], 1
9+
; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], -1
10+
; CHECK-NEXT: [[DOTLOBIT_NOT:%.*]] = lshr i32 [[TMP2]], 31
1111
; CHECK-NEXT: ret i32 [[DOTLOBIT_NOT]]
1212
;
1313
%t0 = icmp sgt i32 %a, -1
@@ -36,8 +36,8 @@ define i32 @test2(i32 %a, i32 %b) nounwind readnone {
3636
define i32 @test3(i32 %a, i32 %b) nounwind readnone {
3737
; CHECK-LABEL: @test3(
3838
; CHECK-NEXT: [[T2_UNSHIFTED:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
39-
; CHECK-NEXT: [[T2_UNSHIFTED_LOBIT:%.*]] = lshr i32 [[T2_UNSHIFTED]], 31
40-
; CHECK-NEXT: [[T2_UNSHIFTED_LOBIT_NOT:%.*]] = xor i32 [[T2_UNSHIFTED_LOBIT]], 1
39+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[T2_UNSHIFTED]], -1
40+
; CHECK-NEXT: [[T2_UNSHIFTED_LOBIT_NOT:%.*]] = lshr i32 [[TMP1]], 31
4141
; CHECK-NEXT: ret i32 [[T2_UNSHIFTED_LOBIT_NOT]]
4242
;
4343
%t0 = lshr i32 %a, 31
@@ -68,8 +68,8 @@ define <2 x i32> @test3vec(<2 x i32> %a, <2 x i32> %b) nounwind readnone {
6868
define i32 @test3i(i32 %a, i32 %b) nounwind readnone {
6969
; CHECK-LABEL: @test3i(
7070
; CHECK-NEXT: [[T01:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
71-
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[T01]], 31
72-
; CHECK-NEXT: [[T4:%.*]] = xor i32 [[TMP1]], 1
71+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[T01]], -1
72+
; CHECK-NEXT: [[T4:%.*]] = lshr i32 [[TMP1]], 31
7373
; CHECK-NEXT: ret i32 [[T4]]
7474
;
7575
%t0 = lshr i32 %a, 29

llvm/test/Transforms/InstCombine/icmp.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ define <2 x i32> @test1vec(<2 x i32> %X) {
2525

2626
define i32 @test2(i32 %X) {
2727
; CHECK-LABEL: @test2(
28-
; CHECK-NEXT: [[X_LOBIT:%.*]] = lshr i32 [[X:%.*]], 31
29-
; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = xor i32 [[X_LOBIT]], 1
28+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], -1
29+
; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = lshr i32 [[TMP1]], 31
3030
; CHECK-NEXT: ret i32 [[X_LOBIT_NOT]]
3131
;
3232
%a = icmp ult i32 %X, -2147483648
@@ -36,8 +36,8 @@ define i32 @test2(i32 %X) {
3636

3737
define <2 x i32> @test2vec(<2 x i32> %X) {
3838
; CHECK-LABEL: @test2vec(
39-
; CHECK-NEXT: [[X_LOBIT:%.*]] = lshr <2 x i32> [[X:%.*]], <i32 31, i32 31>
40-
; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = xor <2 x i32> [[X_LOBIT]], <i32 1, i32 1>
39+
; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X:%.*]], <i32 -1, i32 -1>
40+
; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = lshr <2 x i32> [[TMP1]], <i32 31, i32 31>
4141
; CHECK-NEXT: ret <2 x i32> [[X_LOBIT_NOT]]
4242
;
4343
%a = icmp ult <2 x i32> %X, <i32 -2147483648, i32 -2147483648>

llvm/test/Transforms/InstCombine/xor.ll

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,8 +1015,8 @@ define i32 @not_is_canonical(i32 %x, i32 %y) {
10151015

10161016
define i8 @not_shl(i8 %x) {
10171017
; CHECK-LABEL: @not_shl(
1018-
; CHECK-NEXT: [[A:%.*]] = shl i8 [[X:%.*]], 7
1019-
; CHECK-NEXT: [[R:%.*]] = xor i8 [[A]], -128
1018+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X:%.*]], -1
1019+
; CHECK-NEXT: [[R:%.*]] = shl i8 [[TMP1]], 7
10201020
; CHECK-NEXT: ret i8 [[R]]
10211021
;
10221022
%a = shl i8 %x, 7
@@ -1026,15 +1026,17 @@ define i8 @not_shl(i8 %x) {
10261026

10271027
define <2 x i8> @not_shl_vec(<2 x i8> %x) {
10281028
; CHECK-LABEL: @not_shl_vec(
1029-
; CHECK-NEXT: [[A:%.*]] = shl <2 x i8> [[X:%.*]], <i8 5, i8 5>
1030-
; CHECK-NEXT: [[R:%.*]] = xor <2 x i8> [[A]], <i8 -32, i8 -32>
1029+
; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[X:%.*]], <i8 -1, i8 -1>
1030+
; CHECK-NEXT: [[R:%.*]] = shl <2 x i8> [[TMP1]], <i8 5, i8 5>
10311031
; CHECK-NEXT: ret <2 x i8> [[R]]
10321032
;
10331033
%a = shl <2 x i8> %x, <i8 5, i8 5>
10341034
%r = xor <2 x i8> %a, <i8 224, i8 224>
10351035
ret <2 x i8> %r
10361036
}
10371037

1038+
; negative test
1039+
10381040
define i8 @not_shl_extra_use(i8 %x) {
10391041
; CHECK-LABEL: @not_shl_extra_use(
10401042
; CHECK-NEXT: [[A:%.*]] = shl i8 [[X:%.*]], 7
@@ -1048,6 +1050,8 @@ define i8 @not_shl_extra_use(i8 %x) {
10481050
ret i8 %r
10491051
}
10501052

1053+
; negative test
1054+
10511055
define i8 @not_shl_wrong_const(i8 %x) {
10521056
; CHECK-LABEL: @not_shl_wrong_const(
10531057
; CHECK-NEXT: [[A:%.*]] = shl i8 [[X:%.*]], 6
@@ -1061,8 +1065,8 @@ define i8 @not_shl_wrong_const(i8 %x) {
10611065

10621066
define i8 @not_lshr(i8 %x) {
10631067
; CHECK-LABEL: @not_lshr(
1064-
; CHECK-NEXT: [[A:%.*]] = lshr i8 [[X:%.*]], 5
1065-
; CHECK-NEXT: [[R:%.*]] = xor i8 [[A]], 7
1068+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X:%.*]], -1
1069+
; CHECK-NEXT: [[R:%.*]] = lshr i8 [[TMP1]], 5
10661070
; CHECK-NEXT: ret i8 [[R]]
10671071
;
10681072
%a = lshr i8 %x, 5
@@ -1072,15 +1076,17 @@ define i8 @not_lshr(i8 %x) {
10721076

10731077
define <2 x i8> @not_lshr_vec(<2 x i8> %x) {
10741078
; CHECK-LABEL: @not_lshr_vec(
1075-
; CHECK-NEXT: [[A:%.*]] = lshr <2 x i8> [[X:%.*]], <i8 7, i8 7>
1076-
; CHECK-NEXT: [[R:%.*]] = xor <2 x i8> [[A]], <i8 1, i8 1>
1079+
; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[X:%.*]], <i8 -1, i8 -1>
1080+
; CHECK-NEXT: [[R:%.*]] = lshr <2 x i8> [[TMP1]], <i8 7, i8 7>
10771081
; CHECK-NEXT: ret <2 x i8> [[R]]
10781082
;
10791083
%a = lshr <2 x i8> %x, <i8 7, i8 7>
10801084
%r = xor <2 x i8> %a, <i8 1, i8 1>
10811085
ret <2 x i8> %r
10821086
}
10831087

1088+
; negative test
1089+
10841090
define i8 @not_lshr_extra_use(i8 %x) {
10851091
; CHECK-LABEL: @not_lshr_extra_use(
10861092
; CHECK-NEXT: [[A:%.*]] = lshr i8 [[X:%.*]], 5
@@ -1094,6 +1100,8 @@ define i8 @not_lshr_extra_use(i8 %x) {
10941100
ret i8 %r
10951101
}
10961102

1103+
; negative test
1104+
10971105
define i8 @not_lshr_wrong_const(i8 %x) {
10981106
; CHECK-LABEL: @not_lshr_wrong_const(
10991107
; CHECK-NEXT: [[A:%.*]] = lshr i8 [[X:%.*]], 5
@@ -1116,6 +1124,8 @@ define i8 @ashr_not(i8 %x) {
11161124
ret i8 %r
11171125
}
11181126

1127+
; Unlike the logicial shifts, 'not' is canonicalized after ashr.
1128+
11191129
define i8 @not_ashr(i8 %x) {
11201130
; CHECK-LABEL: @not_ashr(
11211131
; CHECK-NEXT: [[A:%.*]] = ashr i8 [[X:%.*]], 5

0 commit comments

Comments
 (0)