From: kadach@isi.ITfs.nsk.SU (Andrew Cadach)
Subject: GCC 1.39 bug report and suggestions
Organization: Institute of Informatics Systems
Distribution: gnu
Date: Thu, 16 Apr 1992 17:43:27 GMT

                       Dear sir!
   Please, take my apologies wasting your time.

   I'm trying to install GCC version 1.39  at  Atari  Mega  ST2
computer with MC68000 CPU. I had some problems concerning  such
fact that GCC itself is written in non-ANSI way.

   The matter is that Turbo C compiler I used passes parameters
in registers and looks at type they have: `int',  `char',  etc.
are passed in d0-d2, pointers are passed  in  a0-a1.  Sure  you
know what could happen if compiler doesn't know  the  functions
prototypes. By the way I believe that pass  to  function  wrong
number of parameters or  unite  in  single  array  pointers  to
functions with different kind and/or number  of  parameters  in
hope that all parameters are passed by stack and there will  no
any problems is not the best style of programming. In  my  case
it could just produce wrong code!

   Another problem was following:  compiler  assumed  `int'  as
16-bits, and I've found several places where  suggestion  about
32-bits `int' was important. By the way, would you be  so  kind
and let me to put you some questions:

   1.  Why  in  functions, such  as   'gen_rtx',   'gen_rtvec',
       'emit_library_call', etc. which are need to be  declared
       as  variable  number  of  parameters  functions, authers
       supposed that all pushes at  the  stack  will  be  right
       aligned (i.e. 32-bits)?

   2.  Why authers of 'emit-rtl' supposed that 1st 32  bits  of
       `rtx' structure will contain  ALL  the  flags  (function
       `rtx_alloc', which clears it),  code  and  mode  fields?
       Some compilers could think otherwise...

   And now about the bug itself.

   When I've changed calling convention in  GCC  it  starts  to
produce wrong code. Compiling flags was following:

