Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Sequence point compiler error

  1. #1
    Senior Member Projectitis's Avatar
    Join Date
    Feb 2018
    Location
    New Zealand
    Posts
    141

    Sequence point compiler error

    Hi all,

    I have the a few calculations like the following:
    Code:
    c = (*p++ << 14) | ((*p << 6) & 0b0011100000000000) | ((*p++ << 7) & 0b0000011110000000) | ((*p >> 1) & 0b0000000001111111);
    I get a -Wsequence-point operation error because the compiler cannot guarantee that the operations are done in this exact order above, which affects the result.

    Is there a compiler directive (or something similar) to tell the compiler to follow this exact order, or do I have to break these up and trust the compiler to optimise? e.g.:
    Code:
    c = (*p++ << 14);
    c |= ((*p << 6) & 0b0011100000000000);
    c |= ((*p++ << 7) & 0b0000011110000000);
    c |= ((*p >> 1) & 0b0000000001111111);

  2. #2
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    I had a similar situation with -Waggressive-loop-optimizations

    The corresponding flag to disable these optimizations is -Wno-aggressive-loop-optimizations

    In your case it should be -Wno-sequence-point

  3. #3
    Senior Member Projectitis's Avatar
    Join Date
    Feb 2018
    Location
    New Zealand
    Posts
    141
    Thank you
    Do you know if it is possible to do this at the code level (not at compiler)? It would be great if there was something like:
    Code:
    c = (*p++ << 14) | ((*p << 6) & 0b0011100000000000) | ((*p++ << 7) & 0b0000011110000000) | ((*p >> 1) & 0b0000000001111111) __attribute__ ((do_not_dick_with_this));
    Unfortunately I haven't found anything yet!

  4. #4
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    That's beyond my skills ;-)

    But if you want to disable optimizations for a bloc of code you can use gcc pragmas - You can even put these lines in a function and use an attribute with optimization flags - Have a look here : https://gcc.gnu.org/onlinedocs/gcc-7...ion-Attributes

    I presume that if optimization is fully turned off the compiler will not do anything special with you line of code... But I'm not sure

  5. #5
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    2,956

    Cool

    Quote Originally Posted by Projectitis View Post
    Hi all,

    I have the a few calculations like the following:
    Code:
    c = (*p++ << 14) | ((*p << 6) & 0b0011100000000000) | ((*p++ << 7) & 0b0000011110000000) | ((*p >> 1) & 0b0000000001111111);
    I get a -Wsequence-point operation error because the compiler cannot guarantee that the operations are done in this exact order above, which affects the result.

    Is there a compiler directive (or something similar) to tell the compiler to follow this exact order, or do I have to break these up and trust the compiler to optimise? e.g.:
    Code:
    c = (*p++ << 14);
    c |= ((*p << 6) & 0b0011100000000000);
    c |= ((*p++ << 7) & 0b0000011110000000);
    c |= ((*p >> 1) & 0b0000000001111111);
    From an ISO C (and C++) standards point of view, your single line of code is indeterminate because you have side effects within the statement that can be done in multiple ways. The end of the statement is a sequence point where any visible side effects must be in place. Quoting from page 34 of the final draft of the C11 standard (I'm more familiar with the C standard since I was on the original X3J11 C standards body, but I believe C++ has similar wording since it was created from C):

    EXAMPLE 7 The grouping of an expression does not completely determine its evaluation. In the following fragment

    Code:
    #include <stdio.h>
    int sum;
    char *p;
    /* ... */
    sum = sum * 10 - '0' + (*p++ = getchar());
    the expression statement is grouped as if it were written as

    Code:
    sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));
    but the actual increment of p can occur at any time between the previous sequence point and the next sequence point (the ';'), and the call to getchar can occur at any point prior to the need of its returned value.

    Here are the rules for sequence points (page 503, informative annex C)

    The following are the sequence points described in 5.1.2.3:
    • Between the evaluations of the function designator and actual arguments in a function call and the actual call. (6.5.2.2).
    • Between the evaluations of the first and second operands of the following operators: logical AND && (6.5.13); logical OR || (6.5.14); comma , (6.5.17).
    • Between the evaluations of the first operand of the conditional ? : operator and whichever of the second and third operands is evaluated (6.5.15).
    • The end of a full declarator: declarators (6.7.6);
    • Between the evaluation of a full expression and the next full expression to be evaluated. The following are full expressions: an initializer that is not part of a compound literal (6.7.9); the expression in an expression statement (6.8.3); the controlling expression of a selection statement (if or switch) (6.8.4); the controlling expression of a while or do statement (6.8.5); each of the (optional) expressions of a for statement (6.8.5.3); the (optional) expression in a return statement (6.8.6.4).
    • Immediately before a library function returns (7.1.4).
    • After the actions associated with each formatted input/output function conversion specifier (7.21.6, 7.29.2).
    • Immediately before and immediately after each call to a comparison function, and also between any call to a comparison function and any movement of the objects passed as arguments to that call (7.22.5).

  6. #6
    Remove the pointer increments, and use array addressing instead. In other words,
    Code:
        unsigned short  c =  (p[0] << 14)
                          | ((p[1] << 6) & 14336)
                          | ((p[1] << 7) & 1920)
                          | ((p[2] >> 1) & 127);
        p += 3;
    Assuming you enable optimizations (I always use -O2 or -Os), it should generate at least as good code as the single expression that violates sequence point rules.

    In fact, I can confirm that on arm-gcc 4.9.3, using -O2 or -Os, compiling for Thumb on Cortex-M0 or Cortex-M4, the following two functions generate the exact same machine code:
    Code:
    unsigned int good(const unsigned short *p)
    {
        unsigned short  c =  (p[0] << 14)
                          | ((p[1] << 6) & 14336)
                          | ((p[1] << 7) & 1920)
                          | ((p[2] >> 1) & 127);
        return c;
    }
    
    unsigned int bad(const unsigned short *p)
    {
        unsigned short  c =  (*p++ << 14)
                          | ((*p   << 6) & 0b0011100000000000)
                          | ((*p++ << 7) & 0b0000011110000000)
                          | ((*p   >> 1) & 0b0000000001111111);
        return c;
    }
    In general, if you are unsure, compile the code to assembly using option -S, and compare the number of non-comment lines, to get an idea if there is a big difference in the machine code implementation or not.

    Personally, I strive for robustness first, speed second, so I never even consider code like the "bad" form.

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    429
    For a quick check you can also use the compiler explorer:

    https://godbolt.org/z/gmpL_J

    I like how the compiler implements bad() :-)

  8. #8
    Member
    Join Date
    Mar 2019
    Location
    Bordeaux / France
    Posts
    68
    Whoo !!! This tool is sick !! Thanks for the info !

  9. #9
    Senior Member Projectitis's Avatar
    Join Date
    Feb 2018
    Location
    New Zealand
    Posts
    141
    Wow, thank you everyone!
    Tactif CIE - thanks for the info on pragmas.
    Nominal Animal - once again, right on the mark with the tip on array addressing.
    MichaelMeissner - Thanks for the clarification on sequence points.
    luni - fantastic tool - what a find!

    EDIT: That 'bad' function is a crackup!
    Last edited by Projectitis; 05-06-2019 at 08:27 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •