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:
parent
8dd06c7b17
commit
8998944672
@ -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().
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]]
|
||||
;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user