diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -143,7 +143,7 @@ /// recursively. Return true if any instructions were deleted. bool RecursivelyDeleteTriviallyDeadInstructions( Value *V, const TargetLibraryInfo *TLI = nullptr, - MemorySSAUpdater *MSSAU = nullptr); + MemorySSAUpdater *MSSAU = nullptr, AssumptionCache *AC = nullptr); /// Delete all of the instructions in `DeadInsts`, and all other instructions /// that deleting these in turn causes to be trivially dead. @@ -155,7 +155,8 @@ /// empty afterward. void RecursivelyDeleteTriviallyDeadInstructions( SmallVectorImpl &DeadInsts, - const TargetLibraryInfo *TLI = nullptr, MemorySSAUpdater *MSSAU = nullptr); + const TargetLibraryInfo *TLI = nullptr, MemorySSAUpdater *MSSAU = nullptr, + AssumptionCache *AC = nullptr); /// Same functionality as RecursivelyDeleteTriviallyDeadInstructions, but allow /// instructions that are not trivially dead. These will be ignored. @@ -163,7 +164,8 @@ /// were found and deleted. bool RecursivelyDeleteTriviallyDeadInstructionsPermissive( SmallVectorImpl &DeadInsts, - const TargetLibraryInfo *TLI = nullptr, MemorySSAUpdater *MSSAU = nullptr); + const TargetLibraryInfo *TLI = nullptr, MemorySSAUpdater *MSSAU = nullptr, + AssumptionCache *AC = nullptr); /// If the specified value is an effectively dead PHI node, due to being a /// def-use chain of single-use nodes that either forms a cycle or is terminated @@ -242,7 +244,8 @@ /// branches to us and one of our successors, fold the setcc into the /// predecessor and use logical operations to pick the right destination. bool FoldBranchToCommonDest(BranchInst *BI, MemorySSAUpdater *MSSAU = nullptr, - unsigned BonusInstThreshold = 1); + unsigned BonusInstThreshold = 1, + AssumptionCache *AC = nullptr); /// This function takes a virtual register computed by an Instruction and /// replaces it with a slot in the stack frame, allocated via alloca. diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -1184,7 +1184,14 @@ } } - RecursivelyDeleteTriviallyDeadInstructions(DeadInsts); + AssumptionCache *AC = nullptr; + for (auto &Inst : DeadInsts) + if (Inst.pointsToAliveValue()) { + AC = InfoCache.AG.getAnalysis( + *cast(Inst)->getFunction()); + break; + } + RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, nullptr, nullptr, AC); if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) { SmallVector ToBeDeletedBBs; diff --git a/llvm/lib/Transforms/Scalar/InstSimplifyPass.cpp b/llvm/lib/Transforms/Scalar/InstSimplifyPass.cpp --- a/llvm/lib/Transforms/Scalar/InstSimplifyPass.cpp +++ b/llvm/lib/Transforms/Scalar/InstSimplifyPass.cpp @@ -66,7 +66,8 @@ } } } - RecursivelyDeleteTriviallyDeadInstructions(DeadInstsInBB, SQ.TLI); + RecursivelyDeleteTriviallyDeadInstructions(DeadInstsInBB, SQ.TLI, nullptr, + SQ.AC); } // Place the list of instructions to simplify on the next loop iteration diff --git a/llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp b/llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp --- a/llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp @@ -152,7 +152,7 @@ // iteration over all instructions in all the loop blocks. if (!DeadInsts.empty()) { Changed = true; - RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, &TLI, MSSAU); + RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, &TLI, MSSAU, &AC); } if (MSSAU && VerifyMemorySSA) diff --git a/llvm/lib/Transforms/Scalar/NaryReassociate.cpp b/llvm/lib/Transforms/Scalar/NaryReassociate.cpp --- a/llvm/lib/Transforms/Scalar/NaryReassociate.cpp +++ b/llvm/lib/Transforms/Scalar/NaryReassociate.cpp @@ -243,7 +243,7 @@ WeakVH NewIExist = NewI; // If SeenExprs/NewIExist contains I's WeakTrackingVH/WeakVH, that // entry will be replaced with nullptr if deleted. - RecursivelyDeleteTriviallyDeadInstructions(&*I, TLI); + RecursivelyDeleteTriviallyDeadInstructions(&*I, TLI, nullptr, AC); if (!NewIExist) { // Rare occation where the new instruction (NewI) have been removed, // probably due to parts of the input code was dead from the diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -77,6 +77,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ValueMapper.h" +#include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include #include #include @@ -440,21 +441,22 @@ /// trivially dead, delete them too, recursively. Return true if any /// instructions were deleted. bool llvm::RecursivelyDeleteTriviallyDeadInstructions( - Value *V, const TargetLibraryInfo *TLI, MemorySSAUpdater *MSSAU) { + Value *V, const TargetLibraryInfo *TLI, MemorySSAUpdater *MSSAU, + AssumptionCache *AC) { Instruction *I = dyn_cast(V); if (!I || !isInstructionTriviallyDead(I, TLI)) return false; SmallVector DeadInsts; DeadInsts.push_back(I); - RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, TLI, MSSAU); + RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, TLI, MSSAU, AC); return true; } bool llvm::RecursivelyDeleteTriviallyDeadInstructionsPermissive( SmallVectorImpl &DeadInsts, const TargetLibraryInfo *TLI, - MemorySSAUpdater *MSSAU) { + MemorySSAUpdater *MSSAU, AssumptionCache *AC) { unsigned S = 0, E = DeadInsts.size(), Alive = 0; for (; S != E; ++S) { auto *I = cast(DeadInsts[S]); @@ -465,13 +467,13 @@ } if (Alive == E) return false; - RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, TLI, MSSAU); + RecursivelyDeleteTriviallyDeadInstructions(DeadInsts, TLI, MSSAU, AC); return true; } void llvm::RecursivelyDeleteTriviallyDeadInstructions( SmallVectorImpl &DeadInsts, const TargetLibraryInfo *TLI, - MemorySSAUpdater *MSSAU) { + MemorySSAUpdater *MSSAU, AssumptionCache *AC) { // Process the dead instruction list until empty. while (!DeadInsts.empty()) { Value *V = DeadInsts.pop_back_val(); @@ -484,6 +486,7 @@ // Don't lose the debug info while deleting the instructions. salvageDebugInfo(*I); + salvageKnowledge(I, AC); // Null out all of the instruction's operands to see if any operand becomes // dead as we go. @@ -570,6 +573,7 @@ const TargetLibraryInfo *TLI) { if (isInstructionTriviallyDead(I, TLI)) { salvageDebugInfo(*I); + salvageKnowledge(I); // Null out all of the instruction's operands to see if any operand becomes // dead as we go. diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp --- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp +++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp @@ -214,7 +214,7 @@ while (!DeadInsts.empty()) { Value *V = DeadInsts.pop_back_val(); if (Instruction *Inst = dyn_cast_or_null(V)) - RecursivelyDeleteTriviallyDeadInstructions(Inst); + RecursivelyDeleteTriviallyDeadInstructions(Inst, nullptr, nullptr, AC); } } diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -691,8 +691,8 @@ } // end anonymous namespace -static void EraseTerminatorAndDCECond(Instruction *TI, - MemorySSAUpdater *MSSAU = nullptr) { +static void EraseTerminatorAndDCECond(Instruction *TI, MemorySSAUpdater *MSSAU, + AssumptionCache *AC) { Instruction *Cond = nullptr; if (SwitchInst *SI = dyn_cast(TI)) { Cond = dyn_cast(SI->getCondition()); @@ -876,7 +876,7 @@ << "Through successor TI: " << *TI << "Leaving: " << *NI << "\n"); - EraseTerminatorAndDCECond(TI); + EraseTerminatorAndDCECond(TI, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -941,7 +941,7 @@ << "Through successor TI: " << *TI << "Leaving: " << *NI << "\n"); - EraseTerminatorAndDCECond(TI); + EraseTerminatorAndDCECond(TI, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -1202,7 +1202,7 @@ setBranchWeights(NewSI, MDWeights); } - EraseTerminatorAndDCECond(PTI); + EraseTerminatorAndDCECond(PTI, /*MSSAU*/ nullptr, Options.AC); // Okay, last check. If BB is still a successor of PSI, then we must // have an infinite loop case. If so, add an infinitely looping block @@ -1439,7 +1439,7 @@ for (BasicBlock *Succ : successors(BB1)) AddPredecessorToBlock(Succ, BIParent, BB1); - EraseTerminatorAndDCECond(BI); + EraseTerminatorAndDCECond(BI, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -2499,7 +2499,7 @@ TrueSucc->removePredecessor(BI->getParent()); FalseSucc->removePredecessor(BI->getParent()); Builder.CreateRetVoid(); - EraseTerminatorAndDCECond(BI); + EraseTerminatorAndDCECond(BI, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -2555,7 +2555,7 @@ << "\n " << *BI << "\nNewRet = " << *RI << "\nTRUEBLOCK: " << *TrueSucc << "\nFALSEBLOCK: " << *FalseSucc); - EraseTerminatorAndDCECond(BI); + EraseTerminatorAndDCECond(BI, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -2604,7 +2604,8 @@ /// and one of our successors, fold the block into the predecessor and use /// logical operations to pick the right destination. bool llvm::FoldBranchToCommonDest(BranchInst *BI, MemorySSAUpdater *MSSAU, - unsigned BonusInstThreshold) { + unsigned BonusInstThreshold, + AssumptionCache *AC) { BasicBlock *BB = BI->getParent(); const unsigned PredCount = pred_size(BB); @@ -2885,7 +2886,7 @@ // Change PBI from Conditional to Unconditional. BranchInst *New_PBI = BranchInst::Create(TrueDest, PBI); - EraseTerminatorAndDCECond(PBI, MSSAU); + EraseTerminatorAndDCECond(PBI, MSSAU, AC); PBI = New_PBI; } @@ -3069,8 +3070,8 @@ // If QTB does not exist, then QFB's only predecessor has a conditional // branch to QFB and PostBB. BasicBlock *TruePred = QTB ? QTB : QFB->getSinglePredecessor(); - BasicBlock *NewBB = SplitBlockPredecessors(PostBB, { QFB, TruePred}, - "condstore.split"); + BasicBlock *NewBB = + SplitBlockPredecessors(PostBB, {QFB, TruePred}, "condstore.split"); if (!NewBB) return false; PostBB = NewBB; @@ -3591,7 +3592,7 @@ Builder.CreateBr(FalseBB); } - EraseTerminatorAndDCECond(OldTerm); + EraseTerminatorAndDCECond(OldTerm, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -3882,7 +3883,7 @@ } // Erase the old branch instruction. - EraseTerminatorAndDCECond(BI); + EraseTerminatorAndDCECond(BI, /*MSSAU*/ nullptr, Options.AC); LLVM_DEBUG(dbgs() << " ** 'icmp' chain result is:\n" << *BB << '\n'); return true; @@ -4328,7 +4329,7 @@ Builder.CreateAssumption(Cond); Builder.CreateBr(BI->getSuccessor(0)); } - EraseTerminatorAndDCECond(BI); + EraseTerminatorAndDCECond(BI, /*MSSAU*/ nullptr, Options.AC); Changed = true; } } else if (auto *SI = dyn_cast(TI)) { @@ -4411,7 +4412,7 @@ return true; } -static void createUnreachableSwitchDefault(SwitchInst *Switch) { +void createUnreachableSwitchDefault(SwitchInst *Switch, AssumptionCache *AC) { LLVM_DEBUG(dbgs() << "SimplifyCFG: switch default is dead.\n"); BasicBlock *NewDefaultBlock = SplitBlockPredecessors(Switch->getDefaultDest(), Switch->getParent(), ""); @@ -4419,7 +4420,7 @@ SplitBlock(&*NewDefaultBlock, &NewDefaultBlock->front()); auto *NewTerminator = NewDefaultBlock->getTerminator(); new UnreachableInst(Switch->getContext(), NewTerminator); - EraseTerminatorAndDCECond(NewTerminator); + EraseTerminatorAndDCECond(NewTerminator, /*MSSAU*/ nullptr, AC); } /// Turn a switch with two reachable destinations into an integer range @@ -4534,7 +4535,7 @@ // Clean up the default block - it may have phis or other instructions before // the unreachable terminator. if (!HasDefault) - createUnreachableSwitchDefault(SI); + createUnreachableSwitchDefault(SI, Options.AC); // Drop the switch. SI->eraseFromParent(); @@ -4580,7 +4581,7 @@ if (HasDefault && DeadCases.empty() && NumUnknownBits < 64 /* avoid overflow */ && SI->getNumCases() == (1ULL << NumUnknownBits)) { - createUnreachableSwitchDefault(SI); + createUnreachableSwitchDefault(SI, AC); return true; } @@ -5792,14 +5793,14 @@ if (IBI->getNumDestinations() == 0) { // If the indirectbr has no successors, change it to unreachable. new UnreachableInst(IBI->getContext(), IBI); - EraseTerminatorAndDCECond(IBI); + EraseTerminatorAndDCECond(IBI, /*MSSAU*/ nullptr, Options.AC); return true; } if (IBI->getNumDestinations() == 1) { // If the indirectbr has one successor, change it to a direct branch. BranchInst::Create(IBI->getDestination(0), IBI); - EraseTerminatorAndDCECond(IBI); + EraseTerminatorAndDCECond(IBI, /*MSSAU*/ nullptr, Options.AC); return true; } @@ -5937,7 +5938,8 @@ // branches to us and our successor, fold the comparison into the // predecessor and use logical operations to update the incoming value // for PHI nodes in common successor. - if (FoldBranchToCommonDest(BI, nullptr, Options.BonusInstThreshold)) + if (FoldBranchToCommonDest(BI, nullptr, Options.BonusInstThreshold, + Options.AC)) return requestResimplify(); return false; } @@ -5994,14 +5996,16 @@ ConstantInt *TorF = *Imp ? ConstantInt::getTrue(BB->getContext()) : ConstantInt::getFalse(BB->getContext()); BI->setCondition(TorF); - RecursivelyDeleteTriviallyDeadInstructions(OldCond); + RecursivelyDeleteTriviallyDeadInstructions(OldCond, nullptr, nullptr, + Options.AC); return requestResimplify(); } // If this basic block is ONLY a compare and a branch, and if a predecessor // branches to us and one of our successors, fold the comparison into the // predecessor and use logical operations to pick the right destination. - if (FoldBranchToCommonDest(BI, nullptr, Options.BonusInstThreshold)) + if (FoldBranchToCommonDest(BI, nullptr, Options.BonusInstThreshold, + Options.AC)) return requestResimplify(); // We have a conditional branch to two blocks that are only reachable diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2-preservation.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2-preservation.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2-preservation.ll @@ -0,0 +1,36 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=attributor --enable-knowledge-retention -aa-pipeline='basic-aa' -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s + +%struct.ss = type { i32, i64 } + +define internal void @f(%struct.ss* byval %b, i32* byval %X) nounwind { +entry: + %tmp = getelementptr %struct.ss, %struct.ss* %b, i32 0, i32 0 + %tmp1 = load i32, i32* %tmp, align 4 + %tmp2 = add i32 %tmp1, 1 + store i32 %tmp2, i32* %tmp, align 4 + + store i32 0, i32* %X + ret void +} + +define i32 @test(i32* %X) { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_SS:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 +; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 +; CHECK-NEXT: store i64 2, i64* [[TMP4]], align 4 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[X:%.*]], i64 4), "align"(%struct.ss* [[S]], i64 8), "dereferenceable"(i32* [[X]], i64 4), "dereferenceable"(%struct.ss* [[S]], i64 12), "nonnull"(%struct.ss* [[S]]), "nonnull"(i32* [[X]]) ] +; CHECK-NEXT: ret i32 0 +; +entry: + %S = alloca %struct.ss + %tmp1 = getelementptr %struct.ss, %struct.ss* %S, i32 0, i32 0 + store i32 1, i32* %tmp1, align 8 + %tmp4 = getelementptr %struct.ss, %struct.ss* %S, i32 0, i32 1 + store i64 2, i64* %tmp4, align 4 + call void @f( %struct.ss* byval %S, i32* byval %X) + ret i32 0 +} diff --git a/llvm/test/Transforms/InstSimplify/load.ll b/llvm/test/Transforms/InstSimplify/load.ll --- a/llvm/test/Transforms/InstSimplify/load.ll +++ b/llvm/test/Transforms/InstSimplify/load.ll @@ -1,20 +1,29 @@ -; NOTE: Assertions have been autogenerated by update_test_checks.py -; RUN: opt < %s -instsimplify -S | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instsimplify -S | FileCheck %s --check-prefixes=CHECK,NO_ASSUME +; RUN: opt < %s -instsimplify --enable-knowledge-retention -S | FileCheck %s --check-prefixes=CHECK,USE_ASSUME @zeroinit = constant {} zeroinitializer @undef = constant {} undef define i32 @crash_on_zeroinit() { -; CHECK-LABEL: @crash_on_zeroinit( -; CHECK: ret i32 0 +; NO_ASSUME-LABEL: @crash_on_zeroinit( +; NO_ASSUME-NEXT: ret i32 0 +; +; USE_ASSUME-LABEL: @crash_on_zeroinit( +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* bitcast ({}* @zeroinit to i32*), i64 4), "nonnull"(i32* bitcast ({}* @zeroinit to i32*)) ] +; USE_ASSUME-NEXT: ret i32 0 ; %load = load i32, i32* bitcast ({}* @zeroinit to i32*) ret i32 %load } define i32 @crash_on_undef() { -; CHECK-LABEL: @crash_on_undef( -; CHECK: ret i32 undef +; NO_ASSUME-LABEL: @crash_on_undef( +; NO_ASSUME-NEXT: ret i32 undef +; +; USE_ASSUME-LABEL: @crash_on_undef( +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* bitcast ({}* @undef to i32*), i64 4), "nonnull"(i32* bitcast ({}* @undef to i32*)) ] +; USE_ASSUME-NEXT: ret i32 undef ; %load = load i32, i32* bitcast ({}* @undef to i32*) ret i32 %load @@ -23,8 +32,13 @@ @GV = private constant [8 x i32] [i32 42, i32 43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49] define <8 x i32> @partial_load() { -; CHECK-LABEL: @partial_load( -; CHECK: ret <8 x i32> +; NO_ASSUME-LABEL: @partial_load( +; NO_ASSUME-NEXT: ret <8 x i32> +; +; USE_ASSUME-LABEL: @partial_load( +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(<8 x i32>* bitcast (i32* getelementptr ([8 x i32], [8 x i32]* @GV, i64 0, i64 -1) to <8 x i32>*), i64 32), "nonnull"(<8 x i32>* bitcast (i32* getelementptr ([8 x i32], [8 x i32]* @GV, i64 0, i64 -1) to <8 x i32>*)) ] +; USE_ASSUME-NEXT: ret <8 x i32> +; %load = load <8 x i32>, <8 x i32>* bitcast (i32* getelementptr ([8 x i32], [8 x i32]* @GV, i64 0, i64 -1) to <8 x i32>*) ret <8 x i32> %load } @@ -33,8 +47,13 @@ ; This does an out of bounds load from the global constant define <3 x float> @load_vec3() { -; CHECK-LABEL: @load_vec3( -; CHECK-NEXT: ret <3 x float> undef +; NO_ASSUME-LABEL: @load_vec3( +; NO_ASSUME-NEXT: ret <3 x float> undef +; +; USE_ASSUME-LABEL: @load_vec3( +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(<3 x float>* getelementptr inbounds (<3 x float>, <3 x float>* @constvec, i64 1), i64 12), "nonnull"(<3 x float>* getelementptr inbounds (<3 x float>, <3 x float>* @constvec, i64 1)) ] +; USE_ASSUME-NEXT: ret <3 x float> undef +; %1 = load <3 x float>, <3 x float>* getelementptr inbounds (<3 x float>, <3 x float>* @constvec, i64 1) ret <3 x float> %1 } diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp --- a/llvm/unittests/Transforms/Utils/LocalTest.cpp +++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp @@ -25,12 +25,18 @@ TEST(Local, RecursivelyDeleteDeadPHINodes) { LLVMContext C; + auto M = std::make_unique("test", C); IRBuilder<> builder(C); + Function *F = Function::Create(FunctionType::get(Type::getVoidTy(C), false), + GlobalValue::ExternalLinkage, "", &*M); + // Make blocks BasicBlock *bb0 = BasicBlock::Create(C); BasicBlock *bb1 = BasicBlock::Create(C); + bb0->insertInto(F); + bb1->insertInto(F); builder.SetInsertPoint(bb0); PHINode *phi = builder.CreatePHI(Type::getInt32Ty(C), 2); @@ -59,11 +65,6 @@ builder.CreateAdd(phi, phi); EXPECT_TRUE(RecursivelyDeleteDeadPHINode(phi)); - - bb0->dropAllReferences(); - bb1->dropAllReferences(); - delete bb0; - delete bb1; } TEST(Local, RemoveDuplicatePHINodes) {