//===- Cloning.cpp - Unit tests for the Cloner ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/DomTreeUpdater.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Constant.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "gtest/gtest.h" using namespace llvm; namespace { class CloneInstruction : public ::testing::Test { protected: void SetUp() override { V = nullptr; } template T *clone(T *V1) { Value *V2 = V1->clone(); Orig.insert(V1); Clones.insert(V2); return cast(V2); } void eraseClones() { for (Value *V : Clones) V->deleteValue(); Clones.clear(); } void TearDown() override { eraseClones(); for (Value *V : Orig) V->deleteValue(); Orig.clear(); if (V) V->deleteValue(); } SmallPtrSet Orig; // Erase on exit SmallPtrSet Clones; // Erase in eraseClones LLVMContext context; Value *V; }; TEST_F(CloneInstruction, OverflowBits) { V = new Argument(Type::getInt32Ty(context)); BinaryOperator *Add = BinaryOperator::Create(Instruction::Add, V, V); BinaryOperator *Sub = BinaryOperator::Create(Instruction::Sub, V, V); BinaryOperator *Mul = BinaryOperator::Create(Instruction::Mul, V, V); BinaryOperator *AddClone = this->clone(Add); BinaryOperator *SubClone = this->clone(Sub); BinaryOperator *MulClone = this->clone(Mul); EXPECT_FALSE(AddClone->hasNoUnsignedWrap()); EXPECT_FALSE(AddClone->hasNoSignedWrap()); EXPECT_FALSE(SubClone->hasNoUnsignedWrap()); EXPECT_FALSE(SubClone->hasNoSignedWrap()); EXPECT_FALSE(MulClone->hasNoUnsignedWrap()); EXPECT_FALSE(MulClone->hasNoSignedWrap()); eraseClones(); Add->setHasNoUnsignedWrap(); Sub->setHasNoUnsignedWrap(); Mul->setHasNoUnsignedWrap(); AddClone = this->clone(Add); SubClone = this->clone(Sub); MulClone = this->clone(Mul); EXPECT_TRUE(AddClone->hasNoUnsignedWrap()); EXPECT_FALSE(AddClone->hasNoSignedWrap()); EXPECT_TRUE(SubClone->hasNoUnsignedWrap()); EXPECT_FALSE(SubClone->hasNoSignedWrap()); EXPECT_TRUE(MulClone->hasNoUnsignedWrap()); EXPECT_FALSE(MulClone->hasNoSignedWrap()); eraseClones(); Add->setHasNoSignedWrap(); Sub->setHasNoSignedWrap(); Mul->setHasNoSignedWrap(); AddClone = this->clone(Add); SubClone = this->clone(Sub); MulClone = this->clone(Mul); EXPECT_TRUE(AddClone->hasNoUnsignedWrap()); EXPECT_TRUE(AddClone->hasNoSignedWrap()); EXPECT_TRUE(SubClone->hasNoUnsignedWrap()); EXPECT_TRUE(SubClone->hasNoSignedWrap()); EXPECT_TRUE(MulClone->hasNoUnsignedWrap()); EXPECT_TRUE(MulClone->hasNoSignedWrap()); eraseClones(); Add->setHasNoUnsignedWrap(false); Sub->setHasNoUnsignedWrap(false); Mul->setHasNoUnsignedWrap(false); AddClone = this->clone(Add); SubClone = this->clone(Sub); MulClone = this->clone(Mul); EXPECT_FALSE(AddClone->hasNoUnsignedWrap()); EXPECT_TRUE(AddClone->hasNoSignedWrap()); EXPECT_FALSE(SubClone->hasNoUnsignedWrap()); EXPECT_TRUE(SubClone->hasNoSignedWrap()); EXPECT_FALSE(MulClone->hasNoUnsignedWrap()); EXPECT_TRUE(MulClone->hasNoSignedWrap()); } TEST_F(CloneInstruction, Inbounds) { V = new Argument(Type::getInt32PtrTy(context)); Constant *Z = Constant::getNullValue(Type::getInt32Ty(context)); std::vector ops; ops.push_back(Z); GetElementPtrInst *GEP = GetElementPtrInst::Create(Type::getInt32Ty(context), V, ops); EXPECT_FALSE(this->clone(GEP)->isInBounds()); GEP->setIsInBounds(); EXPECT_TRUE(this->clone(GEP)->isInBounds()); } TEST_F(CloneInstruction, Exact) { V = new Argument(Type::getInt32Ty(context)); BinaryOperator *SDiv = BinaryOperator::Create(Instruction::SDiv, V, V); EXPECT_FALSE(this->clone(SDiv)->isExact()); SDiv->setIsExact(true); EXPECT_TRUE(this->clone(SDiv)->isExact()); } TEST_F(CloneInstruction, Attributes) { Type *ArgTy1[] = { Type::getInt32PtrTy(context) }; FunctionType *FT1 = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); Function *F1 = Function::Create(FT1, Function::ExternalLinkage); BasicBlock *BB = BasicBlock::Create(context, "", F1); IRBuilder<> Builder(BB); Builder.CreateRetVoid(); Function *F2 = Function::Create(FT1, Function::ExternalLinkage); Argument *A = &*F1->arg_begin(); A->addAttr(Attribute::NoCapture); SmallVector Returns; ValueToValueMapTy VMap; VMap[A] = UndefValue::get(A->getType()); CloneFunctionInto(F2, F1, VMap, false, Returns); EXPECT_FALSE(F2->arg_begin()->hasNoCaptureAttr()); delete F1; delete F2; } TEST_F(CloneInstruction, CallingConvention) { Type *ArgTy1[] = { Type::getInt32PtrTy(context) }; FunctionType *FT1 = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); Function *F1 = Function::Create(FT1, Function::ExternalLinkage); F1->setCallingConv(CallingConv::Cold); BasicBlock *BB = BasicBlock::Create(context, "", F1); IRBuilder<> Builder(BB); Builder.CreateRetVoid(); Function *F2 = Function::Create(FT1, Function::ExternalLinkage); SmallVector Returns; ValueToValueMapTy VMap; VMap[&*F1->arg_begin()] = &*F2->arg_begin(); CloneFunctionInto(F2, F1, VMap, false, Returns); EXPECT_EQ(CallingConv::Cold, F2->getCallingConv()); delete F1; delete F2; } TEST_F(CloneInstruction, DuplicateInstructionsToSplit) { Type *ArgTy1[] = {Type::getInt32PtrTy(context)}; FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); V = new Argument(Type::getInt32Ty(context)); Function *F = Function::Create(FT, Function::ExternalLinkage); BasicBlock *BB1 = BasicBlock::Create(context, "", F); IRBuilder<> Builder1(BB1); BasicBlock *BB2 = BasicBlock::Create(context, "", F); IRBuilder<> Builder2(BB2); Builder1.CreateBr(BB2); Instruction *AddInst = cast(Builder2.CreateAdd(V, V)); Instruction *MulInst = cast(Builder2.CreateMul(AddInst, V)); Instruction *SubInst = cast(Builder2.CreateSub(MulInst, V)); Builder2.CreateRetVoid(); // Dummy DTU. ValueToValueMapTy Mapping; DomTreeUpdater DTU(DomTreeUpdater::UpdateStrategy::Lazy); auto Split = DuplicateInstructionsInSplitBetween(BB2, BB1, SubInst, Mapping, DTU); EXPECT_TRUE(Split); EXPECT_EQ(Mapping.size(), 2u); EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end()); EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end()); auto AddSplit = dyn_cast(Mapping[AddInst]); EXPECT_TRUE(AddSplit); EXPECT_EQ(AddSplit->getOperand(0), V); EXPECT_EQ(AddSplit->getOperand(1), V); EXPECT_EQ(AddSplit->getParent(), Split); auto MulSplit = dyn_cast(Mapping[MulInst]); EXPECT_TRUE(MulSplit); EXPECT_EQ(MulSplit->getOperand(0), AddSplit); EXPECT_EQ(MulSplit->getOperand(1), V); EXPECT_EQ(MulSplit->getParent(), Split); EXPECT_EQ(AddSplit->getNextNode(), MulSplit); EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator()); delete F; } TEST_F(CloneInstruction, DuplicateInstructionsToSplitBlocksEq1) { Type *ArgTy1[] = {Type::getInt32PtrTy(context)}; FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); V = new Argument(Type::getInt32Ty(context)); Function *F = Function::Create(FT, Function::ExternalLinkage); BasicBlock *BB1 = BasicBlock::Create(context, "", F); IRBuilder<> Builder1(BB1); BasicBlock *BB2 = BasicBlock::Create(context, "", F); IRBuilder<> Builder2(BB2); Builder1.CreateBr(BB2); Instruction *AddInst = cast(Builder2.CreateAdd(V, V)); Instruction *MulInst = cast(Builder2.CreateMul(AddInst, V)); Instruction *SubInst = cast(Builder2.CreateSub(MulInst, V)); Builder2.CreateBr(BB2); // Dummy DTU. DomTreeUpdater DTU(DomTreeUpdater::UpdateStrategy::Lazy); ValueToValueMapTy Mapping; auto Split = DuplicateInstructionsInSplitBetween( BB2, BB2, BB2->getTerminator(), Mapping, DTU); EXPECT_TRUE(Split); EXPECT_EQ(Mapping.size(), 3u); EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end()); EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end()); EXPECT_TRUE(Mapping.find(SubInst) != Mapping.end()); auto AddSplit = dyn_cast(Mapping[AddInst]); EXPECT_TRUE(AddSplit); EXPECT_EQ(AddSplit->getOperand(0), V); EXPECT_EQ(AddSplit->getOperand(1), V); EXPECT_EQ(AddSplit->getParent(), Split); auto MulSplit = dyn_cast(Mapping[MulInst]); EXPECT_TRUE(MulSplit); EXPECT_EQ(MulSplit->getOperand(0), AddSplit); EXPECT_EQ(MulSplit->getOperand(1), V); EXPECT_EQ(MulSplit->getParent(), Split); auto SubSplit = dyn_cast(Mapping[SubInst]); EXPECT_EQ(MulSplit->getNextNode(), SubSplit); EXPECT_EQ(SubSplit->getNextNode(), Split->getTerminator()); EXPECT_EQ(Split->getSingleSuccessor(), BB2); EXPECT_EQ(BB2->getSingleSuccessor(), Split); delete F; } TEST_F(CloneInstruction, DuplicateInstructionsToSplitBlocksEq2) { Type *ArgTy1[] = {Type::getInt32PtrTy(context)}; FunctionType *FT = FunctionType::get(Type::getVoidTy(context), ArgTy1, false); V = new Argument(Type::getInt32Ty(context)); Function *F = Function::Create(FT, Function::ExternalLinkage); BasicBlock *BB1 = BasicBlock::Create(context, "", F); IRBuilder<> Builder1(BB1); BasicBlock *BB2 = BasicBlock::Create(context, "", F); IRBuilder<> Builder2(BB2); Builder1.CreateBr(BB2); Instruction *AddInst = cast(Builder2.CreateAdd(V, V)); Instruction *MulInst = cast(Builder2.CreateMul(AddInst, V)); Instruction *SubInst = cast(Builder2.CreateSub(MulInst, V)); Builder2.CreateBr(BB2); // Dummy DTU. DomTreeUpdater DTU(DomTreeUpdater::UpdateStrategy::Lazy); ValueToValueMapTy Mapping; auto Split = DuplicateInstructionsInSplitBetween(BB2, BB2, SubInst, Mapping, DTU); EXPECT_TRUE(Split); EXPECT_EQ(Mapping.size(), 2u); EXPECT_TRUE(Mapping.find(AddInst) != Mapping.end()); EXPECT_TRUE(Mapping.find(MulInst) != Mapping.end()); auto AddSplit = dyn_cast(Mapping[AddInst]); EXPECT_TRUE(AddSplit); EXPECT_EQ(AddSplit->getOperand(0), V); EXPECT_EQ(AddSplit->getOperand(1), V); EXPECT_EQ(AddSplit->getParent(), Split); auto MulSplit = dyn_cast(Mapping[MulInst]); EXPECT_TRUE(MulSplit); EXPECT_EQ(MulSplit->getOperand(0), AddSplit); EXPECT_EQ(MulSplit->getOperand(1), V); EXPECT_EQ(MulSplit->getParent(), Split); EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator()); EXPECT_EQ(Split->getSingleSuccessor(), BB2); EXPECT_EQ(BB2->getSingleSuccessor(), Split); delete F; } static void runWithLoopInfoAndDominatorTree( Module &M, StringRef FuncName, function_ref Test) { auto *F = M.getFunction(FuncName); ASSERT_NE(F, nullptr) << "Could not find " << FuncName; DominatorTree DT(*F); LoopInfo LI(DT); Test(*F, LI, DT); } static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; std::unique_ptr Mod = parseAssemblyString(IR, Err, C); if (!Mod) Err.print("CloneLoop", errs()); return Mod; } TEST(CloneLoop, CloneLoopNest) { // Parse the module. LLVMContext Context; std::unique_ptr M = parseIR( Context, R"(define void @foo(i32* %A, i32 %ub) { entry: %guardcmp = icmp slt i32 0, %ub br i1 %guardcmp, label %for.outer.preheader, label %for.end for.outer.preheader: br label %for.outer for.outer: %j = phi i32 [ 0, %for.outer.preheader ], [ %inc.outer, %for.outer.latch ] br i1 %guardcmp, label %for.inner.preheader, label %for.outer.latch for.inner.preheader: br label %for.inner for.inner: %i = phi i32 [ 0, %for.inner.preheader ], [ %inc, %for.inner ] %idxprom = sext i32 %i to i64 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom store i32 %i, i32* %arrayidx, align 4 %inc = add nsw i32 %i, 1 %cmp = icmp slt i32 %inc, %ub br i1 %cmp, label %for.inner, label %for.inner.exit for.inner.exit: br label %for.outer.latch for.outer.latch: %inc.outer = add nsw i32 %j, 1 %cmp.outer = icmp slt i32 %inc.outer, %ub br i1 %cmp.outer, label %for.outer, label %for.outer.exit for.outer.exit: br label %for.end for.end: ret void })" ); runWithLoopInfoAndDominatorTree( *M, "foo", [&](Function &F, LoopInfo &LI, DominatorTree &DT) { Function::iterator FI = F.begin(); // First basic block is entry - skip it. BasicBlock *Preheader = &*(++FI); BasicBlock *Header = &*(++FI); assert(Header->getName() == "for.outer"); Loop *L = LI.getLoopFor(Header); EXPECT_NE(L, nullptr); EXPECT_EQ(Header, L->getHeader()); EXPECT_EQ(Preheader, L->getLoopPreheader()); ValueToValueMapTy VMap; SmallVector ClonedLoopBlocks; Loop *NewLoop = cloneLoopWithPreheader(Preheader, Preheader, L, VMap, "", &LI, &DT, ClonedLoopBlocks); EXPECT_NE(NewLoop, nullptr); EXPECT_EQ(NewLoop->getSubLoops().size(), 1u); Loop::block_iterator BI = NewLoop->block_begin(); EXPECT_TRUE((*BI)->getName().startswith("for.outer")); EXPECT_TRUE((*(++BI))->getName().startswith("for.inner.preheader")); EXPECT_TRUE((*(++BI))->getName().startswith("for.inner")); EXPECT_TRUE((*(++BI))->getName().startswith("for.inner.exit")); EXPECT_TRUE((*(++BI))->getName().startswith("for.outer.latch")); }); } class CloneFunc : public ::testing::Test { protected: void SetUp() override { SetupModule(); CreateOldFunc(); CreateNewFunc(); SetupFinder(); } void TearDown() override { delete Finder; } void SetupModule() { M = new Module("", C); } void CreateOldFunc() { FunctionType* FuncType = FunctionType::get(Type::getVoidTy(C), false); OldFunc = Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", M); CreateOldFunctionBodyAndDI(); } void CreateOldFunctionBodyAndDI() { DIBuilder DBuilder(*M); IRBuilder<> IBuilder(C); // Function DI auto *File = DBuilder.createFile("filename.c", "/file/dir/"); DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None); DISubroutineType *FuncType = DBuilder.createSubroutineType(ParamTypes); auto *CU = DBuilder.createCompileUnit(dwarf::DW_LANG_C99, DBuilder.createFile("filename.c", "/file/dir"), "CloneFunc", false, "", 0); auto *Subprogram = DBuilder.createFunction( CU, "f", "f", File, 4, FuncType, 3, DINode::FlagZero, DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition); OldFunc->setSubprogram(Subprogram); // Function body BasicBlock* Entry = BasicBlock::Create(C, "", OldFunc); IBuilder.SetInsertPoint(Entry); DebugLoc Loc = DebugLoc::get(3, 2, Subprogram); IBuilder.SetCurrentDebugLocation(Loc); AllocaInst* Alloca = IBuilder.CreateAlloca(IntegerType::getInt32Ty(C)); IBuilder.SetCurrentDebugLocation(DebugLoc::get(4, 2, Subprogram)); Value* AllocaContent = IBuilder.getInt32(1); Instruction* Store = IBuilder.CreateStore(AllocaContent, Alloca); IBuilder.SetCurrentDebugLocation(DebugLoc::get(5, 2, Subprogram)); // Create a local variable around the alloca auto *IntType = DBuilder.createBasicType("int", 32, dwarf::DW_ATE_signed); auto *E = DBuilder.createExpression(); auto *Variable = DBuilder.createAutoVariable(Subprogram, "x", File, 5, IntType, true); auto *DL = DILocation::get(Subprogram->getContext(), 5, 0, Subprogram); DBuilder.insertDeclare(Alloca, Variable, E, DL, Store); DBuilder.insertDbgValueIntrinsic(AllocaContent, Variable, E, DL, Entry); // Also create an inlined variable. // Create a distinct struct type that we should not duplicate during // cloning). auto *StructType = DICompositeType::getDistinct( C, dwarf::DW_TAG_structure_type, "some_struct", nullptr, 0, nullptr, nullptr, 32, 32, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr); auto *InlinedSP = DBuilder.createFunction( CU, "inlined", "inlined", File, 8, FuncType, 9, DINode::FlagZero, DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition); auto *InlinedVar = DBuilder.createAutoVariable(InlinedSP, "inlined", File, 5, StructType, true); auto *Scope = DBuilder.createLexicalBlock( DBuilder.createLexicalBlockFile(InlinedSP, File), File, 1, 1); auto InlinedDL = DebugLoc::get(9, 4, Scope, DebugLoc::get(5, 2, Subprogram)); IBuilder.SetCurrentDebugLocation(InlinedDL); DBuilder.insertDeclare(Alloca, InlinedVar, E, InlinedDL, Store); IBuilder.CreateStore(IBuilder.getInt32(2), Alloca); // Finalize the debug info. DBuilder.finalize(); IBuilder.CreateRetVoid(); // Create another, empty, compile unit. DIBuilder DBuilder2(*M); DBuilder2.createCompileUnit(dwarf::DW_LANG_C99, DBuilder.createFile("extra.c", "/file/dir"), "CloneFunc", false, "", 0); DBuilder2.finalize(); } void CreateNewFunc() { ValueToValueMapTy VMap; NewFunc = CloneFunction(OldFunc, VMap, nullptr); } void SetupFinder() { Finder = new DebugInfoFinder(); Finder->processModule(*M); } LLVMContext C; Function* OldFunc; Function* NewFunc; Module* M; DebugInfoFinder* Finder; }; // Test that a new, distinct function was created. TEST_F(CloneFunc, NewFunctionCreated) { EXPECT_NE(OldFunc, NewFunc); } // Test that a new subprogram entry was added and is pointing to the new // function, while the original subprogram still points to the old one. TEST_F(CloneFunc, Subprogram) { EXPECT_FALSE(verifyModule(*M, &errs())); EXPECT_EQ(3U, Finder->subprogram_count()); EXPECT_NE(NewFunc->getSubprogram(), OldFunc->getSubprogram()); } // Test that instructions in the old function still belong to it in the // metadata, while instruction in the new function belong to the new one. TEST_F(CloneFunc, InstructionOwnership) { EXPECT_FALSE(verifyModule(*M)); inst_iterator OldIter = inst_begin(OldFunc); inst_iterator OldEnd = inst_end(OldFunc); inst_iterator NewIter = inst_begin(NewFunc); inst_iterator NewEnd = inst_end(NewFunc); while (OldIter != OldEnd && NewIter != NewEnd) { Instruction& OldI = *OldIter; Instruction& NewI = *NewIter; EXPECT_NE(&OldI, &NewI); EXPECT_EQ(OldI.hasMetadata(), NewI.hasMetadata()); if (OldI.hasMetadata()) { const DebugLoc& OldDL = OldI.getDebugLoc(); const DebugLoc& NewDL = NewI.getDebugLoc(); // Verify that the debug location data is the same EXPECT_EQ(OldDL.getLine(), NewDL.getLine()); EXPECT_EQ(OldDL.getCol(), NewDL.getCol()); // But that they belong to different functions auto *OldSubprogram = cast(OldDL.getInlinedAtScope()); auto *NewSubprogram = cast(NewDL.getInlinedAtScope()); EXPECT_EQ(OldFunc->getSubprogram(), OldSubprogram); EXPECT_EQ(NewFunc->getSubprogram(), NewSubprogram); } ++OldIter; ++NewIter; } EXPECT_EQ(OldEnd, OldIter); EXPECT_EQ(NewEnd, NewIter); } // Test that the arguments for debug intrinsics in the new function were // properly cloned TEST_F(CloneFunc, DebugIntrinsics) { EXPECT_FALSE(verifyModule(*M)); inst_iterator OldIter = inst_begin(OldFunc); inst_iterator OldEnd = inst_end(OldFunc); inst_iterator NewIter = inst_begin(NewFunc); inst_iterator NewEnd = inst_end(NewFunc); while (OldIter != OldEnd && NewIter != NewEnd) { Instruction& OldI = *OldIter; Instruction& NewI = *NewIter; if (DbgDeclareInst* OldIntrin = dyn_cast(&OldI)) { DbgDeclareInst* NewIntrin = dyn_cast(&NewI); EXPECT_TRUE(NewIntrin); // Old address must belong to the old function EXPECT_EQ(OldFunc, cast(OldIntrin->getAddress())-> getParent()->getParent()); // New address must belong to the new function EXPECT_EQ(NewFunc, cast(NewIntrin->getAddress())-> getParent()->getParent()); if (OldIntrin->getDebugLoc()->getInlinedAt()) { // Inlined variable should refer to the same DILocalVariable as in the // Old Function EXPECT_EQ(OldIntrin->getVariable(), NewIntrin->getVariable()); } else { // Old variable must belong to the old function. EXPECT_EQ(OldFunc->getSubprogram(), cast(OldIntrin->getVariable()->getScope())); // New variable must belong to the new function. EXPECT_EQ(NewFunc->getSubprogram(), cast(NewIntrin->getVariable()->getScope())); } } else if (DbgValueInst* OldIntrin = dyn_cast(&OldI)) { DbgValueInst* NewIntrin = dyn_cast(&NewI); EXPECT_TRUE(NewIntrin); if (!OldIntrin->getDebugLoc()->getInlinedAt()) { // Old variable must belong to the old function. EXPECT_EQ(OldFunc->getSubprogram(), cast(OldIntrin->getVariable()->getScope())); // New variable must belong to the new function. EXPECT_EQ(NewFunc->getSubprogram(), cast(NewIntrin->getVariable()->getScope())); } } ++OldIter; ++NewIter; } } static int GetDICompileUnitCount(const Module& M) { if (const auto* LLVM_DBG_CU = M.getNamedMetadata("llvm.dbg.cu")) { return LLVM_DBG_CU->getNumOperands(); } return 0; } TEST(CloneFunction, CloneEmptyFunction) { StringRef ImplAssembly = R"( define void @foo() { ret void } declare void @bar() )"; LLVMContext Context; SMDiagnostic Error; auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context); EXPECT_TRUE(ImplModule != nullptr); auto *ImplFunction = ImplModule->getFunction("foo"); EXPECT_TRUE(ImplFunction != nullptr); auto *DeclFunction = ImplModule->getFunction("bar"); EXPECT_TRUE(DeclFunction != nullptr); ValueToValueMapTy VMap; SmallVector Returns; ClonedCodeInfo CCI; CloneFunctionInto(ImplFunction, DeclFunction, VMap, true, Returns, "", &CCI); EXPECT_FALSE(verifyModule(*ImplModule, &errs())); EXPECT_FALSE(CCI.ContainsCalls); EXPECT_FALSE(CCI.ContainsDynamicAllocas); } TEST(CloneFunction, CloneFunctionWithInalloca) { StringRef ImplAssembly = R"( declare void @a(i32* inalloca) define void @foo() { %a = alloca inalloca i32 call void @a(i32* inalloca %a) ret void } declare void @bar() )"; LLVMContext Context; SMDiagnostic Error; auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context); EXPECT_TRUE(ImplModule != nullptr); auto *ImplFunction = ImplModule->getFunction("foo"); EXPECT_TRUE(ImplFunction != nullptr); auto *DeclFunction = ImplModule->getFunction("bar"); EXPECT_TRUE(DeclFunction != nullptr); ValueToValueMapTy VMap; SmallVector Returns; ClonedCodeInfo CCI; CloneFunctionInto(DeclFunction, ImplFunction, VMap, true, Returns, "", &CCI); EXPECT_FALSE(verifyModule(*ImplModule, &errs())); EXPECT_TRUE(CCI.ContainsCalls); EXPECT_TRUE(CCI.ContainsDynamicAllocas); } TEST(CloneFunction, CloneFunctionWithSubprograms) { // Tests that the debug info is duplicated correctly when a DISubprogram // happens to be one of the operands of the DISubprogram that is being cloned. // In general, operands of "test" that are distinct should be duplicated, // but in this case "my_operator" should not be duplicated. If it is // duplicated, the metadata in the llvm.dbg.declare could end up with // different duplicates. StringRef ImplAssembly = R"( declare void @llvm.dbg.declare(metadata, metadata, metadata) define void @test() !dbg !5 { call void @llvm.dbg.declare(metadata i8* undef, metadata !4, metadata !DIExpression()), !dbg !6 ret void } declare void @cloned() !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!2} !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1) !1 = !DIFile(filename: "test.cpp", directory: "") !2 = !{i32 1, !"Debug Info Version", i32 3} !3 = distinct !DISubprogram(name: "my_operator", scope: !1, unit: !0, retainedNodes: !{!4}) !4 = !DILocalVariable(name: "awaitables", scope: !3) !5 = distinct !DISubprogram(name: "test", scope: !3, unit: !0) !6 = !DILocation(line: 55, column: 15, scope: !3, inlinedAt: !7) !7 = distinct !DILocation(line: 73, column: 14, scope: !5) )"; LLVMContext Context; SMDiagnostic Error; auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context); EXPECT_TRUE(ImplModule != nullptr); auto *OldFunc = ImplModule->getFunction("test"); EXPECT_TRUE(OldFunc != nullptr); auto *NewFunc = ImplModule->getFunction("cloned"); EXPECT_TRUE(NewFunc != nullptr); ValueToValueMapTy VMap; SmallVector Returns; ClonedCodeInfo CCI; CloneFunctionInto(NewFunc, OldFunc, VMap, true, Returns, "", &CCI); // This fails if the scopes in the llvm.dbg.declare variable and location // aren't the same. EXPECT_FALSE(verifyModule(*ImplModule, &errs())); } TEST(CloneFunction, CloneFunctionToDifferentModule) { StringRef ImplAssembly = R"( define void @foo() { ret void, !dbg !5 } !llvm.module.flags = !{!0} !llvm.dbg.cu = !{!2, !6} !0 = !{i32 1, !"Debug Info Version", i32 3} !1 = distinct !DISubprogram(unit: !2) !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3) !3 = !DIFile(filename: "foo.c", directory: "/tmp") !4 = distinct !DISubprogram(unit: !2) !5 = !DILocation(line: 4, scope: !1) !6 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3) )"; StringRef DeclAssembly = R"( declare void @foo() )"; LLVMContext Context; SMDiagnostic Error; auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context); EXPECT_TRUE(ImplModule != nullptr); // DICompileUnits: !2, !6. Only !2 is reachable from @foo(). EXPECT_TRUE(GetDICompileUnitCount(*ImplModule) == 2); auto* ImplFunction = ImplModule->getFunction("foo"); EXPECT_TRUE(ImplFunction != nullptr); auto DeclModule = parseAssemblyString(DeclAssembly, Error, Context); EXPECT_TRUE(DeclModule != nullptr); // No DICompileUnits defined here. EXPECT_TRUE(GetDICompileUnitCount(*DeclModule) == 0); auto* DeclFunction = DeclModule->getFunction("foo"); EXPECT_TRUE(DeclFunction != nullptr); ValueToValueMapTy VMap; VMap[ImplFunction] = DeclFunction; // No args to map SmallVector Returns; CloneFunctionInto(DeclFunction, ImplFunction, VMap, true, Returns); EXPECT_FALSE(verifyModule(*ImplModule, &errs())); EXPECT_FALSE(verifyModule(*DeclModule, &errs())); // DICompileUnit !2 shall be inserted into DeclModule. EXPECT_TRUE(GetDICompileUnitCount(*DeclModule) == 1); } class CloneModule : public ::testing::Test { protected: void SetUp() override { SetupModule(); CreateOldModule(); CreateNewModule(); } void SetupModule() { OldM = new Module("", C); } void CreateOldModule() { auto *CD = OldM->getOrInsertComdat("comdat"); CD->setSelectionKind(Comdat::ExactMatch); auto GV = new GlobalVariable( *OldM, Type::getInt32Ty(C), false, GlobalValue::ExternalLinkage, ConstantInt::get(Type::getInt32Ty(C), 1), "gv"); GV->addMetadata(LLVMContext::MD_type, *MDNode::get(C, {})); GV->setComdat(CD); DIBuilder DBuilder(*OldM); IRBuilder<> IBuilder(C); auto *FuncType = FunctionType::get(Type::getVoidTy(C), false); auto *PersFn = Function::Create(FuncType, GlobalValue::ExternalLinkage, "persfn", OldM); auto *F = Function::Create(FuncType, GlobalValue::PrivateLinkage, "f", OldM); F->setPersonalityFn(PersFn); F->setComdat(CD); // Create debug info auto *File = DBuilder.createFile("filename.c", "/file/dir/"); DITypeRefArray ParamTypes = DBuilder.getOrCreateTypeArray(None); DISubroutineType *DFuncType = DBuilder.createSubroutineType(ParamTypes); auto *CU = DBuilder.createCompileUnit(dwarf::DW_LANG_C99, DBuilder.createFile("filename.c", "/file/dir"), "CloneModule", false, "", 0); // Function DI auto *Subprogram = DBuilder.createFunction( CU, "f", "f", File, 4, DFuncType, 3, DINode::FlagZero, DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition); F->setSubprogram(Subprogram); // Create and assign DIGlobalVariableExpression to gv auto GVExpression = DBuilder.createGlobalVariableExpression( Subprogram, "gv", "gv", File, 1, DBuilder.createNullPtrType(), false); GV->addDebugInfo(GVExpression); // DIGlobalVariableExpression not attached to any global variable auto Expr = DBuilder.createExpression( ArrayRef{dwarf::DW_OP_constu, 42U, dwarf::DW_OP_stack_value}); DBuilder.createGlobalVariableExpression( Subprogram, "unattached", "unattached", File, 1, DBuilder.createNullPtrType(), false, true, Expr); auto *Entry = BasicBlock::Create(C, "", F); IBuilder.SetInsertPoint(Entry); IBuilder.CreateRetVoid(); // Finalize the debug info DBuilder.finalize(); } void CreateNewModule() { NewM = llvm::CloneModule(*OldM).release(); } LLVMContext C; Module *OldM; Module *NewM; }; 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); } TEST_F(CloneModule, GlobalMetadata) { GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); EXPECT_NE(nullptr, NewGV->getMetadata(LLVMContext::MD_type)); } TEST_F(CloneModule, GlobalDebugInfo) { GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); EXPECT_TRUE(NewGV != nullptr); // Find debug info expression assigned to global SmallVector GVs; NewGV->getDebugInfo(GVs); EXPECT_EQ(GVs.size(), 1U); DIGlobalVariableExpression *GVExpr = GVs[0]; DIGlobalVariable *GV = GVExpr->getVariable(); EXPECT_TRUE(GV != nullptr); EXPECT_EQ(GV->getName(), "gv"); EXPECT_EQ(GV->getLine(), 1U); // Assert that the scope of the debug info attached to // global variable matches the cloned function. DISubprogram *SP = NewM->getFunction("f")->getSubprogram(); EXPECT_TRUE(SP != nullptr); EXPECT_EQ(GV->getScope(), SP); } TEST_F(CloneModule, CompileUnit) { // Find DICompileUnit listed in llvm.dbg.cu auto *NMD = NewM->getNamedMetadata("llvm.dbg.cu"); EXPECT_TRUE(NMD != nullptr); EXPECT_EQ(NMD->getNumOperands(), 1U); DICompileUnit *CU = dyn_cast(NMD->getOperand(0)); EXPECT_TRUE(CU != nullptr); // Assert this CU is consistent with the cloned function debug info DISubprogram *SP = NewM->getFunction("f")->getSubprogram(); EXPECT_TRUE(SP != nullptr); EXPECT_EQ(SP->getUnit(), CU); // Check globals listed in CU have the correct scope DIGlobalVariableExpressionArray GlobalArray = CU->getGlobalVariables(); EXPECT_EQ(GlobalArray.size(), 2U); for (DIGlobalVariableExpression *GVExpr : GlobalArray) { DIGlobalVariable *GV = GVExpr->getVariable(); EXPECT_EQ(GV->getScope(), SP); } } TEST_F(CloneModule, Comdat) { GlobalVariable *NewGV = NewM->getGlobalVariable("gv"); auto *CD = NewGV->getComdat(); ASSERT_NE(nullptr, CD); EXPECT_EQ("comdat", CD->getName()); EXPECT_EQ(Comdat::ExactMatch, CD->getSelectionKind()); Function *NewF = NewM->getFunction("f"); EXPECT_EQ(CD, NewF->getComdat()); } }