From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2140) id 67611385842C; Wed, 4 Aug 2021 08:17:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 67611385842C 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)] fix handling of indirect calls X-Act-Checkin: gcc X-Git-Author: Alexandre Oliva X-Git-Refname: refs/users/aoliva/heads/strub X-Git-Oldrev: 3e6dac6622bdf98bad30229373cfd8eb4c2436b8 X-Git-Newrev: a00086813f5ca93baf77276a281daf53803f5c62 Message-Id: <20210804081742.67611385842C@sourceware.org> Date: Wed, 4 Aug 2021 08:17: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, 04 Aug 2021 08:17:42 -0000 https://gcc.gnu.org/g:a00086813f5ca93baf77276a281daf53803f5c62 commit a00086813f5ca93baf77276a281daf53803f5c62 Author: Alexandre Oliva Date: Wed Aug 4 05:14:09 2021 -0300 fix handling of indirect calls Diff: --- gcc/builtins.c | 6 +- gcc/ipa-strub.c | 707 ++++++++++++--------- gcc/testsuite/c-c++-common/strub-O0.c | 14 + gcc/testsuite/c-c++-common/strub-O1.c | 15 + gcc/testsuite/c-c++-common/strub-O2.c | 16 + gcc/testsuite/c-c++-common/strub-O2fni.c | 15 + gcc/testsuite/c-c++-common/strub-O3.c | 12 + gcc/testsuite/c-c++-common/strub-O3fni.c | 15 + gcc/testsuite/c-c++-common/strub-Og.c | 16 + gcc/testsuite/c-c++-common/strub-Os.c | 18 + gcc/testsuite/c-c++-common/strub-all1.c | 32 + gcc/testsuite/c-c++-common/strub-all2.c | 24 + gcc/testsuite/c-c++-common/strub-at-calls1.c | 30 + gcc/testsuite/c-c++-common/strub-at-calls2.c | 23 + gcc/testsuite/c-c++-common/strub-default1.c | 40 ++ gcc/testsuite/c-c++-common/strub-default2.c | 29 + gcc/testsuite/c-c++-common/strub-internal1.c | 31 + gcc/testsuite/c-c++-common/strub-internal2.c | 21 + gcc/testsuite/c-c++-common/torture/strub-data1.c | 13 + gcc/testsuite/c-c++-common/torture/strub-data2.c | 14 + gcc/testsuite/c-c++-common/torture/strub-data3.c | 14 + gcc/testsuite/c-c++-common/torture/strub-data4.c | 14 + gcc/testsuite/c-c++-common/torture/strub-data5.c | 15 + .../c-c++-common/torture/strub-indcall1.c | 14 + .../c-c++-common/torture/strub-indcall2.c | 14 + .../c-c++-common/torture/strub-indcall3.c | 14 + gcc/testsuite/g++.dg/torture/strub-init1.C | 13 + gcc/testsuite/g++.dg/torture/strub-init2.C | 14 + gcc/testsuite/g++.dg/torture/strub-init3.C | 13 + 29 files changed, 927 insertions(+), 289 deletions(-) diff --git a/gcc/builtins.c b/gcc/builtins.c index 73d80482c32..f387d93974f 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -7923,7 +7923,7 @@ expand_builtin_strub_enter (tree exp) if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; - if (!optimize || flag_no_inline) + if (optimize < 1 || flag_no_inline) return NULL_RTX; rtx stktop = expand_builtin_stack_address (); @@ -7947,7 +7947,7 @@ expand_builtin_strub_update (tree exp) if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) return NULL_RTX; - if (optimize < 2 || flag_no_inline) + if (optimize < 2 || optimize_size || flag_no_inline) return NULL_RTX; rtx stktop = expand_builtin_stack_address (); @@ -8005,7 +8005,7 @@ expand_builtin_strub_leave (tree exp) ptr_mode, NULL_RTX, done, NULL, profile_probability::very_likely ()); - if (optimize < 3 || !flag_inline_functions) + if (optimize < 3) expand_call (exp, NULL_RTX, true); else { diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c index 992ab511b26..e302f0ec1c5 100644 --- a/gcc/ipa-strub.c +++ b/gcc/ipa-strub.c @@ -146,6 +146,17 @@ enum strub_mode { /* This denotes an always_inline function that requires strubbing. It can only be called from, and inlined into, other strubbing contexts. */ STRUB_INLINABLE = -3, + + /* This denotes a function that accesses strub variables, so it would call for + internal strubbing (whether or not it's eligible for that), but since + at-calls strubbing is viable, that's selected as an optimization. This + mode addresses the inconvenience that such functions may have different + modes selected depending on optimization flags, and get a different + callable status depending on that choice: if we assigned them + STRUB_AT_CALLS mode, they would be callable when optimizing, whereas + STRUB_INTERNAL would not be callable. */ + STRUB_AT_CALLS_OPT = -4, + }; static tree @@ -418,6 +429,13 @@ can_strub_internally_p (cgraph_node *node, bool report = false) node->decl); } + if (result) + /* Since we're not changing the function identity proper, just + moving its full implementation, we *could* disable + fun->cannot_be_copied_reason and/or temporarily drop a noclone + attribute. */ + gcc_checking_assert (tree_versionable_function_p (node->decl)); + return result; } @@ -590,7 +608,9 @@ compute_strub_mode (cgraph_node *node, tree strub_attr) = (at_calls_eligible && (strub_attr || (node->has_gimple_body_p () +#if 0 /* We no longer use collect_callers, so we can probably drop it. */ && node->get_availability () > AVAIL_INTERPOSABLE +#endif && ((!node->externally_visible #if 0 /* We wish to bypass the test below for functions that are @@ -659,7 +679,9 @@ compute_strub_mode (cgraph_node *node, tree strub_attr) && (strub_flag_internal || !at_calls_viable)) ? STRUB_INTERNAL : (strub_enable && at_calls_viable) - ? STRUB_AT_CALLS + ? (strub_required && !strub_attr + ? STRUB_AT_CALLS_OPT + : STRUB_AT_CALLS) : consider_callable ? STRUB_CALLABLE : STRUB_DISABLED); @@ -689,6 +711,18 @@ compute_strub_mode (cgraph_node *node, tree strub_attr) reported in set_strub_mode_to. */ break; + case STRUB_AT_CALLS_OPT: + /* Functions that select this mode do so because of references to strub + variables. Even if we choose at-calls as an optimization, the + requirements for internal strub must still be satisfied. Optimization + options may render implicit at-calls strub not viable (-O0 sets + force_output for static non-inline functions), and it would not be good + if changing optimization options turned a well-formed into an + ill-formed one. */ + if (!internal_viable) + can_strub_internally_p (node, true); + break; + case STRUB_WRAPPED: case STRUB_WRAPPER: default: @@ -772,20 +806,21 @@ set_strub_mode_to (cgraph_node *node, enum strub_mode mode) DECL_ATTRIBUTES (node->decl)); } -static enum strub_mode +static void 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)) + switch (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_AT_CALLS_OPT: + return; case STRUB_DISABLED: case STRUB_AT_CALLS: @@ -809,11 +844,9 @@ set_strub_mode (cgraph_node *node) : compute_strub_mode (node, attr)); set_strub_mode_to (node, mode); - - return mode; } -/* Non-strub functions shouldn't be called from strub functions, +/* Non-strub functions shouldn't be called from within strub contexts, except through callable ones. Always inline strub functions can only be called from strub functions. */ @@ -826,6 +859,7 @@ strub_callable_from_p (cgraph_node *callee, cgraph_node *caller) switch (caller_mode) { case STRUB_WRAPPED: + case STRUB_AT_CALLS_OPT: case STRUB_AT_CALLS: case STRUB_INTERNAL: case STRUB_INLINABLE: @@ -844,10 +878,11 @@ strub_callable_from_p (cgraph_node *callee, cgraph_node *caller) { case STRUB_WRAPPED: case STRUB_AT_CALLS: - case STRUB_INTERNAL: case STRUB_INLINABLE: break; + case STRUB_AT_CALLS_OPT: + case STRUB_INTERNAL: case STRUB_WRAPPER: return (flag_strub >= 0); @@ -879,6 +914,7 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller) case STRUB_AT_CALLS: case STRUB_INTERNAL: case STRUB_INLINABLE: + case STRUB_AT_CALLS_OPT: break; case STRUB_WRAPPER: @@ -898,6 +934,7 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller) case STRUB_AT_CALLS: case STRUB_INTERNAL: case STRUB_INLINABLE: + case STRUB_AT_CALLS_OPT: return true; case STRUB_WRAPPER: @@ -925,29 +962,35 @@ verify_strub () enum strub_mode caller_mode = get_strub_mode (node); bool strub_context = (caller_mode == STRUB_AT_CALLS + || caller_mode == STRUB_AT_CALLS_OPT || caller_mode == STRUB_WRAPPED || caller_mode == STRUB_INLINABLE); + for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee) + { + gcc_checking_assert (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); + } + 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); + { + gcc_checking_assert (!e->indirect_unknown_callee); + 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 @@ -1014,6 +1057,40 @@ public: virtual bool gate (function *) { return flag_strub; } virtual unsigned int execute (function *); +#define DEF_TYPE(NAME, INIT) \ + static inline tree get_ ## NAME () { \ + static tree type = NULL_TREE; \ + if (!type) \ + type = (INIT); \ + return type; \ + } + + /* Use a distinct ptr_type_node to denote the watermark, so that we can + recognize it in arg lists and avoid modifying types twice. */ + DEF_TYPE (wmt, build_distinct_type_copy (ptr_type_node)) + + DEF_TYPE (pwmt, build_pointer_type (get_wmt ())) + + DEF_TYPE (qpwmt, + build_qualified_type (get_pwmt (), + TYPE_QUAL_RESTRICT + | TYPE_QUAL_CONST)) + + DEF_TYPE (pptr, build_pointer_type (ptr_type_node)) + + DEF_TYPE (qpptr, + build_qualified_type (get_pptr (), + TYPE_QUAL_RESTRICT + | TYPE_QUAL_CONST)) + + DEF_TYPE (qpvalst, + build_qualified_type (build_pointer_type + (va_list_type_node), + TYPE_QUAL_RESTRICT + | TYPE_QUAL_CONST)) + +#undef DEF_TYPE + #define DEF_NM_BUILTIN(NAME, CODE, FNTYPELIST) \ static tree get_ ## NAME () { \ tree decl = builtin_decl_explicit (CODE); \ @@ -1062,13 +1139,13 @@ public: DEF_SS_BUILTIN (enter, ". Ot", BUILT_IN___STRUB_ENTER, - (void_type_node, get_pptr (), NULL)) + (void_type_node, get_qpwmt (), NULL)) DEF_SS_BUILTIN (update, ". Wt", BUILT_IN___STRUB_UPDATE, - (void_type_node, get_pptr (), NULL)) + (void_type_node, get_qpwmt (), NULL)) DEF_SS_BUILTIN (leave, ". w ", BUILT_IN___STRUB_LEAVE, - (void_type_node, get_pptr (), NULL)) + (void_type_node, get_qpwmt (), NULL)) #undef DEF_SS_BUILTIN @@ -1086,28 +1163,8 @@ public: #undef DEF_IDENT -#define DEF_TYPE(NAME, INIT) \ - static inline tree get_ ## NAME () { \ - static tree type = NULL_TREE; \ - if (!type) \ - type = (INIT); \ - return type; \ - } - - DEF_TYPE (pptr, build_pointer_type (ptr_type_node)) - - DEF_TYPE (qpptr, - build_qualified_type (get_pptr (), - TYPE_QUAL_RESTRICT - | TYPE_QUAL_CONST)) - - DEF_TYPE (qpvalst, - build_qualified_type (build_pointer_type - (va_list_type_node), - TYPE_QUAL_RESTRICT - | TYPE_QUAL_CONST)) - -#undef DEF_TYPE + static inline int adjust_at_calls_type (tree); + static inline void adjust_at_calls_call (cgraph_edge *, int); static inline gimple_seq call_update_watermark (tree wmptr, cgraph_node *node, profile_count count, @@ -1409,18 +1466,14 @@ remove_named_attribute_unsharing (const char *name, tree *attrs) static int last_cgraph_order; -static bool +static void ipa_strub_set_mode_for_new_functions () { - 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 (symtab->order == last_cgraph_order) + return; 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. */ @@ -1434,29 +1487,241 @@ ipa_strub_set_mode_for_new_functions () if (node->order < last_cgraph_order) continue; - enum strub_mode mode = set_strub_mode (node); + set_strub_mode (node); + } + + last_cgraph_order = symtab->order; +} - if (mode == STRUB_AT_CALLS || mode == STRUB_INTERNAL) - any_strub = true; +/* Adjust a STRUB_AT_CALLS function TYPE, adding a watermark pointer if it + hasn't been added yet. Return the named argument count. */ +int +pass_ipa_strub::adjust_at_calls_type (tree type) +{ + int named_args = 0; + + if (!TYPE_ARG_TYPES (type)) + return named_args; + + tree *tlist = &TYPE_ARG_TYPES (type); + tree qpwmptrt = get_qpwmt (); + while (*tlist && TREE_VALUE (*tlist) != void_type_node) + { + /* The type has alreayd been adjusted. */ + if (TREE_VALUE (*tlist) == qpwmptrt) + return named_args; + named_args++; + *tlist = tree_cons (TREE_PURPOSE (*tlist), + TREE_VALUE (*tlist), + TREE_CHAIN (*tlist)); + tlist = &TREE_CHAIN (*tlist); } - if (any_strub) - last_cgraph_order = symtab->order; + /* Add the new argument after all named arguments, so as to not mess with + attributes that reference parameters. */ + *tlist = tree_cons (NULL_TREE, get_qpwmt (), *tlist); - return any_strub; +#if ATTR_FNSPEC_DECONST_WATERMARK + if (!type_already_adjusted) + { + int flags = flags_from_decl_or_type (type); + tree fnspec = lookup_attribute ("fn spec", type); + + 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, if needed, before adding + parameters. */ + TYPE_ATTRIBUTES (type) + = tree_cons (get_identifier ("fn spec"), + build_tree_list (NULL_TREE, + build_string (tgtlen, nspec)), + TYPE_ATTRIBUTES (type)); + } + } +#endif + + return named_args; +} + +void +pass_ipa_strub::adjust_at_calls_call (cgraph_edge *e, int named_args) +{ + gcall *ocall = e->call_stmt; + gimple_stmt_iterator gsi = gsi_for_stmt (ocall); + + /* Make sure we haven't modified this call yet. */ + gcc_checking_assert (!(int (gimple_call_num_args (ocall)) > named_args + && (TREE_TYPE (gimple_call_arg (ocall, named_args)) + == get_pwmt ()))); + + /* ??? If it's a (tail?) call within a strub context, maybe pass on + the strub watermark instead of wrapping the call. */ + + /* Initialize the watermark before the call. */ + tree swm = create_tmp_var (get_wmt (), ".strub.watermark"); + TREE_ADDRESSABLE (swm) = true; + tree swmp = build1 (ADDR_EXPR, get_pwmt (), swm); + + tree enter = get_enter (); + gcall *stptr = gimple_build_call (enter, 1, + unshare_expr (swmp)); + gsi_insert_before (&gsi, stptr, GSI_SAME_STMT); +#if !IMPLICIT_CGRAPH_EDGES + e->caller->create_edge (cgraph_node::get_create (enter), + stptr, gsi_bb (gsi)->count, false); +#endif + + /* Replace the call with one that passes the swmp argument first. */ + gcall *wrcall; + { gcall *stmt = ocall; + // Mostly copied from gimple_call_copy_skip_args. + int i = 0; + int nargs = gimple_call_num_args (stmt); + auto_vec vargs (MAX (nargs, named_args) + 1); + gcall *new_stmt; + + /* pr71109.c calls a prototypeless function, then defines it with + additional arguments. It's ill-formed, but after it's inlined, + it somehow works out. */ + for (; i < named_args && i < nargs; i++) + vargs.quick_push (gimple_call_arg (stmt, i)); + for (; i < named_args; i++) + vargs.quick_push (null_pointer_node); + + vargs.quick_push (unshare_expr (swmp)); + + for (; i < nargs; i++) +#if 0 + if (!bitmap_bit_p (args_to_skip, i)) +#endif + vargs.quick_push (gimple_call_arg (stmt, i)); + + if (gimple_call_internal_p (stmt)) +#if 0 + /* + new_stmt = gimple_build_call_internal_vec (gimple_call_internal_fn (stmt), + vargs); + */ +#endif + gcc_unreachable (); + else + new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs); + + if (gimple_call_lhs (stmt)) + gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); + +#if 0 + gimple_set_vuse (new_stmt, gimple_vuse (stmt)); + gimple_set_vdef (new_stmt, gimple_vdef (stmt)); +#else + gimple_move_vops (new_stmt, stmt); +#endif + + if (gimple_has_location (stmt)) + gimple_set_location (new_stmt, gimple_location (stmt)); + gimple_call_copy_flags (new_stmt, stmt); + gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); + + gimple_set_modified (new_stmt, true); + + wrcall = new_stmt; + } + + update_stmt (wrcall); + gsi_replace (&gsi, wrcall, true); + cgraph_edge::set_call_stmt (e, wrcall, false); + + /* Insert the strub code after the call. */ + gimple_seq seq = NULL; + + { +#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, + unshare_expr (swmp)); + gimple_seq_add_stmt (&seq, sleave); + + gassign *clobber = gimple_build_assign (swm, + build_clobber + (TREE_TYPE (swm))); + gimple_seq_add_stmt (&seq, clobber); + } + + gsi_insert_finally_seq_after_call (gsi, seq); } unsigned int pass_ipa_strub_mode::execute (function *) { last_cgraph_order = 0; - bool any_strub = ipa_strub_set_mode_for_new_functions (); + ipa_strub_set_mode_for_new_functions (); - if (!any_strub) - flag_strub = 0; - else - /* Verify before any inlining or other transformations. */ - verify_strub (); + /* Verify before any inlining or other transformations. */ + verify_strub (); return 0; } @@ -1474,52 +1739,50 @@ pass_ipa_strub::execute (function *) ipa_strub_set_mode_for_new_functions (); + /* First, adjust the signature of at-calls functions. We adjust types of + at-calls functions first, so that we don't modify types in place unless + strub is explicitly requested. */ FOR_EACH_FUNCTION (onode) { enum strub_mode mode = get_strub_mode (onode); - if (mode == STRUB_AT_CALLS) + if (mode == STRUB_AT_CALLS + || mode == STRUB_AT_CALLS_OPT) { - int named_args = 0; - - /* Adjust the signature, and all callers. Add the new - argument after all named arguments, so as to not mess with - attr_fnspec or any other attributes that reference - parameters. */ - TREE_TYPE (onode->decl) = build_distinct_type_copy (TREE_TYPE - (onode->decl)); + /* Create a type variant if strubbing was not explicitly requested in + the function type. */ + if (get_strub_mode_from_type (TREE_TYPE (onode->decl)) != mode) + TREE_TYPE (onode->decl) = build_distinct_type_copy (TREE_TYPE + (onode->decl)); + + int named_args = adjust_at_calls_type (TREE_TYPE (onode->decl)); + + /* An external function explicitly declared with strub won't have a + body. Even with implicit at-calls strub, a function may have had its + body removed after we selected the mode, and then we have nothing + further to do. */ + if (!onode->has_gimple_body_p ()) + continue; tree *pargs = &DECL_ARGUMENTS (onode->decl); /* A noninterposable_alias reuses the same parm decl chain, don't add - the parm twice. We still have to adjust the type. */ + the parm twice. */ bool aliased_parms = (onode->alias && *pargs && DECL_CONTEXT (*pargs) != onode->decl); - if (TYPE_ARG_TYPES (TREE_TYPE (onode->decl))) - { - tree *tlist = &TYPE_ARG_TYPES (TREE_TYPE (onode->decl)); - while (*pargs) - { - named_args++; - *tlist = tree_cons (TREE_PURPOSE (*tlist), - TREE_VALUE (*tlist), - TREE_CHAIN (*tlist)); - tlist = &TREE_CHAIN (*tlist); - pargs = &DECL_CHAIN (*pargs); - } - *tlist = tree_cons (NULL_TREE, get_qpptr (), *tlist); - } - if (aliased_parms) continue; + for (int i = 0; i < named_args; i++) + pargs = &DECL_CHAIN (*pargs); + tree wmptr = build_decl (DECL_SOURCE_LOCATION (onode->decl), PARM_DECL, get_watermark_ptr (), - get_qpptr ()); + get_qpwmt ()); DECL_ARTIFICIAL (wmptr) = 1; - DECL_ARG_TYPE (wmptr) = get_qpptr (); + DECL_ARG_TYPE (wmptr) = get_qpwmt (); DECL_CONTEXT (wmptr) = onode->decl; TREE_USED (wmptr) = 1; DECL_CHAIN (wmptr) = *pargs; @@ -1528,197 +1791,17 @@ pass_ipa_strub::execute (function *) if (onode->alias) continue; +#if 0 /* Calls are now adjusted when examining callers. */ unsigned c; cgraph_edge *e; FOR_EACH_VEC_ELT (onode->collect_callers (), c, e) { push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl)); - - gcall *ocall = e->call_stmt; - gimple_stmt_iterator gsi = gsi_for_stmt (ocall); - - /* ??? If it's a (tail?) call within a strub context, maybe pass on - the strub watermark instead of wrapping the call. */ - - /* Initialize the watermark before the call. */ - tree swm = create_tmp_var (ptr_type_node, ".strub.watermark"); - TREE_ADDRESSABLE (swm) = true; - tree swmp = build1 (ADDR_EXPR, get_pptr (), swm); - - tree enter = get_enter (); - gcall *stptr = gimple_build_call (enter, 1, - unshare_expr (swmp)); - gsi_insert_before (&gsi, stptr, GSI_SAME_STMT); -#if !IMPLICIT_CGRAPH_EDGES - e->caller->create_edge (cgraph_node::get_create (enter), - stptr, gsi_bb (gsi)->count, false); -#endif - - /* Replace the call with one that passes the swmp argument first. */ - gcall *wrcall; - { gcall *stmt = ocall; - // Mostly copied from gimple_call_copy_skip_args. - int i = 0; - int nargs = gimple_call_num_args (stmt); - auto_vec vargs (MAX (nargs, named_args) + 1); - gcall *new_stmt; - - /* pr71109.c calls a prototypeless function, then defines it with - additional arguments. It's ill-formed, but after it's inlined, - it somehow works out. */ - for (; i < named_args && i < nargs; i++) - vargs.quick_push (gimple_call_arg (stmt, i)); - for (; i < named_args; i++) - vargs.quick_push (null_pointer_node); - - vargs.quick_push (unshare_expr (swmp)); - - for (; i < nargs; i++) -#if 0 - if (!bitmap_bit_p (args_to_skip, i)) -#endif - vargs.quick_push (gimple_call_arg (stmt, i)); - - if (gimple_call_internal_p (stmt)) -#if 0 - /* - new_stmt = gimple_build_call_internal_vec (gimple_call_internal_fn (stmt), - vargs); - */ -#endif - gcc_unreachable (); - else - new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs); - - if (gimple_call_lhs (stmt)) - gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); - -#if 0 - gimple_set_vuse (new_stmt, gimple_vuse (stmt)); - gimple_set_vdef (new_stmt, gimple_vdef (stmt)); -#else - gimple_move_vops (new_stmt, stmt); -#endif - - if (gimple_has_location (stmt)) - gimple_set_location (new_stmt, gimple_location (stmt)); - gimple_call_copy_flags (new_stmt, stmt); - gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); - - gimple_set_modified (new_stmt, true); - - wrcall = new_stmt; - } - - update_stmt (wrcall); - gsi_replace (&gsi, wrcall, true); - cgraph_edge::set_call_stmt (e, wrcall, false); - - /* Insert the strub code after the call. */ - gimple_seq seq = NULL; - - { -#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, - unshare_expr (swmp)); - gimple_seq_add_stmt (&seq, sleave); - - gassign *clobber = gimple_build_assign (swm, - build_clobber - (TREE_TYPE (swm))); - gimple_seq_add_stmt (&seq, clobber); - } - - gsi_insert_finally_seq_after_call (gsi, seq); - + adjust_at_calls_call (e, named_args); 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; - cgraph_node *nnode = onode; push_cfun (DECL_STRUCT_FUNCTION (nnode->decl)); @@ -1758,11 +1841,61 @@ pass_ipa_strub::execute (function *) #if 0 compute_fn_summary (onode, true); #endif - continue; + } + } + + FOR_EACH_FUNCTION (onode) + { + if (!onode->has_gimple_body_p ()) + continue; + + /* Adjust unknown-callee indirect calls with STRUB_AT_CALLS types within + onode. */ + if (onode->indirect_calls) + { + push_cfun (DECL_STRUCT_FUNCTION (onode->decl)); + for (cgraph_edge *e = onode->indirect_calls; e; e = e->next_callee) + { + gcc_checking_assert (e->indirect_unknown_callee); + + 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_AT_CALLS + && callee_mode != STRUB_AT_CALLS_OPT) + continue; + + int named_args = adjust_at_calls_type (callee_fntype); + + adjust_at_calls_call (e, named_args); + } + pop_cfun (); + } + + if (onode->callees) + { + push_cfun (DECL_STRUCT_FUNCTION (onode->decl)); + for (cgraph_edge *e = onode->callees; e; e = e->next_callee) + { + gcc_checking_assert (!e->indirect_unknown_callee); + + enum strub_mode callee_mode = get_strub_mode (e->callee); + + if (callee_mode != STRUB_AT_CALLS + && callee_mode != STRUB_AT_CALLS_OPT) + continue; + + int named_args = adjust_at_calls_type (TREE_TYPE (e->callee->decl)); + + adjust_at_calls_call (e, named_args); + } + pop_cfun (); } - if (mode != STRUB_INTERNAL - || !onode->has_gimple_body_p ()) + enum strub_mode mode = get_strub_mode (onode); + + if (mode != STRUB_INTERNAL) continue; #if 0 @@ -1772,6 +1905,7 @@ pass_ipa_strub::execute (function *) &DECL_ATTRIBUTES (onode->decl)); #endif +#if 0 if (!DECL_STRUCT_FUNCTION (onode->decl)) { inform (DECL_SOURCE_LOCATION (onode->decl), @@ -1799,6 +1933,7 @@ pass_ipa_strub::execute (function *) onode->decl); continue; } +#endif bool is_stdarg = calls_builtin_va_start_p (onode);; bool apply_args = calls_builtin_apply_args_p (onode); @@ -1835,7 +1970,7 @@ pass_ipa_strub::execute (function *) ipa_adjusted_param wmadj = {}; wmadj.op = IPA_PARAM_OP_NEW; - wmadj.type = get_qpptr (); + wmadj.type = get_qpwmt (); vec_safe_push (nparms, wmadj); } ipa_param_adjustments adj (nparms, -1, false); @@ -2497,9 +2632,9 @@ pass_ipa_strub::execute (function *) gcall *wrcall = as_a (gsi_stmt (gsi)); - tree swm = create_tmp_var (ptr_type_node, ".strub.watermark"); + tree swm = create_tmp_var (get_wmt (), ".strub.watermark"); TREE_ADDRESSABLE (swm) = true; - tree swmp = build1 (ADDR_EXPR, get_pptr (), swm); + tree swmp = build1 (ADDR_EXPR, get_pwmt (), swm); tree enter = get_enter (); gcall *stptr = gimple_build_call (enter, 1, unshare_expr (swmp)); diff --git a/gcc/testsuite/c-c++-common/strub-O0.c b/gcc/testsuite/c-c++-common/strub-O0.c new file mode 100644 index 00000000000..6afe0fd5de1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O0.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O0 -fstrub=default -fdump-rtl-expand" } */ + +/* At -O0, none of the strub builtins are expanded inline. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O1.c b/gcc/testsuite/c-c++-common/strub-O1.c new file mode 100644 index 00000000000..1cdeaecaf32 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fstrub=default -fdump-rtl-expand" } */ + +/* At -O1, without -fno-inline, we fully expand enter, but neither update nor + leave. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O2.c b/gcc/testsuite/c-c++-common/strub-O2.c new file mode 100644 index 00000000000..9cc39e763b1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O2.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=default -fdump-rtl-expand" } */ + +/* At -O2, without -fno-inline, we fully expand enter and update, and add a test + around the leave call. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump "\n\[(\]call_insn\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O2fni.c b/gcc/testsuite/c-c++-common/strub-O2fni.c new file mode 100644 index 00000000000..51cae845d5f --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O2fni.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=default -fdump-rtl-expand -fno-inline-functions" } */ + +/* With -fno-inline-functions, none of the strub builtins are inlined. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\n\[(\]call_insn\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O3.c b/gcc/testsuite/c-c++-common/strub-O3.c new file mode 100644 index 00000000000..1fcde345d36 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O3.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fstrub=default -fdump-rtl-expand" } */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_leave" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O3fni.c b/gcc/testsuite/c-c++-common/strub-O3fni.c new file mode 100644 index 00000000000..8f67b613be8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O3fni.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fstrub=default -fdump-rtl-expand -fno-inline" } */ + +/* With -fno-inline-functions, none of the strub builtins are inlined. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\n\[(\]call_insn\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-Og.c b/gcc/testsuite/c-c++-common/strub-Og.c new file mode 100644 index 00000000000..6f60349573f --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-Og.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-Og -fstrub=default -fdump-rtl-expand" } */ + +/* At -Og, without -fno-inline, we fully expand enter, but neither update nor + leave. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\n\[(\]call_insn\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-Os.c b/gcc/testsuite/c-c++-common/strub-Os.c new file mode 100644 index 00000000000..5d1c08a7528 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-Os.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -fstrub=default -fdump-rtl-expand" } */ + +/* At -Os, without -fno-inline, we fully expand enter, and also update. The + expanded update might be larger than a call proper, but argument saving and + restoring required by the call will most often make it larger. The leave + call is left untouched. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\n\[(\]call_insn\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-all1.c b/gcc/testsuite/c-c++-common/strub-all1.c new file mode 100644 index 00000000000..54daf84656c --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-all1.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=all -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the + strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ +static void +__attribute__ ((__always_inline__)) +h() { /* { dg-warning "might not be inlinable" } */ +} + +/* g becomes STRUB_AT_CALLS, because of the flag. */ +static inline void +g() { + h(); +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]3\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]1\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 1 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]1\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-1\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-2\[)\]" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-all2.c b/gcc/testsuite/c-c++-common/strub-all2.c new file mode 100644 index 00000000000..f377541cff0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-all2.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=all -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* g becomes STRUB_INTERNAL, because of the flag. Without inline, force_output + is set for static non-inline functions when not optimizing, and that keeps + only_called_directly_p from returning true, which makes STRUB_AT_CALLS + non-viable. */ +static void +g() { +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-1\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-2\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-at-calls1.c b/gcc/testsuite/c-c++-common/strub-at-calls1.c new file mode 100644 index 00000000000..0d1b9fce833 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-at-calls1.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=at-calls -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the + strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ +static void +__attribute__ ((__always_inline__)) +h() { /* { dg-warning "might not be inlinable" } */ +} + +/* g becomes STRUB_AT_CALLS, because of the flag. */ +static inline void +g() { + h(); +} + +/* f does NOT become STRUB_AT_CALLS because it is visible; it becomes + STRUB_CALLABLE. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]1\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]3\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]1\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]3\[)\]" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-at-calls2.c b/gcc/testsuite/c-c++-common/strub-at-calls2.c new file mode 100644 index 00000000000..530eee36d06 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-at-calls2.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=at-calls -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* g does NOT become STRUB_AT_CALLS because it's not viable. Without inline, + force_output is set for static non-inline functions when not optimizing, and + that keeps only_called_directly_p from returning true, which makes + STRUB_AT_CALLS non-viable. It becomes STRUB_CALLABLE instead. */ +static void +g() { +} + +/* f does NOT become STRUB_AT_CALLS because it is visible; it becomes + STRUB_CALLABLE. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]3\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]3\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-default1.c b/gcc/testsuite/c-c++-common/strub-default1.c new file mode 100644 index 00000000000..a1e1803aadc --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-default1.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strubm -fdump-ipa-strub" } */ + +static int __attribute__ ((__strub__)) var; + +/* h becomes STRUB_STRUB_INLINABLE, because of the use of the strub variable, + and the always_inline flag. It would get inlined before pass_ipa_strub, if + it weren't for the error. */ +static void +__attribute__ ((__always_inline__)) +h() { /* { dg-warning "might not be inlinable" } */ + var++; +} + +/* g becomes STRUB_AT_CALLS_OPT, because of the use of the strub variable, and + the viability of at-calls strubbing. */ +static inline void +g() { + var--; + h(); +} + +/* f becomes STRUB_INTERNAL because of the use of the strub variable, and gets + split into STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + var++; + g(); /* { dg-error "calling non-strub" } */ +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-3\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-4\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 1 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-3\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-4\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-1\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-2\[)\]" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-default2.c b/gcc/testsuite/c-c++-common/strub-default2.c new file mode 100644 index 00000000000..e8be1aeef10 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-default2.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strubm -fdump-ipa-strub" } */ + +static int __attribute__ ((__strub__)) var; + +/* g becomes STRUB_INTERNAL because of the use of the strub variable, and gets + split into STRUB_WRAPPER and STRUB_WRAPPED. It's not STRUB_AT_CALLS_OPT + because force_output is set for static non-inline functions when not + optimizing, and that keeps only_called_directly_p from returning true, which + makes STRUB_AT_CALLS[_OPT] non-viable. */ +static void +g() { + var--; +} + +/* f becomes STRUB_INTERNAL because of the use of the strub variable, and gets + split into STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + var++; + g(); /* { dg-error "calling non-strub" } */ +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-1\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-2\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-internal1.c b/gcc/testsuite/c-c++-common/strub-internal1.c new file mode 100644 index 00000000000..b9bd787df0a --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-internal1.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=internal -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the + strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ +static void +__attribute__ ((__always_inline__)) +h() { /* { dg-warning "might not be inlinable" } */ +} + +/* g becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +static inline void +g() { + h(); +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]3\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-1\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-2\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-internal2.c b/gcc/testsuite/c-c++-common/strub-internal2.c new file mode 100644 index 00000000000..a6e69357b23 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-internal2.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=internal -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* g becomes STRUB_INTERNAL, because of the flag. */ +static void +g() { +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-1\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]-2\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data1.c b/gcc/testsuite/c-c++-common/torture/strub-data1.c new file mode 100644 index 00000000000..62a03891ab6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data1.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* The pointed-to data enables strubbing if accessed. */ +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data2.c b/gcc/testsuite/c-c++-common/torture/strub-data2.c new file mode 100644 index 00000000000..9b7df13a280 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* The pointer itself is a strub variable, enabling internal strubbing when + its value is used. */ +int __attribute__ ((__strub__)) *ptr; + +int *f() { + return ptr; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data3.c b/gcc/testsuite/c-c++-common/torture/strub-data3.c new file mode 100644 index 00000000000..515706caa32 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data3.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* The pointer itself is a strub variable, that would enable internal strubbing + if its value was used. Here, it's only overwritten, so no strub. */ +int __attribute__ ((__strub__)) var; + +void f() { + var = 0; +} + +/* { dg-final { scan-ipa-dump-not "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data4.c b/gcc/testsuite/c-c++-common/torture/strub-data4.c new file mode 100644 index 00000000000..0ec9e35429f --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data4.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* The pointer itself is a strub variable, that would enable internal strubbing + if its value was used. Here, it's only overwritten, so no strub. */ +int __attribute__ ((__strub__)) *ptr; + +void f() { + ptr = 0; +} + +/* { dg-final { scan-ipa-dump-not "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data5.c b/gcc/testsuite/c-c++-common/torture/strub-data5.c new file mode 100644 index 00000000000..251790d4bbb --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data5.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -Werror" } */ + +/* The pointer itself is a strub variable, that would enable internal strubbing + if its value was used. Here, it's only overwritten, so no strub. */ +typedef int __attribute__ ((__strub__)) strub_int; +strub_int *ptr; + +int *f () { + return ptr; /* { dg-warn "incompatible" } */ +} + +strub_int *g () { + return f (); /* { dg-warn "incompatible" } */ +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-indcall1.c b/gcc/testsuite/c-c++-common/torture/strub-indcall1.c new file mode 100644 index 00000000000..b8adf8009e8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-indcall1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +typedef void __attribute__ ((__strub__)) fntype (); +fntype (*ptr); + +void f() { + ptr (); +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "(&\.strub\.watermark\.\[0-9\]\+)" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-indcall2.c b/gcc/testsuite/c-c++-common/torture/strub-indcall2.c new file mode 100644 index 00000000000..5b2c35ad6a7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-indcall2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +typedef void __attribute__ ((__strub__)) fntype (int, int); +fntype (*ptr); + +void f() { + ptr (0, 0); +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "(0, 0, &\.strub\.watermark\.\[0-9\]\+)" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-indcall3.c b/gcc/testsuite/c-c++-common/torture/strub-indcall3.c new file mode 100644 index 00000000000..5ee50456dc9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-indcall3.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +typedef void __attribute__ ((__strub__)) fntype (int, int, ...); +fntype (*ptr); + +void f() { + ptr (0, 0, 1, 1); +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "(0, 0, &\.strub\.watermark\.\[0-9\]\+, 1, 1)" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/g++.dg/torture/strub-init1.C b/gcc/testsuite/g++.dg/torture/strub-init1.C new file mode 100644 index 00000000000..e51ae802be4 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/strub-init1.C @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +extern int __attribute__((__strub__)) initializer (); + +int f() { + static int x = initializer (); + return x; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/g++.dg/torture/strub-init2.C b/gcc/testsuite/g++.dg/torture/strub-init2.C new file mode 100644 index 00000000000..edcb7bf8ad2 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/strub-init2.C @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +extern int __attribute__((__strub__)) initializer (); + +static int x = initializer (); + +int f() { + return x; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/g++.dg/torture/strub-init3.C b/gcc/testsuite/g++.dg/torture/strub-init3.C new file mode 100644 index 00000000000..bacf823ca4e --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/strub-init3.C @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +extern int __attribute__((__strub__)) initializer (); + +int f() { + int x = initializer (); + return x; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */