Skip to content

Commit b0eeacb

Browse files
authored
[InstCombine] Fold icmp of trunc nuw/nsw (#90436)
Convert the existing foldICmpTruncWithTruncOrExt() fold to work with trunc nowrap flags instead of computeKnownBits(). This also allows us to generalize the fold to work with signed comparisons. Interestingly, apart from the obvious combinations like signed predicates with trunc nsw, some non-obvious ones are also legal. For example for unsigned predicates we can do the transform for two trunc nsw as well (rather than only trunc nuw). Proofs: https://alive2.llvm.org/ce/z/ndewwK
1 parent fc83eda commit b0eeacb

File tree

3 files changed

+83
-76
lines changed

3 files changed

+83
-76
lines changed

llvm/include/llvm/IR/PatternMatch.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,6 +1839,19 @@ template <typename Op_t> struct NNegZExt_match {
18391839
}
18401840
};
18411841

1842+
template <typename Op_t, unsigned WrapFlags = 0> struct NoWrapTrunc_match {
1843+
Op_t Op;
1844+
1845+
NoWrapTrunc_match(const Op_t &OpMatch) : Op(OpMatch) {}
1846+
1847+
template <typename OpTy> bool match(OpTy *V) {
1848+
if (auto *I = dyn_cast<TruncInst>(V))
1849+
return (I->getNoWrapKind() & WrapFlags) == WrapFlags &&
1850+
Op.match(I->getOperand(0));
1851+
return false;
1852+
}
1853+
};
1854+
18421855
/// Matches BitCast.
18431856
template <typename OpTy>
18441857
inline CastOperator_match<OpTy, Instruction::BitCast>
@@ -1900,6 +1913,20 @@ inline CastOperator_match<OpTy, Instruction::Trunc> m_Trunc(const OpTy &Op) {
19001913
return CastOperator_match<OpTy, Instruction::Trunc>(Op);
19011914
}
19021915

