1/* Subroutines for insn-output.c for NetWare.
2   Contributed by Jan Beulich (jbeulich@novell.com)
3   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING.  If not, write to
19the Free Software Foundation, 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA.  */
21
22#include "config.h"
23#include "system.h"
24#include "coretypes.h"
25#include "tm.h"
26#include "rtl.h"
27#include "regs.h"
28#include "hard-reg-set.h"
29#include "output.h"
30#include "tree.h"
31#include "flags.h"
32#include "tm_p.h"
33#include "toplev.h"
34#include "ggc.h"
35
36
37/* Return string which is the former assembler name modified with an
38   underscore prefix and a suffix consisting of an atsign (@) followed
39   by the number of bytes of arguments */
40
41static tree
42gen_stdcall_or_fastcall_decoration (tree decl, char prefix)
43{
44  unsigned total = 0;
45  /* ??? This probably should use XSTR (XEXP (DECL_RTL (decl), 0), 0) instead
46     of DECL_ASSEMBLER_NAME.  */
47  const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
48  char *newsym;
49  tree formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl));
50
51  if (formal_type != NULL_TREE)
52    {
53      /* These attributes are ignored for variadic functions in
54	 i386.c:ix86_return_pops_args. For compatibility with MS
55	 compiler do not add @0 suffix here.  */
56      if (TREE_VALUE (tree_last (formal_type)) != void_type_node)
57	return NULL_TREE;
58
59      /* Quit if we hit an incomplete type.  Error is reported
60	 by convert_arguments in c-typeck.c or cp/typeck.c.  */
61      while (TREE_VALUE (formal_type) != void_type_node
62	     && COMPLETE_TYPE_P (TREE_VALUE (formal_type)))
63	{
64	  unsigned parm_size
65	    = TREE_INT_CST_LOW (TYPE_SIZE (TREE_VALUE (formal_type)));
66
67	  /* Must round up to include padding.  This is done the same
68	     way as in store_one_arg.  */
69	  parm_size = ((parm_size + PARM_BOUNDARY - 1)
70		       / PARM_BOUNDARY * PARM_BOUNDARY);
71	  total += parm_size;
72	  formal_type = TREE_CHAIN (formal_type);
73	}
74    }
75
76  newsym = alloca (1 + strlen (asmname) + 1 + 10 + 1);
77  return get_identifier_with_length (newsym,
78				     sprintf (newsym,
79					      "%c%s@%u",
80					      prefix,
81					      asmname,
82					      total / BITS_PER_UNIT));
83}
84
85/* Return string which is the former assembler name modified with an
86   _n@ prefix where n represents the number of arguments passed in
87   registers */
88
89static tree
90gen_regparm_prefix (tree decl, unsigned nregs)
91{
92  unsigned total = 0;
93  /* ??? This probably should use XSTR (XEXP (DECL_RTL (decl), 0), 0) instead
94     of DECL_ASSEMBLER_NAME.  */
95  const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
96  char *newsym;
97  tree formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl));
98
99  if (formal_type != NULL_TREE)
100    {
101      /* This attribute is ignored for variadic functions.  */
102      if (TREE_VALUE (tree_last (formal_type)) != void_type_node)
103	return NULL_TREE;
104
105      /* Quit if we hit an incomplete type.  Error is reported
106	 by convert_arguments in c-typeck.c or cp/typeck.c.  */
107      while (TREE_VALUE (formal_type) != void_type_node
108	     && COMPLETE_TYPE_P (TREE_VALUE (formal_type)))
109	{
110	  unsigned parm_size
111	    = TREE_INT_CST_LOW (TYPE_SIZE (TREE_VALUE (formal_type)));
112
113	  /* Must round up to include padding.  This is done the same
114	     way as in store_one_arg.  */
115	  parm_size = ((parm_size + PARM_BOUNDARY - 1)
116		       / PARM_BOUNDARY * PARM_BOUNDARY);
117	  total += parm_size;
118	  formal_type = TREE_CHAIN (formal_type);
119	}
120    }
121
122  if (nregs > total / BITS_PER_WORD)
123    nregs = total / BITS_PER_WORD;
124  gcc_assert (nregs <= 9);
125  newsym = alloca (3 + strlen (asmname) + 1);
126  return get_identifier_with_length (newsym,
127				     sprintf (newsym,
128					      "_%u@%s",
129					      nregs,
130					      asmname));
131}
132
133void
134i386_nlm_encode_section_info (tree decl, rtx rtl, int first)
135{
136  default_encode_section_info (decl, rtl, first);
137
138  if (first
139      && TREE_CODE (decl) == FUNCTION_DECL
140      && *IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)) != '*'
141      && !strchr (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), '@'))
142    {
143      tree type_attributes = TYPE_ATTRIBUTES (TREE_TYPE (decl));
144      tree newid;
145
146      if (lookup_attribute ("stdcall", type_attributes))
147	newid = gen_stdcall_or_fastcall_decoration (decl, '_');
148      else if (lookup_attribute ("fastcall", type_attributes))
149	newid = gen_stdcall_or_fastcall_decoration (decl, FASTCALL_PREFIX);
150      else if ((newid = lookup_attribute ("regparm", type_attributes)) != NULL_TREE)
151	newid = gen_regparm_prefix (decl,
152		      TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (newid))));
153      if (newid != NULL_TREE)
154	{
155	  rtx rtlname = XEXP (rtl, 0);
156
157	  if (GET_CODE (rtlname) == MEM)
158	    rtlname = XEXP (rtlname, 0);
159	  XSTR (rtlname, 0) = IDENTIFIER_POINTER (newid);
160	  /* These attributes must be present on first declaration,
161	     change_decl_assembler_name will warn if they are added
162	     later and the decl has been referenced, but duplicate_decls
163	     should catch the mismatch before this is called.  */
164	  change_decl_assembler_name (decl, newid);
165	}
166    }
167}
168
169/* Strip the stdcall/fastcall/regparm pre-/suffix.  */
170
171const char *
172i386_nlm_strip_name_encoding (const char *str)
173{
174  const char *name = default_strip_name_encoding (str);
175
176  if (*str != '*' && (*name == '_' || *name == '@'))
177    {
178      const char *p = strchr (name + 1, '@');
179
180      if (p)
181	{
182	  ++name;
183	  if (ISDIGIT (p[1]))
184	    name = ggc_alloc_string (name, p - name);
185	  else
186	    {
187	      gcc_assert (ISDIGIT (*name));
188	      name++;
189	      gcc_assert (name == p);
190	    }
191	}
192    }
193  return name;
194}
195