Skip to content

Commit 59388fb

Browse files
authored
[InstCombine] Preserve NSW/NUW flags when folding const BOp with min/max (#143471)
When folding `X Pred C2 ? X BOp C1 : C2 BOp C1` to `min/max(X, C2) BOp C1`, if NUW/NSW flags are present on `X BOp C1` and could be safely applied to `C2 BOp C1`, then they may be added on the BOp after the fold is complete. https://alive2.llvm.org/ce/z/n_3aNJ Preserving these flags can allow subsequent transforms to re-order the min/max and BOp, which in the case of NVPTX would allow for some potential future transformations which would improve instruction-selection.
1 parent f6bf3bd commit 59388fb

File tree

3 files changed

+110
-11
lines changed

3 files changed

+110
-11
lines changed

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,8 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
771771
Value *A, Value *B, Instruction &Outer,
772772
SelectPatternFlavor SPF2, Value *C);
773773
Instruction *foldSelectInstWithICmp(SelectInst &SI, ICmpInst *ICI);
774+
Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal,
775+
Value *FalseVal);
774776
Instruction *foldSelectValueEquivalence(SelectInst &SI, CmpInst &CI);
775777
bool replaceInInstruction(Value *V, Value *Old, Value *New,
776778
unsigned Depth = 0);

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,9 +1879,9 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
18791879

18801880
/// Fold `X Pred C1 ? X BOp C2 : C1 BOp C2` to `min/max(X, C1) BOp C2`.
18811881
/// This allows for better canonicalization.
1882-
static Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal,
1883-
Value *FalseVal,
1884-
IRBuilderBase &Builder) {
1882+
Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp,
1883+
Value *TrueVal,
1884+
Value *FalseVal) {
18851885
Constant *C1, *C2, *C3;
18861886
Value *X;
18871887
CmpPredicate Predicate;
@@ -1945,11 +1945,29 @@ static Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal,
19451945
return nullptr;
19461946
}
19471947

1948-
Intrinsic::ID IntrinsicID = getMinMaxIntrinsic(SPF);
1949-
Value *Intrinsic = Builder.CreateBinaryIntrinsic(IntrinsicID, X, RHS);
1950-
return IsIntrinsic ? Builder.CreateBinaryIntrinsic(Opcode, Intrinsic, C2)
1951-
: Builder.CreateBinOp(Instruction::BinaryOps(Opcode),
1952-
Intrinsic, C2);
1948+
Intrinsic::ID MinMaxID = getMinMaxIntrinsic(SPF);
1949+
Value *MinMax = Builder.CreateBinaryIntrinsic(MinMaxID, X, RHS);
1950+
if (IsIntrinsic)
1951+
return Builder.CreateBinaryIntrinsic(Opcode, MinMax, C2);
1952+
1953+
const auto BinOpc = Instruction::BinaryOps(Opcode);
1954+
Value *BinOp = Builder.CreateBinOp(BinOpc, MinMax, C2);
1955+
1956+
// If we can attach no-wrap flags to the new instruction, do so if the
1957+
// old instruction had them and C1 BinOp C2 does not overflow.
1958+
if (Instruction *BinOpInst = dyn_cast<Instruction>(BinOp)) {
1959+
if (BinOpc == Instruction::Add || BinOpc == Instruction::Sub ||
1960+
BinOpc == Instruction::Mul) {
1961+
Instruction *OldBinOp = cast<BinaryOperator>(TrueVal);
1962+
if (OldBinOp->hasNoSignedWrap() &&
1963+
willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned=*/true))
1964+
BinOpInst->setHasNoSignedWrap();
1965+
if (OldBinOp->hasNoUnsignedWrap() &&
1966+
willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned=*/false))
1967+
BinOpInst->setHasNoUnsignedWrap();
1968+
}
1969+
}
1970+
return BinOp;
19531971
}
19541972

19551973
/// Visit a SelectInst that has an ICmpInst as its first operand.
@@ -2027,7 +2045,7 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
20272045
if (Value *V = foldAbsDiff(ICI, TrueVal, FalseVal, Builder))
20282046
return replaceInstUsesWith(SI, V);
20292047

2030-
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal, Builder))
2048+
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal))
20312049
return replaceInstUsesWith(SI, V);
20322050

20332051
return Changed ? &SI : nullptr;

llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ define i8 @add_and_sgt(i8 %x) {
55
; CHECK-LABEL: define i8 @add_and_sgt(
66
; CHECK-SAME: i8 [[X:%.*]]) {
77
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 8)
8-
; CHECK-NEXT: [[S:%.*]] = add nuw i8 [[TMP1]], 16
8+
; CHECK-NEXT: [[S:%.*]] = add nuw nsw i8 [[TMP1]], 16
99
; CHECK-NEXT: ret i8 [[S]]
1010
;
1111
%add = add nsw i8 %x, 16
@@ -155,7 +155,7 @@ define i8 @multi_use_cond_and_sel(i8 %x) {
155155
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X]], 8
156156
; CHECK-NEXT: call void @use(i1 [[CMP]])
157157
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 8)
158-
; CHECK-NEXT: [[S:%.*]] = add nuw i8 [[TMP1]], 16
158+
; CHECK-NEXT: [[S:%.*]] = add nuw nsw i8 [[TMP1]], 16
159159
; CHECK-NEXT: call void @use_byte(i8 [[S]])
160160
; CHECK-NEXT: ret i8 [[S]]
161161
;
@@ -450,3 +450,82 @@ define i8 @umax_sgt(i8 %x) {
450450
%s = select i1 %cmp, i8 100, i8 %umax
451451
ret i8 %s
452452
}
453+
454+
define i8 @add_sgt_nuw_nsw_safe(i8 %x) {
455+
; CHECK-LABEL: define i8 @add_sgt_nuw_nsw_safe(
456+
; CHECK-SAME: i8 [[X:%.*]]) {
457+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100)
458+
; CHECK-NEXT: [[S:%.*]] = add nuw nsw i8 [[TMP1]], 1
459+
; CHECK-NEXT: ret i8 [[S]]
460+
;
461+
%add = add nuw nsw i8 %x, 1
462+
%cmp = icmp sgt i8 %x, 100
463+
%s = select i1 %cmp, i8 101, i8 %add
464+
ret i8 %s
465+
}
466+
467+
define i8 @add_sgt_nuw_only(i8 %x) {
468+
; CHECK-LABEL: define i8 @add_sgt_nuw_only(
469+
; CHECK-SAME: i8 [[X:%.*]]) {
470+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100)
471+
; CHECK-NEXT: [[S:%.*]] = add nuw i8 [[TMP1]], 50
472+
; CHECK-NEXT: ret i8 [[S]]
473+
;
474+
%add = add nuw nsw i8 %x, 50
475+
%cmp = icmp sgt i8 %x, 100
476+
%s = select i1 %cmp, i8 150, i8 %add
477+
ret i8 %s
478+
}
479+
480+
define i8 @add_sgt_nsw_only(i8 %x) {
481+
; CHECK-LABEL: define i8 @add_sgt_nsw_only(
482+
; CHECK-SAME: i8 [[X:%.*]]) {
483+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100)
484+
; CHECK-NEXT: [[S:%.*]] = add nsw i8 [[TMP1]], -99
485+
; CHECK-NEXT: ret i8 [[S]]
486+
;
487+
%add = add nuw nsw i8 %x, -99
488+
%cmp = icmp sgt i8 %x, 100
489+
%s = select i1 %cmp, i8 1, i8 %add
490+
ret i8 %s
491+
}
492+
493+
494+
define i8 @mul_ult_nuw_nsw_safe(i8 %x) {
495+
; CHECK-LABEL: define i8 @mul_ult_nuw_nsw_safe(
496+
; CHECK-SAME: i8 [[X:%.*]]) {
497+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 10)
498+
; CHECK-NEXT: [[S:%.*]] = mul nuw nsw i8 [[TMP1]], 3
499+
; CHECK-NEXT: ret i8 [[S]]
500+
;
501+
%mul = mul nuw nsw i8 %x, 3
502+
%cmp = icmp ult i8 %x, 10
503+
%s = select i1 %cmp, i8 30, i8 %mul
504+
ret i8 %s
505+
}
506+
507+
define i8 @mul_ult_nuw_only(i8 %x) {
508+
; CHECK-LABEL: define i8 @mul_ult_nuw_only(
509+
; CHECK-SAME: i8 [[X:%.*]]) {
510+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 10)
511+
; CHECK-NEXT: [[S:%.*]] = mul nuw i8 [[TMP1]], 25
512+
; CHECK-NEXT: ret i8 [[S]]
513+
;
514+
%mul = mul nuw nsw i8 %x, 25
515+
%cmp = icmp ult i8 %x, 10
516+
%s = select i1 %cmp, i8 250, i8 %mul
517+
ret i8 %s
518+
}
519+
520+
define i8 @mul_ult_nsw_only(i8 %x) {
521+
; CHECK-LABEL: define i8 @mul_ult_nsw_only(
522+
; CHECK-SAME: i8 [[X:%.*]]) {
523+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 40)
524+
; CHECK-NEXT: [[S:%.*]] = mul nsw i8 [[TMP1]], -2
525+
; CHECK-NEXT: ret i8 [[S]]
526+
;
527+
%mul = mul nuw nsw i8 %x, -2
528+
%cmp = icmp ult i8 %x, 40
529+
%s = select i1 %cmp, i8 -80, i8 %mul
530+
ret i8 %s
531+
}

0 commit comments

Comments
 (0)