This is an archive of the discontinued LLVM Phabricator instance.

[CGSCC][NewPM] Fix adding mutually recursive new functions
ClosedPublic

Authored by aeubanks on Sep 14 2020, 11:17 AM.

Details

Summary

When adding a new function via addNewFunctionIntoRefSCC(), it creates a
new node and immediately populates the edges. Since populateSlow() calls
G->get() on all referenced functions, it will create a node (but not
populate it) for functions that haven't yet been added. If we add two
mutually recursive functions, the assert that the node should never have
been created will fire when the second function is added. So here we
remove that assert since the node may have already been created (but not
yet populated).

createNode() is only called from addNewFunctionInto{,Ref}SCC().

https://bugs.llvm.org/show_bug.cgi?id=47502

Diff Detail

Event Timeline

aeubanks created this revision.Sep 14 2020, 11:17 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 14 2020, 11:17 AM
aeubanks requested review of this revision.Sep 14 2020, 11:17 AM

Does it make sense to copy the unit test and keep the original one as well?

aeubanks updated this revision to Diff 292013.Sep 15 2020, 1:24 PM

separate test into its own

jdoerfert added inline comments.Sep 15 2020, 1:31 PM
llvm/lib/Analysis/LazyCallGraph.cpp
1599

If lookup succeeds, should we just return the result or does it make sense to re-populate etc? We could call this getOrCreateNode?

aeubanks added inline comments.Sep 15 2020, 3:25 PM
llvm/lib/Analysis/LazyCallGraph.cpp
1599

get() will create a node but not populate it. In the adding mutually recursive functions case, we will have called get() on a new function, creating a new node, but not populate it. Then when we add the second function, the corresponding node will exist but not be populated, so it needs to be populated then.

So I think the current name still makes sense, it's "creating" a node in the sense that it populates it, although the node may have already been allocated. Open to suggestion though.

This revision is now accepted and ready to land.Sep 15 2020, 3:31 PM

I'd like to revisit this and open a discussion. The createNode is only called in addNewFunctionIntoSCC and addNewFunctionIntoRefSCC, where the former is only called in unit tests and the latter is only used by co-routines. I'm trying to understand what the expected changes in the CallGraph are for some of these methods, if this is specific to coroutines or there are other usecases and if we can get better testing.
Is registerOutlinedFunction used out-of tree?

@jdoerfert, @modocache: Could you help me with some context please? (I'm happy to use llvm-dev@ for the discussion too.)

I'd like to revisit this and open a discussion.

Sure. I assume we don't need to revert the patch but we can start a discussion with no pressure, right?

The createNode is only called in addNewFunctionIntoSCC and addNewFunctionIntoRefSCC, where the former is only called in unit tests and the latter is only used by co-routines. I'm trying to understand what the expected changes in the CallGraph are for some of these methods, if this is specific to coroutines or there are other usecases and if we can get better testing.
Is registerOutlinedFunction used out-of tree?

Except the Attributor and coroutines there is no user of the CallGraphUpdater (I know of).
The Attributor does not yet use this function but it is not unreasonable to expect it will be used.
There is a good chance the "shallow wrapper" functionality should actually do so, though that feature is on its own not the most useful anyway.

@jdoerfert, @modocache: Could you help me with some context please? (I'm happy to use llvm-dev@ for the discussion too.)

I'm fine with LLVM-dev, TBH I need to talk about updating the LazyCallGraph soon anyway. I think the current &updateCGAndAnalysisManagerForCGSCCPass in conjunction with the ::run method don't allow you to delete more than one function from the SCC at a time, which is bad. I might simply use it wrong but whatever it is, I'm all for a discussion on how we expose new PM CGSCC updates :) [= how we properly replace/implement something like the CallGraphUpdater]

I'd like to revisit this and open a discussion.

Sure. I assume we don't need to revert the patch but we can start a discussion with no pressure, right?

Yes, exactly. The status before this patch was broken anyway, no sense reverting. I'm looking to understand whether there are ways to improve the infrastructure/APIs.

The createNode is only called in addNewFunctionIntoSCC and addNewFunctionIntoRefSCC, where the former is only called in unit tests and the latter is only used by co-routines. I'm trying to understand what the expected changes in the CallGraph are for some of these methods, if this is specific to coroutines or there are other usecases and if we can get better testing.
Is registerOutlinedFunction used out-of tree?

Except the Attributor and coroutines there is no user of the CallGraphUpdater (I know of).
The Attributor does not yet use this function but it is not unreasonable to expect it will be used.
There is a good chance the "shallow wrapper" functionality should actually do so, though that feature is on its own not the most useful anyway.

@jdoerfert, @modocache: Could you help me with some context please? (I'm happy to use llvm-dev@ for the discussion too.)

I'm fine with LLVM-dev, TBH I need to talk about updating the LazyCallGraph soon anyway. I think the current &updateCGAndAnalysisManagerForCGSCCPass in conjunction with the ::run method don't allow you to delete more than one function from the SCC at a time, which is bad. I might simply use it wrong but whatever it is, I'm all for a discussion on how we expose new PM CGSCC updates :) [= how we properly replace/implement something like the CallGraphUpdater]

SG, let's move the discussion to llvm-dev.
My goal for now is to understand the uses and desired updates, so the APIs doing the updates can be dedicated to those updates. For example, I'm not clear that the update used for the cloned functions in coroutines should be the same as for outlining, but perhaps a common API is the right approach. I don't have enough info at this point, and there aren't any uses for the outlining case that I can see.

We have quite a few "outlining" passes but most are module passes I suppose.
Assuming we want to work towards CGSCC passes, and maybe, maybe parallelize them at some point, we should allow proper updates of the CG.

I am reasonably certain we will soonish look at integrating functionality that will subsume hot-cold splitting into the Attributor. At that point we will do real "outlining". Other use cases are certainly not unreasonable. What I try to say is that CoroSplit is probably not the only user of outlining.

We have quite a few "outlining" passes but most are module passes I suppose.
Assuming we want to work towards CGSCC passes, and maybe, maybe parallelize them at some point, we should allow proper updates of the CG.

I am reasonably certain we will soonish look at integrating functionality that will subsume hot-cold splitting into the Attributor. At that point we will do real "outlining". Other use cases are certainly not unreasonable. What I try to say is that CoroSplit is probably not the only user of outlining.

Sounds great, that's exactly what I'd like to understand as use cases :-).

When I was looking into the history of these functions, I also felt like I was missing some background.

Also, I'm running into an issue where the newly added functions aren't having passes run on them. It seems like new call edges to outlined functions should be handled in updateCGAndAnalysisManagerForPass() and the addNewFunctionInto*SCC() shouldn't be necessary. In fact, there are a couple TODOs there about allowing more than just new trivial call/ref edges. Looking into that now.