diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp index a842a5edcb8a3..b82dd88309614 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp @@ -1290,6 +1290,8 @@ static Value *simplifyUsingControlFlow(InstCombiner &Self, PHINode &PN, // ... ... // \ / // phi [v1] [v2] + if (!PN.getType()->isIntegerTy()) + return nullptr; // Make sure all inputs are constants. if (!all_of(PN.operands(), [](Value *V) { return isa(V); })) return nullptr; @@ -1301,6 +1303,7 @@ static Value *simplifyUsingControlFlow(InstCombiner &Self, PHINode &PN, // Determine which value the condition of the idom has for which successor. LLVMContext &Context = PN.getContext(); + unsigned PNBitWidth = PN.getType()->getScalarSizeInBits(); auto *IDom = DT.getNode(BB)->getIDom()->getBlock(); Value *Cond; SmallDenseMap SuccForValue; @@ -1314,65 +1317,109 @@ static Value *simplifyUsingControlFlow(InstCombiner &Self, PHINode &PN, return nullptr; Cond = BI->getCondition(); + + if (Cond->getType() != PN.getType()) + return nullptr; + AddSucc(ConstantInt::getTrue(Context), BI->getSuccessor(0)); AddSucc(ConstantInt::getFalse(Context), BI->getSuccessor(1)); } else if (auto *SI = dyn_cast(IDom->getTerminator())) { Cond = SI->getCondition(); + unsigned CondBitWidth = Cond->getType()->getScalarSizeInBits(); ++SuccCount[SI->getDefaultDest()]; - for (auto Case : SI->cases()) - AddSucc(Case.getCaseValue(), Case.getCaseSuccessor()); + for (auto Case : SI->cases()) { + ConstantInt *CaseValue = Case.getCaseValue(); + if (CondBitWidth > PNBitWidth) { + CaseValue = ConstantInt::get( + Context, Case.getCaseValue()->getValue().trunc(PNBitWidth)); + } + AddSucc(CaseValue, Case.getCaseSuccessor()); + } } else { return nullptr; } - if (Cond->getType() != PN.getType()) - return nullptr; + unsigned CondBitWidth = Cond->getType()->getScalarSizeInBits(); // Check that edges outgoing from the idom's terminators dominate respective // inputs of the Phi. - std::optional Invert; - for (auto Pair : zip(PN.incoming_values(), PN.blocks())) { - auto *Input = cast(std::get<0>(Pair)); - BasicBlock *Pred = std::get<1>(Pair); - auto IsCorrectInput = [&](ConstantInt *Input) { - // The input needs to be dominated by the corresponding edge of the idom. - // This edge cannot be a multi-edge, as that would imply that multiple - // different condition values follow the same edge. - auto It = SuccForValue.find(Input); - return It != SuccForValue.end() && SuccCount[It->second] == 1 && - DT.dominates(BasicBlockEdge(IDom, It->second), - BasicBlockEdge(Pred, BB)); - }; - - // Depending on the constant, the condition may need to be inverted. - bool NeedsInvert; - if (IsCorrectInput(Input)) - NeedsInvert = false; - else if (IsCorrectInput(cast(ConstantExpr::getNot(Input)))) - NeedsInvert = true; - else - return nullptr; + auto CheckInputs = [&](Instruction::CastOps CastType) -> std::optional { + std::optional Invert; + for (auto Pair : zip(PN.incoming_values(), PN.blocks())) { + auto *Input = cast(std::get<0>(Pair)); + BasicBlock *Pred = std::get<1>(Pair); + + auto IsCorrectInput = [&](ConstantInt *Input) { + if (CondBitWidth < PNBitWidth) { + if ((CastType == Instruction::SExt && + !Input->getValue().isSignedIntN(CondBitWidth)) || + (CastType == Instruction::ZExt && + !Input->getValue().isIntN(CondBitWidth))) + return false; + + Input = + ConstantInt::get(Context, Input->getValue().trunc(CondBitWidth)); + } + // The input needs to be dominated by the corresponding edge of the + // idom. This edge cannot be a multi-edge, as that would imply that + // multiple different condition values follow the same edge. + auto It = SuccForValue.find(Input); + return It != SuccForValue.end() && SuccCount[It->second] == 1 && + DT.dominates(BasicBlockEdge(IDom, It->second), + BasicBlockEdge(Pred, BB)); + }; + + // Depending on the constant, the condition may need to be inverted. + bool NeedsInvert; + if (IsCorrectInput(Input)) + NeedsInvert = false; + else if (IsCorrectInput(cast(ConstantExpr::getNot(Input)))) + NeedsInvert = true; + else + return std::nullopt; - // Make sure the inversion requirement is always the same. - if (Invert && *Invert != NeedsInvert) - return nullptr; + // Make sure the inversion requirement is always the same. + if (Invert && *Invert != NeedsInvert) + return std::nullopt; - Invert = NeedsInvert; + Invert = NeedsInvert; + } + return Invert; + }; + + Instruction::CastOps CastType = + CondBitWidth == PNBitWidth ? Instruction::BitCast + : CondBitWidth < PNBitWidth ? Instruction::ZExt + : Instruction::Trunc; + + auto Result = CheckInputs(CastType); + if (!Result && CondBitWidth < PNBitWidth) { + CastType = Instruction::SExt; + Result = CheckInputs(CastType); } + if (!Result) + return nullptr; + bool Invert = *Result; - if (!*Invert) + if (!Invert && CastType == Instruction::BitCast) return Cond; - // This Phi is actually opposite to branching condition of IDom. We invert - // the condition that will potentially open up some opportunities for - // sinking. auto InsertPt = BB->getFirstInsertionPt(); - if (InsertPt != BB->end()) { - Self.Builder.SetInsertPoint(&*BB, InsertPt); - return Self.Builder.CreateNot(Cond); + if (InsertPt == BB->end()) + return nullptr; + + Self.Builder.SetInsertPoint(&*BB, InsertPt); + + if (CastType != Instruction::BitCast) { + Cond = Self.Builder.CreateCast(CastType, Cond, PN.getType()); + if (!Invert) + return Cond; } - return nullptr; + // This Phi is actually opposite to branching condition of IDom. We invert + // the condition that will potentially open up some opportunities for + // sinking. + return Self.Builder.CreateNot(Cond); } // Fold iv = phi(start, iv.next = iv2.next op start) diff --git a/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll b/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll index a91a983be49bc..3ec226fc58d6b 100644 --- a/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll +++ b/llvm/test/DebugInfo/X86/instcombine-fold-cast-into-phi.ll @@ -4,7 +4,7 @@ ;; user (otherwise the dbg use becomes poison after the original phi is ;; deleted). Check the new phi inherits the DebugLoc. -; CHECK: %[[phi:.*]] = phi i8 [ 1, %{{.*}} ], [ 0, %{{.*}} ], !dbg ![[dbg:[0-9]+]] +; CHECK: %[[phi:.*]] = phi i8 [ 2, %{{.*}} ], [ 0, %{{.*}} ], !dbg ![[dbg:[0-9]+]] ; CHECK: #dbg_value(i8 %[[phi]], ![[#]], !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value) ; CHECK: ![[dbg]] = !DILocation(line: 123, @@ -19,7 +19,7 @@ if.then: ; preds = entry br label %if.end if.end: ; preds = %if.then, %entry - %p.0 = phi i32 [ 1, %if.then ], [ 0, %entry ], !dbg !13 + %p.0 = phi i32 [ 2, %if.then ], [ 0, %entry ], !dbg !13 call void @llvm.dbg.value(metadata i32 %p.0, metadata !4, metadata !DIExpression()), !dbg !13 %x = trunc i32 %p.0 to i8 %callff = call float @ff(i8 %x) diff --git a/llvm/test/Transforms/InstCombine/simple_phi_condition.ll b/llvm/test/Transforms/InstCombine/simple_phi_condition.ll index e1a90d6a1c688..61f834fa984eb 100644 --- a/llvm/test/Transforms/InstCombine/simple_phi_condition.ll +++ b/llvm/test/Transforms/InstCombine/simple_phi_condition.ll @@ -669,7 +669,7 @@ define i32 @test_phi_to_zext(i8 noundef %0) { ; CHECK: sw.1: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[DOT0:%.*]] = phi i32 [ 1, [[SW_1]] ], [ 0, [[SW_0]] ], [ 255, [[SW_255]] ] +; CHECK-NEXT: [[DOT0:%.*]] = zext i8 [[TMP0]] to i32 ; CHECK-NEXT: ret i32 [[DOT0]] ; entry: @@ -713,7 +713,8 @@ define i32 @test_phi_to_zext_inverted(i8 noundef %0) { ; CHECK: sw.1: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[DOT0:%.*]] = phi i32 [ -256, [[SW_255]] ], [ -2, [[SW_1]] ], [ -1, [[SW_0]] ] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[TMP0]] to i32 +; CHECK-NEXT: [[DOT0:%.*]] = xor i32 [[TMP1]], -1 ; CHECK-NEXT: ret i32 [[DOT0]] ; entry: @@ -740,12 +741,20 @@ merge: ret i32 %.0 } -define i8 @test_multiple_predecessors_phi_to_zext(i1 %cond, i1 %cond2) { +define i8 @test_multiple_predecessors_phi_to_zext(i2 %cond, i2 %cond2) { ; CHECK-LABEL: @test_multiple_predecessors_phi_to_zext( ; CHECK-NEXT: entry: -; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK-NEXT: switch i2 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i2 1, label [[IF_TRUE:%.*]] +; CHECK-NEXT: i2 0, label [[IF_FALSE:%.*]] +; CHECK-NEXT: ] ; CHECK: if.true: -; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF2_TRUE:%.*]], label [[IF2_FALSE:%.*]] +; CHECK-NEXT: switch i2 [[COND2:%.*]], label [[DEFAULT]] [ +; CHECK-NEXT: i2 1, label [[IF2_TRUE:%.*]] +; CHECK-NEXT: i2 0, label [[IF2_FALSE:%.*]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: unreachable ; CHECK: if.false: ; CHECK-NEXT: br label [[MERGE:%.*]] ; CHECK: if2.true: @@ -753,14 +762,23 @@ define i8 @test_multiple_predecessors_phi_to_zext(i1 %cond, i1 %cond2) { ; CHECK: if2.false: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[RET:%.*]] = phi i8 [ 0, [[IF_FALSE]] ], [ 1, [[IF2_TRUE]] ], [ 1, [[IF2_FALSE]] ] +; CHECK-NEXT: [[RET:%.*]] = zext i2 [[COND]] to i8 ; CHECK-NEXT: ret i8 [[RET]] ; entry: - br i1 %cond, label %if.true, label %if.false + switch i2 %cond, label %default [ + i2 1, label %if.true + i2 0, label %if.false + ] if.true: - br i1 %cond2, label %if2.true, label %if2.false + switch i2 %cond2, label %default [ + i2 1, label %if2.true + i2 0, label %if2.false + ] + +default: + unreachable if.false: br label %merge @@ -793,7 +811,7 @@ define i32 @test_phi_to_sext(i8 noundef %0) { ; CHECK: sw.1: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[DOT0:%.*]] = phi i32 [ 1, [[SW_1]] ], [ 0, [[SW_0]] ], [ -1, [[SW_255]] ] +; CHECK-NEXT: [[DOT0:%.*]] = sext i8 [[TMP0]] to i32 ; CHECK-NEXT: ret i32 [[DOT0]] ; entry: @@ -837,7 +855,8 @@ define i32 @test_phi_to_sext_inverted(i8 noundef %0) { ; CHECK: sw.1: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[DOT0:%.*]] = phi i32 [ -2, [[SW_1]] ], [ -1, [[SW_0]] ], [ 0, [[SW_255]] ] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[TMP0]], -1 +; CHECK-NEXT: [[DOT0:%.*]] = sext i8 [[TMP1]] to i32 ; CHECK-NEXT: ret i32 [[DOT0]] ; entry: @@ -864,12 +883,20 @@ merge: ret i32 %.0 } -define i8 @test_multiple_predecessors_phi_to_sext(i1 %cond, i1 %cond2) { +define i8 @test_multiple_predecessors_phi_to_sext(i2 %cond, i2 %cond2) { ; CHECK-LABEL: @test_multiple_predecessors_phi_to_sext( ; CHECK-NEXT: entry: -; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] +; CHECK-NEXT: switch i2 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i2 -1, label [[IF_TRUE:%.*]] +; CHECK-NEXT: i2 0, label [[IF_FALSE:%.*]] +; CHECK-NEXT: ] ; CHECK: if.true: -; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF2_TRUE:%.*]], label [[IF2_FALSE:%.*]] +; CHECK-NEXT: switch i2 [[COND2:%.*]], label [[DEFAULT]] [ +; CHECK-NEXT: i2 -1, label [[IF2_TRUE:%.*]] +; CHECK-NEXT: i2 0, label [[IF2_FALSE:%.*]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: unreachable ; CHECK: if.false: ; CHECK-NEXT: br label [[MERGE:%.*]] ; CHECK: if2.true: @@ -877,14 +904,23 @@ define i8 @test_multiple_predecessors_phi_to_sext(i1 %cond, i1 %cond2) { ; CHECK: if2.false: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[RET:%.*]] = phi i8 [ 0, [[IF_FALSE]] ], [ -1, [[IF2_TRUE]] ], [ -1, [[IF2_FALSE]] ] +; CHECK-NEXT: [[RET:%.*]] = sext i2 [[COND]] to i8 ; CHECK-NEXT: ret i8 [[RET]] ; entry: - br i1 %cond, label %if.true, label %if.false + switch i2 %cond, label %default [ + i2 3, label %if.true + i2 0, label %if.false + ] if.true: - br i1 %cond2, label %if2.true, label %if2.false + switch i2 %cond2, label %default [ + i2 3, label %if2.true + i2 0, label %if2.false + ] + +default: + unreachable if.false: br label %merge @@ -995,7 +1031,7 @@ define i8 @test_phi_to_trunc(i32 %0) { ; CHECK: sw.255: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[DOT0:%.*]] = phi i8 [ -1, [[SW_255]] ], [ 1, [[SW_1]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[DOT0:%.*]] = trunc i32 [[TMP0]] to i8 ; CHECK-NEXT: ret i8 [[DOT0]] ; entry: @@ -1034,7 +1070,8 @@ define i8 @test_phi_to_trunc_inverted(i32 %0) { ; CHECK: sw.255: ; CHECK-NEXT: br label [[MERGE]] ; CHECK: merge: -; CHECK-NEXT: [[DOT0:%.*]] = phi i8 [ 0, [[SW_255]] ], [ -2, [[SW_1]] ], [ -1, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[TMP0]] to i8 +; CHECK-NEXT: [[DOT0:%.*]] = xor i8 [[TMP1]], -1 ; CHECK-NEXT: ret i8 [[DOT0]] ; entry: