mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-21 03:28:13 +00:00
[LCG] Teach the ref edge removal to handle a ref edge that is trivial
due to a call cycle. This actually crashed the ref removal before. I've added a unittest that covers this kind of interesting graph structure and mutation. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@290645 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
effab69f94
commit
3ab88a9f0d
@ -1117,6 +1117,13 @@ LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN, Node &TargetN) {
|
||||
if (&SourceN == &TargetN)
|
||||
return Result;
|
||||
|
||||
// If this ref edge is within an SCC then there are sufficient other edges to
|
||||
// form a cycle without this edge so removing it is a no-op.
|
||||
SCC &SourceC = *G->lookupSCC(SourceN);
|
||||
SCC &TargetC = *G->lookupSCC(TargetN);
|
||||
if (&SourceC == &TargetC)
|
||||
return Result;
|
||||
|
||||
// We build somewhat synthetic new RefSCCs by providing a postorder mapping
|
||||
// for each inner SCC. We also store these associated with *nodes* rather
|
||||
// than SCCs because this saves a round-trip through the node->SCC map and in
|
||||
@ -1139,7 +1146,6 @@ LazyCallGraph::RefSCC::removeInternalRefEdge(Node &SourceN, Node &TargetN) {
|
||||
// and handle participants in that cycle without walking all the edges that
|
||||
// form the connections, and instead by relying on the fundamental guarantee
|
||||
// coming into this operation.
|
||||
SCC &TargetC = *G->lookupSCC(TargetN);
|
||||
for (Node &N : TargetC)
|
||||
PostOrderMapping[&N] = RootPostOrderNumber;
|
||||
|
||||
|
@ -1600,6 +1600,84 @@ TEST(LazyCallGraphTest, InternalEdgeRemoval) {
|
||||
EXPECT_EQ(E, J);
|
||||
}
|
||||
|
||||
TEST(LazyCallGraphTest, InternalNoOpEdgeRemoval) {
|
||||
LLVMContext Context;
|
||||
// A graph with a single cycle formed both from call and reference edges
|
||||
// which makes the reference edges trivial to delete. The graph looks like:
|
||||
//
|
||||
// Reference edges: a -> b -> c -> a
|
||||
// Call edges: a -> c -> b -> a
|
||||
std::unique_ptr<Module> M = parseAssembly(
|
||||
Context, "define void @a(i8** %ptr) {\n"
|
||||
"entry:\n"
|
||||
" call void @b(i8** %ptr)\n"
|
||||
" store i8* bitcast (void(i8**)* @c to i8*), i8** %ptr\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @b(i8** %ptr) {\n"
|
||||
"entry:\n"
|
||||
" store i8* bitcast (void(i8**)* @a to i8*), i8** %ptr\n"
|
||||
" call void @c(i8** %ptr)\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @c(i8** %ptr) {\n"
|
||||
"entry:\n"
|
||||
" call void @a(i8** %ptr)\n"
|
||||
" store i8* bitcast (void(i8**)* @b to i8*), i8** %ptr\n"
|
||||
" ret void\n"
|
||||
"}\n");
|
||||
LazyCallGraph CG(*M);
|
||||
|
||||
// Force the graph to be fully expanded.
|
||||
auto I = CG.postorder_ref_scc_begin(), E = CG.postorder_ref_scc_end();
|
||||
LazyCallGraph::RefSCC &RC = *I;
|
||||
EXPECT_EQ(E, std::next(I));
|
||||
|
||||
LazyCallGraph::SCC &C = *RC.begin();
|
||||
EXPECT_EQ(RC.end(), std::next(RC.begin()));
|
||||
|
||||
LazyCallGraph::Node &AN = *CG.lookup(lookupFunction(*M, "a"));
|
||||
LazyCallGraph::Node &BN = *CG.lookup(lookupFunction(*M, "b"));
|
||||
LazyCallGraph::Node &CN = *CG.lookup(lookupFunction(*M, "c"));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(AN));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(BN));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(CN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(AN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(BN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(CN));
|
||||
|
||||
// Remove the edge from a -> c which doesn't change anything.
|
||||
SmallVector<LazyCallGraph::RefSCC *, 1> NewRCs =
|
||||
RC.removeInternalRefEdge(AN, CN);
|
||||
EXPECT_EQ(0u, NewRCs.size());
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(AN));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(BN));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(CN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(AN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(BN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(CN));
|
||||
auto J = CG.postorder_ref_scc_begin();
|
||||
EXPECT_EQ(I, J);
|
||||
EXPECT_EQ(&RC, &*J);
|
||||
EXPECT_EQ(E, std::next(J));
|
||||
|
||||
// Remove the edge from b -> a and c -> b; again this doesn't change
|
||||
// anything.
|
||||
NewRCs = RC.removeInternalRefEdge(BN, AN);
|
||||
NewRCs = RC.removeInternalRefEdge(CN, BN);
|
||||
EXPECT_EQ(0u, NewRCs.size());
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(AN));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(BN));
|
||||
EXPECT_EQ(&RC, CG.lookupRefSCC(CN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(AN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(BN));
|
||||
EXPECT_EQ(&C, CG.lookupSCC(CN));
|
||||
J = CG.postorder_ref_scc_begin();
|
||||
EXPECT_EQ(I, J);
|
||||
EXPECT_EQ(&RC, &*J);
|
||||
EXPECT_EQ(E, std::next(J));
|
||||
}
|
||||
|
||||
TEST(LazyCallGraphTest, InternalCallEdgeToRef) {
|
||||
LLVMContext Context;
|
||||
// A nice fully connected (including self-edges) SCC (and RefSCC)
|
||||
|
Loading…
Reference in New Issue
Block a user