public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/aoliva/heads/strub)] fix handling of indirect calls
@ 2021-08-04 8:17 Alexandre Oliva
0 siblings, 0 replies; only message in thread
From: Alexandre Oliva @ 2021-08-04 8:17 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:a00086813f5ca93baf77276a281daf53803f5c62
commit a00086813f5ca93baf77276a281daf53803f5c62
Author: Alexandre Oliva <oliva@adacore.com>
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<char> 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<tree> 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<tree, va_gc> *inputs = NULL;
+ vec<tree, va_gc> *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<tree, va_gc> *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<tree> 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<tree, va_gc> *inputs = NULL;
- vec<tree, va_gc> *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<tree, va_gc> *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<char> 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 <gcall *> (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" } } */
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2021-08-04 8:17 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-04 8:17 [gcc(refs/users/aoliva/heads/strub)] fix handling of indirect calls Alexandre Oliva
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).