Subject: Re: bug#13342: [PARTIALLY SOLVED] bug#13342: Errors trying to build Guile 2.0.7

From: Mark H Weaver <[email-masked]>
Date: Sun, 27 Jan 2013 20:46:22 -0500

Hi Ludovic,

Thanks for looking into this!  I think I understand the problem now.

[email-masked] (Ludovic Courtès) writes:
> Consider this example:
>
> #include <stdint.h>
>
> int64_t
> test_sum (int8_t a, int64_t b)
> {
>   return a + b;
> }
>
> When compiled with GCC 4.6, the assembly is:
>
> test_sum:
> .LFB0:
> 	.cfi_startproc
> 	movsbq	%dil, %rdi
> 	leaq	(%rdi,%rsi), %rax
> 	ret
> 	.cfi_endproc
>
> With Clang 3.1, it is:
>
> test_sum:                               # @test_sum
> 	.cfi_startproc
> # BB#0:
> 	movslq	%edi, %rax
> 	addq	%rsi, %rax
> 	ret
>
> The ‘movsbq’ emitted by GCC arranges to keep only the 8 LSBs.  Clang
> does no such thing, thus keeping all the bits of the first operand in
> the addition.

This is the key revelation, although I've reached a different conclusion
about where the bug is.

> I looked at Section 3.2.3 (“Parameter Passing”) of the SysV ABI x86_64
> PS but couldn’t find any evidence as to what the correct behavior is.

I read the same section, and although it is not as clear as I'd prefer,
my interpretation is that the caller is responsible for sign-extending
signed chars to ints.  This is also consistent with something I vaguely
remember reading in K&R long ago, namely that 'char' and 'short'
arguments are coerced to 'int' before making a function call.

Clang strictly requires callers to sign-extend, whereas GCC is tolerant
of callers who fail to do so.  IMO, both behaviors are permitted by the
ABI.

The problem is that libffi does *not* sign-extend arguments passed in
registers when making calls, which is IMO a bug that has gone (mostly)
unnoticed because of the tolerance and ubiquity of GCC.

> However, on the caller side, both compilers emit the same code.  This
> program:
>
> #include <stdint.h>
>
> extern int64_t test_sum (int8_t a, int64_t b);
>
> int64_t
> foo (void)
> {
>   return test_sum (-1, 123132);
> }
>
> leads to the following assembly with both compilers:
>
> foo:                                    # @foo
> 	.cfi_startproc
> 	movl	$-1, %edi
> 	movl	$123132, %esi           # imm = 0x1E0FC
> 	jmp	test_sum                # TAILCALL
>
> (And as we’ve seen, libffi does the same.)

No, libffi does *not* do the same.  Take a look at the relevant code:

  https://github.com/atgreen/libffi/blob/master/src/x86/ffi64.c#L488

As you can see in lines 487 and 488, arguments passed in registers are
never sign-extended, but rather zero-extended.  The register values are
then copied whole in the darwin-specific assembly stub:

  https://github.com/atgreen/libffi/blob/master/src/x86/darwin64.S#L61

Interestingly, arguments passed on the stack *are* sign-extended:

  https://github.com/atgreen/libffi/blob/master/src/x86/darwin64.S#L120

* * * * *

In summary, I think this is a bug in libffi.

Note that it has already been reported that the libffi testsuite shows
many failures on OS X Lion, and the failures appear to be related to
this precise issue:

  http://sourceware.org/ml/libffi-discuss/2012/msg00162.html

The libffi maintainer wrote "I'm going to chalk this up to compiler
bugs", based on his observation that the tests worked properly when
compiled with -O0.  I think it's time to raise this issue again on the
libffi-discuss mailing list.

In any case, it's certainly not a bug in Guile.  The bug is either in
LLVM/Clang or libffi, depending on how one chooses to interpret the
x86-64 API.

    Regards,
      Mark