-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Coroutines][LazyCallGraph] addSplitRefRecursiveFunctions allows spurious ref edges between new functions. #116285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0e33bea
d66bb39
e360642
9331a61
62882ec
92c9362
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1720,6 +1720,7 @@ void LazyCallGraph::addSplitRefRecursiveFunctions( | |
for (Function *NewFunction : NewFunctions) { | ||
Node &NewN = initNode(*NewFunction); | ||
|
||
// Make the original function reference each new function | ||
OriginalN->insertEdgeInternal(NewN, Edge::Kind::Ref); | ||
|
||
// Check if there is any edge from any new function back to any function in | ||
|
@@ -1775,8 +1776,9 @@ void LazyCallGraph::addSplitRefRecursiveFunctions( | |
if (F1 == F2) | ||
continue; | ||
Node &N2 = get(*F2); | ||
assert(!N1->lookup(N2)->isCall() && | ||
"Edges between new functions must be ref edges"); | ||
assert(!N1->lookup(N2) || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so it seems like we're not actually adding the spurious ref edges since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the confusion on this. I will make another PR for this change soon. |
||
(!N1->lookup(N2)->isCall() && | ||
TylerNowicki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"Edges between new functions must be ref edges")); | ||
} | ||
} | ||
#endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't
initNode
above populate the edges out of the node? that should already be adding the edges in the code you're adding belowThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this doesn't add edges between nodes if there are no edges between them. Right now when we create a continuation we clone the whole thing, the entire function and fixup the entry point, then it is added to the LazyCallGraph (so there is a phi in the continuation with references to all other new continuations), then do a clean-up that removes all the unused code including the phi.
Obviously cloning the whole function, just for the phi references, then removing all of the unused code is wasted compile-time. I will be making a change that prunes the BBs while creating the continuation. The only problem is now the phi with references to the other continuations is not there. But that phi being there is a bit artificial anyway since we remove it. Instead, this function can add the edges that were not represented in the continuation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LazyCallGraph models the actual IR, so if there aren't any references in the actual IR, there shouldn't be an edge in LazyCallGraph. The LazyCallGraph APIs should be called after all transformations are done. I think I'm not fully understanding your comment, a concrete example would be helpful.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you describe differs from what is happening right now (without my patch). Here are some more details on how it works right now. First for the coroutine function we create a single/unified return block for each suspend point and create a phi in the return BB that takes a reference to a continuation function. We create one empty continuation function for each suspend point. Then for each continuation function we clone the contents of the coroutine function in its entirety and fix-up the entry point. Notice that the return BB and its phi are retained in each continuation function. Then we call addSplitRefRecursiveFunctions and then we do simplify-cfg. The simplify-cfg removes all of the unnecessary blocks and as a result it will remove some or all of the edges in the phi we added to the return BB.
For example, if we have a coroutine like
Then we first generate
At this point we call addSplitRefRecursiveFunctions, adding the new continuation functions to the call graph and they will form a (Ref)SCC.
However, then we do simplify-cfg and the resulting functions look like
So, the question is, what is the correct call-graph? Based on previous comments I assumed we actually wanted to keep the current behavior and keep the continuation functions in the same (Ref)SCC. See an earlier comment by John.
The Async method and the returned-continuation method both use addSplitRefRecursiveFunctions to add their new continuation functions to the call graph. As John said we don't need to worry about the switch method because it uses addSplitFunction to add its funclets to the call graph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a unit test that resembles the above example.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just have addSplitRefRecursiveFunctions() call updateCGAndAnalysisManagerForCGSCCPass() then? Otherwise, it feels like we are just reinventing the function to identify the SCCs and RefSCCs in addSplitRefRecursiveFunctions(). The alternative is before adding the new functions to the CallGraph I add unreachable blocks to create 'fake' references between the new functions on the incoming edges of an unused phi node to give the new functions the appearance of being a RefSCC. Then after addSplitRefRecursiveFunctions() I delete those and call updateCGAndAnalysisManagerForCGSCCPass().
Both of these options don't sound great. It would be a lot better if the rule was that the callgraph matches the IR following completion of the pass. It seems overly restrictive to require the call-graph to match the IR at arbitrary points in the pass transformation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm confused based on the description and the previous discussion. Are you planning on first creating all the split functions in full (referencing each other), then one by one simplify-cfg each one, or are you planning on simplifying the cfg as you clone, meaning there's never actually a point where we have a fully connected RefSCC? The discussion above seems to imply the former, and the PR description seems to imply the latter.
If it's the former, then something like
is simple and should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My request is the latter option you gave. Sorry for the confusion. I will create an RFC to explain my request more concisely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another clarifying question, does the original function still always reference all newly split functions?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is the case. I don't see that changing.