From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2140) id 533CD398547F; Wed, 28 Jul 2021 04:30:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 533CD398547F Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Alexandre Oliva To: gcc-cvs@gcc.gnu.org Subject: [gcc(refs/users/aoliva/heads/strub)] make builtins callable X-Act-Checkin: gcc X-Git-Author: Alexandre Oliva X-Git-Refname: refs/users/aoliva/heads/strub X-Git-Oldrev: fb2b0649a5615004db14ddf4dc8d49c11ee51da6 X-Git-Newrev: 8efece23e8d3c6c98983bf6a75d750947183d5fe Message-Id: <20210728043042.533CD398547F@sourceware.org> Date: Wed, 28 Jul 2021 04:30:42 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 28 Jul 2021 04:30:42 -0000 https://gcc.gnu.org/g:8efece23e8d3c6c98983bf6a75d750947183d5fe commit 8efece23e8d3c6c98983bf6a75d750947183d5fe Author: Alexandre Oliva Date: Mon Jul 26 12:15:36 2021 -0300 make builtins callable Diff: --- gcc/builtins.c | 8 - gcc/ipa-strub.c | 588 ++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 399 insertions(+), 197 deletions(-) diff --git a/gcc/builtins.c b/gcc/builtins.c index 373b4e90f70..23fe484dc7e 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -7987,14 +7987,6 @@ expand_builtin_strub_leave (tree exp) tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, build_int_cst (TREE_TYPE (wmptr), 0)); rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); - - /* If the function called between enter and leave is const or pure, - we may assume it won't change the watermark passed indirectly to - it. These ensure the value is neither dropped nor assumed - unchanged. */ - emit_insn (gen_rtx_USE (VOIDmode, wmark)); - emit_insn (gen_rtx_CLOBBER (VOIDmode, wmark)); - rtx wmarkr = force_reg (ptr_mode, wmark); #ifndef STACK_GROWS_DOWNWARD diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c index 7b05321e980..a32b9b9ead1 100644 --- a/gcc/ipa-strub.c +++ b/gcc/ipa-strub.c @@ -45,7 +45,6 @@ along with GCC; see the file COPYING3. If not see #include "symbol-summary.h" #include "ipa-prop.h" #include "ipa-fnsummary.h" -#include "symtab-thunks.h" #include "gimple-fold.h" #include "fold-const.h" #include "gimple-walk.h" @@ -59,7 +58,26 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "intl.h" #include "ipa-strub.h" -#include "attr-fnspec.h" + +#if BUILDING_GCC_MAJOR >= 11 +# include "symtab-thunks.h" +# include "attr-fnspec.h" +# define HAVE_ATTR_FNSPEC 1 +# define FOR_GCC_11P 1 +#else +# define HAVE_ATTR_FNSPEC 0 +# define FOR_GCC_11P 0 +#endif + +/* Const and pure functions that gain a watermark parameter for strub purposes + are still regarded as such, which may cause the inline expansions of the + __strub builtins to malfunction. Ideally, attribute "fn spec" would enable + us to inform the backend about requirements and side effects of the call, but + call_fusage building in calls.c:expand_call does not even look at + attr_fnspec, so we resort to asm loads and updates to attain an equivalent + effect. Once expand_call gains the ability to issue extra memory uses and + clobbers based on pure/const function's fnspec, we can define this to 1. */ +#define ATTR_FNSPEC_DECONST_WATERMARK 0 enum strub_mode { /* This mode denotes a regular function, that does not require stack @@ -438,6 +456,34 @@ strub_from_body_p (cgraph_node *node) return false; } +/* Return true iff node is associated with a builtin that should be callable + from strub contexts. */ +static inline bool +strub_callable_builtin_p (cgraph_node *node) +{ + if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL) + return false; + + enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl); + + switch (fcode) + { + case BUILT_IN_NONE: + gcc_unreachable (); + + /* ??? Make all builtins callable. We wish to make any builtin call the + compiler might introduce on its own callable. Anything that is + predictable enough as to be known not to allow stack data that should + be strubbed to unintentionally escape to non-strub contexts can be + allowed, and pretty much every builtin appears to fit this description. + The exceptions to this rule seem to be rare, and only available as + explicit __builtin calls, so let's keep it simple and allow all of + them... */ + default: + return true; + } +} + static enum strub_mode compute_strub_mode (cgraph_node *node, tree strub_attr) { @@ -495,7 +541,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr) = (!strub_flag_disabled && (strub_attr ? req_mode == STRUB_CALLABLE - : strub_flag_viable)); + : (strub_flag_viable + || strub_callable_builtin_p (node)))); /* This is a shorthand for either strub-enabled mode. */ const bool consider_strub @@ -730,6 +777,26 @@ set_strub_mode (cgraph_node *node) { tree attr = get_strub_attr_from_decl (node->decl); + if (attr) + switch (enum strub_mode mode = get_strub_mode_from_attr (attr)) + { + /* These can't have been requested through user attributes, so we must + have already gone through them. */ + case STRUB_WRAPPER: + case STRUB_WRAPPED: + case STRUB_INLINABLE: + return mode; + + case STRUB_DISABLED: + case STRUB_AT_CALLS: + case STRUB_INTERNAL: + case STRUB_CALLABLE: + break; + + default: + gcc_unreachable (); + } + cgraph_node *xnode = node; if (node->alias) xnode = node->ultimate_alias_target (); @@ -845,6 +912,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller) return false; } +/* Check that strub functions don't call non-strub functions, and that + always_inline strub functions are only called by strub + functions. */ +static void +verify_strub () +{ + cgraph_node *node; + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + { + enum strub_mode caller_mode = get_strub_mode (node); + bool strub_context + = (caller_mode == STRUB_AT_CALLS + || caller_mode == STRUB_WRAPPED + || caller_mode == STRUB_INLINABLE); + + for (cgraph_edge *e = node->callees; e; e = e->next_callee) + if (e->indirect_unknown_callee) + { + if (!strub_context) + continue; + + tree callee_fntype = gimple_call_fntype (e->call_stmt); + enum strub_mode callee_mode + = get_strub_mode_from_type (callee_fntype); + + if (callee_mode == STRUB_DISABLED + || callee_mode == STRUB_INTERNAL) + error_at (gimple_location (e->call_stmt), + "indirect non-strub call in strub context %qD", + node->decl); + } + else if (!strub_callable_from_p (e->callee, node)) + error_at (gimple_location (e->call_stmt), + "calling non-strub %qD in strub context %qD", + e->callee->decl, node->decl); + } + + /* ??? Check strub-wise pointer type compatibility of variables and + functions, or is this already taken care of on account of the + attribute's being marked as affecting type identity? */ +} + namespace { const pass_data pass_data_ipa_strub_mode = { @@ -907,8 +1017,6 @@ public: NULL, NULL); \ TREE_NOTHROW (decl) = true; \ set_builtin_decl ((CODE), decl, true); \ - set_strub_mode_to (cgraph_node::get_create (decl), \ - STRUB_CALLABLE); \ } \ return decl; \ } @@ -926,7 +1034,7 @@ public: { \ tree type = build_function_type_list FNTYPELIST; \ tree attrs = NULL; \ - if (FNSPEC) \ + if (FNSPEC && HAVE_ATTR_FNSPEC) \ attrs = tree_cons (get_identifier ("fn spec"), \ build_tree_list \ (NULL_TREE, \ @@ -939,8 +1047,6 @@ public: "__strub_" #NAME, attrs); \ TREE_NOTHROW (decl) = true; \ set_builtin_decl ((CODE), decl, true); \ - set_strub_mode_to (cgraph_node::get_create (decl), \ - STRUB_CALLABLE); \ } \ return decl; \ } @@ -1014,116 +1120,6 @@ public: } // anon namespace -#if 0 -static bool -may_throw_p (gcall *stmt) -{ - return flag_exceptions && !gimple_call_nothrow_p (stmt); -} - -static bool -strub_this_call_p (gcall *stmt) -{ - if (gimple_call_internal_p (stmt)) - return false; - - /* If there's no outgoing path in which to do the scrubbing, don't - bother. */ - if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt)) - return false; - - /* ??? Maybe non-mandatory tail calls should be disabled for - scrubbing. Or maybe it won't matter, as long as both tail-caller - and callee are scrubbing-capable. */ - if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt)) - return false; - - if (gimple_alloca_call_p (stmt)) - return true; - - tree fndecl = gimple_call_fndecl (stmt); - if (!fndecl) - return true; - - if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL) - return true; - - enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); - - switch (fcode) - { - CASE_BUILT_IN_ALLOCA: - return true; - - case BUILT_IN_NONE: - return true; - - case BUILT_IN___STRUB_ENTER: - case BUILT_IN___STRUB_UPDATE: - case BUILT_IN___STRUB_LEAVE: - return false; - - case BUILT_IN_CLASSIFY_TYPE: - case BUILT_IN_CONSTANT_P: - return false; - - case BUILT_IN_RETURN_ADDRESS: - case BUILT_IN_FRAME_ADDRESS: - case BUILT_IN_STACK_ADDRESS: - case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: - return false; - - case BUILT_IN_STACK_SAVE: - case BUILT_IN_STACK_RESTORE: - case BUILT_IN_ASAN_ALLOCAS_UNPOISON: - return false; - - case BUILT_IN_SETJMP_SETUP: - case BUILT_IN_SETJMP_RECEIVER: - case BUILT_IN_LONGJMP: - case BUILT_IN_NONLOCAL_GOTO: - case BUILT_IN_UPDATE_SETJMP_BUF: - case BUILT_IN_TRAP: - case BUILT_IN_UNREACHABLE: - return false; - - case BUILT_IN_UNWIND_INIT: - case BUILT_IN_DWARF_CFA: -#ifdef DWARF2_UNWIND_INFO - case BUILT_IN_DWARF_SP_COLUMN: - case BUILT_IN_INIT_DWARF_REG_SIZES: -#endif - case BUILT_IN_FROB_RETURN_ADDR: - case BUILT_IN_EXTRACT_RETURN_ADDR: - case BUILT_IN_EH_RETURN: - case BUILT_IN_EH_RETURN_DATA_REGNO: - case BUILT_IN_EXTEND_POINTER: - case BUILT_IN_EH_POINTER: - case BUILT_IN_EH_FILTER: - case BUILT_IN_EH_COPY_VALUES: - return false; - - case BUILT_IN_VA_START: - case BUILT_IN_VA_END: - case BUILT_IN_VA_COPY: - case BUILT_IN_EXPECT: - case BUILT_IN_EXPECT_WITH_PROBABILITY: - case BUILT_IN_ASSUME_ALIGNED: - case BUILT_IN_PREFETCH: - return false; - - case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: - case BUILT_IN_OBJECT_SIZE: - case BUILT_IN_THREAD_POINTER: - case BUILT_IN_SET_THREAD_POINTER: - return false; - - default: - return true; - } -} -#endif - typedef hash_set indirect_parms_t; static tree @@ -1402,32 +1398,61 @@ remove_named_attribute_unsharing (const char *name, tree *attrs) } } -unsigned int -pass_ipa_strub_mode::execute (function *) +static int last_cgraph_order; + +static bool +ipa_strub_set_mode_for_new_functions () { - cgraph_node *onode; + if (last_cgraph_order && symtab->order == last_cgraph_order) + /* If we're called again after the first call, + then the first call must have returned true. */ + return true; - /* If no strub flag was given in the command line, - set the actual default. */ - if (flag_strub == -2) - flag_strub = 3; // for testing only; was: -1; + cgraph_node *node; bool any_strub = false; + /* Go through the functions twice, once over non-aliases, and then over + aliases, so that aliases can reuse the mode computation of their ultimate + targets. */ for (int aliases = 0; aliases <= 1; aliases++) - FOR_EACH_FUNCTION (onode) + FOR_EACH_FUNCTION (node) { - if (!onode->alias != !aliases) + if (!node->alias != !aliases) + continue; + + /* Already done. */ + if (node->order < last_cgraph_order) continue; - enum strub_mode mode = set_strub_mode (onode); + enum strub_mode mode = set_strub_mode (node); if (mode == STRUB_AT_CALLS || mode == STRUB_INTERNAL) any_strub = true; } + if (any_strub) + last_cgraph_order = symtab->order; + + return any_strub; +} + +unsigned int +pass_ipa_strub_mode::execute (function *) +{ + /* If no strub flag was given in the command line, + set the actual default. */ + if (flag_strub == -2) + flag_strub = 3; // for testing only; was: -1; + + last_cgraph_order = 0; + bool any_strub = ipa_strub_set_mode_for_new_functions (); + if (!any_strub) flag_strub = 0; + else + /* Verify before any inlining or other transformations. */ + verify_strub (); return 0; } @@ -1438,54 +1463,13 @@ make_pass_ipa_strub_mode (gcc::context *ctxt) return new pass_ipa_strub_mode (ctxt); } -/* Check that strub functions don't call non-strub functions, and that - always_inline strub functions are only called by strub - functions. */ -static void -verify_strub () -{ - cgraph_node *node; - - FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) - { - enum strub_mode caller_mode = get_strub_mode (node); - bool strub_context - = (caller_mode == STRUB_AT_CALLS - || caller_mode == STRUB_WRAPPED - || caller_mode == STRUB_INLINABLE); - - for (cgraph_edge *e = node->callees; e; e = e->next_callee) - if (e->indirect_unknown_callee) - { - if (!strub_context) - continue; - - tree callee_fntype = gimple_call_fntype (e->call_stmt); - enum strub_mode callee_mode - = get_strub_mode_from_type (callee_fntype); - - if (callee_mode == STRUB_DISABLED - || callee_mode == STRUB_INTERNAL) - error_at (gimple_location (e->call_stmt), - "indirect non-strub call in strub context %qD", - node->decl); - } - else if (!strub_callable_from_p (e->callee, node)) - error_at (gimple_location (e->call_stmt), - "calling non-strub %qD in strub context %qD", - e->callee->decl, node->decl); - } - - /* ??? Check strub-wise pointer type compatibility of variables and - functions, or is this already taken care of on account of the - attribute's being marked as affecting type identity? */ -} - unsigned int pass_ipa_strub::execute (function *) { cgraph_node *onode; + ipa_strub_set_mode_for_new_functions (); + FOR_EACH_FUNCTION (onode) { enum strub_mode mode = get_strub_mode (onode); @@ -1572,7 +1556,7 @@ pass_ipa_strub::execute (function *) // Mostly copied from gimple_call_copy_skip_args. int i = 0; int nargs = gimple_call_num_args (stmt); - auto_vec vargs (nargs + 1); + auto_vec vargs (MAX (nargs, named_args) + 1); gcall *new_stmt; /* pr71109.c calls a prototypeless function, then defines it with @@ -1630,15 +1614,47 @@ pass_ipa_strub::execute (function *) gimple_seq seq = NULL; { -#if 0 - tree lswm = create_tmp_var (ptr_type_node, ".L.strub.watermark"); - gassign *load = gimple_build_assign (lswm, swm); - gimple_seq_add_stmt (&seq, load); -#else - tree lswm = unshare_expr (swmp); +#if !ATTR_FNSPEC_DECONST_WATERMARK + /* If the call will be assumed to not modify or even read the + watermark, make it read and modified ourselves. */ + if ((gimple_call_flags (wrcall) + & (ECF_CONST | ECF_PURE | ECF_NOVOPS))) + { + vec *inputs = NULL; + vec *outputs = NULL; + vec_safe_push (outputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (2, "=m")), + swm)); + vec_safe_push (inputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (1, "m")), + swm)); + gasm *forcemod = gimple_build_asm_vec ("", inputs, outputs, + NULL, NULL); + gimple_seq_add_stmt (&seq, forcemod); + + /* If the call will be assumed to not even read the watermark, + make sure it is already in memory before the call. */ + if ((gimple_call_flags (wrcall) & ECF_CONST)) + { + vec *inputs = NULL; + vec_safe_push (inputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (1, "m")), + swm)); + gasm *force_store = gimple_build_asm_vec ("", inputs, NULL, + NULL, NULL); + gsi_insert_before (&gsi, force_store, GSI_SAME_STMT); + } + } #endif - gcall *sleave = gimple_build_call (get_leave (), 1, lswm); + gcall *sleave = gimple_build_call (get_leave (), 1, + unshare_expr (swmp)); gimple_seq_add_stmt (&seq, sleave); gassign *clobber = gimple_build_assign (swm, @@ -1652,6 +1668,50 @@ pass_ipa_strub::execute (function *) pop_cfun (); } +#if ATTR_FNSPEC_DECONST_WATERMARK + { + int flags = flags_from_decl_or_type (onode->decl); + tree fnspec = lookup_attribute ("fn spec", TREE_TYPE (onode->decl)); + + if ((flags & (ECF_CONST | ECF_PURE | ECF_NOVOPS)) || fnspec) + { + size_t xargs = 1; + size_t curlen = 0, tgtlen = 2 + 2 * (named_args + xargs); + auto_vec nspecv (tgtlen); + char *nspec = &nspecv[0]; /* It will *not* be NUL-terminated! */ + if (fnspec) + { + tree fnspecstr = TREE_VALUE (TREE_VALUE (fnspec)); + curlen = TREE_STRING_LENGTH (fnspecstr); + memcpy (nspec, TREE_STRING_POINTER (fnspecstr), curlen); + } + if (!curlen) + { + nspec[curlen++] = '.'; + nspec[curlen++] = ((flags & ECF_CONST) + ? 'c' + : (flags & ECF_PURE) + ? 'p' + : ' '); + } + while (curlen < tgtlen - 2 * xargs) + { + nspec[curlen++] = '.'; + nspec[curlen++] = ' '; + } + nspec[curlen++] = 'W'; + nspec[curlen++] = 't'; + + /* The type has already been copied before adding parameters. */ + TYPE_ATTRIBUTES (TREE_TYPE (onode->decl)) + = tree_cons (get_identifier ("fn spec"), + build_tree_list (NULL_TREE, + build_string (tgtlen, nspec)), + TYPE_ATTRIBUTES (TREE_TYPE (onode->decl))); + } + } +#endif + if (!onode->has_gimple_body_p ()) continue; @@ -1807,11 +1867,13 @@ pass_ipa_strub::execute (function *) anyway, but performed at the caller. */ indirect_parms_t indirect_nparms (3, false); unsigned adjust_ftype = 0; + unsigned named_args = 0; for (tree parm = DECL_ARGUMENTS (onode->decl), nparm = DECL_ARGUMENTS (nnode->decl), nparmt = TYPE_ARG_TYPES (TREE_TYPE (nnode->decl)); parm; - parm = DECL_CHAIN (parm), + named_args++, + parm = DECL_CHAIN (parm), nparm = DECL_CHAIN (nparm), nparmt = nparmt ? TREE_CHAIN (nparmt) : NULL_TREE) if (!(0 /* DECL_BY_REFERENCE (narg) */ @@ -1840,7 +1902,11 @@ pass_ipa_strub::execute (function *) relayout_decl (nparm); TREE_ADDRESSABLE (nparm) = 0; DECL_BY_REFERENCE (nparm) = 0; +#if FOR_GCC_11P DECL_NOT_GIMPLE_REG_P (nparm) = 0; +#else + DECL_GIMPLE_REG_P (nparm) = 1; +#endif /* ??? This avoids mismatches in debug info bind stmts in e.g. a-chahan . */ DECL_ABSTRACT_ORIGIN (nparm) = NULL; @@ -1860,6 +1926,7 @@ pass_ipa_strub::execute (function *) gcc_checking_assert (TYPE_ARG_TYPES (nftype) != TYPE_ARG_TYPES (TREE_TYPE (onode->decl))); +#if HAVE_ATTR_FNSPEC /* Check that fnspec still works for the modified function signature, and drop it otherwise. */ bool drop_fnspec = false; @@ -1871,15 +1938,20 @@ pass_ipa_strub::execute (function *) retcopy = (unsigned) -1; unsigned i = 0; +#endif for (tree nparm = DECL_ARGUMENTS (nnode->decl), nparmt = TYPE_ARG_TYPES (nftype); adjust_ftype > 0; - nparm = DECL_CHAIN (nparm), nparmt = TREE_CHAIN (nparmt), i++) +#if HAVE_ATTR_FNSPEC + i++, +#endif + nparm = DECL_CHAIN (nparm), nparmt = TREE_CHAIN (nparmt)) if (indirect_nparms.contains (nparm)) { TREE_VALUE (nparmt) = TREE_TYPE (nparm); adjust_ftype--; +#if HAVE_ATTR_FNSPEC if (fnspec && !drop_fnspec) { if (i == retcopy) @@ -1907,16 +1979,93 @@ pass_ipa_strub::execute (function *) drop_fnspec = true; } } +#endif } +#if HAVE_ATTR_FNSPEC /* ??? Maybe we could adjust it instead. */ if (drop_fnspec) remove_named_attribute_unsharing ("fn spec", &TYPE_ATTRIBUTES (nftype)); +#endif TREE_TYPE (nnode->decl) = nftype; } +#if ATTR_FNSPEC_DECONST_WATERMARK + { + int flags = flags_from_decl_or_type (nnode->decl); + tree fnspec = lookup_attribute ("fn spec", TREE_TYPE (nnode->decl)); + + if ((flags & (ECF_CONST | ECF_PURE | ECF_NOVOPS)) || fnspec) + { + size_t xargs = 1 + int (is_stdarg) + int (apply_args); + size_t curlen = 0, tgtlen = 2 + 2 * (named_args + xargs); + auto_vec nspecv (tgtlen); + char *nspec = &nspecv[0]; /* It will *not* be NUL-terminated! */ + bool no_writes_p = true; + if (fnspec) + { + tree fnspecstr = TREE_VALUE (TREE_VALUE (fnspec)); + curlen = TREE_STRING_LENGTH (fnspecstr); + memcpy (nspec, TREE_STRING_POINTER (fnspecstr), curlen); + if (!(flags & (ECF_CONST | ECF_PURE | ECF_NOVOPS)) + && curlen >= 2 + && nspec[1] != 'c' && nspec[1] != 'C' + && nspec[1] != 'p' && nspec[1] != 'P') + no_writes_p = false; + } + if (!curlen) + { + nspec[curlen++] = '.'; + nspec[curlen++] = ((flags & ECF_CONST) + ? 'c' + : (flags & ECF_PURE) + ? 'p' + : ' '); + } + while (curlen < tgtlen - 2 * xargs) + { + nspec[curlen++] = '.'; + nspec[curlen++] = ' '; + } + + /* These extra args are unlikely to be present in const or pure + functions. It's conceivable that a function that takes variable + arguments, or that passes its arguments on to another function, + could be const or pure, but it would not modify the arguments, and, + being pure or const, it couldn't possibly modify or even access + memory referenced by them. But it can read from these internal + data structures created by the wrapper, and from any + argument-passing memory referenced by them, so we denote the + possibility of reading from multiple levels of indirection, but + only of reading because const/pure. */ + if (apply_args) + { + nspec[curlen++] = (no_writes_p ? 'r' : '.'); + nspec[curlen++] = (no_writes_p ? 't' : ' '); + } + if (is_stdarg) + { + nspec[curlen++] = (no_writes_p ? 'r' : '.'); + nspec[curlen++] = (no_writes_p ? 't' : ' '); + } + + nspec[curlen++] = 'W'; + nspec[curlen++] = 't'; + + /* The type has already been copied before adding parameters. */ + gcc_checking_assert (TYPE_ARG_TYPES (TREE_TYPE (nnode->decl)) + != TYPE_ARG_TYPES (TREE_TYPE (onode->decl))); + TYPE_ATTRIBUTES (TREE_TYPE (nnode->decl)) + = tree_cons (get_identifier ("fn spec"), + build_tree_list (NULL_TREE, + build_string (tgtlen, nspec)), + TYPE_ATTRIBUTES (TREE_TYPE (nnode->decl))); + } + } +#endif + { tree decl = onode->decl; cgraph_node *target = nnode; @@ -1940,8 +2089,14 @@ pass_ipa_strub::execute (function *) /* Turn alias into thunk and expand it into GIMPLE representation. */ onode->definition = true; +#if FOR_GCC_11P thunk_info::get_create (onode); onode->thunk = true; +#else + memset (&onode->thunk, 0, sizeof (cgraph_thunk_info)); + onode->thunk.thunk_p = true; + onode->thunk.alias = target->decl; +#endif #if !IMPLICIT_CGRAPH_EDGES onode->create_edge (target, NULL, onode->count); #endif @@ -1980,9 +2135,11 @@ pass_ipa_strub::execute (function *) current_function_decl = thunk_fndecl; +#if FOR_GCC_11P /* Ensure thunks are emitted in their correct sections. */ resolve_unique_section (thunk_fndecl, 0, flag_function_sections); +#endif bitmap_obstack_initialize (NULL); @@ -2066,8 +2223,14 @@ pass_ipa_strub::execute (function *) tree addr = build1 (ADDR_EXPR, ref_type, arg); tmp = arg = addr; } +#if ! FOR_GCC_11P + else if (VECTOR_TYPE_P (TREE_TYPE (arg)) + || TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE) + DECL_GIMPLE_REG_P (arg) = 1; +#else else DECL_NOT_GIMPLE_REG_P (arg) = 0; +#endif /* Convert the argument back to the type used by the calling conventions, e.g. a non-prototyped float type is passed as @@ -2151,8 +2314,10 @@ pass_ipa_strub::execute (function *) profile_status_for_fn (cfun) = cfg_count.initialized_p () && cfg_count.ipa_p () ? PROFILE_READ : PROFILE_GUESSED; +#if FOR_GCC_11P /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks. */ // TREE_ASM_WRITTEN (thunk_fndecl) = false; +#endif delete_unreachable_blocks (); update_ssa (TODO_update_ssa); checking_verify_flow_info (); @@ -2160,7 +2325,11 @@ pass_ipa_strub::execute (function *) /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ +#if FOR_GCC_11P onode->thunk = false; +#else + onode->thunk.thunk_p = false; +#endif onode->lowered = true; bitmap_obstack_release (NULL); } @@ -2168,7 +2337,9 @@ pass_ipa_strub::execute (function *) set_cfun (NULL); } +#if FOR_GCC_11P thunk_info::remove (onode); +#endif // some more of create_wrapper at the end of the next block. } @@ -2381,15 +2552,47 @@ pass_ipa_strub::execute (function *) update_stmt (wrcall); { -#if 0 - tree lswm = create_tmp_var (ptr_type_node, ".L.strub.watermark"); - gassign *load = gimple_build_assign (lswm, swm); - gimple_seq_add_stmt (&seq, load); -#else - tree lswm = unshare_expr (swmp); +#if !ATTR_FNSPEC_DECONST_WATERMARK + /* If the call will be assumed to not modify or even read the + watermark, make it read and modified ourselves. */ + if ((gimple_call_flags (wrcall) + & (ECF_CONST | ECF_PURE | ECF_NOVOPS))) + { + vec *inputs = NULL; + vec *outputs = NULL; + vec_safe_push (outputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (2, "=m")), + swm)); + vec_safe_push (inputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (1, "m")), + swm)); + gasm *forcemod = gimple_build_asm_vec ("", inputs, outputs, + NULL, NULL); + gimple_seq_add_stmt (&seq, forcemod); + + /* If the call will be assumed to not even read the watermark, + make sure it is already in memory before the call. */ + if ((gimple_call_flags (wrcall) & ECF_CONST)) + { + vec *inputs = NULL; + vec_safe_push (inputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (1, "m")), + swm)); + gasm *force_store = gimple_build_asm_vec ("", inputs, NULL, + NULL, NULL); + gsi_insert_before (&gsi, force_store, GSI_SAME_STMT); + } + } #endif - gcall *sleave = gimple_build_call (get_leave (), 1, lswm); + gcall *sleave = gimple_build_call (get_leave (), 1, + unshare_expr (swmp)); gimple_seq_add_stmt (&seq, sleave); gassign *clobber = gimple_build_assign (swm, @@ -2418,7 +2621,14 @@ pass_ipa_strub::execute (function *) #endif } - verify_strub (); + if (flag_checking) + { + /* We've already verified before any inlining or other transformations. + Recheck after strub transformations only if checking is enabled, since + they should not introduce any incompatibilities. */ + ipa_strub_set_mode_for_new_functions (); + verify_strub (); + } return 0; }