diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -3267,6 +3267,24 @@ 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 --- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp @@ -667,9 +667,12 @@ 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 --- 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 --- a/llvm/test/Transforms/InstCombine/and-xor-merge.ll +++ b/llvm/test/Transforms/InstCombine/and-xor-merge.ll @@ -28,11 +28,9 @@ 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 --- 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 @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 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 --- a/llvm/test/Transforms/InstCombine/icmp.ll +++ b/llvm/test/Transforms/InstCombine/icmp.ll @@ -25,8 +25,8 @@ 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 <2 x i32> @test2vec(<2 x i32> %X) { ; CHECK-LABEL: @test2vec( -; CHECK-NEXT: [[X_LOBIT:%.*]] = lshr <2 x i32> [[X:%.*]], -; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = xor <2 x i32> [[X_LOBIT]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X:%.*]], +; CHECK-NEXT: [[X_LOBIT_NOT:%.*]] = lshr <2 x i32> [[TMP1]], ; CHECK-NEXT: ret <2 x i32> [[X_LOBIT_NOT]] ; %a = icmp ult <2 x i32> %X, diff --git a/llvm/test/Transforms/InstCombine/xor.ll b/llvm/test/Transforms/InstCombine/xor.ll --- a/llvm/test/Transforms/InstCombine/xor.ll +++ b/llvm/test/Transforms/InstCombine/xor.ll @@ -1015,8 +1015,8 @@ 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 <2 x i8> @not_shl_vec(<2 x i8> %x) { ; CHECK-LABEL: @not_shl_vec( -; CHECK-NEXT: [[A:%.*]] = shl <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[R:%.*]] = xor <2 x i8> [[A]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[R:%.*]] = shl <2 x i8> [[TMP1]], ; CHECK-NEXT: ret <2 x i8> [[R]] ; %a = shl <2 x i8> %x, @@ -1035,6 +1035,8 @@ 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 @@ 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_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 <2 x i8> @not_lshr_vec(<2 x i8> %x) { ; CHECK-LABEL: @not_lshr_vec( -; CHECK-NEXT: [[A:%.*]] = lshr <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[R:%.*]] = xor <2 x i8> [[A]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[R:%.*]] = lshr <2 x i8> [[TMP1]], ; CHECK-NEXT: ret <2 x i8> [[R]] ; %a = lshr <2 x i8> %x, @@ -1081,6 +1085,8 @@ 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 @@ 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 @@ 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