...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
865 lines
29 KiB
C++
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());
|
|
}
|
|
}
|