Hi! As discussed at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71876, we have a _very_ old hack in gcc, that recognizes certain functions by name, and inserts in some cases unsafe attributes, that don't work for a freestanding environment.
It is unsafe to return ECF_MAY_BE_ALLOCA, ECF_LEAF and ECF_NORETURN from special_function_p, just by the name of the function, especially for less well known functions, like "getcontext" or "savectx", which could easily used for something completely different. Moreover, from the backend library we cannot check flag_hosted, or if the function has "C" or "C++" binding. So all these functions would get the leaf attribute, which makes it very easy to construct some kind of wrong code examples. This patch removes the unsafe flags, and adds them to built-in functions instead. The patch removes also support for some completely unknown function names from the middle-end, because these functions were apparently in use at some point in time, but are certainly dead by now. It is however not possible to remove the special handling by name altogether, because the glibc does not add the return_twice function attribute on _setjmp, __sigsetjmp and getcontext until today; a glibc BZ is filed at: https://sourceware.org/bugzilla/show_bug.cgi?id=20382 Without the return_twice attribute we would loose the -Wclobbered warning, and some targets (spark, ia64, maybe others too) would even generate wrong code. Boot-strapped and reg-tested on x86_64-pc-linux-gnu. OK for trunk? Thanks Bernd.
2016-07-19 Bernd Edlinger <bernd.edlin...@hotmail.de> PR middle-end/71876 * builtin-attrs.def (ATTR_RT_NOTHROW_LEAF_LIST): New return twice attribute. * builtins.def (BUILT_IN_GETCONTEXT, BUILT_IN_VFORK): New built-in using ATTR_RT_NOTHROW_LEAF_LIST. (BUILT_IN_SETJMP): Use ATTR_RT_NOTHROW_LEAF_LIST here. * calls.c (special_function_p): Remove special handling of "alloca" by name, as well as "setjmp_syscall", "savectx", "qsetjmp" and the prefixes "__x" and "__builtin_". Remove potentially unsafe ECF_LEAF and ECF_NORETURN from here, use attributes of built-in instead.
Index: gcc/builtin-attrs.def =================================================================== --- gcc/builtin-attrs.def (revision 238382) +++ gcc/builtin-attrs.def (working copy) @@ -131,6 +131,8 @@ DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LIST, AT ATTR_NULL, ATTR_NOTHROW_LIST) DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LEAF_LIST, ATTR_NORETURN,\ ATTR_NULL, ATTR_NOTHROW_LEAF_LIST) +DEF_ATTR_TREE_LIST (ATTR_RT_NOTHROW_LEAF_LIST, ATTR_RETURNS_TWICE,\ + ATTR_NULL, ATTR_NOTHROW_LEAF_LIST) DEF_ATTR_TREE_LIST (ATTR_COLD_NOTHROW_LEAF_LIST, ATTR_COLD,\ ATTR_NULL, ATTR_NOTHROW_LEAF_LIST) DEF_ATTR_TREE_LIST (ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST, ATTR_COLD,\ Index: gcc/builtins.def =================================================================== --- gcc/builtins.def (revision 238382) +++ gcc/builtins.def (working copy) @@ -796,6 +796,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FINITED32, "finit DEF_EXT_LIB_BUILTIN (BUILT_IN_FINITED64, "finited64", BT_FN_INT_DFLOAT64, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_FINITED128, "finited128", BT_FN_INT_DFLOAT128, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_FPCLASSIFY, "fpclassify", BT_FN_INT_INT_INT_INT_INT_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) +DEF_EXT_LIB_BUILTIN (BUILT_IN_GETCONTEXT, "getcontext", BT_FN_INT_PTR, ATTR_RT_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_ISFINITE, "isfinite", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) DEF_GCC_BUILTIN (BUILT_IN_ISINF_SIGN, "isinf_sign", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) DEF_C99_C90RES_BUILTIN (BUILT_IN_ISINF, "isinf", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC) @@ -837,7 +838,7 @@ DEF_LIB_BUILTIN (BUILT_IN_REALLOC, "realloc DEF_GCC_BUILTIN (BUILT_IN_RETURN, "return", BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_RETURN_ADDRESS, "return_address", BT_FN_PTR_UINT, ATTR_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_SAVEREGS, "saveregs", BT_FN_PTR_VAR, ATTR_NULL) -DEF_GCC_BUILTIN (BUILT_IN_SETJMP, "setjmp", BT_FN_INT_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_GCC_BUILTIN (BUILT_IN_SETJMP, "setjmp", BT_FN_INT_PTR, ATTR_RT_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_STRFMON, "strfmon", BT_FN_SSIZE_STRING_SIZE_CONST_STRING_VAR, ATTR_FORMAT_STRFMON_NOTHROW_3_4) DEF_LIB_BUILTIN (BUILT_IN_STRFTIME, "strftime", BT_FN_SIZE_STRING_SIZE_CONST_STRING_CONST_PTR, ATTR_FORMAT_STRFTIME_NOTHROW_3_0) DEF_GCC_BUILTIN (BUILT_IN_TRAP, "trap", BT_FN_VOID, ATTR_NORETURN_NOTHROW_LEAF_LIST) @@ -849,6 +850,7 @@ DEF_GCC_BUILTIN (BUILT_IN_VA_END, "va_end", DEF_GCC_BUILTIN (BUILT_IN_VA_START, "va_start", BT_FN_VOID_VALIST_REF_VAR, ATTR_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_VA_ARG_PACK, "va_arg_pack", BT_FN_INT, ATTR_PURE_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_VA_ARG_PACK_LEN, "va_arg_pack_len", BT_FN_INT, ATTR_PURE_NOTHROW_LEAF_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_VFORK, "vfork", BT_FN_PID, ATTR_RT_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN__EXIT, "_exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN__EXIT2, "_Exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LEAF_LIST) Index: gcc/calls.c =================================================================== --- gcc/calls.c (revision 238382) +++ gcc/calls.c (working copy) @@ -474,8 +474,6 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNU For example, if the function might return more than one time (setjmp), then set RETURNS_TWICE to a nonzero value. - Similarly set NORETURN if the function is in the longjmp family. - Set MAY_BE_ALLOCA for any memory allocation function that might allocate space from the stack such as alloca. */ @@ -491,7 +489,7 @@ special_function_p (const_tree fndecl, int flags) name_decl = DECL_NAME (cgraph_node::get (fndecl)->orig_decl); if (fndecl && name_decl - && IDENTIFIER_LENGTH (name_decl) <= 17 + && IDENTIFIER_LENGTH (name_decl) <= 12 /* Exclude functions not at the file scope, or not `extern', since they are not the magic functions we would otherwise think they are. @@ -506,55 +504,21 @@ special_function_p (const_tree fndecl, int flags) const char *name = IDENTIFIER_POINTER (name_decl); const char *tname = name; - /* We assume that alloca will always be called by name. It - makes no sense to pass it as a pointer-to-function to - anything that does not understand its behavior. */ - if (IDENTIFIER_LENGTH (name_decl) == 6 - && name[0] == 'a' - && ! strcmp (name, "alloca")) - flags |= ECF_MAY_BE_ALLOCA; - - /* Disregard prefix _, __, __x or __builtin_. */ + /* Disregard prefix _ or __. */ if (name[0] == '_') { - if (name[1] == '_' - && name[2] == 'b' - && !strncmp (name + 3, "uiltin_", 7)) - tname += 10; - else if (name[1] == '_' && name[2] == 'x') - tname += 3; - else if (name[1] == '_') + if (name[1] == '_') tname += 2; else tname += 1; } - if (tname[0] == 's') - { - if ((tname[1] == 'e' - && (! strcmp (tname, "setjmp") - || ! strcmp (tname, "setjmp_syscall"))) - || (tname[1] == 'i' - && ! strcmp (tname, "sigsetjmp")) - || (tname[1] == 'a' - && ! strcmp (tname, "savectx"))) - flags |= ECF_RETURNS_TWICE | ECF_LEAF; - - if (tname[1] == 'i' - && ! strcmp (tname, "siglongjmp")) - flags |= ECF_NORETURN; - } - else if ((tname[0] == 'q' && tname[1] == 's' - && ! strcmp (tname, "qsetjmp")) - || (tname[0] == 'v' && tname[1] == 'f' - && ! strcmp (tname, "vfork")) - || (tname[0] == 'g' && tname[1] == 'e' - && !strcmp (tname, "getcontext"))) - flags |= ECF_RETURNS_TWICE | ECF_LEAF; - - else if (tname[0] == 'l' && tname[1] == 'o' - && ! strcmp (tname, "longjmp")) - flags |= ECF_NORETURN; + /* ECF_RETURNS_TWICE is safe even for -ffrestanding. */ + if (! strcmp (tname, "setjmp") + || ! strcmp (tname, "sigsetjmp") + || ! strcmp (tname, "vfork") + || ! strcmp (tname, "getcontext")) + flags |= ECF_RETURNS_TWICE; } if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)