From: Brendan.Lynch@east.SUn.COM (Brendan Lynch - Sun Boston Development Center Software)
Subject: Bug in gcc-2.1 (and 2.0): 'const' function calls.
Organization: GNUs Not Usenet
Distribution: gnu
Date: Tue, 7 Apr 1992 17:32:50 GMT

The gcc-2.1 compiler does not correctly handle deferred argument
poping in a routine in which calls to 'const' functions have been
removed during optimization.  This problem is not
architecture-specific, but should affect all machines for which
deferred argument popping is allowed.

The following example shows this:

The routine:
-------------------------
extern double const sqrt(double);

double a[8];

extern void func2();

void
init(void)
{
	a[0] = sqrt(1.5);
	a[1] = 2 * sqrt(3.25);
	a[2] = 2 * sqrt(3.25);
	a[3] = 2 * sqrt(3.65);
	a[4] = 3 * sqrt(3.25);
	func2(a);
}
-------------------------

generates the following code on a Sun386 (Sun syntax assembler):

-----------------------------
	.file	"trysc.c"
	.version	"GNU C 2.1"
	.optim
gcc2_compiled.:
	.text
	.align 4
.LC0:
	.double 3.0000000000000000000000e+00
	.align 4
.globl init
init:
	pushl %ebp
	movl %esp,%ebp
	subl $8,%esp
	pushl $1073217536
	pushl $0
	call sqrt
	fstpl a
	pushl $1074397184
	pushl $0
	call sqrt
	fstpl -8(%ebp)
	fldl -8(%ebp)
	fadd %st(0),%st
	fstl a+8
	fstpl a+16
	pushl $1074606899
	pushl $858993459
	call sqrt
	fadd %st(0),%st
	fstpl a+24
	fldl -8(%ebp)
	fmull .LC0
	fstpl a+32
	addl $40,%esp		<< error
	pushl $a
	call func2
	leave
	ret
	.align 4
.comm a,64
-----------------------------

The erroneous staement is that which pushes 40 bytes from the stack.
Although there were originally 5 calls to 'sqrt' (each pushing 8 bytes
of arguments), 2 of them have been optimized away, leaving only 24
bytes of arguments on the stack.  Popping 40 bytes causes the stack to
get messed up.

The expand_call routine, in "calls.c" at line 598, has code which
looks as if it were intended to avoid this problem:

  /* When calling a const function, we must pop the stack args right away,
     so that the pop is deleted or moved with the call.  */
  if (is_const)
    NO_DEFER_POP;

Unfortunately this only sets the 'inhibit_defer_pop' flag which is
reset to the value of 'old_inhibit_defer_pop' in 'emit_call_1'
*before* the check to see if the caller must pop the arguments
immediatley after the call.

The fix I have implemented is to set 'old_inhibit_defer_pop' at the
same time; this seems to generate correct code, but I am not sure if
it is the best fix for this problem.

With this fix in place, the following code is generated:

-----------------------------------------
	.file	"trysc.c"
	.version	"GNU C 2.1"
	.optim
gcc2_compiled.:
	.text
	.align 4
.LC0:
	.double 3.0000000000000000000000e+00
	.align 4
.globl init
init:
	pushl %ebp
	movl %esp,%ebp
	subl $8,%esp
	pushl $1073217536
	pushl $0
	call sqrt
	addl $8,%esp
	fstpl a
	pushl $1074397184
	pushl $0
	call sqrt
	addl $8,%esp
	fld %st(0)
	fadd %st,%st(1)
	fxch %st(1)
	fstl a+8
	fstpl a+16
	pushl $1074606899
	pushl $858993459
	fstpl -8(%ebp)
	call sqrt
	addl $8,%esp
	fadd %st(0),%st
	fstpl a+24
	fldl -8(%ebp)
	fmull .LC0
	fstpl a+32
	pushl $a
	call func2
	leave
	ret
	.align 4
.comm a,64
--------------------------------

- ---------------------------------------------

Brendan Lynch

EMail:	internal: blynch@East
	external: Brendan.Lynch@East.Sun.COM


Newsgroups: gnu.gcc.bug