Skip to content

Commit d7fe2cf

Browse files
authored
[InstCombine] Widen Sel width after Cmp to generate Max/Min intrinsics. (#118932)
When Sel(Cmp) are in different integer type, From: (K and N mean width, K < N; a and b are src operands.) bN = Ext(bK) cond = Cmp(aN, bN) aK = Trunc aN retK = Sel(cond, aK, bK) To: bN = Ext(bK) cond = Cmp(aN, bN) retN = Sel(cond, aN, bN) retK = Trunc retN Though Sel's operands width becomes larger, the benefit of making type width in Sel the same as Cmp, is for combing to max/min intrinsics, and also better performance for SIMD instructions. References of correctness: https://alive2.llvm.org/ce/z/Y4Kegm https://alive2.llvm.org/ce/z/qFtjtR Reference of generated code comparision: https://gcc.godbolt.org/z/o97svGvYM https://gcc.godbolt.org/z/59Ynj91ov
1 parent c48d45e commit d7fe2cf

File tree

2 files changed

+116
-33
lines changed

2 files changed

+116
-33
lines changed

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8803,40 +8803,10 @@ static SelectPatternResult matchSelectPattern(CmpInst::Predicate Pred,
88038803
return matchFastFloatClamp(Pred, CmpLHS, CmpRHS, TrueVal, FalseVal, LHS, RHS);
88048804
}
88058805

8806-
/// Helps to match a select pattern in case of a type mismatch.
8807-
///
8808-
/// The function processes the case when type of true and false values of a
8809-
/// select instruction differs from type of the cmp instruction operands because
8810-
/// of a cast instruction. The function checks if it is legal to move the cast
8811-
/// operation after "select". If yes, it returns the new second value of
8812-
/// "select" (with the assumption that cast is moved):
8813-
/// 1. As operand of cast instruction when both values of "select" are same cast
8814-
/// instructions.
8815-
/// 2. As restored constant (by applying reverse cast operation) when the first
8816-
/// value of the "select" is a cast operation and the second value is a
8817-
/// constant.
8818-
/// NOTE: We return only the new second value because the first value could be
8819-
/// accessed as operand of cast instruction.
8820-
static Value *lookThroughCast(CmpInst *CmpI, Value *V1, Value *V2,
8821-
Instruction::CastOps *CastOp) {
8822-
auto *Cast1 = dyn_cast<CastInst>(V1);
8823-
if (!Cast1)
8824-
return nullptr;
8825-
8826-
*CastOp = Cast1->getOpcode();
8827-
Type *SrcTy = Cast1->getSrcTy();
8828-
if (auto *Cast2 = dyn_cast<CastInst>(V2)) {
8829-
// If V1 and V2 are both the same cast from the same type, look through V1.
8830-
if (*CastOp == Cast2->getOpcode() && SrcTy == Cast2->getSrcTy())
8831-
return Cast2->getOperand(0);
8832-
return nullptr;
8833-
}
8834-
8835-
auto *C = dyn_cast<Constant>(V2);
8836-
if (!C)
8837-
return nullptr;
8838-
8806+
static Value *lookThroughCastConst(CmpInst *CmpI, Type *SrcTy, Constant *C,
8807+
Instruction::CastOps *CastOp) {
88398808
const DataLayout &DL = CmpI->getDataLayout();
8809+
88408810
Constant *CastedTo = nullptr;
88418811
switch (*CastOp) {
88428812
case Instruction::ZExt:
@@ -8912,6 +8882,63 @@ static Value *lookThroughCast(CmpInst *CmpI, Value *V1, Value *V2,
89128882
return CastedTo;
89138883
}
89148884

8885+
/// Helps to match a select pattern in case of a type mismatch.
8886+
///
8887+
/// The function processes the case when type of true and false values of a
8888+
/// select instruction differs from type of the cmp instruction operands because
8889+
/// of a cast instruction. The function checks if it is legal to move the cast
8890+
/// operation after "select". If yes, it returns the new second value of
8891+
/// "select" (with the assumption that cast is moved):
8892+
/// 1. As operand of cast instruction when both values of "select" are same cast
8893+
/// instructions.
8894+
/// 2. As restored constant (by applying reverse cast operation) when the first
8895+
/// value of the "select" is a cast operation and the second value is a
8896+
/// constant. It is implemented in lookThroughCastConst().
8897+
/// 3. As one operand is cast instruction and the other is not. The operands in
8898+
/// sel(cmp) are in different type integer.
8899+
/// NOTE: We return only the new second value because the first value could be
8900+
/// accessed as operand of cast instruction.
8901+
static Value *lookThroughCast(CmpInst *CmpI, Value *V1, Value *V2,
8902+
Instruction::CastOps *CastOp) {
8903+
auto *Cast1 = dyn_cast<CastInst>(V1);
8904+
if (!Cast1)
8905+
return nullptr;
8906+
8907+
*CastOp = Cast1->getOpcode();
8908+
Type *SrcTy = Cast1->getSrcTy();
8909+
if (auto *Cast2 = dyn_cast<CastInst>(V2)) {
8910+
// If V1 and V2 are both the same cast from the same type, look through V1.
8911+
if (*CastOp == Cast2->getOpcode() && SrcTy == Cast2->getSrcTy())
8912+
return Cast2->getOperand(0);
8913+
return nullptr;
8914+
}
8915+
8916+
auto *C = dyn_cast<Constant>(V2);
8917+
if (C)
8918+
return lookThroughCastConst(CmpI, SrcTy, C, CastOp);
8919+
8920+
Value *CastedTo = nullptr;
8921+
if (*CastOp == Instruction::Trunc) {
8922+
if (match(CmpI->getOperand(1), m_ZExtOrSExt(m_Specific(V2)))) {
8923+
// Here we have the following case:
8924+
// %y_ext = sext iK %y to iN
8925+
// %cond = cmp iN %x, %y_ext
8926+
// %tr = trunc iN %x to iK
8927+
// %narrowsel = select i1 %cond, iK %tr, iK %y
8928+
//
8929+
// We can always move trunc after select operation:
8930+
// %y_ext = sext iK %y to iN
8931+
// %cond = cmp iN %x, %y_ext
8932+
// %widesel = select i1 %cond, iN %x, iN %y_ext
8933+
// %tr = trunc iN %widesel to iK
8934+
assert(V2->getType() == Cast1->getType() &&
8935+
"V2 and Cast1 should be the same type.");
8936+
CastedTo = CmpI->getOperand(1);
8937+
}
8938+
}
8939+
8940+
return CastedTo;
8941+
}
89158942
SelectPatternResult llvm::matchSelectPattern(Value *V, Value *&LHS, Value *&RHS,
89168943
Instruction::CastOps *CastOp,
89178944
unsigned Depth) {

llvm/test/Transforms/InstCombine/minmax-fold.ll

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,34 @@ define zeroext i8 @look_through_cast2(i32 %x) {
697697
ret i8 %res
698698
}
699699

700+
define i8 @look_through_cast_int_min(i8 %a, i32 %min) {
701+
; CHECK-LABEL: @look_through_cast_int_min(
702+
; CHECK-NEXT: [[A32:%.*]] = sext i8 [[A:%.*]] to i32
703+
; CHECK-NEXT: [[SEL1:%.*]] = call i32 @llvm.smin.i32(i32 [[MIN:%.*]], i32 [[A32]])
704+
; CHECK-NEXT: [[SEL:%.*]] = trunc i32 [[SEL1]] to i8
705+
; CHECK-NEXT: ret i8 [[SEL]]
706+
;
707+
%a32 = sext i8 %a to i32
708+
%cmp = icmp slt i32 %a32, %min
709+
%min8 = trunc i32 %min to i8
710+
%sel = select i1 %cmp, i8 %a, i8 %min8
711+
ret i8 %sel
712+
}
713+
714+
define i16 @look_through_cast_int_max(i16 %a, i32 %max) {
715+
; CHECK-LABEL: @look_through_cast_int_max(
716+
; CHECK-NEXT: [[A32:%.*]] = zext i16 [[A:%.*]] to i32
717+
; CHECK-NEXT: [[SEL1:%.*]] = call i32 @llvm.smax.i32(i32 [[MAX:%.*]], i32 [[A32]])
718+
; CHECK-NEXT: [[SEL:%.*]] = trunc i32 [[SEL1]] to i16
719+
; CHECK-NEXT: ret i16 [[SEL]]
720+
;
721+
%a32 = zext i16 %a to i32
722+
%cmp = icmp sgt i32 %max, %a32
723+
%max8 = trunc i32 %max to i16
724+
%sel = select i1 %cmp, i16 %max8, i16 %a
725+
ret i16 %sel
726+
}
727+
700728
define <2 x i8> @min_through_cast_vec1(<2 x i32> %x) {
701729
; CHECK-LABEL: @min_through_cast_vec1(
702730
; CHECK-NEXT: [[RES1:%.*]] = call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[X:%.*]], <2 x i32> <i32 510, i32 511>)
@@ -721,6 +749,34 @@ define <2 x i8> @min_through_cast_vec2(<2 x i32> %x) {
721749
ret <2 x i8> %res
722750
}
723751

752+
define <8 x i8> @look_through_cast_int_min_vec(<8 x i8> %a, <8 x i32> %min) {
753+
; CHECK-LABEL: @look_through_cast_int_min_vec(
754+
; CHECK-NEXT: [[A32:%.*]] = sext <8 x i8> [[A:%.*]] to <8 x i32>
755+
; CHECK-NEXT: [[SEL1:%.*]] = call <8 x i32> @llvm.umin.v8i32(<8 x i32> [[MIN:%.*]], <8 x i32> [[A32]])
756+
; CHECK-NEXT: [[SEL:%.*]] = trunc <8 x i32> [[SEL1]] to <8 x i8>
757+
; CHECK-NEXT: ret <8 x i8> [[SEL]]
758+
;
759+
%a32 = sext <8 x i8> %a to <8 x i32>
760+
%cmp = icmp ult <8 x i32> %a32, %min
761+
%min8 = trunc <8 x i32> %min to <8 x i8>
762+
%sel = select <8 x i1> %cmp, <8 x i8> %a, <8 x i8> %min8
763+
ret <8 x i8> %sel
764+
}
765+
766+
define <8 x i32> @look_through_cast_int_max_vec(<8 x i32> %a, <8 x i64> %max) {
767+
; CHECK-LABEL: @look_through_cast_int_max_vec(
768+
; CHECK-NEXT: [[A32:%.*]] = zext <8 x i32> [[A:%.*]] to <8 x i64>
769+
; CHECK-NEXT: [[SEL1:%.*]] = call <8 x i64> @llvm.smax.v8i64(<8 x i64> [[MAX:%.*]], <8 x i64> [[A32]])
770+
; CHECK-NEXT: [[SEL:%.*]] = trunc <8 x i64> [[SEL1]] to <8 x i32>
771+
; CHECK-NEXT: ret <8 x i32> [[SEL]]
772+
;
773+
%a32 = zext <8 x i32> %a to <8 x i64>
774+
%cmp = icmp sgt <8 x i64> %a32, %max
775+
%max8 = trunc <8 x i64> %max to <8 x i32>
776+
%sel = select <8 x i1> %cmp, <8 x i32> %a, <8 x i32> %max8
777+
ret <8 x i32> %sel
778+
}
779+
724780
; Remove a min/max op in a sequence with a common operand.
725781
; PR35717: https://bugs.llvm.org/show_bug.cgi?id=35717
726782

0 commit comments

Comments
 (0)