diff --git a/include/llvm/Analysis/LazyCallGraph.h b/include/llvm/Analysis/LazyCallGraph.h index 3a052761ad7..aeeeb9a966a 100644 --- a/include/llvm/Analysis/LazyCallGraph.h +++ b/include/llvm/Analysis/LazyCallGraph.h @@ -43,6 +43,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" @@ -908,7 +909,7 @@ public: /// This sets up the graph and computes all of the entry points of the graph. /// No function definitions are scanned until their nodes in the graph are /// requested during traversal. - LazyCallGraph(Module &M); + LazyCallGraph(Module &M, TargetLibraryInfo &TLI); LazyCallGraph(LazyCallGraph &&G); LazyCallGraph &operator=(LazyCallGraph &&RHS); @@ -966,6 +967,12 @@ public: return insertInto(F, N); } + /// Get the sequence of known and defined library functions. + /// + /// These functions, because they are known to LLVM, can have calls + /// introduced out of thin air from arbitrary IR. + ArrayRef getLibFunctions() const { return LibFunctions; } + ///@{ /// \name Pre-SCC Mutation API /// @@ -1100,6 +1107,11 @@ private: /// These are all of the RefSCCs which have no children. SmallVector LeafRefSCCs; + /// Defined functions that are also known library functions which the + /// optimizer can reason about and therefore might introduce calls to out of + /// thin air. + SmallVector LibFunctions; + /// Helper to insert a new function, with an already looked-up entry in /// the NodeMap. Node &insertInto(Function &F, Node *&MappedN); @@ -1216,8 +1228,8 @@ public: /// /// This just builds the set of entry points to the call graph. The rest is /// built lazily as it is walked. - LazyCallGraph run(Module &M, ModuleAnalysisManager &) { - return LazyCallGraph(M); + LazyCallGraph run(Module &M, ModuleAnalysisManager &AM) { + return LazyCallGraph(M, AM.getResult(M)); } }; diff --git a/lib/Analysis/CGSCCPassManager.cpp b/lib/Analysis/CGSCCPassManager.cpp index 3ddefc6520a..74b5d79ebac 100644 --- a/lib/Analysis/CGSCCPassManager.cpp +++ b/lib/Analysis/CGSCCPassManager.cpp @@ -433,7 +433,7 @@ LazyCallGraph::SCC &llvm::updateCGAndAnalysisManagerForFunctionPass( if (Visited.insert(C).second) Worklist.push_back(C); - LazyCallGraph::visitReferences(Worklist, Visited, [&](Function &Referee) { + auto VisitRef = [&](Function &Referee) { Node &RefereeN = *G.lookup(Referee); Edge *E = N->lookup(RefereeN); // FIXME: Similarly to new calls, we also currently preclude @@ -444,7 +444,12 @@ LazyCallGraph::SCC &llvm::updateCGAndAnalysisManagerForFunctionPass( RetainedEdges.insert(&RefereeN); if (E->isCall()) DemotedCallTargets.insert(&RefereeN); - }); + }; + LazyCallGraph::visitReferences(Worklist, Visited, VisitRef); + + // Include synthetic reference edges to known, defined lib functions. + for (auto *F : G.getLibFunctions()) + VisitRef(*F); // First remove all of the edges that are no longer present in this function. // We have to build a list of dead targets first and then remove them as the diff --git a/lib/Analysis/LazyCallGraph.cpp b/lib/Analysis/LazyCallGraph.cpp index a4c3e43b4b0..5e14c0df6d0 100644 --- a/lib/Analysis/LazyCallGraph.cpp +++ b/lib/Analysis/LazyCallGraph.cpp @@ -106,6 +106,13 @@ LazyCallGraph::EdgeSequence &LazyCallGraph::Node::populateSlow() { LazyCallGraph::Edge::Ref); }); + // Add implicit reference edges to any defined libcall functions (if we + // haven't found an explicit edge). + for (auto *F : G->LibFunctions) + if (!Visited.count(F)) + addEdge(Edges->Edges, Edges->EdgeIndexMap, G->get(*F), + LazyCallGraph::Edge::Ref); + return *Edges; } @@ -120,15 +127,34 @@ LLVM_DUMP_METHOD void LazyCallGraph::Node::dump() const { } #endif -LazyCallGraph::LazyCallGraph(Module &M) { +static bool isKnownLibFunction(Function &F, TargetLibraryInfo &TLI) { + LibFunc LF; + + // Either this is a normal library function or a "vectorizable" function. + return TLI.getLibFunc(F, LF) || TLI.isFunctionVectorizable(F.getName()); +} + +LazyCallGraph::LazyCallGraph(Module &M, TargetLibraryInfo &TLI) { DEBUG(dbgs() << "Building CG for module: " << M.getModuleIdentifier() << "\n"); - for (Function &F : M) - if (!F.isDeclaration() && !F.hasLocalLinkage()) { - DEBUG(dbgs() << " Adding '" << F.getName() - << "' to entry set of the graph.\n"); - addEdge(EntryEdges.Edges, EntryEdges.EdgeIndexMap, get(F), Edge::Ref); - } + for (Function &F : M) { + if (F.isDeclaration()) + continue; + // If this function is a known lib function to LLVM then we want to + // synthesize reference edges to it to model the fact that LLVM can turn + // arbitrary code into a library function call. + if (isKnownLibFunction(F, TLI)) + LibFunctions.push_back(&F); + + if (F.hasLocalLinkage()) + continue; + + // External linkage defined functions have edges to them from other + // modules. + DEBUG(dbgs() << " Adding '" << F.getName() + << "' to entry set of the graph.\n"); + addEdge(EntryEdges.Edges, EntryEdges.EdgeIndexMap, get(F), Edge::Ref); + } // Now add entry nodes for functions reachable via initializers to globals. SmallVector Worklist; @@ -149,7 +175,8 @@ LazyCallGraph::LazyCallGraph(Module &M) { LazyCallGraph::LazyCallGraph(LazyCallGraph &&G) : BPA(std::move(G.BPA)), NodeMap(std::move(G.NodeMap)), EntryEdges(std::move(G.EntryEdges)), SCCBPA(std::move(G.SCCBPA)), - SCCMap(std::move(G.SCCMap)), LeafRefSCCs(std::move(G.LeafRefSCCs)) { + SCCMap(std::move(G.SCCMap)), LeafRefSCCs(std::move(G.LeafRefSCCs)), + LibFunctions(std::move(G.LibFunctions)) { updateGraphPtrs(); } @@ -160,6 +187,7 @@ LazyCallGraph &LazyCallGraph::operator=(LazyCallGraph &&G) { SCCBPA = std::move(G.SCCBPA); SCCMap = std::move(G.SCCMap); LeafRefSCCs = std::move(G.LeafRefSCCs); + LibFunctions = std::move(G.LibFunctions); updateGraphPtrs(); return *this; } diff --git a/test/Other/cgscc-libcall-update.ll b/test/Other/cgscc-libcall-update.ll new file mode 100644 index 00000000000..53fe24fc38a --- /dev/null +++ b/test/Other/cgscc-libcall-update.ll @@ -0,0 +1,35 @@ +; Make sure that the CGSCC pass manager can handle when instcombine simplifies +; one libcall into an unrelated libcall and update the call graph accordingly. +; +; RUN: opt -passes='cgscc(function(instcombine))' -S < %s | FileCheck %s + +define i8* @wibble(i8* %arg1, i8* %arg2) { +; CHECK-LABLE: define @wibble( +bb: + %tmp = alloca [1024 x i8], align 16 + %tmp2 = getelementptr inbounds [1024 x i8], [1024 x i8]* %tmp, i64 0, i64 0 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %tmp2, i8* %arg1, i64 1024, i32 0, i1 false) +; CHECK: call void @llvm.memcpy + %tmp3 = call i64 @llvm.objectsize.i64.p0i8(i8* %tmp2, i1 false, i1 true) + %tmp4 = call i8* @__strncpy_chk(i8* %arg2, i8* %tmp2, i64 1023, i64 %tmp3) +; CHECK-NOT: call +; CHECK: call i8* @strncpy(i8* %arg2, i8* %tmp2, i64 1023) +; CHECK-NOT: call + + ret i8* %tmp4 +; CHECK: ret +} + +define i8* @strncpy(i8* %arg1, i8* %arg2, i64 %size) { +bb: + %result = call i8* @my_special_strncpy(i8* %arg1, i8* %arg2, i64 %size) + ret i8* %result +} + +declare i8* @my_special_strncpy(i8* %arg1, i8* %arg2, i64 %size) + +declare i64 @llvm.objectsize.i64.p0i8(i8*, i1, i1) + +declare i8* @__strncpy_chk(i8*, i8*, i64, i64) + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) diff --git a/test/Other/new-pass-manager.ll b/test/Other/new-pass-manager.ll index bf8e596d118..35f596e7798 100644 --- a/test/Other/new-pass-manager.ll +++ b/test/Other/new-pass-manager.ll @@ -23,6 +23,7 @@ ; CHECK-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(CGSCCAnalysisManager|AnalysisManager<.*LazyCallGraph::SCC.*>).*}},{{.*}}Module> ; CHECK-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(FunctionAnalysisManager|AnalysisManager<.*Function.*>).*}},{{.*}}Module> ; CHECK-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-CGSCC-PASS-NEXT: Running analysis: TargetLibraryAnalysis ; CHECK-CGSCC-PASS-NEXT: Running an SCC pass across the RefSCC: [(foo)] ; CHECK-CGSCC-PASS-NEXT: Starting CGSCC pass manager run ; CHECK-CGSCC-PASS-NEXT: Running pass: NoOpCGSCCPass @@ -407,6 +408,7 @@ ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(CGSCCAnalysisManager|AnalysisManager<.*LazyCallGraph::SCC.*>).*}},{{.*}}Module> ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*(FunctionAnalysisManager|AnalysisManager<.*Function.*>).*}},{{.*}}Module> ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-REPEAT-CGSCC-PASS-NEXT: Running analysis: TargetLibraryAnalysis ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running an SCC pass across the RefSCC: [(foo)] ; CHECK-REPEAT-CGSCC-PASS-NEXT: Starting CGSCC pass manager run ; CHECK-REPEAT-CGSCC-PASS-NEXT: Running pass: RepeatedPass diff --git a/unittests/Analysis/CGSCCPassManagerTest.cpp b/unittests/Analysis/CGSCCPassManagerTest.cpp index d46d9535fa4..e2481826597 100644 --- a/unittests/Analysis/CGSCCPassManagerTest.cpp +++ b/unittests/Analysis/CGSCCPassManagerTest.cpp @@ -9,6 +9,7 @@ #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" @@ -227,6 +228,7 @@ public: "entry:\n" " ret void\n" "}\n")) { + MAM.registerPass([&] { return TargetLibraryAnalysis(); }); MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp index 65730486cd7..9e7e128bcfb 100644 --- a/unittests/Analysis/LazyCallGraphTest.cpp +++ b/unittests/Analysis/LazyCallGraphTest.cpp @@ -216,10 +216,17 @@ static const char DiamondOfTrianglesRefGraph[] = " ret void\n" "}\n"; +static LazyCallGraph buildCG(Module &M) { + TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple())); + TargetLibraryInfo TLI(TLII); + LazyCallGraph CG(M, TLI); + return CG; +} + TEST(LazyCallGraphTest, BasicGraphFormation) { LLVMContext Context; std::unique_ptr M = parseAssembly(Context, DiamondOfTriangles); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // The order of the entry nodes should be stable w.r.t. the source order of // the IR, and everything in our module is an entry node, so just directly @@ -407,7 +414,7 @@ TEST(LazyCallGraphTest, BasicGraphMutation) { "entry:\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); LazyCallGraph::Node &A = CG.get(lookupFunction(*M, "a")); LazyCallGraph::Node &B = CG.get(lookupFunction(*M, "b")); @@ -445,7 +452,7 @@ TEST(LazyCallGraphTest, BasicGraphMutation) { TEST(LazyCallGraphTest, InnerSCCFormation) { LLVMContext Context; std::unique_ptr M = parseAssembly(Context, DiamondOfTriangles); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Now mutate the graph to connect every node into a single RefSCC to ensure // that our inner SCC formation handles the rest. @@ -542,7 +549,7 @@ TEST(LazyCallGraphTest, MultiArmSCC) { " call void @f1()\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -593,7 +600,7 @@ TEST(LazyCallGraphTest, OutgoingEdgeMutation) { "entry:\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -739,7 +746,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertion) { // a3--a2 | // std::unique_ptr M = parseAssembly(Context, DiamondOfTriangles); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -831,7 +838,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionRefGraph) { // references rather than calls. std::unique_ptr M = parseAssembly(Context, DiamondOfTrianglesRefGraph); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -938,7 +945,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeCallCycle) { "entry:\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1015,7 +1022,7 @@ TEST(LazyCallGraphTest, IncomingEdgeInsertionLargeRefCycle) { "entry:\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1077,7 +1084,7 @@ TEST(LazyCallGraphTest, InlineAndDeleteFunction) { // a3--a2 | // std::unique_ptr M = parseAssembly(Context, DiamondOfTriangles); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1221,7 +1228,7 @@ TEST(LazyCallGraphTest, InternalEdgeMutation) { " call void @a()\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1315,7 +1322,7 @@ TEST(LazyCallGraphTest, InternalEdgeRemoval) { " store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1390,7 +1397,7 @@ TEST(LazyCallGraphTest, InternalNoOpEdgeRemoval) { " store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1467,7 +1474,7 @@ TEST(LazyCallGraphTest, InternalCallEdgeToRef) { " call void @c()\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1560,7 +1567,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCall) { " store void()* @a, void()** undef\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1672,7 +1679,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallNoCycleInterleaved) { " store void()* @a, void()** undef\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1802,7 +1809,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeToCallBothPartitionAndMerge) { " store void()* @a, void()** undef\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -1885,7 +1892,7 @@ TEST(LazyCallGraphTest, HandleBlockAddress) { " store i8* blockaddress(@f, %bb), i8** %ptr\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); CG.buildRefSCCs(); auto I = CG.postorder_ref_scc_begin(); @@ -1933,7 +1940,7 @@ TEST(LazyCallGraphTest, ReplaceNodeFunction) { " store i8* bitcast (void(i8**)* @d to i8*), i8** %ptr\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Force the graph to be fully expanded. CG.buildRefSCCs(); @@ -2011,7 +2018,7 @@ TEST(LazyCallGraphTest, RemoveFunctionWithSpurriousRef) { "entry:\n" " ret void\n" "}\n"); - LazyCallGraph CG(*M); + LazyCallGraph CG = buildCG(*M); // Insert spurious ref edges. LazyCallGraph::Node &AN = CG.get(lookupFunction(*M, "a"));