forked from OSchip/llvm-project
[InstCombine] narrow min/max intrinsics with extended inputs
We can sink extends after min/max if they match and would not change the sign-interpreted compare. The only combo that doesn't work is zext+smin/smax because the zexts could change a negative number into positive: https://alive2.llvm.org/ce/z/D6sz6J Sext+umax/umin works: define i32 @src(i8 %x, i8 %y) { %0: %sx = sext i8 %x to i32 %sy = sext i8 %y to i32 %m = umax i32 %sx, %sy ret i32 %m } => define i32 @tgt(i8 %x, i8 %y) { %0: %m = umax i8 %x, %y %r = sext i8 %m to i32 ret i32 %r } Transformation seems to be correct!
This commit is contained in:
parent
07b60d0060
commit
09a136bcc6
|
@ -830,6 +830,30 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
|
|||
|
||||
break;
|
||||
}
|
||||
case Intrinsic::umax:
|
||||
case Intrinsic::umin: {
|
||||
Value *I0 = II->getArgOperand(0), *I1 = II->getArgOperand(1);
|
||||
Value *X, *Y;
|
||||
if (match(I0, m_ZExt(m_Value(X))) && match(I1, m_ZExt(m_Value(Y))) &&
|
||||
(I0->hasOneUse() || I1->hasOneUse()) && X->getType() == Y->getType()) {
|
||||
Value *NarrowMaxMin = Builder.CreateBinaryIntrinsic(IID, X, Y);
|
||||
return CastInst::Create(Instruction::ZExt, NarrowMaxMin, II->getType());
|
||||
}
|
||||
// If both operands of unsigned min/max are sign-extended, it is still ok
|
||||
// to narrow the operation.
|
||||
LLVM_FALLTHROUGH;
|
||||
}
|
||||
case Intrinsic::smax:
|
||||
case Intrinsic::smin: {
|
||||
Value *I0 = II->getArgOperand(0), *I1 = II->getArgOperand(1);
|
||||
Value *X, *Y;
|
||||
if (match(I0, m_SExt(m_Value(X))) && match(I1, m_SExt(m_Value(Y))) &&
|
||||
(I0->hasOneUse() || I1->hasOneUse()) && X->getType() == Y->getType()) {
|
||||
Value *NarrowMaxMin = Builder.CreateBinaryIntrinsic(IID, X, Y);
|
||||
return CastInst::Create(Instruction::SExt, NarrowMaxMin, II->getType());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Intrinsic::bswap: {
|
||||
Value *IIOperand = II->getArgOperand(0);
|
||||
Value *X = nullptr;
|
||||
|
|
|
@ -50,9 +50,8 @@ define i8 @smax_known_bits(i8 %x, i8 %y) {
|
|||
|
||||
define i8 @smax_sext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @smax_sext(
|
||||
; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8
|
||||
; CHECK-NEXT: [[SY:%.*]] = sext i5 [[Y:%.*]] to i8
|
||||
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[SX]], i8 [[SY]])
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.smax.i5(i5 [[X:%.*]], i5 [[Y:%.*]])
|
||||
; CHECK-NEXT: [[M:%.*]] = sext i5 [[TMP1]] to i8
|
||||
; CHECK-NEXT: ret i8 [[M]]
|
||||
;
|
||||
%sx = sext i5 %x to i8
|
||||
|
@ -61,12 +60,14 @@ define i8 @smax_sext(i5 %x, i5 %y) {
|
|||
ret i8 %m
|
||||
}
|
||||
|
||||
; Extra use is ok.
|
||||
|
||||
define i8 @smin_sext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @smin_sext(
|
||||
; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8
|
||||
; CHECK-NEXT: [[SY:%.*]] = sext i5 [[Y:%.*]] to i8
|
||||
; CHECK-NEXT: call void @use(i8 [[SY]])
|
||||
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[SX]], i8 [[SY]])
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.smin.i5(i5 [[X:%.*]], i5 [[Y]])
|
||||
; CHECK-NEXT: [[M:%.*]] = sext i5 [[TMP1]] to i8
|
||||
; CHECK-NEXT: ret i8 [[M]]
|
||||
;
|
||||
%sx = sext i5 %x to i8
|
||||
|
@ -76,12 +77,14 @@ define i8 @smin_sext(i5 %x, i5 %y) {
|
|||
ret i8 %m
|
||||
}
|
||||
|
||||
; Sext doesn't change unsigned min/max comparison of narrow values.
|
||||
|
||||
define i8 @umax_sext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @umax_sext(
|
||||
; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8
|
||||
; CHECK-NEXT: call void @use(i8 [[SX]])
|
||||
; CHECK-NEXT: [[SY:%.*]] = sext i5 [[Y:%.*]] to i8
|
||||
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[SX]], i8 [[SY]])
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.umax.i5(i5 [[X]], i5 [[Y:%.*]])
|
||||
; CHECK-NEXT: [[M:%.*]] = sext i5 [[TMP1]] to i8
|
||||
; CHECK-NEXT: ret i8 [[M]]
|
||||
;
|
||||
%sx = sext i5 %x to i8
|
||||
|
@ -93,9 +96,8 @@ define i8 @umax_sext(i5 %x, i5 %y) {
|
|||
|
||||
define <3 x i8> @umin_sext(<3 x i5> %x, <3 x i5> %y) {
|
||||
; CHECK-LABEL: @umin_sext(
|
||||
; CHECK-NEXT: [[SX:%.*]] = sext <3 x i5> [[X:%.*]] to <3 x i8>
|
||||
; CHECK-NEXT: [[SY:%.*]] = sext <3 x i5> [[Y:%.*]] to <3 x i8>
|
||||
; CHECK-NEXT: [[M:%.*]] = call <3 x i8> @llvm.umin.v3i8(<3 x i8> [[SX]], <3 x i8> [[SY]])
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call <3 x i5> @llvm.umin.v3i5(<3 x i5> [[X:%.*]], <3 x i5> [[Y:%.*]])
|
||||
; CHECK-NEXT: [[M:%.*]] = sext <3 x i5> [[TMP1]] to <3 x i8>
|
||||
; CHECK-NEXT: ret <3 x i8> [[M]]
|
||||
;
|
||||
%sx = sext <3 x i5> %x to <3 x i8>
|
||||
|
@ -104,6 +106,8 @@ define <3 x i8> @umin_sext(<3 x i5> %x, <3 x i5> %y) {
|
|||
ret <3 x i8> %m
|
||||
}
|
||||
|
||||
; Negative test - zext may change sign of inputs
|
||||
|
||||
define i8 @smax_zext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @smax_zext(
|
||||
; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8
|
||||
|
@ -117,6 +121,8 @@ define i8 @smax_zext(i5 %x, i5 %y) {
|
|||
ret i8 %m
|
||||
}
|
||||
|
||||
; Negative test - zext may change sign of inputs
|
||||
|
||||
define i8 @smin_zext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @smin_zext(
|
||||
; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8
|
||||
|
@ -132,9 +138,8 @@ define i8 @smin_zext(i5 %x, i5 %y) {
|
|||
|
||||
define i8 @umax_zext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @umax_zext(
|
||||
; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8
|
||||
; CHECK-NEXT: [[ZY:%.*]] = zext i5 [[Y:%.*]] to i8
|
||||
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[ZX]], i8 [[ZY]])
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.umax.i5(i5 [[X:%.*]], i5 [[Y:%.*]])
|
||||
; CHECK-NEXT: [[M:%.*]] = zext i5 [[TMP1]] to i8
|
||||
; CHECK-NEXT: ret i8 [[M]]
|
||||
;
|
||||
%zx = zext i5 %x to i8
|
||||
|
@ -145,9 +150,8 @@ define i8 @umax_zext(i5 %x, i5 %y) {
|
|||
|
||||
define i8 @umin_zext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @umin_zext(
|
||||
; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8
|
||||
; CHECK-NEXT: [[ZY:%.*]] = zext i5 [[Y:%.*]] to i8
|
||||
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[ZX]], i8 [[ZY]])
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.umin.i5(i5 [[X:%.*]], i5 [[Y:%.*]])
|
||||
; CHECK-NEXT: [[M:%.*]] = zext i5 [[TMP1]] to i8
|
||||
; CHECK-NEXT: ret i8 [[M]]
|
||||
;
|
||||
%zx = zext i5 %x to i8
|
||||
|
@ -156,6 +160,8 @@ define i8 @umin_zext(i5 %x, i5 %y) {
|
|||
ret i8 %m
|
||||
}
|
||||
|
||||
; Negative test - mismatched types
|
||||
|
||||
define i8 @umin_zext_types(i6 %x, i5 %y) {
|
||||
; CHECK-LABEL: @umin_zext_types(
|
||||
; CHECK-NEXT: [[ZX:%.*]] = zext i6 [[X:%.*]] to i8
|
||||
|
@ -169,6 +175,8 @@ define i8 @umin_zext_types(i6 %x, i5 %y) {
|
|||
ret i8 %m
|
||||
}
|
||||
|
||||
; Negative test - mismatched extends
|
||||
|
||||
define i8 @umin_ext(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @umin_ext(
|
||||
; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8
|
||||
|
@ -182,6 +190,8 @@ define i8 @umin_ext(i5 %x, i5 %y) {
|
|||
ret i8 %m
|
||||
}
|
||||
|
||||
; Negative test - too many uses.
|
||||
|
||||
define i8 @umin_zext_uses(i5 %x, i5 %y) {
|
||||
; CHECK-LABEL: @umin_zext_uses(
|
||||
; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8
|
||||
|
|
Loading…
Reference in New Issue