diff options
Diffstat (limited to 'unittests/Transforms/Utils')
-rw-r--r-- | unittests/Transforms/Utils/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unittests/Transforms/Utils/Cloning.cpp | 61 | ||||
-rw-r--r-- | unittests/Transforms/Utils/IntegerDivision.cpp | 16 | ||||
-rw-r--r-- | unittests/Transforms/Utils/Local.cpp | 4 | ||||
-rw-r--r-- | unittests/Transforms/Utils/Makefile | 15 | ||||
-rw-r--r-- | unittests/Transforms/Utils/MemorySSA.cpp | 310 | ||||
-rw-r--r-- | unittests/Transforms/Utils/ValueMapperTest.cpp | 306 |
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 |