2004-06-24  Kamil Iskra  <kamil@science.uva.nl>
            Gunther Nikl  <gni@gecko.de>

	* config/m68k/m68k-protos.h (m68k_init_cumulative_args,
	m68k_function_arg_advance, m68k_function_arg): Add prototypes.
	* config/m68k/m68k.h (MASK_REGPARM, TARGET_REGPARM, M68K_MAX_REGPARM,
	M68K_DEFAULT_REGPARM): New defines.
	(TARGET_SWITCHES): Add -mregparm and -mno-regparm.
	(TARGET_OPTIONS): Add -mregparm=.
	(CLASS_MAX_NREGS): Return GET_MODE_NUNITS(MODE) for FP_REGS.
	(struct m68k_args): New structure.
	(CUMULATIVE_ARGS): Use it.
	(FUNCTION_ARG_REGNO_P, INIT_CUMULATIVE_ARGS, FUNCTION_ARG_ADVANCE,
	FUNCTION_ARG): Use new infrastucture for register parameters.
	(m68k_regparm_string, m68k_regparm) Add extern declarations.
	* config/m68k/m68k.c (m68k_handle_type_attribute,
	m68k_comp_type_attributes): Add prototypes. New functions.
	(m68k_regparm_string, m68k_regparm): New.
	(m68k_attribute_table): Add stkparm and regparm attributes.
	(TARGET_COMP_TYPE_ATTRIBUTES): Define to m68k_comp_type_attributes.
	(override_options): Handle m68k_regparm_string and TARGET_REGPARM.
	(m68k_init_cumulative_args, m68k_function_arg_advance,
	m68k_function_arg): New functions.

diff -ru gcc-3.5/gcc/config/m68k/m68k-protos.h gcc-3.5-gg/gcc/config/m68k/m68k-protos.h
--- gcc-3.5/gcc/config/m68k/m68k-protos.h	Wed Jun 23 16:31:19 2004
+++ gcc-3.5-gg/gcc/config/m68k/m68k-protos.h	Fri Jun 25 14:02:12 2004
@@ -66,3 +66,11 @@
 extern bool use_return_insn (void);
 extern void override_options (void);
 extern void init_68881_table (void);
+
+#ifdef RTX_CODE
+#ifdef TREE_CODE
+extern void m68k_init_cumulative_args (CUMULATIVE_ARGS *, tree);
+extern void m68k_function_arg_advance (CUMULATIVE_ARGS *);
+extern struct rtx_def *m68k_function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree);
+#endif
+#endif
diff -ru gcc-3.5/gcc/config/m68k/m68k.c gcc-3.5-gg/gcc/config/m68k/m68k.c
--- gcc-3.5/gcc/config/m68k/m68k.c	Wed Jun 23 16:31:19 2004
+++ gcc-3.5-gg/gcc/config/m68k/m68k.c	Fri Jun 25 14:04:33 2004
@@ -117,6 +117,8 @@
 static tree m68k_handle_fndecl_attribute (tree *node, tree name,
 					  tree args, int flags,
 					  bool *no_add_attrs);
+static tree m68k_handle_type_attribute (tree *, tree, tree, int, bool *);
+static int m68k_comp_type_attributes (tree, tree);
 static void m68k_compute_frame_layout (void);
 static bool m68k_save_reg (unsigned int regno, bool interrupt_handler);
 static int const_int_cost (rtx);
@@ -125,6 +127,11 @@
 
 /* Specify the identification number of the library being built */
 const char *m68k_library_id_string;
