[ValueTracking] recognize more variants of smin/smax

Try harder to detect obfuscated min/max patterns: the initial pattern was added with D9352 / rL236202. 
There was a bug fix for PR27137 at rL264996, but I think we can do better by folding the corresponding
smax pattern and commuted variants.

The codegen tests demonstrate the effect of ValueTracking on the backend via SelectionDAGBuilder. We
can't expose these differences minimally in IR because we don't have smin/smax intrinsics for IR.

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

llvm-svn: 285499
This commit is contained in:
Sanjay Patel 2016-10-29 16:21:19 +00:00
parent e9fa95e572
commit 36eeb6d6f6
4 changed files with 33 additions and 49 deletions

View File

@ -3969,17 +3969,25 @@ static SelectPatternResult matchSelectPattern(CmpInst::Predicate Pred,
} }
} }
// Y >s C ? ~Y : ~C == ~Y <s ~C ? ~Y : ~C = SMIN(~Y, ~C) // (X >s C) ? ~X : ~C ==> (~X <s ~C) ? ~X : ~C ==> SMIN(~X, ~C)
// (X <s C) ? ~X : ~C ==> (~X >s ~C) ? ~X : ~C ==> SMAX(~X, ~C)
const APInt *C2; const APInt *C2;
if (match(FalseVal, m_APInt(C2))) { if (match(TrueVal, m_Not(m_Specific(CmpLHS))) &&
if (Pred == ICmpInst::ICMP_SGT && match(FalseVal, m_APInt(C2)) && ~(*C1) == *C2 &&
CmpRHS->getType() == FalseVal->getType() && ~(*C1) == *C2 && (Pred == CmpInst::ICMP_SGT || Pred == CmpInst::ICMP_SLT)) {
(match(TrueVal, m_Not(m_Specific(CmpLHS))) || LHS = TrueVal;
match(CmpLHS, m_Not(m_Specific(TrueVal))))) { RHS = FalseVal;
LHS = TrueVal; return {Pred == CmpInst::ICMP_SGT ? SPF_SMIN : SPF_SMAX, SPNB_NA, false};
RHS = FalseVal; }
return {SPF_SMIN, SPNB_NA, false};
} // (X >s C) ? ~C : ~X ==> (~X <s ~C) ? ~C : ~X ==> SMAX(~C, ~X)
// (X <s C) ? ~C : ~X ==> (~X >s ~C) ? ~C : ~X ==> SMIN(~C, ~X)
if (match(FalseVal, m_Not(m_Specific(CmpLHS))) &&
match(TrueVal, m_APInt(C2)) && ~(*C1) == *C2 &&
(Pred == CmpInst::ICMP_SGT || Pred == CmpInst::ICMP_SLT)) {
LHS = TrueVal;
RHS = FalseVal;
return {Pred == CmpInst::ICMP_SGT ? SPF_SMAX : SPF_SMIN, SPNB_NA, false};
} }
} }

View File

