[m-dev.] Mutually recursive tailcalls in hlc.

Paul Bone paul at bone.id.au
Tue Nov 10 13:37:46 AEDT 2015

On Tue, Nov 10, 2015 at 12:13:41PM +1100, Julien Fischer wrote:
> On Tue, 10 Nov 2015, Paul Bone wrote:
> >>I was asking about a conditional case, not the general case. You're
> >>right that I should ask about more than just gcc, though.
> >>
> >>Let me try again with a specific condition. Can the compilers we want
> >>perform tail call elimination if the signatures of the caller and
> >>callee are the same? Your argument transformation satisfies this
> >>condition, and the optimizations you mentioned are also relevant, but
> >>the actual trampoline would be redundant and may impede other C
> >>optimizations (such as inlining).
> >>
> >>Like I said earlier, it's not that I disagree with your proposal. But
> >>if someone asks, I'd like to be able to name a compiler we rely on
> >>that doesn't do tail call elimination under this condition.
> >
> >You're right, it may not be worth my/YesLogic's time to implement the
> >general solution.
> >
> >I'm not sure.  AFAIK the popular C compilers for use with Mercury are GCC,
> >Clang and VC++.
> From time-to-time we have to (and should be able to) support new C
> compilers.  (It wasn't so long since clang was one such new compiler.)
> Furthormore, one of the points of the high-level C backend is that any
> C compiler that handles standard C (and isn't broken in one of many
> stupid ways) ought to be usable with it.

Agreed.  At least this change won't prevent your program from being compiled
if the C compiler's behaviour changes.  However it still depends on the C
compiler's behaviour, if the behaviour changes the optimisation may break
silently, causing a performance regression.

Worse still, if programmers come to depend on this optimisation and it
breaks, their programs are more-likely to segfault because they expected it
to work.  I don't know if anyone has noticed (I haven't) if this happens to
people when they port from low-level C to high-level C.

Depending on this behaviour, with Mark's proposal, also means _not_ applying
the general optimisation, so falling-back to the general optimisation will
not happen automatically.  This does happen automatically when using
inlining to avoid mutually recursive tailcalls.

I feel ambivalent about it.  My main concern is that it depends on the
behaviour of C compilers.

> >We could check Clang, it'll be documented somewhere.
> You'll probably need to dig into the clang / LLVM source code for that,
> the user documentation is singularly useless on the subject.  (Ditto for
> GCC's for that matter.)
> Taking the standard example of mutual recursion:
>     bool is_even(int n)
>     {
>         if (n == 0) {
>            return true;
>         } else {
>            return is_odd(n - 1);
>        }
>     }
>     bool is_odd(int n)
>     {
>         if (n == 0) {
>            return false;
>        } else {
>           return is_even(n - 1);
>        }
>     }
> On my system if inlining is disabled, then GCC (4.3) eliminates the tail
> calls in the above, clang (Apple version 6) doesn't.  (If inlining is
> enabled, then one of the above functions is inlined in the body of the
> other and there are no indirect tail calls.)
> >I'm not sure how we'd find out something like this for VC++.
> I've had a look at the assembly that VC++ (2013) generates for the above
> (with inlining disabled) and the tail calls are eliminated.

Okay, so that's:

    GCC: Yes
    Clang: No, XXX check the source
    VC++: Yes

Given Clang's popularity I would prefer not to rely on this.  However it'd
still be good to support it, as it ought to be faster.

Paul Bone

More information about the developers mailing list