Hello, This patch cleans up a bit the calling convention checking in i386 architecture. By this I could fix some pretty old bugs about the -mrtd option. See as reference http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11772 and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9601
For none-prototyped function calls nowadays the linker is able to do the stdcall fixup successful (avoiding warning via -Wl,--enable-stdcall-fixup). So we emit for this case just the undecorated function's symbol name. ChangeLog 2011-04-07 Kai Tietz PR target/9601 PR target/11772 * config/i386/i386-protos.h (ix86_get_callcvt): New prototype. * config/i386/i386.c (ix86_handle_cconv_attribute): Adjust comment. (ix86_is_msabi_thiscall): Removed. (ix86_is_type_thiscall): Likewise. (ix86_get_callcvt): New function. (ix86_comp_type_attributes): Simplify check. (ix86_function_regparm): Use ix86_get_callcvt for calling convention attribute checks. (ix86_return_pops_args): Likewise. (ix86_static_chain): Likewise. (x86_this_parameter): Likewise. (x86_output_mi_thunk): Likewise. * config/i386/i386.h (IX86_CALLCVT_CDECL, IX86_CALLCVT_STDCALL, IX86_CALLCVT_FASTCALL, IX86_CALLCVT_THISCALL, IX86_CALLCVT_REGPARM, IX86_CALLCVT_SSEREGPARM): New macros to represent calling convention by flag-values. (IX86_BASE_CALLCVT): Helper macro. * config/i386/netware.c (i386_nlm_maybe_mangle_decl_assembler_name): Use ix86_get_callcvt for calling convention attribute checks. * config/i386/winnt.c (i386_pe_maybe_mangle_decl_assembler_name): Likewise. (gen_stdcall_or_fastcall_suffix): Handle TARGET_RTD case for none-prototyped function calls. Tested for i686-w64-mingw32, x86_64-w64-mingw32, and x86_64-pc-linux-gnu. Ok for apply? Regards, Kai
Index: gcc/gcc/config/i386/i386-protos.h =================================================================== --- gcc.orig/gcc/config/i386/i386-protos.h 2011-01-13 20:55:30.000000000 +0100 +++ gcc/gcc/config/i386/i386-protos.h 2011-04-07 11:02:16.645985200 +0200 @@ -191,6 +191,8 @@ extern tree ix86_handle_shared_attribute extern tree ix86_handle_selectany_attribute (tree *, tree, tree, int, bool *); extern int x86_field_alignment (tree, int); extern tree ix86_valid_target_attribute_tree (tree); +extern unsigned int ix86_get_callcvt (const_tree); + #endif extern rtx ix86_tls_get_addr (void); Index: gcc/gcc/config/i386/i386.c =================================================================== --- gcc.orig/gcc/config/i386/i386.c 2011-04-06 19:13:30.000000000 +0200 +++ gcc/gcc/config/i386/i386.c 2011-04-07 14:51:22.411975800 +0200 @@ -5312,7 +5312,7 @@ ix86_handle_cconv_attribute (tree *node, return NULL_TREE; } - /* Can combine regparm with all attributes but fastcall. */ + /* Can combine regparm with all attributes but fastcall, and thiscall. */ if (is_attribute_p ("regparm", name)) { tree cst; @@ -5436,38 +5436,50 @@ ix86_handle_cconv_attribute (tree *node, return NULL_TREE; } -/* This function checks if the method-function has default __thiscall - calling-convention for 32-bit msabi. - It returns true if TYPE is of kind METHOD_TYPE, no stdarg function, - and the MS_ABI 32-bit is used. Otherwise it returns false. */ +/* This function determines from TYPE the calling-convention. */ -static bool -ix86_is_msabi_thiscall (const_tree type) +unsigned int +ix86_get_callcvt (const_tree type) { - if (TARGET_64BIT || ix86_function_type_abi (type) != MS_ABI - || TREE_CODE (type) != METHOD_TYPE || stdarg_p (type)) - return false; - /* Check for different calling-conventions. */ - if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (type)) - || lookup_attribute ("stdcall", TYPE_ATTRIBUTES (type)) - || lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type)) - || lookup_attribute ("regparm", TYPE_ATTRIBUTES (type)) - || lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type))) - return false; - return true; -} + unsigned int ret = 0; -/* This function checks if the thiscall attribute is set for the TYPE, - or if it is an method-type with default thiscall convention. - It returns true if function match, otherwise false is returned. */ + if (TARGET_64BIT) + return IX86_CALLCVT_CDECL; -static bool -ix86_is_type_thiscall (const_tree type) -{ - if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type)) - || ix86_is_msabi_thiscall (type)) - return true; - return false; + if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (type))) + ret |= IX86_CALLCVT_CDECL; + else if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (type))) + ret |= IX86_CALLCVT_STDCALL; + else if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type))) + ret |= IX86_CALLCVT_FASTCALL; + else if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type))) + ret |= IX86_CALLCVT_THISCALL; + + /* Regparam isn't allowed for thiscall and fastcall. */ + if ((ret & (IX86_CALLCVT_THISCALL | IX86_CALLCVT_FASTCALL)) == 0) + { + if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (type))) + ret |= IX86_CALLCVT_REGPARM; + if (lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type))) + ret |= IX86_CALLCVT_SSEREGPARM; + } + + if (IX86_BASE_CALLCVT(ret) != 0) + return ret; + + /* Special case regparm/sseregparm, which are either cdecl or stdcall. */ + if ((ret & (IX86_CALLCVT_REGPARM | IX86_CALLCVT_SSEREGPARM)) != 0) + return (ret | ((TARGET_RTD && !stdarg_p (type)) ? IX86_CALLCVT_STDCALL + : IX86_CALLCVT_CDECL)); + + /* We don't have found a default call-convention specifier, + so apply default. */ + if (TARGET_RTD && !stdarg_p (type)) + return IX86_CALLCVT_STDCALL; + else if (TREE_CODE (type) != METHOD_TYPE || stdarg_p (type) + || ix86_function_type_abi (type) != MS_ABI) + return IX86_CALLCVT_CDECL; + return IX86_CALLCVT_THISCALL; } /* Return 0 if the attributes for two types are incompatible, 1 if they @@ -5477,43 +5489,18 @@ ix86_is_type_thiscall (const_tree type) static int ix86_comp_type_attributes (const_tree type1, const_tree type2) { - /* Check for mismatch of non-default calling convention. */ - bool is_thiscall = ix86_is_msabi_thiscall (type1); - const char *const rtdstr = TARGET_RTD ? (is_thiscall ? "thiscall" : "cdecl") : "stdcall"; + unsigned int ccvt1, ccvt2; if (TREE_CODE (type1) != FUNCTION_TYPE && TREE_CODE (type1) != METHOD_TYPE) return 1; - /* Check for mismatched fastcall/regparm types. */ - if ((!lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type1)) - != !lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type2))) - || (ix86_function_regparm (type1, NULL) - != ix86_function_regparm (type2, NULL))) + ccvt1 = ix86_get_callcvt (type1); + ccvt2 = ix86_get_callcvt (type2); + if (ccvt1 != ccvt2) return 0; - - /* Check for mismatched sseregparm types. */ - if (!lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type1)) - != !lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type2))) - return 0; - - /* Check for mismatched thiscall types. */ - if (is_thiscall && !TARGET_RTD) - { - if (!lookup_attribute ("cdecl", TYPE_ATTRIBUTES (type1)) - != !lookup_attribute ("cdecl", TYPE_ATTRIBUTES (type2))) - return 0; - } - else if (!is_thiscall || TARGET_RTD) - { - if (!lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type1)) - != !lookup_attribute ("thiscall", 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))) + if (ix86_function_regparm (type1, NULL) + != ix86_function_regparm (type2, NULL)) return 0; return 1; @@ -5528,23 +5515,26 @@ ix86_function_regparm (const_tree type, { tree attr; int regparm; + unsigned int ccvt; if (TARGET_64BIT) return (ix86_function_type_abi (type) == SYSV_ABI ? X86_64_REGPARM_MAX : X86_64_MS_REGPARM_MAX); - + ccvt = ix86_get_callcvt (type); regparm = ix86_regparm; - attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type)); - if (attr) + + if ((ccvt & IX86_CALLCVT_REGPARM) != 0) { - regparm = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr))); - return regparm; + attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type)); + if (attr) + { + regparm = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr))); + return regparm; + } } - - if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type))) + else if (ccvt == IX86_CALLCVT_FASTCALL) return 2; - - if (ix86_is_type_thiscall (type)) + else if (ccvt == IX86_CALLCVT_THISCALL) return 1; /* Use register calling convention for local functions when possible. */ @@ -5695,28 +5685,27 @@ ix86_keep_aggregate_return_pointer (tree static int ix86_return_pops_args (tree fundecl, tree funtype, int size) { - int rtd; + int rtd = 0; + unsigned int ccvt; /* None of the 64-bit ABIs pop arguments. */ if (TARGET_64BIT) return 0; - rtd = TARGET_RTD && (!fundecl || TREE_CODE (fundecl) != IDENTIFIER_NODE); + ccvt = ix86_get_callcvt (funtype); - /* Cdecl functions override -mrtd, and never pop the stack. */ - if (! lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) + switch (IX86_BASE_CALLCVT (ccvt)) { - /* 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)) - || ix86_is_type_thiscall (funtype)) - rtd = 1; - - if (rtd && ! stdarg_p (funtype)) - return size; + case IX86_CALLCVT_STDCALL: + case IX86_CALLCVT_FASTCALL: + case IX86_CALLCVT_THISCALL: + rtd = 1; + break; } + if (rtd && ! stdarg_p (funtype)) + return size; + /* Lose any fake structure return argument if it is passed on the stack. */ if (aggregate_value_p (TREE_TYPE (funtype), fundecl) && !ix86_keep_aggregate_return_pointer (funtype)) @@ -6048,12 +6037,13 @@ init_cumulative_args (CUMULATIVE_ARGS *c else look for regparm information. */ if (fntype) { - if (ix86_is_type_thiscall (fntype)) + unsigned int ccvt = ix86_get_callcvt (fntype); + if (ccvt == IX86_CALLCVT_THISCALL) { cum->nregs = 1; cum->fastcall = 1; /* Same first register as in fastcall. */ } - else if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype))) + else if (ccvt == IX86_CALLCVT_FASTCALL) { cum->nregs = 2; cum->fastcall = 1; @@ -9839,13 +9829,14 @@ find_drap_reg (void) /* Reuse static chain register if it isn't used for parameter passing. */ - if (ix86_function_regparm (TREE_TYPE (decl), decl) <= 2 - && !lookup_attribute ("fastcall", - TYPE_ATTRIBUTES (TREE_TYPE (decl))) - && !ix86_is_type_thiscall (TREE_TYPE (decl))) - return CX_REG; - else - return DI_REG; + if (ix86_function_regparm (TREE_TYPE (decl), decl) <= 2) + { + unsigned int ccvt = ix86_get_callcvt (TREE_TYPE (decl)); + if (ccvt != IX86_CALLCVT_FASTCALL + && ccvt != IX86_CALLCVT_THISCALL) + return CX_REG; + } + return DI_REG; } } @@ -23282,17 +23273,20 @@ ix86_static_chain (const_tree fndecl, bo else { tree fntype; + unsigned int ccvt; + /* By default in 32-bit mode we use ECX to pass the static chain. */ regno = CX_REG; fntype = TREE_TYPE (fndecl); - if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype))) + ccvt = ix86_get_callcvt (fntype); + if (ccvt == IX86_CALLCVT_FASTCALL) { /* Fastcall functions use ecx/edx for arguments, which leaves us with EAX for the static chain. */ regno = AX_REG; } - else if (ix86_is_type_thiscall (fntype)) + else if (ccvt == IX86_CALLCVT_THISCALL) { /* Thiscall functions use ecx for arguments, which leaves us with EAX for the static chain. */ @@ -29846,10 +29840,11 @@ x86_this_parameter (tree function) if (nregs > 0 && !stdarg_p (type)) { int regno; + unsigned int ccvt = ix86_get_callcvt (type); - if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type))) + if (ccvt == IX86_CALLCVT_FASTCALL) regno = aggr ? DX_REG : CX_REG; - else if (ix86_is_type_thiscall (type)) + else if (ccvt == IX86_CALLCVT_THISCALL) { regno = CX_REG; if (aggr) @@ -29966,9 +29961,9 @@ x86_output_mi_thunk (FILE *file, else { int tmp_regno = CX_REG; - if (lookup_attribute ("fastcall", - TYPE_ATTRIBUTES (TREE_TYPE (function))) - || ix86_is_type_thiscall (TREE_TYPE (function))) + unsigned int ccvt = ix86_get_callcvt (TREE_TYPE (function)); + if (ccvt == IX86_CALLCVT_FASTCALL + || ccvt == IX86_CALLCVT_THISCALL) tmp_regno = AX_REG; tmp = gen_rtx_REG (SImode, tmp_regno); } Index: gcc/gcc/config/i386/i386.h =================================================================== --- gcc.orig/gcc/config/i386/i386.h 2011-04-03 13:59:54.000000000 +0200 +++ gcc/gcc/config/i386/i386.h 2011-04-07 11:02:37.430624500 +0200 @@ -2368,6 +2368,18 @@ extern void debug_dispatch_window (int); ((VALUE) = GET_MODE_BITSIZE (MODE), TARGET_BMI) +/* Flags returned by ix86_get_callcvt (). */ +#define IX86_CALLCVT_CDECL 0x1 +#define IX86_CALLCVT_STDCALL 0x2 +#define IX86_CALLCVT_FASTCALL 0x4 +#define IX86_CALLCVT_THISCALL 0x8 +#define IX86_CALLCVT_REGPARM 0x10 +#define IX86_CALLCVT_SSEREGPARM 0x20 + +#define IX86_BASE_CALLCVT(FLAGS) \ + ((FLAGS) & (IX86_CALLCVT_CDECL | IX86_CALLCVT_STDCALL \ + | IX86_CALLCVT_FASTCALL | IX86_CALLCVT_THISCALL)) + /* Local variables: version-control: t Index: gcc/gcc/config/i386/netware.c =================================================================== --- gcc.orig/gcc/config/i386/netware.c 2010-11-30 21:01:15.000000000 +0100 +++ gcc/gcc/config/i386/netware.c 2011-04-07 13:09:47.983581700 +0200 @@ -148,12 +148,14 @@ i386_nlm_maybe_mangle_decl_assembler_nam { tree type_attributes = TYPE_ATTRIBUTES (TREE_TYPE (decl)); tree new_id; + unsigned int ccvt = ix86_get_callcvt (TREE_TYPE (decl)); - if (lookup_attribute ("stdcall", type_attributes)) + if ((ccvt & IX86_CALLCVT_STDCALL) != 0) new_id = gen_stdcall_or_fastcall_decoration (decl, id, '_'); - else if (lookup_attribute ("fastcall", type_attributes)) + else if (ccvt == IX86_CALLCVT_FASTCALL) new_id = gen_stdcall_or_fastcall_decoration (decl, id, FASTCALL_PREFIX); - else if ((new_id = lookup_attribute ("regparm", type_attributes))) + else if ((cvt & IX86_CALLCVT_REGPARM) != 0 + && (new_id = lookup_attribute ("regparm", type_attributes))) new_id = gen_regparm_prefix (decl, id, TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (new_id)))); else Index: gcc/gcc/config/i386/winnt.c =================================================================== --- gcc.orig/gcc/config/i386/winnt.c 2011-01-13 20:55:30.000000000 +0100 +++ gcc/gcc/config/i386/winnt.c 2011-04-07 15:21:42.568606300 +0200 @@ -170,7 +170,7 @@ gen_stdcall_or_fastcall_suffix (tree dec HOST_WIDE_INT total = 0; const char *old_str = IDENTIFIER_POINTER (id != NULL_TREE ? id : DECL_NAME (decl)); char *new_str, *p; - tree type = TREE_TYPE (decl); + tree type = TREE_TYPE (DECL_ORIGIN (decl)); tree arg; function_args_iterator args_iter; @@ -202,7 +202,12 @@ gen_stdcall_or_fastcall_suffix (tree dec / parm_boundary_bytes * parm_boundary_bytes); total += parm_size; } - } + } + else if (TARGET_RTD && fastcall == false) + /* If we are using -mrtd emit undecorated symbol and let linker + do the proper resolving. */ + return NULL_RTX; + /* Assume max of 8 base 10 digits in the suffix. */ p = new_str = XALLOCAVEC (char, 1 + strlen (old_str) + 1 + 8 + 1); if (fastcall) @@ -222,10 +227,10 @@ i386_pe_maybe_mangle_decl_assembler_name if (TREE_CODE (decl) == FUNCTION_DECL) { - tree type_attributes = TYPE_ATTRIBUTES (TREE_TYPE (decl)); - if (lookup_attribute ("stdcall", type_attributes)) + unsigned int ccvt = ix86_get_callcvt (TREE_TYPE (decl)); + if ((ccvt & IX86_CALLCVT_STDCALL) != 0) new_id = gen_stdcall_or_fastcall_suffix (decl, id, false); - else if (lookup_attribute ("fastcall", type_attributes)) + else if (ccvt == IX86_CALLCVT_FASTCALL) new_id = gen_stdcall_or_fastcall_suffix (decl, id, true); }