@ -18,16 +18,12 @@ define <4 x i32> @smin_vec1(<4 x i32> %x) {
ret <4 x i32> %sel ret <4 x i32> %sel
} }
; FIXME: These are signed min/max ops.
define <4 x i32> @smin_vec2(<4 x i32> %x) { define <4 x i32> @smin_vec2(<4 x i32> %x) {
; CHECK-LABEL: smin_vec2: ; CHECK-LABEL: smin_vec2:
; CHECK: # BB#0: ; CHECK: # BB#0:
; CHECK-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1 ; CHECK-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1
; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm1 ; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm0
; CHECK-NEXT: vpxor %xmm2, %xmm2, %xmm2 ; CHECK-NEXT: vpminsd %xmm1, %xmm0, %xmm0
; CHECK-NEXT: vpcmpgtd %xmm0, %xmm2, %xmm0
; CHECK-NEXT: vpor %xmm1, %xmm0, %xmm0
; CHECK-NEXT: retq ; CHECK-NEXT: retq
; ;
%not_x = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1> %not_x = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1>
@ -40,11 +36,8 @@ define <4 x i32> @smax_vec1(<4 x i32> %x) {
; CHECK-LABEL: smax_vec1: ; CHECK-LABEL: smax_vec1:
; CHECK: # BB#0: ; CHECK: # BB#0:
; CHECK-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1 ; CHECK-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1
; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm2
; CHECK-NEXT: vpxor %xmm3, %xmm3, %xmm3
; CHECK-NEXT: vpcmpgtd %xmm0, %xmm3, %xmm0
; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm0 ; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm0
; CHECK-NEXT: vpor %xmm2, %xmm0, %xmm0 ; CHECK-NEXT: vpmaxsd %xmm1, %xmm0, %xmm0
; CHECK-NEXT: retq ; CHECK-NEXT: retq
; ;
%not_x = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1> %not_x = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1>
@ -57,10 +50,8 @@ define <4 x i32> @smax_vec2(<4 x i32> %x) {
; CHECK-LABEL: smax_vec2: ; CHECK-LABEL: smax_vec2:
; CHECK: # BB#0: ; CHECK: # BB#0:
; CHECK-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1 ; CHECK-NEXT: vpcmpeqd %xmm1, %xmm1, %xmm1
; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm1 ; CHECK-NEXT: vpxor %xmm1, %xmm0, %xmm0
; CHECK-NEXT: vpxor %xmm2, %xmm2, %xmm2 ; CHECK-NEXT: vpmaxsd %xmm1, %xmm0, %xmm0
; CHECK-NEXT: vpcmpgtd %xmm2, %xmm0, %xmm0
; CHECK-NEXT: vpor %xmm1, %xmm0, %xmm0
; CHECK-NEXT: retq ; CHECK-NEXT: retq
; ;
%not_x = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1> %not_x = xor <4 x i32> %x, <i32 -1, i32 -1, i32 -1, i32 -1>

View File

@ -140,12 +140,7 @@ define i32 @max_of_min(i32 %a) {
; max(min(%a, -1), -1) == -1 (swap predicate and select ops) ; max(min(%a, -1), -1) == -1 (swap predicate and select ops)
define i32 @max_of_min_swap(i32 %a) { define i32 @max_of_min_swap(i32 %a) {
; CHECK-LABEL: @max_of_min_swap( ; CHECK-LABEL: @max_of_min_swap(
; CHECK-NEXT: [[NOT_A:%.*]] = xor i32 %a, -1 ; CHECK-NEXT: ret i32 -1
; CHECK-NEXT: [[C0:%.*]] = icmp slt i32 %a, 0
; CHECK-NEXT: [[S0:%.*]] = select i1 [[C0]], i32 -1, i32 [[NOT_A]]
; CHECK-NEXT: [[C1:%.*]] = icmp sgt i32 [[S0]], -1
; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1]], i32 [[S0]], i32 -1
; CHECK-NEXT: ret i32 [[S1]]
; ;
%not_a = xor i32 %a, -1 %not_a = xor i32 %a, -1
%c0 = icmp slt i32 %a, 0 %c0 = icmp slt i32 %a, 0
@ -158,12 +153,7 @@ define i32 @max_of_min_swap(i32 %a) {
; min(max(%a, -1), -1) == -1 ; min(max(%a, -1), -1) == -1
define i32 @min_of_max(i32 %a) { define i32 @min_of_max(i32 %a) {
; CHECK-LABEL: @min_of_max( ; CHECK-LABEL: @min_of_max(
; CHECK-NEXT: [[NOT_A:%.*]] = xor i32 %a, -1 ; CHECK-NEXT: ret i32 -1
; CHECK-NEXT: [[C0:%.*]] = icmp slt i32 %a, 0
; CHECK-NEXT: [[S0:%.*]] = select i1 [[C0]], i32 [[NOT_A]], i32 -1
; CHECK-NEXT: [[C1:%.*]] = icmp slt i32 [[S0]], -1
; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1]], i32 [[S0]], i32 -1
; CHECK-NEXT: ret i32 [[S1]]
; ;
%not_a = xor i32 %a, -1 %not_a = xor i32 %a, -1
%c0 = icmp slt i32 %a, 0 %c0 = icmp slt i32 %a, 0
@ -176,12 +166,7 @@ define i32 @min_of_max(i32 %a) {
; min(max(%a, -1), -1) == -1 (swap predicate and select ops) ; min(max(%a, -1), -1) == -1 (swap predicate and select ops)
define i32 @min_of_max_swap(i32 %a) { define i32 @min_of_max_swap(i32 %a) {
; CHECK-LABEL: @min_of_max_swap( ; CHECK-LABEL: @min_of_max_swap(
; CHECK-NEXT: [[NOT_A:%.*]] = xor i32 %a, -1 ; CHECK-NEXT: ret i32 -1
; CHECK-NEXT: [[C0:%.*]] = icmp sgt i32 %a, 0
; CHECK-NEXT: [[S0:%.*]] = select i1 [[C0]], i32 -1, i32 [[NOT_A]]
; CHECK-NEXT: [[C1:%.*]] = icmp slt i32 [[S0]], -1
; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1]], i32 [[S0]], i32 -1
; CHECK-NEXT: ret i32 [[S1]]
; ;
%not_a = xor i32 %a, -1 %not_a = xor i32 %a, -1
%c0 = icmp sgt i32 %a, 0 %c0 = icmp sgt i32 %a, 0

View File

@ -1,4 +1,3 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -instcombine -S | FileCheck %s ; RUN: opt < %s -instcombine -S | FileCheck %s
; PR1822 ; PR1822
@ -1765,14 +1764,15 @@ define i32 @PR23757(i32 %x) {
ret i32 %sel ret i32 %sel
} }
; max(max(~a, -1), -1) --> max(~a, -1)
define i32 @PR27137(i32 %a) { define i32 @PR27137(i32 %a) {
; CHECK-LABEL: @PR27137( ; CHECK-LABEL: @PR27137(
; CHECK-NEXT: %not_a = xor i32 %a, -1 ; CHECK-NEXT: [[NOT_A:%.*]] = xor i32 %a, -1
; CHECK-NEXT: %c0 = icmp slt i32 %a, 0 ; CHECK-NEXT: [[C0:%.*]] = icmp slt i32 %a, 0
; CHECK-NEXT: %s0 = select i1 %c0, i32 %not_a, i32 -1 ; CHECK-NEXT: [[S0:%.*]] = select i1 [[C0]], i32 [[NOT_A]], i32 -1
; CHECK-NEXT: %c1 = icmp sgt i32 %s0, -1 ; CHECK-NEXT: ret i32 [[S0]]
; CHECK-NEXT: %s1 = select i1 %c1, i32 %s0, i32 -1 ;
; CHECK-NEXT: ret i32 %s1
%not_a = xor i32 %a, -1 %not_a = xor i32 %a, -1
%c0 = icmp slt i32 %a, 0 %c0 = icmp slt i32 %a, 0