[InstCombine,InstSimplify] Optimize select followed by and/or/xor

This patch adds `A & (A && B)` -> `A && B`  (similarly for or + logical or)

Also, this patch adds `~(select C, (icmp pred X, Y), const)` -> `select C, (icmp pred' X, Y), ~const`.

Alive2 proof:
merge_and: https://alive2.llvm.org/ce/z/teMR97
merge_or: https://alive2.llvm.org/ce/z/b4yZUp
xor_and: https://alive2.llvm.org/ce/z/_-TXHi
xor_or: https://alive2.llvm.org/ce/z/2uYx_a

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D94861
This commit is contained in:
Juneyoung Lee 2021-01-19 09:00:46 +09:00
parent 14573d44ae
commit 0441df94ad
3 changed files with 68 additions and 43 deletions

View File

@ -2127,12 +2127,21 @@ static Value *SimplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
Instruction::Xor, Q, MaxRecurse))
return V;
// If the operation is with the result of a select instruction, check whether
// operating on either branch of the select always yields the same value.
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1))
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1)) {
if (Op0->getType()->isIntOrIntVectorTy(1)) {
// A & (A && B) -> A && B
if (match(Op1, m_Select(m_Specific(Op0), m_Value(), m_Zero())))
return Op1;
else if (match(Op0, m_Select(m_Specific(Op1), m_Value(), m_Zero())))
return Op0;
}
// If the operation is with the result of a select instruction, check
// whether operating on either branch of the select always yields the same
// value.
if (Value *V = ThreadBinOpOverSelect(Instruction::And, Op0, Op1, Q,
MaxRecurse))
return V;
}
// If the operation is with the result of a phi instruction, check whether
// operating on all incoming values of the phi always yields the same value.
@ -2303,12 +2312,21 @@ static Value *SimplifyOrInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
Instruction::And, Q, MaxRecurse))
return V;
// If the operation is with the result of a select instruction, check whether
// operating on either branch of the select always yields the same value.
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1))
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1)) {
if (Op0->getType()->isIntOrIntVectorTy(1)) {
// A | (A || B) -> A || B
if (match(Op1, m_Select(m_Specific(Op0), m_One(), m_Value())))
return Op1;
else if (match(Op0, m_Select(m_Specific(Op1), m_One(), m_Value())))
return Op0;
}
// If the operation is with the result of a select instruction, check
// whether operating on either branch of the select always yields the same
// value.
if (Value *V = ThreadBinOpOverSelect(Instruction::Or, Op0, Op1, Q,
MaxRecurse))
return V;
}
// (A & C1)|(B & C2)
const APInt *C1, *C2;

View File

