Re: endianness and failure with inline

Discussion in 'C Programming' started by Seebs, Oct 14, 2010.

  1. Seebs

    Seebs Guest

    On 2010-10-14, doriangray <> wrote:
    > 1) Why the gcc compiler fails to compile this code?


    I dunno.

    > $ gcc -std=c99 file.c
    > /tmp/ccKmnUPF.o: In function `main':
    > file.c:(.text+0x27): undefined reference to `isLE99'
    > collect2: ld returned 1 exit status


    This seems to be related to the std=c99 flag.

    > 2) Are these portable ways to establish whether a 32bit architecture
    > is Little Endian or not?


    No. There really aren't portable ways, honestly -- the question isn't
    even entirely well-defined.

    I have very, very, rarely seen any reason to, though -- I simply
    haven't seen code that legitimately needs to know.

    > inline int isLE99(void) {


    Hmm. One thing is, there's at least some reserved symbols starting
    with "is*" for function names -- for future expansion of ctype.h. However,
    that doesn't seem to be it. If I build this program as-is with
    gcc in std=c99 mode, it fails with a similar message. Changing
    "inline" to "static inline" fixes it, though. So does changing it to
    "extern inline". This could be a bug.

    I don't really understand why you declare the "C89" and "C99" versions;
    it seems very unlikely that they'll actually behave differently on
    all that many targets, and the differences don't have much to do with
    C99. Note that, for the level of precision you seem to be going for,
    you'll do just as well to set an unsigned long to 0x12, then verify that
    the first byte of it appears to be 0x12 -- the rest is all irrelevant.

    I have no idea why gcc is having issues with the inline specifier.
    I also don't have any idea why you put it there to begin with.

    Ohhh! I think I may have found something suspicious. It seems that
    inlining doesn't work without some kind of -O. Thus:

    cc --std=c99 -o foo foo.c

    fails, but:

    cc -O --std=c99 -o foo foo.c

    It's quite possible that this is a bug, or possibly a documented
    limitation.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 14, 2010
    #1
    1. Advertising

  2. doriangray wrote:
    > Seebs wrote:
    >> On 2010-10-14, doriangray<> wrote:
    >>> 1) Why the gcc compiler fails to compile this code?

    >>
    >> I dunno.
    >>
    >>> $ gcc -std=c99 file.c
    >>> /tmp/ccKmnUPF.o: In function `main':
    >>> file.c:(.text+0x27): undefined reference to `isLE99'
    >>> collect2: ld returned 1 exit status


    isLE99() is declared `inline` with neither `static` nor `extern`, which
    means the definition of that function is an inline definition -- which
    does not provide an external definition for that function. As
    optimizations are not enabled, the call to isLE99() is not actually
    inlined, and thus an unresolved reference to isLE99() remains.

    [snip]
    > That's really strange,
    > maybe I am wrong but I remember of earlier gcc versions that didn't
    > complain with the specifier "inline" alone.


    Earlier versions of GCC had the meaning of `inline` and `extern inline`
    swapped with respect to C99. It was fixed in 4.3, AFAIK.

    > Ironically, it compiles fine with just "gcc file.c", due to a compiler
    > extension. Nothing on the gcc documentation makes me think that such a
    > failure is expected.


    N1256 section 6.7.4 paragraph 6 does ;-)

    See also the GCC option -fgnu89-inline.
    --
    Marcin Grzegorczyk
    Marcin Grzegorczyk, Oct 14, 2010
    #2
    1. Advertising

  3. Seebs

    Seebs Guest

    On 2010-10-14, Marcin Grzegorczyk <> wrote:
    >> Ironically, it compiles fine with just "gcc file.c", due to a compiler
    >> extension. Nothing on the gcc documentation makes me think that such a
    >> failure is expected.


    > N1256 section 6.7.4 paragraph 6 does ;-)


    I'm looking at that in the final standard, and I see nothing to justify
    this failure. So far as I can tell, the "inline" qualifier does not authorize
    a compiler to fail to translate the program. The call to isLE99() doesn't
    *need* an external definition, any more than a call to something declared
    "static" would.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 14, 2010
    #3
  4. Seebs wrote:
    > On 2010-10-14, Marcin Grzegorczyk <> wrote:
    >>> Ironically, it compiles fine with just "gcc file.c", due to a compiler
    >>> extension. Nothing on the gcc documentation makes me think that such a
    >>> failure is expected.

    >>
    >> N1256 section 6.7.4 paragraph 6 does ;-)

    >
    > I'm looking at that in the final standard, and I see nothing to justify
    > this failure. So far as I can tell, the "inline" qualifier does not authorize
    > a compiler to fail to translate the program. The call to isLE99() doesn't
    > *need* an external definition, any more than a call to something declared
    > "static" would.


    The last sentence of that paragraph says

    # It is unspecified whether a call to the
    # function uses the inline definition or the external definition.

    So the compiler is free to ignore the inline definition altogether, and
    there is no external definition of isLE99() but it is referenced.
    --
    Marcin Grzegorczyk
    Marcin Grzegorczyk, Oct 14, 2010
    #4
  5. Seebs

    Seebs Guest

    On 2010-10-14, Marcin Grzegorczyk <> wrote:
    > So the compiler is free to ignore the inline definition altogether, and
    > there is no external definition of isLE99() but it is referenced.


    Ahh! I see. That was what I'd missed -- you're required to provide an
    external definition of any symbol you call, even if you *also* provide an
    inline one. Since the inline one doesn't provide an external definition,
    the program's behavior is not defined, but no diagnostic is required because
    it's Semantics, not Constraints. (The last bit was helpfully pointed out to
    me by one of the nice folks at Code Sourcery.)

    So in short, the program is actually incorrect, but it happens that gcc will,
    in many modes, choose a way of translating it which happens to work.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 14, 2010
    #5
  6. Seebs

    Ian Collins Guest

    On 10/15/10 12:02 PM, Seebs wrote:
    > On 2010-10-14, Ian Collins<> wrote:
    >> "An inline definition does not provide an external definition for the
    >> function, and does not forbid an external definition in another
    >> translation unit. An inline definition provides an alternative to an
    >> external definition, which a translator may use to implement any call to
    >> the function in the same translation unit. It is unspecified whether a
    >> call to the function uses the inline definition or the external definition."

    >
    >> Note "An inline definition provides an alternative to an external
    >> definition".

    >
    >> So the behaviour is only unspecified if both an external *and* inline
    >> definition are present.

    >
    > My theory is that it's always unspecified whether you use the inline one
    > or the external one. And if there isn't an external one, then the behavior
    > is undefined.


    It's only undefined if there are both!

    Otherwise behaviour of the majority of source files with inline
    functions is undefined. 6.7.4/6 is specifically discussing functions
    with external linkage.

    Having both inline and external version of the same function is probably
    rather unusual (I've never seen it).

    --
    Ian Collins
    Ian Collins, Oct 15, 2010
    #6
  7. Ian Collins wrote:
    > On 10/15/10 12:02 PM, Seebs wrote:
    >> On 2010-10-14, Ian Collins<> wrote:
    >>> "An inline definition does not provide an external definition for the
    >>> function, and does not forbid an external definition in another
    >>> translation unit. An inline definition provides an alternative to an
    >>> external definition, which a translator may use to implement any call to
    >>> the function in the same translation unit. It is unspecified whether a
    >>> call to the function uses the inline definition or the external
    >>> definition."
    >>>
    >>> Note "An inline definition provides an alternative to an external
    >>> definition".
    >>>
    >>> So the behaviour is only unspecified if both an external *and* inline
    >>> definition are present.

    >>
    >> My theory is that it's always unspecified whether you use the inline one
    >> or the external one. And if there isn't an external one, then the
    >> behavior
    >> is undefined.

    >
    > It's only undefined if there are both!


    No; if there are both it's merely unspecified which one is used. OTOH,
    if the external definition is missing, the behaviour is undefined,
    because the function has external linkage (6.9p5).

    >[...]
    > Having both inline and external version of the same function is probably
    > rather unusual (I've never seen it).


    They're usually "the same" definition, but one translation unit makes
    that definition external (by declaring it `extern`) while in all the
    other translation units it's an inline definition. See the example
    under 6.7.4; ordinarily, the definitions of the inline functions would
    be placed in a separate file, which would be then included as needed.
    --
    Marcin Grzegorczyk
    Marcin Grzegorczyk, Oct 15, 2010
    #7
  8. Seebs

    Ian Collins Guest

    On 10/15/10 12:33 PM, Marcin Grzegorczyk wrote:
    > Ian Collins wrote:
    >> On 10/15/10 12:02 PM, Seebs wrote:
    >>> On 2010-10-14, Ian Collins<> wrote:
    >>>> "An inline definition does not provide an external definition for the
    >>>> function, and does not forbid an external definition in another
    >>>> translation unit. An inline definition provides an alternative to an
    >>>> external definition, which a translator may use to implement any
    >>>> call to
    >>>> the function in the same translation unit. It is unspecified whether a
    >>>> call to the function uses the inline definition or the external
    >>>> definition."
    >>>>
    >>>> Note "An inline definition provides an alternative to an external
    >>>> definition".
    >>>>
    >>>> So the behaviour is only unspecified if both an external *and* inline
    >>>> definition are present.
    >>>
    >>> My theory is that it's always unspecified whether you use the inline one
    >>> or the external one. And if there isn't an external one, then the
    >>> behavior
    >>> is undefined.

    >>
    >> It's only undefined if there are both!

    >
    > No; if there are both it's merely unspecified which one is used. OTOH,
    > if the external definition is missing, the behaviour is undefined,
    > because the function has external linkage (6.9p5).


    True, but only if the function has been declared with external linkage.
    If the only declaration is the inline definition, all is sweet. This
    was the case in the OP's example.

    >> [...]
    >> Having both inline and external version of the same function is probably
    >> rather unusual (I've never seen it).

    >
    > They're usually "the same" definition, but one translation unit makes
    > that definition external (by declaring it `extern`) while in all the
    > other translation units it's an inline definition. See the example under
    > 6.7.4; ordinarily, the definitions of the inline functions would be
    > placed in a separate file, which would be then included as needed.


    Which is still unusual.

    --
    Ian Collins
    Ian Collins, Oct 15, 2010
    #8
  9. Seebs

    Seebs Guest

    On 2010-10-14, Ian Collins <> wrote:
    > It's only undefined if there are both!


    If there are both, it's *unspecified* -- but it's not undefined. You get
    one or the other.

    But the statement that it's unspecified which you get isn't actually qualified
    by an "if there are both" -- it's just a plain statement that it's
    unspecified.

    Now, think about the question: How would you know whether an external one
    existed? You're translating foo.c. You don't know whether bar.c has an
    external function or not. You can't make your decision contingent on that
    knowledge. You can just decide whether to generate a call to the external
    version or to inline the inline version. Either is permitted.

    > Otherwise behaviour of the majority of source files with inline
    > functions is undefined. 6.7.4/6 is specifically discussing functions
    > with external linkage.


    I'm not sure of this.

    In any event, we asked gcc maintainers, and they gave us this explanation,
    so at least some gcc folks think that behavior is correct, and I'm willing to
    take their word for it.

    > Having both inline and external version of the same function is probably
    > rather unusual (I've never seen it).


    The impression I get is that if you declare something "extern inline", you
    by definition get both.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 15, 2010
    #9
  10. Seebs

    Ian Collins Guest

    On 10/15/10 01:36 PM, doriangray wrote:
    > Ian Collins wrote:
    > [...]
    >>> No; if there are both it's merely unspecified which one is used. OTOH,
    >>> if the external definition is missing, the behaviour is undefined,
    >>> because the function has external linkage (6.9p5).

    >>
    >> True, but only if the function has been declared with external linkage.
    >> If the only declaration is the inline definition, all is sweet. This was
    >> the case in the OP's example.

    > [...]
    >
    >
    > Exactly. I thought that "inline" was just a hint to the compiler who
    > is free to ignore hints, as it would with "register" variables.
    > Why should we bother about something that the compiler is free to
    > ignore? And also, isLE99() is only called in the same module where its
    > defined, so I can't still understand what undefined reference means.


    A compiler bug.

    In your example, in terms of linkage "inline" is the same as "static".

    --
    Ian Collins
    Ian Collins, Oct 15, 2010
    #10
  11. Seebs

    Seebs Guest

    On 2010-10-15, doriangray <> wrote:
    > Exactly. I thought that "inline" was just a hint to the compiler who
    > is free to ignore hints, as it would with "register" variables.
    > Why should we bother about something that the compiler is free to
    > ignore? And also, isLE99() is only called in the same module where its
    > defined, so I can't still understand what undefined reference means.


    Okay, here's the thing.

    isLE99() isn't static, so there's not a static definition of it.

    It's declared inline, there's not an external definition of it.

    There's a call to it. That call could EITHER be to the inline version
    defined in this file, or to an external version defined in some other
    file -- we don't know whether or not such an external version exists,
    and indeed, an external one could be created long after this file has
    been compiled! So the compiler has a free choice; it can either generate
    a call to the externally-visible symbol (which could come from this file
    or any other) or inline.

    It chooses not to inline, but to generate a call. However, when we get to
    linking, there's no external definition available, so the link fails with
    an undefined reference.

    We compiled something which was permitted to generate EITHER an inlined
    version of the function OR a call to an external version of the function.

    We compiled an inline version of the function, which could OPTIONALLY be
    inlined, or could be ignored -- but which COULD NOT create an external
    version of the function.

    The compiler does not have the option of creating an external version
    of the function, because it's declared inline without an extern modifier.
    However, it does have the option of translating the call expression into
    a call to the external identifier, on the grounds that you may well have
    provided an external definition in another module.

    Then, when linking comes around, we find that we have a call to a symbol
    that no one ever defined. Whoops.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 15, 2010
    #11
  12. Seebs

    Ian Collins Guest

    On 10/15/10 01:42 PM, Seebs wrote:
    > On 2010-10-15, doriangray<> wrote:
    >> Exactly. I thought that "inline" was just a hint to the compiler who
    >> is free to ignore hints, as it would with "register" variables.
    >> Why should we bother about something that the compiler is free to
    >> ignore? And also, isLE99() is only called in the same module where its
    >> defined, so I can't still understand what undefined reference means.

    >
    > Okay, here's the thing.
    >
    > isLE99() isn't static, so there's not a static definition of it.
    >
    > It's declared inline, there's not an external definition of it.
    >
    > There's a call to it. That call could EITHER be to the inline version
    > defined in this file, or to an external version defined in some other
    > file -- we don't know whether or not such an external version exists,
    > and indeed, an external one could be created long after this file has
    > been compiled! So the compiler has a free choice; it can either generate
    > a call to the externally-visible symbol (which could come from this file
    > or any other) or inline.
    >
    > It chooses not to inline, but to generate a call. However, when we get to
    > linking, there's no external definition available, so the link fails with
    > an undefined reference.
    >
    > We compiled something which was permitted to generate EITHER an inlined
    > version of the function OR a call to an external version of the function.


    I think your are probably correct here. But this is one case where the
    correct behaviour is counter-intuitive.

    Compiling with optimisation on does inline the function.

    gcc 3.x makes the symbol global, Sun c99 make is local, which is what I
    consider the "sensible" thing to do.

    Causing a linkage failure depending on optimisation level isn't very
    user friendly.

    --
    Ian Collins
    Ian Collins, Oct 15, 2010
    #12
  13. Seebs

    Seebs Guest

    On 2010-10-15, Ian Collins <> wrote:
    > I think your are probably correct here. But this is one case where the
    > correct behaviour is counter-intuitive.


    Yes, yes it is! It was sufficiently counterintuitive that I actually reported
    it to the toolchain vendor at $dayjob to see whether they thought it was a
    bug, but their explanation convinced me that, although I may think it's a
    bug, if it is, it's a bug in the standard.

    And thinking about it more, I don't think it's a bug in the standard; it's
    just a limitation that comes about as a side-effect of the rule allowing
    vendors to pick which of two versions of a function to use.

    > Causing a linkage failure depending on optimisation level isn't very
    > user friendly.


    I'd agree. The problem is, I'm not sure I see a way to improve it without
    breaking something.

    The real-world usage case is that you might have a generic version of a
    function which works in all sorts of cases, and then a local inline version
    which can perform better in a specific case, but which you don't want to
    use elsewhere.

    According to several compiler vendors, people ACTUALLY DO THIS. They'll
    do things like having an external rotate() which can rotate arbitrary
    objects, and a local inline rotate() which works exceptionally fast, but
    only on circles. And the thing is, it's very important for the performance
    freaks to *permit* that. But it's not very helpful to vendors to *require*
    that it work as-expected, because that implies making inlining work, and
    you are NEVER required to actually successfully inline. So if your
    compiler doesn't actually HAVE inlining as a possibility, it *MUST* generate
    calls to the external symbol -- and because we just explicitly allowed
    the inline function to have the same name as the external symbol, you can't
    generate an external-linkage version of the function that the call can
    get linked against.

    So this really does seem to be a necessary side-effect, but I'm not
    comfortable with using such a weak, mamby-pamby, word as "counterintuitive"
    to describe it. The directly-downwind faster than the wind wind-powered
    vehicle is "counterintuitive"*. This is much, much, more confusing.

    -s
    [*] As in, most engineers will immediately tell you that it's obviously
    impossible, and some will continue to insist this even after seeing it done,
    though the majority seem to get it after a while. Although it's not
    C-related, it is an awesome example of how cool physics gets around the
    edges. See also fasterthanthewind.org.
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 15, 2010
    #13
  14. Seebs

    Seebs Guest

    On 2010-10-15, doriangray <> wrote:
    > Thanks now it sounds much clearer, even if I'd prefer the compiler to
    > treat it as static if it chooses not to make it inline.
    > Would it sound more reasonable? Anyway, Thank you all.


    That makes some sense to me, yes. I think there is probably a sound
    reason for which it wasn't mandated, but it would certainly have surprised
    me less.

    Thanks a ton for the fascinating example/test case. Excellent question.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 15, 2010
    #14
  15. Seebs

    Ian Collins Guest

    On 10/15/10 04:01 PM, Seebs wrote:
    > On 2010-10-15, Ian Collins<> wrote:
    >> I think your are probably correct here. But this is one case where the
    >> correct behaviour is counter-intuitive.

    >
    > Yes, yes it is! It was sufficiently counterintuitive that I actually reported
    > it to the toolchain vendor at $dayjob to see whether they thought it was a
    > bug, but their explanation convinced me that, although I may think it's a
    > bug, if it is, it's a bug in the standard.
    >
    > And thinking about it more, I don't think it's a bug in the standard; it's
    > just a limitation that comes about as a side-effect of the rule allowing
    > vendors to pick which of two versions of a function to use.
    >
    >> Causing a linkage failure depending on optimisation level isn't very
    >> user friendly.

    >
    > I'd agree. The problem is, I'm not sure I see a way to improve it without
    > breaking something.


    It was an interesting discussion, just when I thought I knew all the
    intricacies of inline, I found I didn't. I was assuming C and C++
    followed the same rules (as some compilers appear to), but this is
    another of those grey areas where the languages subtly differ.

    > The real-world usage case is that you might have a generic version of a
    > function which works in all sorts of cases, and then a local inline version
    > which can perform better in a specific case, but which you don't want to
    > use elsewhere.


    <snip>

    The C++ standard wording (7.1.2/4) forbids this, but provides extra
    constraints and clarification:

    "An inline function shall be defined in every translation unit in which
    it is used and shall have exactly the same definition in every case"

    Regarding linkage, it goes on to say:

    "If a function with external linkage is declared inline in one
    translation unit, it shall be declared inline in all translation units
    in which it appears; no diagnostic is required. An inline function with
    external linkage shall have the same address in all translation units."

    and adds this which I don't think is mentioned in the C standard:

    "A static local variable in an extern inline function always refers to
    the same object. A string literal in an extern inline function is the
    same object in different translation units."

    > So this really does seem to be a necessary side-effect, but I'm not
    > comfortable with using such a weak, mamby-pamby, word as "counterintuitive"
    > to describe it.


    A can't think of a better one I'd use in polite company!

    > The directly-downwind faster than the wind wind-powered
    > vehicle is "counterintuitive"*. This is much, much, more confusing.


    Ah, you've been watching multi-hull yacht racing!

    --
    Ian Collins
    Ian Collins, Oct 15, 2010
    #15
  16. Seebs

    Seebs Guest

    On 2010-10-15, Ian Collins <> wrote:
    > On 10/15/10 04:01 PM, Seebs wrote:
    >> The directly-downwind faster than the wind wind-powered
    >> vehicle is "counterintuitive"*. This is much, much, more confusing.


    > Ah, you've been watching multi-hull yacht racing!


    Actually, no, it was a land-based cart. The official world record holder
    in the newly-minted "directly down wind faster than the wind" category
    for the land sail people. It did ~28mph in a 10mph wind, directly downwind,
    powered only by the wind. Very impressive bit of work.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
    Seebs, Oct 15, 2010
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. pramod
    Replies:
    22
    Views:
    1,827
    Lew Pitcher
    Jan 6, 2004
  2. kelvSYC

    Endianness and streams

    kelvSYC, Jun 5, 2005, in forum: C++
    Replies:
    8
    Views:
    447
    Pete Becker
    Jun 6, 2005
  3. pramod

    endianness and sscanf/sprintf

    pramod, Dec 31, 2003, in forum: C Programming
    Replies:
    22
    Views:
    775
    Lew Pitcher
    Jan 6, 2004
  4. Case

    memcpy() and endianness

    Case, May 10, 2004, in forum: C Programming
    Replies:
    26
    Views:
    1,774
    Dan Pop
    May 12, 2004
  5. gamehack

    Bit shifts and endianness

    gamehack, Jan 5, 2006, in forum: C Programming
    Replies:
    72
    Views:
    6,851
    Dave Thompson
    Jan 11, 2006
Loading...

Share This Page