diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -6472,6 +6472,31 @@ if (Instruction *Res = foldReductionIdiom(I, Builder, DL)) return Res; + { + Value *A, *B, *C, *D; + + // Find hidden xor optimization (only works if `C` and `D` have no common bits) + // (icmp eq (and (select A, C, D), (select B, C, D)), 0) --> (xor A, B) + if (I.getPredicate() == ICmpInst::ICMP_EQ && match(Op0, + m_c_And( + m_Select(m_Value(A), m_Value(C), m_Value(D)), + m_Select(m_Value(B), m_Deferred(C), m_Deferred(D)) + ) + ) && match(Op1, m_Zero()) && isKnownNonZero(C, DL, /*Depth*/0, &AC, &I, &DT) && isKnownNonZero(D, DL, /*Depth*/0, &AC, &I, &DT) && haveNoCommonBitsSet(C, D, DL, &AC, &I, &DT)) { + return BinaryOperator::CreateXor(A, B); + } + // (icmp ne (and (select A, C, D), (select B, C, D)), 0) --> (xot (xor A, B), true) + if (I.getPredicate() == ICmpInst::ICMP_NE && match(Op0, + m_OneUse(m_c_And( + m_OneUse(m_Select(m_Value(A), m_Value(C), m_Value(D))), + m_OneUse(m_Select(m_Value(B), m_Deferred(C), m_Deferred(D))) + )) + ) && match(Op1, m_Zero()) && isKnownNonZero(C, DL, /*Depth*/0, &AC, &I, &DT) && isKnownNonZero(D, DL, /*Depth*/0, &AC, &I, &DT) && haveNoCommonBitsSet(C, D, DL, &AC, &I, &DT)) { + Value *Xor = Builder.CreateXor(A, B); + return BinaryOperator::CreateNeg(Xor); + } + } + return Changed ? &I : nullptr; } diff --git a/llvm/test/Transforms/InstCombine/select_and_icmpeq.ll b/llvm/test/Transforms/InstCombine/select_and_icmpeq.ll --- a/llvm/test/Transforms/InstCombine/select_and_icmpeq.ll +++ b/llvm/test/Transforms/InstCombine/select_and_icmpeq.ll @@ -1,18 +1,16 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=instcombine -S | FileCheck %s -declare void @use32(i32) -declare void @use16(i16) -declare void @use7(i7) -declare void @use3(i3) - define i1 @select_and_icmpeq_i32_commuted1(i1 %x, i1 %y) { ; CHECK-LABEL: @select_and_icmpeq_i32_commuted1( -; CHECK-NEXT: [[ICMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i32 2, i32 1 +; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i32 1, i32 2 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[S1]], [[S2]] +; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i32 [[AND]], 0 ; CHECK-NEXT: ret i1 [[ICMP]] ; %s1 = select i1 %x, i32 2, i32 1 - %s2 = select i1 %y, i32 2, i32 1 + %s2 = select i1 %y, i32 1, i32 2 %and = and i32 %s1, %s2 %icmp = icmp eq i32 %and, 0 ret i1 %icmp @@ -62,16 +60,13 @@ %s1 = select i1 %y, i9 3, i9 16 %s2 = select i1 %x, i9 3, i9 16 %and = and i9 %s2, %s1 - %icmp = icmp eq i9 %and, 0 + %icmp = icmp eq i9 0, %and ret i1 %icmp } define <5 x i1> @select_and_icmpeq_i1vec(<5 x i1> %x, <5 x i1> %y) { ; CHECK-LABEL: @select_and_icmpeq_i1vec( -; CHECK-NEXT: [[S1:%.*]] = select <5 x i1> [[Y:%.*]], <5 x i32> , <5 x i32> -; CHECK-NEXT: [[S2:%.*]] = select <5 x i1> [[X:%.*]], <5 x i32> , <5 x i32> -; CHECK-NEXT: [[AND:%.*]] = and <5 x i32> [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp eq <5 x i32> [[AND]], zeroinitializer +; CHECK-NEXT: [[ICMP:%.*]] = xor <5 x i1> [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret <5 x i1> [[ICMP]] ; %s1 = select <5 x i1> %y, <5 x i32> , <5 x i32> @@ -80,75 +75,3 @@ %icmp = icmp eq <5 x i32> %and, ret <5 x i1> %icmp } - -define i1 @select_and_icmpeq_i32_multiuse_select1(i1 %x, i1 %y) { -; CHECK-LABEL: @select_and_icmpeq_i32_multiuse_select1( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i32 4, i32 1 -; CHECK-NEXT: call void @use32(i32 [[S1]]) -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i32 4, i32 1 -; CHECK-NEXT: [[AND:%.*]] = and i32 [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i32 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] -; - %s1 = select i1 %y, i32 4, i32 1 - call void @use32(i32 %s1) ; extra use of first select - %s2 = select i1 %x, i32 4, i32 1 - %and = and i32 %s2, %s1 - %icmp = icmp eq i32 %and, 0 - ret i1 %icmp -} - -define i1 @select_and_icmpeq_i16_multiuse_select2(i1 %x, i1 %y) { -; CHECK-LABEL: @select_and_icmpeq_i16_multiuse_select2( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i16 4, i16 1 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i16 4, i16 1 -; CHECK-NEXT: call void @use16(i16 [[S2]]) -; CHECK-NEXT: [[AND:%.*]] = and i16 [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i16 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] -; - %s1 = select i1 %y, i16 4, i16 1 - %s2 = select i1 %x, i16 4, i16 1 - call void @use16(i16 %s2) ; extra use of second select - %and = and i16 %s2, %s1 - %icmp = icmp eq i16 %and, 0 - ret i1 %icmp -} - -define i1 @select_and_icmpeq_i3_multiuse_and(i1 %x, i1 %y) { -; CHECK-LABEL: @select_and_icmpeq_i3_multiuse_and( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i3 1, i3 2 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i3 1, i3 2 -; CHECK-NEXT: [[AND:%.*]] = and i3 [[S2]], [[S1]] -; CHECK-NEXT: call void @use3(i3 [[AND]]) -; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i3 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] -; - %s1 = select i1 %y, i3 1, i3 2 - %s2 = select i1 %x, i3 1, i3 2 - %and = and i3 %s2, %s1 - call void @use3(i3 %and) ; extra use of and - %icmp = icmp eq i3 %and, 0 - ret i1 %icmp -} - -define i1 @select_and_icmpeq_i7_multiuse_all(i1 %x, i1 %y) { -; CHECK-LABEL: @select_and_icmpeq_i7_multiuse_all( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i7 1, i7 2 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i7 1, i7 2 -; CHECK-NEXT: [[AND:%.*]] = and i7 [[S2]], [[S1]] -; CHECK-NEXT: call void @use7(i7 [[S1]]) -; CHECK-NEXT: call void @use7(i7 [[S2]]) -; CHECK-NEXT: call void @use7(i7 [[AND]]) -; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i7 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] -; - %s1 = select i1 %y, i7 1, i7 2 - %s2 = select i1 %x, i7 1, i7 2 - %and = and i7 %s2, %s1 - call void @use7(i7 %s1) ; extra use of first select - call void @use7(i7 %s2) ; extra use of second select - call void @use7(i7 %and) ; extra use of and - %icmp = icmp eq i7 %and, 0 - ret i1 %icmp -} diff --git a/llvm/test/Transforms/InstCombine/select_and_icmpne.ll b/llvm/test/Transforms/InstCombine/select_and_icmpne.ll --- a/llvm/test/Transforms/InstCombine/select_and_icmpne.ll +++ b/llvm/test/Transforms/InstCombine/select_and_icmpne.ll @@ -8,11 +8,8 @@ define i1 @select_and_icmpne_i32_commuted1(i1 %x, i1 %y) { ; CHECK-LABEL: @select_and_icmpne_i32_commuted1( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i32 2, i32 1 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i32 2, i32 1 -; CHECK-NEXT: [[AND:%.*]] = and i32 [[S1]], [[S2]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i1 [[TMP1]] ; %s1 = select i1 %x, i32 2, i32 1 %s2 = select i1 %y, i32 2, i32 1 @@ -23,11 +20,8 @@ define i1 @select_and_icmpne_i32_commuted2(i1 %x, i1 %y) { ; CHECK-LABEL: @select_and_icmpne_i32_commuted2( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i32 4, i32 1 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i32 4, i32 1 -; CHECK-NEXT: [[AND:%.*]] = and i32 [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i1 [[TMP1]] ; %s1 = select i1 %y, i32 4, i32 1 %s2 = select i1 %x, i32 4, i32 1 @@ -38,11 +32,8 @@ define i1 @select_and_icmpne_i32_commuted3(i1 %x, i1 %y) { ; CHECK-LABEL: @select_and_icmpne_i32_commuted3( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i32 3, i32 12 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i32 3, i32 12 -; CHECK-NEXT: [[AND:%.*]] = and i32 [[S1]], [[S2]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: ret i1 [[TMP1]] ; %s1 = select i1 %y, i32 3, i32 12 %s2 = select i1 %x, i32 3, i32 12 @@ -53,11 +44,8 @@ define i1 @select_and_icmpne_i32_commuted4(i1 %x, i1 %y) { ; CHECK-LABEL: @select_and_icmpne_i32_commuted4( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i32 3, i32 16 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i32 3, i32 16 -; CHECK-NEXT: [[AND:%.*]] = and i32 [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i1 [[TMP1]] ; %s1 = select i1 %y, i32 3, i32 16 %s2 = select i1 %x, i32 3, i32 16 @@ -68,11 +56,8 @@ define i1 @select_and_icmpne_i9(i1 %x, i1 %y) { ; CHECK-LABEL: @select_and_icmpne_i9( -; CHECK-NEXT: [[S1:%.*]] = select i1 [[Y:%.*]], i9 3, i9 16 -; CHECK-NEXT: [[S2:%.*]] = select i1 [[X:%.*]], i9 3, i9 16 -; CHECK-NEXT: [[AND:%.*]] = and i9 [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i9 [[AND]], 0 -; CHECK-NEXT: ret i1 [[ICMP]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i1 [[TMP1]] ; %s1 = select i1 %y, i9 3, i9 16 %s2 = select i1 %x, i9 3, i9 16 @@ -83,11 +68,8 @@ define <5 x i1> @select_and_icmpne_i1vec(<5 x i1> %x, <5 x i1> %y) { ; CHECK-LABEL: @select_and_icmpne_i1vec( -; CHECK-NEXT: [[S1:%.*]] = select <5 x i1> [[Y:%.*]], <5 x i32> , <5 x i32> -; CHECK-NEXT: [[S2:%.*]] = select <5 x i1> [[X:%.*]], <5 x i32> , <5 x i32> -; CHECK-NEXT: [[AND:%.*]] = and <5 x i32> [[S2]], [[S1]] -; CHECK-NEXT: [[ICMP:%.*]] = icmp ne <5 x i32> [[AND]], zeroinitializer -; CHECK-NEXT: ret <5 x i1> [[ICMP]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <5 x i1> [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret <5 x i1> [[TMP1]] ; %s1 = select <5 x i1> %y, <5 x i32> , <5 x i32> %s2 = select <5 x i1> %x, <5 x i32> , <5 x i32>