Sequence point compiler error

Status
Not open for further replies.

Projectitis

Well-known member
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);
 
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
 
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!
 
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).
 
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.
 
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:
Status
Not open for further replies.
Back
Top