[llvm-branch-commits] [llvm] 0441df9 - [InstCombine, InstSimplify] Optimize select followed by and/or/xor
Juneyoung Lee via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Jan 18 16:18:33 PST 2021
Author: Juneyoung Lee
Date: 2021-01-19T09:14:17+09:00
New Revision: 0441df94ad874c0c59a3785bd54a3d2f9a616fac
URL: https://github.com/llvm/llvm-project/commit/0441df94ad874c0c59a3785bd54a3d2f9a616fac
DIFF: https://github.com/llvm/llvm-project/commit/0441df94ad874c0c59a3785bd54a3d2f9a616fac.diff
LOG: [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
Added:
Modified:
llvm/lib/Analysis/InstructionSimplify.cpp
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
llvm/test/Transforms/InstCombine/select-safe-transforms.ll
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 3cab06079a87..c40e5c36cdc7 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -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;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 352126fa07ca..59291617d2b0 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -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);
}
}
diff --git a/llvm/test/Transforms/InstCombine/select-safe-transforms.ll b/llvm/test/Transforms/InstCombine/select-safe-transforms.ll
index 48235863d9ff..35f100302d47 100644
--- a/llvm/test/Transforms/InstCombine/select-safe-transforms.ll
+++ b/llvm/test/Transforms/InstCombine/select-safe-transforms.ll
@@ -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
More information about the llvm-branch-commits
mailing list