[cpp-threads] Sequence points and function calls (see N1944)

Nick Maclaren nmm1 at cus.cam.ac.uk
Sat Mar 4 10:48:09 GMT 2006


This problem was raised a long time back on the SC22WG14 reflector, and
arose as the result of an argument between a programmer and an
implementor.  There was, of course, no consensus on what the C standard
requires, though there was a consensus on its intent.  Yes, it is a real
problem, but I haven't investigated current compilers to find what they
do.  Note that it is independent of the C99 inline function fiasco, and
applies to all of C89, C99 and C++.  Consider:

    #include <stdio.h>
    static volatile int a = 1, b = 1;
    int fred (int z) { int x = a, y = b; a = x+y+z; return a;}
    int joe (int z) { int x = a, y = b; b = x+y+z; return b;}
    int main (void) {
        int c = fred(1+1)+joe(1+1);
        printf("%d %d %d\n",a,b,c);
        return 0;
    }

When the programmer switches on inlining, this might be converted to:

    #include <stdio.h>
    static volatile int a = 1, b = 1;
    int main (void) {
        int c, p, q, x1, y1, z1, x2, y2, z2;
        z1 = 1+1;
        x1 = a;
        y1 = b;
        p = a = x1+y1+z1;
        z2 = 1+1;
        x2 = a;
        y2 = b;
        q = b = x2+y2+z2;
        c = p+q;
        printf("%d %d %d\n",a,b,c);
        return 0;
    }

But it might also be converted to:

    #include <stdio.h>
    static volatile int a = 1, b = 1;
    int main (void) {
        int c, p, q, x1, y1, z1, x2, y2, z2;
        z1 = 1+1;
        z2 = 1+1;
        x1 = a;
        x2 = a;
        y1 = b;
        y2 = b;
        p = a = x1+y1+z1;
        q = b = x2+y2+z2;
        c = p+q;
        printf("%d %d %d\n",a,b,c);
        return 0;
    }

The original prints either "4 7 11" or "7 4 11", the second prints "4 7
11" but the third prints "4 4 8".  Now, we need to see why the third
form is forbidden, if indeed it is.  Let's use N1944, but the problem is
there with the current wording, too, and in C89 and C99.  So let's
check:

     The least requirements on a conforming implementation are:

        * Accesses to volatile objects occur strictly according to the
          rules of the abstract machine.

Check.  This example isn't about advanced optimisation, but is about
what the abstract machine actually requires.

All actions within the body of fred are executed in exactly the order
that they are written, so we need not look at that aspect further.  The
same applies to joe.

     All evaluations and side effects associated with the postfix
     expression designating the function to call, and with any argument
     expressions, are sequenced before the execution of the called
     function (whether or not the function is inline).

Check.  The arguments to fred are executed before any part of the body
of fred, and similarly for joe.

     [Note: Side
     effects and evaluations from different argument expressions are
     not sequenced relative to one another. -end note]

Check.  But this is merely informative.

     The execution of
     the called function is sequenced before any further operation of
     the calling function.

Check.  Once the first action associated with the body of either fred
or joe has started, no action associated with main is started until
all actions associated with both fred and joe have finished.

     [Note: In other words, function executions
     do not "interleave" with each other. -end note] ...

FAIL!  But this is merely informative.

     The sequencing
     constraints on the execution of the function are features of the
     function calls as evaluated, regardless of the syntax of the
     expression that calls the function.

Check.  Irrelevant to this.


The ONLY thing that blocks the third form is an erroneous note - it is
erroneous because it states that the conclusion follows from the
previous normative statement, where it does not.

Almost everyone agrees that the intent is to forbid the third form,
but I think that the above makes it clear that the current and proposed
wording does not do so.  It should.

[ As an aside, I believe that any decently parallel language should
permit the third form, safely, but I also believe that you can't get
there starting from C.  So let's not waste time on trying to turn
C++ into Haskell. ]


Regards,
Nick Maclaren,
University of Cambridge Computing Service,
New Museums Site, Pembroke Street, Cambridge CB2 3QH, England.
Email:  nmm1 at cam.ac.uk
Tel.:  +44 1223 334761    Fax:  +44 1223 334679



More information about the cpp-threads mailing list