This is an archive of the discontinued LLVM Phabricator instance.

Add an option to disable tail-call optimization for escaping blocks
ClosedPublic

Authored by ahatanak on Feb 27 2018, 4:23 PM.

Details

Summary

This patch adds a command line option (-fdisable-tail-calls-esca
ping-blocks) that annotates escaping block invoke functions with attribute "disable-tail-calls". This is an option that helps users in debugging their code who spend a lot of time trying to figure out where a block came from.

The user who is asking for this command line option does not want to disable tail-call optimization for non-escaping blocks. For example, in the following code, we should not disable tail-call optimization for the block that is directly passed to function "noescapefunc":

void foo3(void);

void foo1() {
  noescapefunc(^{ foo3(); }); // do not disable tail-call.
  BlockTy b = ^{ foo3(); }; // disable tail-call.
  noescapefunc(b);
}

Ideally, I think we want to avoid disabling tail-call optimization for block "b" too, as it doesn't escape. However, this patch doesn't do anything to avoid disabling tail-call optimization for the block, since that would require a more complex analysis.

rdar://problem/35758207

Diff Detail

Repository
rL LLVM

Event Timeline

ahatanak created this revision.Feb 27 2018, 4:23 PM

Can you explain the rationale for limiting this to escaping blocks in more depth? That seems like an extremely orthogonal limitation; the user might be thinking about a very specific block and not really considering this in general.

This is limited to escaping blocks because disabling tail-call optimizations for all blocks might impact performance. The user is claiming that non-escaping blocks are often used in areas that are performance-sensitive (for example, dispatch_sync() and -[NSArray enumerateObjectsUsingBlock:] in a tight loop), so disabling tail-call optimization indiscriminately can cause performance degradation (and clients might decide not to use the command line option because of that).

TCO is a pretty neglible optimization; its primary advantage is slightly better locality for stack memory.

I guess the more compelling argument is that non-escaping blocks can by definition only be executed with the original block-creating code still active, so someone debugging a crash downstack of a TCO'ed block will still have a pretty strong piece of context to start from. An escaping block, meanwhile, could be executed by anything, so TCO'ing it might leave the stack trace with absolutely no hint about what's happened.

Alright, I can accept that.

include/clang/Driver/Options.td
1419 ↗(On Diff #136179)

These are pretty unidiomatic option names. I would suggest one of these:

  • [fixed]-fescaping-block-tail-calls[/fixed] (the default) and [fixed]-fno-escaping-block-tail-calls[/fixed]
  • [fixed]-enable-escaping-block-tail-calls[/fixed] (the default) and [fixed]-disable-escaping-block-tail-calls[/fixed]
include/clang/Frontend/CodeGenOptions.def
66 ↗(On Diff #136179)

"from" instead of "for" would be clearer, I think.

lib/CodeGen/CodeGenModule.h
469 ↗(On Diff #136179)

This seems like something that Sema should store on the BlockDecl.

rjmccall added inline comments.Feb 28 2018, 10:15 PM
include/clang/Driver/Options.td
1419 ↗(On Diff #136179)

Wow, this is not even close to Phabricator markup, I don't know what I was thinking.

ahatanak updated this revision to Diff 136580.Mar 1 2018, 12:01 PM
ahatanak marked 3 inline comments as done.

Address review comments.

include/clang/Driver/Options.td
1419 ↗(On Diff #136179)

I chose the first option "-fno-escaping-block-tail-calls/-fescaping-block-tail-calls".

rjmccall added inline comments.Mar 1 2018, 12:06 PM
lib/Sema/SemaExpr.cpp
4846 ↗(On Diff #136580)

Can this be based on the noescape parameter bit on the function type?

ahatanak marked an inline comment as done.Mar 1 2018, 12:27 PM
ahatanak added inline comments.
lib/Sema/SemaExpr.cpp
4846 ↗(On Diff #136580)

Oh that's right. It should be able to set the DoesNotEscape bit of a block when it's passed to an indirect function call. I added an IRGen test case that checks tail-call is enabled in such cases.

ahatanak updated this revision to Diff 136584.Mar 1 2018, 12:29 PM
ahatanak marked an inline comment as done.

Check the function prototype's noescape bit.

rjmccall accepted this revision.Mar 1 2018, 12:36 PM

Alright, this looks good to me.

lib/Sema/SemaExpr.cpp
4846 ↗(On Diff #136580)

Thanks!

This revision is now accepted and ready to land.Mar 1 2018, 12:36 PM
This revision was automatically updated to reflect the committed changes.