[llvm] ec06b38 - [InstCombine] canonicalize 'not' ops before logical shifts
Sanjay Patel via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 22 06:39:37 PDT 2020
Author: Sanjay Patel
Date: 2020-08-22T09:38:13-04:00
New Revision: ec06b381304140b2553cfdfae5a063f39c5c59ff
URL: https://github.com/llvm/llvm-project/commit/ec06b381304140b2553cfdfae5a063f39c5c59ff
DIFF: https://github.com/llvm/llvm-project/commit/ec06b381304140b2553cfdfae5a063f39c5c59ff.diff
LOG: [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
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
llvm/test/Transforms/InstCombine/2010-11-01-lshr-mask.ll
llvm/test/Transforms/InstCombine/and-xor-merge.ll
llvm/test/Transforms/InstCombine/compare-signs.ll
llvm/test/Transforms/InstCombine/icmp.ll
llvm/test/Transforms/InstCombine/xor.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 3abdf4072589..85d84fb5e5db 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3267,6 +3267,24 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (match(Op0, m_Or(m_Value(X), m_APInt(C))) &&
MaskedValueIsZero(X, *C, 0, &I))
return BinaryOperator::CreateXor(X, ConstantInt::get(Ty, *C ^ *RHSC));
+
+ // If RHSC is inverting the remaining bits of shifted X,
+ // canonicalize to a 'not' before the shift to help SCEV and codegen:
+ // (X << C) ^ RHSC --> ~X << C
+ if (match(Op0, m_OneUse(m_Shl(m_Value(X), m_APInt(C)))) &&
+ *RHSC == APInt::getAllOnesValue(Ty->getScalarSizeInBits()).shl(*C)) {
+ Value *NotX = Builder.CreateNot(X);
+ return BinaryOperator::CreateShl(NotX, ConstantInt::get(Ty, *C));
+ }
+ // (X >>u C) ^ RHSC --> ~X >>u C
+ if (match(Op0, m_OneUse(m_LShr(m_Value(X), m_APInt(C)))) &&
+ *RHSC == APInt::getAllOnesValue(Ty->getScalarSizeInBits()).lshr(*C)) {
+ Value *NotX = Builder.CreateNot(X);
+ return BinaryOperator::CreateLShr(NotX, ConstantInt::get(Ty, *C));
+ }
+ // TODO: We could handle 'ashr' here as well. That would be matching
+ // a 'not' op and moving it before the shift. Doing that requires
+ // preventing the inverse fold in canShiftBinOpWithConstantRHS().
}
}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
index e6d1b31e8df5..983b45cbb11f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
@@ -667,9 +667,12 @@ static bool canShiftBinOpWithConstantRHS(BinaryOperator &Shift,
case Instruction::Add:
return Shift.getOpcode() == Instruction::Shl;
case Instruction::Or:
- case Instruction::Xor:
case Instruction::And:
return true;
+ case Instruction::Xor:
+ // Do not change a 'not' of logical shift because that would create a normal
+ // 'xor'. The 'not' is likely better for analysis, SCEV, and codegen.
+ return !(Shift.isLogicalShift() && match(BO, m_Not(m_Value())));
}
}
diff --git a/llvm/test/Transforms/InstCombine/2010-11-01-lshr-mask.ll b/llvm/test/Transforms/InstCombine/2010-11-01-lshr-mask.ll
index 7d75e16793a7..f367b47fe05b 100644
--- a/llvm/test/Transforms/InstCombine/2010-11-01-lshr-mask.ll
+++ b/llvm/test/Transforms/InstCombine/2010-11-01-lshr-mask.ll
@@ -5,9 +5,9 @@
define i32 @main(i32 %argc) {
; CHECK-LABEL: @main(
; CHECK-NEXT: [[T3151:%.*]] = trunc i32 [[ARGC:%.*]] to i8
-; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[T3151]], 5
-; CHECK-NEXT: [[T4126:%.*]] = and i8 [[TMP1]], 64
-; CHECK-NEXT: [[T4127:%.*]] = xor i8 [[T4126]], 64
+; CHECK-NEXT: [[T3163:%.*]] = xor i8 [[T3151]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[T3163]], 5
+; CHECK-NEXT: [[T4127:%.*]] = and i8 [[TMP1]], 64
; CHECK-NEXT: [[T4086:%.*]] = zext i8 [[T4127]] to i32
; CHECK-NEXT: ret i32 [[T4086]]
;
diff --git a/llvm/test/Transforms/InstCombine/and-xor-merge.ll b/llvm/test/Transforms/InstCombine/and-xor-merge.ll
index 3d86754a6635..fdb6e8365ef6 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-merge.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-merge.ll
@@ -28,11 +28,9 @@ define i32 @test2(i32 %x, i32 %y, i32 %z) {
define i32 @PR38761(i32 %a, i32 %b) {
; CHECK-LABEL: @PR38761(
-; CHECK-NEXT: [[A_LOBIT:%.*]] = lshr i32 [[A:%.*]], 31
-; CHECK-NEXT: [[A_LOBIT_NOT:%.*]] = xor i32 [[A_LOBIT]], 1
-; CHECK-NEXT: [[B_LOBIT:%.*]] = lshr i32 [[B:%.*]], 31
-; CHECK-NEXT: [[B_LOBIT_NOT:%.*]] = xor i32 [[B_LOBIT]], -1
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[A_LOBIT_NOT]], [[B_LOBIT_NOT]]
+; CHECK-NEXT: [[B_LOBIT_NOT1_DEMORGAN:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[B_LOBIT_NOT1:%.*]] = xor i32 [[B_LOBIT_NOT1_DEMORGAN]], -1
+; CHECK-NEXT: [[AND:%.*]] = lshr i32 [[B_LOBIT_NOT1]], 31
; CHECK-NEXT: ret i32 [[AND]]
;
%a.lobit = lshr i32 %a, 31
diff --git a/llvm/test/Transforms/InstCombine/compare-signs.ll b/llvm/test/Transforms/InstCombine/compare-signs.ll
index c6c56f2361ec..99a117adbb90 100644
--- a/llvm/test/Transforms/InstCombine/compare-signs.ll
+++ b/llvm/test/Transforms/InstCombine/compare-signs.ll
@@ -6,8 +6,8 @@
define i32 @test1(i32 %a, i32 %b) nounwind readnone {
; CHECK-LABEL: @test1(
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
-; CHECK-NEXT: [[DOTLOBIT:%.*]] = lshr i32 [[TMP1]], 31
-; CHECK-NEXT: [[DOTLOBIT_NOT:%.*]] = xor i32 [[DOTLOBIT]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], -1
+; CHECK-NEXT: [[DOTLOBIT_NOT:%.*]] = lshr i32 [[TMP2]], 31
; CHECK-NEXT: ret i32 [[DOTLOBIT_NOT]]
;
%t0 = icmp sgt i32 %a, -1
@@ -36,8 +36,8 @@ define i32 @test2(i32 %a, i32 %b) nounwind readnone {
define i32 @test3(i32 %a, i32 %b) nounwind readnone {
; CHECK-LABEL: @test3(
; CHECK-NEXT: [[T2_UNSHIFTED:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[T2_UNSHIFTED_LOBIT:%.*]] = lshr i32 [[T2_UNSHIFTED]], 31
-; CHECK-NEXT: [[T2_UNSHIFTED_LOBIT_NOT:%.*]] = xor i32 [[T2_UNSHIFTED_LOBIT]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[T2_UNSHIFTED]], -1
+; CHECK-NEXT: [[T2_UNSHIFTED_LOBIT_NOT:%.*]] = lshr i32 [[TMP1]], 31
; CHECK-NEXT: ret i32 [[T2_UNSHIFTED_LOBIT_NOT]]
;
%t0 = lshr i32 %a, 31
@@ -68,8 +68,8 @@ define <2 x i32> @test3vec(<2 x i32> %a, <2 x i32> %b) nounwind readnone {
define i32 @test3i(i32 %a, i32 %b) nounwind readnone {
; CHECK-LABEL: @test3i(
; CHECK-NEXT: [[T01:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[T01]], 31
-; CHECK-NEXT: [[T4:%.*]] = xor i32 [[TMP1]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[T01]], -1
+; CHECK-NEXT: [[T4:%.*]] = lshr i32 [[TMP1]], 31
; CHECK-NEXT: ret i32 [[T4]]
;
%t0 = lshr i32 %a, 29
diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index 7addc85c4c8b..3b223d71f9d4 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -25,8 +25,8 @@ define <2 x i32> @test1vec(<2 x i32> %X) {
define i32 @test2(i32 %X) {
; CHECK-LABEL: @test2(
-; CHECK-NEXT: [[X_LOBIT:%.*]] = lshr i32 [[X:%.*]], 31
-; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = xor i32 [[X_LOBIT]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = lshr i32 [[TMP1]], 31
; CHECK-NEXT: ret i32 [[X_LOBIT_NOT]]
;
%a = icmp ult i32 %X, -2147483648
@@ -36,8 +36,8 @@ define i32 @test2(i32 %X) {
define <2 x i32> @test2vec(<2 x i32> %X) {
; CHECK-LABEL: @test2vec(
-; CHECK-NEXT: [[X_LOBIT:%.*]] = lshr <2 x i32> [[X:%.*]], <i32 31, i32 31>
-; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = xor <2 x i32> [[X_LOBIT]], <i32 1, i32 1>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X:%.*]], <i32 -1, i32 -1>
+; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = lshr <2 x i32> [[TMP1]], <i32 31, i32 31>
; CHECK-NEXT: ret <2 x i32> [[X_LOBIT_NOT]]
;
%a = icmp ult <2 x i32> %X, <i32 -2147483648, i32 -2147483648>
diff --git a/llvm/test/Transforms/InstCombine/xor.ll b/llvm/test/Transforms/InstCombine/xor.ll
index 5db898552562..312b0125f626 100644
--- a/llvm/test/Transforms/InstCombine/xor.ll
+++ b/llvm/test/Transforms/InstCombine/xor.ll
@@ -1015,8 +1015,8 @@ define i32 @not_is_canonical(i32 %x, i32 %y) {
define i8 @not_shl(i8 %x) {
; CHECK-LABEL: @not_shl(
-; CHECK-NEXT: [[A:%.*]] = shl i8 [[X:%.*]], 7
-; CHECK-NEXT: [[R:%.*]] = xor i8 [[A]], -128
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[R:%.*]] = shl i8 [[TMP1]], 7
; CHECK-NEXT: ret i8 [[R]]
;
%a = shl i8 %x, 7
@@ -1026,8 +1026,8 @@ define i8 @not_shl(i8 %x) {
define <2 x i8> @not_shl_vec(<2 x i8> %x) {
; CHECK-LABEL: @not_shl_vec(
-; CHECK-NEXT: [[A:%.*]] = shl <2 x i8> [[X:%.*]], <i8 5, i8 5>
-; CHECK-NEXT: [[R:%.*]] = xor <2 x i8> [[A]], <i8 -32, i8 -32>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[X:%.*]], <i8 -1, i8 -1>
+; CHECK-NEXT: [[R:%.*]] = shl <2 x i8> [[TMP1]], <i8 5, i8 5>
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%a = shl <2 x i8> %x, <i8 5, i8 5>
@@ -1035,6 +1035,8 @@ define <2 x i8> @not_shl_vec(<2 x i8> %x) {
ret <2 x i8> %r
}
+; negative test
+
define i8 @not_shl_extra_use(i8 %x) {
; CHECK-LABEL: @not_shl_extra_use(
; CHECK-NEXT: [[A:%.*]] = shl i8 [[X:%.*]], 7
@@ -1048,6 +1050,8 @@ define i8 @not_shl_extra_use(i8 %x) {
ret i8 %r
}
+; negative test
+
define i8 @not_shl_wrong_const(i8 %x) {
; CHECK-LABEL: @not_shl_wrong_const(
; CHECK-NEXT: [[A:%.*]] = shl i8 [[X:%.*]], 6
@@ -1061,8 +1065,8 @@ define i8 @not_shl_wrong_const(i8 %x) {
define i8 @not_lshr(i8 %x) {
; CHECK-LABEL: @not_lshr(
-; CHECK-NEXT: [[A:%.*]] = lshr i8 [[X:%.*]], 5
-; CHECK-NEXT: [[R:%.*]] = xor i8 [[A]], 7
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[R:%.*]] = lshr i8 [[TMP1]], 5
; CHECK-NEXT: ret i8 [[R]]
;
%a = lshr i8 %x, 5
@@ -1072,8 +1076,8 @@ define i8 @not_lshr(i8 %x) {
define <2 x i8> @not_lshr_vec(<2 x i8> %x) {
; CHECK-LABEL: @not_lshr_vec(
-; CHECK-NEXT: [[A:%.*]] = lshr <2 x i8> [[X:%.*]], <i8 7, i8 7>
-; CHECK-NEXT: [[R:%.*]] = xor <2 x i8> [[A]], <i8 1, i8 1>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[X:%.*]], <i8 -1, i8 -1>
+; CHECK-NEXT: [[R:%.*]] = lshr <2 x i8> [[TMP1]], <i8 7, i8 7>
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%a = lshr <2 x i8> %x, <i8 7, i8 7>
@@ -1081,6 +1085,8 @@ define <2 x i8> @not_lshr_vec(<2 x i8> %x) {
ret <2 x i8> %r
}
+; negative test
+
define i8 @not_lshr_extra_use(i8 %x) {
; CHECK-LABEL: @not_lshr_extra_use(
; CHECK-NEXT: [[A:%.*]] = lshr i8 [[X:%.*]], 5
@@ -1094,6 +1100,8 @@ define i8 @not_lshr_extra_use(i8 %x) {
ret i8 %r
}
+; negative test
+
define i8 @not_lshr_wrong_const(i8 %x) {
; CHECK-LABEL: @not_lshr_wrong_const(
; CHECK-NEXT: [[A:%.*]] = lshr i8 [[X:%.*]], 5
@@ -1116,6 +1124,8 @@ define i8 @ashr_not(i8 %x) {
ret i8 %r
}
+; Unlike the logicial shifts, 'not' is canonicalized after ashr.
+
define i8 @not_ashr(i8 %x) {
; CHECK-LABEL: @not_ashr(
; CHECK-NEXT: [[A:%.*]] = ashr i8 [[X:%.*]], 5
More information about the llvm-commits
mailing list