Artur Pilipenko 925afc1ce7 Fix for "DICompileUnit not listed in llvm.dbg.cu" verification error after ...
...cloning a function from a different module

Currently when a function with debug info is cloned from a different module, the 
cloned function may have hanging DICompileUnits, so that the module with the 
cloned function fails debug info verification.

The proposed fix inserts all DICompileUnits reachable from the cloned function 
to "llvm.dbg.cu" metadata operands of the cloned function module. 

Reviewed By: aprantl, efriedma

Differential Revision: https://reviews.llvm.org/D66510

Patch by Oleg Pliss (Oleg.Pliss@azul.com)

llvm-svn: 370265
2019-08-28 21:27:50 +00:00

865 lines
29 KiB
C++

//===- 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/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 <typename T>
T *clone(T *V1) {
Value *V2 = V1->clone();
Orig.insert(V1);
Clones.insert(V2);
return cast<T>(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<Value *, 4> Orig; // Erase on exit
SmallPtrSet<Value *, 4> 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<Value *> 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<ReturnInst*, 4> 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<ReturnInst*, 4> 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<Instruction>(Builder2.CreateAdd(V, V));
Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));
Instruction *SubInst = cast<Instruction>(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<Instruction>(Mapping[AddInst]);
EXPECT_TRUE(AddSplit);
EXPECT_EQ(AddSplit->getOperand(0), V);
EXPECT_EQ(AddSplit->getOperand(1), V);
EXPECT_EQ(AddSplit->getParent(), Split);
auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);
EXPECT_TRUE(MulSplit);
EXPECT_EQ(MulSplit->getOperand(0), AddSplit);
EXPECT_EQ(MulSplit->getOperand(1), V);
EXPECT_EQ(MulSplit->getParent(), Split);
EXPECT_EQ(AddSplit->getNextNode(), MulSplit);
EXPECT_EQ(MulSplit->getNextNode(), Split->getTerminator());
delete F;
}
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<Instruction>(Builder2.CreateAdd(V, V));
Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));
Instruction *SubInst = cast<Instruction>(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<Instruction>(Mapping[AddInst]);
EXPECT_TRUE(AddSplit);
EXPECT_EQ(AddSplit->getOperand(0), V);
EXPECT_EQ(AddSplit->getOperand(1), V);
EXPECT_EQ(AddSplit->getParent(), Split);
auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);
EXPECT_TRUE(MulSplit);
EXPECT_EQ(MulSplit->getOperand(0), AddSplit);
EXPECT_EQ(MulSplit->getOperand(1), V);
EXPECT_EQ(MulSplit->getParent(), Split);
auto SubSplit = dyn_cast<Instruction>(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<Instruction>(Builder2.CreateAdd(V, V));
Instruction *MulInst = cast<Instruction>(Builder2.CreateMul(AddInst, V));
Instruction *SubInst = cast<Instruction>(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<Instruction>(Mapping[AddInst]);
EXPECT_TRUE(AddSplit);
EXPECT_EQ(AddSplit->getOperand(0), V);
EXPECT_EQ(AddSplit->getOperand(1), V);
EXPECT_EQ(AddSplit->getParent(), Split);
auto MulSplit = dyn_cast<Instruction>(Mapping[MulInst]);
EXPECT_TRUE(MulSplit);
EXPECT_EQ(MulSplit->getOperand(0), AddSplit);
EXPECT_EQ(MulSplit->getOperand(1), V);
EXPECT_EQ(MulSplit->getParent(), Split);
EXPECT_EQ(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<void(Function &F, LoopInfo &LI, DominatorTree &DT)> 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<Module> parseIR(LLVMContext &C, const char *IR) {
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
if (!Mod)
Err.print("CloneLoop", errs());
return Mod;
}
TEST(CloneLoop, CloneLoopNest) {
// Parse the module.
LLVMContext Context;
std::unique_ptr<Module> 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<BasicBlock *, 4> 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<DISubprogram>(OldDL.getInlinedAtScope());
auto *NewSubprogram = cast<DISubprogram>(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<DbgDeclareInst>(&OldI)) {
DbgDeclareInst* NewIntrin = dyn_cast<DbgDeclareInst>(&NewI);
EXPECT_TRUE(NewIntrin);
// Old address must belong to the old function
EXPECT_EQ(OldFunc, cast<AllocaInst>(OldIntrin->getAddress())->
getParent()->getParent());
// New address must belong to the new function
EXPECT_EQ(NewFunc, cast<AllocaInst>(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<DISubprogram>(OldIntrin->getVariable()->getScope()));
// New variable must belong to the new function.
EXPECT_EQ(NewFunc->getSubprogram(),
cast<DISubprogram>(NewIntrin->getVariable()->getScope()));
}
} else if (DbgValueInst* OldIntrin = dyn_cast<DbgValueInst>(&OldI)) {
DbgValueInst* NewIntrin = dyn_cast<DbgValueInst>(&NewI);
EXPECT_TRUE(NewIntrin);
if (!OldIntrin->getDebugLoc()->getInlinedAt()) {
// Old variable must belong to the old function.
EXPECT_EQ(OldFunc->getSubprogram(),
cast<DISubprogram>(OldIntrin->getVariable()->getScope()));
// New variable must belong to the new function.
EXPECT_EQ(NewFunc->getSubprogram(),
cast<DISubprogram>(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, 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<ReturnInst*, 8> 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<uint64_t>{dwarf::DW_OP_constu, 42U, dwarf::DW_OP_stack_value});
DBuilder.createGlobalVariableExpression(
Subprogram, "unattached", "unattached", File, 1,
DBuilder.createNullPtrType(), false, 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<DIGlobalVariableExpression *, 1> 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<llvm::DICompileUnit>(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());
}
}