diff options
Diffstat (limited to 'unittests/Transforms')
-rw-r--r-- | unittests/Transforms/Scalar/LoopPassManagerTest.cpp | 399 | ||||
-rw-r--r-- | unittests/Transforms/Utils/CMakeLists.txt | 1 | ||||
-rw-r--r-- | unittests/Transforms/Utils/Cloning.cpp | 65 | ||||
-rw-r--r-- | unittests/Transforms/Utils/IntegerDivision.cpp | 16 | ||||
-rw-r--r-- | unittests/Transforms/Utils/MemorySSA.cpp | 534 |
5 files changed, 339 insertions, 676 deletions
diff --git a/unittests/Transforms/Scalar/LoopPassManagerTest.cpp b/unittests/Transforms/Scalar/LoopPassManagerTest.cpp index a099e35c7f19..227060f0a46e 100644 --- a/unittests/Transforms/Scalar/LoopPassManagerTest.cpp +++ b/unittests/Transforms/Scalar/LoopPassManagerTest.cpp @@ -46,7 +46,10 @@ public: DerivedT *Handle; - Analysis(DerivedT &Handle) : Handle(&Handle) {} + Analysis(DerivedT &Handle) : Handle(&Handle) { + static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value, + "Must pass the derived type to this template!"); + } public: class Result { @@ -152,7 +155,10 @@ public: DerivedT *Handle; - Pass(DerivedT &Handle) : Handle(&Handle) {} + Pass(DerivedT &Handle) : Handle(&Handle) { + static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value, + "Must pass the derived type to this template!"); + } public: PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM, @@ -244,27 +250,38 @@ protected: public: LoopPassManagerTest() - : M(parseIR(Context, "define void @f() {\n" - "entry:\n" - " br label %loop.0\n" - "loop.0:\n" - " br i1 undef, label %loop.0.0, label %end\n" - "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.1\n" - "loop.0.1:\n" - " br i1 undef, label %loop.0.1, label %loop.0\n" - "end:\n" - " ret void\n" - "}\n" - "\n" - "define void @g() {\n" - "entry:\n" - " br label %loop.g.0\n" - "loop.g.0:\n" - " br i1 undef, label %loop.g.0, label %end\n" - "end:\n" - " ret void\n" - "}\n")), + : M(parseIR(Context, + "define void @f(i1* %ptr) {\n" + "entry:\n" + " br label %loop.0\n" + "loop.0:\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %end\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" + "loop.0.0:\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n" + "loop.0.1.ph:\n" + " br label %loop.0.1\n" + "loop.0.1:\n" + " %cond.0.1 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.1, label %loop.0.1, label %loop.0.latch\n" + "loop.0.latch:\n" + " br label %loop.0\n" + "end:\n" + " ret void\n" + "}\n" + "\n" + "define void @g(i1* %ptr) {\n" + "entry:\n" + " br label %loop.g.0\n" + "loop.g.0:\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.g.0, label %end\n" + "end:\n" + " ret void\n" + "}\n")), LAM(true), FAM(true), MAM(true) { // Register our mock analysis. LAM.registerPass([&] { return MLAHandle.getAnalysis(); }); @@ -551,7 +568,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); // Not preserving `AAManager`. - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); PA.preserve<LoopAnalysis>(); PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -568,24 +584,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - // Not preserving `AssumptionAnalysis`. - PA.preserve<DominatorTreeAnalysis>(); - PA.preserve<LoopAnalysis>(); - PA.preserve<LoopAnalysisManagerFunctionProxy>(); - PA.preserve<ScalarEvolutionAnalysis>(); - return PA; - })); - EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _)); - EXPECT_CALL(MLAHandle, run(HasName("loop.0.1"), _, _)); - EXPECT_CALL(MLAHandle, run(HasName("loop.0"), _, _)); - FPM.addPass(MFPHandle.getPass()); - FPM.addPass(createFunctionToLoopPassAdaptor( - RequireAnalysisLoopPass<MockLoopAnalysisHandle::Analysis>())); - - EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { - auto PA = PreservedAnalyses::none(); - PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); // Not preserving `DominatorTreeAnalysis`. PA.preserve<LoopAnalysis>(); PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -602,7 +600,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); // Not preserving the `LoopAnalysis`. PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -619,7 +616,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); PA.preserve<LoopAnalysis>(); // Not preserving the `LoopAnalysisManagerFunctionProxy`. @@ -636,7 +632,6 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { EXPECT_CALL(MFPHandle, run(HasName("f"), _)).WillOnce(InvokeWithoutArgs([] { auto PA = PreservedAnalyses::none(); PA.preserve<AAManager>(); - PA.preserve<AssumptionAnalysis>(); PA.preserve<DominatorTreeAnalysis>(); PA.preserve<LoopAnalysis>(); PA.preserve<LoopAnalysisManagerFunctionProxy>(); @@ -654,7 +649,7 @@ TEST_F(LoopPassManagerTest, InvalidationOfBundledAnalyses) { // 'g' once with a requires pass and then run our mock pass over g a bunch // but just get cached results each time. EXPECT_CALL(MLAHandle, run(HasName("loop.g.0"), _, _)); - EXPECT_CALL(MFPHandle, run(HasName("g"), _)).Times(7); + EXPECT_CALL(MFPHandle, run(HasName("g"), _)).Times(6); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); MPM.run(*M, MAM); @@ -842,17 +837,29 @@ TEST_F(LoopPassManagerTest, IndirectOuterPassInvalidation) { TEST_F(LoopPassManagerTest, LoopChildInsertion) { // Super boring module with three loops in a single loop nest. - M = parseIR(Context, "define void @f() {\n" + M = parseIR(Context, "define void @f(i1* %ptr) {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" - " br i1 undef, label %loop.0.0, label %end\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %end\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.1\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n" + "loop.0.1.ph:\n" + " br label %loop.0.1\n" "loop.0.1:\n" - " br i1 undef, label %loop.0.1, label %loop.0.2\n" + " %cond.0.1 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n" + "loop.0.2.ph:\n" + " br label %loop.0.2\n" "loop.0.2:\n" - " br i1 undef, label %loop.0.2, label %loop.0\n" + " %cond.0.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n" + "loop.0.latch:\n" + " br label %loop.0\n" "end:\n" " ret void\n" "}\n"); @@ -861,20 +868,34 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { // easily. Function &F = *M->begin(); ASSERT_THAT(F, HasName("f")); + Argument &Ptr = *F.arg_begin(); auto BBI = F.begin(); BasicBlock &EntryBB = *BBI++; ASSERT_THAT(EntryBB, HasName("entry")); BasicBlock &Loop0BB = *BBI++; ASSERT_THAT(Loop0BB, HasName("loop.0")); + BasicBlock &Loop00PHBB = *BBI++; + ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph")); BasicBlock &Loop00BB = *BBI++; ASSERT_THAT(Loop00BB, HasName("loop.0.0")); + BasicBlock &Loop01PHBB = *BBI++; + ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph")); BasicBlock &Loop01BB = *BBI++; ASSERT_THAT(Loop01BB, HasName("loop.0.1")); + BasicBlock &Loop02PHBB = *BBI++; + ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph")); BasicBlock &Loop02BB = *BBI++; ASSERT_THAT(Loop02BB, HasName("loop.0.2")); + BasicBlock &Loop0LatchBB = *BBI++; + ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch")); BasicBlock &EndBB = *BBI++; ASSERT_THAT(EndBB, HasName("end")); ASSERT_THAT(BBI, F.end()); + auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB, + const char *Name, BasicBlock *BB) { + auto *Cond = new LoadInst(&Ptr, Name, /*isVolatile*/ true, BB); + BranchInst::Create(TrueBB, FalseBB, Cond, BB); + }; // Build the pass managers and register our pipeline. We build a single loop // pass pipeline consisting of three mock pass runs over each loop. After @@ -909,20 +930,33 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { // When running over the middle loop, the second run inserts two new child // loops, inserting them and itself into the worklist. - BasicBlock *NewLoop010BB; + BasicBlock *NewLoop010BB, *NewLoop01LatchBB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)) .WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { auto *NewLoop = new Loop(); L.addChildLoop(NewLoop); - NewLoop010BB = BasicBlock::Create(Context, "loop.0.1.0", &F, &Loop02BB); - BranchInst::Create(&Loop01BB, NewLoop010BB, - UndefValue::get(Type::getInt1Ty(Context)), - NewLoop010BB); - Loop01BB.getTerminator()->replaceUsesOfWith(&Loop01BB, NewLoop010BB); - AR.DT.addNewBlock(NewLoop010BB, &Loop01BB); + auto *NewLoop010PHBB = + BasicBlock::Create(Context, "loop.0.1.0.ph", &F, &Loop02PHBB); + NewLoop010BB = + BasicBlock::Create(Context, "loop.0.1.0", &F, &Loop02PHBB); + NewLoop01LatchBB = + BasicBlock::Create(Context, "loop.0.1.latch", &F, &Loop02PHBB); + Loop01BB.getTerminator()->replaceUsesOfWith(&Loop01BB, NewLoop010PHBB); + BranchInst::Create(NewLoop010BB, NewLoop010PHBB); + CreateCondBr(NewLoop01LatchBB, NewLoop010BB, "cond.0.1.0", + NewLoop010BB); + BranchInst::Create(&Loop01BB, NewLoop01LatchBB); + AR.DT.addNewBlock(NewLoop010PHBB, &Loop01BB); + AR.DT.addNewBlock(NewLoop010BB, NewLoop010PHBB); + AR.DT.addNewBlock(NewLoop01LatchBB, NewLoop010BB); + AR.DT.verifyDomTree(); + L.addBasicBlockToLoop(NewLoop010PHBB, AR.LI); NewLoop->addBasicBlockToLoop(NewLoop010BB, AR.LI); + L.addBasicBlockToLoop(NewLoop01LatchBB, AR.LI); + NewLoop->verifyLoop(); + L.verifyLoop(); Updater.addChildLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -943,21 +977,27 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { // In the second run over the middle loop after we've visited the new child, // we add another child to check that we can repeatedly add children, and add // children to a loop that already has children. - BasicBlock *NewLoop011BB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)) .WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { auto *NewLoop = new Loop(); L.addChildLoop(NewLoop); - NewLoop011BB = BasicBlock::Create(Context, "loop.0.1.1", &F, &Loop02BB); - BranchInst::Create(&Loop01BB, NewLoop011BB, - UndefValue::get(Type::getInt1Ty(Context)), - NewLoop011BB); - NewLoop010BB->getTerminator()->replaceUsesOfWith(&Loop01BB, - NewLoop011BB); - AR.DT.addNewBlock(NewLoop011BB, NewLoop010BB); + auto *NewLoop011PHBB = BasicBlock::Create(Context, "loop.0.1.1.ph", &F, NewLoop01LatchBB); + auto *NewLoop011BB = BasicBlock::Create(Context, "loop.0.1.1", &F, NewLoop01LatchBB); + NewLoop010BB->getTerminator()->replaceUsesOfWith(NewLoop01LatchBB, + NewLoop011PHBB); + BranchInst::Create(NewLoop011BB, NewLoop011PHBB); + CreateCondBr(NewLoop01LatchBB, NewLoop011BB, "cond.0.1.1", + NewLoop011BB); + AR.DT.addNewBlock(NewLoop011PHBB, NewLoop010BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop011BB, NewLoop011PHBB); + AR.DT.changeImmediateDominator(AR.DT[NewLoop01LatchBB], NewDTNode); + AR.DT.verifyDomTree(); + L.addBasicBlockToLoop(NewLoop011PHBB, AR.LI); NewLoop->addBasicBlockToLoop(NewLoop011BB, AR.LI); + NewLoop->verifyLoop(); + L.verifyLoop(); Updater.addChildLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -999,17 +1039,29 @@ TEST_F(LoopPassManagerTest, LoopChildInsertion) { TEST_F(LoopPassManagerTest, LoopPeerInsertion) { // Super boring module with two loop nests and loop nest with two child // loops. - M = parseIR(Context, "define void @f() {\n" + M = parseIR(Context, "define void @f(i1* %ptr) {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" - " br i1 undef, label %loop.0.0, label %loop.2\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %loop.2.ph\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.2\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.2.ph\n" + "loop.0.2.ph:\n" + " br label %loop.0.2\n" "loop.0.2:\n" - " br i1 undef, label %loop.0.2, label %loop.0\n" + " %cond.0.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2, label %loop.0.2, label %loop.0.latch\n" + "loop.0.latch:\n" + " br label %loop.0\n" + "loop.2.ph:\n" + " br label %loop.2\n" "loop.2:\n" - " br i1 undef, label %loop.2, label %end\n" + " %cond.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.2, label %loop.2, label %end\n" "end:\n" " ret void\n" "}\n"); @@ -1018,21 +1070,34 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { // easily. Function &F = *M->begin(); ASSERT_THAT(F, HasName("f")); + Argument &Ptr = *F.arg_begin(); auto BBI = F.begin(); BasicBlock &EntryBB = *BBI++; ASSERT_THAT(EntryBB, HasName("entry")); BasicBlock &Loop0BB = *BBI++; ASSERT_THAT(Loop0BB, HasName("loop.0")); + BasicBlock &Loop00PHBB = *BBI++; + ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph")); BasicBlock &Loop00BB = *BBI++; ASSERT_THAT(Loop00BB, HasName("loop.0.0")); + BasicBlock &Loop02PHBB = *BBI++; + ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph")); BasicBlock &Loop02BB = *BBI++; ASSERT_THAT(Loop02BB, HasName("loop.0.2")); + BasicBlock &Loop0LatchBB = *BBI++; + ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch")); + BasicBlock &Loop2PHBB = *BBI++; + ASSERT_THAT(Loop2PHBB, HasName("loop.2.ph")); BasicBlock &Loop2BB = *BBI++; ASSERT_THAT(Loop2BB, HasName("loop.2")); BasicBlock &EndBB = *BBI++; ASSERT_THAT(EndBB, HasName("end")); ASSERT_THAT(BBI, F.end()); - Constant *Undefi1 = UndefValue::get(Type::getInt1Ty(Context)); + auto CreateCondBr = [&](BasicBlock *TrueBB, BasicBlock *FalseBB, + const char *Name, BasicBlock *BB) { + auto *Cond = new LoadInst(&Ptr, Name, /*isVolatile*/ true, BB); + BranchInst::Create(TrueBB, FalseBB, Cond, BB); + }; // Build the pass managers and register our pipeline. We build a single loop // pass pipeline consisting of three mock pass runs over each loop. After @@ -1059,19 +1124,24 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { EXPECT_CALL(MLAHandle, run(HasName("loop.0.0"), _, _)); // On the second run, we insert a sibling loop. - BasicBlock *NewLoop01BB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)) .WillOnce(Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { auto *NewLoop = new Loop(); L.getParentLoop()->addChildLoop(NewLoop); - NewLoop01BB = BasicBlock::Create(Context, "loop.0.1", &F, &Loop02BB); - BranchInst::Create(&Loop02BB, NewLoop01BB, Undefi1, NewLoop01BB); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02BB, NewLoop01BB); - auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, &Loop00BB); - AR.DT.changeImmediateDominator(AR.DT[&Loop02BB], NewDTNode); + auto *NewLoop01PHBB = BasicBlock::Create(Context, "loop.0.1.ph", &F, &Loop02PHBB); + auto *NewLoop01BB = BasicBlock::Create(Context, "loop.0.1", &F, &Loop02PHBB); + BranchInst::Create(NewLoop01BB, NewLoop01PHBB); + CreateCondBr(&Loop02PHBB, NewLoop01BB, "cond.0.1", NewLoop01BB); + Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02PHBB, NewLoop01PHBB); + AR.DT.addNewBlock(NewLoop01PHBB, &Loop00BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop01BB, NewLoop01PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop02PHBB], NewDTNode); + AR.DT.verifyDomTree(); + L.getParentLoop()->addBasicBlockToLoop(NewLoop01PHBB, AR.LI); NewLoop->addBasicBlockToLoop(NewLoop01BB, AR.LI); + L.getParentLoop()->verifyLoop(); Updater.addSiblingLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -1104,22 +1174,45 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { L.getParentLoop()->addChildLoop(NewLoops[0]); L.getParentLoop()->addChildLoop(NewLoops[1]); NewLoops[1]->addChildLoop(NewLoops[2]); + auto *NewLoop03PHBB = + BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB); auto *NewLoop03BB = - BasicBlock::Create(Context, "loop.0.3", &F, &Loop2BB); + BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB); + auto *NewLoop04PHBB = + BasicBlock::Create(Context, "loop.0.4.ph", &F, &Loop0LatchBB); auto *NewLoop04BB = - BasicBlock::Create(Context, "loop.0.4", &F, &Loop2BB); + BasicBlock::Create(Context, "loop.0.4", &F, &Loop0LatchBB); + auto *NewLoop040PHBB = + BasicBlock::Create(Context, "loop.0.4.0.ph", &F, &Loop0LatchBB); auto *NewLoop040BB = - BasicBlock::Create(Context, "loop.0.4.0", &F, &Loop2BB); - Loop02BB.getTerminator()->replaceUsesOfWith(&Loop0BB, NewLoop03BB); - BranchInst::Create(NewLoop04BB, NewLoop03BB, Undefi1, NewLoop03BB); - BranchInst::Create(&Loop0BB, NewLoop040BB, Undefi1, NewLoop04BB); - BranchInst::Create(NewLoop04BB, NewLoop040BB, Undefi1, NewLoop040BB); - AR.DT.addNewBlock(NewLoop03BB, &Loop02BB); - AR.DT.addNewBlock(NewLoop04BB, NewLoop03BB); - AR.DT.addNewBlock(NewLoop040BB, NewLoop04BB); + BasicBlock::Create(Context, "loop.0.4.0", &F, &Loop0LatchBB); + auto *NewLoop04LatchBB = + BasicBlock::Create(Context, "loop.0.4.latch", &F, &Loop0LatchBB); + Loop02BB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB, NewLoop03PHBB); + BranchInst::Create(NewLoop03BB, NewLoop03PHBB); + CreateCondBr(NewLoop04PHBB, NewLoop03BB, "cond.0.3", NewLoop03BB); + BranchInst::Create(NewLoop04BB, NewLoop04PHBB); + CreateCondBr(&Loop0LatchBB, NewLoop040PHBB, "cond.0.4", NewLoop04BB); + BranchInst::Create(NewLoop040BB, NewLoop040PHBB); + CreateCondBr(NewLoop04LatchBB, NewLoop040BB, "cond.0.4.0", NewLoop040BB); + BranchInst::Create(NewLoop04BB, NewLoop04LatchBB); + AR.DT.addNewBlock(NewLoop03PHBB, &Loop02BB); + AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB); + AR.DT.addNewBlock(NewLoop04PHBB, NewLoop03BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop04BB, NewLoop04PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB], NewDTNode); + AR.DT.addNewBlock(NewLoop040PHBB, NewLoop04BB); + AR.DT.addNewBlock(NewLoop040BB, NewLoop040PHBB); + AR.DT.addNewBlock(NewLoop04LatchBB, NewLoop040BB); + AR.DT.verifyDomTree(); + L.getParentLoop()->addBasicBlockToLoop(NewLoop03PHBB, AR.LI); NewLoops[0]->addBasicBlockToLoop(NewLoop03BB, AR.LI); + L.getParentLoop()->addBasicBlockToLoop(NewLoop04PHBB, AR.LI); NewLoops[1]->addBasicBlockToLoop(NewLoop04BB, AR.LI); + NewLoops[1]->addBasicBlockToLoop(NewLoop040PHBB, AR.LI); NewLoops[2]->addBasicBlockToLoop(NewLoop040BB, AR.LI); + NewLoops[1]->addBasicBlockToLoop(NewLoop04LatchBB, AR.LI); + L.getParentLoop()->verifyLoop(); Updater.addSiblingLoops({NewLoops[0], NewLoops[1]}); return PreservedAnalyses::all(); })); @@ -1158,12 +1251,17 @@ TEST_F(LoopPassManagerTest, LoopPeerInsertion) { LPMUpdater &Updater) { auto *NewLoop = new Loop(); AR.LI.addTopLevelLoop(NewLoop); + auto *NewLoop1PHBB = BasicBlock::Create(Context, "loop.1.ph", &F, &Loop2BB); auto *NewLoop1BB = BasicBlock::Create(Context, "loop.1", &F, &Loop2BB); - BranchInst::Create(&Loop2BB, NewLoop1BB, Undefi1, NewLoop1BB); - Loop0BB.getTerminator()->replaceUsesOfWith(&Loop2BB, NewLoop1BB); - auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, &Loop0BB); - AR.DT.changeImmediateDominator(AR.DT[&Loop2BB], NewDTNode); + BranchInst::Create(NewLoop1BB, NewLoop1PHBB); + CreateCondBr(&Loop2PHBB, NewLoop1BB, "cond.1", NewLoop1BB); + Loop0BB.getTerminator()->replaceUsesOfWith(&Loop2PHBB, NewLoop1PHBB); + AR.DT.addNewBlock(NewLoop1PHBB, &Loop0BB); + auto *NewDTNode = AR.DT.addNewBlock(NewLoop1BB, NewLoop1PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop2PHBB], NewDTNode); + AR.DT.verifyDomTree(); NewLoop->addBasicBlockToLoop(NewLoop1BB, AR.LI); + NewLoop->verifyLoop(); Updater.addSiblingLoops({NewLoop}); return PreservedAnalyses::all(); })); @@ -1193,19 +1291,36 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { // Build a module with a single loop nest that contains one outer loop with // three subloops, and one of those with its own subloop. We will // incrementally delete all of these to test different deletion scenarios. - M = parseIR(Context, "define void @f() {\n" + M = parseIR(Context, "define void @f(i1* %ptr) {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" - " br i1 undef, label %loop.0.0, label %end\n" + " %cond.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0, label %loop.0.0.ph, label %end\n" + "loop.0.0.ph:\n" + " br label %loop.0.0\n" "loop.0.0:\n" - " br i1 undef, label %loop.0.0, label %loop.0.1\n" + " %cond.0.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.0, label %loop.0.0, label %loop.0.1.ph\n" + "loop.0.1.ph:\n" + " br label %loop.0.1\n" "loop.0.1:\n" - " br i1 undef, label %loop.0.1, label %loop.0.2\n" + " %cond.0.1 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.1, label %loop.0.1, label %loop.0.2.ph\n" + "loop.0.2.ph:\n" + " br label %loop.0.2\n" "loop.0.2:\n" - " br i1 undef, label %loop.0.2.0, label %loop.0\n" + " %cond.0.2 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2, label %loop.0.2.0.ph, label %loop.0.latch\n" + "loop.0.2.0.ph:\n" + " br label %loop.0.2.0\n" "loop.0.2.0:\n" - " br i1 undef, label %loop.0.2.0, label %loop.0.2\n" + " %cond.0.2.0 = load volatile i1, i1* %ptr\n" + " br i1 %cond.0.2.0, label %loop.0.2.0, label %loop.0.2.latch\n" + "loop.0.2.latch:\n" + " br label %loop.0.2\n" + "loop.0.latch:\n" + " br label %loop.0\n" "end:\n" " ret void\n" "}\n"); @@ -1214,44 +1329,64 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { // easily. Function &F = *M->begin(); ASSERT_THAT(F, HasName("f")); + Argument &Ptr = *F.arg_begin(); auto BBI = F.begin(); BasicBlock &EntryBB = *BBI++; ASSERT_THAT(EntryBB, HasName("entry")); BasicBlock &Loop0BB = *BBI++; ASSERT_THAT(Loop0BB, HasName("loop.0")); + BasicBlock &Loop00PHBB = *BBI++; + ASSERT_THAT(Loop00PHBB, HasName("loop.0.0.ph")); BasicBlock &Loop00BB = *BBI++; ASSERT_THAT(Loop00BB, HasName("loop.0.0")); + BasicBlock &Loop01PHBB = *BBI++; + ASSERT_THAT(Loop01PHBB, HasName("loop.0.1.ph")); BasicBlock &Loop01BB = *BBI++; ASSERT_THAT(Loop01BB, HasName("loop.0.1")); + BasicBlock &Loop02PHBB = *BBI++; + ASSERT_THAT(Loop02PHBB, HasName("loop.0.2.ph")); BasicBlock &Loop02BB = *BBI++; ASSERT_THAT(Loop02BB, HasName("loop.0.2")); + BasicBlock &Loop020PHBB = *BBI++; + ASSERT_THAT(Loop020PHBB, HasName("loop.0.2.0.ph")); BasicBlock &Loop020BB = *BBI++; ASSERT_THAT(Loop020BB, HasName("loop.0.2.0")); + BasicBlock &Loop02LatchBB = *BBI++; + ASSERT_THAT(Loop02LatchBB, HasName("loop.0.2.latch")); + BasicBlock &Loop0LatchBB = *BBI++; + ASSERT_THAT(Loop0LatchBB, HasName("loop.0.latch")); BasicBlock &EndBB = *BBI++; ASSERT_THAT(EndBB, HasName("end")); ASSERT_THAT(BBI, F.end()); - Constant *Undefi1 = UndefValue::get(Type::getInt1Ty(Context)); // Helper to do the actual deletion of a loop. We directly encode this here // to isolate ourselves from the rest of LLVM and for simplicity. Here we can // egregiously cheat based on knowledge of the test case. For example, we // have no PHI nodes and there is always a single i-dom. - auto DeleteLoopBlocks = [](Loop &L, BasicBlock &IDomBB, + auto RemoveLoop = [](Loop &L, BasicBlock &IDomBB, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { - for (BasicBlock *LoopBB : L.blocks()) { + assert(L.empty() && "Can only delete leaf loops with this routine!"); + SmallVector<BasicBlock *, 4> LoopBBs(L.block_begin(), L.block_end()); + Updater.markLoopAsDeleted(L); + IDomBB.getTerminator()->replaceUsesOfWith(L.getHeader(), + L.getUniqueExitBlock()); + for (BasicBlock *LoopBB : LoopBBs) { SmallVector<DomTreeNode *, 4> ChildNodes(AR.DT[LoopBB]->begin(), AR.DT[LoopBB]->end()); for (DomTreeNode *ChildNode : ChildNodes) AR.DT.changeImmediateDominator(ChildNode, AR.DT[&IDomBB]); AR.DT.eraseNode(LoopBB); + AR.LI.removeBlock(LoopBB); LoopBB->dropAllReferences(); } - SmallVector<BasicBlock *, 4> LoopBBs(L.block_begin(), L.block_end()); - Updater.markLoopAsDeleted(L); - AR.LI.markAsRemoved(&L); for (BasicBlock *LoopBB : LoopBBs) LoopBB->eraseFromParent(); + + if (Loop *ParentL = L.getParentLoop()) + return ParentL->removeChildLoop(find(*ParentL, &L)); + + return AR.LI.removeLoop(find(AR.LI, &L)); }; // Build up the pass managers. @@ -1294,9 +1429,10 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { .WillOnce( Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { + Loop *ParentL = L.getParentLoop(); AR.SE.forgetLoop(&L); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop01BB, &Loop02BB); - DeleteLoopBlocks(L, Loop00BB, AR, Updater); + delete RemoveLoop(L, Loop01PHBB, AR, Updater); + ParentL->verifyLoop(); return PreservedAnalyses::all(); })); @@ -1337,38 +1473,40 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)) .WillOnce(Invoke(getLoopAnalysisResult)); - BasicBlock *NewLoop03BB; + BasicBlock *NewLoop03PHBB; EXPECT_CALL(MLPHandle, run(HasName("loop.0.2"), _, _, _)) .WillOnce( Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { - // Delete the inner loop first. we also do this manually because we - // want to preserve the loop object and reuse it. + // Remove the inner loop first but retain it to reuse later. AR.SE.forgetLoop(*L.begin()); - Loop02BB.getTerminator()->replaceUsesOfWith(&Loop020BB, &Loop02BB); - assert(std::next((*L.begin())->block_begin()) == - (*L.begin())->block_end() && - "There should only be one block."); - assert(AR.DT[&Loop020BB]->getNumChildren() == 0 && - "Cannot have children in the domtree!"); - AR.DT.eraseNode(&Loop020BB); - Updater.markLoopAsDeleted(**L.begin()); - AR.LI.removeBlock(&Loop020BB); - auto *OldL = L.removeChildLoop(L.begin()); - Loop020BB.eraseFromParent(); + auto *OldL = RemoveLoop(**L.begin(), Loop020PHBB, AR, Updater); auto *ParentL = L.getParentLoop(); AR.SE.forgetLoop(&L); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop02BB, &Loop0BB); - DeleteLoopBlocks(L, Loop00BB, AR, Updater); + delete RemoveLoop(L, Loop02PHBB, AR, Updater); // Now insert a new sibling loop, reusing a loop pointer. ParentL->addChildLoop(OldL); - NewLoop03BB = BasicBlock::Create(Context, "loop.0.3", &F, &EndBB); - BranchInst::Create(&Loop0BB, NewLoop03BB, Undefi1, NewLoop03BB); - Loop00BB.getTerminator()->replaceUsesOfWith(&Loop0BB, NewLoop03BB); - AR.DT.addNewBlock(NewLoop03BB, &Loop00BB); + NewLoop03PHBB = + BasicBlock::Create(Context, "loop.0.3.ph", &F, &Loop0LatchBB); + auto *NewLoop03BB = + BasicBlock::Create(Context, "loop.0.3", &F, &Loop0LatchBB); + BranchInst::Create(NewLoop03BB, NewLoop03PHBB); + auto *Cond = new LoadInst(&Ptr, "cond.0.3", /*isVolatile*/ true, + NewLoop03BB); + BranchInst::Create(&Loop0LatchBB, NewLoop03BB, Cond, NewLoop03BB); + Loop02PHBB.getTerminator()->replaceUsesOfWith(&Loop0LatchBB, + NewLoop03PHBB); + AR.DT.addNewBlock(NewLoop03PHBB, &Loop02PHBB); + AR.DT.addNewBlock(NewLoop03BB, NewLoop03PHBB); + AR.DT.changeImmediateDominator(AR.DT[&Loop0LatchBB], + AR.DT[NewLoop03BB]); + AR.DT.verifyDomTree(); + ParentL->addBasicBlockToLoop(NewLoop03PHBB, AR.LI); OldL->addBasicBlockToLoop(NewLoop03BB, AR.LI); + OldL->verifyLoop(); + ParentL->verifyLoop(); Updater.addSiblingLoops({OldL}); return PreservedAnalyses::all(); })); @@ -1401,8 +1539,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { AR.SE.forgetLoop(&L); - Loop0BB.getTerminator()->replaceUsesOfWith(&Loop00BB, NewLoop03BB); - DeleteLoopBlocks(L, Loop0BB, AR, Updater); + delete RemoveLoop(L, Loop00PHBB, AR, Updater); return PreservedAnalyses::all(); })); @@ -1413,8 +1550,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { AR.SE.forgetLoop(&L); - Loop0BB.getTerminator()->replaceUsesOfWith(NewLoop03BB, &Loop0BB); - DeleteLoopBlocks(L, Loop0BB, AR, Updater); + delete RemoveLoop(L, *NewLoop03PHBB, AR, Updater); return PreservedAnalyses::all(); })); @@ -1425,8 +1561,7 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { Invoke([&](Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &Updater) { AR.SE.forgetLoop(&L); - EntryBB.getTerminator()->replaceUsesOfWith(&Loop0BB, &EndBB); - DeleteLoopBlocks(L, EntryBB, AR, Updater); + delete RemoveLoop(L, EntryBB, AR, Updater); return PreservedAnalyses::all(); })); diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt index c0f37418e492..0fc19ef09fb0 100644 --- a/unittests/Transforms/Utils/CMakeLists.txt +++ b/unittests/Transforms/Utils/CMakeLists.txt @@ -11,6 +11,5 @@ add_llvm_unittest(UtilsTests FunctionComparator.cpp IntegerDivision.cpp Local.cpp - MemorySSA.cpp ValueMapperTest.cpp ) diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp index 216bd32c50d2..403c9c06c18a 100644 --- a/unittests/Transforms/Utils/Cloning.cpp +++ b/unittests/Transforms/Utils/Cloning.cpp @@ -163,7 +163,7 @@ TEST_F(CloneInstruction, Attributes) { Function *F2 = Function::Create(FT1, Function::ExternalLinkage); Attribute::AttrKind AK[] = { Attribute::NoCapture }; - AttributeSet AS = AttributeSet::get(context, 0, AK); + AttributeList AS = AttributeList::get(context, 0, AK); Argument *A = &*F1->arg_begin(); A->addAttr(AS); @@ -201,6 +201,53 @@ TEST_F(CloneInstruction, CallingConvention) { delete F2; } +TEST_F(CloneInstruction, DuplicateInstructionsToSplit) { + Type *ArgTy1[] = {Type::getInt32PtrTy(context)}; + FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); + V = new Argument(Type::getInt32Ty(context)); + + Function *F = Function::Create(FT, Function::ExternalLinkage); + + BasicBlock *BB1 = BasicBlock::Create(context, "", F); + IRBuilder<> Builder1(BB1); + + BasicBlock *BB2 = BasicBlock::Create(context, "", F); + IRBuilder<> Builder2(BB2); + + Builder1.CreateBr(BB2); + + Instruction *AddInst = cast<Instruction>(Builder2.CreateAdd(V, V)); + Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V)); + Instruction *SubInst = cast<Instruction>(Builder2.CreateSub(MulInst, V)); + Builder2.CreateRetVoid(); + + ValueToValueMapTy Mapping; + + auto Split = DuplicateInstructionsInSplitBetween(BB2, BB1, SubInst, Mapping); + + EXPECT_TRUE(Split); + EXPECT_EQ(Mapping.size(), 2u); + EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end()); + EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end()); + + auto AddSplit = dyn_cast<Instruction>(Mapping[AddInst]); + EXPECT_TRUE(AddSplit); + EXPECT_EQ(AddSplit->getOperand(0), V); + EXPECT_EQ(AddSplit->getOperand(1), V); + EXPECT_EQ(AddSplit->getParent(), Split); + + auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]); + EXPECT_TRUE(MulSplit); + EXPECT_EQ(MulSplit->getOperand(0), AddSplit); + EXPECT_EQ(MulSplit->getOperand(1), V); + EXPECT_EQ(MulSplit->getParent(), Split); + + EXPECT_EQ(AddSplit->getNextNode(), MulSplit); + EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator()); + + delete F; +} + class CloneFunc : public ::testing::Test { protected: void SetUp() override { @@ -405,10 +452,14 @@ protected: void SetupModule() { OldM = new Module("", C); } void CreateOldModule() { + auto *CD = OldM->getOrInsertComdat("comdat"); + CD->setSelectionKind(Comdat::ExactMatch); + auto GV = new GlobalVariable( *OldM, Type::getInt32Ty(C), false, GlobalValue::ExternalLinkage, ConstantInt::get(Type::getInt32Ty(C), 1), "gv"); GV->addMetadata(LLVMContext::MD_type, *MDNode::get(C, {})); + GV->setComdat(CD); DIBuilder DBuilder(*OldM); IRBuilder<> IBuilder(C); @@ -419,6 +470,7 @@ protected: auto *F = Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM); F->setPersonalityFn(PersFn); + F->setComdat(CD); // Create debug info auto *File = DBuilder.createFile("filename.c", "/file/dir/"); @@ -472,4 +524,15 @@ TEST_F(CloneModule, GlobalMetadata) { GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); EXPECT_NE(nullptr, NewGV->getMetadata(LLVMContext::MD_type)); } + +TEST_F(CloneModule, Comdat) { + GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); + auto *CD = NewGV->getComdat(); + ASSERT_NE(nullptr, CD); + EXPECT_EQ("comdat", CD->getName()); + EXPECT_EQ(Comdat::ExactMatch, CD->getSelectionKind()); + + Function *NewF = NewM->getFunction("f"); + EXPECT_EQ(CD, NewF->getComdat()); +} } diff --git a/unittests/Transforms/Utils/IntegerDivision.cpp b/unittests/Transforms/Utils/IntegerDivision.cpp index b6b1b1665ab1..e337b9f547a8 100644 --- a/unittests/Transforms/Utils/IntegerDivision.cpp +++ b/unittests/Transforms/Utils/IntegerDivision.cpp @@ -29,7 +29,7 @@ TEST(IntegerDivision, SDiv) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -59,7 +59,7 @@ TEST(IntegerDivision, UDiv) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -89,7 +89,7 @@ TEST(IntegerDivision, SRem) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -119,7 +119,7 @@ TEST(IntegerDivision, URem) { Function *F = Function::Create(FunctionType::get(Builder.getInt32Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -150,7 +150,7 @@ TEST(IntegerDivision, SDiv64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -180,7 +180,7 @@ TEST(IntegerDivision, UDiv64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -210,7 +210,7 @@ TEST(IntegerDivision, SRem64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); @@ -240,7 +240,7 @@ TEST(IntegerDivision, URem64) { Function *F = Function::Create(FunctionType::get(Builder.getInt64Ty(), ArgTys, false), GlobalValue::ExternalLinkage, "F", &M); - assert(F->getArgumentList().size() == 2); + assert(F->arg_size() == 2); BasicBlock *BB = BasicBlock::Create(C, "", F); Builder.SetInsertPoint(BB); diff --git a/unittests/Transforms/Utils/MemorySSA.cpp b/unittests/Transforms/Utils/MemorySSA.cpp deleted file mode 100644 index 945fe32c316c..000000000000 --- a/unittests/Transforms/Utils/MemorySSA.cpp +++ /dev/null @@ -1,534 +0,0 @@ -//===- MemorySSA.cpp - Unit tests for MemorySSA ---------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "llvm/Transforms/Utils/MemorySSA.h" -#include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/Analysis/BasicAliasAnalysis.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/LLVMContext.h" -#include "gtest/gtest.h" - -using namespace llvm; - -const static char DLString[] = "e-i64:64-f80:128-n8:16:32:64-S128"; - -/// There's a lot of common setup between these tests. This fixture helps reduce -/// that. Tests should mock up a function, store it in F, and then call -/// setupAnalyses(). -class MemorySSATest : public testing::Test { -protected: - // N.B. Many of these members depend on each other (e.g. the Module depends on - // the Context, etc.). So, order matters here (and in TestAnalyses). - LLVMContext C; - Module M; - IRBuilder<> B; - DataLayout DL; - TargetLibraryInfoImpl TLII; - TargetLibraryInfo TLI; - Function *F; - - // Things that we need to build after the function is created. - struct TestAnalyses { - DominatorTree DT; - AssumptionCache AC; - AAResults AA; - BasicAAResult BAA; - // We need to defer MSSA construction until AA is *entirely* set up, which - // requires calling addAAResult. Hence, we just use a pointer here. - std::unique_ptr<MemorySSA> MSSA; - MemorySSAWalker *Walker; - - TestAnalyses(MemorySSATest &Test) - : DT(*Test.F), AC(*Test.F), AA(Test.TLI), - BAA(Test.DL, Test.TLI, AC, &DT) { - AA.addAAResult(BAA); - MSSA = make_unique<MemorySSA>(*Test.F, &AA, &DT); - Walker = MSSA->getWalker(); - } - }; - - std::unique_ptr<TestAnalyses> Analyses; - - void setupAnalyses() { - assert(F); - Analyses.reset(new TestAnalyses(*this)); - } - -public: - MemorySSATest() - : M("MemorySSATest", C), B(C), DL(DLString), TLI(TLII), F(nullptr) {} -}; - -TEST_F(MemorySSATest, CreateALoad) { - // We create a diamond where there is a store on one side, and then after - // building MemorySSA, create a load after the merge point, and use it to test - // updating by creating an access for the load. - F = Function::Create( - FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), - GlobalValue::ExternalLinkage, "F", &M); - BasicBlock *Entry(BasicBlock::Create(C, "", F)); - BasicBlock *Left(BasicBlock::Create(C, "", F)); - BasicBlock *Right(BasicBlock::Create(C, "", F)); - BasicBlock *Merge(BasicBlock::Create(C, "", F)); - B.SetInsertPoint(Entry); - B.CreateCondBr(B.getTrue(), Left, Right); - B.SetInsertPoint(Left); - Argument *PointerArg = &*F->arg_begin(); - B.CreateStore(B.getInt8(16), PointerArg); - BranchInst::Create(Merge, Left); - BranchInst::Create(Merge, Right); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - // Add the load - B.SetInsertPoint(Merge); - LoadInst *LoadInst = B.CreateLoad(PointerArg); - - // MemoryPHI should already exist. - MemoryPhi *MP = MSSA.getMemoryAccess(Merge); - EXPECT_NE(MP, nullptr); - - // Create the load memory acccess - MemoryUse *LoadAccess = cast<MemoryUse>( - MSSA.createMemoryAccessInBB(LoadInst, MP, Merge, MemorySSA::Beginning)); - MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); - EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); - MSSA.verifyMemorySSA(); -} - -TEST_F(MemorySSATest, MoveAStore) { - // We create a diamond where there is a in the entry, a store on one side, and - // a load at the end. After building MemorySSA, we test updating by moving - // the store from the side block to the entry block. - F = Function::Create( - FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), - GlobalValue::ExternalLinkage, "F", &M); - BasicBlock *Entry(BasicBlock::Create(C, "", F)); - BasicBlock *Left(BasicBlock::Create(C, "", F)); - BasicBlock *Right(BasicBlock::Create(C, "", F)); - BasicBlock *Merge(BasicBlock::Create(C, "", F)); - B.SetInsertPoint(Entry); - Argument *PointerArg = &*F->arg_begin(); - StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg); - B.CreateCondBr(B.getTrue(), Left, Right); - B.SetInsertPoint(Left); - StoreInst *SideStore = B.CreateStore(B.getInt8(16), PointerArg); - BranchInst::Create(Merge, Left); - BranchInst::Create(Merge, Right); - B.SetInsertPoint(Merge); - B.CreateLoad(PointerArg); - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - - // Move the store - SideStore->moveBefore(Entry->getTerminator()); - MemoryAccess *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore); - MemoryAccess *SideStoreAccess = MSSA.getMemoryAccess(SideStore); - MemoryAccess *NewStoreAccess = MSSA.createMemoryAccessAfter( - SideStore, EntryStoreAccess, EntryStoreAccess); - EntryStoreAccess->replaceAllUsesWith(NewStoreAccess); - MSSA.removeMemoryAccess(SideStoreAccess); - MSSA.verifyMemorySSA(); -} - -TEST_F(MemorySSATest, RemoveAPhi) { - // We create a diamond where there is a store on one side, and then a load - // after the merge point. This enables us to test a bunch of different - // removal cases. - F = Function::Create( - FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), - GlobalValue::ExternalLinkage, "F", &M); - BasicBlock *Entry(BasicBlock::Create(C, "", F)); - BasicBlock *Left(BasicBlock::Create(C, "", F)); - BasicBlock *Right(BasicBlock::Create(C, "", F)); - BasicBlock *Merge(BasicBlock::Create(C, "", F)); - B.SetInsertPoint(Entry); - B.CreateCondBr(B.getTrue(), Left, Right); - B.SetInsertPoint(Left); - Argument *PointerArg = &*F->arg_begin(); - StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); - BranchInst::Create(Merge, Left); - BranchInst::Create(Merge, Right); - B.SetInsertPoint(Merge); - LoadInst *LoadInst = B.CreateLoad(PointerArg); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - // Before, the load will be a use of a phi<store, liveonentry>. - MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst)); - MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst)); - MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); - EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); - // Kill the store - MSSA.removeMemoryAccess(StoreAccess); - MemoryPhi *MP = cast<MemoryPhi>(DefiningAccess); - // Verify the phi ended up as liveonentry, liveonentry - for (auto &Op : MP->incoming_values()) - EXPECT_TRUE(MSSA.isLiveOnEntryDef(cast<MemoryAccess>(Op.get()))); - // Replace the phi uses with the live on entry def - MP->replaceAllUsesWith(MSSA.getLiveOnEntryDef()); - // Verify the load is now defined by liveOnEntryDef - EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); - // Remove the PHI - MSSA.removeMemoryAccess(MP); - MSSA.verifyMemorySSA(); -} - -TEST_F(MemorySSATest, RemoveMemoryAccess) { - // We create a diamond where there is a store on one side, and then a load - // after the merge point. This enables us to test a bunch of different - // removal cases. - F = Function::Create( - FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), - GlobalValue::ExternalLinkage, "F", &M); - BasicBlock *Entry(BasicBlock::Create(C, "", F)); - BasicBlock *Left(BasicBlock::Create(C, "", F)); - BasicBlock *Right(BasicBlock::Create(C, "", F)); - BasicBlock *Merge(BasicBlock::Create(C, "", F)); - B.SetInsertPoint(Entry); - B.CreateCondBr(B.getTrue(), Left, Right); - B.SetInsertPoint(Left); - Argument *PointerArg = &*F->arg_begin(); - StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); - BranchInst::Create(Merge, Left); - BranchInst::Create(Merge, Right); - B.SetInsertPoint(Merge); - LoadInst *LoadInst = B.CreateLoad(PointerArg); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - // Before, the load will be a use of a phi<store, liveonentry>. It should be - // the same after. - MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst)); - MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst)); - MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); - EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); - // The load is currently clobbered by one of the phi arguments, so the walker - // should determine the clobbering access as the phi. - EXPECT_EQ(DefiningAccess, Walker->getClobberingMemoryAccess(LoadInst)); - MSSA.removeMemoryAccess(StoreAccess); - MSSA.verifyMemorySSA(); - // After the removeaccess, let's see if we got the right accesses - // The load should still point to the phi ... - EXPECT_EQ(DefiningAccess, LoadAccess->getDefiningAccess()); - // but we should now get live on entry for the clobbering definition of the - // load, since it will walk past the phi node since every argument is the - // same. - // XXX: This currently requires either removing the phi or resetting optimized - // on the load - - EXPECT_FALSE( - MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst))); - // If we reset optimized, we get live on entry. - LoadAccess->resetOptimized(); - EXPECT_TRUE( - MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst))); - // The phi should now be a two entry phi with two live on entry defs. - for (const auto &Op : DefiningAccess->operands()) { - MemoryAccess *Operand = cast<MemoryAccess>(&*Op); - EXPECT_TRUE(MSSA.isLiveOnEntryDef(Operand)); - } - - // Now we try to remove the single valued phi - MSSA.removeMemoryAccess(DefiningAccess); - MSSA.verifyMemorySSA(); - // Now the load should be a load of live on entry. - EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); -} - -// We had a bug with caching where the walker would report MemoryDef#3's clobber -// (below) was MemoryDef#1. -// -// define void @F(i8*) { -// %A = alloca i8, i8 1 -// ; 1 = MemoryDef(liveOnEntry) -// store i8 0, i8* %A -// ; 2 = MemoryDef(1) -// store i8 1, i8* %A -// ; 3 = MemoryDef(2) -// store i8 2, i8* %A -// } -TEST_F(MemorySSATest, TestTripleStore) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "", F)); - Type *Int8 = Type::getInt8Ty(C); - Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); - StoreInst *S1 = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); - StoreInst *S2 = B.CreateStore(ConstantInt::get(Int8, 1), Alloca); - StoreInst *S3 = B.CreateStore(ConstantInt::get(Int8, 2), Alloca); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - unsigned I = 0; - for (StoreInst *V : {S1, S2, S3}) { - // Everything should be clobbered by its defining access - MemoryAccess *DefiningAccess = MSSA.getMemoryAccess(V)->getDefiningAccess(); - MemoryAccess *WalkerClobber = Walker->getClobberingMemoryAccess(V); - EXPECT_EQ(DefiningAccess, WalkerClobber) - << "Store " << I << " doesn't have the correct clobbering access"; - // EXPECT_EQ expands such that if we increment I above, it won't get - // incremented except when we try to print the error message. - ++I; - } -} - -// ...And fixing the above bug made it obvious that, when walking, MemorySSA's -// walker was caching the initial node it walked. This was fine (albeit -// mostly redundant) unless the initial node being walked is a clobber for the -// query. In that case, we'd cache that the node clobbered itself. -TEST_F(MemorySSATest, TestStoreAndLoad) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "", F)); - Type *Int8 = Type::getInt8Ty(C); - Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); - Instruction *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); - Instruction *LI = B.CreateLoad(Alloca); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LI); - EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SI)); - EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SI))); -} - -// Another bug (related to the above two fixes): It was noted that, given the -// following code: -// ; 1 = MemoryDef(liveOnEntry) -// store i8 0, i8* %1 -// -// ...A query to getClobberingMemoryAccess(MemoryAccess*, MemoryLocation) would -// hand back the store (correctly). A later call to -// getClobberingMemoryAccess(const Instruction*) would also hand back the store -// (incorrectly; it should return liveOnEntry). -// -// This test checks that repeated calls to either function returns what they're -// meant to. -TEST_F(MemorySSATest, TestStoreDoubleQuery) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "", F)); - Type *Int8 = Type::getInt8Ty(C); - Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); - StoreInst *SI = B.CreateStore(ConstantInt::get(Int8, 0), Alloca); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - MemoryAccess *StoreAccess = MSSA.getMemoryAccess(SI); - MemoryLocation StoreLoc = MemoryLocation::get(SI); - MemoryAccess *Clobber = - Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc); - MemoryAccess *LiveOnEntry = Walker->getClobberingMemoryAccess(SI); - - EXPECT_EQ(Clobber, StoreAccess); - EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry)); - - // Try again (with entries in the cache already) for good measure... - Clobber = Walker->getClobberingMemoryAccess(StoreAccess, StoreLoc); - LiveOnEntry = Walker->getClobberingMemoryAccess(SI); - EXPECT_EQ(Clobber, StoreAccess); - EXPECT_TRUE(MSSA.isLiveOnEntryDef(LiveOnEntry)); -} - -// Bug: During phi optimization, the walker wouldn't cache to the proper result -// in the farthest-walked BB. -// -// Specifically, it would assume that whatever we walked to was a clobber. -// "Whatever we walked to" isn't a clobber if we hit a cache entry. -// -// ...So, we need a test case that looks like: -// A -// / \ -// B | -// \ / -// C -// -// Where, when we try to optimize a thing in 'C', a blocker is found in 'B'. -// The walk must determine that the blocker exists by using cache entries *while -// walking* 'B'. -TEST_F(MemorySSATest, PartialWalkerCacheWithPhis) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "A", F)); - Type *Int8 = Type::getInt8Ty(C); - Constant *One = ConstantInt::get(Int8, 1); - Constant *Zero = ConstantInt::get(Int8, 0); - Value *AllocA = B.CreateAlloca(Int8, One, "a"); - Value *AllocB = B.CreateAlloca(Int8, One, "b"); - BasicBlock *IfThen = BasicBlock::Create(C, "B", F); - BasicBlock *IfEnd = BasicBlock::Create(C, "C", F); - - B.CreateCondBr(UndefValue::get(Type::getInt1Ty(C)), IfThen, IfEnd); - - B.SetInsertPoint(IfThen); - Instruction *FirstStore = B.CreateStore(Zero, AllocA); - B.CreateStore(Zero, AllocB); - Instruction *ALoad0 = B.CreateLoad(AllocA, ""); - Instruction *BStore = B.CreateStore(Zero, AllocB); - // Due to use optimization/etc. we make a store to A, which is removed after - // we build MSSA. This helps keep the test case simple-ish. - Instruction *KillStore = B.CreateStore(Zero, AllocA); - Instruction *ALoad = B.CreateLoad(AllocA, ""); - B.CreateBr(IfEnd); - - B.SetInsertPoint(IfEnd); - Instruction *BelowPhi = B.CreateStore(Zero, AllocA); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - // Kill `KillStore`; it exists solely so that the load after it won't be - // optimized to FirstStore. - MSSA.removeMemoryAccess(MSSA.getMemoryAccess(KillStore)); - KillStore->eraseFromParent(); - auto *ALoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(ALoad)); - EXPECT_EQ(ALoadMA->getDefiningAccess(), MSSA.getMemoryAccess(BStore)); - - // Populate the cache for the store to AllocB directly after FirstStore. It - // should point to something in block B (so something in D can't be optimized - // to it). - MemoryAccess *Load0Clobber = Walker->getClobberingMemoryAccess(ALoad0); - EXPECT_EQ(MSSA.getMemoryAccess(FirstStore), Load0Clobber); - - // If the bug exists, this will introduce a bad cache entry for %a on BStore. - // It will point to the store to %b after FirstStore. This only happens during - // phi optimization. - MemoryAccess *BottomClobber = Walker->getClobberingMemoryAccess(BelowPhi); - MemoryAccess *Phi = MSSA.getMemoryAccess(IfEnd); - EXPECT_EQ(BottomClobber, Phi); - - // This query will first check the cache for {%a, BStore}. It should point to - // FirstStore, not to the store after FirstStore. - MemoryAccess *UseClobber = Walker->getClobberingMemoryAccess(ALoad); - EXPECT_EQ(UseClobber, MSSA.getMemoryAccess(FirstStore)); -} - -// Test that our walker properly handles loads with the invariant group -// attribute. It's a bit hacky, since we add the invariant attribute *after* -// building MSSA. Otherwise, the use optimizer will optimize it for us, which -// isn't what we want. -// FIXME: It may be easier/cleaner to just add an 'optimize uses?' flag to MSSA. -TEST_F(MemorySSATest, WalkerInvariantLoadOpt) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "", F)); - Type *Int8 = Type::getInt8Ty(C); - Constant *One = ConstantInt::get(Int8, 1); - Value *AllocA = B.CreateAlloca(Int8, One, ""); - - Instruction *Store = B.CreateStore(One, AllocA); - Instruction *Load = B.CreateLoad(AllocA); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - auto *LoadMA = cast<MemoryUse>(MSSA.getMemoryAccess(Load)); - auto *StoreMA = cast<MemoryDef>(MSSA.getMemoryAccess(Store)); - EXPECT_EQ(LoadMA->getDefiningAccess(), StoreMA); - - // ...At the time of writing, no cache should exist for LoadMA. Be a bit - // flexible to future changes. - Walker->invalidateInfo(LoadMA); - Load->setMetadata(LLVMContext::MD_invariant_load, MDNode::get(C, {})); - - MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LoadMA); - EXPECT_EQ(LoadClobber, MSSA.getLiveOnEntryDef()); -} - -// Test loads get reoptimized properly by the walker. -TEST_F(MemorySSATest, WalkerReopt) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "", F)); - Type *Int8 = Type::getInt8Ty(C); - Value *AllocaA = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); - Instruction *SIA = B.CreateStore(ConstantInt::get(Int8, 0), AllocaA); - Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B"); - Instruction *SIB = B.CreateStore(ConstantInt::get(Int8, 0), AllocaB); - Instruction *LIA = B.CreateLoad(AllocaA); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker *Walker = Analyses->Walker; - - MemoryAccess *LoadClobber = Walker->getClobberingMemoryAccess(LIA); - MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LIA)); - EXPECT_EQ(LoadClobber, MSSA.getMemoryAccess(SIA)); - EXPECT_TRUE(MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(SIA))); - MSSA.removeMemoryAccess(LoadAccess); - - // Create the load memory access pointing to an unoptimized place. - MemoryUse *NewLoadAccess = cast<MemoryUse>(MSSA.createMemoryAccessInBB( - LIA, MSSA.getMemoryAccess(SIB), LIA->getParent(), MemorySSA::End)); - // This should it cause it to be optimized - EXPECT_EQ(Walker->getClobberingMemoryAccess(NewLoadAccess), LoadClobber); - EXPECT_EQ(NewLoadAccess->getDefiningAccess(), LoadClobber); -} - -// Test out MemorySSA::spliceMemoryAccessAbove. -TEST_F(MemorySSATest, SpliceAboveMemoryDef) { - F = Function::Create(FunctionType::get(B.getVoidTy(), {}, false), - GlobalValue::ExternalLinkage, "F", &M); - B.SetInsertPoint(BasicBlock::Create(C, "", F)); - - Type *Int8 = Type::getInt8Ty(C); - Value *A = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "A"); - Value *B_ = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B"); - Value *C = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "C"); - - StoreInst *StoreA0 = B.CreateStore(ConstantInt::get(Int8, 0), A); - StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 0), B_); - LoadInst *LoadB = B.CreateLoad(B_); - StoreInst *StoreA1 = B.CreateStore(ConstantInt::get(Int8, 4), A); - // splice this above StoreB - StoreInst *StoreC = B.CreateStore(ConstantInt::get(Int8, 4), C); - StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 4), A); - LoadInst *LoadC = B.CreateLoad(C); - - setupAnalyses(); - MemorySSA &MSSA = *Analyses->MSSA; - MemorySSAWalker &Walker = *Analyses->Walker; - - StoreC->moveBefore(StoreB); - MSSA.spliceMemoryAccessAbove(cast<MemoryDef>(MSSA.getMemoryAccess(StoreB)), - MSSA.getMemoryAccess(StoreC)); - - MSSA.verifyMemorySSA(); - - EXPECT_EQ(MSSA.getMemoryAccess(StoreB)->getDefiningAccess(), - MSSA.getMemoryAccess(StoreC)); - EXPECT_EQ(MSSA.getMemoryAccess(StoreC)->getDefiningAccess(), - MSSA.getMemoryAccess(StoreA0)); - EXPECT_EQ(MSSA.getMemoryAccess(StoreA2)->getDefiningAccess(), - MSSA.getMemoryAccess(StoreA1)); - EXPECT_EQ(Walker.getClobberingMemoryAccess(LoadB), - MSSA.getMemoryAccess(StoreB)); - EXPECT_EQ(Walker.getClobberingMemoryAccess(LoadC), - MSSA.getMemoryAccess(StoreC)); - - // exercise block numbering - EXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreC), - MSSA.getMemoryAccess(StoreB))); - EXPECT_TRUE(MSSA.locallyDominates(MSSA.getMemoryAccess(StoreA1), - MSSA.getMemoryAccess(StoreA2))); -} |