From f13dc126f3d65c397fa41da151b4e5b234d00e8a Mon Sep 17 00:00:00 2001 From: Andreas Jonson Date: Mon, 4 Aug 2025 15:19:35 +0200 Subject: [PATCH 1/2] [InstCombine] Add test for trunc with dominating icmp. --- llvm/test/Transforms/InstCombine/trunc.ll | 142 ++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll index dfe9d941f840c..faea50042c32f 100644 --- a/llvm/test/Transforms/InstCombine/trunc.ll +++ b/llvm/test/Transforms/InstCombine/trunc.ll @@ -1218,3 +1218,145 @@ define i2 @neg_trunc_nsw_i2_non_zero(i8 %1) { %ret = trunc nsw i8 %1 to i2 ret i2 %ret } + +define i1 @trunc_nuw_i1_dominating_icmp_ne_1(i8 %x) { +; CHECK-LABEL: @trunc_nuw_i1_dominating_icmp_ne_1( +; CHECK-NEXT: [[ICMP_NOT:%.*]] = icmp eq i8 [[X:%.*]], 1 +; CHECK-NEXT: br i1 [[ICMP_NOT]], label [[BB2:%.*]], label [[BB1:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[RET1:%.*]] = trunc nuw i8 [[X]] to i1 +; CHECK-NEXT: ret i1 [[RET1]] +; CHECK: bb2: +; CHECK-NEXT: ret i1 true +; + %icmp = icmp ne i8 %x, 1 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc nuw i8 %x to i1 + ret i1 %ret1 +bb2: + %ret2 = trunc nuw i8 %x to i1 + ret i1 %ret2 +} + +define i1 @trunc_nuw_i1_dominating_icmp_eq_1(i8 %x) { +; CHECK-LABEL: @trunc_nuw_i1_dominating_icmp_eq_1( +; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i8 [[X:%.*]], 1 +; CHECK-NEXT: br i1 [[ICMP]], label [[BB1:%.*]], label [[BB2:%.*]] +; CHECK: bb1: +; CHECK-NEXT: ret i1 true +; CHECK: bb2: +; CHECK-NEXT: [[RET2:%.*]] = trunc nuw i8 [[X]] to i1 +; CHECK-NEXT: ret i1 [[RET2]] +; + %icmp = icmp eq i8 %x, 1 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc nuw i8 %x to i1 + ret i1 %ret1 +bb2: + %ret2 = trunc nuw i8 %x to i1 + ret i1 %ret2 +} + +define i1 @trunc_nuw_i1_dominating_icmp_ne_0(i8 %x) { +; CHECK-LABEL: @trunc_nuw_i1_dominating_icmp_ne_0( +; CHECK-NEXT: [[ICMP_NOT:%.*]] = icmp eq i8 [[X:%.*]], 0 +; CHECK-NEXT: br i1 [[ICMP_NOT]], label [[BB2:%.*]], label [[BB1:%.*]] +; CHECK: bb1: +; CHECK-NEXT: ret i1 true +; CHECK: bb2: +; CHECK-NEXT: ret i1 false +; + %icmp = icmp ne i8 %x, 0 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc nuw i8 %x to i1 + ret i1 %ret1 +bb2: + %ret2 = trunc nuw i8 %x to i1 + ret i1 %ret2 +} + +define i1 @trunc_nuw_i1_dominating_icmp_eq_0(i8 %x) { +; CHECK-LABEL: @trunc_nuw_i1_dominating_icmp_eq_0( +; CHECK-NEXT: [[ICMP:%.*]] = icmp eq i8 [[X:%.*]], 0 +; CHECK-NEXT: br i1 [[ICMP]], label [[BB1:%.*]], label [[BB2:%.*]] +; CHECK: bb1: +; CHECK-NEXT: ret i1 false +; CHECK: bb2: +; CHECK-NEXT: ret i1 true +; + %icmp = icmp eq i8 %x, 0 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc nuw i8 %x to i1 + ret i1 %ret1 +bb2: + %ret2 = trunc nuw i8 %x to i1 + ret i1 %ret2 +} + +define i1 @neg_trunc_i1_dominating_icmp_ne_1(i8 %x) { +; CHECK-LABEL: @neg_trunc_i1_dominating_icmp_ne_1( +; CHECK-NEXT: [[ICMP_NOT:%.*]] = icmp eq i8 [[X:%.*]], 1 +; CHECK-NEXT: br i1 [[ICMP_NOT]], label [[BB2:%.*]], label [[BB1:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[RET1:%.*]] = trunc i8 [[X]] to i1 +; CHECK-NEXT: ret i1 [[RET1]] +; CHECK: bb2: +; CHECK-NEXT: ret i1 true +; + %icmp = icmp ne i8 %x, 1 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc i8 %x to i1 + ret i1 %ret1 +bb2: + %ret2 = trunc i8 %x to i1 + ret i1 %ret2 +} + +define i2 @neg_trunc_nuw_i2_dominating_icmp_ne_1(i8 %x) { +; CHECK-LABEL: @neg_trunc_nuw_i2_dominating_icmp_ne_1( +; CHECK-NEXT: [[ICMP_NOT:%.*]] = icmp eq i8 [[X:%.*]], 1 +; CHECK-NEXT: br i1 [[ICMP_NOT]], label [[BB2:%.*]], label [[BB1:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[RET1:%.*]] = trunc nuw i8 [[X]] to i2 +; CHECK-NEXT: ret i2 [[RET1]] +; CHECK: bb2: +; CHECK-NEXT: ret i2 1 +; + %icmp = icmp ne i8 %x, 1 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc nuw i8 %x to i2 + ret i2 %ret1 +bb2: + %ret2 = trunc nuw i8 %x to i2 + ret i2 %ret2 +} + +define i1 @neg_trunc_nuw_i1_not_dominating_icmp_ne_1(i8 %x, i1 %y) { +; CHECK-LABEL: @neg_trunc_nuw_i1_not_dominating_icmp_ne_1( +; CHECK-NEXT: br i1 [[Y:%.*]], label [[BB1:%.*]], label [[BB0:%.*]] +; CHECK: bb0: +; CHECK-NEXT: [[ICMP_NOT:%.*]] = icmp eq i8 [[X:%.*]], 1 +; CHECK-NEXT: br i1 [[ICMP_NOT]], label [[BB2:%.*]], label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: [[RET1:%.*]] = trunc nuw i8 [[X]] to i1 +; CHECK-NEXT: ret i1 [[RET1]] +; CHECK: bb2: +; CHECK-NEXT: ret i1 true +; + br i1 %y, label %bb1, label %bb0 +bb0: + %icmp = icmp ne i8 %x, 1 + br i1 %icmp, label %bb1, label %bb2 +bb1: + %ret1 = trunc nuw i8 %x to i1 + ret i1 %ret1 +bb2: + %ret2 = trunc nuw i8 %x to i1 + ret i1 %ret2 +} From 424fe1ef7ae369b307abfac0634a978916a4ca09 Mon Sep 17 00:00:00 2001 From: Andreas Jonson Date: Mon, 4 Aug 2025 15:20:58 +0200 Subject: [PATCH 2/2] [InstCombine] Fold trunc nuw i1 to false when dominated by icmp. --- .../InstCombine/InstCombineCasts.cpp | 32 +++++++++++++++++++ llvm/test/Transforms/InstCombine/trunc.ll | 6 ++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index a43a6ee1f58b0..8529d7b30b56d 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -951,6 +951,38 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) { } } + // Fold Trunc nuw i1 With Dominating ICmp + if (DestWidth == 1 && Trunc.hasNoUnsignedWrap()) { + auto HandleDomCond = [&](ICmpInst::Predicate DomPred, + const APInt *DomC) -> Instruction * { + ConstantRange DominatingCR = + ConstantRange::makeExactICmpRegion(DomPred, *DomC); + if (!DominatingCR.contains(APInt(SrcWidth, 1))) + return replaceInstUsesWith(Trunc, Builder.getFalse()); + return nullptr; + }; + + for (BranchInst *BI : DC.conditionsFor(Src)) { + CmpPredicate DomPred; + const APInt *DomC; + if (!match(BI->getCondition(), + m_ICmp(DomPred, m_Specific(Src), m_APInt(DomC)))) + continue; + + BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0)); + if (DT.dominates(Edge0, Trunc.getParent())) { + if (auto *V = HandleDomCond(DomPred, DomC)) + return V; + } else { + BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1)); + if (DT.dominates(Edge1, Trunc.getParent())) + if (auto *V = + HandleDomCond(CmpInst::getInversePredicate(DomPred), DomC)) + return V; + } + } + } + if (DestWidth == 1 && (Trunc.hasNoUnsignedWrap() || Trunc.hasNoSignedWrap()) && isKnownNonZero(Src, SQ.getWithInstruction(&Trunc))) diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll index faea50042c32f..a03aa88063247 100644 --- a/llvm/test/Transforms/InstCombine/trunc.ll +++ b/llvm/test/Transforms/InstCombine/trunc.ll @@ -1224,8 +1224,7 @@ define i1 @trunc_nuw_i1_dominating_icmp_ne_1(i8 %x) { ; CHECK-NEXT: [[ICMP_NOT:%.*]] = icmp eq i8 [[X:%.*]], 1 ; CHECK-NEXT: br i1 [[ICMP_NOT]], label [[BB2:%.*]], label [[BB1:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[RET1:%.*]] = trunc nuw i8 [[X]] to i1 -; CHECK-NEXT: ret i1 [[RET1]] +; CHECK-NEXT: ret i1 false ; CHECK: bb2: ; CHECK-NEXT: ret i1 true ; @@ -1246,8 +1245,7 @@ define i1 @trunc_nuw_i1_dominating_icmp_eq_1(i8 %x) { ; CHECK: bb1: ; CHECK-NEXT: ret i1 true ; CHECK: bb2: -; CHECK-NEXT: [[RET2:%.*]] = trunc nuw i8 [[X]] to i1 -; CHECK-NEXT: ret i1 [[RET2]] +; CHECK-NEXT: ret i1 false ; %icmp = icmp eq i8 %x, 1 br i1 %icmp, label %bb1, label %bb2