Skip to content
Snippets Groups Projects
Commit 3c7a35de authored by Sanjay Patel's avatar Sanjay Patel
Browse files

[EarlyCSE] recognize commuted and swapped variants of min/max as equivalent (PR35642)

As shown in:
https://bugs.llvm.org/show_bug.cgi?id=35642
...we can have different forms of min/max, so we should recognize those here in EarlyCSE 
similar to how we already handle binops and compares that can commute.

Differential Revision: https://reviews.llvm.org/D41136

llvm-svn: 320640
parent 75f8360e
No related branches found
No related tags found
No related merge requests found
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h" #include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h" #include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h" #include "llvm/IR/DataLayout.h"
...@@ -142,6 +143,19 @@ unsigned DenseMapInfo<SimpleValue>::getHashValue(SimpleValue Val) { ...@@ -142,6 +143,19 @@ unsigned DenseMapInfo<SimpleValue>::getHashValue(SimpleValue Val) {
return hash_combine(Inst->getOpcode(), Pred, LHS, RHS); return hash_combine(Inst->getOpcode(), Pred, LHS, RHS);
} }
// Hash min/max (cmp + select) to allow for commuted operands, non-canonical
// compare predicate (eg, the compare for smin may use 'sgt' rather than
// 'slt'), and non-canonical operands in the compare.
Value *A, *B;
SelectPatternFlavor SPF = matchSelectPattern(Inst, A, B).Flavor;
// TODO: We should also detect abs and FP min/max.
if (SPF == SPF_SMIN || SPF == SPF_SMAX ||
SPF == SPF_UMIN || SPF == SPF_UMAX) {
if (A > B)
std::swap(A, B);
return hash_combine(Inst->getOpcode(), SPF, A, B);
}
if (CastInst *CI = dyn_cast<CastInst>(Inst)) if (CastInst *CI = dyn_cast<CastInst>(Inst))
return hash_combine(CI->getOpcode(), CI->getType(), CI->getOperand(0)); return hash_combine(CI->getOpcode(), CI->getType(), CI->getOperand(0));
...@@ -200,6 +214,19 @@ bool DenseMapInfo<SimpleValue>::isEqual(SimpleValue LHS, SimpleValue RHS) { ...@@ -200,6 +214,19 @@ bool DenseMapInfo<SimpleValue>::isEqual(SimpleValue LHS, SimpleValue RHS) {
LHSCmp->getSwappedPredicate() == RHSCmp->getPredicate(); LHSCmp->getSwappedPredicate() == RHSCmp->getPredicate();
} }
// Min/max can occur with commuted operands, non-canonical predicates, and/or
// non-canonical operands.
Value *LHSA, *LHSB;
SelectPatternFlavor LSPF = matchSelectPattern(LHSI, LHSA, LHSB).Flavor;
// TODO: We should also detect abs and FP min/max.
if (LSPF == SPF_SMIN || LSPF == SPF_SMAX ||
LSPF == SPF_UMIN || LSPF == SPF_UMAX) {
Value *RHSA, *RHSB;
SelectPatternFlavor RSPF = matchSelectPattern(RHSI, RHSA, RHSB).Flavor;
return (LSPF == RSPF && ((LHSA == RHSA && LHSB == RHSB) ||
(LHSA == RHSB && LHSB == RHSA)));
}
return false; return false;
} }
......
...@@ -78,8 +78,7 @@ define i8 @smin_commute(i8 %a, i8 %b) { ...@@ -78,8 +78,7 @@ define i8 @smin_commute(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %b, %a ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a ; CHECK-NEXT: [[R:%.*]] = mul i8 [[M1]], [[M1]]
; CHECK-NEXT: [[R:%.*]] = mul i8 [[M1]], [[M2]]
; CHECK-NEXT: ret i8 [[R]] ; CHECK-NEXT: ret i8 [[R]]
; ;
%cmp1 = icmp slt i8 %a, %b %cmp1 = icmp slt i8 %a, %b
...@@ -97,9 +96,7 @@ define i1 @smin_swapped(i8 %a, i8 %b) { ...@@ -97,9 +96,7 @@ define i1 @smin_swapped(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %a, i8 %b ; CHECK-NEXT: ret i1 true
; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[M2]], [[M1]]
; CHECK-NEXT: ret i1 [[R]]
; ;
%cmp1 = icmp sgt i8 %a, %b %cmp1 = icmp sgt i8 %a, %b
%cmp2 = icmp slt i8 %a, %b %cmp2 = icmp slt i8 %a, %b
...@@ -114,9 +111,7 @@ define i8 @smax_commute(i8 %a, i8 %b) { ...@@ -114,9 +111,7 @@ define i8 @smax_commute(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %b, %a ; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a ; CHECK-NEXT: ret i8 0
; CHECK-NEXT: [[R:%.*]] = urem i8 [[M2]], [[M1]]
; CHECK-NEXT: ret i8 [[R]]
; ;
%cmp1 = icmp sgt i8 %a, %b %cmp1 = icmp sgt i8 %a, %b
%cmp2 = icmp sgt i8 %b, %a %cmp2 = icmp sgt i8 %b, %a
...@@ -131,9 +126,7 @@ define i8 @smax_swapped(i8 %a, i8 %b) { ...@@ -131,9 +126,7 @@ define i8 @smax_swapped(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %a, %b
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %a, i8 %b ; CHECK-NEXT: ret i8 1
; CHECK-NEXT: [[R:%.*]] = sdiv i8 [[M1]], [[M2]]
; CHECK-NEXT: ret i8 [[R]]
; ;
%cmp1 = icmp slt i8 %a, %b %cmp1 = icmp slt i8 %a, %b
%cmp2 = icmp sgt i8 %a, %b %cmp2 = icmp sgt i8 %a, %b
...@@ -148,9 +141,7 @@ define i8 @umin_commute(i8 %a, i8 %b) { ...@@ -148,9 +141,7 @@ define i8 @umin_commute(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 %b, %a ; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a ; CHECK-NEXT: ret i8 0
; CHECK-NEXT: [[R:%.*]] = sub i8 [[M2]], [[M1]]
; CHECK-NEXT: ret i8 [[R]]
; ;
%cmp1 = icmp ult i8 %a, %b %cmp1 = icmp ult i8 %a, %b
%cmp2 = icmp ult i8 %b, %a %cmp2 = icmp ult i8 %b, %a
...@@ -167,9 +158,7 @@ define <2 x i8> @umin_swapped(<2 x i8> %a, <2 x i8> %b) { ...@@ -167,9 +158,7 @@ define <2 x i8> @umin_swapped(<2 x i8> %a, <2 x i8> %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt <2 x i8> %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt <2 x i8> %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult <2 x i8> %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp ult <2 x i8> %a, %b
; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> %b, <2 x i8> %a ; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> %b, <2 x i8> %a
; CHECK-NEXT: [[M2:%.*]] = select <2 x i1> [[CMP2]], <2 x i8> %a, <2 x i8> %b ; CHECK-NEXT: ret <2 x i8> zeroinitializer
; CHECK-NEXT: [[R:%.*]] = sub <2 x i8> [[M2]], [[M1]]
; CHECK-NEXT: ret <2 x i8> [[R]]
; ;
%cmp1 = icmp ugt <2 x i8> %a, %b %cmp1 = icmp ugt <2 x i8> %a, %b
%cmp2 = icmp ult <2 x i8> %a, %b %cmp2 = icmp ult <2 x i8> %a, %b
...@@ -184,9 +173,7 @@ define i8 @umax_commute(i8 %a, i8 %b) { ...@@ -184,9 +173,7 @@ define i8 @umax_commute(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %b, %a ; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %b, %a
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a ; CHECK-NEXT: ret i8 1
; CHECK-NEXT: [[R:%.*]] = udiv i8 [[M1]], [[M2]]
; CHECK-NEXT: ret i8 [[R]]
; ;
%cmp1 = icmp ugt i8 %a, %b %cmp1 = icmp ugt i8 %a, %b
%cmp2 = icmp ugt i8 %b, %a %cmp2 = icmp ugt i8 %b, %a
...@@ -201,8 +188,7 @@ define i8 @umax_swapped(i8 %a, i8 %b) { ...@@ -201,8 +188,7 @@ define i8 @umax_swapped(i8 %a, i8 %b) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %a, %b
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %a, i8 %b ; CHECK-NEXT: [[R:%.*]] = add i8 [[M1]], [[M1]]
; CHECK-NEXT: [[R:%.*]] = add i8 [[M2]], [[M1]]
; CHECK-NEXT: ret i8 [[R]] ; CHECK-NEXT: ret i8 [[R]]
; ;
%cmp1 = icmp ult i8 %a, %b %cmp1 = icmp ult i8 %a, %b
...@@ -213,3 +199,22 @@ define i8 @umax_swapped(i8 %a, i8 %b) { ...@@ -213,3 +199,22 @@ define i8 @umax_swapped(i8 %a, i8 %b) {
ret i8 %r ret i8 %r
} }
; Min/max may exist with non-canonical operands. Value tracking can match those.
define i8 @smax_nsw(i8 %a, i8 %b) {
; CHECK-LABEL: @smax_nsw(
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 %a, %b
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[SUB]], 0
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 0, i8 [[SUB]]
; CHECK-NEXT: ret i8 0
;
%sub = sub nsw i8 %a, %b
%cmp1 = icmp slt i8 %a, %b
%cmp2 = icmp sgt i8 %sub, 0
%m1 = select i1 %cmp1, i8 0, i8 %sub
%m2 = select i1 %cmp2, i8 %sub, i8 0
%r = sub i8 %m2, %m1
ret i8 %r
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment