This is an archive of the discontinued LLVM Phabricator instance.

[FunctionAttrs] Identify norecurse functions
ClosedPublic

Authored by jmolloy on Nov 2 2015, 5:06 AM.

Details

Summary

A function can be marked as norecurse if:

  • The SCC to which it belongs has cardinality 1.
  • It does not call any non-norecurse function. This includes self-recursion.

We can simply build up norecurse attributes in the existing SCC pass.

Diff Detail

Repository
rL LLVM

Event Timeline

jmolloy updated this revision to Diff 38894.Nov 2 2015, 5:06 AM
jmolloy retitled this revision from to [FunctionAttrs] Identify norecurse functions.
jmolloy updated this object.
jmolloy set the repository for this revision to rL LLVM.
jmolloy added a subscriber: llvm-commits.
jmolloy updated this revision to Diff 38901.Nov 2 2015, 5:32 AM
mehdi_amini edited edge metadata.Nov 2 2015, 11:22 AM

I don't know the CGSCC enough: what about a function whose address is taken? What about potential call site from a different module you have no visibility to?

Hi,

My understanding is that CGSCC behaves conservatively - if a function's address is taken, an edge is added from the "Externally called" node to that function. However, in our case we don't actually need to care what can *call* a function, we just need to know whether or not we have full knowledge about what that function *can call*. That's why we only mark functions norecurse if we've enumerated successfully all call edges and found them not to form a cycle or encounter the special "calls external" node in which case all bets are off.

James

reames added a subscriber: reames.Nov 4 2015, 9:53 AM
reames added inline comments.
lib/Transforms/IPO/FunctionAttrs.cpp
82

It looks like this change needs to be rebased over Chandler's recent ones. Can you update the patch?

1876

If there's a single element in the SCC, why do you need the any_of?

1884

The change needs to be conditional on whether the function was already marked recursive. Otherwise, you'll be misleading the wrapping code about changes being made.

test/Transforms/FunctionAttrs/norecurse.ll
10

"self_rec" rather than "g"?

16

for readability, I'd call these two something like: mutually_rec1 and mutually_rec2

manmanren edited edge metadata.Nov 4 2015, 11:44 AM

Thanks for working on it!
Manman

lib/Transforms/IPO/FunctionAttrs.cpp
1867

This comment seems to be incorrect. Should it be "must not call itself or any function that may be recursive"?

1876

Here it is checking the edges from the single element in the SCC.

1881

Can you add comments to explain how self-recursion is handled here?

mehdi_amini added inline comments.Nov 4 2015, 12:08 PM
lib/Transforms/IPO/FunctionAttrs.cpp
1867

It can call a recursive function, as long at it is not involved in the recursion.

i.e. if:
a() calls b()
b() calls c()
c() calls b()

a is not recursive, but b() and c() are.

The issue with external function is that you don't know if they can call back into a() or one of its callers.
Or did I misunderstand what you meant?

1876

A comment might be useful?

manmanren added inline comments.Nov 4 2015, 1:30 PM
lib/Transforms/IPO/FunctionAttrs.cpp
1867

I was talking about what is actually implemented. But it is not the necessary condition.

// For a function not to recurse, it must be in its own SCC and must not

// call itself or any function that may be recursive.

is incorrect.

But this makes sense:
We mark a function as norecurse if it is in its own SCC and it does not call itself
or any function that may be recursive.

jmolloy updated this revision to Diff 39515.Nov 6 2015, 5:37 AM
jmolloy edited edge metadata.

Hi,

Thanks all for the feedback. This new version is rebased to HEAD on top of Chandler's changes, and improves the way the algorithm works.

I've added an extra inference rule - if a function F has only one callsite and that callsite is itself in a norecurse function, then F cannot recurse. Obviously this lends itself to top-down propagation rather than the bottom-up propagation we get with SCC passes.

Now, we do the same bottom-up approach but if we see a function that is not obviously recursive but can't be proven non-recursive we save it and after the SCC is over we go and revisit it, top-down. This allows more functions to be marked norecurse.

I think I've acted on all the review comments so far, and I've added more comments into the code to make it clearer what's going on.

Cheers,

James

mehdi_amini added inline comments.Nov 6 2015, 7:47 AM
lib/Transforms/IPO/FunctionAttrs.cpp
71

This is sad: it will run at the end of the SCC PassManager, it means you need a barrier after this pass in the pipeline before running another pass. (I don't have a better solution to suggest though).

1778

Why one use? If F has multiple callers but they are all "norecurse", shouldn't it be enough?

manmanren added inline comments.Nov 6 2015, 9:39 AM
lib/Transforms/IPO/FunctionAttrs.cpp
1778

During bottom-up traversal, have the uses of F been visited and possibly marked as norecurse?

jmolloy updated this revision to Diff 39680.Nov 9 2015, 4:47 AM

Hi Mehdi,

I agree with you, having thought about it. I've updated the patch to be less strict.

Cheers,

James

jmolloy updated this revision to Diff 39793.Nov 10 2015, 1:19 AM

Hi Mehdi,

Sorry, I created the last diff from an old branch. This now contains the correct code.

Cheers,

James

manmanren added inline comments.Nov 10 2015, 9:15 AM
lib/Transforms/IPO/FunctionAttrs.cpp
1778

There may be an ordering problem on setting NoRecurse. Have users of F been traversed? A testing case that exercises this would be nice.

1811

A testing case that covers this would be nice. I didn't really look through your testing cases, so you may already have one there :]

jmolloy marked 2 inline comments as done.Nov 10 2015, 9:44 AM

Hi Manman,

I think these should already be tested by the tests @called_by_norecurse and @called_by_norecurse_indirectly?

Cheers,

James

Hi Manman,

Chatting with Mehdi on IRC, I elaborated a bit on what that first check is doing; it's just an optimization. It's the same check as in the top-down-only function, it just runs a bit earlier so has the potential to catch more cases, earlier, opening up more opportunities for the other bottom-up checks.

Mehdi rightly stated that it only affects functions that were marked norecurse before FunctionAttrs ran. He also mentioned that the checks should be factored together, which I agree with (changing the first check in bottom-up to just call addNoRecurseAttrsTopDownOnly().

James

jmolloy updated this revision to Diff 39932.Nov 11 2015, 8:24 AM

Hi Mehdi,

I've removed the offending bottom-up check as we discussed yesterday.

Cheers,

James

mehdi_amini accepted this revision.Nov 11 2015, 1:21 PM
mehdi_amini edited edge metadata.
This revision is now accepted and ready to land.Nov 11 2015, 1:21 PM