This is an archive of the discontinued LLVM Phabricator instance.

[CallGraph][FIX] Ensure generic intrinsics are represented in the CG
ClosedPublic

Authored by jdoerfert on Jan 7 2023, 1:15 AM.

Details

Summary

Intrinsics have historically been excluded from the call graph with an
exception of 3 special ones added at some point. This meant that passes
depending on the call graph needed to handle intrinsics explicitly as
the underlying assumption, namely that intrinsics can't call or modify
things, doesn't hold. We are slowly moving away from special handling of
intrinsics, or at least towards explicitly checking what intrinsics we
want to handle differently.

This patch:

  • Includes most intrinsics in the call graph. "Assume-like" intrinsics are excluded. What assume-like means is a question for a later time.
  • Fixes the annotations on patchpoint intrinsics as they can (and will) callback into the module.
  • Removes the special handling of intrinsics in the GlobalsAA pass.
  • Removes the IntrinsicInst::isLeaf method.

Properly
Fixes: https://github.com/llvm/llvm-project/issues/52706

See also:
https://discourse.llvm.org/t/intrinsics-are-not-special-stop-pretending-i-mean-it/67545

Diff Detail

Event Timeline

jdoerfert created this revision.Jan 7 2023, 1:15 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 7 2023, 1:15 AM
jdoerfert requested review of this revision.Jan 7 2023, 1:15 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 7 2023, 1:15 AM
arsenm added a comment.Jan 7 2023, 5:35 AM

Should include the GVN testcase

llvm/lib/Analysis/CallGraph.cpp
102

This is only tangentially related but this misses call targets from constantexpr casted functions, and aliases. I was debugging some issue related to this not long ago

Should include the GVN testcase

Good point, I'll add https://godbolt.org/z/qn3YhWEKo

llvm/lib/Analysis/CallGraph.cpp
102

Please fix that independently. Let's keep this about intrinsics.

jdoerfert updated this revision to Diff 487115.Jan 7 2023, 11:47 AM

Add test case, fix oversight.

arsenm accepted this revision.Jan 9 2023, 10:59 AM

The previous handling seemed like a weak attempt to define nocallback

This revision is now accepted and ready to land.Jan 9 2023, 10:59 AM
nikic added a subscriber: nikic.Jan 9 2023, 11:40 AM

Can you please a) land the changes to patchpoint attributes and b) the conversion from isLeaf to !NoCallback separately, and then rebase this? Those two changes are obviously correct and desirable, but I'm not sure about the rest of the intrinsic intrinsic modelling here. It doesn't really seem the job of the call graph to model nocallback intrinsics?

nikic added inline comments.Jan 9 2023, 11:43 AM
llvm/lib/Analysis/CallGraph.cpp
37

So this means that for example llvm.umax is part of the call graph but llvm.objectsize is not -- the intended logic here isn't clear to me.

llvm/test/Transforms/GVN/intrinsics_in_cg.ll
1

Precommit test.

9

intrinsic

jdoerfert marked 2 inline comments as done.EditedJan 9 2023, 2:12 PM

Can you please a) land the changes to patchpoint attributes and b) the conversion from isLeaf to !NoCallback separately, and then rebase this? Those two changes are obviously correct and desirable, but I'm not sure about the rest of the intrinsic intrinsic modelling here. It doesn't really seem the job of the call graph to model nocallback intrinsics?

a) Will do, also the test.
b) This is not as simple as there are two isLeaf uses that mean different things. See below.

W/o modeling intrinsics we try to make up for them in other places, e.g., GlobalsAA. That is arguably undesirable. It is also not about modeling nocallback intrinsics per se.
Right now, we add most intrinsics to the CG but only add *incoming* egdges for isLeaf intrinsics and never outgoing edges. Which means our CG is totally broken and mostly useless wrt. intrinsics.
Even the leaf ones are not represented reasonably because they are called but themselves call nothing (according to the CG). That works for some other magic reasons.

With this patch we add incoming and outgoing edges to intrinsic nodes where they make sense. So if the node is called, it has an incoming edge, if it can call back, it has an outgoing edge.
These are two separate changes and both needed to make the CG sane.

TLDR; Intrinsics are not special in any way. We should treat them like function declarations, which this patch does.

llvm/lib/Analysis/CallGraph.cpp
37

It is as clear as it was before. We added both of the above to the call graph, but not dbg stuff. And then we did not add a single edge connecting the intrinsics we added (except 3), which means adding them was a waste to begin with and totally unhelpful. I'll roll back to !isDbgInfo but that makes as little sense to me as the above.

jdoerfert updated this revision to Diff 487562.Jan 9 2023, 2:43 PM

Precommit test, only exclude dbg intrinsics from the CG (as before), precommit the patchpoint attribute change.

