[gnu.gcc.bug] Bug in handling of anonymous args on Pyramid

jonathan@comp.vuw.ac.nz (07/13/89)

GCC version:
     GNU CC 1.35

Machine::
	Pyramid 90x family.

Symptom::
    Functions using stdargs mysteriously lose their first unnamed
    argument.

Repeat-by::
    Compile and run the following program with GCC on a Pyramid.
    Notice the lack of a "1" in the output.

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

void test_stdarg (FILE * stream, ...);

int
main (argc, argv)
    int argc;
    char *argv [];
{
  test_stdarg (stderr, 1, 2, 3, 4, 5, 6);
  return (0);
}

void
test_stdarg ( FILE *stream, ...)
{
  extern int _doprnt();
  va_list ap;
  int i, arg;
	
  va_start(ap, stream);
  for (i =0; i< 6;i++) {
   arg = va_arg(ap, int);
   fprintf(stream, "%d ", arg);
  }
  fprintf(stream, "\n");
  va_end(ap);
}


Output::
     2 3 4 5 6 0

Assembler Output::
    Ommited.

Analysis::
    This problem is not caused by stdarg.h looking one arg further
    ahead than it should.  I tried changing stdarg.h to make va_arg()
    return the argument *before* the correct one. With that change,
    the example above produce "8584 2 3 4 5 6".  8584 happens to be the
    value of stderr, the last named arg.

    (This gets long-winded and technical, starting now.)

    Running with the debugger on this and other examples shows that
    that expand_call() always passes the last named arg to
    FUNCTION_ARG with NAMED non-zero. The Pyramid FUNCTION_ARG obediently
    passes this arg in a register (if one is available). 
    (It may be that the Pyramid md is not doing something right here;
    but it is doing exactly what the Internals manual and RMS say it
    should.)

    To say it another way, in expr.c,

      /* Decide where to pass this arg.  */
      /* args[i].reg is nonzero if all or part is passed in registers.
	 args[i].partial is nonzero if part but not all is passed in registers,
	  and the exact value says how many words are passed in registers.  */

      if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
	  && args_size.var == 0
	  /* error_mark_node here is a flag for the fake argument
	     for a structure value address.  */
	  && TREE_PURPOSE (p) != error_mark_node)
	{
	  args[i].reg = FUNCTION_ARG (args_so_far, TYPE_MODE (type), type,
				      i < n_named_args);
	  /* If this argument needs more than the usual parm alignment, do
	     extrinsic padding to reach that alignment.  */

    the expression "i < n_named_args" is true for the last named arg,
    so it gets passed in a register. It is false for all the unnamed args,
    so the md passes them all on the stack, as RMS says it should.

    When compiling the callee, stmt.c finds that this same last named
    argument is arriving in a register:

      if (TREE_CODE (TYPE_SIZE (TREE_TYPE (parm))) == INTEGER_CST
	  || stack_offset.var != 0)
	{
#ifdef FUNCTION_INCOMING_ARG
	  entry_parm
	    = FUNCTION_INCOMING_ARG (args_so_far, passed_mode,
				     DECL_ARG_TYPE (parm), 1);
#else
	  entry_parm
	    = FUNCTION_ARG (args_so_far, passed_mode, DECL_ARG_TYPE (parm), 1);
#endif
	}
      /* If this parm was passed part in regs and part in memory,
	 pretend it arrived entirely in memory
	 by pushing the register-part onto the stack.

	 In the special case of a DImode or DFmode that is split,
	 we could put it together in a pseudoreg directly,
	 but for now that's not worth bothering with.  */

-->  /* If this is the last named arg and anonymous args follow,
-->	 likewise pretend this arg arrived on the stack
	 so varargs can find the anonymous args following it.  */
      {
	int nregs = 0;
	int i;
	
	...	

    It subsequently builds RTXes to store the last named arg
    back into the first argument slot on the stack. This
    is exactly where the caller put the first unnamed arg,
    which gets clobbered.


Fix::
    Not obvious to me.  I'm not even sure where the bug is.
    I suspect no-one has found this bug before because all other
    machines that pass args in registers have well-defined shadow
    locations on the stack, and the current code simply stores
    the last named arg into its location.
    This is not the case for Pyramids; args passed in regs have no
    natural home on the stack.

    It's easy to fix the caller side by making expand_expr pass the last
    named arg on the stack:

*** /usr/src/gnu/gcc/expr.c	Wed May 24 10:46:14 1989
--- ./expr.c	Thu Jul 13 19:56:26 1989
***************
*** 3887,3893 ****
--- 3887,3897 ----
       This may actually be 1 too large, but that happens
       only in the case when all args are named, so no trouble results.  */
    if (TYPE_ARG_TYPES (funtype) != 0)
+ #if 0
      n_named_args = list_length (TYPE_ARG_TYPES (funtype));
+ #else
+     n_named_args = list_length (TYPE_ARG_TYPES (funtype)) -1;
+ #endif
    else
      /* If we know nothing, treat all args as named.  */
      n_named_args = num_actuals;


    But this doesn't help the callee.  FUNCTION_INCOMING_ARG
    is only ever called in one place, and NAMED is *always* one, so
    assign_parms will always believe that the last named arg
    came in a register (should one be available).
    Perhaps this should be changed to zero for last named args?

    I'm not sure what effect such changes will have on other machines that
    pass args in registers.  Mips machines allocate a stack slot
    even for args that are passed in registers, so the current code
    shoul work on a Mips. How about Sparc?

    Is the approach I suggest in any case the right sort of things to
    do?  If  not, what is?