[llvm-commits] [PATCH] Remove tail marker when changing an argument to an alloca.

Duncan Sands baldrick at free.fr
Thu Jan 3 21:14:45 PST 2013

Hi Rafael,

On 03/01/13 20:21, Rafael EspĂ­ndola wrote:
>> on the other hand, the optimizers will turn recursive tail calls into a
>> loop.  I didn't check whether this optimization tests for byval, but it
>> would be a wrong to generate a loop when there are byval parameters in
>> general.
>> There may be other cases like this.  I think it would be wise to audit the
>> code base to see what deductions transforms make based on the tailcall flag
>> being set.  That should give a better idea of whether it is better to simply
>> disallow byval or not (or allow some kind of compromise).  Whatever the
>> conclusion is, it would be good to document it explicitly.
> That is not illegal in general, is it? The simple function from the
> example above:
> %X = type { i32 }
> define void @g(%X* byval %a) {
> entry:
>    tail call void @f(%X* %a)
>    ret void
> }
> declare void @f(%X* byval)
> Can be turned into an infinite loop by an IL pass (but no pass in
> -std-compile-opts does it right now).

well, it's not going to be turned into an infinite loop because it's not
recursive!  However consider something like:

%X = type { i32 }
define void @g(%X* byval %a, i32 %level) {
   %as_int = bitcast %X* %a to i32*
   store i32 %level, i32* %as_int
   %new_level = add i32 %level, 1
   tail call void @g(%X* byval %a, i32 %new_level)
   ret void

Each store is to a new piece of memory, however if you turn this into a loop
then each store is to the same piece of memory (unless you introduce an alloca
representing the byval memory inside the loop).  That said, it's not clear to
me that that's actually a problem.  The reason is: in order for this to matter,
someone needs to read from the memory after the call returns, and notice that
the callee changed it (due to the 'byval' callers aren't supposed to see writes
to the memory that the callee made).  However our tail recursion optimizer
currently won't rewrite into a loop if there is anything non-trivial after the
tail call.  In particular there can't be a load or anything similar that could
notice that the %a memory was changed by the call.

Maybe this is a general argument: if there is a tail call to a function with
a byval parameter, and the call is in tail position (i.e. at the end with
nothing substantial after it) then you don't need to bother making a copy of
the byval argument.

(I'm a bit worried about examples where the tail callee returns the argument
somehow, enabling somewhere else to peek inside it and observe the lack of a
byval copy, but I didn't come up with an example yet in which the peeking
didn't involve undefined behaviour).

OK, I think I've convinced myself that allowing tail call on something with
a byval argument can be useful, and that the tail recursion optimization
will work correctly with it.

Ciao, Duncan.

  The real constraint is that the
> pointer value can be different in the caller and callee, but
> optimizations have to handle that even if the call is not tail.
>> Ciao, Duncan.
> Cheers,
> Rafael

More information about the llvm-commits mailing list