From e91f04de4bb14746e48091a26234c87287e73163 Mon Sep 17 00:00:00 2001 From: "Casper S. Hornstrup" Date: Thu, 19 Dec 2002 22:00:33 +0000 Subject: [PATCH] i386.c (ix86_handle_cdecl_attribute): Check for attributes incompatible with fastcall attribute. * config/i386/i386.c (ix86_handle_cdecl_attribute): Check for attributes incompatible with fastcall attribute. (ix86_handle_regparm_attribute): Likewise. * config/i386/i386.c (ix86_comp_type_attributes): Check for mismatched fastcall types. * config/i386/cygwin.h (TARGET_OS_CPP_BUILTINS): Add fastcall attributes. (ASM_OUTPUT_LABELREF): Define as i386_pe_output_labelref. * config/i386/i386-protos.h (i386_pe_output_labelref): Declare. * config/i386/winnt.c (i386_pe_mark_dllimport). Add __imp_ prefix in i386_pe_output_labelref rather than here. (gen_fastcall_suffix): New function. Decorates a label name with the fastcall prefix (@) and the stdcall suffix. (i386_pe_encode_section_info): Call gen_fastcall_suffix() if a symbol has a fastcall attribute. (i386_pe_output_labelref): New function. Outputs a label reference. * config/i386/i386.c (ix86_attribute_table): Accept 'fastcall' as a valid attribute. (ix86_return_pops_args): Fastcall functions pop the stack. (init_cumulative_args): Reserve registers ECX and EDX if function has fastcall attribute. (function_arg): Use registers ECX and EDX if function has fastcall attribute. * config/i386/i386.h (CUMULATIVE_ARGS): Add fastcall attribute flag. (DLL_IMPORT_EXPORT_PREFIX): Redefine as '#'. (FASTCALL_PREFIX): Define as '@'. * config/i386/mingw32.h (TARGET_OS_CPP_BUILTINS): Add fastcall attributes. * doc/extend.texi: Add documentation of fastcall attribute. * testsuite/gcc.dg/i386-fastcall-1.c: New. Co-Authored-By: Danny Smith Co-Authored-By: Eric Kohl From-SVN: r60337 --- gcc/ChangeLog | 36 ++++++++++ gcc/config/i386/cygwin.h | 6 +- gcc/config/i386/i386-protos.h | 1 + gcc/config/i386/i386.c | 74 ++++++++++++++++++-- gcc/config/i386/i386.h | 5 +- gcc/config/i386/mingw32.h | 2 + gcc/config/i386/winnt.c | 97 ++++++++++++++++++++++++-- gcc/doc/extend.texi | 8 +++ gcc/testsuite/ChangeLog | 4 ++ gcc/testsuite/gcc.dg/i386-fastcall-1.c | 17 +++++ 10 files changed, 234 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/i386-fastcall-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 07810fd944d..7ca33750c02 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,39 @@ +2002-12-19 Casper S. Hornstrup + Danny Smith + Eric Kohl + + * config/i386/i386.c (ix86_handle_cdecl_attribute): Check for + attributes incompatible with fastcall attribute. + (ix86_handle_regparm_attribute): Likewise. + + * config/i386/i386.c (ix86_comp_type_attributes): Check for mismatched + fastcall types. + + * config/i386/cygwin.h (TARGET_OS_CPP_BUILTINS): Add fastcall + attributes. + (ASM_OUTPUT_LABELREF): Define as i386_pe_output_labelref. + * config/i386/i386-protos.h (i386_pe_output_labelref): Declare. + * config/i386/winnt.c (i386_pe_mark_dllimport). Add __imp_ prefix in + i386_pe_output_labelref rather than here. + (gen_fastcall_suffix): New function. Decorates a label name with the + fastcall prefix (@) and the stdcall suffix. + (i386_pe_encode_section_info): Call gen_fastcall_suffix() if a symbol + has a fastcall attribute. + (i386_pe_output_labelref): New function. Outputs a label reference. + * config/i386/i386.c (ix86_attribute_table): Accept 'fastcall' as a + valid attribute. + (ix86_return_pops_args): Fastcall functions pop the stack. + (init_cumulative_args): Reserve registers ECX and EDX if function has + fastcall attribute. + (function_arg): Use registers ECX and EDX if function has fastcall + attribute. + * config/i386/i386.h (CUMULATIVE_ARGS): Add fastcall attribute flag. + (DLL_IMPORT_EXPORT_PREFIX): Redefine as '#'. + (FASTCALL_PREFIX): Define as '@'. + * config/i386/mingw32.h (TARGET_OS_CPP_BUILTINS): Add fastcall + attributes. + * doc/extend.texi: Add documentation of fastcall attribute. + 2002-12-19 Nathanael Nerode * configure.in: FORBUILD when build!=host changed from diff --git a/gcc/config/i386/cygwin.h b/gcc/config/i386/cygwin.h index f630d1f8450..76978980c28 100644 --- a/gcc/config/i386/cygwin.h +++ b/gcc/config/i386/cygwin.h @@ -61,11 +61,13 @@ Boston, MA 02111-1307, USA. */ builtin_define ("_X86_=1"); \ builtin_assert ("system=winnt"); \ builtin_define ("__stdcall=__attribute__((__stdcall__))"); \ + builtin_define ("__fastcall=__attribute__((__fastcall__))"); \ builtin_define ("__cdecl=__attribute__((__cdecl__))"); \ builtin_define ("__declspec(x)=__attribute__((x))"); \ if (!flag_iso) \ { \ builtin_define ("_stdcall=__attribute__((__stdcall__))"); \ + builtin_define ("_fastcall=__attribute__((__fastcall__))"); \ builtin_define ("_cdecl=__attribute__((__cdecl__))"); \ } \ MAYBE_UWIN_CPP_BUILTINS (); \ @@ -271,9 +273,7 @@ do { \ /* Output a reference to a label. */ #undef ASM_OUTPUT_LABELREF -#define ASM_OUTPUT_LABELREF(STREAM, NAME) \ - fprintf (STREAM, "%s%s", USER_LABEL_PREFIX, \ - i386_pe_strip_name_encoding (NAME)) \ +#define ASM_OUTPUT_LABELREF i386_pe_output_labelref /* Output a common block. */ #undef ASM_OUTPUT_COMMON diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index e403950ab32..a06a160bfbc 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -233,3 +233,4 @@ extern void i386_pe_asm_file_end PARAMS ((FILE *)); extern void i386_pe_encode_section_info PARAMS ((tree, int)); extern const char *i386_pe_strip_name_encoding PARAMS ((const char *)); extern const char *i386_pe_strip_name_encoding_full PARAMS ((const char *)); +extern void i386_pe_output_labelref PARAMS ((FILE *, const char *)); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index b12a7f2323b..4ebf16b0875 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -1443,6 +1443,9 @@ const struct attribute_spec ix86_attribute_table[] = /* Stdcall attribute says callee is responsible for popping arguments if they are not variable. */ { "stdcall", 0, 0, false, true, true, ix86_handle_cdecl_attribute }, + /* Fastcall attribute says callee is responsible for popping arguments + if they are not variable. */ + { "fastcall", 0, 0, false, true, true, ix86_handle_cdecl_attribute }, /* Cdecl attribute says the callee is a normal C declaration */ { "cdecl", 0, 0, false, true, true, ix86_handle_cdecl_attribute }, /* Regparm attribute specifies how many integer arguments are to be @@ -1510,7 +1513,7 @@ ix86_function_ok_for_sibcall (decl, exp) return true; } -/* Handle a "cdecl" or "stdcall" attribute; +/* Handle a "cdecl", "stdcall", or "fastcall" attribute; arguments as in struct attribute_spec.handler. */ static tree ix86_handle_cdecl_attribute (node, name, args, flags, no_add_attrs) @@ -1529,6 +1532,27 @@ ix86_handle_cdecl_attribute (node, name, args, flags, no_add_attrs) IDENTIFIER_POINTER (name)); *no_add_attrs = true; } + else + { + if (is_attribute_p ("fastcall", name)) + { + if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node))) + { + error ("fastcall and stdcall attributes are not compatible"); + } + else if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (*node))) + { + error ("fastcall and regparm attributes are not compatible"); + } + } + else if (is_attribute_p ("stdcall", name)) + { + if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) + { + error ("fastcall and stdcall attributes are not compatible"); + } + } + } if (TARGET_64BIT) { @@ -1575,6 +1599,11 @@ ix86_handle_regparm_attribute (node, name, args, flags, no_add_attrs) IDENTIFIER_POINTER (name), REGPARM_MAX); *no_add_attrs = true; } + + if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node))) + { + error ("fastcall and regparm attributes are not compatible"); + } } return NULL_TREE; @@ -1595,6 +1624,11 @@ ix86_comp_type_attributes (type1, type2) if (TREE_CODE (type1) != FUNCTION_TYPE) return 1; + /* Check for mismatched fastcall types */ + if (!lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type1)) + != !lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type2))) + return 0; + /* Check for mismatched return types (cdecl vs stdcall). */ if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1)) != !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2))) @@ -1645,8 +1679,9 @@ ix86_return_pops_args (fundecl, funtype, size) /* Cdecl functions override -mrtd, and never pop the stack. */ if (! lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) { - /* Stdcall functions will pop the stack if not variable args. */ - if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype))) + /* Stdcall and fastcall functions will pop the stack if not variable args. */ + if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)) + || lookup_attribute ("fastcall", TYPE_ATTRIBUTES (funtype))) rtd = 1; if (rtd @@ -1732,6 +1767,17 @@ init_cumulative_args (cum, fntype, libname) } cum->maybe_vaarg = false; + /* Use ecx and edx registers if function has fastcall attribute */ + if (fntype && !TARGET_64BIT) + { + if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype))) + { + cum->nregs = 2; + cum->fastcall = 1; + } + } + + /* Determine if this function has variable arguments. This is indicated by the last argument being 'void_type_mode' if there are no variable arguments. If there are variable arguments, then @@ -1746,7 +1792,10 @@ init_cumulative_args (cum, fntype, libname) if (next_param == 0 && TREE_VALUE (param) != void_type_node) { if (!TARGET_64BIT) - cum->nregs = 0; + { + cum->nregs = 0; + cum->fastcall = 0; + } cum->maybe_vaarg = true; } } @@ -2396,7 +2445,22 @@ function_arg (cum, mode, type, named) case HImode: case QImode: if (words <= cum->nregs) - ret = gen_rtx_REG (mode, cum->regno); + { + int regno = cum->regno; + + /* Fastcall allocates the first two DWORD (SImode) or + smaller arguments to ECX and EDX. */ + if (cum->fastcall) + { + if (mode == BLKmode || mode == DImode) + break; + + /* ECX not EAX is the first allocated register. */ + if (regno == 0) + regno = 2; + } + ret = gen_rtx_REG (mode, regno); + } break; case TImode: if (cum->sse_nregs) diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index c1f40dc209c..964237d10bb 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -1710,6 +1710,7 @@ typedef struct ix86_args { int words; /* # words passed so far */ int nregs; /* # registers available for passing */ int regno; /* next available register number */ + int fastcall; /* fastcall calling convention is used */ int sse_words; /* # sse words passed so far */ int sse_nregs; /* # sse registers available for passing */ int sse_regno; /* next available sse register number */ @@ -3479,7 +3480,9 @@ enum fp_cw_mode {FP_CW_STORED, FP_CW_UNINITIALIZED, FP_CW_ANY}; #define MACHINE_DEPENDENT_REORG(X) x86_machine_dependent_reorg(X) -#define DLL_IMPORT_EXPORT_PREFIX '@' +#define DLL_IMPORT_EXPORT_PREFIX '#' + +#define FASTCALL_PREFIX '@' /* Local variables: diff --git a/gcc/config/i386/mingw32.h b/gcc/config/i386/mingw32.h index e7c5e8b6bcc..78dd19bdd81 100644 --- a/gcc/config/i386/mingw32.h +++ b/gcc/config/i386/mingw32.h @@ -50,11 +50,13 @@ Boston, MA 02111-1307, USA. */ builtin_define_std ("WINNT"); \ builtin_define ("_X86_=1"); \ builtin_define ("__stdcall=__attribute__((__stdcall__))"); \ + builtin_define ("__fastcall=__attribute__((__fastcall__))"); \ builtin_define ("__cdecl=__attribute__((__cdecl__))"); \ builtin_define ("__declspec(x)=__attribute__((x))"); \ if (!flag_iso) \ { \ builtin_define ("_stdcall=__attribute__((__stdcall__))"); \ + builtin_define ("_fastcall=__attribute__((__fastcall__))"); \ builtin_define ("_cdecl=__attribute__((__cdecl__))"); \ } \ EXTRA_OS_CPP_BUILTINS (); \ diff --git a/gcc/config/i386/winnt.c b/gcc/config/i386/winnt.c index bc2527aaca5..89dd007693c 100644 --- a/gcc/config/i386/winnt.c +++ b/gcc/config/i386/winnt.c @@ -48,6 +48,7 @@ Boston, MA 02111-1307, USA. */ static tree associated_type PARAMS ((tree)); const char * gen_stdcall_suffix PARAMS ((tree)); +const char * gen_fastcall_suffix PARAMS ((tree)); int i386_pe_dllexport_p PARAMS ((tree)); int i386_pe_dllimport_p PARAMS ((tree)); void i386_pe_mark_dllexport PARAMS ((tree)); @@ -315,8 +316,8 @@ i386_pe_mark_dllimport (decl) return; } - newname = alloca (strlen (oldname) + 11); - sprintf (newname, "%ci._imp__%s", DLL_IMPORT_EXPORT_PREFIX, oldname); + newname = alloca (strlen (oldname) + 4); + sprintf (newname, "%ci.%s", DLL_IMPORT_EXPORT_PREFIX, oldname); /* We pass newname through get_identifier to ensure it has a unique address. RTL processing can sometimes peek inside the symbol ref @@ -333,6 +334,43 @@ i386_pe_mark_dllimport (decl) DECL_NON_ADDR_CONST_P (decl) = 1; } +/* Return string which is the former assembler name modified with a + prefix consisting of FASTCALL_PREFIX and a suffix consisting of an + atsign (@) followed by the number of bytes of arguments. */ + +const char * +gen_fastcall_suffix (decl) + tree decl; +{ + int total = 0; + + const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + char *newsym; + + if (TYPE_ARG_TYPES (TREE_TYPE (decl))) + if (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (decl)))) + == void_type_node) + { + tree formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl)); + + while (TREE_VALUE (formal_type) != void_type_node) + { + int 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 = xmalloc (strlen (asmname) + 11); + sprintf (newsym, "%c%s@%d", FASTCALL_PREFIX, asmname, total/BITS_PER_UNIT); + return IDENTIFIER_POINTER (get_identifier (newsym)); +} + /* Return string which is the former assembler name modified with a suffix consisting of an atsign (@) followed by the number of bytes of arguments */ @@ -389,10 +427,16 @@ i386_pe_encode_section_info (decl, first) } if (TREE_CODE (decl) == FUNCTION_DECL) - if (lookup_attribute ("stdcall", - TYPE_ATTRIBUTES (TREE_TYPE (decl)))) - XEXP (DECL_RTL (decl), 0) = - gen_rtx (SYMBOL_REF, Pmode, gen_stdcall_suffix (decl)); + { + if (lookup_attribute ("stdcall", + TYPE_ATTRIBUTES (TREE_TYPE (decl)))) + XEXP (DECL_RTL (decl), 0) = + gen_rtx (SYMBOL_REF, Pmode, gen_stdcall_suffix (decl)); + else if (lookup_attribute ("fastcall", + TYPE_ATTRIBUTES (TREE_TYPE (decl)))) + XEXP (DECL_RTL (decl), 0) = + gen_rtx (SYMBOL_REF, Pmode, gen_fastcall_suffix (decl)); + } /* Mark the decl so we can tell from the rtl whether the object is dllexport'd or dllimport'd. */ @@ -426,7 +470,8 @@ i386_pe_encode_section_info (decl, first) } } -/* Strip only the leading encoding, leaving the stdcall suffix. */ +/* Strip only the leading encoding, leaving the stdcall suffix and fastcall + prefix if it exists. */ const char * i386_pe_strip_name_encoding (str) @@ -455,6 +500,44 @@ i386_pe_strip_name_encoding_full (str) return name; } +/* Output a reference to a label. Fastcall symbols are prefixed with @, + whereas symbols for functions using other calling conventions don't + have a prefix (unless they are marked dllimport or dllexport). */ + +void i386_pe_output_labelref (stream, name) + FILE *stream; + const char *name; +{ + char prefix[4]; + + sprintf (prefix, "%ci.", DLL_IMPORT_EXPORT_PREFIX); + if (strncmp (name, prefix, strlen (prefix)) == 0) + { + if (name[3] == FASTCALL_PREFIX) + { + fprintf (stream, "__imp_%s", + i386_pe_strip_name_encoding (name)); + } + else + { + fprintf (stream, "__imp__%s", + i386_pe_strip_name_encoding (name)); + } + } + else if ((name[0] == FASTCALL_PREFIX) + || ((name[0] == DLL_IMPORT_EXPORT_PREFIX) + && (name[3] == FASTCALL_PREFIX))) + { + fprintf (stream, "%s", + i386_pe_strip_name_encoding (name)); + } + else + { + fprintf (stream, "%s%s", USER_LABEL_PREFIX, + i386_pe_strip_name_encoding (name)); + } +} + void i386_pe_unique_section (decl, reloc) tree decl; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index b904c7eb1a1..e18ad13bfa8 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2370,6 +2370,14 @@ pass arguments, unless it takes a variable number of arguments. The PowerPC compiler for Windows NT currently ignores the @code{stdcall} attribute. +@item fastcall +@cindex functions that pop the argument stack on the 386 +On the Intel 386, the @code{fastcall} attribute causes the compiler to +pass the first two arguments in the registers ECX and EDX. Subsequent +arguments are passed on the stack. The called function will pop the +arguments off the stack. If the number of arguments is variable all +arguments are pushed on the stack. + @item cdecl @cindex functions that do pop the argument stack on the 386 @opindex mrtd diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0e978c29b7a..2db4a8e43ae 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2002-12-19 Casper S. Hornstrup + + * gcc.dg/i386-fastcall-1.c: New. + 2002-12-19 Eric Botcazou * gcc.c-torture/execute/20021219-1.c: New test. diff --git a/gcc/testsuite/gcc.dg/i386-fastcall-1.c b/gcc/testsuite/gcc.dg/i386-fastcall-1.c new file mode 100644 index 00000000000..c286ed3aec7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/i386-fastcall-1.c @@ -0,0 +1,17 @@ +/* { dg-do compile { target i386-pc-mingw32* i386-pc-cygwin* } } */ + +void +__attribute__ ((fastcall)) +f1() { } + +void +_fastcall +f2() { } + +void +__fastcall +f3() { } + +int +__attribute__ ((fastcall)) +f4(int x, int y, int z) { } -- 2.30.2