Bug 69221 - gcc on m68k miscompiles function type casts
Summary: gcc on m68k miscompiles function type casts
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 5.3.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2016-01-10 21:32 UTC by Sergei Trofimovich
Modified: 2016-01-10 23:49 UTC (History)
2 users (show)

See Also:
Host: x86_64-pc-linux-gnu
Target: m68k-unknown-linux-gnu
Known to work:
Known to fail:
Last reconfirmed:

sample with broken ABI (373 bytes, text/x-csrc)
2016-01-10 21:32 UTC, Sergei Trofimovich

Note You need to log in before you can comment on or make changes to this bug.
Description Sergei Trofimovich 2016-01-10 21:32:29 UTC
Created attachment 37298 [details]
sample with broken ABI

This came up as a miscompilation of GHC:

Consider the following small snippet:

    /* needed to introduce a symbol */
    extern void* f(); /* real prototype is 'int f(void);' */

    int g(void)
        /* cast a symbol to known function type, returns 'int' */
        return 1 + ((int (*)(void))f)();

    /* somewhere in other translation unit */
    int __f (void)
        return 42;

    /* just for comparison */
    void * __vf (void)
        return (void*)42;

$ m68k-unknown-linux-gnu-gcc -O2 -S demo1.c -fomit-frame-pointer
[ Also issues a warning:
  warning: function called through a non-compatible type
     return 1 + ((int (*)(void))f)();

generates the following assembly:

        jsr f
        move.l %a0,%d0 ; 'g' kills register 'd0', resould of called would-be 'f'
        addq.l #1,%d0

        moveq #42,%d0

        move.w #42,%a0
        moveq #42,%d0

The idea here is 
    extern void* f();
line brings a .text symbol of yet unknown propotype.

Later *at .c file generation time) we get information about
symbol 'f' and cast it to known type:
    ((int (*)(void))f)();

But caller does not expect result to present in 'd0' data register, expects 'a0' address register.

[a note: GHC used to introduce unknown symbols in scope as
    extern void* f(void);
but that broke on PPC64 ELFv2 and we changed to less precise
    extern void* f();

Looks like GCC bug to ignore actual 'int (*)(void)' ABI of a symbol at call site.
Comment 1 Andreas Schwab 2016-01-10 22:06:57 UTC
The declaration of f isn't compatible with its defintion.  A cast doesn't change that fact.
Comment 2 Sergei Trofimovich 2016-01-10 22:27:46 UTC
(In reply to Andreas Schwab from comment #1)
> The declaration of f isn't compatible with its defintion.  A cast doesn't
> change that fact.

Correct. But is it relevant here? Cast is the only real thing gcc sees.
'void * f();' is an incomplete declaration.

Is there a way to introduce a symbol without any declaration so cast would be only source of ABI information?

If I change 'int' to 'float' in original sample it will be compiled correctly.
    // was return 1 + ((int (*)(void))f)();
    return 1 + (int)((float (*)(void))f)();
GCC won't try to cast from %a0 but will pick %fp0:

        jsr f
        fintrz.x %fp0,%fp0
        fmove.l %fp0,%d0
        addq.l #1,%d0

I suspect gcc actually knows it can use 'int' / 'void*' interchangeaby. But on m68k it's not true.