| /* Subroutines for insn-output.c for NetWare. |
| Contributed by Jan Beulich (jbeulich@novell.com) |
| Copyright (C) 2004 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC 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. |
| |
| GCC 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 GCC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "output.h" |
| #include "tree.h" |
| #include "flags.h" |
| #include "tm_p.h" |
| #include "toplev.h" |
| #include "ggc.h" |
| |
| |
| /* Return string which is the former assembler name modified with an |
| underscore prefix and a suffix consisting of an atsign (@) followed |
| by the number of bytes of arguments */ |
| |
| static tree |
| gen_stdcall_or_fastcall_decoration (tree decl, char prefix) |
| { |
| unsigned total = 0; |
| /* ??? This probably should use XSTR (XEXP (DECL_RTL (decl), 0), 0) instead |
| of DECL_ASSEMBLER_NAME. */ |
| const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| char *newsym; |
| tree formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl)); |
| |
| if (formal_type != NULL_TREE) |
| { |
| /* These attributes are ignored for variadic functions in |
| i386.c:ix86_return_pops_args. For compatibility with MS |
| compiler do not add @0 suffix here. */ |
| if (TREE_VALUE (tree_last (formal_type)) != void_type_node) |
| return NULL_TREE; |
| |
| /* Quit if we hit an incomplete type. Error is reported |
| by convert_arguments in c-typeck.c or cp/typeck.c. */ |
| while (TREE_VALUE (formal_type) != void_type_node |
| && COMPLETE_TYPE_P (TREE_VALUE (formal_type))) |
| { |
| unsigned parm_size |
| = TREE_INT_CST_LOW (TYPE_SIZE (TREE_VALUE (formal_type))); |
| |
| /* Must round up to include padding. This is done the same |
| way as in store_one_arg. */ |
| parm_size = ((parm_size + PARM_BOUNDARY - 1) |
| / PARM_BOUNDARY * PARM_BOUNDARY); |
| total += parm_size; |
| formal_type = TREE_CHAIN (formal_type); |
| } |
| } |
| |
| newsym = alloca (1 + strlen (asmname) + 1 + 10 + 1); |
| return get_identifier_with_length (newsym, |
| sprintf (newsym, |
| "%c%s@%u", |
| prefix, |
| asmname, |
| total / BITS_PER_UNIT)); |
| } |
| |
| /* Return string which is the former assembler name modified with an |
| _n@ prefix where n represents the number of arguments passed in |
| registers */ |
| |
| static tree |
| gen_regparm_prefix (tree decl, unsigned nregs) |
| { |
| unsigned total = 0; |
| /* ??? This probably should use XSTR (XEXP (DECL_RTL (decl), 0), 0) instead |
| of DECL_ASSEMBLER_NAME. */ |
| const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| char *newsym; |
| tree formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl)); |
| |
| if (formal_type != NULL_TREE) |
| { |
| /* This attribute is ignored for variadic functions. */ |
| if (TREE_VALUE (tree_last (formal_type)) != void_type_node) |
| return NULL_TREE; |
| |
| /* Quit if we hit an incomplete type. Error is reported |
| by convert_arguments in c-typeck.c or cp/typeck.c. */ |
| while (TREE_VALUE (formal_type) != void_type_node |
| && COMPLETE_TYPE_P (TREE_VALUE (formal_type))) |
| { |
| unsigned parm_size |
| = TREE_INT_CST_LOW (TYPE_SIZE (TREE_VALUE (formal_type))); |
| |
| /* Must round up to include padding. This is done the same |
| way as in store_one_arg. */ |
| parm_size = ((parm_size + PARM_BOUNDARY - 1) |
| / PARM_BOUNDARY * PARM_BOUNDARY); |
| total += parm_size; |
| formal_type = TREE_CHAIN (formal_type); |
| } |
| } |
| |
| if (nregs > total / BITS_PER_WORD) |
| nregs = total / BITS_PER_WORD; |
| if (nregs > 9) abort(); |
| newsym = alloca (3 + strlen (asmname) + 1); |
| return get_identifier_with_length (newsym, |
| sprintf (newsym, |
| "_%u@%s", |
| nregs, |
| asmname)); |
| } |
| |
| void |
| i386_nlm_encode_section_info (tree decl, rtx rtl, int first) |
| { |
| default_encode_section_info (decl, rtl, first); |
| |
| if (first |
| && TREE_CODE (decl) == FUNCTION_DECL |
| && *IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)) != '*' |
| && !strchr (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), '@')) |
| { |
| tree type_attributes = TYPE_ATTRIBUTES (TREE_TYPE (decl)); |
| tree newid; |
| |
| if (lookup_attribute ("stdcall", type_attributes)) |
| newid = gen_stdcall_or_fastcall_decoration (decl, '_'); |
| else if (lookup_attribute ("fastcall", type_attributes)) |
| newid = gen_stdcall_or_fastcall_decoration (decl, FASTCALL_PREFIX); |
| else if ((newid = lookup_attribute ("regparm", type_attributes)) != NULL_TREE) |
| newid = gen_regparm_prefix (decl, |
| TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (newid)))); |
| if (newid != NULL_TREE) |
| { |
| rtx rtlname = XEXP (rtl, 0); |
| |
| if (GET_CODE (rtlname) == MEM) |
| rtlname = XEXP (rtlname, 0); |
| XSTR (rtlname, 0) = IDENTIFIER_POINTER (newid); |
| /* These attributes must be present on first declaration, |
| change_decl_assembler_name will warn if they are added |
| later and the decl has been referenced, but duplicate_decls |
| should catch the mismatch before this is called. */ |
| change_decl_assembler_name (decl, newid); |
| } |
| } |
| } |
| |
| /* Strip the stdcall/fastcall/regparm pre-/suffix. */ |
| |
| const char * |
| i386_nlm_strip_name_encoding (const char *str) |
| { |
| const char *name = default_strip_name_encoding (str); |
| |
| if (*str != '*' && (*name == '_' || *name == '@')) |
| { |
| const char *p = strchr (name + 1, '@'); |
| |
| if (p) |
| { |
| ++name; |
| if (ISDIGIT (p[1])) |
| name = ggc_alloc_string (name, p - name); |
| else if (!ISDIGIT (*name) || ++name != p) |
| abort(); |
| } |
| } |
| return name; |
| } |