1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[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
This commit is contained in:
Sanjay Patel 2020-08-22 09:38:13 -04:00
parent 8dd06c7b17
commit 8998944672
7 changed files with 56 additions and 27 deletions

View File

@ -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().
}
}

View File

@ -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())));
}
}

View File

@ -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]]
;

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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