[comp.sys.sgi] C compiler weirdness?

drb@eecg.toronto.edu (David R. Blythe) (04/08/91)

I recently dug up a copy of the hoc calculator described in kernighan & pike
only to discover that the C compiler does not increment pc until after the
function call returns in the following statment:
                  (*(*pc++))();
whereas the rest of the program is assuming it is done before the function is
actually called.  Is the compiler interpretation supposed to be implementation
dependent?  I would suspect not, but then I can't believe no one else has
run into this before (On the other hand K&P is pre-ANSI).  Sorry, if this has
been hashed out before.  And for those keeping score this is IRIX 3.3.1

	david blythe
	ontario centre for large scale computation
	drb@clsc.utoronto.ca

sweetmr@SCT60A.SUNYCT.EDU (michael sweet) (04/09/91)

> only to discover that the C compiler does not increment pc until after the
> function call returns in the following statement:
>           (*(*pc++))();

The time of increment/decrement IS compiler dependent.  The only constraint
with ++/-- is that '++i' increments 'i' before using that value, and 'i--'
after the valuye is used.  This causes MUCH heartache in the above code and
the following (which I see all too often, and should NEVER be used if you want
to port code to a different machine!)

 while (*s)
  *s = *++s;

(or similar...)  What the person probably wants to do is delete a character
from a string (starting at 's'...).  The idiot assumes that '*s' is evaluated
for the assignment FIRST, and then 's' is incremented.  A nicer way (which
will get optimized away anyway) and MORE READABLE way is:

 while (*s)
  {
   *s = *(s+1);
   s++;
  };


The KISS philosophy is best when coding in C (keeps you from going crazy!)

 -Mike Sweet

------------------------------------------------------------------------------
"The only        TASC                      (315) 724-1100 (voice)
 truth is that   555 French Road           (315) 724-2031 (fax)
 there are no    New Hartford, NY  13413   Internet: sweetmr@sct60a.sunyct.edu
 truths, only beliefs."                    Delphi:   DODGECOLT
------------------------------------------------------------------------------

sysmark@aurora.physics.utoronto.ca (Mark Bartelt) (04/13/91)

David Blythe wrote ...

| I recently dug up a copy of the hoc calculator described in kernighan & pike
| only to discover that the C compiler does not increment pc until after the
| function call returns in the following statment:
|                   (*(*pc++))();
| whereas the rest of the program is assuming it is done before the function is
| actually called.  Is the compiler interpretation supposed to be implementation
| dependent?  I would suspect not, but then I can't believe no one else has

... to which Michael Sweet replied ...

| The time of increment/decrement IS compiler dependent.  The only constraint
| with ++/-- is that '++i' increments 'i' before using that value, and 'i--'
| after the valuye is used.  This causes MUCH heartache in the above code and
| the following (which I see all too often, and should NEVER be used if you want
| to port code to a different machine!)
|
|  while (*s)
|   *s = *++s;

Sorry, but this example is irrelevant to the bug being reported (and it is a
bug, if the behaviour of the SGI compiler is in fact what David reports it to
be; I haven't yet confirmed that it is, though I have no reason to doubt that
it is).

It's certainly true that the *s = *++s stuff is a proper example of bad code,
since C "does not specify the order in which the operands of an operator are
evaluated" [K&R-2, 2.12].

