This is an archive of the discontinued LLVM Phabricator instance.

[AA] A global cannot escape through nocapture/nocallback call.
ClosedPublic

Authored by vzakhari on Nov 18 2022, 3:56 PM.

Details

Summary

When an internal global is passed to a 'nocallback' call as
a 'nocapture' pointer, it cannot escape through this call and
be indirectly referenced in this module.
So it must not alias with any pointer in the module.

This may provide some remedy for Fortran module-private array descriptors
that are usually passed by address to some runtime functions
(e.g. to allocation/deallocation functions). In general, a good aliasing
information derived from Fortran language rules would solve the same issue,
but I think this change may be beneficial as-is (given that nocapture,
nocallback attributes are properly set).

Diff Detail

Event Timeline

vzakhari created this revision.Nov 18 2022, 3:56 PM
Herald added a project: Restricted Project. · View Herald TranscriptNov 18 2022, 3:56 PM
Herald added a subscriber: hiraditya. · View Herald Transcript
vzakhari requested review of this revision.Nov 18 2022, 3:56 PM
Herald added a project: Restricted Project. · View Herald TranscriptNov 18 2022, 3:56 PM

I like the idea. The tests should be augmented though. First, run sroa on the test, there is no need for such verbose IR.
Then, add a second version for all of them that calls an unknown function in the loop. As written, I can always hoist all of them, even if we don't do it yet.
I would personally use the check line script but I guess that one can check it like this too.

llvm/lib/Analysis/GlobalsModRef.cpp
367

Usually we check the call for attributes, not the function.

llvm/test/Analysis/GlobalsModRef/noescape-nocapture-nocallback.ll
5

No tripple needed.

153

Maybe write it inline, easier to read.
declare ... nocallback

vzakhari updated this revision to Diff 476653.Nov 18 2022, 8:11 PM

Thank you for the review, Johannes!

I am not sure why the hoisting was legal in the previous version. I guess it has something to do with the fact that clobbering a part of pointer with -1 may be considering an undefined behavior, so I changed the test to store a pointer inside the loop.
I also added cases with an unknown call, but it cannot be just any unknown call. Without nosync or nocallback a call may modify a global even if it was not captured.

jdoerfert accepted this revision.Nov 19 2022, 11:51 AM

LG

Thank you for the review, Johannes!

I am not sure why the hoisting was legal in the previous version. I guess it has something to do with the fact that clobbering a part of pointer with -1 may be considering an undefined behavior, so I changed the test to store a pointer inside the loop.

So, I didn't even see the UB, which was there. It was legal to hoist, and is in the versions w/o a call, because nothing in the loop can effectively alias the obj global.
The store can still not alias the obj even if we don't figure that out right now. Consequently, the obj is invariant and can be hoisted just after the function that takes obj.
The version with the unknown call are different. If obj escaped earlier, then the unknown call might have an impact on it and we cannot hoist the load.

I also added cases with an unknown call, but it cannot be just any unknown call. Without nosync or nocallback a call may modify a global even if it was not captured.

Fair point. We want to check that we know it wasn't captured, so the annotated unknown call looks good.

We could even do better in the nocapture case w/o nocallback, maybe add a TODO as this case can be handled if all uses in the module are nocapture effectively.

This revision is now accepted and ready to land.Nov 19 2022, 11:51 AM

We could even do better in the nocapture case w/o nocallback, maybe add a TODO as this case can be handled if all uses in the module are nocapture effectively.

Good point. I will add a note.

llvm/test/Analysis/GlobalsModRef/noescape-nocapture-nocallback.ll
67

Johannes, I have hard time figuring out why this code may be optimized without the capture analysis.

Doesn't this store conflict with the above load, in general? I think it can, if somewhere inside the module I do obj0.p = obj0. So the capture analysis is required to prove that they do not conflict.

vzakhari updated this revision to Diff 478288.Nov 28 2022, 11:15 AM

Added TODO + rebase.

This revision was landed with ongoing or failed builds.Nov 28 2022, 12:58 PM
This revision was automatically updated to reflect the committed changes.