@ -3444,19 +3444,32 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
}
}
// Pull 'not' into operands of select if both operands are one-use compares.
// Pull 'not' into operands of select if both operands are one-use compares
// or one is one-use compare and the other one is a constant.
// Inverting the predicates eliminates the 'not' operation.
// Example:
// not (select ?, (cmp TPred, ?, ?), (cmp FPred, ?, ?) -->
// not (select ?, (cmp TPred, ?, ?), (cmp FPred, ?, ?) -->
// select ?, (cmp InvTPred, ?, ?), (cmp InvFPred, ?, ?)
// TODO: Canonicalize by hoisting 'not' into an arm of the select if only
// 1 select operand is a cmp?
// not (select ?, (cmp TPred, ?, ?), true -->
// select ?, (cmp InvTPred, ?, ?), false
if (auto *Sel = dyn_cast<SelectInst>(Op0)) {
auto *CmpT = dyn_cast<CmpInst>(Sel->getTrueValue());
auto *CmpF = dyn_cast<CmpInst>(Sel->getFalseValue());
if (CmpT && CmpF && CmpT->hasOneUse() && CmpF->hasOneUse()) {
CmpT->setPredicate(CmpT->getInversePredicate());
CmpF->setPredicate(CmpF->getInversePredicate());
Value *TV = Sel->getTrueValue();
Value *FV = Sel->getFalseValue();
auto *CmpT = dyn_cast<CmpInst>(TV);
auto *CmpF = dyn_cast<CmpInst>(FV);
bool InvertibleT = (CmpT && CmpT->hasOneUse()) || isa<Constant>(TV);
bool InvertibleF = (CmpF && CmpF->hasOneUse()) || isa<Constant>(FV);
if (InvertibleT && InvertibleF) {
Constant *One = cast<Constant>(Op1);
if (CmpT)
CmpT->setPredicate(CmpT->getInversePredicate());
else
Sel->setTrueValue(ConstantExpr::getNot(cast<Constant>(TV)));
if (CmpF)
CmpF->setPredicate(CmpF->getInversePredicate());
else
Sel->setFalseValue(ConstantExpr::getNot(cast<Constant>(FV)));
return replaceInstUsesWith(I, Sel);
}
}

View File

@ -56,8 +56,7 @@ define i1 @cond_eq_or_const(i8 %X, i8 %Y) {
define i1 @merge_and(i1 %X, i1 %Y) {
; CHECK-LABEL: @merge_and(
; CHECK-NEXT: [[C:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false
; CHECK-NEXT: [[RES:%.*]] = and i1 [[C]], [[X]]
; CHECK-NEXT: ret i1 [[RES]]
; CHECK-NEXT: ret i1 [[C]]
;
%c = select i1 %X, i1 %Y, i1 false
%res = and i1 %X, %c
@ -67,8 +66,7 @@ define i1 @merge_and(i1 %X, i1 %Y) {
define i1 @merge_or(i1 %X, i1 %Y) {
; CHECK-LABEL: @merge_or(
; CHECK-NEXT: [[C:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]]
; CHECK-NEXT: [[RES:%.*]] = or i1 [[C]], [[X]]
; CHECK-NEXT: ret i1 [[RES]]
; CHECK-NEXT: ret i1 [[C]]
;
%c = select i1 %X, i1 true, i1 %Y
%res = or i1 %X, %c
@ -77,10 +75,10 @@ define i1 @merge_or(i1 %X, i1 %Y) {
define i1 @xor_and(i1 %c, i32 %X, i32 %Y) {
; CHECK-LABEL: @xor_and(
; CHECK-NEXT: [[COMP:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i1 [[COMP]], i1 false
; CHECK-NEXT: [[RES:%.*]] = xor i1 [[SEL]], true
; CHECK-NEXT: ret i1 [[RES]]
; CHECK-NEXT: [[COMP:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 true, i1 [[COMP]]
; CHECK-NEXT: ret i1 [[SEL]]
;
%comp = icmp ult i32 %X, %Y
%sel = select i1 %c, i1 %comp, i1 false
@ -90,10 +88,9 @@ define i1 @xor_and(i1 %c, i32 %X, i32 %Y) {
define <2 x i1> @xor_and2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
; CHECK-LABEL: @xor_and2(
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 true, i1 false>
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
; CHECK-NEXT: ret <2 x i1> [[RES]]
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 false, i1 true>
; CHECK-NEXT: ret <2 x i1> [[SEL]]
;
%comp = icmp ult <2 x i32> %X, %Y
%sel = select <2 x i1> %c, <2 x i1> %comp, <2 x i1> <i1 true, i1 false>
@ -105,10 +102,9 @@ define <2 x i1> @xor_and2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
define <2 x i1> @xor_and3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
; CHECK-LABEL: @xor_and3(
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 icmp eq (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 false>
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
; CHECK-NEXT: ret <2 x i1> [[RES]]
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 icmp ne (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 true>
; CHECK-NEXT: ret <2 x i1> [[SEL]]
;
%comp = icmp ult <2 x i32> %X, %Y
%sel = select <2 x i1> %c, <2 x i1> %comp, <2 x i1> <i1 icmp eq (i8* @glb, i8* inttoptr (i64 1234 to i8*)), i1 false>
@ -118,10 +114,10 @@ define <2 x i1> @xor_and3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
define i1 @xor_or(i1 %c, i32 %X, i32 %Y) {
; CHECK-LABEL: @xor_or(
; CHECK-NEXT: [[COMP:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i1 true, i1 [[COMP]]
; CHECK-NEXT: [[RES:%.*]] = xor i1 [[SEL]], true
; CHECK-NEXT: ret i1 [[RES]]
; CHECK-NEXT: [[COMP:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 [[COMP]], i1 false
; CHECK-NEXT: ret i1 [[SEL]]
;
%comp = icmp ult i32 %X, %Y
%sel = select i1 %c, i1 true, i1 %comp
@ -131,10 +127,9 @@ define i1 @xor_or(i1 %c, i32 %X, i32 %Y) {
define <2 x i1> @xor_or2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
; CHECK-LABEL: @xor_or2(
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 true, i1 false>, <2 x i1> [[COMP]]
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
; CHECK-NEXT: ret <2 x i1> [[RES]]
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 false, i1 true>, <2 x i1> [[COMP]]
; CHECK-NEXT: ret <2 x i1> [[SEL]]
;
%comp = icmp ult <2 x i32> %X, %Y
%sel = select <2 x i1> %c, <2 x i1> <i1 true, i1 false>, <2 x i1> %comp
@ -144,10 +139,9 @@ define <2 x i1> @xor_or2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
define <2 x i1> @xor_or3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
; CHECK-LABEL: @xor_or3(
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 icmp eq (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 false>, <2 x i1> [[COMP]]
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
; CHECK-NEXT: ret <2 x i1> [[RES]]
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 icmp ne (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 true>, <2 x i1> [[COMP]]
; CHECK-NEXT: ret <2 x i1> [[SEL]]
;
%comp = icmp ult <2 x i32> %X, %Y
%sel = select <2 x i1> %c, <2 x i1> <i1 icmp eq (i8* @glb, i8* inttoptr (i64 1234 to i8*)), i1 false>, <2 x i1> %comp