diff options
Diffstat (limited to 'llvm/unittests/Analysis/LazyCallGraphTest.cpp')
-rw-r--r-- | llvm/unittests/Analysis/LazyCallGraphTest.cpp | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/llvm/unittests/Analysis/LazyCallGraphTest.cpp b/llvm/unittests/Analysis/LazyCallGraphTest.cpp index c2c1cd2..b154c6f 100644 --- a/llvm/unittests/Analysis/LazyCallGraphTest.cpp +++ b/llvm/unittests/Analysis/LazyCallGraphTest.cpp @@ -13,6 +13,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" @@ -2170,4 +2171,685 @@ TEST(LazyCallGraphTest, RemoveFunctionWithSpurriousRef) { EXPECT_EQ(&RC2, &*I++); EXPECT_EQ(CG.postorder_ref_scc_end(), I); } + +TEST(LazyCallGraphTest, AddSplitFunction1) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + (void)ReturnInst::Create(Context, GBB); + + // Create f -call-> g. + (void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC1 = &*I++; + EXPECT_EQ(RC1, CG.lookupRefSCC(*GN)); + LazyCallGraph::RefSCC *RC2 = &*I++; + EXPECT_EQ(RC2, ORC); + EXPECT_EQ(RC2, CG.lookupRefSCC(FN)); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); +} + +TEST(LazyCallGraphTest, AddSplitFunction2) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + (void)ReturnInst::Create(Context, GBB); + + // Create f -ref-> g. + (void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC1 = &*I++; + EXPECT_EQ(RC1, CG.lookupRefSCC(*GN)); + LazyCallGraph::RefSCC *RC2 = &*I++; + EXPECT_EQ(RC2, ORC); + EXPECT_EQ(RC2, CG.lookupRefSCC(FN)); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); +} + +TEST(LazyCallGraphTest, AddSplitFunction3) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -ref-> f. + (void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -call-> g. + (void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + EXPECT_EQ(2, RC->size()); + EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[1]); +} + +TEST(LazyCallGraphTest, AddSplitFunction4) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -ref-> f. + (void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -ref-> g. + (void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + // Order doesn't matter for sibling SCCs. + EXPECT_EQ(2, RC->size()); + EXPECT_EQ(&CG.lookupSCC(*GN)->getOuterRefSCC(), RC); + EXPECT_EQ(&CG.lookupSCC(FN)->getOuterRefSCC(), RC); +} + +TEST(LazyCallGraphTest, AddSplitFunction5) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -call-> f. + (void)CallInst::Create(&F, {}, "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -ref-> g. + (void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + EXPECT_EQ(2, RC->size()); + EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[1]); +} + +TEST(LazyCallGraphTest, AddSplitFunction6) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -call-> f. + (void)CallInst::Create(&F, {}, "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -call-> g. + (void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + EXPECT_EQ(1, RC->size()); + EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]); +} + +TEST(LazyCallGraphTest, AddSplitFunction7) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " call void @f2()\n" + " ret void\n" + "}\n" + "define void @f2() {\n" + " call void @f()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + Function &F2 = lookupFunction(*M, "f2"); + LazyCallGraph::Node &F2N = CG.get(F2); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -call-> f2. + (void)CallInst::Create(&F2, {}, "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -call-> g. + (void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + EXPECT_EQ(1, RC->size()); + EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(F2N), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]); +} + +TEST(LazyCallGraphTest, AddSplitFunction8) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " call void @f2()\n" + " ret void\n" + "}\n" + "define void @f2() {\n" + " call void @f()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + Function &F2 = lookupFunction(*M, "f2"); + LazyCallGraph::Node &F2N = CG.get(F2); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -call-> f2. + (void)CallInst::Create(&F2, {}, "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -ref-> g. + (void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + EXPECT_EQ(2, RC->size()); + EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(F2N), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[1]); +} + +TEST(LazyCallGraphTest, AddSplitFunction9) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " call void @f2()\n" + " ret void\n" + "}\n" + "define void @f2() {\n" + " call void @f()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + Function &F2 = lookupFunction(*M, "f2"); + LazyCallGraph::Node &F2N = CG.get(F2); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -ref-> f2. + (void)CastInst::CreatePointerCast(&F2, Type::getInt8PtrTy(Context), "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -call-> g. + (void)CallInst::Create(G, {}, "", &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitFunction(F, *G); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + EXPECT_EQ(2, RC->size()); + EXPECT_EQ(CG.lookupSCC(*GN), &(*RC)[0]); + EXPECT_EQ(CG.lookupSCC(FN), &(*RC)[1]); + EXPECT_EQ(CG.lookupSCC(F2N), &(*RC)[1]); +} + +TEST(LazyCallGraphTest, AddSplitFunctions1) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + (void)ReturnInst::Create(Context, GBB); + + // Create f -ref-> g. + (void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G})); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC1 = &*I++; + EXPECT_EQ(RC1, CG.lookupRefSCC(*GN)); + LazyCallGraph::RefSCC *RC2 = &*I++; + EXPECT_EQ(RC2, ORC); + EXPECT_EQ(RC2, CG.lookupRefSCC(FN)); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); +} + +TEST(LazyCallGraphTest, AddSplitFunctions2) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g", F.getParent()); + BasicBlock *GBB = BasicBlock::Create(Context, "", G); + // Create g -ref-> f. + (void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", GBB); + (void)ReturnInst::Create(Context, GBB); + + // Create f -ref-> g. + (void)CastInst::CreatePointerCast(G, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G})); + + LazyCallGraph::Node *GN = CG.lookup(*G); + EXPECT_TRUE(GN); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, CG.lookupRefSCC(*GN)); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + // Order doesn't matter for sibling SCCs. + EXPECT_EQ(2, RC->size()); + EXPECT_EQ(&CG.lookupSCC(*GN)->getOuterRefSCC(), RC); + EXPECT_EQ(&CG.lookupSCC(FN)->getOuterRefSCC(), RC); +} + +TEST(LazyCallGraphTest, AddSplitFunctions3) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g1", F.getParent()); + auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g2", F.getParent()); + BasicBlock *G1BB = BasicBlock::Create(Context, "", G1); + BasicBlock *G2BB = BasicBlock::Create(Context, "", G2); + // Create g1 -ref-> g2 and g2 -ref-> g1. + (void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", G1BB); + (void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", G2BB); + (void)ReturnInst::Create(Context, G1BB); + (void)ReturnInst::Create(Context, G2BB); + + // Create f -ref-> g1 and f -ref-> g2. + (void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + (void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G1, G2})); + + LazyCallGraph::Node *G1N = CG.lookup(*G1); + EXPECT_TRUE(G1N); + LazyCallGraph::Node *G2N = CG.lookup(*G2); + EXPECT_TRUE(G2N); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC1 = &*I++; + EXPECT_EQ(2, RC1->size()); + EXPECT_EQ(RC1, CG.lookupRefSCC(*G1N)); + EXPECT_EQ(RC1, CG.lookupRefSCC(*G2N)); + LazyCallGraph::RefSCC *RC2 = &*I++; + EXPECT_EQ(RC2, ORC); + EXPECT_EQ(RC2, CG.lookupRefSCC(FN)); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); +} + +TEST(LazyCallGraphTest, AddSplitFunctions4) { + LLVMContext Context; + std::unique_ptr<Module> M = parseAssembly(Context, "define void @f() {\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g1", F.getParent()); + auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g2", F.getParent()); + BasicBlock *G1BB = BasicBlock::Create(Context, "", G1); + BasicBlock *G2BB = BasicBlock::Create(Context, "", G2); + // Create g1 -ref-> g2 and g2 -ref-> g1. + (void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", G1BB); + (void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", G2BB); + // Create g2 -ref-> f. + (void)CastInst::CreatePointerCast(&F, Type::getInt8PtrTy(Context), "", G2BB); + (void)ReturnInst::Create(Context, G1BB); + (void)ReturnInst::Create(Context, G2BB); + + // Create f -ref-> g1 and f -ref-> g2. + (void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + (void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G1, G2})); + + LazyCallGraph::Node *G1N = CG.lookup(*G1); + EXPECT_TRUE(G1N); + LazyCallGraph::Node *G2N = CG.lookup(*G2); + EXPECT_TRUE(G2N); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(RC, ORC); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + // Order doesn't matter for sibling SCCs. + EXPECT_EQ(3, RC->size()); + EXPECT_EQ(&CG.lookupSCC(FN)->getOuterRefSCC(), RC); + EXPECT_EQ(&CG.lookupSCC(*G1N)->getOuterRefSCC(), RC); + EXPECT_EQ(&CG.lookupSCC(*G2N)->getOuterRefSCC(), RC); + EXPECT_EQ(RC, CG.lookupRefSCC(*G1N)); + EXPECT_EQ(RC, CG.lookupRefSCC(*G2N)); +} + +TEST(LazyCallGraphTest, AddSplitFunctions5) { + LLVMContext Context; + std::unique_ptr<Module> M = + parseAssembly(Context, "define void @f() {\n" + " %1 = bitcast void ()* @f2 to i8*\n" + " ret void\n" + "}\n" + "define void @f2() {\n" + " call void @f()\n" + " ret void\n" + "}\n"); + LazyCallGraph CG = buildCG(*M); + + Function &F = lookupFunction(*M, "f"); + LazyCallGraph::Node &FN = CG.get(F); + Function &F2 = lookupFunction(*M, "f2"); + LazyCallGraph::Node &F2N = CG.get(F); + + // Force the graph to be fully expanded. + CG.buildRefSCCs(); + auto I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *ORC = &*I++; + EXPECT_EQ(CG.postorder_ref_scc_end(), I); + + auto *G1 = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g1", F.getParent()); + auto *G2 = Function::Create(F.getFunctionType(), F.getLinkage(), + F.getAddressSpace(), "g2", F.getParent()); + BasicBlock *G1BB = BasicBlock::Create(Context, "", G1); + BasicBlock *G2BB = BasicBlock::Create(Context, "", G2); + // Create g1 -ref-> g2 and g2 -ref-> g1. + (void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", G1BB); + (void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", G2BB); + // Create g2 -ref-> f2. + (void)CastInst::CreatePointerCast(&F2, Type::getInt8PtrTy(Context), "", G2BB); + (void)ReturnInst::Create(Context, G1BB); + (void)ReturnInst::Create(Context, G2BB); + + // Create f -ref-> g1 and f -ref-> g2. + (void)CastInst::CreatePointerCast(G1, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + (void)CastInst::CreatePointerCast(G2, Type::getInt8PtrTy(Context), "", + &*F.getEntryBlock().begin()); + + EXPECT_FALSE(verifyModule(*M, &errs())); + + CG.addSplitRefRecursiveFunctions(F, SmallVector<Function *, 1>({G1, G2})); + + LazyCallGraph::Node *G1N = CG.lookup(*G1); + EXPECT_TRUE(G1N); + LazyCallGraph::Node *G2N = CG.lookup(*G2); + EXPECT_TRUE(G2N); + + I = CG.postorder_ref_scc_begin(); + LazyCallGraph::RefSCC *RC = &*I++; + EXPECT_EQ(4, RC->size()); + EXPECT_EQ(RC, ORC); + EXPECT_EQ(RC, CG.lookupRefSCC(*G1N)); + EXPECT_EQ(RC, CG.lookupRefSCC(*G2N)); + EXPECT_EQ(RC, CG.lookupRefSCC(FN)); + EXPECT_EQ(RC, CG.lookupRefSCC(F2N)); + EXPECT_EQ(CG.postorder_ref_scc_end(), I); +} } |