From: michael@garfield.cs.mun.ca (Mike Rendell)
Subject: Double arg passing bugs, gcc 2.2.2 for hppa1.1 (+fixes)
Organization: CS Dept., Memorial University of Newfoundland
Distribution: gnu
Date: Sat, 26 Sep 1992 16:29:00 GMT

Gcc 2.2.2 for hppa1.1 has two problems with passing double arguments:
one for varargs functions and one for functions whose fourth argument
is a double.

The first problem occurs when a double argument is passed as one of the
first three arguments to a varargs function: the value returned by
va_arg() is incorrect.  The following program demonstrates the problem:

	#include	<stdio.h>
	#include	<stdarg.h>

	void
	foobar(char *str, ...)
	{
		va_list	args;

		va_start(args, str);
		printf("%s %g\n", str, va_arg(args, double));
		va_end(args);
	}

	void
	main(void)
	{
		foobar("hi", 5.0);
	}

The problem is that the function hppa_builtin_saveregs() in config/pa.c
flushes float registers %fr3,%fr4,%fr5,%fr6 and double registers %fr4,%fr6
instead of float registers %fr4,%fr5,%fr6,%fr7 and double registers
%fr5,%fr7.  The following context diff for config/pa.c fixes the problem.

*** pa.c.orig	Fri Sep 25 13:24:07 1992
--- pa.c	Fri Sep 25 13:25:56 1992
***************
*** 2374,2392 ****
  					     0, 0, OPTAB_LIB_WIDEN)));
    /* Store fp registers. */
    emit_move_insn (gen_rtx (MEM, SFmode, float_addr),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 60 : 39));
    emit_move_insn (gen_rtx (MEM, SFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 4))),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 58 : 38));
    emit_move_insn (gen_rtx (MEM, SFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 8))),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 56 : 37));
    emit_move_insn (gen_rtx (MEM, SFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 12))),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 54 : 36));
    /* 
     * Allocate space for the double args, and store it in the 
     * __va_double member.
--- 2374,2392 ----
  					     0, 0, OPTAB_LIB_WIDEN)));
    /* Store fp registers. */
    emit_move_insn (gen_rtx (MEM, SFmode, float_addr),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 62 : 39));
    emit_move_insn (gen_rtx (MEM, SFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 4))),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 60 : 38));
    emit_move_insn (gen_rtx (MEM, SFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 8))),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 58 : 37));
    emit_move_insn (gen_rtx (MEM, SFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 12))),
! 		  gen_rtx (REG, SFmode, TARGET_SNAKE ? 56 : 36));
    /* 
     * Allocate space for the double args, and store it in the 
     * __va_double member.
***************
*** 2409,2418 ****
    /* Store fp registers as doubles. */
  
    emit_move_insn (gen_rtx (MEM, DFmode, float_addr),
! 		  (gen_rtx (REG, DFmode, TARGET_SNAKE ? 60 : 39)));
    emit_move_insn (gen_rtx (MEM, DFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 8))),
! 		  gen_rtx (REG, DFmode, TARGET_SNAKE ? 56 : 37));
    return copy_to_reg (XEXP (block, 0));
  }
--- 2409,2418 ----
    /* Store fp registers as doubles. */
  
    emit_move_insn (gen_rtx (MEM, DFmode, float_addr),
! 		  (gen_rtx (REG, DFmode, TARGET_SNAKE ? 62 : 39)));
    emit_move_insn (gen_rtx (MEM, DFmode, gen_rtx (PLUS, Pmode, float_addr,
  						 gen_rtx (CONST_INT,
  							  Pmode, 8))),
! 		  gen_rtx (REG, DFmode, TARGET_SNAKE ? 58 : 37));
    return copy_to_reg (XEXP (block, 0));
  }


The second problem occurs when a function's fourth argument is a double
and the preceeding three arguments are pointers/ints/shorts/chars.
The .EXPORT directive generated for such a function says the fourth
argument is in a register (ARG3=FR,ARG4=FU) when in fact it is not.
The following program demonstrates the problem:

	int
	blah(int a, int b, int c, double d)
	{
		return (int) d;
	}

	void
	main(void)
	{
		blah(1, 2, 3, 4.0);
	}

The result from gcc is:

% gcc -v -O t.c
Reading specs from /usr/local/lib/gcc-lib/hppa1.1-hp720-hpux/2.2.2/specs
gcc version 2.2.2
 /usr/local/lib/gcc-lib/hppa1.1-hp720-hpux/2.2.2/cpp -lang-c -v -undef -D__GNUC__=2 -Dhppa -Dhp9000s800 -Dhp9000s700 -Dhp9k8 -DPWB -Dhpux -Dunix -D_HPUX_SOURCE -D__hppa__ -D__hp9000s800__ -D__hp9000s700__ -D__hp9k8__ -D__PWB__ -D__hpux__ -D__unix__ -D___HPUX_SOURCE__ -D__hppa -D__hp9000s800 -D__hp9000s700 -D__hp9k8 -D__PWB -D__hpux -D__unix -D___HPUX_SOURCE -D__OPTIMIZE__ t.c /usr/tmp/cca10054.i
