diff --git a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h index 3df3495f84470..df7e5f209ab5d 100644 --- a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h +++ b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h @@ -102,6 +102,8 @@ class PredicateBase { /// Fetch condition in the form of PredicateConstraint, if possible. LLVM_ABI std::optional getConstraint() const; + /// Fetch condition in the form of a ConstantRange, if possible. + LLVM_ABI std::optional getRangeConstraint() const; protected: PredicateBase(PredicateType PT, Value *Op, Value *Condition) @@ -157,18 +159,22 @@ class PredicateBranch : public PredicateWithEdge { class PredicateSwitch : public PredicateWithEdge { public: - Value *CaseValue; - // This is the switch instruction. - SwitchInst *Switch; + using CaseValuesVec = SmallVector; + CaseValuesVec CaseValues; + bool IsDefault; PredicateSwitch(Value *Op, BasicBlock *SwitchBB, BasicBlock *TargetBB, - Value *CaseValue, SwitchInst *SI) + ArrayRef CaseValues, SwitchInst *SI, + bool IsDefault) : PredicateWithEdge(PT_Switch, Op, SwitchBB, TargetBB, SI->getCondition()), - CaseValue(CaseValue), Switch(SI) {} + CaseValues(CaseValues), IsDefault(IsDefault) {} + PredicateSwitch(Value *Op, BasicBlock *SwitchBB, BasicBlock *TargetBB, + CaseValuesVec &&CaseValues, SwitchInst *SI, bool IsDefault) + : PredicateWithEdge(PT_Switch, Op, SwitchBB, TargetBB, + SI->getCondition()), + CaseValues(CaseValues), IsDefault(IsDefault) {} PredicateSwitch() = delete; - static bool classof(const PredicateBase *PB) { - return PB->Type == PT_Switch; - } + static bool classof(const PredicateBase *PB) { return PB->Type == PT_Switch; } }; /// Encapsulates PredicateInfo, including all data associated with memory diff --git a/llvm/lib/Transforms/Utils/PredicateInfo.cpp b/llvm/lib/Transforms/Utils/PredicateInfo.cpp index a9ab3b3144829..da828fef583f1 100644 --- a/llvm/lib/Transforms/Utils/PredicateInfo.cpp +++ b/llvm/lib/Transforms/Utils/PredicateInfo.cpp @@ -14,8 +14,10 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" @@ -442,6 +444,7 @@ void PredicateInfoBuilder::processBranch( } } } + // Process a block terminating switch, and place relevant operations to be // renamed into OpsToRename. void PredicateInfoBuilder::processSwitch( @@ -450,21 +453,41 @@ void PredicateInfoBuilder::processSwitch( Value *Op = SI->getCondition(); if ((!isa(Op) && !isa(Op)) || Op->hasOneUse()) return; + using CaseValuesVec = PredicateSwitch::CaseValuesVec; + + BasicBlock *DefaultDest = SI->getDefaultDest(); + // Remember all cases for PT_Switch related to the default dest. + CaseValuesVec AllCases; + AllCases.reserve(SI->getNumCases()); - // Remember how many outgoing edges there are to every successor. - SmallDenseMap SwitchEdges; - for (BasicBlock *TargetBlock : successors(BranchBB)) - ++SwitchEdges[TargetBlock]; + // For each successor, remember all its related case values. + SmallDenseMap SwitchEdges; - // Now propagate info for each case value for (auto C : SI->cases()) { BasicBlock *TargetBlock = C.getCaseSuccessor(); - if (SwitchEdges.lookup(TargetBlock) == 1) { - PredicateSwitch *PS = new (Allocator) PredicateSwitch( - Op, SI->getParent(), TargetBlock, C.getCaseValue(), SI); - addInfoFor(OpsToRename, Op, PS); - } + /// TODO: Replace this if with an assertion if we can guarantee that + /// this function must be called after SimplifyCFG, as a canonical switch + /// should not have case dest being the default dest. + if (TargetBlock == DefaultDest) + continue; + // Only collect real case values + ConstantInt *CaseValue = C.getCaseValue(); + AllCases.push_back(CaseValue); + SwitchEdges[TargetBlock].push_back(CaseValue); } + + // Now propagate info for each case successor + for (auto *CaseSucc : SwitchEdges.keys()) { + auto &CaseValues = SwitchEdges.at(CaseSucc); + PredicateSwitch *PS = new (Allocator) PredicateSwitch( + Op, SI->getParent(), CaseSucc, std::move(CaseValues), SI, false); + addInfoFor(OpsToRename, Op, PS); + } + + // Finally, propagate info for the default case + PredicateSwitch *PS = new (Allocator) PredicateSwitch( + Op, SI->getParent(), DefaultDest, std::move(AllCases), SI, true); + addInfoFor(OpsToRename, Op, PS); } // Build predicate info for our function @@ -500,8 +523,8 @@ void PredicateInfoBuilder::buildPredicateInfo() { // Given the renaming stack, make all the operands currently on the stack real // by inserting them into the IR. Return the last operation's value. Value *PredicateInfoBuilder::materializeStack(unsigned int &Counter, - ValueDFSStack &RenameStack, - Value *OrigOp) { + ValueDFSStack &RenameStack, + Value *OrigOp) { // Find the first thing we have to materialize auto RevIter = RenameStack.rbegin(); for (; RevIter != RenameStack.rend(); ++RevIter) @@ -601,7 +624,8 @@ void PredicateInfoBuilder::renameUses(SmallVectorImpl &OpsToRename) { // block, and handle it specially. We know that it goes last, and only // dominate phi uses. auto BlockEdge = getBlockEdge(PossibleCopy); - if (!BlockEdge.second->getSinglePredecessor()) { + // We use unique predecessor to identify the mult-cases dest in switch + if (!BlockEdge.second->getUniquePredecessor()) { VD.LocalNum = LN_Last; auto *DomNode = DT.getNode(BlockEdge.first); if (DomNode) { @@ -759,8 +783,63 @@ std::optional PredicateBase::getConstraint() const { // TODO: Make this an assertion once RenamedOp is fully accurate. return std::nullopt; } + const auto &PS = *cast(this); + unsigned NumCases = PS.CaseValues.size(); + assert(NumCases != 0 && "PT_Switch with no cases is invalid"); + // PT_Switch with >1 cases is too complex to derive a PredicateConstraint. + if (NumCases > 1) + return std::nullopt; + // If we have a single case, we can derive a predicate constraint. + return { + {PS.IsDefault ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, PS.CaseValues[0]}}; + } + llvm_unreachable("Unknown predicate type"); +} - return {{CmpInst::ICMP_EQ, cast(this)->CaseValue}}; +std::optional PredicateBase::getRangeConstraint() const { + switch (Type) { + case PT_Assume: + case PT_Branch: { + // For PT_Assume/PT_Branch, we derive the condition constant range from + // its predicate constraint. + const std::optional &Constraint = getConstraint(); + if (!Constraint) + return std::nullopt; + CmpInst::Predicate Pred = Constraint->Predicate; + Value *OtherOp = Constraint->OtherOp; + const APInt *IntOp; + // If the other operand is not a constant integer, we can't derive a + // constant range. + if (!match(OtherOp, m_APInt(IntOp))) + return std::nullopt; + return {ConstantRange::makeExactICmpRegion(Pred, *IntOp)}; + } + case PT_Switch: + // For PT_Switch, we directly derive the constant range from its case + // values. + if (Condition != RenamedOp) { + // TODO: Make this an assertion once RenamedOp is fully accurate. + return std::nullopt; + } + + const auto &PS = *cast(this); + assert(!PS.CaseValues.empty() && "SwitchInfo with no cases is invalid"); + + unsigned BitWidth = PS.Condition->getType()->getScalarSizeInBits(); + + // For case values, CR = emptyset ∪ {case1, case2,..., caseN} + // For default, CR = fullset ∩ ~{case1} ∩ ~{case2} ∩ ... ∩ ~{caseN} + bool IsDefault = PS.IsDefault; + ConstantRange CR = IsDefault ? ConstantRange::getFull(BitWidth) + : ConstantRange::getEmpty(BitWidth); + for (ConstantInt *Case : PS.CaseValues) { + assert(Case && "CaseValue in switch should not be null"); + CR = IsDefault + ? CR.intersectWith(ConstantRange(Case->getValue()).inverse()) + : CR.unionWith(Case->getValue()); + } + + return {CR}; } llvm_unreachable("Unknown predicate type"); } @@ -818,8 +897,20 @@ class PredicateInfoAnnotatedWriter : public AssemblyAnnotationWriter { PB->To->printAsOperand(OS); OS << "]"; } else if (const auto *PS = dyn_cast(PI)) { - OS << "; switch predicate info { CaseValue: " << *PS->CaseValue - << " Edge: ["; + OS << "; switch predicate info { "; + if (PS->IsDefault) { + OS << "Case: default"; + } else if (PS->CaseValues.size() == 1) { + OS << "CaseValue: " << *PS->CaseValues[0]; + } else { + auto CaseValues = + llvm::map_range(PS->CaseValues, [](ConstantInt *Case) { + return std::to_string(Case->getSExtValue()); + }); + OS << "CaseValues: " << *PS->Condition->getType() << " [ " + << join(CaseValues, ", ") << " ]"; + } + OS << " Edge: ["; PS->From->printAsOperand(OS); OS << ","; PS->To->printAsOperand(OS); diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 4947d03a2dc66..3cde239c7af65 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -2021,33 +2021,13 @@ void SCCPInstVisitor::handleCallArguments(CallBase &CB) { void SCCPInstVisitor::handlePredicate(Instruction *I, Value *CopyOf, const PredicateBase *PI) { + const std::optional &RangeConstraint = + PI->getRangeConstraint(); ValueLatticeElement CopyOfVal = getValueState(CopyOf); - const std::optional &Constraint = PI->getConstraint(); - if (!Constraint) { - mergeInValue(ValueState[I], I, CopyOfVal); - return; - } - - CmpInst::Predicate Pred = Constraint->Predicate; - Value *OtherOp = Constraint->OtherOp; - - // Wait until OtherOp is resolved. - if (getValueState(OtherOp).isUnknown()) { - addAdditionalUser(OtherOp, I); - return; - } - - ValueLatticeElement CondVal = getValueState(OtherOp); - ValueLatticeElement &IV = ValueState[I]; - if (CondVal.isConstantRange() || CopyOfVal.isConstantRange()) { - auto ImposedCR = - ConstantRange::getFull(DL.getTypeSizeInBits(CopyOf->getType())); - - // Get the range imposed by the condition. - if (CondVal.isConstantRange()) - ImposedCR = ConstantRange::makeAllowedICmpRegion( - Pred, CondVal.getConstantRange()); + auto MergeInValueWithImposedCR = [this, I, CopyOfVal, + CopyOf](ValueLatticeElement &IV, + ConstantRange ImposedCR) { // Combine range info for the original value with the new range from the // condition. auto CopyOfCR = CopyOfVal.asConstantRange(CopyOf->getType(), @@ -2067,18 +2047,63 @@ void SCCPInstVisitor::handlePredicate(Instruction *I, Value *CopyOf, // unless we have conditions that are always true/false (e.g. icmp ule // i32, %a, i32_max). For the latter overdefined/empty range will be // inferred, but the branch will get folded accordingly anyways. - addAdditionalUser(OtherOp, I); mergeInValue( IV, I, ValueLatticeElement::getRange(NewCR, /*MayIncludeUndef*/ false)); + }; + + if (RangeConstraint) { + // If we can derive a constant range directly from the predicate info, + // simply merge it into the lattice value. + // In such case, the relevant operands must be constants, and thus we do not + // need addAdditionalUser for such operands. + MergeInValueWithImposedCR(ValueState[I], *RangeConstraint); + return; + } + + // If we can't simply get the constant range directly from the predicate info, + // then fallback to PredicateConstraint and let SCCPSolver resolve the + // possible Imposed CR. + + const std::optional &Constraint = PI->getConstraint(); + if (!Constraint) { + mergeInValue(ValueState[I], I, CopyOfVal); + return; + } + + CmpInst::Predicate Pred = Constraint->Predicate; + Value *OtherOp = Constraint->OtherOp; + + // Wait until OtherOp is resolved. + if (getValueState(OtherOp).isUnknown()) { + addAdditionalUser(OtherOp, I); return; - } else if (Pred == CmpInst::ICMP_EQ && - (CondVal.isConstant() || CondVal.isNotConstant())) { + } + + ValueLatticeElement CondVal = getValueState(OtherOp); + ValueLatticeElement &IV = ValueState[I]; + if (CondVal.isConstantRange() || CopyOfVal.isConstantRange()) { + // Get the range imposed by the condition. + auto ImposedCR = + CondVal.isConstantRange() + ? ConstantRange::makeAllowedICmpRegion(Pred, + CondVal.getConstantRange()) + : ConstantRange::getFull(DL.getTypeSizeInBits(CopyOf->getType())); + + addAdditionalUser(OtherOp, I); + MergeInValueWithImposedCR(IV, ImposedCR); + return; + } + + if (Pred == CmpInst::ICMP_EQ && + (CondVal.isConstant() || CondVal.isNotConstant())) { // For non-integer values or integer constant expressions, only // propagate equal constants or not-constants. addAdditionalUser(OtherOp, I); mergeInValue(IV, I, CondVal); return; - } else if (Pred == CmpInst::ICMP_NE && CondVal.isConstant()) { + } + + if (Pred == CmpInst::ICMP_NE && CondVal.isConstant()) { // Propagate inequalities. addAdditionalUser(OtherOp, I); mergeInValue(IV, I, ValueLatticeElement::getNot(CondVal.getConstant())); diff --git a/llvm/test/Transforms/NewGVN/edge.ll b/llvm/test/Transforms/NewGVN/edge.ll index 143e52cd139c5..4235f7fbfef14 100644 --- a/llvm/test/Transforms/NewGVN/edge.ll +++ b/llvm/test/Transforms/NewGVN/edge.ll @@ -262,3 +262,98 @@ return: ret double %retval } + +define i32 @switch_default_dest(i32 %x) { +; CHECK-LABEL: define i32 @switch_default_dest( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: case0: +; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[PHI:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[PHI]] +; CHECK: default: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[CASE1]] ], [ 1, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ] +; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]] +; CHECK-NEXT: ret i32 [[FOO]] +; +case0: + switch i32 %x, label %default [ + i32 0, label %phi + i32 1, label %case1 + ] + +case1: + br label %phi + +default: + br label %phi + +phi: + %res = phi i32 [ 0, %case1 ], [ 1, %case0 ], [ %x, %default ] + %foo = add i32 %res, %x + ret i32 %foo +} + +define i32 @switch_multicases_dest(i32 %x) { +; CHECK-LABEL: define i32 @switch_multicases_dest( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[X]], label [[PHI:%.*]] [ +; CHECK-NEXT: i32 0, label [[CASE:%.*]] +; CHECK-NEXT: i32 1, label [[CASE]] +; CHECK-NEXT: ] +; CHECK: case: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]] +; CHECK-NEXT: ret i32 [[FOO]] +; +entry: + switch i32 %x, label %phi [ + i32 0, label %case + i32 1, label %case + ] + +case: + br label %phi + +phi: + %res = phi i32 [ %x, %case ], [ 0, %entry ] + %foo = add i32 %res, %x + ret i32 %foo +} + +define i32 @switch_multicases_dest2(i32 %x) { +; CHECK-LABEL: define i32 @switch_multicases_dest2( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[PHI:%.*]] +; CHECK-NEXT: i32 1, label [[PHI]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ] +; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]] +; CHECK-NEXT: ret i32 [[FOO]] +; +entry: + switch i32 %x, label %default [ + i32 0, label %phi + i32 1, label %phi + ] + +default: + br label %phi + +phi: + %res = phi i32 [ %x, %entry ], [ %x, %entry ], [ 0, %default ] + %foo = add i32 %res, %x + ret i32 %foo +} diff --git a/llvm/test/Transforms/SCCP/switch.ll b/llvm/test/Transforms/SCCP/switch.ll index fb81213ed12dc..dfbacdb84e54c 100644 --- a/llvm/test/Transforms/SCCP/switch.ll +++ b/llvm/test/Transforms/SCCP/switch.ll @@ -37,8 +37,8 @@ entry: switch: switch i32 -1, label %switch.default [ - i32 0, label %end - i32 1, label %end + i32 0, label %end + i32 1, label %end ] switch.default: @@ -65,8 +65,8 @@ entry: switch: switch i32 0, label %switch.default [ - i32 0, label %end - i32 1, label %end + i32 0, label %end + i32 1, label %end ] switch.default: @@ -102,11 +102,11 @@ entry: switch: %x = load i32, ptr %p, !range !{i32 0, i32 3} switch i32 %x, label %switch.default [ - i32 0, label %switch.default - i32 1, label %switch.0 - i32 2, label %switch.0 - i32 3, label %switch.1 - i32 4, label %switch.1 + i32 0, label %switch.default + i32 1, label %switch.0 + i32 2, label %switch.0 + i32 3, label %switch.1 + i32 4, label %switch.1 ] switch.default: @@ -140,10 +140,10 @@ define i32 @test_local_range(ptr %p) { ; %x = load i32, ptr %p, !range !{i32 0, i32 3} switch i32 %x, label %switch.default [ - i32 0, label %switch.0 - i32 1, label %switch.1 - i32 2, label %switch.2 - i32 3, label %switch.3 + i32 0, label %switch.0 + i32 1, label %switch.1 + i32 2, label %switch.2 + i32 3, label %switch.3 ] switch.default: @@ -182,12 +182,12 @@ define i32 @test_duplicate_successors(ptr %p) { ; %x = load i32, ptr %p, !range !{i32 0, i32 3} switch i32 %x, label %switch.default [ - i32 0, label %switch.0 - i32 1, label %switch.0 - i32 2, label %switch.1 - i32 3, label %switch.1 - i32 4, label %switch.2 - i32 5, label %switch.2 + i32 0, label %switch.0 + i32 1, label %switch.0 + i32 2, label %switch.1 + i32 3, label %switch.1 + i32 4, label %switch.2 + i32 5, label %switch.2 ] switch.default: @@ -223,10 +223,10 @@ define internal i32 @test_ip_range(i32 %x) { ; CHECK-NEXT: ret i32 3 ; switch i32 %x, label %switch.default [ - i32 0, label %switch.0 - i32 1, label %switch.1 - i32 2, label %switch.2 - i32 3, label %switch.3 + i32 0, label %switch.0 + i32 1, label %switch.1 + i32 2, label %switch.2 + i32 3, label %switch.3 ], !prof !{!"branch_weights", i32 1, i32 2, i32 3, i32 4, i32 5} switch.default: @@ -295,8 +295,8 @@ else.2: switch: %p = phi i32 [ 0, %then.1 ], [ 2, %else.1 ], [ undef, %else.2 ] switch i32 %p, label %switch.default [ - i32 0, label %end.1 - i32 3, label %end.2 + i32 0, label %end.1 + i32 3, label %end.2 ] switch.default: @@ -346,10 +346,10 @@ entry: if.then: switch i32 %x, label %sw.epilog [ - i32 0, label %sw.bb - i32 1, label %sw.bb2 - i32 2, label %sw.bb4 - i32 3, label %sw.bb6 + i32 0, label %sw.bb + i32 1, label %sw.bb2 + i32 2, label %sw.bb4 + i32 3, label %sw.bb6 ] sw.bb: @@ -377,6 +377,98 @@ return: ret i32 %retval.0 } +define i1 @switch_default_dest(i32 %x) { +; CHECK-LABEL: define i1 @switch_default_dest( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: case0: +; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[PHI:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[PHI]] +; CHECK: default: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 2, [[CASE1]] ], [ 3, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ] +; CHECK-NEXT: ret i1 false +; +case0: + switch i32 %x, label %default [ + i32 0, label %phi + i32 1, label %case1 + ] + +case1: + br label %phi + +default: + br label %phi + +phi: + %res = phi i32 [ 2, %case1 ], [ 3, %case0 ], [ %x, %default ] + %ret = icmp ult i32 %res, 2 + ret i1 %ret +} + +define i1 @switch_multicases_dest(i32 %x) { +; CHECK-LABEL: define i1 @switch_multicases_dest( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[X]], label [[PHI:%.*]] [ +; CHECK-NEXT: i32 0, label [[CASE:%.*]] +; CHECK-NEXT: i32 1, label [[CASE]] +; CHECK-NEXT: ] +; CHECK: case: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: ret i1 true +; +entry: + switch i32 %x, label %phi [ + i32 0, label %case + i32 1, label %case + ] + +case: + br label %phi + +phi: + %res = phi i32 [ %x, %case ], [ 0, %entry ] + %ret = icmp ult i32 %res, 2 + ret i1 %ret +} + +define i1 @switch_multicases_dest2(i32 %x) { +; CHECK-LABEL: define i1 @switch_multicases_dest2( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[PHI:%.*]] +; CHECK-NEXT: i32 1, label [[PHI]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ] +; CHECK-NEXT: ret i1 true +; +entry: + switch i32 %x, label %default [ + i32 0, label %phi + i32 1, label %phi + ] + +default: + br label %phi + +phi: + %res = phi i32 [ %x, %entry ], [ %x, %entry ], [ 0, %default ] + %ret = icmp ult i32 %res, 2 + ret i1 %ret +} + declare void @llvm.assume(i1) ;. diff --git a/llvm/test/Transforms/Util/PredicateInfo/condprop.ll b/llvm/test/Transforms/Util/PredicateInfo/condprop.ll index 256d0d908ec1e..2e51bdf6cfc35 100644 --- a/llvm/test/Transforms/Util/PredicateInfo/condprop.ll +++ b/llvm/test/Transforms/Util/PredicateInfo/condprop.ll @@ -134,6 +134,8 @@ define void @test4(i1 %b, i32 %x) { ; CHECK-NEXT: br i1 [[B:%.*]], label [[SW:%.*]], label [[CASE3:%.*]] ; CHECK: sw: ; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32 +; CHECK: [[X_1:%.*]] = bitcast i32 [[X]] to i32 +; CHECK: [[X_2:%.*]] = bitcast i32 [[X]] to i32 ; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ ; CHECK-NEXT: i32 0, label [[CASE0:%.*]] ; CHECK-NEXT: i32 1, label [[CASE1:%.*]] @@ -142,13 +144,13 @@ define void @test4(i1 %b, i32 %x) { ; CHECK-NEXT: i32 4, label [[DEFAULT]] ; CHECK-NEXT: ] ; CHECK: default: -; CHECK-NEXT: call void @bar(i32 [[X]]) +; CHECK-NEXT: call void @bar(i32 [[X_0]]) ; CHECK-NEXT: ret void ; CHECK: case0: -; CHECK-NEXT: call void @bar(i32 [[X]]) +; CHECK-NEXT: call void @bar(i32 [[X_1]]) ; CHECK-NEXT: ret void ; CHECK: case1: -; CHECK-NEXT: call void @bar(i32 [[X_0]]) +; CHECK-NEXT: call void @bar(i32 [[X_2]]) ; CHECK-NEXT: ret void ; CHECK: case3: ; CHECK-NEXT: call void @bar(i32 [[X]]) @@ -157,11 +159,11 @@ define void @test4(i1 %b, i32 %x) { br i1 %b, label %sw, label %case3 sw: switch i32 %x, label %default [ - i32 0, label %case0 - i32 1, label %case1 - i32 2, label %case0 - i32 3, label %case3 - i32 4, label %default + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case0 + i32 3, label %case3 + i32 4, label %default ] default: call void @bar(i32 %x) diff --git a/llvm/test/Transforms/Util/PredicateInfo/edge.ll b/llvm/test/Transforms/Util/PredicateInfo/edge.ll index ef757f323921a..4f7e75f68ac15 100644 --- a/llvm/test/Transforms/Util/PredicateInfo/edge.ll +++ b/llvm/test/Transforms/Util/PredicateInfo/edge.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -passes=print-predicateinfo < %s 2>&1 | FileCheck %s +; RUN: opt -passes=print-predicateinfo -disable-output < %s 2>&1 | FileCheck %s define i32 @f1(i32 %x) { ; CHECK-LABEL: @f1( @@ -54,7 +54,7 @@ define i32 @f3(i32 %x) { ; CHECK-NEXT: bb0: ; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32 ; CHECK-NEXT: switch i32 [[X]], label [[BB1:%.*]] [ -; CHECK-NEXT: i32 0, label [[BB2:%.*]] +; CHECK-NEXT: i32 0, label [[BB2:%.*]] ; CHECK-NEXT: ] ; CHECK: bb1: ; CHECK-NEXT: br label [[BB2]] @@ -240,3 +240,99 @@ return: ret double %retval } + + +define i32 @switch_default_dest(i32 %x) { +; CHECK-LABEL: @switch_default_dest( +; CHECK-NEXT: case0: +; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32 +; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[PHI:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[PHI]] +; CHECK: default: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[CASE1]] ], [ 1, [[CASE0:%.*]] ], [ [[X_0]], [[DEFAULT]] ] +; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]] +; CHECK-NEXT: ret i32 [[FOO]] +; +case0: + switch i32 %x, label %default [ + i32 0, label %phi + i32 1, label %case1 + ] + +case1: + br label %phi + +default: + br label %phi + +phi: + %res = phi i32 [ 0, %case1 ], [ 1, %case0 ], [ %x, %default ] + %foo = add i32 %res, %x + ret i32 %foo +} + +define i32 @switch_multicases_dest(i32 %x) { +; CHECK-LABEL: @switch_multicases_dest( +; CHECK-NEXT: entry: +; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32 +; CHECK-NEXT: switch i32 [[X]], label [[PHI:%.*]] [ +; CHECK-NEXT: i32 0, label [[CASE:%.*]] +; CHECK-NEXT: i32 1, label [[CASE]] +; CHECK-NEXT: ] +; CHECK: case: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X_0]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]] +; CHECK-NEXT: ret i32 [[FOO]] +; +entry: + switch i32 %x, label %phi [ + i32 0, label %case + i32 1, label %case + ] + +case: + br label %phi + +phi: + %res = phi i32 [ %x, %case ], [ 0, %entry ] + %foo = add i32 %res, %x + ret i32 %foo +} + +define i32 @switch_multicases_dest2(i32 %x) { +; CHECK-LABEL: @switch_multicases_dest2( +; CHECK-NEXT: entry: +; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32 +; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[PHI:%.*]] +; CHECK-NEXT: i32 1, label [[PHI]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: br label [[PHI]] +; CHECK: phi: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X_0]], [[ENTRY:%.*]] ], [ [[X_0]], [[ENTRY]] ], [ 0, [[DEFAULT]] ] +; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]] +; CHECK-NEXT: ret i32 [[FOO]] +; +entry: + switch i32 %x, label %default [ + i32 0, label %phi + i32 1, label %phi + ] + +default: + br label %phi + +phi: + %res = phi i32 [ %x, %entry ], [ %x, %entry ], [ 0, %default ] + %foo = add i32 %res, %x + ret i32 %foo +}