[m-dev.] Mutually recursive tailcalls in hlc.
Julien Fischer
jfischer at opturion.com
Tue Nov 10 14:23:20 AEDT 2015
Hi Paul,
On Tue, 10 Nov 2015, Paul Bone wrote:
>> 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.
It depends what you are trying to achieve. If you want to make
guarantees about about tailcalls being eliminated in a specified class
of mutually recursive predicates then I think you will have to handle it
in the Mercury compiler. Leaving it up to the C compiler is at the very
least sensitive to which optimization flags the C compiler is invoked
with and the Mercury implementation itself often varies flags those in
order to workaround bugs. Furthermore, users have the option of setting
C compiler optimization flags hemselves (and indeed, may have to because
of the contents of foreign_proc pragmas.)
Incidentally, the trampolining technique ought to also work (with
appropriate modifications) with both the C# and Java backends.
>>> 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
It would be worth trying with a newer version of clang. The version on
my machine appears to be a little old. (3.5 as opposed to the latest
which is 3.8.)
I'd like to see what happens with a more realistic case than the above
though (i.e. with C code generated by the Mercury compiler for a non toy
program).
Julien.
More information about the developers
mailing list