-O
-fdefer-pop
-fstrength-reduce
-fcombine-regs
-fforce-mem
-fforce-addr
-fomit-frame-pointer
-ffunction-cse
-fno-inline-functions

   When I ran GCC with `-fno-function-cse' option, it  produced
needed instruction (other's flags are the same as above).

   Full source program and GCC output listed below.
---------------------------------------------------------
typedef int ar1[52];
typedef ar1 ar2[52];

void test(ar2 p, int n)
{
  while ( ++p[n][n] );
}
---------------------------------------------------------
test:
 movem.l D3-D4,-(SP)
 move.l A0,D3
 move.l D0,D4
 asl.l #2,D4
 move.l #208,D1
 jsr (A0)          ; suppose __mulsi3!!! a0 is undefined!!!
 move.l D3,A0
 add.l D0,A0
L2:
 move.l (A0,D4.l),D0
 move.l D0,D2
 addq.l #1,D2
 move.l D2,(A0,D4.l)
 addq.l #1,D0
 bne L2
 movem.l (SP)+,D3-D4
 rts
---------------------------------------------------------

                    I've changed following:
                    ----------------------

/* file tm-m68k.h */

#define DEBUG_REGS      0x307  /* d0-d2/a0-a1 */
#define DEBUG_DREGS     3
#define DEBUG_AREGS     10    /* 8+2 */

#define CALL_USED_REGISTERS                     \
 {1, 1, 1, 0, 0, 0, 0, 0, /* d0-d3 */           \
  1, 1, 0, 0, 0, 0, 0, 1, /* a0-a1 */           \
  1, 1, 0, 0, 0, 0, 0, 0,                       \
  /* FPA registers.  */                         \
  1, 1, 1, 1, 0, 0, 0, 0,                       \
  0, 0, 0, 0, 0, 0, 0, 0,                       \
  0, 0, 0, 0, 0, 0, 0, 0,                       \
  0, 0, 0, 0, 0, 0, 0, 0, }

#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0 || (N) == 8)
#define FUNCTION_ARG_REGNO_P(N) (DEBUG_REGS&(1<<(N))

void *fnc_value (void *type, void *func);
#define FUNCTION_VALUE(VALTYPE, FUNC)\
(fnc_value (VALTYPE, FUNC))

typedef struct
{
  short
    areg, /* how much address registers already occupied */
    dreg, /* how much data    registers already occupied */
    cntp, /* incoming parameter counter                  */
    parm; /* total number of named parameters or         */
          /* -1 for undeclared and library functions.    */
          /* We'll try to pass parameters according to   */
          /* their type for this functions anyway.       */
          /* For declared functions only named parameters*/
          /* are passed in registers; extra parameters   */
          /* will be passed in stack slots               */
}
CUMULATIVE_ARGS;
typedef void fnc_fnc_type (
    CUMULATIVE_ARGS *cum, int mode, void *type, int named);

void fnc_init (CUMULATIVE_ARGS *cum, void *fntype);
#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE)        \
(fnc_init (&(CUM),FNTYPE))

extern fnc_fnc_type fnc_advance;
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)    \
(fnc_advance (&(CUM), MODE, TYPE, NAMED))

extern fnc_fnc_type fnc_arg;
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
(fnc_arg (&(CUM), MODE, TYPE, NAMED))

extern fnc_fnc_type fnc_arg_partial;
#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
(fnc_arg_partial (&(CUM), MODE, TYPE, NAMED))

/* my own file */

#define VARG(x) ((long)(x))
void
fnc_init (cum, decl)
     CUMULATIVE_ARGS *cum;      /* argument info to initialize */
     void *decl;                /* tree ptr for function decl */
{
  short parm;

  cum->dreg = 0;
  cum->areg = 8;
  cum->cntp = 0;
  parm = -1;
  if (decl != 0)
  {
    tree arg;
    arg = TYPE_ARG_TYPES (((tree)decl));
    if (arg != 0) /* we have named parameters */
    {
      tree type;
      parm = 0;

      /* arg == 0    if `func (type1 p1,..,typeN pN, ... )' */
      /* arg == void if `func (type1 p1,..,typeN pN, void)' */

      while (arg != 0 && (type = TREE_VALUE (arg)) != void_type_node)
      {
        ++parm;
        arg = TREE_CHAIN (arg);
      }
    }
  }
  cum->parm = parm;
  if (TARGET_DEBUGE_MODE)
    fprintf (stderr, "fnc_init: total named parameters = %ld\n", VARG (parm));
}

void
fnc_advance (cum, mode, type, named)
     CUMULATIVE_ARGS *cum;      /* current arg information */
     int mode;                  /* current arg mode */
     void *type;                /* type of the argument or 0 if lib support */
     int named;
{
  named = cum->cntp < cum->parm || cum->parm < 0;
  /* suppose unnamed if and only if it is really unnamed */

  ++cum->cntp;
  if (!named) return;
  switch (mode)
  {
    default:
      error ("Illegal mode given to fnc_advance");
    case VOIDmode:
    case BLKmode:
      break;

    case DImode:
    case DFmode:
      cum->dreg += 2;
      break;

    case QImode:
    case HImode:
    case SImode:
    case SFmode:
      if (type != 0 && TREE_CODE (((tree)type)) == POINTER_TYPE)
        ++cum->areg;
      else
        ++cum->dreg;
  }
}

void *
fnc_arg (cum, mode, type, named)
     CUMULATIVE_ARGS *cum;      /* current arg information */
     int mode;                  /* current arg mode */
     void *type;                /* type of the argument or 0 if lib support */
     int named;                 /* != 0 for normal args, == 0 for ... args */
{
  named = cum->cntp < cum->parm || cum->parm < 0;
  /* suppose unnamed if and only if it is really unnamed */
  if (!named) return (0);
  switch (mode)
  {
    default:
      error ("Illegal mode given to fnc_arg");
    case VOIDmode:
    case BLKmode:
      break;

    case DImode:
    case DFmode:
      if (cum->dreg < DEBUG_DREGS)
        return (gen_rtx (REG, mode, VARG (cum->dreg)));
    case SFmode:
    case QImode:
    case HImode:
    case SImode:
      if (type != 0 && TREE_CODE (((tree)type)) == POINTER_TYPE)
      {
        if (cum->areg < DEBUG_AREGS )
          return (gen_rtx (REG, mode, VARG (cum->areg)));
      }
      else
      {
        if (cum->dreg < DEBUG_DREGS )
          return (gen_rtx (REG, mode, VARG (cum->dreg)));
      }
  }
  return (0);
}

