diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -671,6 +671,38 @@ return Builder.CreateOr(V, Y); } +/// Canonicalize a set or clear of a masked set of constant bits to +/// select-of-constants form. +static Instruction *foldSetClearBits(SelectInst &Sel, + InstCombiner::BuilderTy &Builder) { + Value *Cond = Sel.getCondition(); + Value *T = Sel.getTrueValue(); + Value *F = Sel.getFalseValue(); + Type *Ty = Sel.getType(); + Value *X; + const APInt *NotC, *C; + + // Cond ? (X & ~C) : (X | C) --> (X & ~C) | (Cond ? 0 : C) + if (match(T, m_And(m_Value(X), m_APInt(NotC))) && + match(F, m_OneUse(m_Or(m_Specific(X), m_APInt(C)))) && *NotC == ~(*C)) { + Constant *Zero = ConstantInt::getNullValue(Ty); + Constant *OrC = ConstantInt::get(Ty, *C); + Value *NewSel = Builder.CreateSelect(Cond, Zero, OrC, "masksel", &Sel); + return BinaryOperator::CreateOr(T, NewSel); + } + + // Cond ? (X | C) : (X & ~C) --> (X & ~C) | (Cond ? C : 0) + if (match(F, m_And(m_Value(X), m_APInt(NotC))) && + match(T, m_OneUse(m_Or(m_Specific(X), m_APInt(C)))) && *NotC == ~(*C)) { + Constant *Zero = ConstantInt::getNullValue(Ty); + Constant *OrC = ConstantInt::get(Ty, *C); + Value *NewSel = Builder.CreateSelect(Cond, OrC, Zero, "masksel", &Sel); + return BinaryOperator::CreateOr(F, NewSel); + } + + return nullptr; +} + /// Transform patterns such as (a > b) ? a - b : 0 into usub.sat(a, b). /// There are 8 commuted/swapped variants of this pattern. /// TODO: Also support a - UMIN(a,b) patterns. @@ -2553,6 +2585,8 @@ return Add; if (Instruction *Add = foldOverflowingAddSubSelect(SI, Builder)) return Add; + if (Instruction *Or = foldSetClearBits(SI, Builder)) + return Or; // Turn (select C, (op X, Y), (op X, Z)) -> (op X, (select C, Y, Z)) auto *TI = dyn_cast(TrueVal); diff --git a/llvm/test/Transforms/InstCombine/cast.ll b/llvm/test/Transforms/InstCombine/cast.ll --- a/llvm/test/Transforms/InstCombine/cast.ll +++ b/llvm/test/Transforms/InstCombine/cast.ll @@ -666,9 +666,10 @@ define i64 @test51(i64 %A, i1 %cond) { ; ALL-LABEL: @test51( ; ALL-NEXT: [[C:%.*]] = and i64 [[A:%.*]], 4294967294 -; ALL-NEXT: [[D:%.*]] = or i64 [[A]], 1 -; ALL-NEXT: [[E:%.*]] = select i1 [[COND:%.*]], i64 [[C]], i64 [[D]] -; ALL-NEXT: [[SEXT:%.*]] = shl i64 [[E]], 32 +; ALL-NEXT: [[NOT_COND:%.*]] = xor i1 [[COND:%.*]], true +; ALL-NEXT: [[MASKSEL:%.*]] = zext i1 [[NOT_COND]] to i64 +; ALL-NEXT: [[E:%.*]] = or i64 [[C]], [[MASKSEL]] +; ALL-NEXT: [[SEXT:%.*]] = shl nuw i64 [[E]], 32 ; ALL-NEXT: [[F:%.*]] = ashr exact i64 [[SEXT]], 32 ; ALL-NEXT: ret i64 [[F]] ; diff --git a/llvm/test/Transforms/InstCombine/select-with-bitwise-ops.ll b/llvm/test/Transforms/InstCombine/select-with-bitwise-ops.ll --- a/llvm/test/Transforms/InstCombine/select-with-bitwise-ops.ll +++ b/llvm/test/Transforms/InstCombine/select-with-bitwise-ops.ll @@ -1456,8 +1456,8 @@ define i8 @set_bits(i8 %x, i1 %b) { ; CHECK-LABEL: @set_bits( ; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[X]], 5 -; CHECK-NEXT: [[COND:%.*]] = select i1 [[B:%.*]], i8 [[OR]], i8 [[AND]] +; CHECK-NEXT: [[MASKSEL:%.*]] = select i1 [[B:%.*]], i8 5, i8 0 +; CHECK-NEXT: [[COND:%.*]] = or i8 [[AND]], [[MASKSEL]] ; CHECK-NEXT: ret i8 [[COND]] ; %and = and i8 %x, 250 @@ -1466,6 +1466,8 @@ ret i8 %cond } +; Negative test + define i8 @set_bits_not_inverse_constant(i8 %x, i1 %b) { ; CHECK-LABEL: @set_bits_not_inverse_constant( ; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6 @@ -1483,8 +1485,8 @@ ; CHECK-LABEL: @set_bits_extra_use1( ; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6 ; CHECK-NEXT: call void @use(i8 [[AND]]) -; CHECK-NEXT: [[OR:%.*]] = or i8 [[X]], 5 -; CHECK-NEXT: [[COND:%.*]] = select i1 [[B:%.*]], i8 [[OR]], i8 [[AND]] +; CHECK-NEXT: [[MASKSEL:%.*]] = select i1 [[B:%.*]], i8 5, i8 0 +; CHECK-NEXT: [[COND:%.*]] = or i8 [[AND]], [[MASKSEL]] ; CHECK-NEXT: ret i8 [[COND]] ; %and = and i8 %x, 250 @@ -1494,6 +1496,8 @@ ret i8 %cond } +; Negative test + define i8 @set_bits_extra_use2(i8 %x, i1 %b) { ; CHECK-LABEL: @set_bits_extra_use2( ; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6 @@ -1512,8 +1516,8 @@ define <2 x i8> @clear_bits(<2 x i8> %x, <2 x i1> %b) { ; CHECK-LABEL: @clear_bits( ; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[OR:%.*]] = or <2 x i8> [[X]], -; CHECK-NEXT: [[COND:%.*]] = select <2 x i1> [[B:%.*]], <2 x i8> [[AND]], <2 x i8> [[OR]] +; CHECK-NEXT: [[MASKSEL:%.*]] = select <2 x i1> [[B:%.*]], <2 x i8> zeroinitializer, <2 x i8> +; CHECK-NEXT: [[COND:%.*]] = or <2 x i8> [[AND]], [[MASKSEL]] ; CHECK-NEXT: ret <2 x i8> [[COND]] ; %and = and <2 x i8> %x, @@ -1522,6 +1526,8 @@ ret <2 x i8> %cond } +; Negative test + define <2 x i8> @clear_bits_not_inverse_constant(<2 x i8> %x, <2 x i1> %b) { ; CHECK-LABEL: @clear_bits_not_inverse_constant( ; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], @@ -1539,8 +1545,8 @@ ; CHECK-LABEL: @clear_bits_extra_use1( ; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[X:%.*]], ; CHECK-NEXT: call void @use_vec(<2 x i8> [[AND]]) -; CHECK-NEXT: [[OR:%.*]] = or <2 x i8> [[X]], -; CHECK-NEXT: [[COND:%.*]] = select i1 [[B:%.*]], <2 x i8> [[AND]], <2 x i8> [[OR]] +; CHECK-NEXT: [[MASKSEL:%.*]] = select i1 [[B:%.*]], <2 x i8> zeroinitializer, <2 x i8> +; CHECK-NEXT: [[COND:%.*]] = or <2 x i8> [[AND]], [[MASKSEL]] ; CHECK-NEXT: ret <2 x i8> [[COND]] ; %and = and <2 x i8> %x, @@ -1550,6 +1556,8 @@ ret <2 x i8> %cond } +; Negative test + define i8 @clear_bits_extra_use2(i8 %x, i1 %b) { ; CHECK-LABEL: @clear_bits_extra_use2( ; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], -6