Thread: Problem with compiling code with option -O2

  1. #1
    Registered User
    Join Date
    Jan 2009
    Posts
    7

    Problem with compiling code with option -O2

    Hello, everyone. I have met a problem when I was trying compiling the next code.

    Code:
    #include <stdio.h>
    
    typedef union {
        short s[2];
        int i[1];
    }foo;
    
    int main()
    {
    
        foo f;
        short *a = f.s;
        int   cnt = 0x22222222;;
    
        f.s[0]=0x1111;
        f.s[1]=0x1111;
    
        printf("%x %x\n", a[0], a[1]);
        printf("%x %x\n", f.s[0], f.s[1]);
        printf("\n");
    
        *f.i = cnt;
    
        printf("%x %x\n", a[0], a[1]);
        printf("%x %x\n", f.s[0], f.s[1]);
        printf("\n");
    
    
        printf("%x %x\n", a[0], a[1]);
        printf("%x %x\n", f.s[0], f.s[1]);
        printf("\n");
    
        return 0;
    }
    The compiler is gcc 3.2.3. When I used options -O. I got the result which I expected:
    1111 1111
    1111 1111

    2222 2222
    2222 2222

    2222 2222
    2222 2222

    But when I used options -O2, I got the next result:
    1111 1111
    1111 1111

    1111 1111
    2222 2222

    2222 2222
    2222 2222

    Could you explain where is the problem in the code?
    Thanks for your help.

  2. #2
    Beginner leiming's Avatar
    Join Date
    Jan 2008
    Location
    Fujian, China
    Posts
    25
    1111 1111
    1111 1111

    2222 1111
    2222 2222

    2222 2222
    2222 2222

    Gcc 3.4.5 if -O2 is turned on

  3. #3
    Beginner leiming's Avatar
    Join Date
    Jan 2008
    Location
    Fujian, China
    Posts
    25
    I have come up with an idea,
    is it because that a is only a pointer to 2 bytes so the compiler doesn't think it has effect beyond the 2 bytes to assign a value to pointer a

  4. #4
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Looks like the compiler deduces that "*f.i = cnt;" and "a[0], a[1]" cannot access the same memory location, so it does puts a[0] and a[1] on the stack (in order to pass it to printf) prior or at the same time as modifying "*f.i". This is technically allowed, because accessing the member of a union as a short after assigning to the int version to is undefined behavior.

    I surprised the compiler makes such a determination, however.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  5. #5
    Registered User
    Join Date
    Apr 2008
    Posts
    396
    compiling with -fno-strict-aliasing -O2 produces the correct result (or so it seems).

    The wrong part is to declare i as a pointer (or an array, nevermind), because the compiler assumes it is not an alias to s, replacing int i[1]; by int i; in the union, and *f.i=0x2..2 by f.i=0x2..2 produces the correct result with -O2 and no additional option (because the compiler knows s and i are related).
    Last edited by root4; 01-07-2009 at 02:32 AM.

  6. #6
    Registered User
    Join Date
    Jan 2009
    Posts
    7
    Thank you for your comment.
    Actually, this sample is modified from the next code:
    Code:
    #include <stdio.h>
    
    int main()
    {
    
      volatile short a[2];
      int   cnt = 0x22222222;
    
          a[0]=0x1111;
          a[1]=0x1111;
    
          printf("%x %x\n", a[0], a[1]);
    
          *(int *)a = (int)cnt; /* violation of aliasing rules */
    
          printf("%x %x\n", a[0], a[1]);
    
          printf("%x %x\n", a[0], a[1]);
    
      return 0;
    }
    According the information from google, I have known the line *(int *)a = (int)cnt; violate the aliasing rules. So I tried to fix this using union. But it seems not work. I want to figure out where the problem is and how to avoid it.
    Some information from google may be helpful.
    https://www.securecoding.cert.org/co...ompatible+type

    Any helpful comment is welcome.

  7. #7
    Registered User
    Join Date
    Apr 2008
    Posts
    396
    The page you linked provide a solution (the one you tried in your first post, except member i is not an array) and it works. Your last problem occurs when casting a pointer to a (non-compatible) type (an array of 2 shorts in not necessarily equivalent to an int, it depends on the implementation type size [the standard only says an int and a short have to be at least 16 bits long]). Plus in your case, indianness may also be an issue.

  8. #8
    Registered User
    Join Date
    Jan 2009
    Posts
    7
    Thank you for your comment.
    But I want to ask some further question.

    Quote Originally Posted by root4 View Post
    The wrong part is to declare i as a pointer (or an array, nevermind), because the compiler assumes it is not an alias to s, replacing int i[1]; by int i; in the union, and *f.i=0x2..2 by f.i=0x2..2 produces the correct result with -O2 and no additional option (because the compiler knows s and i are related).
    In an union, how compiler determines one member is an alias to others or not? Is there any rule?

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by koushikyou View Post
    Thank you for your comment.
    But I want to ask some further question.



    In an union, how compiler determines one member is an alias to others or not? Is there any rule?
    I expect it ALWAYS assumes members are aliased - since storing different forms of data aliased is the exact purpose of unions.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    Registered User
    Join Date
    Jan 2009
    Posts
    7
    Quote Originally Posted by root4 View Post

    The wrong part is to declare i as a pointer (or an array, nevermind), because the compiler assumes it is not an alias to s, replacing int i[1]; by int i; in the union, and *f.i=0x2..2 by f.i=0x2..2 produces the correct result with -O2 and no additional option (because the compiler knows s and i are related).
    If there is something bad in the union, how to explain the next code.
    Code:
    #include <stdio.h>
    
    typedef union {
        short s[2];
        int i;
    }foo;
    
    int main()
    {
    
        foo f;
        short *a = f.s;
        int * b = &f.i;
        int   cnt = 0x22222222;;
    
        f.s[0]=0x1111;
        f.s[1]=0x1111;
    
        printf("%x %x\n", a[0], a[1]);
        printf("%x %x\n", f.s[0], f.s[1]);
        printf("\n");
    
       // f.i = cnt;
        *b = cnt;
    
        printf("%x %x\n", a[0], a[1]);
        printf("%x %x\n", f.s[0], f.s[1]);
        printf("\n");
    
    
        printf("%x %x\n", a[0], a[1]);
        printf("%x %x\n", f.s[0], f.s[1]);
        printf("\n");
    
        return 0;
    }
    The code results:
    1111 1111
    1111 1111

    1111 1111
    2222 2222

    2222 2222
    2222 2222

  11. #11
    Registered User
    Join Date
    Apr 2008
    Posts
    396
    After a quick test, simply replacing "*f.i=..." by "f.i[0]=..." seems enough to tell the optimizer 'i' is an alias for 's'. Using 'i' as a pointer, knowing it is of a different type of 's' and using strict aliasing makes the optimizer thinks they are unrelated objects. So in fact, you don't even have to modify your union, ignore my comment about that.

  12. #12
    Registered User
    Join Date
    Jan 2009
    Posts
    7
    Quote Originally Posted by matsp View Post
    I expect it ALWAYS assumes members are aliased - since storing different forms of data aliased is the exact purpose of unions.

    --
    Mats
    I agree with you.

  13. #13
    Registered User
    Join Date
    Apr 2008
    Posts
    396
    Yes, replace all occurences of 's' in what I told, by 'a'...

    To sum up (how I see things, but I'm not expert on the topic):
    's' and 'i' are aliases by default, 'a' is an alias of 's' when initialized, then 'i' is modified, it is accessed as a pointer and its type is _different_ of 'a', so the optimizer (strict aliasing) assumes those objects are no longer related, and applies its optimization (as details by King Mir probably). Same thing with the last example 'b'.
    Last edited by root4; 01-07-2009 at 03:40 AM.

  14. #14
    Registered User
    Join Date
    Jan 2009
    Posts
    7
    Is it a bad coding style or bug of gcc?

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by koushikyou View Post
    Is it a bad coding style or bug of gcc?
    Depends on how you look at it - you are aliasing pointers, and gcc -O2 assumes "pointers do not alias" - if you want to avoid that, you should turn of that particular optimization step for the relevant code with -fno-strict-aliasing

    But it is at the very least bad coding style to do this - even if it's technically not in itself incorrect and the C standard does certainly not forbid pointer aliases.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Problem Compiling
    By Flakster in forum C++ Programming
    Replies: 4
    Last Post: 06-13-2006, 01:09 AM
  2. Help on compiling code
    By mattyp1977 in forum C++ Programming
    Replies: 4
    Last Post: 03-26-2006, 05:07 PM
  3. Problem : Threads WILL NOT DIE!!
    By hanhao in forum C++ Programming
    Replies: 2
    Last Post: 04-16-2004, 01:37 PM
  4. Request for comments
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 01-02-2004, 10:33 AM
  5. Big Code, Little Problem
    By CodeMonkey in forum Windows Programming
    Replies: 4
    Last Post: 10-03-2001, 05:14 PM

Tags for this Thread