Changes between Version 16 and Version 17 of Guidelines/WarningsGuidelines


Ignore:
Timestamp:
Jan 20, 2011, 12:49:09 AM (12 years ago)
Author:
phorgan1
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Guidelines/WarningsGuidelines

    v16 v17  
    359359'''-pedantic'''
    360360
    361     This instructs GCC to consider the release version of C or C++ and to first issue all warnings required by that version of the standard, and second warn about the use of gcc extenstions to the language.  This would, for instance warn about the use of ''#ident'' since it is a gcc extension and is not in any of the standards.  Using -pedantic makes it easier to write portable code, or at least to know when it is not.  Best used with the ''-std=xxxxx'' argument to the compiler, so that you know what version of the standard you're being compared to.  Currently if ''-std=xxxxx'' is not specified, it's as if you'd written ''-std=gnu++98''.  Much of boost uses facilities from more recent versions of the standard, so it might make more sense to explicitly specify ''-std=c++0x'' or ''-std=gnu++0x''.  ''-std=c++0x'' is most portable.  See the page [http://gcc.gnu.org/onlinedocs/gcc/Standards.html] for more information.
     361    This instructs GCC to consider the release version of C or C++ and to first issue all warnings required by that version of the standard, and second warn about the use of gcc extensions to the language.  This would, for instance warn about the use of ''#ident'' since it is a gcc extension and is not in any of the standards.  Using -pedantic makes it easier to write portable code, or at least to know when it is not.  Best used with the ''-std=xxxxx'' argument to the compiler, so that you know what version of the standard you're being compared to.  Currently if ''-std=xxxxx'' is not specified, it's as if you'd written ''-std=gnu++98''.  Much of boost uses facilities from more recent versions of the standard, so it might make more sense to explicitly specify ''-std=c++0x'' or ''-std=gnu++0x''.  ''-std=c++0x'' is most portable.  See the page [http://gcc.gnu.org/onlinedocs/gcc/Standards.html] for more information.
    362362
    363363
     
    564564
    565565THAT's what the warning is trying to tell you, that the optimizer is going to do things that you don't like.  In this case seeing that acopy is set to a and never touched again, strict aliasing lets it optimize by just returning the original value of a at the end.
     566==== Broken version ====
    566567{{{
    567568uint32_t
     
    586587}
    587588}}}
     589So what goes wrong? Since a uint16_t can't alias a uint32_t, under the rules, it's ignored in
     590considering what to do with acopy. Since it sees that nothing is done with acopy inside the
     591swaphalves function, it just returns the original value of a.
     592
    588593Here's the (annotated) x86 assembler generated by gcc 4.4.1 for swaphalves, let's see what went wrong:
    589594{{{
     
    612617    ret
    613618}}}
    614 Scary, isn't it?  Of course you could use -fno-strict-aliasing to get the right output, but the generated code won't be as good.  A better way to accomplish the same thing without the warning's or the incorrect output is to define swaphalves like this:
     619Scary, isn't it? Of course, if you are using gcc, you could use -fno-strict-aliasing to get the right output,
     620but the generated code won't be as good. A better way to accomplish the same thing without the
     621warnings or the incorrect output is to define swaphalves like this. N.B. this is supported in C99 and
     622later C specs, as noted in this footnote to 6.5.2.3 Structure and union members:[[BR]]
     623{{{
     62485)
     625If the member used to access the contents of a union object is not the
     626same as the member last used to store a value in the object, the appropriate
     627part of the object representation of the value is reinterpreted as an object
     628representation in the new type as described in 6.2.6 (a process sometimes
     629called ‘‘type punning’’). This might be a trap representation.
     630}}}
     631but your mileage may vary in C++, almost all compilers support it, but the spec doesn't allow it. Right
     632after this discussion I'll have another solution with memcpy that may be slightly less efficient, (but
     633probably not), and is supported by both C and C++):
     634
     635==== Union version. Fixed for C but not guaranteed portable to C++ ====
    615636
    616637{{{
     
    618639swaphalves(uint32_t a)
    619640{
    620     union swapem{
    621         uint32_t a;
    622         uint16_t b[2];
    623     };
     641    typedef union {
     642        uint32_t as32bit;
     643        uint16_t as16bit[2];
     644    } swapem;
     645       
    624646    swapem s={a};
    625647    uint16_t tmp;
    626     tmp=s.b[0];
    627     s.b[0]=s.b[1];
    628     s.b[1]=tmp;
    629     return s.a;
     648    tmp=s.as16bit[0];
     649    s.as16bit[0]=s.as16bit[1];
     650    s.as16bit[1]=tmp;
     651    return s.as32bit;
    630652}
    631653}}}
     
    633655{{{
    634656_Z10swaphalvesj:
    635     pushl   %ebp                        # save the original value of ebp
     657    pushl   %ebp                # save the original value of ebp
    636658    movl    %esp, %ebp          # point ebp at the stack frame
    637659    movl    8(%ebp), %eax       # get a in eax
    638     popl    %ebp                        # get the original ebp value back
     660    popl    %ebp                # get the original ebp value back
    639661    roll    $16, %eax           # swap the two halves of a and return it
    640662    ret
    641663}}}
    642 
     664        So do it wrong, via strange casts and get incorrect code, or by turning off strict-aliasing get inefficient code, or do it right and get efficient code.
     665
     666        You can also accomplish the same thing by using memcpy with char* to move the data around for the swap, and it will probably be as efficient.  Wait, you ask me, how can that be?  The will be at least to calls to memcpy added to the mix!  Well gcc and other modern compilers have smart optimizers and will, in many cases, (including this one), elide the calls to memcpy.  That makes it the most portable, and as efficient as any other method.  Here's how it would look:
     667
     668==== memcpy version, compliant to C and C++ specs and efficient ====
     669
     670{{{
     671uint32_t
     672swaphalves(uint32_t a)
     673{
     674    uint16_t as16bit[2],tmp;
     675
     676    memcpy(as16bit, &a, sizeof(a));
     677    tmp = as16bit[0];
     678    as16bit[0] = as16bit[1];
     679    as16bit[1] = tmp;
     680    memcpy(&a, as16bit, sizeof(a));
     681    return a;
     682}
     683}}}
     684        For the above code, a C compiler will generate code similar to the previous solution, but with the addition of two calls to memcpy (possibly optimized out).  gcc generates code identical to the previous solution.  You can imagine other variants that substitute reading and writing through a char pointer locally for the calls to memcpy.
    643685
    644686=== Suppressing Warnings in GCC ===