summaryrefslogtreecommitdiffstats
path: root/llvm/unittests/Analysis/MemorySSATest.cpp
diff options
context:
space:
mode:
authorNico Weber <nicolasweber@gmx.de>2018-09-03 12:43:26 +0000
committerNico Weber <nicolasweber@gmx.de>2018-09-03 12:43:26 +0000
commit8267b333ee9aaa7cedc6d8aab6ddf15629d9e255 (patch)
tree1e1efbcc60829d02625e8e6d5c4b149626be0eaf /llvm/unittests/Analysis/MemorySSATest.cpp
parentd8e7ed6457b1ecad3ec2dc41151092e5a637540c (diff)
downloadbcm5719-llvm-8267b333ee9aaa7cedc6d8aab6ddf15629d9e255.tar.gz
bcm5719-llvm-8267b333ee9aaa7cedc6d8aab6ddf15629d9e255.zip
Rename a few unittests/.../Foo.cpp files to FooTest.cpp
The convention for unit test sources is that they're called FooTest.cpp. No behavior change. https://reviews.llvm.org/D51579 llvm-svn: 341313
Diffstat (limited to 'llvm/unittests/Analysis/MemorySSATest.cpp')
-rw-r--r--llvm/unittests/Analysis/MemorySSATest.cpp1395
1 files changed, 1395 insertions, 0 deletions
diff --git a/llvm/unittests/Analysis/MemorySSATest.cpp b/llvm/unittests/Analysis/MemorySSATest.cpp
new file mode 100644
index 00000000000..76a49196ac3
--- /dev/null
+++ b/llvm/unittests/Analysis/MemorySSATest.cpp
@@ -0,0 +1,1395 @@
+//===- 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/Analysis/MemorySSA.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/BasicAliasAnalysis.h"
+#include "llvm/Analysis/MemorySSAUpdater.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.F, 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;
+ MemorySSAUpdater Updater(&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>(Updater.createMemoryAccessInBB(
+ LoadInst, MP, Merge, MemorySSA::Beginning));
+ MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();
+ EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));
+ MSSA.verifyMemorySSA();
+}
+TEST_F(MemorySSATest, CreateLoadsAndStoreUpdater) {
+ // We create a diamond, then build memoryssa with no memory accesses, and
+ // incrementally update it by inserting a store in the, entry, a load in the
+ // merge point, then a store in the branch, another load in the merge point,
+ // and then a store in the entry.
+ 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, Left->begin());
+ Argument *PointerArg = &*F->arg_begin();
+ B.SetInsertPoint(Left);
+ B.CreateBr(Merge);
+ B.SetInsertPoint(Right);
+ B.CreateBr(Merge);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+ // Add the store
+ B.SetInsertPoint(Entry, Entry->begin());
+ StoreInst *EntryStore = B.CreateStore(B.getInt8(16), PointerArg);
+ MemoryAccess *EntryStoreAccess = Updater.createMemoryAccessInBB(
+ EntryStore, nullptr, Entry, MemorySSA::Beginning);
+ Updater.insertDef(cast<MemoryDef>(EntryStoreAccess));
+
+ // Add the load
+ B.SetInsertPoint(Merge, Merge->begin());
+ LoadInst *FirstLoad = B.CreateLoad(PointerArg);
+
+ // MemoryPHI should not already exist.
+ MemoryPhi *MP = MSSA.getMemoryAccess(Merge);
+ EXPECT_EQ(MP, nullptr);
+
+ // Create the load memory access
+ MemoryUse *FirstLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(
+ FirstLoad, nullptr, Merge, MemorySSA::Beginning));
+ Updater.insertUse(FirstLoadAccess);
+ // Should just have a load using the entry access, because it should discover
+ // the phi is trivial
+ EXPECT_EQ(FirstLoadAccess->getDefiningAccess(), EntryStoreAccess);
+
+ // Create a store on the left
+ // Add the store
+ B.SetInsertPoint(Left, Left->begin());
+ StoreInst *LeftStore = B.CreateStore(B.getInt8(16), PointerArg);
+ MemoryAccess *LeftStoreAccess = Updater.createMemoryAccessInBB(
+ LeftStore, nullptr, Left, MemorySSA::Beginning);
+ Updater.insertDef(cast<MemoryDef>(LeftStoreAccess), false);
+ // We don't touch existing loads, so we need to create a new one to get a phi
+ // Add the second load
+ B.SetInsertPoint(Merge, Merge->begin());
+ LoadInst *SecondLoad = B.CreateLoad(PointerArg);
+
+ // MemoryPHI should not already exist.
+ MP = MSSA.getMemoryAccess(Merge);
+ EXPECT_EQ(MP, nullptr);
+
+ // Create the load memory access
+ MemoryUse *SecondLoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(
+ SecondLoad, nullptr, Merge, MemorySSA::Beginning));
+ Updater.insertUse(SecondLoadAccess);
+ // Now the load should be a phi of the entry store and the left store
+ MemoryPhi *MergePhi =
+ dyn_cast<MemoryPhi>(SecondLoadAccess->getDefiningAccess());
+ EXPECT_NE(MergePhi, nullptr);
+ EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), LeftStoreAccess);
+ // Now create a store below the existing one in the entry
+ B.SetInsertPoint(Entry, --Entry->end());
+ StoreInst *SecondEntryStore = B.CreateStore(B.getInt8(16), PointerArg);
+ MemoryAccess *SecondEntryStoreAccess = Updater.createMemoryAccessInBB(
+ SecondEntryStore, nullptr, Entry, MemorySSA::End);
+ // Insert it twice just to test renaming
+ Updater.insertDef(cast<MemoryDef>(SecondEntryStoreAccess), false);
+ EXPECT_NE(FirstLoadAccess->getDefiningAccess(), MergePhi);
+ Updater.insertDef(cast<MemoryDef>(SecondEntryStoreAccess), true);
+ EXPECT_EQ(FirstLoadAccess->getDefiningAccess(), MergePhi);
+ // and make sure the phi below it got updated, despite being blocks away
+ MergePhi = dyn_cast<MemoryPhi>(SecondLoadAccess->getDefiningAccess());
+ EXPECT_NE(MergePhi, nullptr);
+ EXPECT_EQ(MergePhi->getIncomingValue(0), SecondEntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), LeftStoreAccess);
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, CreateALoadUpdater) {
+ // We create a diamond, then build memoryssa with no memory accesses, and
+ // incrementally update it by inserting a store in one of the branches, and a
+ // load in the merge point
+ 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, Left->begin());
+ Argument *PointerArg = &*F->arg_begin();
+ B.SetInsertPoint(Left);
+ B.CreateBr(Merge);
+ B.SetInsertPoint(Right);
+ B.CreateBr(Merge);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+ B.SetInsertPoint(Left, Left->begin());
+ // Add the store
+ StoreInst *SI = B.CreateStore(B.getInt8(16), PointerArg);
+ MemoryAccess *StoreAccess =
+ Updater.createMemoryAccessInBB(SI, nullptr, Left, MemorySSA::Beginning);
+ Updater.insertDef(cast<MemoryDef>(StoreAccess));
+
+ // Add the load
+ B.SetInsertPoint(Merge, Merge->begin());
+ LoadInst *LoadInst = B.CreateLoad(PointerArg);
+
+ // MemoryPHI should not already exist.
+ MemoryPhi *MP = MSSA.getMemoryAccess(Merge);
+ EXPECT_EQ(MP, nullptr);
+
+ // Create the load memory acccess
+ MemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(
+ LoadInst, nullptr, Merge, MemorySSA::Beginning));
+ Updater.insertUse(LoadAccess);
+ MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess();
+ EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess));
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, SinkLoad) {
+ 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, Left->begin());
+ Argument *PointerArg = &*F->arg_begin();
+ B.SetInsertPoint(Left);
+ B.CreateBr(Merge);
+ B.SetInsertPoint(Right);
+ B.CreateBr(Merge);
+
+ // Load in left block
+ B.SetInsertPoint(Left, Left->begin());
+ LoadInst *LoadInst1 = B.CreateLoad(PointerArg);
+ // Store in merge block
+ B.SetInsertPoint(Merge, Merge->begin());
+ B.CreateStore(B.getInt8(16), PointerArg);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+
+ // Mimic sinking of a load:
+ // - clone load
+ // - insert in "exit" block
+ // - insert in mssa
+ // - remove from original block
+
+ LoadInst *LoadInstClone = cast<LoadInst>(LoadInst1->clone());
+ Merge->getInstList().insert(Merge->begin(), LoadInstClone);
+ MemoryAccess * NewLoadAccess =
+ Updater.createMemoryAccessInBB(LoadInstClone, nullptr,
+ LoadInstClone->getParent(),
+ MemorySSA::Beginning);
+ Updater.insertUse(cast<MemoryUse>(NewLoadAccess));
+ MSSA.verifyMemorySSA();
+ Updater.removeMemoryAccess(MSSA.getMemoryAccess(LoadInst1));
+ 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. This destroys the old
+ // access.
+ 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;
+ MemorySSAUpdater Updater(&MSSA);
+ // Move the store
+ SideStore->moveBefore(Entry->getTerminator());
+ MemoryAccess *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);
+ MemoryAccess *SideStoreAccess = MSSA.getMemoryAccess(SideStore);
+ MemoryAccess *NewStoreAccess = Updater.createMemoryAccessAfter(
+ SideStore, EntryStoreAccess, EntryStoreAccess);
+ EntryStoreAccess->replaceAllUsesWith(NewStoreAccess);
+ Updater.removeMemoryAccess(SideStoreAccess);
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, MoveAStoreUpdater) {
+ // 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. This destroys the old
+ // access.
+ 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);
+ auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg);
+ BranchInst::Create(Merge, Left);
+ BranchInst::Create(Merge, Right);
+ B.SetInsertPoint(Merge);
+ auto *MergeLoad = B.CreateLoad(PointerArg);
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+
+ // Move the store
+ SideStore->moveBefore(Entry->getTerminator());
+ auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);
+ auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore);
+ auto *NewStoreAccess = Updater.createMemoryAccessAfter(
+ SideStore, EntryStoreAccess, EntryStoreAccess);
+ // Before, the load will point to a phi of the EntryStore and SideStore.
+ auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad));
+ EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess()));
+ MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess());
+ EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);
+ Updater.removeMemoryAccess(SideStoreAccess);
+ Updater.insertDef(cast<MemoryDef>(NewStoreAccess));
+ // After it's a phi of the new side store access.
+ EXPECT_EQ(MergePhi->getIncomingValue(0), NewStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), NewStoreAccess);
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, MoveAStoreUpdaterMove) {
+ // 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. This does not destroy
+ // the old access.
+ 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);
+ auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg);
+ BranchInst::Create(Merge, Left);
+ BranchInst::Create(Merge, Right);
+ B.SetInsertPoint(Merge);
+ auto *MergeLoad = B.CreateLoad(PointerArg);
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+
+ // Move the store
+ auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);
+ auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore);
+ // Before, the load will point to a phi of the EntryStore and SideStore.
+ auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad));
+ EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess()));
+ MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess());
+ EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);
+ SideStore->moveBefore(*EntryStore->getParent(), ++EntryStore->getIterator());
+ Updater.moveAfter(SideStoreAccess, EntryStoreAccess);
+ // After, it's a phi of the side store.
+ EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), SideStoreAccess);
+
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, MoveAStoreAllAround) {
+ // 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, then to the other side
+ // block, then to before the load. This does not destroy the old access.
+ 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);
+ auto *SideStore = B.CreateStore(B.getInt8(16), PointerArg);
+ BranchInst::Create(Merge, Left);
+ BranchInst::Create(Merge, Right);
+ B.SetInsertPoint(Merge);
+ auto *MergeLoad = B.CreateLoad(PointerArg);
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+
+ // Move the store
+ auto *EntryStoreAccess = MSSA.getMemoryAccess(EntryStore);
+ auto *SideStoreAccess = MSSA.getMemoryAccess(SideStore);
+ // Before, the load will point to a phi of the EntryStore and SideStore.
+ auto *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(MergeLoad));
+ EXPECT_TRUE(isa<MemoryPhi>(LoadAccess->getDefiningAccess()));
+ MemoryPhi *MergePhi = cast<MemoryPhi>(LoadAccess->getDefiningAccess());
+ EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(0), SideStoreAccess);
+ // Move the store before the entry store
+ SideStore->moveBefore(*EntryStore->getParent(), EntryStore->getIterator());
+ Updater.moveBefore(SideStoreAccess, EntryStoreAccess);
+ // After, it's a phi of the entry store.
+ EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);
+ MSSA.verifyMemorySSA();
+ // Now move the store to the right branch
+ SideStore->moveBefore(*Right, Right->begin());
+ Updater.moveToPlace(SideStoreAccess, Right, MemorySSA::Beginning);
+ MSSA.verifyMemorySSA();
+ EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), SideStoreAccess);
+ // Now move it before the load
+ SideStore->moveBefore(MergeLoad);
+ Updater.moveBefore(SideStoreAccess, LoadAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(0), EntryStoreAccess);
+ EXPECT_EQ(MergePhi->getIncomingValue(1), EntryStoreAccess);
+ 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;
+ MemorySSAUpdater Updater(&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
+ Updater.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
+ Updater.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;
+ MemorySSAUpdater Updater(&MSSA);
+
+ // 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));
+ Updater.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
+ Updater.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;
+ MemorySSAUpdater Updater(&MSSA);
+
+ // Kill `KillStore`; it exists solely so that the load after it won't be
+ // optimized to FirstStore.
+ Updater.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;
+ MemorySSAUpdater Updater(&MSSA);
+
+ 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)));
+ Updater.removeMemoryAccess(LoadAccess);
+
+ // Create the load memory access pointing to an unoptimized place.
+ MemoryUse *NewLoadAccess = cast<MemoryUse>(Updater.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 MemorySSAUpdater::moveBefore
+TEST_F(MemorySSATest, MoveAboveMemoryDef) {
+ 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);
+ 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;
+
+ MemorySSAUpdater Updater(&MSSA);
+ StoreC->moveBefore(StoreB);
+ Updater.moveBefore(cast<MemoryDef>(MSSA.getMemoryAccess(StoreC)),
+ cast<MemoryDef>(MSSA.getMemoryAccess(StoreB)));
+
+ 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)));
+}
+
+TEST_F(MemorySSATest, Irreducible) {
+ // Create the equivalent of
+ // x = something
+ // if (...)
+ // goto second_loop_entry
+ // while (...) {
+ // second_loop_entry:
+ // }
+ // use(x)
+
+ SmallVector<PHINode *, 8> Inserted;
+ IRBuilder<> B(C);
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ // Make blocks
+ BasicBlock *IfBB = BasicBlock::Create(C, "if", F);
+ BasicBlock *LoopStartBB = BasicBlock::Create(C, "loopstart", F);
+ BasicBlock *LoopMainBB = BasicBlock::Create(C, "loopmain", F);
+ BasicBlock *AfterLoopBB = BasicBlock::Create(C, "afterloop", F);
+ B.SetInsertPoint(IfBB);
+ B.CreateCondBr(B.getTrue(), LoopMainBB, LoopStartBB);
+ B.SetInsertPoint(LoopStartBB);
+ B.CreateBr(LoopMainBB);
+ B.SetInsertPoint(LoopMainBB);
+ B.CreateCondBr(B.getTrue(), LoopStartBB, AfterLoopBB);
+ B.SetInsertPoint(AfterLoopBB);
+ Argument *FirstArg = &*F->arg_begin();
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAUpdater Updater(&MSSA);
+ // Create the load memory acccess
+ LoadInst *LoadInst = B.CreateLoad(FirstArg);
+ MemoryUse *LoadAccess = cast<MemoryUse>(Updater.createMemoryAccessInBB(
+ LoadInst, nullptr, AfterLoopBB, MemorySSA::Beginning));
+ Updater.insertUse(LoadAccess);
+ MSSA.verifyMemorySSA();
+}
+
+TEST_F(MemorySSATest, MoveToBeforeLiveOnEntryInvalidatesCache) {
+ // Create:
+ // %1 = alloca i8
+ // ; 1 = MemoryDef(liveOnEntry)
+ // store i8 0, i8* %1
+ // ; 2 = MemoryDef(1)
+ // store i8 0, i8* %1
+ //
+ // ...And be sure that MSSA's caching doesn't give us `1` for the clobber of
+ // `2` after `1` is removed.
+ IRBuilder<> B(C);
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ BasicBlock *Entry = BasicBlock::Create(C, "if", F);
+ B.SetInsertPoint(Entry);
+
+ Value *A = B.CreateAlloca(B.getInt8Ty());
+ StoreInst *StoreA = B.CreateStore(B.getInt8(0), A);
+ StoreInst *StoreB = B.CreateStore(B.getInt8(0), A);
+
+ setupAnalyses();
+
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ auto *DefA = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));
+ auto *DefB = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));
+
+ MemoryAccess *BClobber = MSSA.getWalker()->getClobberingMemoryAccess(DefB);
+ ASSERT_EQ(DefA, BClobber);
+
+ MemorySSAUpdater(&MSSA).removeMemoryAccess(DefA);
+ StoreA->eraseFromParent();
+
+ EXPECT_EQ(DefB->getDefiningAccess(), MSSA.getLiveOnEntryDef());
+
+ EXPECT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(DefB),
+ MSSA.getLiveOnEntryDef())
+ << "(DefA = " << DefA << ")";
+}
+
+TEST_F(MemorySSATest, RemovingDefInvalidatesCache) {
+ // Create:
+ // %x = alloca i8
+ // %y = alloca i8
+ // ; 1 = MemoryDef(liveOnEntry)
+ // store i8 0, i8* %x
+ // ; 2 = MemoryDef(1)
+ // store i8 0, i8* %y
+ // ; 3 = MemoryDef(2)
+ // store i8 0, i8* %x
+ //
+ // And be sure that MSSA's caching handles the removal of def `1`
+ // appropriately.
+ IRBuilder<> B(C);
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ BasicBlock *Entry = BasicBlock::Create(C, "if", F);
+ B.SetInsertPoint(Entry);
+
+ Value *X = B.CreateAlloca(B.getInt8Ty());
+ Value *Y = B.CreateAlloca(B.getInt8Ty());
+ StoreInst *StoreX1 = B.CreateStore(B.getInt8(0), X);
+ StoreInst *StoreY = B.CreateStore(B.getInt8(0), Y);
+ StoreInst *StoreX2 = B.CreateStore(B.getInt8(0), X);
+
+ setupAnalyses();
+
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ auto *DefX1 = cast<MemoryDef>(MSSA.getMemoryAccess(StoreX1));
+ auto *DefY = cast<MemoryDef>(MSSA.getMemoryAccess(StoreY));
+ auto *DefX2 = cast<MemoryDef>(MSSA.getMemoryAccess(StoreX2));
+
+ EXPECT_EQ(DefX2->getDefiningAccess(), DefY);
+ MemoryAccess *X2Clobber = MSSA.getWalker()->getClobberingMemoryAccess(DefX2);
+ ASSERT_EQ(DefX1, X2Clobber);
+
+ MemorySSAUpdater(&MSSA).removeMemoryAccess(DefX1);
+ StoreX1->eraseFromParent();
+
+ EXPECT_EQ(DefX2->getDefiningAccess(), DefY);
+ EXPECT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(DefX2),
+ MSSA.getLiveOnEntryDef())
+ << "(DefX1 = " << DefX1 << ")";
+}
+
+// Test Must alias for optimized uses
+TEST_F(MemorySSATest, TestLoadMustAlias) {
+ 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");
+ Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");
+
+ B.CreateStore(ConstantInt::get(Int8, 1), AllocaB);
+ // Check load from LOE
+ LoadInst *LA1 = B.CreateLoad(AllocaA, "");
+ // Check load alias cached for second load
+ LoadInst *LA2 = B.CreateLoad(AllocaA, "");
+
+ B.CreateStore(ConstantInt::get(Int8, 1), AllocaA);
+ // Check load from store/def
+ LoadInst *LA3 = B.CreateLoad(AllocaA, "");
+ // Check load alias cached for second load
+ LoadInst *LA4 = B.CreateLoad(AllocaA, "");
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ unsigned I = 0;
+ for (LoadInst *V : {LA1, LA2}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), None)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+ for (LoadInst *V : {LA3, LA4}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), MustAlias)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+// Test Must alias for optimized defs.
+TEST_F(MemorySSATest, TestStoreMustAlias) {
+ 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");
+ Value *AllocaB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");
+ StoreInst *SA1 = B.CreateStore(ConstantInt::get(Int8, 1), AllocaA);
+ StoreInst *SB1 = B.CreateStore(ConstantInt::get(Int8, 1), AllocaB);
+ StoreInst *SA2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaA);
+ StoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaB);
+ StoreInst *SA3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaA);
+ StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 3), AllocaB);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ unsigned I = 0;
+ for (StoreInst *V : {SA1, SB1, SA2, SB2, SA3, SB3}) {
+ MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemDef->isOptimized(), false)
+ << "Store " << I << " is optimized from the start?";
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MayAlias)
+ << "Store " << I
+ << " has correct alias information before being optimized?";
+ if (V == SA1)
+ Walker->getClobberingMemoryAccess(V);
+ else {
+ MemoryAccess *Def = MemDef->getDefiningAccess();
+ MemoryAccess *Clob = Walker->getClobberingMemoryAccess(V);
+ EXPECT_NE(Def, Clob) << "Store " << I
+ << " has Defining Access equal to Clobbering Access";
+ }
+ EXPECT_EQ(MemDef->isOptimized(), true)
+ << "Store " << I << " was not optimized";
+ if (I == 0 || I == 1)
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), None)
+ << "Store " << I << " doesn't have the correct alias information";
+ else
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MustAlias)
+ << "Store " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+// Test May alias for optimized uses.
+TEST_F(MemorySSATest, TestLoadMayAlias) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(),
+ {B.getInt8PtrTy(), B.getInt8PtrTy()},
+ false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ auto *ArgIt = F->arg_begin();
+ Argument *PointerA = &*ArgIt;
+ Argument *PointerB = &*(++ArgIt);
+ B.CreateStore(ConstantInt::get(Int8, 1), PointerB);
+ LoadInst *LA1 = B.CreateLoad(PointerA, "");
+ B.CreateStore(ConstantInt::get(Int8, 0), PointerA);
+ LoadInst *LB1 = B.CreateLoad(PointerB, "");
+ B.CreateStore(ConstantInt::get(Int8, 0), PointerA);
+ LoadInst *LA2 = B.CreateLoad(PointerA, "");
+ B.CreateStore(ConstantInt::get(Int8, 0), PointerB);
+ LoadInst *LB2 = B.CreateLoad(PointerB, "");
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ unsigned I = 0;
+ for (LoadInst *V : {LA1, LB1}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), MayAlias)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+ for (LoadInst *V : {LA2, LB2}) {
+ MemoryUse *MemUse = dyn_cast_or_null<MemoryUse>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemUse->getOptimizedAccessType(), MustAlias)
+ << "Load " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+// Test May alias for optimized defs.
+TEST_F(MemorySSATest, TestStoreMayAlias) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(),
+ {B.getInt8PtrTy(), B.getInt8PtrTy()},
+ false),
+ GlobalValue::ExternalLinkage, "F", &M);
+ B.SetInsertPoint(BasicBlock::Create(C, "", F));
+ Type *Int8 = Type::getInt8Ty(C);
+ auto *ArgIt = F->arg_begin();
+ Argument *PointerA = &*ArgIt;
+ Argument *PointerB = &*(++ArgIt);
+ Value *AllocaC = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "C");
+ // Store into arg1, must alias because it's LOE => must
+ StoreInst *SA1 = B.CreateStore(ConstantInt::get(Int8, 0), PointerA);
+ // Store into arg2, may alias store to arg1 => may
+ StoreInst *SB1 = B.CreateStore(ConstantInt::get(Int8, 1), PointerB);
+ // Store into aloca, no alias with args, so must alias LOE => must
+ StoreInst *SC1 = B.CreateStore(ConstantInt::get(Int8, 2), AllocaC);
+ // Store into arg1, may alias store to arg2 => may
+ StoreInst *SA2 = B.CreateStore(ConstantInt::get(Int8, 3), PointerA);
+ // Store into arg2, may alias store to arg1 => may
+ StoreInst *SB2 = B.CreateStore(ConstantInt::get(Int8, 4), PointerB);
+ // Store into aloca, no alias with args, so must alias SC1 => must
+ StoreInst *SC2 = B.CreateStore(ConstantInt::get(Int8, 5), AllocaC);
+ // Store into arg2, must alias store to arg2 => must
+ StoreInst *SB3 = B.CreateStore(ConstantInt::get(Int8, 6), PointerB);
+ std::initializer_list<StoreInst *> Sts = {SA1, SB1, SC1, SA2, SB2, SC2, SB3};
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ unsigned I = 0;
+ for (StoreInst *V : Sts) {
+ MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemDef->isOptimized(), false)
+ << "Store " << I << " is optimized from the start?";
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MayAlias)
+ << "Store " << I
+ << " has correct alias information before being optimized?";
+ ++I;
+ }
+
+ for (StoreInst *V : Sts)
+ Walker->getClobberingMemoryAccess(V);
+
+ I = 0;
+ for (StoreInst *V : Sts) {
+ MemoryDef *MemDef = dyn_cast_or_null<MemoryDef>(MSSA.getMemoryAccess(V));
+ EXPECT_EQ(MemDef->isOptimized(), true)
+ << "Store " << I << " was not optimized";
+ if (I == 1 || I == 3 || I == 4)
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MayAlias)
+ << "Store " << I << " doesn't have the correct alias information";
+ else if (I == 0 || I == 2)
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), None)
+ << "Store " << I << " doesn't have the correct alias information";
+ else
+ EXPECT_EQ(MemDef->getOptimizedAccessType(), MustAlias)
+ << "Store " << I << " doesn't have the correct alias information";
+ // 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;
+ }
+}
+
+TEST_F(MemorySSATest, LifetimeMarkersAreClobbers) {
+ // Example code:
+ // define void @a(i8* %foo) {
+ // %bar = getelementptr i8, i8* %foo, i64 1
+ // store i8 0, i8* %foo
+ // store i8 0, i8* %bar
+ // call void @llvm.lifetime.end.p0i8(i64 8, i32* %p)
+ // call void @llvm.lifetime.start.p0i8(i64 8, i32* %p)
+ // store i8 0, i8* %foo
+ // store i8 0, i8* %bar
+ // ret void
+ // }
+ //
+ // Patterns like this are possible after inlining; the stores to %foo and %bar
+ // should both be clobbered by the lifetime.start call if they're dominated by
+ // it.
+
+ IRBuilder<> B(C);
+ F = Function::Create(
+ FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ // Make blocks
+ BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
+
+ B.SetInsertPoint(Entry);
+ Value *Foo = &*F->arg_begin();
+
+ Value *Bar = B.CreateGEP(Foo, B.getInt64(1), "bar");
+
+ B.CreateStore(B.getInt8(0), Foo);
+ B.CreateStore(B.getInt8(0), Bar);
+
+ auto GetLifetimeIntrinsic = [&](Intrinsic::ID ID) {
+ return Intrinsic::getDeclaration(&M, ID, {Foo->getType()});
+ };
+
+ B.CreateCall(GetLifetimeIntrinsic(Intrinsic::lifetime_end),
+ {B.getInt64(2), Foo});
+ Instruction *LifetimeStart = B.CreateCall(
+ GetLifetimeIntrinsic(Intrinsic::lifetime_start), {B.getInt64(2), Foo});
+
+ Instruction *FooStore = B.CreateStore(B.getInt8(0), Foo);
+ Instruction *BarStore = B.CreateStore(B.getInt8(0), Bar);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ MemoryAccess *LifetimeStartAccess = MSSA.getMemoryAccess(LifetimeStart);
+ ASSERT_NE(LifetimeStartAccess, nullptr);
+
+ MemoryAccess *FooAccess = MSSA.getMemoryAccess(FooStore);
+ ASSERT_NE(FooAccess, nullptr);
+
+ MemoryAccess *BarAccess = MSSA.getMemoryAccess(BarStore);
+ ASSERT_NE(BarAccess, nullptr);
+
+ MemoryAccess *FooClobber =
+ MSSA.getWalker()->getClobberingMemoryAccess(FooAccess);
+ EXPECT_EQ(FooClobber, LifetimeStartAccess);
+
+ MemoryAccess *BarClobber =
+ MSSA.getWalker()->getClobberingMemoryAccess(BarAccess);
+ EXPECT_EQ(BarClobber, LifetimeStartAccess);
+}
+
+TEST_F(MemorySSATest, DefOptimizationsAreInvalidatedOnMoving) {
+ IRBuilder<> B(C);
+ F = Function::Create(FunctionType::get(B.getVoidTy(), {B.getInt1Ty()}, false),
+ GlobalValue::ExternalLinkage, "F", &M);
+
+ // Make a CFG like
+ // entry
+ // / \
+ // a b
+ // \ /
+ // c
+ //
+ // Put a def in A and a def in B, move the def from A -> B, observe as the
+ // optimization is invalidated.
+ BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
+ BasicBlock *BlockA = BasicBlock::Create(C, "a", F);
+ BasicBlock *BlockB = BasicBlock::Create(C, "b", F);
+ BasicBlock *BlockC = BasicBlock::Create(C, "c", F);
+
+ B.SetInsertPoint(Entry);
+ Type *Int8 = Type::getInt8Ty(C);
+ Value *Alloca = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "alloc");
+ StoreInst *StoreEntry = B.CreateStore(B.getInt8(0), Alloca);
+ B.CreateCondBr(B.getTrue(), BlockA, BlockB);
+
+ B.SetInsertPoint(BlockA);
+ StoreInst *StoreA = B.CreateStore(B.getInt8(1), Alloca);
+ B.CreateBr(BlockC);
+
+ B.SetInsertPoint(BlockB);
+ StoreInst *StoreB = B.CreateStore(B.getInt8(2), Alloca);
+ B.CreateBr(BlockC);
+
+ B.SetInsertPoint(BlockC);
+ B.CreateUnreachable();
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+
+ auto *AccessEntry = cast<MemoryDef>(MSSA.getMemoryAccess(StoreEntry));
+ auto *StoreAEntry = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));
+ auto *StoreBEntry = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));
+
+ ASSERT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(StoreAEntry),
+ AccessEntry);
+ ASSERT_TRUE(StoreAEntry->isOptimized());
+
+ ASSERT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(StoreBEntry),
+ AccessEntry);
+ ASSERT_TRUE(StoreBEntry->isOptimized());
+
+ // Note that if we did InsertionPlace::Beginning, we don't go out of our way
+ // to invalidate the cache for StoreBEntry. If the user wants to actually do
+ // moves like these, it's up to them to ensure that nearby cache entries are
+ // correctly invalidated (which, in general, requires walking all instructions
+ // that the moved instruction dominates. So we probably shouldn't be doing
+ // moves like this in general. Still, works as a test-case. ;) )
+ MemorySSAUpdater(&MSSA).moveToPlace(StoreAEntry, BlockB,
+ MemorySSA::InsertionPlace::End);
+ ASSERT_FALSE(StoreAEntry->isOptimized());
+ ASSERT_EQ(MSSA.getWalker()->getClobberingMemoryAccess(StoreAEntry),
+ StoreBEntry);
+}
+
+TEST_F(MemorySSATest, TestOptimizedDefsAreProperUses) {
+ F = Function::Create(FunctionType::get(B.getVoidTy(),
+ {B.getInt8PtrTy(), B.getInt8PtrTy()},
+ 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");
+ Value *AllocB = B.CreateAlloca(Int8, ConstantInt::get(Int8, 1), "B");
+
+ StoreInst *StoreA = B.CreateStore(ConstantInt::get(Int8, 0), AllocA);
+ StoreInst *StoreB = B.CreateStore(ConstantInt::get(Int8, 1), AllocB);
+ StoreInst *StoreA2 = B.CreateStore(ConstantInt::get(Int8, 2), AllocA);
+
+ setupAnalyses();
+ MemorySSA &MSSA = *Analyses->MSSA;
+ MemorySSAWalker *Walker = Analyses->Walker;
+
+ // If these don't hold, there's no chance of the test result being useful.
+ ASSERT_EQ(Walker->getClobberingMemoryAccess(StoreA),
+ MSSA.getLiveOnEntryDef());
+ ASSERT_EQ(Walker->getClobberingMemoryAccess(StoreB),
+ MSSA.getLiveOnEntryDef());
+ auto *StoreAAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA));
+ auto *StoreA2Access = cast<MemoryDef>(MSSA.getMemoryAccess(StoreA2));
+ ASSERT_EQ(Walker->getClobberingMemoryAccess(StoreA2), StoreAAccess);
+ ASSERT_EQ(StoreA2Access->getOptimized(), StoreAAccess);
+
+ auto *StoreBAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreB));
+ ASSERT_LT(StoreAAccess->getID(), StoreBAccess->getID());
+ ASSERT_LT(StoreBAccess->getID(), StoreA2Access->getID());
+
+ auto SortVecByID = [](std::vector<const MemoryDef *> &Defs) {
+ llvm::sort(Defs.begin(), Defs.end(),
+ [](const MemoryDef *LHS, const MemoryDef *RHS) {
+ return LHS->getID() < RHS->getID();
+ });
+ };
+
+ auto SortedUserList = [&](const MemoryDef *MD) {
+ std::vector<const MemoryDef *> Result;
+ transform(MD->users(), std::back_inserter(Result),
+ [](const User *U) { return cast<MemoryDef>(U); });
+ SortVecByID(Result);
+ return Result;
+ };
+
+ // Use std::vectors, since they have nice pretty-printing if the test fails.
+ // Parens are necessary because EXPECT_EQ is a macro, and we have commas in
+ // our init lists...
+ EXPECT_EQ(SortedUserList(StoreAAccess),
+ (std::vector<const MemoryDef *>{StoreBAccess, StoreA2Access}));
+
+ EXPECT_EQ(SortedUserList(StoreBAccess),
+ std::vector<const MemoryDef *>{StoreA2Access});
+
+ // StoreAAccess should be present twice, since it uses liveOnEntry for both
+ // its defining and optimized accesses. This is a bit awkward, and is not
+ // relied upon anywhere at the moment. If this is painful, we can fix it.
+ EXPECT_EQ(SortedUserList(cast<MemoryDef>(MSSA.getLiveOnEntryDef())),
+ (std::vector<const MemoryDef *>{StoreAAccess, StoreAAccess,
+ StoreBAccess}));
+}
OpenPOWER on IntegriCloud