return;
Node &N = *NI->second;
- NodeMap.erase(NI);
-
- // Remove this from the entry edges if present.
- EntryEdges.removeEdgeInternal(N);
// Cannot remove a function which has yet to be visited in the DFS walk, so
// if we have a node at all then we must have an SCC and RefSCC.
assert(CI != SCCMap.end() &&
"Tried to remove a node without an SCC after DFS walk started!");
SCC &C = *CI->second;
+ RefSCC *RC = &C.getOuterRefSCC();
+
+ // In extremely rare cases, we can delete a dead function which is still in a
+ // non-trivial RefSCC. This can happen due to spurious ref edges sticking
+ // around after an IR function reference is removed.
+ if (RC->size() != 1) {
+ SmallVector<Node *, 0> NodesInRC;
+ for (SCC &OtherC : *RC) {
+ for (Node &OtherN : OtherC)
+ NodesInRC.push_back(&OtherN);
+ }
+ for (Node *OtherN : NodesInRC) {
+ if ((*OtherN)->lookup(N)) {
+ auto NewRefSCCs =
+ RC->removeInternalRefEdge(*OtherN, ArrayRef<Node *>(&N));
+ // If we've split into multiple RefSCCs, RC is now invalid and the
+ // RefSCC containing C will be different.
+ if (!NewRefSCCs.empty())
+ RC = &C.getOuterRefSCC();
+ }
+ }
+ }
+
+ NodeMap.erase(NI);
+ EntryEdges.removeEdgeInternal(N);
SCCMap.erase(CI);
- RefSCC &RC = C.getOuterRefSCC();
// This node must be the only member of its SCC as it has no callers, and
// that SCC must be the only member of a RefSCC as it has no references.
// Validate these properties first.
assert(C.size() == 1 && "Dead functions must be in a singular SCC");
- assert(RC.size() == 1 && "Dead functions must be in a singular RefSCC");
+ assert(RC->size() == 1 && "Dead functions must be in a singular RefSCC");
// Finally clear out all the data structures from the node down through the
// components. postorder_ref_scc_iterator will skip empty RefSCCs, so no need
N.G = nullptr;
N.F = nullptr;
C.clear();
- RC.clear();
- RC.G = nullptr;
+ RC->clear();
+ RC->G = nullptr;
// Nothing to delete as all the objects are allocated in stable bump pointer
// allocators.
EXPECT_EQ(&DN, &(*BN)[DN].getNode());
}
-TEST(LazyCallGraphTest, RemoveFunctionWithSpurriousRef) {
+TEST(LazyCallGraphTest, RemoveFunctionWithSpuriousRef) {
LLVMContext Context;
// A graph with a couple of RefSCCs.
std::unique_ptr<Module> M =
EXPECT_EQ(CG.postorder_ref_scc_end(), I);
}
+TEST(LazyCallGraphTest, RemoveFunctionWithSpuriousRefRecursive) {
+ LLVMContext Context;
+ std::unique_ptr<Module> M =
+ parseAssembly(Context, "define void @a(ptr %p) {\n"
+ " store ptr @b, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @b(ptr %p) {\n"
+ " store ptr @c, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @c(ptr %p) {\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG = buildCG(*M);
+
+ LazyCallGraph::Node &AN = CG.get(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &BN = CG.get(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &CN = CG.get(lookupFunction(*M, "c"));
+ AN.populate();
+ BN.populate();
+ CN.populate();
+ // Insert spurious ref edge.
+ CG.insertEdge(CN, AN, LazyCallGraph::Edge::Ref);
+
+ // Force the graph to be fully expanded.
+ CG.buildRefSCCs();
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ ASSERT_EQ(RC.size(), 3);
+
+ EXPECT_EQ(&RC, CG.lookupRefSCC(AN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(BN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(CN));
+
+ // Now delete 'a'. There are no uses of this function but there are
+ // spurious references.
+ CG.removeDeadFunction(AN.getFunction());
+
+ // The only observable change should be that the RefSCC is gone from the
+ // postorder sequence.
+ I = CG.postorder_ref_scc_begin();
+ EXPECT_EQ(CG.lookupRefSCC(CN), &*I++);
+ EXPECT_EQ(CG.lookupRefSCC(BN), &*I++);
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+}
+
+TEST(LazyCallGraphTest, RemoveFunctionWithSpuriousRefRecursive2) {
+ LLVMContext Context;
+ std::unique_ptr<Module> M =
+ parseAssembly(Context, "define void @a(ptr %p) {\n"
+ " store ptr @b, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @b(ptr %p) {\n"
+ " store ptr @c, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @c(ptr %p) {\n"
+ " store ptr @b, ptr %p\n"
+ " store ptr @d, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @d(ptr %p) {\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG = buildCG(*M);
+
+ LazyCallGraph::Node &AN = CG.get(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &BN = CG.get(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &CN = CG.get(lookupFunction(*M, "c"));
+ LazyCallGraph::Node &DN = CG.get(lookupFunction(*M, "d"));
+ AN.populate();
+ BN.populate();
+ CN.populate();
+ DN.populate();
+ // Insert spurious ref edge.
+ CG.insertEdge(DN, AN, LazyCallGraph::Edge::Ref);
+
+ // Force the graph to be fully expanded.
+ CG.buildRefSCCs();
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ ASSERT_EQ(4, RC.size());
+
+ EXPECT_EQ(&RC, CG.lookupRefSCC(AN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(BN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(CN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(DN));
+
+ // Now delete 'a'. There are no uses of this function but there are
+ // spurious references.
+ CG.removeDeadFunction(AN.getFunction());
+
+ // The only observable change should be that the RefSCC is gone from the
+ // postorder sequence.
+ I = CG.postorder_ref_scc_begin();
+ EXPECT_EQ(CG.lookupRefSCC(DN), &*I++);
+ EXPECT_EQ(CG.lookupRefSCC(CN), &*I);
+ EXPECT_EQ(CG.lookupRefSCC(BN), &*I++);
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+}
+
+TEST(LazyCallGraphTest, RemoveFunctionWithSpuriousRefRecursive3) {
+ LLVMContext Context;
+ std::unique_ptr<Module> M =
+ parseAssembly(Context, "define void @a(ptr %p) {\n"
+ " store ptr @b, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @b(ptr %p) {\n"
+ " store ptr @c, ptr %p\n"
+ " ret void\n"
+ "}\n"
+ "define void @c(ptr %p) {\n"
+ " ret void\n"
+ "}\n");
+ LazyCallGraph CG = buildCG(*M);
+
+ LazyCallGraph::Node &AN = CG.get(lookupFunction(*M, "a"));
+ LazyCallGraph::Node &BN = CG.get(lookupFunction(*M, "b"));
+ LazyCallGraph::Node &CN = CG.get(lookupFunction(*M, "c"));
+ AN.populate();
+ BN.populate();
+ CN.populate();
+ // Insert spurious ref edges.
+ CG.insertEdge(CN, AN, LazyCallGraph::Edge::Ref);
+ CG.insertEdge(BN, AN, LazyCallGraph::Edge::Ref);
+
+ // Force the graph to be fully expanded.
+ CG.buildRefSCCs();
+ auto I = CG.postorder_ref_scc_begin();
+ LazyCallGraph::RefSCC &RC = *I++;
+ EXPECT_EQ(CG.postorder_ref_scc_end(), I);
+
+ ASSERT_EQ(RC.size(), 3);
+
+ EXPECT_EQ(&RC, CG.lookupRefSCC(AN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(BN));
+ EXPECT_EQ(&RC, CG.lookupRefSCC(CN));
+
+ // Now delete 'a'. There are no uses of this function but there are
+ // spurious references.
+ CG.removeDeadFunction(AN.getFunction());
+
+ // The only observable change should be that the RefSCC is gone from the
+ // postorder sequence.
+ I = CG.postorder_ref_scc_begin();
+ EXPECT_EQ(CG.lookupRefSCC(CN), &*I++);
+ EXPECT_EQ(CG.lookupRefSCC(BN), &*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"