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 dfe9d941f840c..a03aa88063247 100644 --- a/llvm/test/Transforms/InstCombine/trunc.ll +++ b/llvm/test/Transforms/InstCombine/trunc.ll @@ -1218,3 +1218,143 @@ 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: ret i1 false +; 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: ret i1 false +; + %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 +}