However, this has no bearing on David's question.  The critical issue here
is what happens during expression evaluation.  Since the incrementing of an
operand (after noting its value) is part of the definition of the semantics
of the postfix ++ operator, it seems quite clear that postfix incrementing
has to be treated as an atomic operation.  In other words, the operand gets
incremented after its value is noted, but before anything else happens.  K&R
is admittedly a bit vague about this ("After the value is noted, the operand
is incremented ..." [A7.3.4]), but I think it's universally acknowledged that
"after" means "immediately after".  I'm sure that some high-level guru will
correct me if I'm wrong, but I don't think I am.

As a further confirmation that the delay (until after the return from the
function call) in the postincrement of pc is truly a bug, doesn't the fact
that David's example was taken from Kernighan and Pike, coupled with the
fact that bwk is the 'K' in "K&R", suggest something?

Mark Bartelt                                                    416/978-5619
Canadian Institute for                                 mark@cita.toronto.edu
Theoretical Astrophysics                               mark@cita.utoronto.ca

davea@quasar.wpd.sgi.com (David B.Anderson) (04/13/91)

In article <1991Apr12.175909.5194@helios.physics.utoronto.ca> mark@cita.toronto.edu writes:
>David Blythe wrote ...
>
>| I recently dug up a copy of the hoc calculator described in kernighan & pike
>| only to discover that the C compiler does not increment pc until after the
>| function call returns in the following statment:
>|                   (*(*pc++))();
>| whereas the rest of the program is assuming it is done before the function is
>| actually called.  Is the compiler interpretation supposed to be implementation
>| dependent?  I would suspect not, but then I can't believe no one else has
[ ]
>Sorry, but this example is irrelevant to the bug being reported (and it is a
>bug, if the behaviour of the SGI compiler is in fact what David reports it to

Since Mark Bartelt claims that the compiler is wrong, I'll step up to its
defense (I do not claim that the compiler version used is ANSI C):

ANSI C: 3.3.2.2, page 42. line 20.
``The order of evaluation of the function designator, the arguments,
and subexpressions within the arguments is unspecified, but there is
a sequence point before the actual call.''

The Standard does not say whether the sequence point is before or
after the evaluation of the function designator.

The following is thus _a_ legal sequence:
        evaluate arguments (none in the case above)
        sequence point (arguments evalation complete)
        evaluate function designator
        call function
        increment pc
        statement sequence point

So IMO our code generation is legal in this case.  (case dismissed :-)

Regards,
[ David B. Anderson  Silicon Graphics  (415)335-1548  davea@sgi.com ]
[``What can go wrong?''                           --Calvin to Hobbes]

sysmark@aurora.physics.utoronto.ca (Mark Bartelt) (04/13/91)

In article <97139@sgi.sgi.com> davea@quasar.UUCP (David B.Anderson) writes:

| Since Mark Bartelt claims that the compiler is wrong, I'll step up to its
| defense (I do not claim that the compiler version used is ANSI C):
|
| ANSI C: 3.3.2.2, page 42. line 20.
| ``The order of evaluation of the function designator, the arguments,
| and subexpressions within the arguments is unspecified, but there is
| a sequence point before the actual call.''
|
| The Standard does not say whether the sequence point is before or
| after the evaluation of the function designator.
|
| The following is thus _a_ legal sequence:
|         evaluate arguments (none in the case above)
|         sequence point (arguments evalation complete)
|         evaluate function designator
|         call function
|         increment pc
|         statement sequence point
|
| So IMO our code generation is legal in this case.  (case dismissed :-)

Hmm...  Interesting interpretation.  But ...  I *think* (and though I feel
reasonably proficient in C, I nonetheless lay no claim to being in a class
with, say, Henry Spencer, let alone dmr or bwk) that in this case the

|         evaluate function designator

*includes* the incrementing of pc.  Observe that the sequence

	note the value of the operand
	increment the operand

is, in its entirety, part of the semantics of the postfix ++ operator.
At least, that's *my* interpretation of what the standard says.  Maybe
we should cross-post this to comp.lang.c (which would generate all sorts
of comments, most of them bogus), or forward the discussion to dmr and
bwk, and see whether either of them care to provide a definitive answer.

Mark Bartelt                                               416/978-5619
Canadian Institute for                            mark@cita.toronto.edu
Theoretical Astrophysics                          mark@cita.utoronto.ca

vd09+@andrew.cmu.edu (Vincent M. Del Vecchio) (04/14/91)

I'm not sure I understand what the discussion is about here, though I'd like
to.  Is the complaint that pc gets incremented after the function returns,
instead of immediately before?  In other words, the correct function gets
called, but pc has a "wrong" value during the execution of the function?

Or is it something else?

-Vincent Del Vecchio
vd09@andrew.cmu.edu

jit@SLIC.CELLBIO.DUKE.EDU (Jit Keong Tan) (04/14/91)

Let me join the crowd.


In article <1991Apr12.175909.5194@helios.physics.utoronto.ca> mark@cita.toronto.edu writes:
>David Blythe wrote ...
>
> I recently dug up a copy of the hoc calculator described in kernighan & pike
> only to discover that the C compiler does not increment pc until after the
> function call returns in the following statment:
>                   (*(*pc++))();
> whereas the rest of the program is assuming it is done before the function is
> actually called.  Is the compiler interpretation supposed to be implementation
> dependent?  I would suspect not, but then I can't believe no one else has

CASE 1
======
My interpretation of (*(*pc++))() is

1) Parse the innermost parenthesis:
   i) location A =  *pc
   ii) pc++
2)  *(location A)  is a function pointer, call the function.

So pc should increment BEFORE the function is called.


CASE 2
======
If it is (**pc++)(), it should be fairly straight forward.

1) location A = *pc
2) *(location A) is a function pointer, call the function
3) pc++

So the increment should be AFTER the function is called.

===========================================

I wrote a little program to support my argument but of all 3 compilers
that I tested (SGI, GNU, SUN) did not fully support me.  So I may be
wrong.  But I still like to think that I have interpreted it correctly.

Here's my little program:
----------------------------------------------------------------------
/* JKT test of compiler behavior on function pointer */
/* Any comment is very much welcome.  jit@slic.cellbio.duke.edu */

#include <stdio.h>