+/* Specify number of registers for integer, pointer and float arguments.  */
+const char *m68k_regparm_string;
+
+/* Specify number of registers for integer, pointer and float arguments.  */
+int m68k_regparm;
 
 /* Nonzero if the last compare/test insn had FP operands.  The
    sCC expanders peek at this to determine what to do for the
@@ -190,9 +197,19 @@
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
   { "interrupt_handler", 0, 0, true,  false, false, m68k_handle_fndecl_attribute },
+
+  /* Stkparm attribute specifies to pass arguments on the stack */
+  { "stkparm",           0, 0, false, true,  true,  m68k_handle_type_attribute },
+  /* Regparm attribute specifies how many integer arguments are to be
+     passed in registers.  */
+  { "regparm",           0, 1, false, true,  true,  m68k_handle_type_attribute },
+
   { NULL,                0, 0, false, false, false, NULL }
 };
 
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES m68k_comp_type_attributes
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 /* Sometimes certain combinations of command options do not make
@@ -239,6 +256,19 @@
   if (TARGET_SEP_DATA || TARGET_ID_SHARED_LIBRARY)
     flag_pic = 2;
 
+  /* Validate -mregparm and -mregparm= value.  */
+  if (m68k_regparm_string)
+    {
+      m68k_regparm = atoi (m68k_regparm_string);
+      if (m68k_regparm < 1 || m68k_regparm > M68K_MAX_REGPARM)
+	error ("-mregparm=%d is not between 1 and %d",
+	       m68k_regparm, M68K_MAX_REGPARM);
+      target_flags |= MASK_REGPARM;
+    }
+  else
+    if (TARGET_REGPARM)
+      m68k_regparm = M68K_DEFAULT_REGPARM;
+
   /* -fPIC uses 32-bit pc-relative displacements, which don't exist
      until the 68020.  */
   if (!TARGET_68020 && !TARGET_COLDFIRE && (flag_pic == 2))
@@ -261,6 +291,8 @@
   SUBTARGET_OVERRIDE_OPTIONS;
 }
 
+/* Attributes support.  */
+
 /* Return nonzero if FUNC is an interrupt function as specified by the
    "interrupt_handler" attribute.  */
 static bool
@@ -293,6 +325,247 @@
   return NULL_TREE;
 }
 
