* GCCJIT and proper tail calls @ 2016-01-01 0:00 Marc Nieper-Wißkirchen 2016-01-01 0:00 ` David Malcolm 0 siblings, 1 reply; 6+ messages in thread From: Marc Nieper-Wißkirchen @ 2016-01-01 0:00 UTC (permalink / raw) To: jit Last year there was a discussion on the gccjit mailing list whether it is possible to guarantee the elimination of proper tail calls. Compiling Scheme code with gccjit was given as an example because the language demands proper tail call elimination (for example, loops are usually implemented by recursive function calls). The point I would like to make that proper tail call elimination on gccjit's side remains crucial even if Scheme didn't ask for proper tail call elimination: Scheme allows programs to get hold of the current continuation of the program. One way to implement this is to globally rewrite the program into continuation-passing style. In other words, every call (outside of calling library functions) would become a proper tail call. If gccjit chose not to eliminate the calls in favour of jumps, the stack usage of the transformed program would be proportional to its running time. LLVM has the `musttail' marker http://llvm.org/docs/LangRef.html#call-instruction <http://llvm.org/docs/LangRef.html#call-instruction> to guarantee proper tail call elimination independent of any optimization pass (or to signal an error in case the requirements for the elimination are not met). Such a thing should be possible for gccjit as well (so that the aforementioned program in continuation-passing style would still work even without an optimization pass eliminating non-marked proper tail calls). -- Marc ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: GCCJIT and proper tail calls 2016-01-01 0:00 GCCJIT and proper tail calls Marc Nieper-Wißkirchen @ 2016-01-01 0:00 ` David Malcolm 2016-01-01 0:00 ` Basile Starynkevitch 0 siblings, 1 reply; 6+ messages in thread From: David Malcolm @ 2016-01-01 0:00 UTC (permalink / raw) To: Marc Nieper-Wißkirchen, jit On Wed, 2016-05-11 at 09:38 +0200, Marc Nieper-WiÃkirchen wrote: > Last year there was a discussion on the gccjit mailing list whether > it > is possible to guarantee the elimination of proper tail calls. For reference, here's the archive for that discussion: https://gcc.gnu.org/ml/jit/2015-q3/msg00065.html I didn't understand the significance at the time; sorry. > Compiling Scheme code with gccjit was given as an example because the > language demands proper tail call elimination (for example, loops are > usually implemented by recursive function calls). > > The point I would like to make that proper tail call elimination on > gccjit's side remains crucial even if Scheme didn't ask for proper > tail > call elimination: Scheme allows programs to get hold of the current > continuation of the program. One way to implement this is to globally > rewrite the program into continuation-passing style. In other words, > every call (outside of calling library functions) would become a > proper > tail call. If gccjit chose not to eliminate the calls in favour of > jumps, the stack usage of the transformed program would be > proportional > to its running time. > > LLVM has the `musttail' marker > http://llvm.org/docs/LangRef.html#call-instruction > <http://llvm.org/docs/LangRef.html#call-instruction> to guarantee > proper > tail call elimination independent of any optimization pass (or to > signal > an error in case the requirements for the elimination are not met). > > Such a thing should be possible for gccjit as well (so that the > aforementioned program in continuation-passing style would still work > even without an optimization pass eliminating non-marked proper tail > calls). Thanks for the explanation. I think I understand now. I did some investigation; some notes: gcc can do tail-call optimization both at the gimple level, and at the RTL level. For the gimple level an example is here: https://gcc.gnu.org/onlinedocs/gcc-6.1.0/jit/intro/tutorial04.html#el imination-of-tail-recursion It's in "tailr", a gimple pass; it optimizes tail-recursion into iteration (gcc/tree-tailall.c). This pass also sets up some flags for sibling-call optimization at the RTL level. Relevant gimple flags: enum gf_mask There's a GF_CALL_TAILCALL and there's room for more GF_CALL_ flags GF_CALL_TAILCALL is: * set/cleared by gimple_call_set_tail * queried by gimple_call_tail_p cfgexpand.c has: CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt); which then gets used in calls.c:expand_call for turning a CALL_EXPR rtx_insn into lower-level insns. However there are various ways in which gcc can decide not to do TCO, e.g. these comment from calls.c: /* Tail calls can make things harder to debug, and we've traditionally pushed these optimizations into -O2. Don't try if we're already expanding a call, as that means we're an argument. Don't try if there's cleanups, as we know there's code to follow the call. */ and the code below here in calls.c. /* Rest of purposes for tail call optimizations to fail. */ So it sounds like implementing this would require the following: (a) internally we'd need another flag e.g.: GF_CALL_MUSTTAILCALL or somesuch, and presumably a CALL_EXPR_MUSTTAILCALL (b) exposing some way of setting the flag from the libgccjit.h API. Currently to construct a tail call in libgccjit you need to do something like: gcc_jit_rvalue *return_val = gcc_jit_context_new_call (/* args */); gcc_jit_block_end_with_return (block, loc, return_val); There are two ways to construct a call: gcc_jit_context_new_call gcc_jit_context_new_call_through_ptr and two ways to build a return: gcc_jit_block_end_with_return gcc_jit_block_end_with_void_return Perhaps the API entrypoint would be to mark the call rvalue, with something like: extern void gcc_jit_rvalue_set_tail_call (gcc_jit_rvalue *rvalue, int is_tail_call); to avoid having to add another flag to the new_call API entrypoints. (c) to extend gcc/calls.c to support this flag. This could well be non -trivial - the function in question (expand_call) is over 1000 lines long, and there's various platform specific logic in the relevant code. Would it be acceptable to issue an error if one of the issues arises? This function turns calls into lower-level RTL instructions; it first attempts to generate the insns for a sibling call, but if that fails it falls back to making the insns for a "normal" call. Presumably we want to make sure that flagged calls use the sibling-call form of the RTL. (d) test cases, docs, etc. I'm a bit nervous about having a jit-specific backend feature; I wonder if this is something we could expose in the C/C++ frontends somehow (attributes, pragmas, a command-line flag, etc?). Do any Scheme implementations auto-generate C? Dave ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: GCCJIT and proper tail calls 2016-01-01 0:00 ` David Malcolm @ 2016-01-01 0:00 ` Basile Starynkevitch 2016-01-01 0:00 ` Marc Nieper-Wißkirchen 0 siblings, 1 reply; 6+ messages in thread From: Basile Starynkevitch @ 2016-01-01 0:00 UTC (permalink / raw) To: David Malcolm, Marc Nieper-Wißkirchen, jit On 05/12/2016 09:59 PM, David Malcolm wrote: > Do any Scheme implementations auto-generate C? Several of them do. Chicken Scheme https://www.call-cc.org/ and Bigloo https://www-sop.inria.fr/indes/fp/Bigloo/ notably. Also, Christian Queinnec wrote an excellent book: Lisp In Small Pieces https://pages.lip6.fr/Christian.Queinnec/WWW/LiSP.html which explain in great details several (interpreted & compiled) implementations of Lisp, including a Scheme translator to C. The common point is that none the Scheme to C compilers I know are expecting or building upon the ability of the C compiler to emit proper tail-recursive calls. So GCCJIT really needs support for that (and IMHO, GCC itself should have a pragma to check that and warn if a given call cannot be compiled as a tail-calll). Regards. -- Basile STARYNKEVITCH http://starynkevitch.net/Basile/ email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359 8, rue de la Faiencerie, 92340 Bourg La Reine, France *** opinions {are only mine, sont seulement les miennes} *** ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: GCCJIT and proper tail calls 2016-01-01 0:00 ` Basile Starynkevitch @ 2016-01-01 0:00 ` Marc Nieper-Wißkirchen 2016-01-01 0:00 ` David Malcolm 0 siblings, 1 reply; 6+ messages in thread From: Marc Nieper-Wißkirchen @ 2016-01-01 0:00 UTC (permalink / raw) To: Basile Starynkevitch, David Malcolm, jit If GCC (and possibly a further ISO C dialect) included a way to express that a certain call should be compiled as a proper tail call (that is, as a jump), that would be awesome. There is a proposal for the Rust language which uses the keyword `become' instead of `return' to express an explicit tail call. If the C front-end of GCC has a way to express explicit tail calls (and thus the middle- and back-ends include support for it), say `return __musttail__ f(x)', the contract could be that (a) the call is in fact in tail position, (b) the tail-called function has the same (or an equivalent) prototype as the caller, and (c) no automatic variables of the caller are accessed anymore after the tail-call happened. The point of (c) is that it solves the problem discussed here: http://www.drdobbs.com/tackling-c-tail-calls/184401756. The example code is: int* global; bar () { ... *global = 42; } foo () { ... global = &local; ... bar (); } (a) could be verified statically, (b) could be either verified statically or (when violated) lead to undefined behaviour, and (c) would lead to undefined behaviour if the contract was broken. In case the current backends of GCC are not able to do proper tail calls even in case the restrictions (a) - (c) are fulfilled, one may have to add another part (d) to the contract to have reliable proper tail calls in a well-defined subject. Tail recursion (a function tail-calls itself), while often being cited when trying to explain the benefits of proper tail call elimination, is less of a problem for language front-ends, because these kind of tail calls can be statically detected and rewritten by the front-end. The true power of proper tail call elimination comes from eliminating sibling tail calls because this is in some sense a global program transformation. As a side note: The other JIT compiler of the GNU project, GNU lightning, does support proper tail call elimination (again in some restricted, but reliable form). They use the word trampoline for it: https://www.gnu.org/software/lightning/manual/lightning.html. While the use cases of GNU lightning are somewhat different from those of gccjit, it would be nice if the two JIT implementation would be on par in that regard. -- Marc Am 13.05.2016 um 06:19 schrieb Basile Starynkevitch: > On 05/12/2016 09:59 PM, David Malcolm wrote: > > > Do any Scheme implementations auto-generate C? > > Several of them do. Chicken Scheme https://www.call-cc.org/ and Bigloo > https://www-sop.inria.fr/indes/fp/Bigloo/ notably. > > Also, Christian Queinnec wrote an excellent book: Lisp In Small Pieces > https://pages.lip6.fr/Christian.Queinnec/WWW/LiSP.html which explain in > great details several (interpreted & compiled) implementations of Lisp, > including a Scheme translator to C. > > The common point is that none the Scheme to C compilers I know are > expecting or building upon the ability of the C compiler to emit proper > tail-recursive calls. So GCCJIT really needs support for that (and IMHO, > GCC itself should have a pragma to check that and warn if a given call > cannot be compiled as a tail-calll). > > Regards. > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: GCCJIT and proper tail calls 2016-01-01 0:00 ` Marc Nieper-Wißkirchen @ 2016-01-01 0:00 ` David Malcolm 2016-01-01 0:00 ` Basile Starynkevitch 0 siblings, 1 reply; 6+ messages in thread From: David Malcolm @ 2016-01-01 0:00 UTC (permalink / raw) To: Marc Nieper-Wißkirchen, Basile Starynkevitch, jit On Fri, 2016-05-13 at 08:23 +0200, Marc Nieper-WiÃkirchen wrote: > If GCC (and possibly a further ISO C dialect) included a way to > express > that a certain call should be compiled as a proper tail call (that > is, > as a jump), that would be awesome. > There is a proposal for the Rust language which uses the keyword > `become' instead of `return' to express an explicit tail call. If the > C > front-end of GCC has a way to express explicit tail calls (and thus > the > middle- and back-ends include support for it), say `return > __musttail__ > f(x)', the contract could be that > > (a) the call is in fact in tail position, > (b) the tail-called function has the same (or an equivalent) > prototype > as the caller, and > (c) no automatic variables of the caller are accessed anymore after > the > tail-call happened. > > The point of (c) is that it solves the problem discussed here: > http://www.drdobbs.com/tackling-c-tail-calls/184401756. The example > code is: > > int* global; > bar () > { > ... > *global = 42; > } > foo () > { > ... > global = &local; > ... > bar (); > } > > (a) could be verified statically, > (b) could be either verified statically or (when violated) lead to > undefined behaviour, and > (c) would lead to undefined behaviour if the contract was broken. > > In case the current backends of GCC are not able to do proper tail > calls > even in case the restrictions (a) - (c) are fulfilled, one may have > to > add another part (d) to the contract to have reliable proper tail > calls > in a well-defined subject. > > Tail recursion (a function tail-calls itself), while often being > cited > when trying to explain the benefits of proper tail call elimination, > is > less of a problem for language front-ends, because these kind of tail > calls can be statically detected and rewritten by the front-end. The > true power of proper tail call elimination comes from eliminating > sibling tail calls because this is in some sense a global program > transformation. > > As a side note: The other JIT compiler of the GNU project, GNU > lightning, does support proper tail call elimination (again in some > restricted, but reliable form). They use the word trampoline for it: > https://www.gnu.org/software/lightning/manual/lightning.html. While > the > use cases of GNU lightning are somewhat different from those of > gccjit, > it would be nice if the two JIT implementation would be on par in > that > regard. Thanks Marc and Basile. I've been experimenting with this; I have a patch which adds a new libgccjit entrypoint: extern void gcc_jit_rvalue_set_bool_require_tail_call (gcc_jit_rvalue *call, int require_tail_call); and this successfully sets a new flag on the CALL_EXPR (in gcc's "tree" representation), but the flag doesn't yet do anything in the RTL expansion phase. My planned next step is to do it for some C code to easily exercise the code generator. I'm thinking of using a compiler plugin to manually set the flag on the calls (to avoid needing any C syntax for this). > -- > > Marc > > Am 13.05.2016 um 06:19 schrieb Basile Starynkevitch: > > On 05/12/2016 09:59 PM, David Malcolm wrote: > > > > > Do any Scheme implementations auto-generate C? > > > > Several of them do. Chicken Scheme https://www.call-cc.org/ and > > Bigloo > > https://www-sop.inria.fr/indes/fp/Bigloo/ notably. > > > > Also, Christian Queinnec wrote an excellent book: Lisp In Small > > Pieces > > https://pages.lip6.fr/Christian.Queinnec/WWW/LiSP.html which > > explain in > > great details several (interpreted & compiled) implementations of > > Lisp, > > including a Scheme translator to C. > > > > The common point is that none the Scheme to C compilers I know are > > expecting or building upon the ability of the C compiler to emit > > proper > > tail-recursive calls. So GCCJIT really needs support for that (and > > IMHO, > > GCC itself should have a pragma to check that and warn if a given > > call > > cannot be compiled as a tail-calll). > > > > Regards. > > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: GCCJIT and proper tail calls 2016-01-01 0:00 ` David Malcolm @ 2016-01-01 0:00 ` Basile Starynkevitch 0 siblings, 0 replies; 6+ messages in thread From: Basile Starynkevitch @ 2016-01-01 0:00 UTC (permalink / raw) To: David Malcolm, Marc Nieper-Wißkirchen, jit On 05/13/2016 03:29 PM, David Malcolm wrote: > On Fri, 2016-05-13 at 08:23 +0200, Marc Nieper-WiÃkirchen wrote: >> If GCC (and possibly a further ISO C dialect) included a way to >> express >> that a certain call should be compiled as a proper tail call (that >> is, >> as a jump), that would be awesome. I believe that would be a really good idea. >> >> As a side note: The other JIT compiler of the GNU project, GNU >> lightning, does support proper tail call elimination (again in some >> restricted, but reliable form). They use the word trampoline for it: >> https://www.gnu.org/software/lightning/manual/lightning.html. While >> the >> use cases of GNU lightning are somewhat different from those of >> gccjit, >> it would be nice if the two JIT implementation would be on par in >> that >> regard. > > Thanks Marc and Basile. I've been experimenting with this; I have a > patch which adds a new libgccjit entrypoint: > > extern void > gcc_jit_rvalue_set_bool_require_tail_call (gcc_jit_rvalue *call, > int require_tail_call); > > and this successfully sets a new flag on the CALL_EXPR (in gcc's "tree" > representation), but the flag doesn't yet do anything in the RTL > expansion phase. > > My planned next step is to do it for some C code to easily exercise the > code generator. I'm thinking of using a compiler plugin to manually > set the flag on the calls (to avoid needing any C syntax for this). If pushing into GCC trunk some pragma (for C only, not for C++), perhaps #pragma GCC expect_tail_call /* warn if the following call to foobar is not a tail-recursive call */ foobar(x,y) is not too hard, I believe it is the way to go. Because such a pragma would be extremely useful to the many compiler writers which are emitting C code. Of course the syntax of such pragma could be improved (and maybe discussed with Clang/LLVM folks, to agree on a common syntax). But my belief is that many compilers which are emitting C code would be very happy with such a pragma (and perhaps even some low-level system or application coders). My point is that tail recursion is really important. A lot of languages (Scheme, Ocaml) are requiring it in the language specification. So implementations won't emit C code and pray that GCC is doing the right thing. They need to be "sure" of that. Of course, if adding such a pragma is too hard, that is a different story. If it is simpler to make it some builtin, that is ok. Or it might be some syntax extension, perhaps goto return foobar(x,y); Cheers. -- Basile STARYNKEVITCH http://starynkevitch.net/Basile/ email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359 8, rue de la Faiencerie, 92340 Bourg La Reine, France *** opinions {are only mine, sont seulement les miennes} *** ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2016-05-13 13:59 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-01-01 0:00 GCCJIT and proper tail calls Marc Nieper-Wißkirchen 2016-01-01 0:00 ` David Malcolm 2016-01-01 0:00 ` Basile Starynkevitch 2016-01-01 0:00 ` Marc Nieper-Wißkirchen 2016-01-01 0:00 ` David Malcolm 2016-01-01 0:00 ` Basile Starynkevitch
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).