1916+
/// Matches trunc nuw.
1917+
template <typename OpTy>
1918+
inline NoWrapTrunc_match<OpTy, TruncInst::NoUnsignedWrap>
1919+
m_NUWTrunc(const OpTy &Op) {
1920+
return NoWrapTrunc_match<OpTy, TruncInst::NoUnsignedWrap>(Op);
1921+
}
1922+
1923+
/// Matches trunc nsw.
1924+
template <typename OpTy>
1925+
inline NoWrapTrunc_match<OpTy, TruncInst::NoSignedWrap>
1926+
m_NSWTrunc(const OpTy &Op) {
1927+
return NoWrapTrunc_match<OpTy, TruncInst::NoSignedWrap>(Op);
1928+
}
1929+
19031930
template <typename OpTy>
19041931
inline match_combine_or<CastOperator_match<OpTy, Instruction::Trunc>, OpTy>
19051932
m_TruncOrSelf(const OpTy &Op) {

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,19 +1479,29 @@ Instruction *InstCombinerImpl::foldICmpTruncConstant(ICmpInst &Cmp,
14791479
return nullptr;
14801480
}
14811481

1482-
/// Fold icmp (trunc X), (trunc Y).
1483-
/// Fold icmp (trunc X), (zext Y).
1482+
/// Fold icmp (trunc nuw/nsw X), (trunc nuw/nsw Y).
1483+
/// Fold icmp (trunc nuw/nsw X), (zext/sext Y).
14841484
Instruction *
14851485
InstCombinerImpl::foldICmpTruncWithTruncOrExt(ICmpInst &Cmp,
14861486
const SimplifyQuery &Q) {
1487-
if (Cmp.isSigned())
1488-
return nullptr;
1489-
14901487
Value *X, *Y;
14911488
ICmpInst::Predicate Pred;
1492-
bool YIsZext = false;
1489+
bool YIsSExt = false;
14931490
// Try to match icmp (trunc X), (trunc Y)
14941491
if (match(&Cmp, m_ICmp(Pred, m_Trunc(m_Value(X)), m_Trunc(m_Value(Y))))) {
1492+
unsigned NoWrapFlags = cast<TruncInst>(Cmp.getOperand(0))->getNoWrapKind() &
1493+
cast<TruncInst>(Cmp.getOperand(1))->getNoWrapKind();
1494+
if (Cmp.isSigned()) {
1495+
// For signed comparisons, both truncs must be nsw.
1496+
if (!(NoWrapFlags & TruncInst::NoSignedWrap))
1497+
return nullptr;
1498+
} else {
1499+
// For unsigned and equality comparisons, either both must be nuw or
1500+
// both must be nsw, we don't care which.
1501+
if (!NoWrapFlags)
1502+
return nullptr;
1503+
}
1504+
14951505
if (X->getType() != Y->getType() &&
14961506
(!Cmp.getOperand(0)->hasOneUse() || !Cmp.getOperand(1)->hasOneUse()))
14971507
return nullptr;
@@ -1501,12 +1511,19 @@ InstCombinerImpl::foldICmpTruncWithTruncOrExt(ICmpInst &Cmp,
15011511
Pred = Cmp.getSwappedPredicate(Pred);
15021512
}
15031513
}
1504-
// Try to match icmp (trunc X), (zext Y)
1505-
else if (match(&Cmp, m_c_ICmp(Pred, m_Trunc(m_Value(X)),
1506-
m_OneUse(m_ZExt(m_Value(Y))))))
1507-
1508-
YIsZext = true;
1509-
else
1514+
// Try to match icmp (trunc nuw X), (zext Y)
1515+
else if (!Cmp.isSigned() &&
1516+
match(&Cmp, m_c_ICmp(Pred, m_NUWTrunc(m_Value(X)),
1517+
m_OneUse(m_ZExt(m_Value(Y)))))) {
1518+
// Can fold trunc nuw + zext for unsigned and equality predicates.
1519+
}
1520+
// Try to match icmp (trunc nsw X), (sext Y)
1521+
else if (match(&Cmp, m_c_ICmp(Pred, m_NSWTrunc(m_Value(X)),
1522+
m_OneUse(m_ZExtOrSExt(m_Value(Y)))))) {
1523+
// Can fold trunc nsw + zext/sext for all predicates.
1524+
YIsSExt =
1525+
isa<SExtInst>(Cmp.getOperand(0)) || isa<SExtInst>(Cmp.getOperand(1));
1526+
} else
15101527
return nullptr;
15111528

15121529
Type *TruncTy = Cmp.getOperand(0)->getType();
@@ -1518,19 +1535,7 @@ InstCombinerImpl::foldICmpTruncWithTruncOrExt(ICmpInst &Cmp,
15181535
!isDesirableIntType(X->getType()->getScalarSizeInBits()))
15191536
return nullptr;
15201537

1521-
// Check if the trunc is unneeded.
1522-
KnownBits KnownX = llvm::computeKnownBits(X, /*Depth*/ 0, Q);
1523-
if (KnownX.countMaxActiveBits() > TruncBits)
1524-
return nullptr;
1525-
1526-
if (!YIsZext) {
1527-
// If Y is also a trunc, make sure it is unneeded.
1528-
KnownBits KnownY = llvm::computeKnownBits(Y, /*Depth*/ 0, Q);
1529-
if (KnownY.countMaxActiveBits() > TruncBits)
1530-
return nullptr;
1531-
}
1532-
1533-
Value *NewY = Builder.CreateZExtOrTrunc(Y, X->getType());
1538+
Value *NewY = Builder.CreateIntCast(Y, X->getType(), YIsSExt);
15341539
return new ICmpInst(Pred, X, NewY);
15351540
}
15361541

llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll

Lines changed: 26 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,7 @@ define i1 @icmp_trunc_x_zext_y_fail_multiuse(i32 %x, i8 %y) {
271271

272272
define i1 @trunc_unsigned_nuw(i16 %x, i16 %y) {
273273
; CHECK-LABEL: @trunc_unsigned_nuw(
274-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw i16 [[X:%.*]] to i8
275-
; CHECK-NEXT: [[YT:%.*]] = trunc nuw i16 [[Y:%.*]] to i8
276-
; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[XT]], [[YT]]
274+
; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[X:%.*]], [[Y:%.*]]
277275
; CHECK-NEXT: ret i1 [[C]]
278276
;
279277
%xt = trunc nuw i16 %x to i8
@@ -284,9 +282,7 @@ define i1 @trunc_unsigned_nuw(i16 %x, i16 %y) {
284282

285283
define i1 @trunc_unsigned_nsw(i16 %x, i16 %y) {
286284
; CHECK-LABEL: @trunc_unsigned_nsw(
287-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i16 [[X:%.*]] to i8
288-
; CHECK-NEXT: [[YT:%.*]] = trunc nsw i16 [[Y:%.*]] to i8
289-
; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[XT]], [[YT]]
285+
; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[X:%.*]], [[Y:%.*]]
290286
; CHECK-NEXT: ret i1 [[C]]
291287
;
292288
%xt = trunc nsw i16 %x to i8
@@ -297,9 +293,7 @@ define i1 @trunc_unsigned_nsw(i16 %x, i16 %y) {
297293

298294
define i1 @trunc_unsigned_both(i16 %x, i16 %y) {
299295
; CHECK-LABEL: @trunc_unsigned_both(
300-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i16 [[X:%.*]] to i8
301-
; CHECK-NEXT: [[YT:%.*]] = trunc nuw nsw i16 [[Y:%.*]] to i8
302-
; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[XT]], [[YT]]
296+
; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[X:%.*]], [[Y:%.*]]
303297
; CHECK-NEXT: ret i1 [[C]]
304298
;
305299
%xt = trunc nuw nsw i16 %x to i8
@@ -336,9 +330,7 @@ define i1 @trunc_signed_nuw(i16 %x, i16 %y) {
336330

337331
define i1 @trunc_signed_nsw(i16 %x, i16 %y) {
338332
; CHECK-LABEL: @trunc_signed_nsw(
339-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i16 [[X:%.*]] to i8
340-
; CHECK-NEXT: [[YT:%.*]] = trunc nsw i16 [[Y:%.*]] to i8
341-
; CHECK-NEXT: [[C:%.*]] = icmp slt i8 [[XT]], [[YT]]
333+
; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[X:%.*]], [[Y:%.*]]
342334
; CHECK-NEXT: ret i1 [[C]]
343335
;
344336
%xt = trunc nsw i16 %x to i8
@@ -349,9 +341,7 @@ define i1 @trunc_signed_nsw(i16 %x, i16 %y) {
349341

350342
define i1 @trunc_signed_both(i16 %x, i16 %y) {
351343
; CHECK-LABEL: @trunc_signed_both(
352-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i16 [[X:%.*]] to i8
353-
; CHECK-NEXT: [[YT:%.*]] = trunc nuw nsw i16 [[Y:%.*]] to i8
354-
; CHECK-NEXT: [[C:%.*]] = icmp slt i8 [[XT]], [[YT]]
344+
; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[X:%.*]], [[Y:%.*]]
355345
; CHECK-NEXT: ret i1 [[C]]
356346
;
357347
%xt = trunc nuw nsw i16 %x to i8
@@ -375,9 +365,7 @@ define i1 @trunc_signed_either(i16 %x, i16 %y) {
375365

376366
define i1 @trunc_equality_nuw(i16 %x, i16 %y) {
377367
; CHECK-LABEL: @trunc_equality_nuw(
378-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw i16 [[X:%.*]] to i8
379-
; CHECK-NEXT: [[YT:%.*]] = trunc nuw i16 [[Y:%.*]] to i8
380-
; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[XT]], [[YT]]
368+
; CHECK-NEXT: [[C:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
381369
; CHECK-NEXT: ret i1 [[C]]
382370
;
383371
%xt = trunc nuw i16 %x to i8
@@ -388,9 +376,7 @@ define i1 @trunc_equality_nuw(i16 %x, i16 %y) {
388376

389377
define i1 @trunc_equality_nsw(i16 %x, i16 %y) {
390378
; CHECK-LABEL: @trunc_equality_nsw(
391-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i16 [[X:%.*]] to i8
392-
; CHECK-NEXT: [[YT:%.*]] = trunc nsw i16 [[Y:%.*]] to i8
393-
; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[XT]], [[YT]]
379+
; CHECK-NEXT: [[C:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
394380
; CHECK-NEXT: ret i1 [[C]]
395381
;
396382
%xt = trunc nsw i16 %x to i8
@@ -401,9 +387,7 @@ define i1 @trunc_equality_nsw(i16 %x, i16 %y) {
401387

402388
define i1 @trunc_equality_both(i16 %x, i16 %y) {
403389
; CHECK-LABEL: @trunc_equality_both(
404-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i16 [[X:%.*]] to i8
405-
; CHECK-NEXT: [[YT:%.*]] = trunc nuw nsw i16 [[Y:%.*]] to i8
406-
; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[XT]], [[YT]]
390+
; CHECK-NEXT: [[C:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
407391
; CHECK-NEXT: ret i1 [[C]]
408392
;
409393
%xt = trunc nuw nsw i16 %x to i8
@@ -427,9 +411,8 @@ define i1 @trunc_equality_either(i16 %x, i16 %y) {
427411

428412
define i1 @trunc_unsigned_nuw_zext(i32 %x, i8 %y) {
429413
; CHECK-LABEL: @trunc_unsigned_nuw_zext(
430-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw i32 [[X:%.*]] to i16
431-
; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16
432-
; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[XT]], [[YE]]
414+
; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32
415+
; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[TMP1]], [[X:%.*]]
433416
; CHECK-NEXT: ret i1 [[C]]
434417
;
435418
%xt = trunc nuw i32 %x to i16
@@ -453,9 +436,8 @@ define i1 @trunc_unsigned_nuw_sext(i32 %x, i8 %y) {
453436

454437
define i1 @trunc_unsigned_nsw_zext(i32 %x, i8 %y) {
455438
; CHECK-LABEL: @trunc_unsigned_nsw_zext(
456-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16
457-
; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16
458-
; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[XT]], [[YE]]
439+
; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32
440+
; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[TMP1]], [[X:%.*]]
459441
; CHECK-NEXT: ret i1 [[C]]
460442
;
461443
%xt = trunc nsw i32 %x to i16
@@ -466,9 +448,8 @@ define i1 @trunc_unsigned_nsw_zext(i32 %x, i8 %y) {
466448

467449
define i1 @trunc_unsigned_nsw_sext(i32 %x, i8 %y) {
468450
; CHECK-LABEL: @trunc_unsigned_nsw_sext(
469-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16
470-
; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16
471-
; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[XT]], [[YE]]
451+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32
452+
; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[TMP1]], [[X:%.*]]
472453
; CHECK-NEXT: ret i1 [[C]]
473454
;
474455
%xt = trunc nsw i32 %x to i16
@@ -479,9 +460,8 @@ define i1 @trunc_unsigned_nsw_sext(i32 %x, i8 %y) {
479460

480461
define i1 @trunc_signed_nsw_sext(i32 %x, i8 %y) {
481462
; CHECK-LABEL: @trunc_signed_nsw_sext(
482-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16
483-
; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16
484-
; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[XT]], [[YE]]
463+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32
464+
; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[TMP1]], [[X:%.*]]
485465
; CHECK-NEXT: ret i1 [[C]]
486466
;
487467
%xt = trunc nsw i32 %x to i16
@@ -492,9 +472,8 @@ define i1 @trunc_signed_nsw_sext(i32 %x, i8 %y) {
492472

493473
define i1 @trunc_signed_nsw_zext(i32 %x, i8 %y) {
494474
; CHECK-LABEL: @trunc_signed_nsw_zext(
495-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16
496-
; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16
497-
; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[XT]], [[YE]]
475+
; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32
476+
; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[TMP1]], [[X:%.*]]
498477
; CHECK-NEXT: ret i1 [[C]]
499478
;
500479
%xt = trunc nsw i32 %x to i16
@@ -531,9 +510,8 @@ define i1 @trunc_signed_nuw_zext(i32 %x, i8 %y) {
531510

532511
define i1 @trunc_equality_nuw_zext(i32 %x, i8 %y) {
533512
; CHECK-LABEL: @trunc_equality_nuw_zext(
534-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw i32 [[X:%.*]] to i16
535-
; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16
536-
; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]]
513+
; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32
514+
; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]]
537515
; CHECK-NEXT: ret i1 [[C]]
538516
;
539517
%xt = trunc nuw i32 %x to i16
@@ -557,9 +535,8 @@ define i1 @trunc_equality_nuw_sext(i32 %x, i8 %y) {
557535

558536
define i1 @trunc_equality_nsw_zext(i32 %x, i8 %y) {
559537
; CHECK-LABEL: @trunc_equality_nsw_zext(
560-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16
561-
; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16
562-
; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]]
538+
; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32
539+
; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]]
563540
; CHECK-NEXT: ret i1 [[C]]
564541
;
565542
%xt = trunc nsw i32 %x to i16
@@ -570,9 +547,8 @@ define i1 @trunc_equality_nsw_zext(i32 %x, i8 %y) {
570547

571548
define i1 @trunc_equality_nsw_sext(i32 %x, i8 %y) {
572549
; CHECK-LABEL: @trunc_equality_nsw_sext(
573-
; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16
574-
; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16
575-
; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]]
550+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32
551+
; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]]
576552
; CHECK-NEXT: ret i1 [[C]]
577553
;
578554
%xt = trunc nsw i32 %x to i16
@@ -583,9 +559,8 @@ define i1 @trunc_equality_nsw_sext(i32 %x, i8 %y) {
583559

584560
define i1 @trunc_equality_both_sext(i32 %x, i8 %y) {
585561
; CHECK-LABEL: @trunc_equality_both_sext(
586-
; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i32 [[X:%.*]] to i16
587-
; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16
588-
; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]]
562+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32
563+
; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]]
589564
; CHECK-NEXT: ret i1 [[C]]
590565
;
591566
%xt = trunc nuw nsw i32 %x to i16

0 commit comments

Comments
 (0)