void (**ptr)();
void (*fptr[3])();

void pr_all_ptr();
void fa();
void fb();
void fc();

main()
{
    fptr[0] = fa;
    fptr[1] = fb;
    fptr[2] = fc;

    ptr = fptr;
    printf("==============Before any changes\n");
    pr_all_ptr();

    printf("==============( **ptr++) ()\n");
    ptr = fptr;
    (**ptr++) ();
    printf("After ==( **ptr++) ()\n");
    pr_all_ptr();

    printf("==============( * (*ptr++)) ()\n");
    ptr = fptr;
    (* (*ptr++)) ();
    printf("After ==( * (*ptr++)) ()\n");
    pr_all_ptr();
}

void pr_all_ptr()
{
    printf("f?():  fa(%X h)  fb(%X h)  fc(%X h)\n",
	    (int)fa, (int)fb, (int)fc);
    printf("fptr: [0](%X h) [1](%X h) [2](%X h)\n",
	    (int)fptr[0], (int)fptr[1], (int)fptr[2]);
    printf("**ptr: %X h\n",(int)**ptr);
}

void fa()
{
    static int count=0;

    printf("fa being called %d\n",count++);
    pr_all_ptr();
    printf("Before Exiting fafafafafafa\n");
}

void fb()
{
    static int count=0;

    printf("fb being called %d\n",count);
    pr_all_ptr();
    printf("Before Exiting fbfbfbfbfbfbfb\n");
}

void fc()
{
    static int count=0;

    printf("fc being called %d\n",count);
    pr_all_ptr();
    printf("Before Exiting fcfcfcfcfcfcfc\n");
}

END OF PROGRAM 
----------------------------------------------------------------------

The following is the output of the sample program from SGI, GNU, SUN:

SGI
==============Before any changes
f?():  fa(40036C h)  fb(4003B4 h)  fc(4003F0 h)
fptr: [0](40036C h) [1](4003B4 h) [2](4003F0 h)
**ptr: 40036C h
==============( **ptr++) ()
fa being called 0
f?():  fa(40036C h)  fb(4003B4 h)  fc(4003F0 h)
fptr: [0](40036C h) [1](4003B4 h) [2](4003F0 h)
**ptr: 40036C h
Before Exiting fafafafafafa
After ==( **ptr++) ()
f?():  fa(40036C h)  fb(4003B4 h)  fc(4003F0 h)
fptr: [0](40036C h) [1](4003B4 h) [2](4003F0 h)
**ptr: 4003B4 h
==============( * (*ptr++)) ()
fa being called 1
f?():  fa(40036C h)  fb(4003B4 h)  fc(4003F0 h)
fptr: [0](40036C h) [1](4003B4 h) [2](4003F0 h)
**ptr: 40036C h
Before Exiting fafafafafafa
After ==( * (*ptr++)) ()
f?():  fa(40036C h)  fb(4003B4 h)  fc(4003F0 h)
fptr: [0](40036C h) [1](4003B4 h) [2](4003F0 h)
**ptr: 4003B4 h

GNU
==============Before any changes
f?():  fa(4003F0 h)  fb(400458 h)  fc(4004B0 h)
fptr: [0](4003F0 h) [1](400458 h) [2](4004B0 h)
**ptr: 4003F0 h
==============( **ptr++) ()
fa being called 0
f?():  fa(4003F0 h)  fb(400458 h)  fc(4004B0 h)
fptr: [0](4003F0 h) [1](400458 h) [2](4004B0 h)
**ptr: 400458 h
Before Exiting fafafafafafa
After ==( **ptr++) ()
f?():  fa(4003F0 h)  fb(400458 h)  fc(4004B0 h)
fptr: [0](4003F0 h) [1](400458 h) [2](4004B0 h)
**ptr: 400458 h
==============( * (*ptr++)) ()
fa being called 1
f?():  fa(4003F0 h)  fb(400458 h)  fc(4004B0 h)
fptr: [0](4003F0 h) [1](400458 h) [2](4004B0 h)
**ptr: 400458 h
Before Exiting fafafafafafa
After ==( * (*ptr++)) ()
f?():  fa(4003F0 h)  fb(400458 h)  fc(4004B0 h)
fptr: [0](4003F0 h) [1](400458 h) [2](4004B0 h)
**ptr: 400458 h

