GNU bug report logs -
#13342
Clang, the FFI, and 8-bit signed integers
Previous Next
Reported by: Peter Teeson <pteeson <at> me.com>
Date: Wed, 2 Jan 2013 23:06:02 UTC
Severity: normal
Tags: notabug
Merged with 13386
Done: ludo <at> gnu.org (Ludovic Courtès)
Bug is archived. No further changes may be made.
Full log
View this message in rfc822 format
Hi Ludovic,
Thanks for looking into this! I think I understand the problem now.
ludo <at> 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
This bug report was last modified 12 years and 110 days ago.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.