[InstCombine] add folds for binop with sexted bool and constant operands

This is a generalization/extension of the existing and/or
folds noted with TODO comments. Those have a one-use
constraint that is not necessary.

Potential follow-ups are noted by the TODO comments in
the new function. We can also call this function from
other binop visit* functions, but we need to add tests
first.

This solves:
https://llvm.org/PR52543

https://alive2.llvm.org/ce/z/NWuCR5
This commit is contained in:
Sanjay Patel 2021-11-20 10:55:41 -05:00
parent 1d007d0e5a
commit 337948ac6e
5 changed files with 40 additions and 2 deletions

View File

@ -2061,7 +2061,14 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
if (Instruction *CastedAnd = foldCastedBitwiseLogic(I))
return CastedAnd;
if (Instruction *Sel = foldBinopOfSextBoolToSelect(I))
return Sel;
// and(sext(A), B) / and(B, sext(A)) --> A ? B : 0, where A is i1 or <N x i1>.
// TODO: Move this into foldBinopOfSextBoolToSelect as a more generalized fold
// with binop identity constant. But creating a select with non-constant
// arm may not be reversible due to poison semantics. Is that a good
// canonicalization?
Value *A;
if (match(Op0, m_OneUse(m_SExt(m_Value(A)))) &&
A->getType()->isIntOrIntVectorTy(1))
@ -2788,7 +2795,14 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
if (Instruction *CastedOr = foldCastedBitwiseLogic(I))
return CastedOr;
if (Instruction *Sel = foldBinopOfSextBoolToSelect(I))
return Sel;
// or(sext(A), B) / or(B, sext(A)) --> A ? -1 : B, where A is i1 or <N x i1>.
// TODO: Move this into foldBinopOfSextBoolToSelect as a more generalized fold
// with binop identity constant. But creating a select with non-constant
// arm may not be reversible due to poison semantics. Is that a good
// canonicalization?
if (match(Op0, m_OneUse(m_SExt(m_Value(A)))) &&
A->getType()->isIntOrIntVectorTy(1))
return SelectInst::Create(A, ConstantInt::getSigned(I.getType(), -1), Op1);

View File

@ -319,6 +319,7 @@ private:
Instruction *scalarizePHI(ExtractElementInst &EI, PHINode *PN);
Instruction *foldBitcastExtElt(ExtractElementInst &ExtElt);
Instruction *foldCastedBitwiseLogic(BinaryOperator &I);
Instruction *foldBinopOfSextBoolToSelect(BinaryOperator &I);
Instruction *narrowBinOp(TruncInst &Trunc);
Instruction *narrowMaskedBinOp(BinaryOperator &And);
Instruction *narrowMathIfNoOverflow(BinaryOperator &I);

View File

@ -967,6 +967,29 @@ Value *InstCombinerImpl::dyn_castNegVal(Value *V) const {
return nullptr;
}
/// A binop with a constant operand and a sign-extended boolean operand may be
/// converted into a select of constants by applying the binary operation to
/// the constant with the two possible values of the extended boolean (0 or -1).
Instruction *InstCombinerImpl::foldBinopOfSextBoolToSelect(BinaryOperator &BO) {
// TODO: Handle non-commutative binop (constant is operand 0).
// TODO: Handle zext.
// TODO: Peek through 'not' of cast.
Value *BO0 = BO.getOperand(0);
Value *BO1 = BO.getOperand(1);
Value *X;
Constant *C;
if (!match(BO0, m_SExt(m_Value(X))) || !match(BO1, m_ImmConstant(C)) ||
!X->getType()->isIntOrIntVectorTy(1))
return nullptr;
// bo (sext i1 X), C --> select X, (bo -1, C), (bo 0, C)
Constant *Ones = ConstantInt::getAllOnesValue(BO.getType());
Constant *Zero = ConstantInt::getNullValue(BO.getType());
Constant *TVal = ConstantExpr::get(BO.getOpcode(), Ones, C);
Constant *FVal = ConstantExpr::get(BO.getOpcode(), Zero, C);
return SelectInst::Create(X, TVal, FVal);
}
static Value *foldOperationIntoSelectOperand(Instruction &I, Value *SO,
InstCombiner::BuilderTy &Builder) {
if (auto *Cast = dyn_cast<CastInst>(&I))

View File

@ -1510,7 +1510,7 @@ define i32 @sext_to_sel_multi_use_constant_mask(i1 %y) {
; CHECK-LABEL: @sext_to_sel_multi_use_constant_mask(
; CHECK-NEXT: [[SEXT:%.*]] = sext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SEXT]])
; CHECK-NEXT: [[R:%.*]] = and i32 [[SEXT]], 42
; CHECK-NEXT: [[R:%.*]] = select i1 [[Y]], i32 42, i32 0
; CHECK-NEXT: ret i32 [[R]]
;
%sext = sext i1 %y to i32

View File

@ -657,7 +657,7 @@ define i32 @sext_to_sel_multi_use_constant_mask(i1 %y) {
; CHECK-LABEL: @sext_to_sel_multi_use_constant_mask(
; CHECK-NEXT: [[SEXT:%.*]] = sext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use(i32 [[SEXT]])
; CHECK-NEXT: [[R:%.*]] = or i32 [[SEXT]], 42
; CHECK-NEXT: [[R:%.*]] = select i1 [[Y]], i32 -1, i32 42
; CHECK-NEXT: ret i32 [[R]]
;
%sext = sext i1 %y to i32