Hi Ludovic, Thanks for looking into this! I think I understand the problem now.
l...@gnu.org (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