Index: include/llvm/Transforms/Utils/Local.h =================================================================== --- include/llvm/Transforms/Utils/Local.h +++ include/llvm/Transforms/Utils/Local.h @@ -120,9 +120,14 @@ /// Also calls RecursivelyDeleteTriviallyDeadInstructions() on any branch/switch /// conditions and indirectbr addresses this might make dead if /// DeleteDeadConditions is true. +/// If \p OnlyFoldToUnconditional is true, then no partial simplifications are +/// allowed. Either the terminator is folded to become unconditional, or no +/// folding at all will be made. bool ConstantFoldTerminator(BasicBlock *BB, bool DeleteDeadConditions = false, const TargetLibraryInfo *TLI = nullptr, - DomTreeUpdater *DTU = nullptr); + DomTreeUpdater *DTU = nullptr, + bool OnlyFoldToUnconditional = false); + //===----------------------------------------------------------------------===// // Local dead code elimination. Index: lib/Transforms/Scalar/SCCP.cpp =================================================================== --- lib/Transforms/Scalar/SCCP.cpp +++ lib/Transforms/Scalar/SCCP.cpp @@ -2017,7 +2017,9 @@ // Ignore blockaddress users; BasicBlock's dtor will handle them. if (!I) continue; - bool Folded = ConstantFoldTerminator(I->getParent()); + bool Folded = ConstantFoldTerminator(I->getParent(), + false, nullptr, nullptr, + true /* OnlyFoldToUnconditional */); if (!Folded) { // If the branch can't be folded, we must have forced an edge // for an indeterminate value. Force the terminator to fold @@ -2041,7 +2043,9 @@ (void)Dest; I->setOperand(0, C); - Folded = ConstantFoldTerminator(I->getParent()); + Folded = ConstantFoldTerminator(I->getParent(), + false, nullptr, nullptr, + true /* OnlyFoldToUnconditional */); } assert(Folded && "Expect TermInst on constantint or blockaddress to be folded"); Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -95,16 +95,10 @@ // Local constant propagation. // -/// ConstantFoldTerminator - If a terminator instruction is predicated on a -/// constant value, convert it into an unconditional branch to the constant -/// destination. This is a nontrivial operation because the successors of this -/// basic block must have their PHI nodes updated. -/// Also calls RecursivelyDeleteTriviallyDeadInstructions() on any branch/switch -/// conditions and indirectbr addresses this might make dead if -/// DeleteDeadConditions is true. bool llvm::ConstantFoldTerminator(BasicBlock *BB, bool DeleteDeadConditions, const TargetLibraryInfo *TLI, - DomTreeUpdater *DTU) { + DomTreeUpdater *DTU, + bool OnlyFoldToUnconditional) { TerminatorInst *T = BB->getTerminator(); IRBuilder<> Builder(T); @@ -175,7 +169,7 @@ // Check to see if this branch is going to the same place as the default // dest. If so, eliminate it as an explicit compare. - if (i->getCaseSuccessor() == DefaultDest) { + if (i->getCaseSuccessor() == DefaultDest && !OnlyFoldToUnconditional) { MDNode *MD = SI->getMetadata(LLVMContext::MD_prof); unsigned NCases = SI->getNumCases(); // Fold the case metadata into the default if there will be any branches @@ -256,7 +250,7 @@ return true; } - if (SI->getNumCases() == 1) { + if (SI->getNumCases() == 1 && !OnlyFoldToUnconditional) { // Otherwise, we can fold this switch into a conditional branch // instruction if it has only one non-default destination. auto FirstCase = *SI->case_begin(); Index: test/Transforms/SCCP/switch-undef-constantfoldterminator.ll =================================================================== --- /dev/null +++ test/Transforms/SCCP/switch-undef-constantfoldterminator.ll @@ -0,0 +1,47 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -ipsccp -S | FileCheck %s + +; This test case used to end up like this: +; +; While deleting: label %lor.rhs +; Use still stuck around after Def is destroyed: br i1 undef, label %lor.rhs, label %land.end +; opt: ../lib/IR/Value.cpp:92: llvm::Value::~Value(): Assertion `use_empty() && "Uses remain when a value is destroyed!"' failed. +; +; due to ConstantFoldTerminator rewriting the switch into +; +; br i1 undef, label %lor.rhs, label %land.end +; +; while SCCP implementation relied on the terminator to always be folded into +; an unconditional branch when ConstantFoldTerminator returned true. + +define void @f4() { +; CHECK-LABEL: define void @f4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALL:%.*]] = call i16 @f3(i16 undef) +; CHECK-NEXT: ret void +; +entry: + %call = call i16 @f3(i16 undef) + ret void +} + +define internal i16 @f3(i16 %p1) { +; CHECK-LABEL: define internal i16 @f3( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LAND_END:%.*]] +; CHECK: land.end: +; CHECK-NEXT: ret i16 undef +; +entry: + switch i16 %p1, label %land.end [ + i16 0, label %land.end + i16 1, label %lor.rhs + ] + +lor.rhs: + br label %land.end + +land.end: + ret i16 0 +} +