int
fnc_arg_partial(cum, mode, type, named)
     CUMULATIVE_ARGS *cum;      /* current arg information */
     int mode;                  /* current arg mode */
     void *type;                /* type of the argument or 0 if lib support */
     int named;                 /* != 0 for normal args, == 0 for ... args */
{
  named = cum->cntp < cum->parm || cum->parm < 0;
  /* suppose unnamed if and only if it is really unnamed */
  if (named
      && (mode == DImode || mode == DFmode)
      && cum->dreg == DEBUG_DREGS-1) return (1);
  return (0);
}

void *
fnc_value (type, func)
  void *type, *func;
{
  if (TREE_CODE (((tree)type)) == POINTER_TYPE)
    return gen_rtx (REG, TYPE_MODE (((tree)type)), VARG (8));
  return  gen_rtx (REG, TYPE_MODE (((tree)type)), VARG (0));
}

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

   Several notes now. The matter is that parameter  `named'  is
wrong for functions with variable number of  parameters  (about
always zero, regardless of function prototype), therefore we're
need to count number  of  really  named  parameters  ourselves.
Please note, that current function epilogue/prologue generators
freely uses a0 - this could cause an  error,  but  this  stupid
register parameter allocator is enough for this test.

   There is more clever parameter allocating  procedures  which
is able to use data or address registers if  there's  too  many
pointer parameters and little number  of  data  parameters  and
vise versa. I believe there's anyway better to pass  parameters
in registers than in stack slots, because soon  of  all  callee
will  move  it's  parameters   from   stack   into   registers.
Register-register move at MC68000 is  3  times  quicklier  than
memory-register,  and  code  becomes  shorter  and  much   more
prettier.

   Another way to improve code for MC68000 is to exchange `link
a6' and `movem ...' instructions. We'll need correct offset  to
`FP' or `SP', but restoreing of registers will be much simplier
and willn't need to use `a0' .  Another  way  is  to  use  `a1'
instead of `a0', because `a1' will never be used for  parameter
returning.

   Third improvment is use frame pointer  if  and  only  if  we
really need it: when current function calls 'alloca'.

   Forth is to use instruction `move.l A0, D0', for examle,  to
implement `tstsi' instruction to test `a0',  if  `d0'  is  free
register. There's need to change `m68k.md' file.

   Fifth is following: if current function does't  contain  any
calls (to library functions as well), there's no need  to  save
it's parameters, which were in registers, in  pseudo-registers.
Sometimes such saving produce suboptimal code  and  unrequired.
It doesn't concern `inline' functions because of side effects.

   I think if you'll try such calling convention you'll  notice
code shortage yourself. May be difference in speed for  MC68040
is not so noticable but for  MC68000  and  MC68020  it's  still
important.

   It will  be  possible  to  use  stack-passing  functions  in
libraries  produced  by  other  compilers,  declaring  all  the
functions as `int foo (...);' to  have  the  compiler  to  pass
parameters in stack and  receive  result  from  `d0',  but  GCC
reports an error for such a  function  declaration,  for  great
sorry.

   May be in  latest  version  of  GCC  such  improvements  are
already present and there's no this bug. I'm sorry very much if
so. May be some days later I'll find the bug myself, but I hope
last suggestion I did may be interesting for you. And I believe
you may be already knew about this bug and able to help.

   I hope my terrible English didn't shoke you.

           Sencirely yours,
               Andrew Cadach,
               Novosibirsk State University student.

               Information Systems Institute,
               Lavrentiev avenue, 6 room 489,
               Akademgorodok,
               Novosibirsk,
               Russia.
               Zip: 630090.
               E-mail: kadach@isi.itfs.nsk.su


Newsgroups: gnu.gcc.bug