Right now, we add most intrinsics to the CG but only add *incoming* egdges for isLeaf intrinsics and never outgoing edges. Which means our CG is totally broken and mostly useless wrt. intrinsics.
Even the leaf ones are not represented reasonably because they are called but themselves call nothing (according to the CG). That works for some other magic reasons.

I don't think this is quite right: For !isLeaf intrinsics an edge to CallsExternalNode is added, not to the intrinsic. This seems consistent with the current model where intrinsics themselves are not part of the call graph, so only the call that may be done by the intrinsic is represented in the CG.

With this patch we add incoming and outgoing edges to intrinsic nodes where they make sense. So if the node is called, it has an incoming edge, if it can call back, it has an outgoing edge.
These are two separate changes and both needed to make the CG sane.

TLDR; Intrinsics are not special in any way. We should treat them like function declarations, which this patch does.

I don't really agree that intrinsics aren't special. Intrinsics are basically an extensible mechanism for custom instructions. Some intrinsics may be call like (those that are !nocallback), some intrinsics may be load/store like (those with memory effects) and some may be more like arithmetic instructions (most intrinsics). With that in mind, I don't think it's unreasonable for a call graph to only model the "call like" intrinsics.

All that being said, as we do happen to encode intrinsics as calls, it's also not unreasonable to model all intrinsics in the call graph either. And it does look like this makes the implementation simpler overall, so that seems fine as long as it doesn't regress optimization. But see the note inline -- I don't think your change to GlobalsAA is right.

llvm/lib/Analysis/GlobalsModRef.cpp
609

This change doesn't look right to me: Now you're going to handle intrinsic calls both via the call graph, and via the crude code directly below. After your changes, shouldn't be be doing a continue on all calls instead? Why is this not causing any test failures -- are we missing coverage? Or am I completely misunderstanding something here?

Right now, we add most intrinsics to the CG but only add *incoming* egdges for isLeaf intrinsics and never outgoing edges. Which means our CG is totally broken and mostly useless wrt. intrinsics.
Even the leaf ones are not represented reasonably because they are called but themselves call nothing (according to the CG). That works for some other magic reasons.

I don't think this is quite right: For !isLeaf intrinsics an edge to CallsExternalNode is added, not to the intrinsic. This seems consistent with the current model where intrinsics themselves are not part of the call graph, so only the call that may be done by the intrinsic is represented in the CG.

You are right, we don't call the intrinsic but the external node. That is even worse. Why do we have the intrinsics in the CG in the first place (among other things) if we don't connect them at all?

With this patch we add incoming and outgoing edges to intrinsic nodes where they make sense. So if the node is called, it has an incoming edge, if it can call back, it has an outgoing edge.
These are two separate changes and both needed to make the CG sane.

TLDR; Intrinsics are not special in any way. We should treat them like function declarations, which this patch does.

I don't really agree that intrinsics aren't special. Intrinsics are basically an extensible mechanism for custom instructions. Some intrinsics may be call like (those that are !nocallback), some intrinsics may be load/store like (those with memory effects) and some may be more like arithmetic instructions (most intrinsics). With that in mind, I don't think it's unreasonable for a call graph to only model the "call like" intrinsics.

FWIW, an intrinsic is (in our encoding) a call. In our model it will execute some code.
Given a call to an intrinsic llvm.abc [attrs] and a function declaration abc [attrs], all we know is [attrs].
The details, e.g., "call like", is part of [attrs] in both cases. If we stop distinguishing them above we will remove bugs and special handling code.

All that being said, as we do happen to encode intrinsics as calls, it's also not unreasonable to model all intrinsics in the call graph either. And it does look like this makes the implementation simpler overall, so that seems fine as long as it doesn't regress optimization. But see the note inline -- I don't think your change to GlobalsAA is right.

Will be addressed before I commit this.


I'm assuming with the fix to GlobalsAA you are OK with this now. I'll commit at some point today.

llvm/lib/Analysis/GlobalsModRef.cpp
609

Agreed, we should do continue. I'll fix that pre-commit.

For intrinsics and declarations the change didn't matter since the code below should just read the attributes again. That's all we knew about them to begin with.
For definitions we could know more then the attributes, but that's somewhat unlikely at this stage since GlobalsAA doesn't really do much deduction, just summarizes the body. Assuming the attributes already summarize the body properly, there would not be a change in the MRI computed for the function.
Still worth skipping them.

we should really replace all the remaining (only 4 passes AFAICT) uses of this old CallGraph in favor of LazyCallGraph

we should really replace all the remaining (only 4 passes AFAICT) uses of this old CallGraph in favor of LazyCallGraph

That would be great. We'll have a proper GVN test for the problem this is solving by then :)