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 <dannysmith@users.sourceforge.net> Co-Authored-By: Eric Kohl <ekohl@rz-online.de> From-SVN: r60337
This commit is contained in:
parent
61138bb628
commit
e91f04de4b
@ -1,3 +1,39 @@
|
||||
2002-12-19 Casper S. Hornstrup <chorns@users.sourceforge.net>
|
||||
Danny Smith <dannysmith@users.sourceforge.net>
|
||||
Eric Kohl <ekohl@rz-online.de>
|
||||
|
||||
* 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 <neroden@gcc.gnu.org>
|
||||
|
||||
* configure.in: FORBUILD when build!=host changed from
|
||||
|
@ -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
|
||||
|
@ -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 *));
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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 (); \
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,7 @@
|
||||
2002-12-19 Casper S. Hornstrup <chorns@users.sourceforge.net>
|
||||
|
||||
* gcc.dg/i386-fastcall-1.c: New.
|
||||
|
||||
2002-12-19 Eric Botcazou <ebotcazou@libertysurf.fr>
|
||||
|
||||
* gcc.c-torture/execute/20021219-1.c: New test.
|
||||
|
17
gcc/testsuite/gcc.dg/i386-fastcall-1.c
Normal file
17
gcc/testsuite/gcc.dg/i386-fastcall-1.c
Normal file
@ -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) { }
|
Loading…
x
Reference in New Issue
Block a user