Last version:
https://patchwork.sourceware.org/project/gcc/patch/[email protected]/
Hi,
this patch got a review by Martin off the mailing list, so I will try to
summarize it here. I've made the following changes:
- Renamed the wrapper functions: Renamed gimple_call_arg_summary to
callback_call_arg_summary, likewise for gimple_call_num_args_summary.
- Martin pointed out that I was using `struct function` in WPA to
identify the .ASSUME bodies, which of course wouldn't work. I
ultimately decided to solve this by adding a one bit flag to cgraph_node
containing the information. It is duplicate without LTO, but we would
have to resort to some pattern matching without it, which wouldn't be
too reliable or readable IMHO.
- The dont_disqualify flag is now included in ipa-sra dumps.
- Added a LTO testcase.
Best regards,
Josef
PR c++/122779
PR ipa/109112
gcc/ChangeLog:
* attr-callback.cc (callback_redirect_edge): New function,
redirects a callback edge.
* attr-callback.h (callback_redirect_edge): Add decl.
* callback-info.cc (callback_info::initialize_internal_summary):
New method, initializes the summary for internal calls.
(callback_call_arg_summary): New function, wrapper around
gimple_call_arg. Remaps arg indices for callbacks if necessary.
(callback_call_num_args_summary): New function, wrapper around
gimple_call_num_args. Returns the correct number of args for
callback calls.
* callback-info.h (callback_call_arg_summary): Add decl.
(callback_call_num_args_summary): Likewise.
* cgraph.cc (cgraph_node::get_edge): Don't return the
dispatching edge for assume callback edges.
(cgraph_edge::set_call_stmt): Don't cascade for assume callback
edges.
(cgraph_edge::make_callback_internal): New method, creates a
callback edge attached to an internal call.
(cgraph_edge::redirect_call_stmt_to_callee): Implement assume
callback edge redirection.
(cgraph_node::dump): Dump assume_function flag.
(cgraph_node::verify_node): Adjust sanity checks.
* cgraph.h (struct cgraph_node): Add assume_function flag to
cgraph_node.
* cgraphclones.cc (cgraph_node::create_clone): Copy
assume_function flag.
* gimple-low.cc (create_assumption_fn): Don't add attrs blocking
ipa optimizations to the new function.
(lower_assumption): Unset stdarg, set assume_function.
* ipa-fnsummary.cc (analyze_function_body): Implement summary
modifications for assume callback calls.
* ipa-param-manipulation.cc (ipa_param_adjustments::modify_call):
Implement call stmt modifications for IFN_ASSUME.
* ipa-sra.cc (struct isra_param_desc): Add dont_disqualify flag.
(struct gensum_param_desc): Likewise.
(ipa_sra_function_summaries::duplicate): Copy dont_disqualify.
(dump_gensum_param_descriptor): Dump dont_disqualify.
(ptr_parm_has_nonarg_uses): Use callback_call_arg_summary.
(create_parameter_descriptors): Initialize dont_disqualify.
(disqualify_split_candidate): Early exit if dont_disqualify.
(scan_function): Add logic for assume callback edge creation.
(isra_analyze_call): Remap args for callback edges.
(process_scan_results): Likewise.
(ipa_sra_generate_summary): Check-create callback_info_sum.
(isra_write_node_summary): Stream out dont_disqualify.
(isra_read_node_info): Stream in dont_disqualify.
(can_be_local_p): New function, returns TRUE iff
node->can_be_local_p or if the function is an .ASSUME body.
(ipa_sra_ipa_function_checks): Use can_be_local_p.
(size_would_violate_limit_p): Use dont_disqualify.
(retval_used_p): Return TRUE for .ASSUME bodies.
* lto-cgraph.cc (lto_output_node): Stream out assume_function flag.
(input_overwrite_node): Stream in assume_function flag.
* tree-inline.cc (initialize_cfun): Copy assume_function flag (struct
function, not cgraph_node).
gcc/testsuite/ChangeLog:
* g++.dg/ipa/ipa-sra-assume-2.C: New test.
* g++.dg/ipa/ipa-sra-assume-3.C: New test.
* g++.dg/ipa/ipa-sra-assume.C: New test.
* g++.dg/lto/ipa-sra-assume_0.C: New test.
* g++.dg/lto/ipa-sra-assume_1.C: New test.
Signed-off-by: Josef Melcr <[email protected]>
---
gcc/attr-callback.cc | 13 +++
gcc/attr-callback.h | 4 +
gcc/callback-info.cc | 43 ++++++++++
gcc/callback-info.h | 14 +++-
gcc/cgraph.cc | 79 ++++++++++++++----
gcc/cgraph.h | 15 +++-
gcc/cgraphclones.cc | 1 +
gcc/gimple-low.cc | 17 ++--
gcc/ipa-fnsummary.cc | 22 ++++-
gcc/ipa-param-manipulation.cc | 27 ++++--
gcc/ipa-sra.cc | 92 +++++++++++++++++----
gcc/lto-cgraph.cc | 2 +
gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C | 33 ++++++++
gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C | 30 +++++++
gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C | 37 +++++++++
gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C | 28 +++++++
gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C | 11 +++
gcc/tree-inline.cc | 1 +
18 files changed, 416 insertions(+), 53 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C
create mode 100644 gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C
create mode 100644 gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C
create mode 100644 gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C
create mode 100644 gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C
diff --git a/gcc/attr-callback.cc b/gcc/attr-callback.cc
index ec89974532c..e1367da37f1 100644
--- a/gcc/attr-callback.cc
+++ b/gcc/attr-callback.cc
@@ -24,6 +24,7 @@
#include "backend.h"
#include "tree.h"
#include "gimple.h"
+#include "fold-const.h"
#include "alloc-pool.h"
#include "cgraph.h"
#include "diagnostic.h"
@@ -362,3 +363,15 @@ callback_num_args (tree attr)
;
return res;
}
+
+/* Redirect call statement of E to the callee of E. Used in
+ cgraph_edge::redirect_call_stmt_to_callee. */
+void
+callback_redirect_edge (cgraph_edge *e)
+{
+ callback_info *ci = callback_info_sum->get (e);
+ tree previous_arg = gimple_call_arg (e->call_stmt, ci->fn_idx);
+ location_t loc = EXPR_LOCATION (previous_arg);
+ tree new_addr = build_fold_addr_expr_loc (loc, e->callee->decl);
+ gimple_call_set_arg (e->call_stmt, ci->fn_idx, new_addr);
+}
diff --git a/gcc/attr-callback.h b/gcc/attr-callback.h
index 8f649006b8c..088509d2221 100644
--- a/gcc/attr-callback.h
+++ b/gcc/attr-callback.h
@@ -75,4 +75,8 @@ bool callback_edge_useful_p (cgraph_edge *e);
takes. */
size_t callback_num_args (tree attr);
+/* Redirect call statement of E to the callee of E. Used in
+ cgraph_edge::redirect_call_stmt_to_callee. */
+void callback_redirect_edge (cgraph_edge *e);
+
#endif /* ATTR_CALLBACK_H */
diff --git a/gcc/callback-info.cc b/gcc/callback-info.cc
index 5309d763936..764ad75a66f 100644
--- a/gcc/callback-info.cc
+++ b/gcc/callback-info.cc
@@ -78,6 +78,29 @@ callback_info::get_id () const
return fn_idx;
}
+/* Initialize the summary for a callback call CBE of internal function IFN. */
+callback_info *
+callback_info::initialize_internal_summary (cgraph_edge *cbe, internal_fn ifn)
+{
+ callback_info_sum_t::check_create_info_sum ();
+ callback_info *ci = callback_info_sum->get_create (cbe);
+ switch (ifn)
+ {
+ case IFN_ASSUME:
+ {
+ ci->fn_idx = 0;
+ unsigned n = gimple_call_num_args (cbe->call_stmt);
+ for (unsigned i = 0; i < n - 1; i++)
+ ci->arg_mapping.safe_push (i + 1);
+ break;
+ }
+ default:
+ gcc_unreachable ();
+ }
+ ci->redirected = false;
+ return ci;
+}
+
/* Populates the callback_info_sum if it's NULL. */
void
callback_info_sum_t::check_create_info_sum (void)
@@ -106,3 +129,23 @@ callback_info_sum_t::duplicate (cgraph_edge *, cgraph_edge
*,
dst_s->arg_mapping = src_s->arg_mapping.copy ();
dst_s->redirected = src_s->redirected;
}
+
+/* Return the argument at IDX if CI is null, otherwise use the mapping in CI
and
+ return the remapped argument. */
+tree
+callback_call_arg_summary (gimple *stmt, unsigned idx, callback_info *ci)
+{
+ if (ci)
+ idx = ci->arg_mapping[idx];
+ return gimple_call_arg (stmt, idx);
+}
+
+/* Return the number of arguments of STMT if CI is null,
+ ci->arg_mapping.length() otherwise. */
+unsigned
+callback_call_num_args_summary (const gcall *stmt, callback_info *ci)
+{
+ if (ci)
+ return ci->arg_mapping.length ();
+ return gimple_call_num_args (stmt);
+}
diff --git a/gcc/callback-info.h b/gcc/callback-info.h
index 2443fa426e6..0d41eb8dbe1 100644
--- a/gcc/callback-info.h
+++ b/gcc/callback-info.h
@@ -40,7 +40,7 @@ public:
auto_vec<int> arg_mapping;
/* TRUE iff the associated callback edge was redirected. */
- bool redirected;
+ bool redirected : 1;
/* Stream in callback_info. */
void stream_in (lto_input_block *ib);
@@ -50,6 +50,10 @@ public:
/* Returns the id of the associated callback attribute. */
unsigned get_id () const;
+
+ /* Initialize the summary for a callback call CBE of internal function IFN.
*/
+ static callback_info *initialize_internal_summary (cgraph_edge *cbe,
+ internal_fn ifn);
};
class callback_info_sum_t : public call_summary<callback_info *>
@@ -72,4 +76,12 @@ public:
extern callback_info_sum_t *callback_info_sum;
+/* Return the argument at IDX if CI is null, otherwise use the mapping in CI
and
+ return the remapped argument. */
+tree callback_call_arg_summary (gimple *stmt, unsigned idx, callback_info *ci);
+
+/* Return the number of arguments of STMT if CI is null,
+ ci->arg_mapping.length() otherwise. */
+unsigned callback_call_num_args_summary (const gcall *stmt, callback_info *ci);
+
#endif /* CALLBACK_INFO_H */
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index b43a249938d..66b0af0f5f1 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -937,7 +937,7 @@ cgraph_node::get_edge (gimple *call_stmt)
comes to callback edges, a call statement might have multiple callback
edges attached to it. These can be easily obtained from the carrying edge
instead. */
- if (e && e->callback)
+ if (e && e->callback && !e->callee->assume_function)
e = e->get_callback_carrying_edge ();
if (n > 100)
@@ -1015,7 +1015,8 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall
*new_stmt,
/* When updating a callback or a callback-carrying edge, update every edge
involved. */
- if (update_derived_edges && (e->callback || e->has_callback))
+ if (update_derived_edges && (e->callback || e->has_callback)
+ && !e->callee->assume_function)
{
cgraph_edge *current, *next, *carrying;
carrying = e->has_callback ? e : e->get_callback_carrying_edge ();
@@ -1383,6 +1384,37 @@ cgraph_edge::make_callback (cgraph_node *n2)
return e2;
}
+
+/* Create a callback edge, representing an indirect call to n2
+ passed to an internal function by argument. As internal calls are not
+ represented in the callgraph, the created edge won't have any associated
+ dispatching edge. The created callback edge will be attached to the
+ internal call statement STMT. Returns created callback edge. */
+
+cgraph_edge *
+cgraph_edge::make_callback_internal (cgraph_node *caller, cgraph_node *target,
+ gcall *call_stmt)
+{
+ cgraph_edge *ret;
+
+ ret
+ = caller->create_edge (target, call_stmt, profile_count::uninitialized ());
+ if (dump_file)
+ {
+ fprintf (dump_file,
+ "Created callback edge %s -> %s belonging to internal call ",
+ ret->caller->dump_name (), ret->callee->dump_name ());
+ print_gimple_stmt (dump_file, call_stmt, 0);
+ }
+ ret->inline_failed = CIF_CALLBACK_EDGE;
+ ret->callback = true;
+ if (TREE_NOTHROW (target->decl))
+ ret->can_throw_external = false;
+ caller->create_reference (target, IPA_REF_ADDR, call_stmt);
+ target->mark_address_taken ();
+ return ret;
+}
+
/* Returns the callback_carrying edge of a callback edge on which
it is called on or NULL when no such edge can be found.
@@ -1837,21 +1869,18 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge
*e,
/* When redirecting a callback edge, all we need to do is replace
the original address with the address of the function we are
redirecting to. */
- if (e->callback)
+ if (e->callback && !e->callee->assume_function)
{
cgraph_edge *carrying = e->get_callback_carrying_edge ();
- if (!callback_is_special_cased (carrying->callee->decl, e->call_stmt)
+ if (carrying
+ && !callback_is_special_cased (carrying->callee->decl, e->call_stmt)
&& !lookup_attribute (CALLBACK_ATTR_IDENT,
DECL_ATTRIBUTES (carrying->callee->decl)))
/* Callback attribute is removed if the dispatching function changes
signature, as the indices wouldn't be correct anymore. These edges
will get cleaned up later, ignore their redirection for now. */
return e->call_stmt;
- callback_info *ci = callback_info_sum->get (e);
- tree previous_arg = gimple_call_arg (e->call_stmt, ci->fn_idx);
- location_t loc = EXPR_LOCATION (previous_arg);
- tree new_addr = build_fold_addr_expr_loc (loc, e->callee->decl);
- gimple_call_set_arg (e->call_stmt, ci->fn_idx, new_addr);
+ callback_redirect_edge (e);
return e->call_stmt;
}
@@ -1904,6 +1933,9 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e,
&& old_fntype == TREE_TYPE (origin->former_clone_of))
|| old_fntype == TREE_TYPE (origin->decl))
gimple_call_set_fntype (new_stmt, TREE_TYPE (e->callee->decl));
+ /* Don't set fntype on internal calls. */
+ else if (gimple_call_internal_p (new_stmt))
+ ;
else
{
tree new_fntype = padjs->build_new_function_type (old_fntype, true);
@@ -1920,10 +1952,20 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge
*e,
BUILT_IN_UNREACHABLE_TRAP))
ipa_verify_edge_has_no_modifications (e);
new_stmt = e->call_stmt;
- gimple_call_set_fndecl (new_stmt, e->callee->decl);
+ /* Redirects callback edges attached to internal calls. */
+ if (e->callback)
+ callback_redirect_edge (e);
+ else
+ gimple_call_set_fndecl (new_stmt, e->callee->decl);
update_stmt_fn (DECL_STRUCT_FUNCTION (e->caller->decl), new_stmt);
}
+ if (e->callback && e->callee->assume_function)
+ {
+ e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
false);
+ return new_stmt;
+ }
+
/* If changing the call to __cxa_pure_virtual or similar noreturn function,
adjust gimple_call_fntype too. */
if (gimple_call_noreturn_p (new_stmt)
@@ -2580,6 +2622,8 @@ cgraph_node::dump (FILE *f)
fprintf (f, " calls_comdat_local");
if (icf_merged)
fprintf (f, " icf_merged");
+ if (assume_function)
+ fprintf (f, " assume_function");
if (merged_comdat)
fprintf (f, " merged_comdat");
if (merged_extern_inline)
@@ -4396,13 +4440,14 @@ cgraph_node::verify_node (void)
if (e->callback)
{
- if (!e->get_callback_carrying_edge ())
- {
- error ("callback edge %s->%s has no callback-carrying",
- identifier_to_locale (e->caller->name ()),
- identifier_to_locale (e->callee->name ()));
- error_found = true;
- }
+ if (!e->get_callback_carrying_edge ()
+ && (!e->call_stmt || !gimple_call_internal_p (e->call_stmt)))
+ {
+ error ("callback edge %s->%s has no callback-carrying edge",
+ identifier_to_locale (e->caller->name ()),
+ identifier_to_locale (e->callee->name ()));
+ error_found = true;
+ }
}
if (e->has_callback
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index ec7112452da..7afe4e0d4c3 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -939,8 +939,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public
symtab_node
only_called_at_startup (false), only_called_at_exit (false),
tm_clone (false), dispatcher_function (false),
dispatcher_resolver_function (false), is_target_clone (false),
- calls_comdat_local (false),
- icf_merged (false), nonfreeing_fn (false), merged_comdat (false),
+ calls_comdat_local (false), icf_merged (false), assume_function(false),
+ nonfreeing_fn (false), merged_comdat (false),
merged_extern_inline (false), parallelized_function (false),
split_part (false), indirect_call_target (false), local (false),
versionable (false), can_change_signature (false),
@@ -1536,6 +1536,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node :
public symtab_node
unsigned calls_comdat_local : 1;
/* True if node has been created by merge operation in IPA-ICF. */
unsigned icf_merged: 1;
+ /* True if node has been created by lowering an [[assume(...)]] statement.
*/
+ unsigned assume_function : 1;
/* True if call to node can't result in a call to free, munmap or
other operation that could make previously non-trapping memory
accesses trapping. */
@@ -1928,6 +1930,15 @@ public:
callback edge. */
cgraph_edge *make_callback (cgraph_node *n2);
+ /* Create a callback edge, representing an indirect call to n2
+ passed to an internal function by argument. As internal calls are not
+ represented in the callgraph, the created edge won't have any associated
+ dispatching edge. edge. The created callback edge will be attached to the
+ internal call statement STMT. Returns created callback edge. */
+ static cgraph_edge *make_callback_internal (cgraph_node *caller,
+ cgraph_node *target,
+ gcall *call_stmt);
+
/* Returns the callback-carrying edge of a callback edge or NULL, if such
edge
cannot be found. An edge is considered callback-carrying, if it has it's
has_callback flag set and shares it's call statement with the edge
diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc
index b5f2ce5ff2f..c5aead0c3b8 100644
--- a/gcc/cgraphclones.cc
+++ b/gcc/cgraphclones.cc
@@ -452,6 +452,7 @@ cgraph_node::create_clone (tree new_decl, profile_count
prof_count,
new_node->tp_first_run = tp_first_run;
new_node->tm_clone = tm_clone;
new_node->icf_merged = icf_merged;
+ new_node->assume_function = assume_function;
new_node->thunk = thunk;
new_node->unit_id = unit_id;
new_node->merged_comdat = merged_comdat;
diff --git a/gcc/gimple-low.cc b/gcc/gimple-low.cc
index 5915d7234e0..6f0f5bedd02 100644
--- a/gcc/gimple-low.cc
+++ b/gcc/gimple-low.cc
@@ -302,18 +302,7 @@ create_assumption_fn (location_t loc)
DECL_EXTERNAL (decl) = 0;
DECL_CONTEXT (decl) = NULL_TREE;
DECL_INITIAL (decl) = make_node (BLOCK);
- tree attributes = DECL_ATTRIBUTES (current_function_decl);
- if (lookup_attribute ("noipa", attributes) == NULL)
- {
- attributes = tree_cons (get_identifier ("noipa"), NULL, attributes);
- if (lookup_attribute ("noinline", attributes) == NULL)
- attributes = tree_cons (get_identifier ("noinline"), NULL, attributes);
- if (lookup_attribute ("noclone", attributes) == NULL)
- attributes = tree_cons (get_identifier ("noclone"), NULL, attributes);
- if (lookup_attribute ("no_icf", attributes) == NULL)
- attributes = tree_cons (get_identifier ("no_icf"), NULL, attributes);
- }
- DECL_ATTRIBUTES (decl) = attributes;
+ DECL_ATTRIBUTES (decl) = DECL_ATTRIBUTES (current_function_decl);
BLOCK_SUPERCONTEXT (DECL_INITIAL (decl)) = decl;
DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl)
= DECL_FUNCTION_SPECIFIC_OPTIMIZATION (current_function_decl);
@@ -673,8 +662,12 @@ lower_assumption (gimple_stmt_iterator *gsi, struct
lower_data *data)
}
DECL_ARGUMENTS (lad.id.dst_fn) = parms;
TREE_TYPE (lad.id.dst_fn) = build_function_type (boolean_type_node, parmt);
+ /* The body function no longer has var. args, unset stdarg. */
+ DECL_STRUCT_FUNCTION (lad.id.dst_fn)->stdarg = 0;
cgraph_node::add_new_function (lad.id.dst_fn, false);
+ cgraph_node *dst_node = cgraph_node::get (lad.id.dst_fn);
+ dst_node->assume_function = 1;
for (unsigned i = 0; i < sz; ++i)
{
diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc
index a6ccdb1852a..1c253aadd21 100644
--- a/gcc/ipa-fnsummary.cc
+++ b/gcc/ipa-fnsummary.cc
@@ -3045,9 +3045,20 @@ analyze_function_body (struct cgraph_node *node, bool
early)
}
if (is_gimple_call (stmt)
- && !gimple_call_internal_p (stmt))
+ && (!gimple_call_internal_p (stmt)
+ || gimple_call_internal_fn (stmt) == IFN_ASSUME))
{
struct cgraph_edge *edge = node->get_edge (stmt);
+ if (!edge)
+ {
+ /* The edge for .ASSUME outlined bodies is created when
+ generating ipa-sra summaries. Ignore the call statement
+ for now. */
+ gcc_checking_assert (gimple_call_internal_p (stmt)
+ && gimple_call_internal_fn (stmt)
+ == IFN_ASSUME);
+ continue;
+ }
ipa_call_summary *es = ipa_call_summaries->get_create (edge);
/* Special case: results of BUILT_IN_CONSTANT_P will be always
@@ -3134,6 +3145,15 @@ analyze_function_body (struct cgraph_node *node, bool
early)
es2->call_stmt_time = 0;
}
}
+ /* Internal calls are not in the callgraph, so edges pointing to
+ .ASSUME bodies are not covered by the above branch, as the
+ dispatching edge doesn't exist. */
+ else if (edge->callback)
+ {
+ gcc_checking_assert (edge->callee->assume_function);
+ es->call_stmt_size = 0;
+ es->call_stmt_time = 0;
+ }
}
/* TODO: When conditional jump or switch is known to be constant, but
diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc
index 6f85d10a29e..9a2e83bf851 100644
--- a/gcc/ipa-param-manipulation.cc
+++ b/gcc/ipa-param-manipulation.cc
@@ -50,7 +50,9 @@ along with GCC; see the file COPYING3. If not see
#include "sreal.h"
#include "ipa-cp.h"
#include "ipa-prop.h"
+#include "data-streamer.h"
#include "attr-callback.h"
+#include "callback-info.h"
/* Actual prefixes of different newly synthesized parameters. Keep in sync
with IPA_PARAM_PREFIX_* defines. */
@@ -719,7 +721,8 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
unsigned len = vec_safe_length (m_adj_params);
auto_vec<tree, 16> vargs (len);
- unsigned old_nargs = gimple_call_num_args (stmt);
+ callback_info *ci = callback_info_sum->get (cs);
+ unsigned old_nargs = callback_call_num_args_summary (stmt, ci);
unsigned orig_nargs = mod_info ? mod_info->index_map.length () : old_nargs;
auto_vec<bool, 16> kept (old_nargs);
kept.quick_grow_cleared (old_nargs);
@@ -728,6 +731,11 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
if (update_references)
current_node->remove_stmt_references (stmt);
+ if (cs->callback && cs->callee->assume_function)
+ {
+ vargs.safe_push (build_fold_addr_expr (callee_decl));
+ }
+
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
gimple_stmt_iterator prev_gsi = gsi;
gsi_prev (&prev_gsi);
@@ -747,7 +755,7 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
gcc_assert (index >= 0);
}
- tree arg = gimple_call_arg (stmt, index);
+ tree arg = callback_call_arg_summary (stmt, index, ci);
vargs.quick_push (arg);
kept[index] = true;
@@ -773,7 +781,7 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
{
int repl_idx = mod_info->pass_through_map[j].new_index;
gcc_assert (repl_idx >= 0);
- repl = gimple_call_arg (stmt, repl_idx);
+ repl = callback_call_arg_summary (stmt, repl_idx, ci);
break;
}
if (repl)
@@ -797,7 +805,7 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
index = mod_info->index_map[apm->base_index];
gcc_assert (index >= 0);
}
- tree base = gimple_call_arg (stmt, index);
+ tree base = callback_call_arg_summary (stmt, index, ci);
/* We create a new parameter out of the value of the old one, we can
do the following kind of transformations:
@@ -920,7 +928,7 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
gcc_assert (always_copy_start >= 0);
}
for (unsigned i = always_copy_start; i < old_nargs; i++)
- vargs.safe_push (gimple_call_arg (stmt, i));
+ vargs.safe_push (callback_call_arg_summary (stmt, i, ci));
}
/* For optimized away parameters, add on the caller side
@@ -1005,7 +1013,14 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
print_gimple_stmt (dump_file, gsi_stmt (gsi), 0);
}
- gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
+ gcall *new_stmt;
+ if (cs->callback && cs->callee->assume_function)
+ {
+ /* Has to be special-cased, as we have to create an internal call. */
+ new_stmt = gimple_build_call_internal_vec (IFN_ASSUME, vargs);
+ }
+ else
+ new_stmt = gimple_build_call_vec (callee_decl, vargs);
hash_set <tree> *ssas_to_remove = NULL;
if (tree lhs = gimple_call_lhs (stmt))
diff --git a/gcc/ipa-sra.cc b/gcc/ipa-sra.cc
index 422bf367b97..282f4658279 100644
--- a/gcc/ipa-sra.cc
+++ b/gcc/ipa-sra.cc
@@ -87,6 +87,8 @@ along with GCC; see the file COPYING3. If not see
#include "sreal.h"
#include "ipa-cp.h"
#include "ipa-prop.h"
+#include "attr-callback.h"
+#include "callback-info.h"
static void ipa_sra_summarize_function (cgraph_node *);
@@ -185,6 +187,10 @@ struct GTY(()) isra_param_desc
unsigned locally_unused : 1;
/* An aggregate that is a candidate for breaking up or complete removal. */
unsigned split_candidate : 1;
+ /* True iff the parameter should not be disqualified as a split candidate.
+ Only useful for forcing transformations, such as when optimizing .ASSUME
+ bodies. */
+ unsigned dont_disqualify : 1;
/* Is this a parameter passing stuff by reference? */
unsigned by_ref : 1;
/* If set, this parameter can only be a candidate for removal if the function
@@ -233,6 +239,10 @@ struct gensum_param_desc
by reference that is a candidate for being converted to a set of
parameters passing those data by value. */
bool split_candidate;
+ /* True iff the parameter should not be disqualified as a split candidate.
+ Only useful for forcing transformations, such as when optimizing .ASSUME
+ bodies. */
+ bool dont_disqualify;
/* Is this a parameter passing stuff by reference (either a pointer or a
source language reference type)? */
bool by_ref;
@@ -461,6 +471,7 @@ ipa_sra_function_summaries::duplicate (cgraph_node *,
cgraph_node *,
d->safe_size = s->safe_size;
d->locally_unused = s->locally_unused;
d->split_candidate = s->split_candidate;
+ d->dont_disqualify = s->dont_disqualify;
d->by_ref = s->by_ref;
d->remove_only_when_retval_removed = s->remove_only_when_retval_removed;
d->split_only_when_retval_removed = s->split_only_when_retval_removed;
@@ -754,7 +765,9 @@ dump_gensum_param_descriptor (FILE *f, gensum_param_desc
*desc)
fprintf (f, " unused with %i call_uses%s\n", desc->call_uses,
desc->remove_only_when_retval_removed ?
" remove_only_when_retval_removed" : "");
- if (!desc->split_candidate)
+ if (desc->dont_disqualify)
+ fprintf (f, " dont_disqualify\n");
+ else if (!desc->split_candidate)
{
fprintf (f, " not a candidate\n");
return;
@@ -1131,12 +1144,13 @@ ptr_parm_has_nonarg_uses (cgraph_node *node, function
*fun, tree parm,
cgraph_edge *cs = node->get_edge (stmt);
gcc_checking_assert (cs);
+ callback_info *ci = callback_info_sum->get (cs);
isra_call_summary *csum = call_sums->get_create (cs);
csum->init_inputs (arg_count);
for (unsigned i = 0; i < arg_count; ++i)
{
- tree arg = gimple_call_arg (stmt, i);
+ tree arg = callback_call_arg_summary (stmt, i, ci);
if (arg == name)
{
@@ -1216,6 +1230,8 @@ create_parameter_descriptors (cgraph_node *node,
gensum_param_desc *desc = &(*param_descriptions)[num];
/* param_descriptions vector is grown cleared in the caller. */
desc->param_number = num;
+ /* dont_disqualify is only used for outlined .ASSUME bodies. */
+ desc->dont_disqualify = node->assume_function;
decl2desc->put (parm, desc);
if (dump_file && (dump_flags & TDF_DETAILS))
@@ -1387,7 +1403,7 @@ get_gensum_param_desc (tree decl)
static void
disqualify_split_candidate (gensum_param_desc *desc, const char *reason)
{
- if (!desc->split_candidate)
+ if (!desc->split_candidate || desc->dont_disqualify)
return;
if (dump_file && (dump_flags & TDF_DETAILS))
@@ -2056,13 +2072,36 @@ scan_function (cgraph_node *node, struct function *fun)
unsigned argument_count = gimple_call_num_args (stmt);
isra_scan_context ctx = ISRA_CTX_ARG;
scan_call_info call_info, *call_info_p = &call_info;
+
if (gimple_call_internal_p (stmt))
{
- call_info_p = NULL;
- ctx = ISRA_CTX_LOAD;
- internal_fn ifn = gimple_call_internal_fn (stmt);
- if (internal_store_fn_p (ifn))
- ctx = ISRA_CTX_STORE;
+ if (gimple_call_internal_fn (stmt) == IFN_ASSUME)
+ {
+ tree callee_decl
+ = TREE_OPERAND (gimple_call_arg (stmt, 0), 0);
+ if (!lookup_attribute ("noipa",
+ DECL_ATTRIBUTES (callee_decl)))
+ {
+ cgraph_node *target
+ = cgraph_node::get_create (callee_decl);
+ cgraph_edge *cbei
+ = cgraph_edge::make_callback_internal (
+ node, target, as_a<gcall *> (stmt));
+ callback_info *ci
+ = callback_info::initialize_internal_summary (
+ cbei, IFN_ASSUME);
+ argument_count = ci->arg_mapping.length ();
+ }
+ }
+ else
+ {
+ call_info_p = NULL;
+ call_info.cs = NULL;
+ ctx = ISRA_CTX_LOAD;
+ internal_fn ifn = gimple_call_internal_fn (stmt);
+ if (internal_store_fn_p (ifn))
+ ctx = ISRA_CTX_STORE;
+ }
}
else
{
@@ -2073,8 +2112,11 @@ scan_function (cgraph_node *node, struct function *fun)
for (unsigned i = 0; i < argument_count; i++)
{
call_info.arg_idx = i;
- scan_expr_access (gimple_call_arg (stmt, i), stmt,
- ctx, bb, call_info_p);
+ callback_info *ci = NULL;
+ if (call_info.cs)
+ ci = callback_info_sum->get (call_info.cs);
+ scan_expr_access (callback_call_arg_summary (stmt, i, ci),
+ stmt, ctx, bb, call_info_p);
}
tree lhs = gimple_call_lhs (stmt);
@@ -2183,13 +2225,14 @@ ssa_name_only_returned_p (function *fun, tree name,
bitmap analyzed)
static void
isra_analyze_call (cgraph_edge *cs)
{
+ callback_info *ci = callback_info_sum->get (cs);
gcall *call_stmt = cs->call_stmt;
- unsigned count = gimple_call_num_args (call_stmt);
+ unsigned count = callback_call_num_args_summary (call_stmt, ci);
isra_call_summary *csum = call_sums->get_create (cs);
for (unsigned i = 0; i < count; i++)
{
- tree arg = gimple_call_arg (call_stmt, i);
+ tree arg = callback_call_arg_summary (call_stmt, i, ci);
if (TREE_CODE (arg) == ADDR_EXPR)
{
poly_int64 poffset, psize, pmax_size;
@@ -2607,7 +2650,8 @@ process_scan_results (cgraph_node *node, struct function
*fun,
const attribute is wrong) and then we just don't care. */
bool uses_memory_as_obtained = vuse && SSA_NAME_IS_DEFAULT_DEF (vuse);
- unsigned count = gimple_call_num_args (call_stmt);
+ callback_info *ci = callback_info_sum->get (cs);
+ unsigned count = callback_call_num_args_summary (call_stmt, ci);
isra_call_summary *csum = call_sums->get_create (cs);
csum->init_inputs (count);
csum->m_before_any_store = uses_memory_as_obtained;
@@ -2690,6 +2734,7 @@ process_scan_results (cgraph_node *node, struct function
*fun,
d->size_reached = s->nonarg_acc_size;
d->locally_unused = s->locally_unused;
d->split_candidate = s->split_candidate;
+ d->dont_disqualify = s->dont_disqualify;
d->by_ref = s->by_ref;
d->remove_only_when_retval_removed = s->remove_only_when_retval_removed;
d->split_only_when_retval_removed = s->split_only_when_retval_removed;
@@ -2778,6 +2823,7 @@ ipa_sra_generate_summary (void)
= (new (ggc_alloc_no_dtor <ipa_sra_function_summaries> ())
ipa_sra_function_summaries (symtab, true));
call_sums = new ipa_sra_call_summaries (symtab);
+ callback_info_sum_t::check_create_info_sum ();
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
ipa_sra_summarize_function (node);
@@ -2852,6 +2898,7 @@ isra_write_node_summary (output_block *ob, cgraph_node
*node)
bitpack_d bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, desc->locally_unused, 1);
bp_pack_value (&bp, desc->split_candidate, 1);
+ bp_pack_value (&bp, desc->dont_disqualify, 1);
bp_pack_value (&bp, desc->by_ref, 1);
gcc_assert (!desc->not_specially_constructed);
bp_pack_value (&bp, desc->remove_only_when_retval_removed, 1);
@@ -2978,6 +3025,7 @@ isra_read_node_info (struct lto_input_block *ib,
cgraph_node *node,
bitpack_d bp = streamer_read_bitpack (ib);
desc->locally_unused = bp_unpack_value (&bp, 1);
desc->split_candidate = bp_unpack_value (&bp, 1);
+ desc->dont_disqualify = bp_unpack_value (&bp, 1);
desc->by_ref = bp_unpack_value (&bp, 1);
desc->not_specially_constructed = 0;
desc->remove_only_when_retval_removed = bp_unpack_value (&bp, 1);
@@ -3114,13 +3162,21 @@ ipa_sra_dump_all_summaries (FILE *f, bool hints)
fprintf (f, "\n\n");
}
+/* Return TRUE iff the node can be made local or if it's an artificial .ASSUME
+ body (addressable, but only visible in the current TU). */
+static bool
+can_be_local_p (cgraph_node *n)
+{
+ return n->can_be_local_p () || n->assume_function;
+}
+
/* Perform function-scope viability tests that can be only made at IPA level
and return false if the function is deemed unsuitable for IPA-SRA. */
static bool
ipa_sra_ipa_function_checks (cgraph_node *node)
{
- if (!node->can_be_local_p ())
+ if (!can_be_local_p (node))
{
if (dump_file)
fprintf (dump_file, "Function %s disqualified because it cannot be "
@@ -3280,6 +3336,9 @@ find_param_access (isra_param_desc *param_desc, unsigned
offset, unsigned size)
static bool
size_would_violate_limit_p (isra_param_desc *desc, unsigned size)
{
+ if (desc->dont_disqualify)
+ return false;
+
unsigned limit = desc->param_size_limit;
if (size > limit
|| (!desc->by_ref && size == limit))
@@ -4028,6 +4087,11 @@ retval_used_p (cgraph_node *node, void *)
return true;
}
+ /* Keep the return value, even though it's not used in the code directly,
+ because it's used by vrp later. */
+ if (node->assume_function)
+ return true;
+
return false;
}
diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc
index adbf59ca821..7163cedbd5e 100644
--- a/gcc/lto-cgraph.cc
+++ b/gcc/lto-cgraph.cc
@@ -562,6 +562,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct
cgraph_node *node,
bp_pack_value (&bp, node->tm_clone, 1);
bp_pack_value (&bp, node->calls_comdat_local, 1);
bp_pack_value (&bp, node->icf_merged, 1);
+ bp_pack_value (&bp, node->assume_function, 1);
bp_pack_value (&bp, node->nonfreeing_fn, 1);
bp_pack_value (&bp, node->merged_comdat, 1);
bp_pack_value (&bp, node->merged_extern_inline, 1);
@@ -1300,6 +1301,7 @@ input_overwrite_node (struct lto_file_decl_data
*file_data,
node->tm_clone = bp_unpack_value (bp, 1);
node->calls_comdat_local = bp_unpack_value (bp, 1);
node->icf_merged = bp_unpack_value (bp, 1);
+ node->assume_function = bp_unpack_value (bp, 1);
node->nonfreeing_fn = bp_unpack_value (bp, 1);
node->merged_comdat = bp_unpack_value (bp, 1);
node->merged_extern_inline = bp_unpack_value (bp, 1);
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C
b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C
new file mode 100644
index 00000000000..009b315ac42
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C
@@ -0,0 +1,33 @@
+/* Test that the assume can be copied before creating the edges. */
+
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+struct S
+{
+ int x, y;
+};
+
+inline bool
+baz (S x)
+{
+ return x.x < 42;
+}
+
+inline bool
+qux (S x, int h)
+{
+ [[assume (baz (x))]];
+ return x.x < 42 && x.y > 52 && h < 50;
+}
+
+void
+corge (S x, S y, S z, bool *p)
+{
+ for (int i = 0; i < x.x + x.y; i++)
+ {
+ p[0] = qux (x, 41);
+ p[1] = qux (y, 60);
+ p[2] = qux (z, 20);
+ }
+}
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C
b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C
new file mode 100644
index 00000000000..f76b7e68773
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C
@@ -0,0 +1,30 @@
+/* Test that the assume can be copied after creating the edges. */
+
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-early-inlining" } */
+
+struct S { int x, y; };
+
+inline bool
+baz (S x)
+{
+ return x.x < 42;
+}
+
+[[gnu::always_inline]]
+inline bool
+qux (S x, int h)
+{
+ [[assume (baz (x))]];
+ return x.x < 42 && x.y > 52 && h < 50;
+}
+
+void
+corge (S x, S y, S z, bool *p)
+{
+ for (int i = 0; i < x.x + x.y; i++) {
+ p[0] = qux (x, 41);
+ p[1] = qux (y, 60);
+ p[2] = qux (z, 20);
+ }
+}
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C
b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C
new file mode 100644
index 00000000000..21e93ebf610
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C
@@ -0,0 +1,37 @@
+/* Test that ipa-sra can work with .ASSUME outlined bodies. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra-details -fdump-tree-optimized" }
*/
+
+struct S
+{
+ bool x;
+ int y;
+};
+
+void
+split_struct (struct S s)
+{
+ [[assume (!s.x)]];
+ if (s.x)
+ __builtin_abort ();
+}
+
+void
+remove_ptr (bool *x)
+{
+ [[assume (!*x)]];
+ if (*x)
+ __builtin_abort ();
+}
+
+void
+two_values (struct S s)
+{
+ [[assume (s.y == 3 || s.y == 4)]];
+ if (s.y == 5)
+ __builtin_abort ();
+}
+
+/* { dg-final { scan-ipa-dump-times "Will split parameter 0" 3 "sra" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_abort" 0 "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C
b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C
new file mode 100644
index 00000000000..c4de5f86d3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C
@@ -0,0 +1,28 @@
+/* { dg-lto-options {{ -O3 -flto -fdump-tree-optimized }} } */
+/* { dg-lto-do link } */
+
+struct S
+{
+ int x;
+};
+
+int bar (struct S);
+
+void
+foo (struct S s)
+{
+ int c = bar (s);
+ for (int i = 0; i < c; i++)
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ int x;
+ __builtin_scanf ("%d", &x);
+ foo ({x});
+ return 0;
+}
+
+/* { dg-final { scan-ltrans-tree-dump-not "__builtin_abort" "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C
b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C
new file mode 100644
index 00000000000..a33ed888a32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C
@@ -0,0 +1,11 @@
+struct S
+{
+ int x;
+};
+
+int
+bar (struct S s)
+{
+ [[assume (s.x < 0)]];
+ return s.x;
+}
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 302ab8d6b7c..b1b058a2eff 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -2896,6 +2896,7 @@ initialize_cfun (tree new_fndecl, tree callee_fndecl,
profile_count count)
cfun->can_delete_dead_exceptions = src_cfun->can_delete_dead_exceptions;
cfun->returns_struct = src_cfun->returns_struct;
cfun->returns_pcc_struct = src_cfun->returns_pcc_struct;
+ cfun->assume_function = src_cfun->assume_function;
init_empty_tree_cfg ();
--
2.54.0