GNU CPP version 2.2.2 (hp9000s800)
 /usr/local/lib/gcc-lib/hppa1.1-hp720-hpux/2.2.2/cc1 /usr/tmp/cca10054.i -quiet -dumpbase t.c -O -version -o /usr/tmp/cca10054.s
GNU C version 2.2.2 (hp9000s800) compiled by GNU C version 2.2.2.
 /usr/local/lib/gcc-lib/hppa1.1-hp720-hpux/2.2.2/as -o t.o /usr/tmp/cca10054.s
/usr/tmp/cca10054.s:14:Illegal argument description: 255
gcc: t.o: No such file or directory
% gcc -O -S t.c
% cat t.s
	.SPACE $PRIVATE$
	.SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31
	.SPACE $TEXT$
	.SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44
	.SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY
	.IMPORT $global$,DATA
	.IMPORT $$dyncall,MILLICODE
; gcc_compiled.:
	.SPACE $TEXT$
	.SUBSPA $CODE$

	.align 4
	.EXPORT blah
	.EXPORT blah,PRIV_LEV=3,ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=FR,ARGW4=FU,RTNVAL=GR
blah
	.PROC
	.CALLINFO FRAME=64,NO_CALLS
	.ENTRY
	copy 4,1
	copy 30,4
	stwm 1,64(0,30)
	ldo -56(%r4),%r19
	fldds 0(0,%r19),%fr4
	fcnvfxt,dbl,sgl %fr4,%fr5
	fstws %fr5,-16(30)
	ldw -16(30),%r28
	ldo 8(4),30
	bv 0(2)
	ldwm -8(30),4
	.EXIT
	.PROCEND
	.IMPORT __main,CODE
	.align 8
LC$0000
	; .double 4.00000000000000000000e+00
	.word 1074790400 ; = 0x40100000
	.word 0 ; = 0x0
	.align 4
	.EXPORT main
	.EXPORT main,PRIV_LEV=3,RTNVAL=GR
main
	.PROC
	.CALLINFO FRAME=128,CALLS,SAVE_RP
	.ENTRY
	stw 2,-20(0,30)
	copy 4,1
	copy 30,4
	stwm 1,128(0,30)
	.CALL 
	bl __main,2
	nop
	ldo -56(%r30),%r20
	ldil L'LC$0000,%r19
	ldo R'LC$0000(%r19),%r19
	ldw 0(0,%r19),%r22
	ldw 4(0,%r19),%r23
	stw %r22,-16(0,30)
	stw %r23,-12(0,30)
	fldds -16(0,30),%fr4
	fstds %fr4,0(0,%r20)
	ldo 1(0),%r26
	ldo 2(0),%r25
	ldo 3(0),%r24
	.CALL ARGW0=GR,ARGW1=GR,ARGW2=GR
	bl blah,2
	nop
	ldo 8(4),30
	ldw -28(0,30),2
	bv 0(2)
	ldwm -8(30),4
	.EXIT
	.PROCEND

Note the .EXPORT line for blah says the fourth argument is in a register while
the code for blah takes the argument from the stack.  The problem is in the
ASM_DECLARE_FUNCTION_NAME macro in config/pa.h which does an alignment check
for a double in the second argument, but not the fourth.  If a double is in
the fourth argument, nothing should be printed.  The following context diff
for config/pa.h fixes the problem (note that i must be incremented to 4
when i == 3 since it is used later in the macro).

*** pa.h.orig	Sat Sep 26 10:50:26 1992
--- pa.h	Sat Sep 26 10:53:09 1992
***************
*** 783,789 ****
  		   fprintf (FILE, ",ARGW%d=FR", i++);			\
  		 else if (TYPE_MODE (DECL_ARG_TYPE (parm)) == DFmode)	\
  		   {							\
! 		      if (i == 1) i++;				        \
  		      ASM_DOUBLE_ARG_DESCRIPTORS (FILE, i++, i++);	\
  		   }							\
  		 else							\
--- 783,793 ----
  		   fprintf (FILE, ",ARGW%d=FR", i++);			\
  		 else if (TYPE_MODE (DECL_ARG_TYPE (parm)) == DFmode)	\
  		   {							\
! 		      if (i & 1)					\
! 			{						\
! 			  i++;						\
! 			  if (i == 4) break;				\
! 			}						\
  		      ASM_DOUBLE_ARG_DESCRIPTORS (FILE, i++, i++);	\
  		   }							\
  		 else							\


--
Michael Rendell - Dept. of Comp. Sci., Memorial University of Newfoundland

Newsgroups: gnu.gcc.bug