SUN
==============Before any changes
f?():  fa(2434 h)  fb(2488 h)  fc(24CC h)
fptr: [0](2434 h) [1](2488 h) [2](24CC h)
**ptr: 2434 h
==============( **ptr++) ()
fa being called 0
f?():  fa(2434 h)  fb(2488 h)  fc(24CC h)
fptr: [0](2434 h) [1](2488 h) [2](24CC h)
**ptr: 2488 h
Before Exiting fafafafafafa
After ==( **ptr++) ()
f?():  fa(2434 h)  fb(2488 h)  fc(24CC h)
fptr: [0](2434 h) [1](2488 h) [2](24CC h)
**ptr: 2488 h
==============( * (*ptr++)) ()
fa being called 1
f?():  fa(2434 h)  fb(2488 h)  fc(24CC h)
fptr: [0](2434 h) [1](2488 h) [2](24CC h)
**ptr: 2488 h
Before Exiting fafafafafafa
After ==( * (*ptr++)) ()
f?():  fa(2434 h)  fb(2488 h)  fc(24CC h)
fptr: [0](2434 h) [1](2488 h) [2](24CC h)
**ptr: 2488 h

--------------------------------------------------------
Jit Keong Tan     | internet: jit@slic.cellbio.duke.edu
(919) 684-8098    | bitnet  : tan00001@dukemc.bitnet
--------------------------------------------------------
U.S. Mail:
Duke University Medical Center
Department Of Cell Biology
Box 3709
Nanaline Duke Bldg, Rm. 349
Durham, NC 27710

davea@quasar.wpd.sgi.com (David B.Anderson) (04/16/91)

David Blythe wrote:
> I recently dug up a copy of the hoc calculator described in kernighan & pike
> only to discover that the C compiler does not increment pc until after the
> function call returns in the following statment:
>                   (*(*pc++))();

Recently, I quoted ANSI C: 3.3.2.2, page 42. line 20.  ``The order of
evaluation of the function designator, the arguments, and subexpressions
within the arguments is unspecified, but there is a sequence point before
the actual call.'' as justifying the Release 3.3.x C compiler in compiling
the above statement.

I was wrong and the 3.3 C compiler is wrong.  The variable ``pc'' must
be incremented after the function address is calculated but *before* 
the function is called.

There is no ambiguity in the Standard, but I somehow misunderstood the
quoted sentence in the Standard.

I would like to thank Mark Bartelt (mark@cita.toronto.edu) for being 
persistent (via e-mail) and helping me to understand my error.

The C compiler bug is fixed in the next release of IRIX (4.0).

My apologies to anyone mislead by my error.

Regards,
[ David B. Anderson  Silicon Graphics  (415)335-1548  davea@sgi.com ]
[``What can go wrong?''                           --Calvin to Hobbes]

sysmark@aurora.physics.utoronto.ca (Mark Bartelt) (04/17/91)

In article <97603@sgi.sgi.com>
davea@quasar.wpd.sgi.com (David B.Anderson) writes:

[  regarding David Blythe's question about the compiler's misbehaviour
   when dealing with (*(*pc++))()  ]

| I would like to thank Mark Bartelt (mark@cita.toronto.edu) for being
| persistent (via e-mail) and helping me to understand my error.

Thanks for the thanks, but most of the credit really should go to Henry
Spencer (UofT's local source of ultimate oracular wisdom on anything and
everything related to C, UNIX, and a host of other things), who offered
his interpretation of what the standard says.  For those of you who may
be interested, here are his comments:

| ANSI C's sequence-point stuff is, in general, enough to make your head
| ache.  However, in this case the standard is clear   [ ... ]
|
|         3.3.2.2:  "The order of evaluation of the function designator,
| the arguments, and the subexpressions within the arguments is unspecified,
| but there is a sequence point before the actual call."
|
| This very issue -- increments vs. function calls -- was a hotly-debated
| point both before and during standardization, hence the very explicit
| resolution of it.

Consulting Henry also afforded an opportunity to get my own (incorrect)
opinions on a couple of matters straightened out.  Specifically:

(1)  In one of my previous postings, I offered biblical quotes (K&R-2) to
support my contention that the compiler was misbehaving.  Dave Anderson
pointed out that doing so was wrong (albeit a common mistake), and that
the standard is now the only proper arbiter of correct behaviour.  Henry
backed this up, saying:

|          your correspondent is correct in saying that nothing short of the
| actual standard is authoritative on the fine points.  K&R2 not only made no
| attempt to be authoritative, but is wrong in small things due to having
| been written based on a late draft rather than the final standard.

(2)  At one point, I asserted that
                                                Since the incrementing of an
 operand (after noting its value) is part of the definition of the semantics
 of the postfix ++ operator, it seems quite clear that postfix incrementing
 has to be treated as an atomic operation.

Wrongo.  Henry offers a correction:

| There has never been any promise that postfix ++ was atomic, and indeed
| some compilers do postpone the increment a little in some circumstances.

Oh, well, it just goes to prove that no matter how cocky and self-confident
I seem to be, there'll always be an opportunity to embarrass myself in public
with a mistake or two, even when I'm right! :-)

Mark Bartelt                                                    416/978-5619
Canadian Institute for                                 mark@cita.toronto.edu
Theoretical Astrophysics                               mark@cita.utoronto.ca