+/* Handle a "regparm" or "stkparm" attribute;
+   arguments as in struct attribute_spec.handler.  */
+
+static tree
+m68k_handle_type_attribute (tree *node, tree name, tree args,
+			    int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_TYPE || TREE_CODE (*node) == METHOD_TYPE)
+    {
+      /* 'regparm' accepts one optional argument - number of registers in
+	 single class that should be used to pass arguments.  */
+      if (is_attribute_p ("regparm", name))
+	{
+	  if (lookup_attribute ("stkparm", TYPE_ATTRIBUTES(*node)))
+	    {
+	      error ("`regparm' and `stkparm' are mutually exclusive");
+	    }
+	  if (args && TREE_CODE (args) == TREE_LIST)
+	    {
+	      tree numofregs = TREE_VALUE (args);
+	      if (numofregs)
+		if (TREE_CODE (numofregs) != INTEGER_CST
+/*
+		    || compare_tree_int(numofregs, 1) < 0
+		    || compare_tree_int(numofregs, M68K_MAX_REGPARM) > 0)
+*/
+		    || TREE_INT_CST_HIGH (numofregs)
+		    || TREE_INT_CST_LOW (numofregs) < 1
+		    || TREE_INT_CST_LOW (numofregs) > M68K_MAX_REGPARM)
+		  {
+		    error ("invalid argument to `regparm' attribute");
+		  }
+	    }
+	}
+      else if (is_attribute_p ("stkparm", name))
+	{
+	  if (lookup_attribute ("regparm", TYPE_ATTRIBUTES(*node)))
+	    {
+	      error ("`regparm' and `stkparm' are mutually exclusive");
+	    }
+	}
+    }
+  else
+    {
+      warning ("`%s' attribute only applies to functions",
+	       IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return zero if the attributes on TYPE1 and TYPE2 are incompatible,
+   one if they are compatible, and two if they are nearly compatible
+   (which causes a warning to be generated). */
+
+static int
+m68k_comp_type_attributes (tree type1, tree type2)
+{
+  /* Functions or methods are incompatible if they specify mutually
+     exclusive ways of passing arguments.  */
+  if (TREE_CODE (type1) == FUNCTION_TYPE || TREE_CODE (type1) == METHOD_TYPE)
+    {
+      tree arg1, arg2;
+      if (!! lookup_attribute ("stkparm", TYPE_ATTRIBUTES (type1)) !=
+	     !! lookup_attribute ("stkparm", TYPE_ATTRIBUTES (type2))
+	  || !! lookup_attribute ("regparm", TYPE_ATTRIBUTES (type1)) !=
+	     !! lookup_attribute ("regparm", TYPE_ATTRIBUTES (type2)))
+	return 0; /* 'regparm' and 'stkparm' are mutually exclusive.  */
+
+      arg1 = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type1));
+      arg2 = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type2));
+      if (arg1 && arg2)
+	{
+	  int num1 = 0, num2 = 0;
+	  if (TREE_VALUE (arg1) && TREE_CODE (TREE_VALUE (arg1)) == TREE_LIST)
+	    {
+	      tree numofregs = TREE_VALUE (TREE_VALUE (arg1));
+	      if (numofregs)
+		num1 = TREE_INT_CST_LOW (numofregs);
+	    }
+	  if (TREE_VALUE (arg2) && TREE_CODE (TREE_VALUE (arg2)) == TREE_LIST)
+	    {
+	      tree numofregs = TREE_VALUE (TREE_VALUE (arg2));
+	      if (numofregs)
+		num2 = TREE_INT_CST_LOW (numofregs);
+	    }
+	  if (num1 != num2)
+	    return 0; /* Different numbers, or no number in one type.  */
+	}
+    }
+  return 1;
+}
+
+/* Argument-passing support functions.  */
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  */
+
+void
+m68k_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype)
+{
+  cum->last_arg_reg = -1;
+  cum->regs_already_used = 0;
+  if (fntype)
+    {
+      if (lookup_attribute ("stkparm", TYPE_ATTRIBUTES (fntype)))
+	cum->num_of_regs = 0;
+      else
+	{
+	  tree ratree = lookup_attribute ("regparm", TYPE_ATTRIBUTES (fntype));
+	  if (ratree)
+	    {
+	      cum->num_of_regs = m68k_regparm ? m68k_regparm
+					      : M68K_DEFAULT_REGPARM;
+	      if (TREE_VALUE (ratree)
+		  && TREE_CODE (TREE_VALUE (ratree)) == TREE_LIST)
+		{
+		  tree num_of_regs = TREE_VALUE (TREE_VALUE (ratree));
+		  cum->num_of_regs =
+		    num_of_regs ? TREE_INT_CST_LOW (num_of_regs) :
+		      (m68k_regparm ? m68k_regparm : M68K_DEFAULT_REGPARM);
+		}
+	    }
+	  else
+	    cum->num_of_regs = m68k_regparm;
+	}
+    }
+  else /* Libcall.  */
+    cum->num_of_regs = 0;
+
+  if (cum->num_of_regs)
+    {
+      /* If this is a vararg call, put all arguments on stack.  */
+      tree param, next_param;
+      for (param = TYPE_ARG_TYPES (fntype); param; param = next_param)
+	{
+	  next_param = TREE_CHAIN (param);
+	  if (!next_param && TREE_VALUE (param) != void_type_node)
+	    cum->num_of_regs = 0;
+	}
+    }
+
+#if ! defined (PCC_STATIC_STRUCT_RETURN) && defined (M68K_STRUCT_VALUE_REGNUM)
+  /* If return value is a structure, and we pass the buffer address in a
+     register, we can't use this register for our own purposes.
+     FIXME: Something similar would be useful for static chain.  */
+  if (fntype && aggregate_value_p (TREE_TYPE (fntype), fntype))
+    cum->regs_already_used |= (1 << M68K_STRUCT_VALUE_REGNUM);
+#endif
+}
+
+/* Update the data in CUM to advance over an argument.  */
+
+void
+m68k_function_arg_advance (CUMULATIVE_ARGS *cum)
+{
+  if (cum->last_arg_reg != -1)
+    {
+      int count;
+      for (count = 0; count < cum->last_arg_len; count++)
+	cum->regs_already_used |= (1 << (cum->last_arg_reg + count));
+      cum->last_arg_reg = -1;
+    }
+}
+
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.  */
+
+struct rtx_def *
+m68k_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
+{
+  if (cum->num_of_regs)
+    {
+      int regbegin = -1, altregbegin = -1, len;
+
+      /* FIXME: The last condition below is a workaround for a bug.  */
+      if (TARGET_68881 && FLOAT_MODE_P (mode) &&
+	  GET_MODE_UNIT_SIZE (mode) <= 12 &&
+	  (GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT || mode == SCmode))
+	{
+	  regbegin = 16; /* FPx */
+	  len = GET_MODE_NUNITS (mode);
+	}
+      /* FIXME: Two last conditions below are workarounds for bugs.  */
+      else if (INTEGRAL_MODE_P (mode) && mode !=CQImode && mode != CHImode)
+	{
+	  if (POINTER_TYPE_P (type))
+	    regbegin = 8; /* Ax */
+	  else
+	    regbegin = 0; /* Dx */
+	  altregbegin = 8 - regbegin;
+	  len = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+	}
+
+      if (regbegin != -1)
+	{
+	  int reg;
+	  long mask;
+
+look_for_reg:
+	  mask = 1 << regbegin;
+	  for (reg = 0; reg < cum->num_of_regs; reg++, mask <<= 1)
+	    if (!(cum->regs_already_used & mask))
+	      {
+		int end;
+		for (end = reg; end < cum->num_of_regs && end < reg + len;
+		     end++, mask <<= 1)
+		  if (cum->regs_already_used & mask)
+		    break;
+		if (end == reg + len)
+		  {
+		    cum->last_arg_reg = reg + regbegin;
+		    cum->last_arg_len = len;
+		    break;
+		  }
+	      }
+
+	  if (reg == cum->num_of_regs && altregbegin != -1)
+	    {
+	      regbegin = altregbegin;
+	      altregbegin = -1;
+	      goto look_for_reg;
+	    }
+	}
+
+      if (cum->last_arg_reg != -1)
+	return gen_rtx_REG (mode, cum->last_arg_reg);
+    }
+  return 0;
+}
+
 static void
 m68k_compute_frame_layout (void)
 {
diff -ru gcc-3.5/gcc/config/m68k/m68k.h gcc-3.5-gg/gcc/config/m68k/m68k.h
--- gcc-3.5/gcc/config/m68k/m68k.h	Wed Jun 23 16:31:20 2004
+++ gcc-3.5-gg/gcc/config/m68k/m68k.h	Fri Jun 25 14:02:12 2004
@@ -224,6 +224,11 @@
 #define MASK_ID_SHARED_LIBRARY	(1<<18)
 #define TARGET_ID_SHARED_LIBRARY	(target_flags & MASK_ID_SHARED_LIBRARY)
 
+/* Compile using the first 'm68k_regparm' data, address and float
+   registers for arguments passing.  */
+#define MASK_REGPARM	(1<<24)
+#define TARGET_REGPARM (target_flags & MASK_REGPARM)
+
 /* Compile for a CPU32.  A 68020 without bitfields is a good
    heuristic for a CPU32.  */
 #define TARGET_CPU32	(TARGET_68020 && !TARGET_BITFIELD)
@@ -334,6 +339,10 @@
       N_("Use different calling convention using 'rtd'") },		\
     { "nortd", - MASK_RTD,						\
       N_("Use normal calling convention") },				\
+    { "regparm", MASK_REGPARM,						\
+      N_("Pass arguments through registers") },				\
+    { "no-regparm", - MASK_REGPARM,					\
+      N_("Don't pass arguments through registers") },			\
     SUBTARGET_SWITCHES							\
     { "", TARGET_DEFAULT, "" }}
 /* TARGET_DEFAULT is defined in m68k-none.h, netbsd.h, etc.  */
@@ -342,6 +351,8 @@
 {									\
   { "shared-library-id=",	&m68k_library_id_string,		\
     N_("ID of shared library to build"), 0},				\
+  { "regparm=",		&m68k_regparm_string,				\
+    N_("Use this register count to pass arguments"), 0},		\
   SUBTARGET_OPTIONS							\
 }
 
@@ -652,7 +663,7 @@
 /* On the m68k, this is the size of MODE in words,
    except in the FP regs, where a single reg is always enough.  */
 #define CLASS_MAX_NREGS(CLASS, MODE)	\
- ((CLASS) == FP_REGS ? 1 \
+ ((CLASS) == FP_REGS ? GET_MODE_NUNITS (MODE) \
   : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
 
 /* Moves between fp regs and other regs are two insns.  */
@@ -710,24 +721,60 @@
 
 #define PCC_STATIC_STRUCT_RETURN
 
-/* On the m68k, all arguments are usually pushed on the stack.  */
-#define FUNCTION_ARG_REGNO_P(N) 0
+/* 1 if N is a possible register number for function argument passing.  */
+#define FUNCTION_ARG_REGNO_P(N)			\
+  ((((int)N) >= 0 && (N) < M68K_MAX_REGPARM)		\
+   || ((N) >= 8 && (N) < 8 + M68K_MAX_REGPARM)	\
+   || (TARGET_68881 && (N) >= 16 && (N) < 16 + M68K_MAX_REGPARM))
 
-/* On the m68k, this is a single integer, which is a number of bytes
-   of arguments scanned so far.  */
-#define CUMULATIVE_ARGS int
+/* On the m68k, this is a structure:
+   num_of_regs: number of data, address and float registers to use for
+     arguments passing (if it's 2, than pass arguments in d0, d1, a0, a1,
+     fp0 and fp1). 0 - pass everything on stack. vararg calls are
+     always passed entirely on stack.
+   regs_already_used: bitmask of the already used registers.
+   last_arg_reg - register number of the most recently passed argument.
+     -1 if passed on stack.
+   last_arg_len - number of registers used by the most recently passed
+     argument.
+*/
+
+struct m68k_args
+{
+  int num_of_regs;
+  long regs_already_used;
+  int last_arg_reg;
+  int last_arg_len;
+};
+
+#define CUMULATIVE_ARGS struct m68k_args
+
+/* Max. number of data, address and float registers to be used for passing
+   integer, pointer and float arguments when TARGET_REGPARM.
+   It's 4, so d0-d3, a0-a3 and fp0-fp3 can be used.  */
+
+#define M68K_MAX_REGPARM 4
+
+/* The default number of data, address and float registers to use when
+   user specified '-mregparm' switch, not '-mregparm=<value>' option.  */
+
+#define M68K_DEFAULT_REGPARM 2
 
-/* On the m68k, the offset starts at 0.  */
 #define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
- ((CUM) = 0)
+  (m68k_init_cumulative_args (&(CUM), (FNTYPE)))
 
 #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)	\
- ((CUM) += ((MODE) != BLKmode			\
-	    ? (GET_MODE_SIZE (MODE) + 3) & ~3	\
-	    : (int_size_in_bytes (TYPE) + 3) & ~3))
+  (m68k_function_arg_advance (&(CUM)))
+
+/* On m68k all args are pushed, except if -mregparm is specified, then
+   a number of arguments is passed in the first 'm68k_regparm' data,
+   address and float registers.
+   Note: by default, the static-chain is passed in a0. Targets that want
+   to make full use of '-mregparm' are advised to pass the static-chain
+   somewhere else.  */
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+  (m68k_function_arg (&(CUM), (MODE), (TYPE)))
 
-/* On the m68k all args are always pushed.  */
-#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) 0
 #define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0
 
 #define FUNCTION_PROFILER(FILE, LABELNO)  \
@@ -1255,6 +1302,8 @@
 
 /* Variables in m68k.c */
 extern const char *m68k_library_id_string;
+extern const char *m68k_regparm_string;
+extern int m68k_regparm;
 extern int m68k_last_compare_had_fp_operands;
 
 
