summaryrefslogtreecommitdiff
path: root/unittests/Transforms/Utils
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/Transforms/Utils')
-rw-r--r--unittests/Transforms/Utils/CMakeLists.txt2
-rw-r--r--unittests/Transforms/Utils/Cloning.cpp61
-rw-r--r--unittests/Transforms/Utils/IntegerDivision.cpp16
-rw-r--r--unittests/Transforms/Utils/Local.cpp4
-rw-r--r--unittests/Transforms/Utils/Makefile15
-rw-r--r--unittests/Transforms/Utils/MemorySSA.cpp310
-rw-r--r--unittests/Transforms/Utils/ValueMapperTest.cpp306
7 files changed, 657 insertions, 57 deletions
diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt
index 517ff99ea46bc..657d151048e81 100644
--- a/unittests/Transforms/Utils/CMakeLists.txt
+++ b/unittests/Transforms/Utils/CMakeLists.txt
@@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
+ Analysis
Core
Support
TransformUtils
@@ -9,5 +10,6 @@ add_llvm_unittest(UtilsTests
Cloning.cpp
IntegerDivision.cpp
Local.cpp
+ MemorySSA.cpp
ValueMapperTest.cpp
)
diff --git a/unittests/Transforms/Utils/Cloning.cpp b/unittests/Transforms/Utils/Cloning.cpp
index 25e322ee5a8e8..f53e0a95e94dc 100644
--- a/unittests/Transforms/Utils/Cloning.cpp
+++ b/unittests/Transforms/Utils/Cloning.cpp
@@ -8,7 +8,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/Cloning.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/Argument.h"
@@ -275,8 +274,7 @@ protected:
void CreateNewFunc() {
ValueToValueMapTy VMap;
- NewFunc = CloneFunction(OldFunc, VMap, true, nullptr);
- M->getFunctionList().push_back(NewFunc);
+ NewFunc = CloneFunction(OldFunc, VMap, nullptr);
}
void SetupFinder() {
@@ -302,31 +300,13 @@ TEST_F(CloneFunc, Subprogram) {
EXPECT_FALSE(verifyModule(*M));
unsigned SubprogramCount = Finder->subprogram_count();
- EXPECT_EQ(2U, SubprogramCount);
+ EXPECT_EQ(1U, SubprogramCount);
auto Iter = Finder->subprograms().begin();
- auto *Sub1 = cast<DISubprogram>(*Iter);
- Iter++;
- auto *Sub2 = cast<DISubprogram>(*Iter);
+ auto *Sub = cast<DISubprogram>(*Iter);
- EXPECT_TRUE(
- (Sub1 == OldFunc->getSubprogram() && Sub2 == NewFunc->getSubprogram()) ||
- (Sub1 == NewFunc->getSubprogram() && Sub2 == OldFunc->getSubprogram()));
-}
-
-// Test that the new subprogram entry was not added to the CU which doesn't
-// contain the old subprogram entry.
-TEST_F(CloneFunc, SubprogramInRightCU) {
- EXPECT_FALSE(verifyModule(*M));
-
- EXPECT_EQ(2U, Finder->compile_unit_count());
-
- auto Iter = Finder->compile_units().begin();
- auto *CU1 = cast<DICompileUnit>(*Iter);
- Iter++;
- auto *CU2 = cast<DICompileUnit>(*Iter);
- EXPECT_TRUE(CU1->getSubprograms().size() == 0 ||
- CU2->getSubprograms().size() == 0);
+ EXPECT_TRUE(Sub == OldFunc->getSubprogram());
+ EXPECT_TRUE(Sub == NewFunc->getSubprogram());
}
// Test that instructions in the old function still belong to it in the
@@ -423,6 +403,7 @@ protected:
void SetupModule() { OldM = new Module("", C); }
void CreateOldModule() {
+ DIBuilder DBuilder(*OldM);
IRBuilder<> IBuilder(C);
auto *FuncType = FunctionType::get(Type::getVoidTy(C), false);
@@ -431,9 +412,25 @@ protected:
auto *F =
Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM);
F->setPersonalityFn(PersFn);
+
+ // Create debug info
+ auto *File = DBuilder.createFile("filename.c", "/file/dir/");
+ DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None);
+ DISubroutineType *DFuncType = DBuilder.createSubroutineType(ParamTypes);
+ auto *CU =
+ DBuilder.createCompileUnit(dwarf::DW_LANG_C99, "filename.c",
+ "/file/dir", "CloneModule", false, "", 0);
+ // Function DI
+ auto *Subprogram = DBuilder.createFunction(CU, "f", "f", File, 4, DFuncType,
+ true, true, 3, 0, false);
+ F->setSubprogram(Subprogram);
+
auto *Entry = BasicBlock::Create(C, "", F);
IBuilder.SetInsertPoint(Entry);
IBuilder.CreateRetVoid();
+
+ // Finalize the debug info
+ DBuilder.finalize();
}
void CreateNewModule() { NewM = llvm::CloneModule(OldM).release(); }
@@ -447,4 +444,18 @@ TEST_F(CloneModule, Verify) {
EXPECT_FALSE(verifyModule(*NewM));
}
+TEST_F(CloneModule, OldModuleUnchanged) {
+ DebugInfoFinder Finder;
+ Finder.processModule(*OldM);
+ EXPECT_EQ(1U, Finder.subprogram_count());
+}
+
+TEST_F(CloneModule, Subprogram) {
+ Function *NewF = NewM->getFunction("f");
+ DISubprogram *SP = NewF->getSubprogram();
+ EXPECT_TRUE(SP != nullptr);
+ EXPECT_EQ(SP->getName(), "f");
+ EXPECT_EQ(SP->getFile()->getFilename(), "filename.c");
+ EXPECT_EQ(SP->getLine(), (unsigned)4);
+}
}
diff --git a/unittests/Transforms/Utils/IntegerDivision.cpp b/unittests/Transforms/Utils/IntegerDivision.cpp
index 4cda2b4e58927..b6b1b1665ab1f 100644
--- a/unittests/Transforms/Utils/IntegerDivision.cpp
+++ b/unittests/Transforms/Utils/IntegerDivision.cpp
@@ -21,7 +21,7 @@ namespace {
TEST(IntegerDivision, SDiv) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -51,7 +51,7 @@ TEST(IntegerDivision, SDiv) {
}
TEST(IntegerDivision, UDiv) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -81,7 +81,7 @@ TEST(IntegerDivision, UDiv) {
}
TEST(IntegerDivision, SRem) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
@@ -111,7 +111,7 @@ TEST(IntegerDivision, SRem) {
}
TEST(IntegerDivision, URem) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
@@ -142,7 +142,7 @@ TEST(IntegerDivision, URem) {
TEST(IntegerDivision, SDiv64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -172,7 +172,7 @@ TEST(IntegerDivision, SDiv64) {
}
TEST(IntegerDivision, UDiv64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test division", C);
IRBuilder<> Builder(C);
@@ -202,7 +202,7 @@ TEST(IntegerDivision, UDiv64) {
}
TEST(IntegerDivision, SRem64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
@@ -232,7 +232,7 @@ TEST(IntegerDivision, SRem64) {
}
TEST(IntegerDivision, URem64) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
Module M("test remainder", C);
IRBuilder<> Builder(C);
diff --git a/unittests/Transforms/Utils/Local.cpp b/unittests/Transforms/Utils/Local.cpp
index 2ff5604755516..5164bdbb2a4ef 100644
--- a/unittests/Transforms/Utils/Local.cpp
+++ b/unittests/Transforms/Utils/Local.cpp
@@ -17,7 +17,7 @@
using namespace llvm;
TEST(Local, RecursivelyDeleteDeadPHINodes) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
IRBuilder<> builder(C);
@@ -60,7 +60,7 @@ TEST(Local, RecursivelyDeleteDeadPHINodes) {
}
TEST(Local, RemoveDuplicatePHINodes) {
- LLVMContext &C(getGlobalContext());
+ LLVMContext C;
IRBuilder<> B(C);
std::unique_ptr<Function> F(
diff --git a/unittests/Transforms/Utils/Makefile b/unittests/Transforms/Utils/Makefile
deleted file mode 100644
index e6c2a2c133aaa..0000000000000
--- a/unittests/Transforms/Utils/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Transforms/Utils/Makefile -----------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-TESTNAME = Utils
-LINK_COMPONENTS := TransformUtils
-
-include $(LEVEL)/Makefile.config
-include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
diff --git a/unittests/Transforms/Utils/MemorySSA.cpp b/unittests/Transforms/Utils/MemorySSA.cpp
new file mode 100644
index 0000000000000..c21121f78705f
--- /dev/null
+++ b/unittests/Transforms/Utils/MemorySSA.cpp
@@ -0,0 +1,310 @@
+//===- 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;
+ MemorySSA MSSA;
+ MemorySSAWalker *Walker;
+
+ TestAnalyses(MemorySSATest &Test)
+ : DT(*Test.F), AC(*Test.F), AA(Test.TLI),
+ BAA(Test.DL, Test.TLI, AC, &DT), MSSA(*Test.F, &AA, &DT) {
+ AA.addAAResult(BAA);
+ 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, CreateALoadAndPhi) {
+ // We create a diamond where there is a store on one side, and then after
+ // running memory ssa, create a load after the merge point, and use it to test
+ // updating by creating an access for the load and a memoryphi.
+ 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);
+
+ setupAnalyses();
+ MemorySSA &MSSA = Analyses->MSSA;
+ // Add the load
+ B.SetInsertPoint(Merge);
+ LoadInst *LoadInst = B.CreateLoad(PointerArg);
+ // Should be no phi to start
+ EXPECT_EQ(MSSA.getMemoryAccess(Merge), nullptr);
+
+ // Create the phi
+ MemoryPhi *MP = MSSA.createMemoryPhi(Merge);
+ MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst));
+ MP->addIncoming(StoreAccess, Left);
+ MP->addIncoming(MSSA.getLiveOnEntryDef(), Right);
+
+ // 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, 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.
+ 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 =
+ cast<MemoryUseOrDef>(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));
+}
diff --git a/unittests/Transforms/Utils/ValueMapperTest.cpp b/unittests/Transforms/Utils/ValueMapperTest.cpp
index 9dbe4dbc56de8..34b62bb930d98 100644
--- a/unittests/Transforms/Utils/ValueMapperTest.cpp
+++ b/unittests/Transforms/Utils/ValueMapperTest.cpp
@@ -7,6 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
@@ -16,31 +19,117 @@ using namespace llvm;
namespace {
-TEST(ValueMapperTest, MapMetadataUnresolved) {
+TEST(ValueMapperTest, mapMDNode) {
+ LLVMContext Context;
+ auto *U = MDTuple::get(Context, None);
+
+ // The node should be unchanged.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(U, ValueMapper(VM).mapMDNode(*U));
+}
+
+TEST(ValueMapperTest, mapMDNodeCycle) {
+ LLVMContext Context;
+ MDNode *U0;
+ MDNode *U1;
+ {
+ Metadata *Ops[] = {nullptr};
+ auto T = MDTuple::getTemporary(Context, Ops);
+ Ops[0] = T.get();
+ U0 = MDTuple::get(Context, Ops);
+ T->replaceOperandWith(0, U0);
+ U1 = MDNode::replaceWithUniqued(std::move(T));
+ U0->resolveCycles();
+ }
+
+ EXPECT_TRUE(U0->isResolved());
+ EXPECT_TRUE(U0->isUniqued());
+ EXPECT_TRUE(U1->isResolved());
+ EXPECT_TRUE(U1->isUniqued());
+ EXPECT_EQ(U1, U0->getOperand(0));
+ EXPECT_EQ(U0, U1->getOperand(0));
+
+ // Cycles shouldn't be duplicated.
+ {
+ ValueToValueMapTy VM;
+ EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0));
+ EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1));
+ }
+
+ // Check the other order.
+ {
+ ValueToValueMapTy VM;
+ EXPECT_EQ(U1, ValueMapper(VM).mapMDNode(*U1));
+ EXPECT_EQ(U0, ValueMapper(VM).mapMDNode(*U0));
+ }
+}
+
+TEST(ValueMapperTest, mapMDNodeDuplicatedCycle) {
+ LLVMContext Context;
+ auto *PtrTy = Type::getInt8Ty(Context)->getPointerTo();
+ std::unique_ptr<GlobalVariable> G0 = llvm::make_unique<GlobalVariable>(
+ PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G0");
+ std::unique_ptr<GlobalVariable> G1 = llvm::make_unique<GlobalVariable>(
+ PtrTy, false, GlobalValue::ExternalLinkage, nullptr, "G1");
+
+ // Create a cycle that references G0.
+ MDNode *N0; // !0 = !{!1}
+ MDNode *N1; // !1 = !{!0, i8* @G0}
+ {
+ auto T0 = MDTuple::getTemporary(Context, nullptr);
+ Metadata *Ops1[] = {T0.get(), ConstantAsMetadata::get(G0.get())};
+ N1 = MDTuple::get(Context, Ops1);
+ T0->replaceOperandWith(0, N1);
+ N0 = MDNode::replaceWithUniqued(std::move(T0));
+ }
+
+ // Resolve N0 and N1.
+ ASSERT_FALSE(N0->isResolved());
+ ASSERT_FALSE(N1->isResolved());
+ N0->resolveCycles();
+ ASSERT_TRUE(N0->isResolved());
+ ASSERT_TRUE(N1->isResolved());
+
+ // Seed the value map to map G0 to G1 and map the nodes. The output should
+ // have new nodes that reference G1 (instead of G0).
+ ValueToValueMapTy VM;
+ VM[G0.get()] = G1.get();
+ MDNode *MappedN0 = ValueMapper(VM).mapMDNode(*N0);
+ MDNode *MappedN1 = ValueMapper(VM).mapMDNode(*N1);
+ EXPECT_NE(N0, MappedN0);
+ EXPECT_NE(N1, MappedN1);
+ EXPECT_EQ(ConstantAsMetadata::get(G1.get()), MappedN1->getOperand(1));
+
+ // Check that the output nodes are resolved.
+ EXPECT_TRUE(MappedN0->isResolved());
+ EXPECT_TRUE(MappedN1->isResolved());
+}
+
+TEST(ValueMapperTest, mapMDNodeUnresolved) {
LLVMContext Context;
TempMDTuple T = MDTuple::getTemporary(Context, None);
ValueToValueMapTy VM;
- EXPECT_EQ(T.get(), MapMetadata(T.get(), VM, RF_NoModuleLevelChanges));
+ EXPECT_EQ(T.get(), ValueMapper(VM, RF_NoModuleLevelChanges).mapMDNode(*T));
}
-TEST(ValueMapperTest, MapMetadataDistinct) {
+TEST(ValueMapperTest, mapMDNodeDistinct) {
LLVMContext Context;
auto *D = MDTuple::getDistinct(Context, None);
{
// The node should be cloned.
ValueToValueMapTy VM;
- EXPECT_NE(D, MapMetadata(D, VM, RF_None));
+ EXPECT_NE(D, ValueMapper(VM).mapMDNode(*D));
}
{
// The node should be moved.
ValueToValueMapTy VM;
- EXPECT_EQ(D, MapMetadata(D, VM, RF_MoveDistinctMDs));
+ EXPECT_EQ(D, ValueMapper(VM, RF_MoveDistinctMDs).mapMDNode(*D));
}
}
-TEST(ValueMapperTest, MapMetadataDistinctOperands) {
+TEST(ValueMapperTest, mapMDNodeDistinctOperands) {
LLVMContext Context;
Metadata *Old = MDTuple::getDistinct(Context, None);
auto *D = MDTuple::getDistinct(Context, Old);
@@ -51,8 +140,211 @@ TEST(ValueMapperTest, MapMetadataDistinctOperands) {
VM.MD()[Old].reset(New);
// Make sure operands are updated.
- EXPECT_EQ(D, MapMetadata(D, VM, RF_MoveDistinctMDs));
+ EXPECT_EQ(D, ValueMapper(VM, RF_MoveDistinctMDs).mapMDNode(*D));
EXPECT_EQ(New, D->getOperand(0));
}
+TEST(ValueMapperTest, mapMDNodeSeeded) {
+ LLVMContext Context;
+ auto *D = MDTuple::getDistinct(Context, None);
+
+ // The node should be moved.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(None, VM.getMappedMD(D));
+
+ VM.MD().insert(std::make_pair(D, TrackingMDRef(D)));
+ EXPECT_EQ(D, *VM.getMappedMD(D));
+ EXPECT_EQ(D, ValueMapper(VM).mapMDNode(*D));
+}
+
+TEST(ValueMapperTest, mapMDNodeSeededWithNull) {
+ LLVMContext Context;
+ auto *D = MDTuple::getDistinct(Context, None);
+
+ // The node should be moved.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(None, VM.getMappedMD(D));
+
+ VM.MD().insert(std::make_pair(D, TrackingMDRef()));
+ EXPECT_EQ(nullptr, *VM.getMappedMD(D));
+ EXPECT_EQ(nullptr, ValueMapper(VM).mapMDNode(*D));
}
+
+TEST(ValueMapperTest, mapMetadataNullMapGlobalWithIgnoreMissingLocals) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+
+ ValueToValueMapTy VM;
+ RemapFlags Flags = RF_IgnoreMissingLocals | RF_NullMapMissingGlobalValues;
+ EXPECT_EQ(nullptr, ValueMapper(VM, Flags).mapValue(*F));
+}
+
+TEST(ValueMapperTest, mapMetadataMDString) {
+ LLVMContext C;
+ auto *S1 = MDString::get(C, "S1");
+ ValueToValueMapTy VM;
+
+ // Make sure S1 maps to itself, but isn't memoized.
+ EXPECT_EQ(S1, ValueMapper(VM).mapMetadata(*S1));
+ EXPECT_EQ(None, VM.getMappedMD(S1));
+
+ // We still expect VM.MD() to be respected.
+ auto *S2 = MDString::get(C, "S2");
+ VM.MD()[S1].reset(S2);
+ EXPECT_EQ(S2, ValueMapper(VM).mapMetadata(*S1));
+}
+
+TEST(ValueMapperTest, mapMetadataGetMappedMD) {
+ LLVMContext C;
+ auto *N0 = MDTuple::get(C, None);
+ auto *N1 = MDTuple::get(C, N0);
+
+ // Make sure hasMD and getMappedMD work correctly.
+ ValueToValueMapTy VM;
+ EXPECT_FALSE(VM.hasMD());
+ EXPECT_EQ(N0, ValueMapper(VM).mapMetadata(*N0));
+ EXPECT_EQ(N1, ValueMapper(VM).mapMetadata(*N1));
+ EXPECT_TRUE(VM.hasMD());
+ ASSERT_NE(None, VM.getMappedMD(N0));
+ ASSERT_NE(None, VM.getMappedMD(N1));
+ EXPECT_EQ(N0, *VM.getMappedMD(N0));
+ EXPECT_EQ(N1, *VM.getMappedMD(N1));
+}
+
+TEST(ValueMapperTest, mapMetadataNoModuleLevelChanges) {
+ LLVMContext C;
+ auto *N0 = MDTuple::get(C, None);
+ auto *N1 = MDTuple::get(C, N0);
+
+ // Nothing should be memoized when RF_NoModuleLevelChanges.
+ ValueToValueMapTy VM;
+ EXPECT_FALSE(VM.hasMD());
+ EXPECT_EQ(N0, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N0));
+ EXPECT_EQ(N1, ValueMapper(VM, RF_NoModuleLevelChanges).mapMetadata(*N1));
+ EXPECT_FALSE(VM.hasMD());
+ EXPECT_EQ(None, VM.getMappedMD(N0));
+ EXPECT_EQ(None, VM.getMappedMD(N1));
+}
+
+TEST(ValueMapperTest, mapMetadataConstantAsMetadata) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+
+ auto *CAM = ConstantAsMetadata::get(F.get());
+ {
+ // ConstantAsMetadata shouldn't be memoized.
+ ValueToValueMapTy VM;
+ EXPECT_EQ(CAM, ValueMapper(VM).mapMetadata(*CAM));
+ EXPECT_FALSE(VM.MD().count(CAM));
+ EXPECT_EQ(CAM, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM));
+ EXPECT_FALSE(VM.MD().count(CAM));
+
+ // But it should respect a mapping that gets seeded.
+ auto *N = MDTuple::get(C, None);
+ VM.MD()[CAM].reset(N);
+ EXPECT_EQ(N, ValueMapper(VM).mapMetadata(*CAM));
+ EXPECT_EQ(N, ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*CAM));
+ }
+
+ std::unique_ptr<Function> F2(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F2"));
+ ValueToValueMapTy VM;
+ VM[F.get()] = F2.get();
+ auto *F2MD = ValueMapper(VM).mapMetadata(*CAM);
+ EXPECT_FALSE(VM.MD().count(CAM));
+ EXPECT_TRUE(F2MD);
+ EXPECT_EQ(F2.get(), cast<ConstantAsMetadata>(F2MD)->getValue());
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+#ifndef NDEBUG
+TEST(ValueMapperTest, mapMetadataLocalAsMetadata) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+ Argument &A = *F->arg_begin();
+
+ // mapMetadata doesn't support LocalAsMetadata. The only valid container for
+ // LocalAsMetadata is a MetadataAsValue instance, so use it directly.
+ auto *LAM = LocalAsMetadata::get(&A);
+ ValueToValueMapTy VM;
+ EXPECT_DEATH(ValueMapper(VM).mapMetadata(*LAM), "Unexpected local metadata");
+ EXPECT_DEATH(ValueMapper(VM, RF_IgnoreMissingLocals).mapMetadata(*LAM),
+ "Unexpected local metadata");
+}
+#endif
+#endif
+
+TEST(ValueMapperTest, mapValueLocalAsMetadata) {
+ LLVMContext C;
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(C), Type::getInt8Ty(C), false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+ Argument &A = *F->arg_begin();
+
+ auto *LAM = LocalAsMetadata::get(&A);
+ auto *MAV = MetadataAsValue::get(C, LAM);
+
+ // The principled answer to a LocalAsMetadata of an unmapped SSA value would
+ // be to return nullptr (regardless of RF_IgnoreMissingLocals).
+ //
+ // However, algorithms that use RemapInstruction assume that each instruction
+ // only references SSA values from previous instructions. Arguments of
+ // such as "metadata i32 %x" don't currently successfully maintain that
+ // property. To keep RemapInstruction from crashing we need a non-null
+ // return here, but we also shouldn't reference the unmapped local. Use
+ // "metadata !{}".
+ auto *N0 = MDTuple::get(C, None);
+ auto *N0AV = MetadataAsValue::get(C, N0);
+ ValueToValueMapTy VM;
+ EXPECT_EQ(N0AV, ValueMapper(VM).mapValue(*MAV));
+ EXPECT_EQ(nullptr, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));
+ EXPECT_FALSE(VM.count(MAV));
+ EXPECT_FALSE(VM.count(&A));
+ EXPECT_EQ(None, VM.getMappedMD(LAM));
+
+ VM[MAV] = MAV;
+ EXPECT_EQ(MAV, ValueMapper(VM).mapValue(*MAV));
+ EXPECT_EQ(MAV, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));
+ EXPECT_TRUE(VM.count(MAV));
+ EXPECT_FALSE(VM.count(&A));
+
+ VM[MAV] = &A;
+ EXPECT_EQ(&A, ValueMapper(VM).mapValue(*MAV));
+ EXPECT_EQ(&A, ValueMapper(VM, RF_IgnoreMissingLocals).mapValue(*MAV));
+ EXPECT_TRUE(VM.count(MAV));
+ EXPECT_FALSE(VM.count(&A));
+}
+
+TEST(ValueMapperTest, mapValueLocalAsMetadataToConstant) {
+ LLVMContext Context;
+ auto *Int8 = Type::getInt8Ty(Context);
+ FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), Int8, false);
+ std::unique_ptr<Function> F(
+ Function::Create(FTy, GlobalValue::ExternalLinkage, "F"));
+
+ // Map a local value to a constant.
+ Argument &A = *F->arg_begin();
+ Constant &C = *ConstantInt::get(Int8, 42);
+ ValueToValueMapTy VM;
+ VM[&A] = &C;
+
+ // Look up the metadata-as-value wrapper. Don't crash.
+ auto *MDA = MetadataAsValue::get(Context, ValueAsMetadata::get(&A));
+ auto *MDC = MetadataAsValue::get(Context, ValueAsMetadata::get(&C));
+ EXPECT_TRUE(isa<LocalAsMetadata>(MDA->getMetadata()));
+ EXPECT_TRUE(isa<ConstantAsMetadata>(MDC->getMetadata()));
+ EXPECT_EQ(&C, ValueMapper(VM).mapValue(A));
+ EXPECT_EQ(MDC, ValueMapper(VM).mapValue(*MDA));
+}
+
+} // end namespace