From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2140) id C6409384F021; Thu, 5 Aug 2021 21:32:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C6409384F021 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)] more tests, red zones, and deferred strubbing X-Act-Checkin: gcc X-Git-Author: Alexandre Oliva X-Git-Refname: refs/users/aoliva/heads/strub X-Git-Oldrev: 5e4f646737086ddf6bdae3810b3595c2e8f95a42 X-Git-Newrev: e811a5b461a93219a3e84b7b50efc99ebc52f76d Message-Id: <20210805213241.C6409384F021@sourceware.org> Date: Thu, 5 Aug 2021 21:32:41 +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: Thu, 05 Aug 2021 21:32:41 -0000 https://gcc.gnu.org/g:e811a5b461a93219a3e84b7b50efc99ebc52f76d commit e811a5b461a93219a3e84b7b50efc99ebc52f76d Author: Alexandre Oliva Date: Thu Aug 5 18:32:13 2021 -0300 more tests, red zones, and deferred strubbing Diff: --- gcc/builtins.c | 86 +++- gcc/ipa-split.c | 7 + gcc/ipa-strub.c | 434 +++++++++++++-------- gcc/ipa-strub.h | 7 + gcc/testsuite/c-c++-common/strub-O2.c | 2 +- gcc/testsuite/c-c++-common/strub-O2fni.c | 6 +- gcc/testsuite/c-c++-common/strub-O3fni.c | 4 +- gcc/testsuite/c-c++-common/strub-Og.c | 2 +- gcc/testsuite/c-c++-common/strub-Os.c | 2 +- gcc/testsuite/c-c++-common/strub-all1.c | 4 +- gcc/testsuite/c-c++-common/strub-apply1.c | 15 + gcc/testsuite/c-c++-common/strub-apply2.c | 12 + gcc/testsuite/c-c++-common/strub-apply3.c | 8 + gcc/testsuite/c-c++-common/strub-apply4.c | 21 + gcc/testsuite/c-c++-common/strub-at-calls1.c | 4 +- gcc/testsuite/c-c++-common/strub-default1.c | 8 +- gcc/testsuite/c-c++-common/strub-defer-O1.c | 7 + gcc/testsuite/c-c++-common/strub-defer-O2.c | 8 + gcc/testsuite/c-c++-common/strub-defer-O3.c | 93 +++++ gcc/testsuite/c-c++-common/strub-defer-Os.c | 7 + gcc/testsuite/c-c++-common/strub-internal1.c | 4 +- gcc/testsuite/c-c++-common/strub-parms1.c | 48 +++ gcc/testsuite/c-c++-common/strub-parms2.c | 36 ++ gcc/testsuite/c-c++-common/strub-parms3.c | 58 +++ .../c-c++-common/torture/strub-callable1.c | 13 + .../c-c++-common/torture/strub-callable2.c | 264 +++++++++++++ gcc/testsuite/c-c++-common/torture/strub-const1.c | 18 + gcc/testsuite/c-c++-common/torture/strub-const2.c | 22 ++ gcc/testsuite/c-c++-common/torture/strub-const3.c | 13 + gcc/testsuite/c-c++-common/torture/strub-const4.c | 17 + gcc/testsuite/c-c++-common/torture/strub-data5.c | 6 +- gcc/testsuite/c-c++-common/torture/strub-pure1.c | 18 + gcc/testsuite/c-c++-common/torture/strub-pure2.c | 22 ++ gcc/testsuite/c-c++-common/torture/strub-pure3.c | 13 + gcc/testsuite/c-c++-common/torture/strub-pure4.c | 17 + gcc/testsuite/c-c++-common/torture/strub-run1.c | 85 ++++ gcc/testsuite/c-c++-common/torture/strub-run2.c | 75 ++++ gcc/testsuite/c-c++-common/torture/strub-run3.c | 75 ++++ .../g++.dg/{wrappers/strub1.C => strub-run1.C} | 1 + gcc/testsuite/g++.dg/wrappers/strub2.C | 22 -- gcc/testsuite/g++.dg/wrappers/strub3.C | 22 -- gcc/testsuite/g++.dg/wrappers/strub4.C | 18 - 42 files changed, 1362 insertions(+), 242 deletions(-) diff --git a/gcc/builtins.c b/gcc/builtins.c index f387d93974f..0341a0923b2 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-fold.h" #include "intl.h" #include "file-prefix-map.h" /* remap_macro_filename() */ +#include "ipa-strub.h" /* strub_watermark_parm() */ #include "gomp-constants.h" #include "omp-general.h" #include "tree-dfa.h" @@ -7926,7 +7927,23 @@ expand_builtin_strub_enter (tree exp) if (optimize < 1 || flag_no_inline) return NULL_RTX; - rtx stktop = expand_builtin_stack_address (); + rtx stktop = NULL_RTX; + +#if 1 || defined RED_ZONE_SIZE + if (tree wmptr = (optimize + ? strub_watermark_parm (current_function_decl) + : NULL_TREE)) + { + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + 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); + stktop = force_reg (ptr_mode, wmark); + } +#endif + + if (!stktop) + stktop = expand_builtin_stack_address (); tree wmptr = CALL_EXPR_ARG (exp, 0); tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); @@ -7952,6 +7969,52 @@ expand_builtin_strub_update (tree exp) rtx stktop = expand_builtin_stack_address (); +#ifdef RED_ZONE_SIZE + /* Here's how the strub enter, update and leave functions deal with red zones. + + If it weren't for red zones, update, called from within a strub context, + would bump the watermark to the top of the stack. Enter and leave, running + in the caller, would use the caller's top of stack address both to + initialize the watermark passed to the callee, and to start strubbing the + stack afterwards. + + Ideally, we'd update the watermark so as to cover the used amount of red + zone, and strub starting at the caller's other end of the (presumably + unused) red zone. Normally, only leaf functions use the red zone, but at + this point we can't tell whether a function is a leaf, nor can we tell how + much of the red zone it uses. Furthermore, some strub contexts may have + been inlined so that update and leave are called from the same stack frame, + and the strub builtins may all have been inlined, turning a strub function + into a leaf. + + So cleaning the range from the caller's stack pointer (one end of the red + zone) to the (potentially inlined) callee's (other end of the) red zone + could scribble over the caller's own red zone. + + We avoid this possibility by arranging for callers that are strub contexts + to use their own watermark as the strub starting point. So, if A calls B, + and B calls C, B will tell A to strub up to the end of B's red zone, and + will strub itself only the part of C's stack frame and red zone that + doesn't overlap with B's. With that, we don't need to know who's leaf and + who isn't: inlined calls will shrink their strub window to zero, each + remaining call will strub some portion of the stack, and eventually the + strub context will return to a caller that isn't a strub context itself, + that will therefore use its own stack pointer as the strub starting point. + It's not a leaf, because strub contexts can't be inlined into non-strub + contexts, so it doesn't use the red zone, and it will therefore correctly + strub up the callee's stack frame up to the end of the callee's red zone. + Neat! */ + if (true /* (flags_from_decl_or_type (current_function_decl) & ECF_LEAF) */) + { + poly_int64 red_zone_size = RED_ZONE_SIZE; +#if STACK_GROWS_DOWNWARD + red_zone_size = -red_zone_size; +#endif + stktop = plus_constant (ptr_mode, stktop, red_zone_size); + stktop = force_reg (ptr_mode, stktop); + } +#endif + tree wmptr = CALL_EXPR_ARG (exp, 0); tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, @@ -7982,7 +8045,23 @@ expand_builtin_strub_leave (tree exp) if (optimize < 2 || flag_no_inline) return NULL_RTX; - rtx stktop = expand_builtin_stack_address (); + rtx stktop = NULL_RTX; + +#if 1 || defined RED_ZONE_SIZE + if (tree wmptr = (optimize + ? strub_watermark_parm (current_function_decl) + : NULL_TREE)) + { + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + 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); + stktop = force_reg (ptr_mode, wmark); + } +#endif + + if (!stktop) + stktop = expand_builtin_stack_address (); tree wmptr = CALL_EXPR_ARG (exp, 0); tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); @@ -8000,6 +8079,9 @@ expand_builtin_strub_leave (tree exp) rtx end = stktop; #endif + /* We're going to modify it, so make sure it's not e.g. the stack pointer. */ + base = copy_to_reg (base); + rtx_code_label *done = gen_label_rtx (); do_compare_rtx_and_jump (base, end, LT, STACK_UNSIGNED, ptr_mode, NULL_RTX, done, NULL, diff --git a/gcc/ipa-split.c b/gcc/ipa-split.c index 5e918ee3fbf..e4ebae472f5 100644 --- a/gcc/ipa-split.c +++ b/gcc/ipa-split.c @@ -104,6 +104,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-fnsummary.h" #include "cfgloop.h" #include "attribs.h" +#include "ipa-strub.h" /* Per basic block info. */ @@ -1791,6 +1792,12 @@ execute_split_functions (void) "section.\n"); return 0; } + if (!strub_splittable_p (node)) + { + if (dump_file) + fprintf (dump_file, "Not splitting: function is a strub context.\n"); + return 0; + } /* We enforce splitting after loop headers when profile info is not available. */ diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c index e302f0ec1c5..05c91a6485c 100644 --- a/gcc/ipa-strub.c +++ b/gcc/ipa-strub.c @@ -210,10 +210,16 @@ get_strub_mode_from_attr (tree strub_attr, bool var_p = false) return mode; } +static enum strub_mode +get_strub_mode_from_decl (tree fndecl) +{ + return get_strub_mode_from_attr (get_strub_attr_from_decl (fndecl)); +} + static enum strub_mode get_strub_mode (cgraph_node *node) { - return get_strub_mode_from_attr (get_strub_attr_from_decl (node->decl)); + return get_strub_mode_from_decl (node->decl); } static enum strub_mode @@ -263,7 +269,7 @@ calls_builtin_apply_args_p (cgraph_node *node, bool report = false) break; sorry_at (gimple_location (e->call_stmt), - "at-calls strub does not support call to %qD", + "at-calls % does not support call to %qD", cdecl); } @@ -292,7 +298,8 @@ can_strub_p (cgraph_node *node, bool report = false) return result; sorry_at (DECL_SOURCE_LOCATION (node->decl), - "%qD is not eligible for strub because of attribute %", + "%qD is not eligible for %" + " because of attribute %", node->decl); } @@ -352,7 +359,7 @@ can_strub_internally_p (cgraph_node *node, bool report = false) return result; sorry_at (DECL_SOURCE_LOCATION (node->decl), - "%qD is not eligible for internal strub" + "%qD is not eligible for internal %" " because of attribute %", node->decl); } @@ -372,7 +379,7 @@ can_strub_internally_p (cgraph_node *node, bool report = false) return result; sorry_at (gimple_location (e->call_stmt), - "internal strub does not support call to %qD", + "internal % does not support call to %qD", cdecl); } @@ -411,7 +418,7 @@ can_strub_internally_p (cgraph_node *node, bool report = false) return result; sorry_at (gimple_location (label_stmt), - "internal strub does not support user labels"); + "internal % does not support user labels"); } } @@ -425,16 +432,16 @@ can_strub_internally_p (cgraph_node *node, bool report = false) return result; sorry_at (DECL_SOURCE_LOCATION (node->decl), - "%qD has too many arguments for internal strub", + "%qD has too many arguments for internal %", 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)); + /* 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 (!result || !node->has_gimple_body_p () + || tree_versionable_function_p (node->decl)); return result; } @@ -489,8 +496,22 @@ strub_callable_builtin_p (cgraph_node *node) 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 + /* This temporarily allocates stack for the call, and we can't reasonably + update the watermark for that. Besides, we don't check the actual call + target, nor its signature, and it seems to be overkill to as much as + try to do so. */ + case BUILT_IN_APPLY: + return false; + + /* Conversely, this shouldn't be called from within strub contexts, since + the caller may have had its signature modified. STRUB_INTERNAL is ok, + the call will remain in the STRUB_WRAPPER, and removed from the + STRUB_WRAPPED clone. */ + case BUILT_IN_APPLY_ARGS: + return false; + + /* ??? Make all other 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. @@ -698,7 +719,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr) { gcc_checking_assert (analyze_body); error_at (DECL_SOURCE_LOCATION (node->decl), - "%qD requires strub, but no viable strub mode was found", + "%qD requires %," + " but no viable % mode was found", node->decl); break; } @@ -751,7 +773,7 @@ set_strub_mode_to (cgraph_node *node, enum strub_mode mode) && mode == STRUB_INLINABLE)) { error_at (DECL_SOURCE_LOCATION (node->decl), - "strub mode %i selected for %qD, when %i was requested", + "% mode %i selected for %qD, when %i was requested", (int) mode, node->decl, (int) get_strub_mode_from_attr (attr)); if (node->alias) @@ -979,7 +1001,7 @@ verify_strub () if (callee_mode == STRUB_DISABLED || callee_mode == STRUB_INTERNAL) error_at (gimple_location (e->call_stmt), - "indirect non-strub call in strub context %qD", + "indirect non-% call in % context %qD", node->decl); } @@ -987,9 +1009,22 @@ verify_strub () { 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); + { + if (get_strub_mode (e->callee) == STRUB_INLINABLE) + error_at (gimple_location (e->call_stmt), + "calling % % %qD" + " in non-% context %qD", + e->callee->decl, node->decl); + else if (fndecl_built_in_p (e->callee->decl, BUILT_IN_APPLY_ARGS) + && get_strub_mode (node) == STRUB_INTERNAL) + /* This is ok, it will be kept in the STRUB_WRAPPER, and removed + from the STRUB_WRAPPED's strub context. */ + continue; + else + error_at (gimple_location (e->call_stmt), + "calling non-% %qD in % context %qD", + e->callee->decl, node->decl); + } } } @@ -1067,27 +1102,25 @@ public: /* 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 (wmt, build_variant_type_copy (ptr_type_node)) - DEF_TYPE (pwmt, build_pointer_type (get_wmt ())) + DEF_TYPE (pwmt, build_reference_type (get_wmt ())) DEF_TYPE (qpwmt, build_qualified_type (get_pwmt (), TYPE_QUAL_RESTRICT - | TYPE_QUAL_CONST)) + /* | TYPE_QUAL_CONST */)) - DEF_TYPE (pptr, build_pointer_type (ptr_type_node)) - - DEF_TYPE (qpptr, - build_qualified_type (get_pptr (), + DEF_TYPE (qptr, + build_qualified_type (ptr_type_node, TYPE_QUAL_RESTRICT | TYPE_QUAL_CONST)) DEF_TYPE (qpvalst, - build_qualified_type (build_pointer_type + build_qualified_type (build_reference_type (va_list_type_node), TYPE_QUAL_RESTRICT - | TYPE_QUAL_CONST)) + /* | TYPE_QUAL_CONST */)) #undef DEF_TYPE @@ -1159,12 +1192,13 @@ public: DEF_IDENT (watermark_ptr) DEF_IDENT (va_list_ptr) - DEF_IDENT (apply_args_ptr) + DEF_IDENT (apply_args) #undef DEF_IDENT static inline int adjust_at_calls_type (tree); static inline void adjust_at_calls_call (cgraph_edge *, int); + static inline void adjust_at_calls_calls (cgraph_node *); static inline gimple_seq call_update_watermark (tree wmptr, cgraph_node *node, profile_count count, @@ -1324,6 +1358,9 @@ add_call_edges_for_seq (gimple_seq seq, profile_count count) static void gsi_insert_finally_seq_after_call (gimple_stmt_iterator gsi, gimple_seq seq) { + if (!seq) + return; + gimple *stmt = gsi_stmt (gsi); gcall *call = is_a (stmt) ? as_a (stmt) : NULL; @@ -1493,6 +1530,64 @@ ipa_strub_set_mode_for_new_functions () last_cgraph_order = symtab->order; } +/* Return FALSE if NODE is a strub context, and TRUE otherwise. */ +bool +strub_splittable_p (cgraph_node *node) +{ + switch (get_strub_mode (node)) + { + case STRUB_WRAPPED: + case STRUB_AT_CALLS: + case STRUB_AT_CALLS_OPT: + case STRUB_INLINABLE: + case STRUB_INTERNAL: + case STRUB_WRAPPER: + return false; + + case STRUB_CALLABLE: + case STRUB_DISABLED: + break; + + default: + gcc_unreachable (); + } + + return true; +} + +/* Return the PARM_DECL of the incoming watermark pointer, if there is one. */ +tree +strub_watermark_parm (tree fndecl) +{ + switch (get_strub_mode_from_decl (fndecl)) + { + case STRUB_WRAPPED: + case STRUB_AT_CALLS: + case STRUB_AT_CALLS_OPT: + break; + + case STRUB_INTERNAL: + case STRUB_WRAPPER: + case STRUB_CALLABLE: + case STRUB_DISABLED: + case STRUB_INLINABLE: + return NULL_TREE; + + default: + gcc_unreachable (); + } + + for (tree parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm)) + if (TREE_TYPE (parm) == pass_ipa_strub::get_qpwmt ()) + { + gcc_checking_assert (DECL_NAME (parm) + == pass_ipa_strub::get_watermark_ptr ()); + return parm; + } + + gcc_unreachable (); +} + /* Adjust a STRUB_AT_CALLS function TYPE, adding a watermark pointer if it hasn't been added yet. Return the named argument count. */ int @@ -1507,7 +1602,7 @@ pass_ipa_strub::adjust_at_calls_type (tree type) tree qpwmptrt = get_qpwmt (); while (*tlist && TREE_VALUE (*tlist) != void_type_node) { - /* The type has alreayd been adjusted. */ + /* The type has already been adjusted. */ if (TREE_VALUE (*tlist) == qpwmptrt) return named_args; named_args++; @@ -1581,22 +1676,30 @@ pass_ipa_strub::adjust_at_calls_call (cgraph_edge *e, int 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); + /* If we're already within a strub context, pass on the incoming watermark + pointer, and omit the enter and leave calls around the modified call. */ + tree swmp = ((optimize_size || optimize > 2) + ? strub_watermark_parm (e->caller->decl) + : NULL_TREE); + bool omit_own_watermark = swmp; + tree swm = NULL_TREE; + if (!omit_own_watermark) + { + swm = create_tmp_var (get_wmt (), ".strub.watermark"); + TREE_ADDRESSABLE (swm) = true; + 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); + /* Initialize the watermark before the call. */ + 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); + 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; @@ -1661,59 +1764,114 @@ pass_ipa_strub::adjust_at_calls_call (cgraph_edge *e, int named_args) /* 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); - } - } + /* 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))) + { + if (!swm) + swm = build2 (MEM_REF, + TREE_TYPE (TREE_TYPE (swmp)), + swmp, + build_int_cst (TREE_TYPE (swmp), 0)); + + vec *inputs = NULL; + vec *outputs = NULL; + vec_safe_push (outputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (2, "=m")), + unshare_expr (swm))); + vec_safe_push (inputs, + build_tree_list + (build_tree_list + (NULL_TREE, build_string (1, "m")), + unshare_expr (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")), + unshare_expr (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); - } + if (!omit_own_watermark) + { + 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); } +void +pass_ipa_strub::adjust_at_calls_calls (cgraph_node *node) +{ + /* Adjust unknown-callee indirect calls with STRUB_AT_CALLS types within + onode. */ + if (node->indirect_calls) + { + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + for (cgraph_edge *e = node->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 (node->callees) + { + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + for (cgraph_edge *e = node->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 (); + } +} + unsigned int pass_ipa_strub_mode::execute (function *) { @@ -1849,54 +2007,13 @@ pass_ipa_strub::execute (function *) 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 (); - } - enum strub_mode mode = get_strub_mode (onode); if (mode != STRUB_INTERNAL) - continue; + { + adjust_at_calls_calls (onode); + continue; + } #if 0 /* Hmm, this is an i386-specific attribute. Do we need machine-specific @@ -1909,7 +2026,7 @@ pass_ipa_strub::execute (function *) if (!DECL_STRUCT_FUNCTION (onode->decl)) { inform (DECL_SOURCE_LOCATION (onode->decl), - "not splitting struct-less function %qD for stack scrubbing", + "not splitting struct-less function %qD for %", onode->decl); continue; } @@ -1917,7 +2034,7 @@ pass_ipa_strub::execute (function *) if (!onode->lowered) { inform (DECL_SOURCE_LOCATION (onode->decl), - "not splitting non-lowered function %qD for stack scrubbing", + "not splitting non-lowered function %qD for %", onode->decl); continue; } @@ -1929,7 +2046,7 @@ pass_ipa_strub::execute (function *) if (!tree_versionable_function_p (onode->decl)) { inform (DECL_SOURCE_LOCATION (onode->decl), - "%qD cannot be split for stack scrubbing", + "%qD cannot be split for %", onode->decl); continue; } @@ -1956,7 +2073,7 @@ pass_ipa_strub::execute (function *) { ipa_adjusted_param aaadj = {}; aaadj.op = IPA_PARAM_OP_NEW; - aaadj.type = get_qpptr (); + aaadj.type = get_qptr (); vec_safe_push (nparms, aaadj); } @@ -1982,7 +2099,7 @@ pass_ipa_strub::execute (function *) if (!nnode) { error_at (DECL_SOURCE_LOCATION (onode->decl), - "failed to split %qD for stack scrubbing", + "failed to split %qD for %", onode->decl); continue; } @@ -1996,6 +2113,8 @@ pass_ipa_strub::execute (function *) set_strub_mode_to (onode, STRUB_WRAPPER); set_strub_mode_to (nnode, STRUB_WRAPPED); + adjust_at_calls_calls (nnode); + /* Decide which of the wrapped function's parms we want to turn into references to the argument passed to the wrapper. In general, we want to copy small arguments, and avoid copying large ones. Variable-sized array @@ -2181,8 +2300,8 @@ pass_ipa_strub::execute (function *) only of reading because const/pure. */ if (apply_args) { - nspec[curlen++] = (no_writes_p ? 'r' : '.'); - nspec[curlen++] = (no_writes_p ? 't' : ' '); + nspec[curlen++] = 'r'; + nspec[curlen++] = ' '; } if (is_stdarg) { @@ -2485,24 +2604,24 @@ pass_ipa_strub::execute (function *) } { - tree aaptr = NULL_TREE; + tree aaval = NULL_TREE; tree vaptr = NULL_TREE; tree wmptr = NULL_TREE; for (tree arg = DECL_ARGUMENTS (nnode->decl); arg; arg = DECL_CHAIN (arg)) { - aaptr = vaptr; + aaval = vaptr; vaptr = wmptr; wmptr = arg; } if (!apply_args) - aaptr = NULL_TREE; + aaval = NULL_TREE; /* The trailing args are [apply_args], [va_list_ptr], and watermark. If we don't have a va_list_ptr, the penultimate argument is apply_args. */ else if (!is_stdarg) - aaptr = vaptr; + aaval = vaptr; if (!is_stdarg) vaptr = NULL_TREE; @@ -2522,10 +2641,10 @@ pass_ipa_strub::execute (function *) if (apply_args) { - DECL_NAME (aaptr) = get_apply_args_ptr (); - DECL_ARTIFICIAL (aaptr) = 1; - DECL_IGNORED_P (aaptr) = 1; - TREE_USED (aaptr) = 1; + DECL_NAME (aaval) = get_apply_args (); + DECL_ARTIFICIAL (aaval) = 1; + DECL_IGNORED_P (aaval) = 1; + TREE_USED (aaval) = 1; } push_cfun (DECL_STRUCT_FUNCTION (nnode->decl)); @@ -2582,9 +2701,10 @@ pass_ipa_strub::execute (function *) else if (fndecl && is_stdarg && fndecl_built_in_p (fndecl, BUILT_IN_VA_START)) { - if (builtin_decl_explicit (BUILT_IN_VA_START) != fndecl) - sorry_at (gimple_location (call), - "nonstandard stdarg conventions"); + /* Using a non-default stdarg ABI makes the function ineligible + for internal strub. */ + gcc_checking_assert (builtin_decl_explicit (BUILT_IN_VA_START) + == fndecl); tree bvacopy = builtin_decl_explicit (BUILT_IN_VA_COPY); gimple_call_set_fndecl (call, bvacopy); tree arg = vaptr; @@ -2607,7 +2727,9 @@ pass_ipa_strub::execute (function *) && fndecl_built_in_p (fndecl, BUILT_IN_APPLY_ARGS)) { tree lhs = gimple_call_lhs (call); - gassign *assign = gimple_build_assign (lhs, aaptr); + gimple *assign = (lhs + ? gimple_build_assign (lhs, aaval) + : gimple_build_nop ()); gsi_replace (&gsi, assign, true); cgraph_edge::remove (e); } @@ -2650,7 +2772,7 @@ pass_ipa_strub::execute (function *) if (apply_args) { - tree aalst = create_tmp_var (ptr_type_node, ".strub.appargs"); + tree aalst = create_tmp_var (ptr_type_node, ".strub.apply_args"); tree bappargs = builtin_decl_explicit (BUILT_IN_APPLY_ARGS); gcall *appargs = gimple_build_call (bappargs, 0); gimple_call_set_lhs (appargs, aalst); diff --git a/gcc/ipa-strub.h b/gcc/ipa-strub.h index cadbca5002a..7403c139d8c 100644 --- a/gcc/ipa-strub.h +++ b/gcc/ipa-strub.h @@ -23,3 +23,10 @@ along with GCC; see the file COPYING3. If not see doesn't have to be called directly by CALLER, but the returned value says nothing about intervening functions. */ extern bool strub_inlinable_p (cgraph_node *callee, cgraph_node *caller); + +/* Return FALSE if NODE is a strub context, and TRUE otherwise. */ +extern bool strub_splittable_p (cgraph_node *node); + +/* Locate and return the watermark_ptr parameter for FNDECL. If FNDECL is not a + strub context, return NULL. */ +extern tree strub_watermark_parm (tree fndecl); diff --git a/gcc/testsuite/c-c++-common/strub-O2.c b/gcc/testsuite/c-c++-common/strub-O2.c index 9cc39e763b1..7848c46d179 100644 --- a/gcc/testsuite/c-c++-common/strub-O2.c +++ b/gcc/testsuite/c-c++-common/strub-O2.c @@ -13,4 +13,4 @@ int f() { /* { 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" } } */ +/* { dg-final { scan-rtl-dump "\[(\]call\[^\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 index 51cae845d5f..85a8f76785e 100644 --- a/gcc/testsuite/c-c++-common/strub-O2fni.c +++ b/gcc/testsuite/c-c++-common/strub-O2fni.c @@ -1,7 +1,7 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fstrub=default -fdump-rtl-expand -fno-inline-functions" } */ +/* { dg-options "-O2 -fstrub=default -fdump-rtl-expand -fno-inline" } */ -/* With -fno-inline-functions, none of the strub builtins are inlined. */ +/* With -fno-inline, none of the strub builtins are inlined. */ int __attribute__ ((__strub__)) var; @@ -12,4 +12,4 @@ int f() { /* { 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" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O3fni.c b/gcc/testsuite/c-c++-common/strub-O3fni.c index 8f67b613be8..a2eedfd96b2 100644 --- a/gcc/testsuite/c-c++-common/strub-O3fni.c +++ b/gcc/testsuite/c-c++-common/strub-O3fni.c @@ -1,7 +1,7 @@ /* { dg-do compile } */ /* { dg-options "-O3 -fstrub=default -fdump-rtl-expand -fno-inline" } */ -/* With -fno-inline-functions, none of the strub builtins are inlined. */ +/* With -fno-inline, none of the strub builtins are inlined. */ int __attribute__ ((__strub__)) var; @@ -12,4 +12,4 @@ int f() { /* { 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" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\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 index 6f60349573f..e5cb1f60541 100644 --- a/gcc/testsuite/c-c++-common/strub-Og.c +++ b/gcc/testsuite/c-c++-common/strub-Og.c @@ -13,4 +13,4 @@ int f() { /* { 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" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\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 index 5d1c08a7528..194aacc2c05 100644 --- a/gcc/testsuite/c-c++-common/strub-Os.c +++ b/gcc/testsuite/c-c++-common/strub-Os.c @@ -15,4 +15,4 @@ int f() { /* { 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" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\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 index 54daf84656c..46e84bf6560 100644 --- a/gcc/testsuite/c-c++-common/strub-all1.c +++ b/gcc/testsuite/c-c++-common/strub-all1.c @@ -3,9 +3,9 @@ /* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ -static void +static inline void __attribute__ ((__always_inline__)) -h() { /* { dg-warning "might not be inlinable" } */ +h() { } /* g becomes STRUB_AT_CALLS, because of the flag. */ diff --git a/gcc/testsuite/c-c++-common/strub-apply1.c b/gcc/testsuite/c-c++-common/strub-apply1.c new file mode 100644 index 00000000000..f180b17f30e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default" } */ + +void __attribute__ ((__strub__ (3))) +apply_function (void *args) +{ + __builtin_apply (0, args, 0); +} + +void __attribute__ ((__strub__ (2))) +apply_args (int i, int j, double d) +{ + void *args = __builtin_apply_args (); + apply_function (args); +} diff --git a/gcc/testsuite/c-c++-common/strub-apply2.c b/gcc/testsuite/c-c++-common/strub-apply2.c new file mode 100644 index 00000000000..379a54b73b7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply2.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default" } */ + +extern void __attribute__ ((__strub__)) +apply_function (void *args); + +void __attribute__ ((__strub__)) +apply_args (int i, int j, double d) /* { dg-error "selected" } */ +{ + void *args = __builtin_apply_args (); /* { dg-message "does not support" } */ + apply_function (args); +} diff --git a/gcc/testsuite/c-c++-common/strub-apply3.c b/gcc/testsuite/c-c++-common/strub-apply3.c new file mode 100644 index 00000000000..9b4786be698 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply3.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default" } */ + +void __attribute__ ((__strub__)) +apply_function (void *args) +{ + __builtin_apply (0, args, 0); /* { dg-error "in .strub. context" } */ +} diff --git a/gcc/testsuite/c-c++-common/strub-apply4.c b/gcc/testsuite/c-c++-common/strub-apply4.c new file mode 100644 index 00000000000..409f747743e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply4.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=default -fdump-ipa-strubm" } */ + +/* Check that implicit enabling of strub mode selects internal strub when the + function uses __builtin_apply_args, that prevents the optimization to + at-calls mode. */ + +int __attribute__ ((__strub__)) var; + +static inline void +apply_args (int i, int j, double d) +{ + var++; + __builtin_apply_args (); +} + +void f() { + apply_args (1, 2, 3); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]2\[)\]" 1 "strubm" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-at-calls1.c b/gcc/testsuite/c-c++-common/strub-at-calls1.c index 0d1b9fce833..d964b07ae5d 100644 --- a/gcc/testsuite/c-c++-common/strub-at-calls1.c +++ b/gcc/testsuite/c-c++-common/strub-at-calls1.c @@ -3,9 +3,9 @@ /* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ -static void +static inline void __attribute__ ((__always_inline__)) -h() { /* { dg-warning "might not be inlinable" } */ +h() { } /* g becomes STRUB_AT_CALLS, because of the flag. */ diff --git a/gcc/testsuite/c-c++-common/strub-default1.c b/gcc/testsuite/c-c++-common/strub-default1.c index a1e1803aadc..d579ec62f55 100644 --- a/gcc/testsuite/c-c++-common/strub-default1.c +++ b/gcc/testsuite/c-c++-common/strub-default1.c @@ -3,12 +3,12 @@ static int __attribute__ ((__strub__)) var; -/* h becomes STRUB_STRUB_INLINABLE, because of the use of the strub variable, +/* h becomes 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 +static inline void __attribute__ ((__always_inline__)) -h() { /* { dg-warning "might not be inlinable" } */ +h() { var++; } @@ -34,7 +34,7 @@ f() { /* { 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 \[(\]-3\[)\]" 1 "strub" } } */ /* { 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-defer-O1.c b/gcc/testsuite/c-c++-common/strub-defer-O1.c new file mode 100644 index 00000000000..7b04eea35d9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-O1.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default -O1" } */ + +/* Check that a strub function called by another strub function does NOT defer + the strubbing to its caller at -O1. */ + +#include "strub-defer-O2.c" diff --git a/gcc/testsuite/c-c++-common/strub-defer-O2.c b/gcc/testsuite/c-c++-common/strub-defer-O2.c new file mode 100644 index 00000000000..67d96419a5e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-O2.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default -O2" } */ + +/* Check that a strub function called by another strub function does NOT defer + the strubbing to its caller at -O2. */ + +#define EXPECT_DEFERRAL ! +#include "strub-defer-O3.c" diff --git a/gcc/testsuite/c-c++-common/strub-defer-O3.c b/gcc/testsuite/c-c++-common/strub-defer-O3.c new file mode 100644 index 00000000000..34828d2711e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-O3.c @@ -0,0 +1,93 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default -O3" } */ + +/* Check that a strub function called by another strub function defers the + strubbing to its caller at -O3. */ + +#ifndef EXPECT_DEFERRAL +# define EXPECT_DEFERRAL +#endif + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +static inline __attribute__ ((__always_inline__, __strub__ (3))) +char * +leak_string (void) +{ + /* We use this variable to avoid any stack red zone. Stack scrubbing covers + it, but __builtin_stack_address, that we take as a reference, doesn't, so + if e.g. callable() were to store the string in the red zone, we wouldn't + find it because it would be outside the range we searched. */ + typedef void __attribute__ ((__strub__ (3))) callable_t (char *); + callable_t *f = 0; + + char s[sizeof (test_string)]; + __builtin_strcpy (s, test_string); + asm ("" : "+m" (s), "+r" (f)); + + if (__builtin_expect (!f, 1)) + return (char*)__builtin_stack_address (); + + f (s); + return 0; +} + +static inline __attribute__ ((__always_inline__, __strub__ (3))) +int +look_for_string (char *e) +{ + char *p = (char*)__builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__strub__ (1), __noinline__, __noclone__)) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (1))) +char * +deferred_at_calls () +{ + char *ret = at_calls (); + if (EXPECT_DEFERRAL !look_for_string (ret)) + __builtin_abort (); + return ret; +} + +static __attribute__ ((__strub__ (2))) +char * +deferred_internal () +{ + char *ret = at_calls (); + if (EXPECT_DEFERRAL !look_for_string (ret)) + __builtin_abort (); + return ret; +} + +int main () +{ + if (look_for_string (deferred_at_calls ())) + __builtin_abort (); + if (look_for_string (deferred_internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/strub-defer-Os.c b/gcc/testsuite/c-c++-common/strub-defer-Os.c new file mode 100644 index 00000000000..b273660aea1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-Os.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default -Os" } */ + +/* Check that a strub function called by another strub function defers the + strubbing to its caller at -Os. */ + +#include "strub-defer-O3.c" diff --git a/gcc/testsuite/c-c++-common/strub-internal1.c b/gcc/testsuite/c-c++-common/strub-internal1.c index b9bd787df0a..a74658c9ac9 100644 --- a/gcc/testsuite/c-c++-common/strub-internal1.c +++ b/gcc/testsuite/c-c++-common/strub-internal1.c @@ -3,9 +3,9 @@ /* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ -static void +static inline void __attribute__ ((__always_inline__)) -h() { /* { dg-warning "might not be inlinable" } */ +h() { } /* g becomes STRUB_INTERNAL because of the flag, and gets split into diff --git a/gcc/testsuite/c-c++-common/strub-parms1.c b/gcc/testsuite/c-c++-common/strub-parms1.c new file mode 100644 index 00000000000..0422ccc7f7d --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-parms1.c @@ -0,0 +1,48 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +#include + +void __attribute__ ((__strub__ (2))) +small_args (int i, long long l, void *p, void **q, double d, char c) +{ +} + +/* { dg-final { scan-ipa-dump "\nvoid small_args.strub.\[0-9\]* \[(\]int i, long long int l, void \\* p, void \\* \\* q, double d, char c, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " small_args.strub.\[0-9\]* \[(\]\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + + +struct large_arg { + int x[128]; +}; + +void __attribute__ ((__strub__ (2))) +large_byref_arg (struct large_arg la) +{ +} + +/* { dg-final { scan-ipa-dump "\nvoid large_byref_arg.strub.\[0-9\]* \[(\]struct large_arg & la, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " large_byref_arg.strub.\[0-9\]* \[(\]&\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + +void __attribute__ ((__strub__ (2))) +std_arg (int i, ...) +{ + va_list vl; + va_start (vl, i); + va_end (vl); +} + +/* { dg-final { scan-ipa-dump "\nvoid std_arg.strub.\[0-9\]* \[(\]int i, \[^&,\]* &\[^&,\]*.strub.va_list_ptr, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " std_arg.strub.\[0-9\]* \[(\]\[^&\]*&.strub.va_list.\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_start \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_copy \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_end \\(" 2 "strub" } } */ + +void __attribute__ ((__strub__ (2))) +apply_args (int i, int j, double d) +{ + __builtin_apply_args (); +} + +/* { dg-final { scan-ipa-dump "\nvoid apply_args.strub.\[0-9\]* \[(\]int i, int j, double d, void \\*\[^&,\]*.strub.apply_args, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " apply_args.strub.\[0-9\]* \[(\]\[^&\]*.strub.apply_args.\[0-9\]*_\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-parms2.c b/gcc/testsuite/c-c++-common/strub-parms2.c new file mode 100644 index 00000000000..aaf02a2cd46 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-parms2.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +#include + +void __attribute__ ((__strub__ (1))) +small_args (int i, long long l, void *p, void **q, double d, char c) +{ +} + +/* { dg-final { scan-ipa-dump "\nvoid small_args \[(\]int i, long long int l, void \\* p, void \\* \\* q, double d, char c, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ + + +struct large_arg { + int x[128]; +}; + +void __attribute__ ((__strub__ (1))) +large_byref_arg (struct large_arg la) +{ +} + +/* { dg-final { scan-ipa-dump "\nvoid large_byref_arg \[(\]struct large_arg la, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ + +void __attribute__ ((__strub__ (1))) +std_arg (int i, ...) +{ + va_list vl; + va_start (vl, i); + va_end (vl); +} + +/* { dg-final { scan-ipa-dump "\nvoid std_arg \[(\]int i, void \\* &\[^&,\]*.strub.watermark_ptr\[, .]*\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_start \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-not "va_copy \\(" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_end \\(" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-parms3.c b/gcc/testsuite/c-c++-common/strub-parms3.c new file mode 100644 index 00000000000..2846098160d --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-parms3.c @@ -0,0 +1,58 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that uses of a strub variable implicitly enables internal strub for + publicly-visible functions, and causes the same transformations to their + signatures as those in strub-parms1.c. */ + +#include + +int __attribute__ ((__strub__)) var; + +void +small_args (int i, long long l, void *p, void **q, double d, char c) +{ + var++; +} + +/* { dg-final { scan-ipa-dump "\nvoid small_args.strub.\[0-9\]* \[(\]int i, long long int l, void \\* p, void \\* \\* q, double d, char c, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " small_args.strub.\[0-9\]* \[(\]\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + + +struct large_arg { + int x[128]; +}; + +void +large_byref_arg (struct large_arg la) +{ + var++; +} + +/* { dg-final { scan-ipa-dump "\nvoid large_byref_arg.strub.\[0-9\]* \[(\]struct large_arg & la, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " large_byref_arg.strub.\[0-9\]* \[(\]&\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + +void +std_arg (int i, ...) +{ + va_list vl; + va_start (vl, i); + var++; + va_end (vl); +} + +/* { dg-final { scan-ipa-dump "\nvoid std_arg.strub.\[0-9\]* \[(\]int i, \[^&,\]* &\[^&,\]*.strub.va_list_ptr, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " std_arg.strub.\[0-9\]* \[(\]\[^&\]*&.strub.va_list.\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_start \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_copy \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_end \\(" 2 "strub" } } */ + +void +apply_args (int i, int j, double d) +{ + var++; + __builtin_apply_args (); +} + +/* { dg-final { scan-ipa-dump "\nvoid apply_args.strub.\[0-9\]* \[(\]int i, int j, double d, void \\*\[^&,\]*.strub.apply_args, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " apply_args.strub.\[0-9\]* \[(\]\[^&\]*.strub.apply_args.\[0-9\]*_\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-callable1.c b/gcc/testsuite/c-c++-common/torture/strub-callable1.c new file mode 100644 index 00000000000..45965f275c9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-callable1.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub -fno-inline" } */ + +/* Check that strub and non-strub functions can be called from non-strub + contexts, and that strub and callable functions can be called from strub + contexts. */ + +#define OMIT_IMPERMISSIBLE_CALLS 1 +#include "strub-callable2.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 45 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 45 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-callable2.c b/gcc/testsuite/c-c++-common/torture/strub-callable2.c new file mode 100644 index 00000000000..38935e3270b --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-callable2.c @@ -0,0 +1,264 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default" } */ + +/* Check that impermissible (cross-strub-context) calls are reported. */ + +extern int __attribute__ ((__strub__ (3))) xcallable (void); +extern int __attribute__ ((__strub__ (2))) xinternal (void); +extern int __attribute__ ((__strub__ (1))) xat_calls (void); +extern int __attribute__ ((__strub__ (0))) xdisabled (void); + +int __attribute__ ((__strub__ (3))) callable (void); +int __attribute__ ((__strub__ (2))) internal (void); +int __attribute__ ((__strub__ (1))) at_calls (void); +int __attribute__ ((__strub__ (0))) disabled (void); + +int __attribute__ ((__strub__)) var; +int var_user (void); + +static inline int __attribute__ ((__always_inline__, __strub__ (3))) +icallable (void); +static inline int __attribute__ ((__always_inline__, __strub__ (2))) +iinternal (void); +static inline int __attribute__ ((__always_inline__, __strub__ (1))) +iat_calls (void); +static inline int __attribute__ ((__always_inline__, __strub__ (0))) +idisabled (void); +static inline int __attribute__ ((__always_inline__)) +ivar_user (void); + +static inline int __attribute__ ((__always_inline__, __strub__ (3))) +i_callable (void) { return 0; } +static inline int __attribute__ ((__always_inline__, __strub__ (2))) +i_internal (void) { return var; } +static inline int __attribute__ ((__always_inline__, __strub__ (1))) +i_at_calls (void) { return var; } +static inline int __attribute__ ((__always_inline__, __strub__ (0))) +i_disabled (void) { return 0; } +static inline int __attribute__ ((__always_inline__)) +i_var_user (void) { return var; } + +#define CALLS_GOOD_FOR_STRUB_CONTEXT(ISEP) \ + do { \ + ret += i ## ISEP ## at_calls (); \ + ret += i ## ISEP ## internal (); \ + ret += i ## ISEP ## var_user (); \ + } while (0) + +#define CALLS_GOOD_FOR_NONSTRUB_CONTEXT(ISEP) \ + do { \ + ret += internal (); \ + ret += disabled (); \ + ret += var_user (); \ + \ + ret += i ## ISEP ## disabled (); \ + \ + ret += xinternal (); \ + ret += xdisabled (); \ + } while (0) + +#define CALLS_GOOD_FOR_EITHER_CONTEXT(ISEP) \ + do { \ + ret += i ## ISEP ## callable (); \ + \ + ret += callable (); \ + ret += at_calls (); \ + \ + ret += xat_calls (); \ + ret += xcallable (); \ + } while (0) + +/* Not a strub context, so it can call anything. + Explicitly declared as callable even from within strub contexts. */ +int __attribute__ ((__strub__ (3))) +callable (void) { + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += iat_calls (); /* { dg-error "in non-.strub. context" } */ + ret += iinternal (); /* { dg-error "in non-.strub. context" } */ + ret += ivar_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); + + return ret; +} + +/* Internal strubbing means the body is a strub context, so it can only call + strub functions, and it's not itself callable from strub functions. */ +int __attribute__ ((__strub__ (2))) +internal (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(); + CALLS_GOOD_FOR_EITHER_CONTEXT(); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += idisabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int __attribute__ ((__strub__ (1))) +at_calls (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(); + CALLS_GOOD_FOR_EITHER_CONTEXT(); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += idisabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int __attribute__ ((__strub__ (0))) +disabled () { + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += iat_calls (); /* { dg-error "in non-.strub. context" } */ + ret += iinternal (); /* { dg-error "in non-.strub. context" } */ + ret += ivar_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); + + return ret; +} + +int +var_user (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(); + CALLS_GOOD_FOR_EITHER_CONTEXT(); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += idisabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int +icallable (void) +{ + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += i_at_calls (); /* { dg-error "in non-.strub. context" } */ + ret += i_internal (); /* { dg-error "in non-.strub. context" } */ + ret += i_var_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); + + return ret; +} + +int +iinternal (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(_); + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += i_disabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int +iat_calls (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(_); + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += i_disabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int +idisabled () { + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += i_at_calls (); /* { dg-error "in non-.strub. context" } */ + ret += i_internal (); /* { dg-error "in non-.strub. context" } */ + ret += i_var_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); + + return ret; +} + +int +ivar_user (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(_); + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += i_disabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-const1.c b/gcc/testsuite/c-c++-common/torture/strub-const1.c new file mode 100644 index 00000000000..100fb0c59a9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub const function call, we issue an asm statement + to make sure the watermark passed to it is held in memory before the call, + and another to make sure it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__, __const__)) +f() { + return 0; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-const2.c b/gcc/testsuite/c-c++-common/torture/strub-const2.c new file mode 100644 index 00000000000..9e818ac9748 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const2.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-const function call, we issue an + asm statement to make sure the watermark passed to it is held in memory + before the call, and another to make sure it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__)) +#if ! __OPTIMIZE__ +__attribute__ ((__const__)) +#endif +f() { + return 0; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-const3.c b/gcc/testsuite/c-c++-common/torture/strub-const3.c new file mode 100644 index 00000000000..d40e8aa45cb --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub const wrapping call, we issue an asm statement + to make sure the watermark passed to it is held in memory before the call, + and another to make sure it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__ (2), __const__)) +f() { + return 0; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-const4.c b/gcc/testsuite/c-c++-common/torture/strub-const4.c new file mode 100644 index 00000000000..d4cbdaf10f3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-const wrapping call, we issue an + asm statement to make sure the watermark passed to it is held in memory + before the call, and another to make sure it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__ (2))) +#if ! __OPTIMIZE__ +__attribute__ ((__const__)) +#endif +f() { + return 0; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data5.c b/gcc/testsuite/c-c++-common/torture/strub-data5.c index 251790d4bbb..07e25af9c53 100644 --- a/gcc/testsuite/c-c++-common/torture/strub-data5.c +++ b/gcc/testsuite/c-c++-common/torture/strub-data5.c @@ -1,15 +1,13 @@ /* { 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" } */ + return ptr; /* { dg-warning "incompatible" } */ } strub_int *g () { - return f (); /* { dg-warn "incompatible" } */ + return f (); /* { dg-warning "incompatible" } */ } diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure1.c b/gcc/testsuite/c-c++-common/torture/strub-pure1.c new file mode 100644 index 00000000000..cb223da6efc --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub pure function call, we issue an asm statement + to make sure the watermark passed to it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__, __pure__)) +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure2.c b/gcc/testsuite/c-c++-common/torture/strub-pure2.c new file mode 100644 index 00000000000..67d1434b1f8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure2.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-pure function call, we issue an asm + statement to make sure the watermark passed to it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__)) +#if ! __OPTIMIZE__ /* At -O0, implicit pure detection doesn't run. */ +__attribute__ ((__pure__)) +#endif +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure3.c b/gcc/testsuite/c-c++-common/torture/strub-pure3.c new file mode 100644 index 00000000000..59f02ea901f --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub pure wrapping call, we issue an asm statement + to make sure the watermark passed to it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__ (2), __pure__)) +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure4.c b/gcc/testsuite/c-c++-common/torture/strub-pure4.c new file mode 100644 index 00000000000..973e909217d --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=default -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-pure wrapping call, we issue an asm + statement to make sure the watermark passed to it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__ (2))) +#if ! __OPTIMIZE__ /* At -O0, implicit pure detection doesn't run. */ +__attribute__ ((__pure__)) +#endif +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-run1.c b/gcc/testsuite/c-c++-common/torture/strub-run1.c new file mode 100644 index 00000000000..828a4cc2998 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run1.c @@ -0,0 +1,85 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default" } */ + +/* Check that a non-strub function leaves a string behind in the stack, and that + equivalent strub functions don't. */ + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +static inline __attribute__ ((__always_inline__, __strub__ (3))) +char * +leak_string (void) +{ + /* We use this variable to avoid any stack red zone. Stack scrubbing covers + it, but __builtin_stack_address, that we take as a reference, doesn't, so + if e.g. callable() were to store the string in the red zone, we wouldn't + find it because it would be outside the range we searched. */ + typedef void __attribute__ ((__strub__ (3))) callable_t (char *); + callable_t *f = 0; + + char s[sizeof (test_string)]; + __builtin_strcpy (s, test_string); + asm ("" : "+m" (s), "+r" (f)); + + if (__builtin_expect (!f, 1)) + return __builtin_stack_address (); + + f (s); + return 0; +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__noinline__, __noclone__)) +char * +callable () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (1))) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (2))) +char * +internal () +{ + return leak_string (); +} + +int main () +{ + if (!look_for_string (callable ())) + __builtin_abort (); + if (look_for_string (at_calls ())) + __builtin_abort (); + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-run2.c b/gcc/testsuite/c-c++-common/torture/strub-run2.c new file mode 100644 index 00000000000..5794b694b2d --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run2.c @@ -0,0 +1,75 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default" } */ + +/* Check that a non-strub function leaves a string behind in the stack, and that + equivalent strub functions don't. */ + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +static inline __attribute__ ((__always_inline__, __strub__ (3))) +char * +leak_string (void) +{ + int len = sizeof (test_string); + asm ("" : "+rm" (len)); + char s[len]; + __builtin_strcpy (s, test_string); + asm ("" : "+m" (s)); + return __builtin_stack_address (); +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__noinline__, __noclone__)) +char * +callable () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (1))) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (2))) +char * +internal () +{ + return leak_string (); +} + +int main () +{ + if (!look_for_string (callable ())) + __builtin_abort (); + if (look_for_string (at_calls ())) + __builtin_abort (); + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-run3.c b/gcc/testsuite/c-c++-common/torture/strub-run3.c new file mode 100644 index 00000000000..7da79055959 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run3.c @@ -0,0 +1,75 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=default" } */ +/* { dg-require-effective-target alloca } */ + +/* Check that a non-strub function leaves a string behind in the stack, and that + equivalent strub functions don't. */ + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +static inline __attribute__ ((__always_inline__, __strub__ (3))) +char * +leak_string (void) +{ + int len = sizeof (test_string); + char *s = __builtin_alloca (len); + __builtin_strcpy (s, test_string); + asm ("" : "+m" (s)); + return __builtin_stack_address (); +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__noinline__, __noclone__)) +char * +callable () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (1))) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ (2))) +char * +internal () +{ + return leak_string (); +} + +int main () +{ + if (!look_for_string (callable ())) + __builtin_abort (); + if (look_for_string (at_calls ())) + __builtin_abort (); + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/g++.dg/wrappers/strub1.C b/gcc/testsuite/g++.dg/strub-run1.C similarity index 90% rename from gcc/testsuite/g++.dg/wrappers/strub1.C rename to gcc/testsuite/g++.dg/strub-run1.C index a474a929649..754291eaa01 100644 --- a/gcc/testsuite/g++.dg/wrappers/strub1.C +++ b/gcc/testsuite/g++.dg/strub-run1.C @@ -1,4 +1,5 @@ // { dg-do run } +// { dg-options "-fstrub=internal" } // Check that we don't get extra copies. diff --git a/gcc/testsuite/g++.dg/wrappers/strub2.C b/gcc/testsuite/g++.dg/wrappers/strub2.C deleted file mode 100644 index 25a62166448..00000000000 --- a/gcc/testsuite/g++.dg/wrappers/strub2.C +++ /dev/null @@ -1,22 +0,0 @@ -// { dg-do run } - -// This doesn't really test anything yet. We should mark the -// variables as requiring strubbing, and somehow check that the -// wrapped functions take the parameter by reference. - -struct T { - char d[32 * sizeof(void*)]; -}; - -T foo (T q) { asm ("" : : "m"(q)); return q; } -T bar (T p) { return foo (p); } - -T tmp; -T tmp2; - -int main () { - __builtin_memset (&tmp, 0x55, sizeof (tmp)); - tmp2 = bar (tmp); - if (__builtin_memcmp (&tmp, &tmp2, sizeof (tmp))) - __builtin_abort (); -} diff --git a/gcc/testsuite/g++.dg/wrappers/strub3.C b/gcc/testsuite/g++.dg/wrappers/strub3.C deleted file mode 100644 index e1b51cd0399..00000000000 --- a/gcc/testsuite/g++.dg/wrappers/strub3.C +++ /dev/null @@ -1,22 +0,0 @@ -// { dg-do run } - -// This doesn't really test anything yet. We should mark the -// variables as requiring strubbing, and somehow check that the -// wrapped functions take the parameter by reference. - -struct T { - char d[32 * sizeof(void*)]; -}; - -static T foo (T q) { asm ("" : : "m"(q)); return q; } -static T bar (T p) { return foo (p); } - -T tmp; -T tmp2; - -int main () { - __builtin_memset (&tmp, 0x55, sizeof (tmp)); - tmp2 = bar (tmp); - if (__builtin_memcmp (&tmp, &tmp2, sizeof (tmp))) - __builtin_abort (); -} diff --git a/gcc/testsuite/g++.dg/wrappers/strub4.C b/gcc/testsuite/g++.dg/wrappers/strub4.C deleted file mode 100644 index d021fca88e4..00000000000 --- a/gcc/testsuite/g++.dg/wrappers/strub4.C +++ /dev/null @@ -1,18 +0,0 @@ -// { dg-do run } - -namespace -{ - class foo - { - public: - foo(); - }; - - foo::foo() {} - - foo bar; -} - -int main() -{ -}