/* emx.c: Functions for emx as target system.

/* Written by Eberhard Mattes, based on i386.c.

This file is part of GNU CC.

GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include <i386/i386.c>


/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific
   attribute for TYPE.  The attributes in ATTRIBUTES have previously been
   assigned to TYPE.  */

int
emx_valid_type_attribute_p (type, attributes, identifier, args)
     tree type;
     tree attributes;
     tree identifier;
     tree args;
{
  if (TREE_CODE (type) != FUNCTION_TYPE
      && TREE_CODE (type) != FIELD_DECL
      && TREE_CODE (type) != TYPE_DECL)
    return 0;

  if (is_attribute_p ("stdcall", identifier))
    return (args == NULL_TREE);

  if (is_attribute_p ("cdecl", identifier))
    return (args == NULL_TREE);

  if (is_attribute_p ("emxcall", identifier))
    return (args == NULL_TREE);

  if (is_attribute_p ("system", identifier))
    return (args == NULL_TREE);

  /* Regparm attribute specifies how many integer arguments are to be
     passed in registers.  */
  if (is_attribute_p ("regparm", identifier))
    {
      tree cst;

      if (!args || TREE_CODE (args) != TREE_LIST
	  || TREE_CHAIN (args) != NULL_TREE
	  || TREE_VALUE (args) == NULL_TREE)
	return 0;

      cst = TREE_VALUE (args);
      if (TREE_CODE (cst) != INTEGER_CST)
	return 0;

      if (TREE_INT_CST_HIGH (cst) != 0
	  || TREE_INT_CST_LOW (cst) < 0
	  || TREE_INT_CST_LOW (cst) > REGPARM_MAX)
	return 0;

      return 1;
    }

  return 0;
}


/* Value is the number of bytes of arguments automatically
   popped when returning from a subroutine call.
   FUNDECL is the declaration node of the function (as a tree),
   FUNTYPE is the data type of the function (as a tree),
   or for a library call it is an identifier node for the subroutine name.
   SIZE is the number of bytes of arguments passed on the stack.  */

int
emx_return_pops_args (fundecl, funtype, size)
     tree fundecl;
     tree funtype;
     int size;
{
  if (TREE_CODE (funtype) == IDENTIFIER_NODE)
    {
      /* Helper library functions don't pop the stack.  */

      return 0;
    }

  /* Override -mrtd if a function attribute is given.  */

  if (fundecl && TREE_CODE_CLASS (TREE_CODE (fundecl)) == 'd')
    {
      if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype)))
        return 0;

      if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)))
        {
          if (TYPE_ARG_TYPES (funtype) == NULL_TREE
              || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
            return size;
        }

      if (lookup_attribute ("system", TYPE_ATTRIBUTES (funtype)))
        {
          /* `system' functions should never pop the stack.  However,
             as RETURN_IN_MEMORY does currently not depend on the
             function attributes, this can't be done.  */

#if 1
          if (aggregate_value_p (TREE_TYPE (funtype)))
            return GET_MODE_SIZE (Pmode);
#endif
          return 0;
        }

      if (lookup_attribute ("emxcall", TYPE_ATTRIBUTES (funtype)))
        {
          /* 'emxcall' functions pop the hidden pointer argument.  */

          if (aggregate_value_p (TREE_TYPE (funtype)))
            return GET_MODE_SIZE (Pmode);
          return 0;
        }

      if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (funtype)))
        {
          /* `optlink' functions taking a fixed number of arguments
             pop the stack.  */

          if (TYPE_ARG_TYPES (funtype) == NULL_TREE
              || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
            return size;

          /* `optlink' functions taking a variable number of arguments
             should also pop the stack, but that's not implemented.
             We treat `optlink' functions taking a variable number of
             arguments like `system' functions.  */

          return 0;
        }
    }

  if (TARGET_RTD)
    {
      /* If -mrtd is given, functions taking a fixed number of
         arguments pop the stack.  */

      if (TYPE_ARG_TYPES (funtype) == NULL_TREE
	  || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
	return size;
    }

  /* By default, functions returning an aggregate pop the hidden
     pointer argument.  */

  if (aggregate_value_p (TREE_TYPE (funtype)))
    return GET_MODE_SIZE (Pmode);

  return 0;
}
