[gnu.gcc.bug] Code generation bug, gcc block expressions

godard@CURLY.SAMSUNG.COM (Ivan Godard) (08/25/89)

We use the block expression language extension "( {...} )" of your compilers to
cope with the compiler's problems with function types.  We use function data
extensively, as variables, as record components, and as parameters and results
of other functions.  Because of abstraction, we often have to cast from one
function type to another, which causes the compilers trouble as you know.  Our
solution was to write a macro:


	#define AsFunc(rt, p, v)  \
		({\
			typedef rt (*F)p;\
			(F)v;\
		})


which gets called with something like:

	AsFunc(void, (char*, int), Foo)

to give Foo the type "void(*)(char*, int)", which the compiler can't handle in
expression context like this.

We have found that using these casts in the parameter lists to function calls
gives incorrect code.  I have cut down an example to tractable size.  


Example follows (the call which gives problems is marked)::
-----------------------------------------------------------------------------

typedef char headerClass;

typedef union {
    headerClass headerClass;          /* format */

    struct {
        headerClass headerClass;      /* commitHeader */
        long who;
        long where;
        } commit;

    struct {
        headerClass headerClass;      /* format */
        char isBasket:1;         /* basket flag */
        char isThunk:1;         /* virtual object flag */
        long oid;                /* the object's own id, for recovery */
        long basket;
                                /* and the basket it was written under */
        int dataLength;
        int relocLength;
        int handleLength;
        } data;

    } objectHeader;

void WriteData(
		      objectHeader* loc,
		      void (*Data)(long, char*),
		      int size,
		      char* state)
    {
;
    };

extern long MarkAccum(int);

long WriteObject(
		       long id,
		       long trans,
		       long occupied,
		       long oids,
		       long relocs)
    {
    long acc;
    objectHeader* loc;
    objectHeader header;

    acc = MarkAccum(sizeof(objectHeader));
    loc->data.isBasket = header.data.isBasket;
    WriteData(loc,			/* <===================== this call */

({typedef void(*F)(long, char*);
(F)WriteObject;
}),
			  sizeof(objectHeader), 0);
	   return id;
    };


---------------------------------------------------------------------------
end of example.

The result of compilation is:


emitted code follows (bad instruction marked):
---------------------------------------------------------------------------
#NO_APP
gcc_compiled.:
.text
	.even
.globl _WriteData
_WriteData:
	link a6,#0
	unlk a6
	rts
	.even
.globl _WriteObject
_WriteObject:
	link a6,#-24
	movel a2,sp@-
	pea 22:w
	jbsr _MarkAccum
	bfexts a6@(-21){#0:#1},d0
	bfins d0,a2@(1){#0:#1}
	clrl sp@-
	pea 22:w
	addqw #4,sp		<============== bad instruction
	pea _WriteObject
	movel a2,sp@-
	jbsr _WriteData
	movel a6@(8),d0
	movel a6@(-28),a2
	unlk a6
	rts


---------------------------------------------------------------------------
end of emitted code


While this example is certainly not minimal, at least some of the monkey puzzle
before the call sems to be necessary for the error.  When there is more puzzle
there, the amount that the stack gets cut back by the erroneous instruction
increases.  We have examples of similar stack cuts when the blocks are used in
other context than a call.  It is not clear whether the offence has to do with
the block expression or the functional type; perhaps both.  It appears in both
optimized and non optimized code.  Given the same input, g++ does not cause the
error. 

Happy debugging!

Ivan