New IPA-SRA

2019-09-20  Martin Jambor  <mjambor@suse.cz>

        * coretypes.h (cgraph_edge): Declare.
        * ipa-param-manipulation.c: Rewrite.
        * ipa-param-manipulation.h: Likewise.
        * Makefile.in (GTFILES): Added ipa-param-manipulation.h and ipa-sra.c.
        (OBJS): Added ipa-sra.o.
        * cgraph.h (ipa_replace_map): Removed fields old_tree, replace_p
        and ref_p, added fields param_adjustments and performed_splits.
        (struct cgraph_clone_info): Remove ags_to_skip and
        combined_args_to_skip, new field param_adjustments.
        (cgraph_node::create_clone): Changed parameters to use
        ipa_param_adjustments.
        (cgraph_node::create_virtual_clone): Likewise.
        (cgraph_node::create_virtual_clone_with_body): Likewise.
        (tree_function_versioning): Likewise.
        (cgraph_build_function_type_skip_args): Removed.
        * cgraph.c (cgraph_edge::redirect_call_stmt_to_callee): Convert to
        using ipa_param_adjustments.
        (clone_of_p): Likewise.
        * cgraphclones.c (cgraph_build_function_type_skip_args): Removed.
        (build_function_decl_skip_args): Likewise.
        (duplicate_thunk_for_node): Adjust parameters using
        ipa_param_body_adjustments, copy param_adjustments instead of
        args_to_skip.
        (cgraph_node::create_clone): Convert to using ipa_param_adjustments.
        (cgraph_node::create_virtual_clone): Likewise.
        (cgraph_node::create_version_clone_with_body): Likewise.
        (cgraph_materialize_clone): Likewise.
        (symbol_table::materialize_all_clones): Likewise.
        * ipa-fnsummary.c (ipa_fn_summary_t::duplicate): Simplify
        ipa_replace_map check.
        * ipa-cp.c (get_replacement_map): Do not initialize removed fields.
        (initialize_node_lattices): Make aware that some parameters might have
        already been removed.
        (want_remove_some_param_p): New function.
        (create_specialized_node): Convert to using ipa_param_adjustments and
        deal with possibly pre-existing adjustments.
        * lto-cgraph.c (output_cgraph_opt_summary_p): Likewise.
        (output_node_opt_summary): Do not stream removed fields.  Stream
        parameter adjustments instead of argumetns to skip.
        (input_node_opt_summary): Likewise.
        (input_node_opt_summary): Likewise.
        * lto-section-in.c (lto_section_name): Added ipa-sra section.
        * lto-streamer.h (lto_section_type): Likewise.
        * tree-inline.h (copy_body_data): New fields killed_new_ssa_names and
	param_body_adjs.
        (copy_decl_to_var): Declare.
        * tree-inline.c (update_clone_info): Do not remap old_tree.
        (remap_gimple_stmt): Use ipa_param_body_adjustments to modify gimple
        statements, walk all extra generated statements and remap their
        operands.
        (redirect_all_calls): Add killed SSA names to a hash set.
        (remap_ssa_name): Do not remap killed SSA names.
        (copy_arguments_for_versioning): Renames to copy_arguments_nochange,
        half of functionality moved to ipa_param_body_adjustments.
        (copy_decl_to_var): Make exported.
        (copy_body): Destroy killed_new_ssa_names hash set.
        (expand_call_inline): Remap performed splits.
        (update_clone_info): Likewise.
        (tree_function_versioning): Simplify tree_map processing.  Updated to
        accept ipa_param_adjustments and use ipa_param_body_adjustments.
        * omp-simd-clone.c (simd_clone_vector_of_formal_parm_types): Adjust
        for the new interface.
        (simd_clone_clauses_extract): Likewise, make args an auto_vec.
        (simd_clone_compute_base_data_type): Likewise.
        (simd_clone_init_simd_arrays): Adjust for the new interface.
        (simd_clone_adjust_argument_types): Likewise.
        (struct modify_stmt_info): Likewise.
        (ipa_simd_modify_stmt_ops): Likewise.
        (ipa_simd_modify_function_body): Likewise.
        (simd_clone_adjust): Likewise.
        * tree-sra.c: Removed IPA-SRA.  Include tree-sra.h.
        (type_internals_preclude_sra_p): Make public.
        * tree-sra.h: New file.
        * ipa-inline-transform.c (save_inline_function_body): Update to
        refelct new tree_function_versioning signature.
        * ipa-prop.c (adjust_agg_replacement_values): Use a helper from
        ipa_param_adjustments to get current parameter indices.
        (ipcp_modif_dom_walker::before_dom_children): Likewise.
        (ipcp_update_bits): Likewise.
        (ipcp_update_vr): Likewise.
        * ipa-split.c (split_function): Convert to using ipa_param_adjustments.
        * ipa-sra.c: New file.
        * multiple_target.c (create_target_clone): Update to reflet new type
        of create_version_clone_with_body.
        * trans-mem.c (ipa_tm_create_version): Update to reflect new type of
        tree_function_versioning.
	(modify_function): Update to reflect new type of
	tree_function_versioning.
        * params.def (PARAM_IPA_SRA_MAX_REPLACEMENTS): New.
        * passes.def: Remove old IPA-SRA and add new one.
        * tree-pass.h (make_pass_early_ipa_sra): Remove declaration.
        (make_pass_ipa_sra): Declare.
	* dbgcnt.def: Remove eipa_sra.  Added ipa_sra_params and
	ipa_sra_retvalues.
	* doc/invoke.texi (ipa-sra-max-replacements): New.

        testsuite/
        * g++.dg/ipa/pr81248.C: Adjust dg-options and dump-scan.
        * gcc.dg/ipa/ipa-sra-1.c: Likewise.
        * gcc.dg/ipa/ipa-sra-10.c: Likewise.
        * gcc.dg/ipa/ipa-sra-11.c: Likewise.
        * gcc.dg/ipa/ipa-sra-3.c: Likewise.
        * gcc.dg/ipa/ipa-sra-4.c: Likewise.
        * gcc.dg/ipa/ipa-sra-5.c: Likewise.
        * gcc.dg/ipa/ipacost-2.c: Disable ipa-sra.
        * gcc.dg/ipa/ipcp-agg-9.c: Likewise.
        * gcc.dg/ipa/pr78121.c: Adjust scan pattern.
        * gcc.dg/ipa/vrp1.c: Likewise.
        * gcc.dg/ipa/vrp2.c: Likewise.
        * gcc.dg/ipa/vrp3.c: Likewise.
        * gcc.dg/ipa/vrp7.c: Likewise.
        * gcc.dg/ipa/vrp8.c: Likewise.
        * gcc.dg/noreorder.c: use noipa attribute instead of noinline.
        * gcc.dg/ipa/20040703-wpa.c: New test.
	* gcc.dg/ipa/ipa-sra-12.c: New test.
	* gcc.dg/ipa/ipa-sra-13.c: Likewise.
	* gcc.dg/ipa/ipa-sra-14.c: Likewise.
	* gcc.dg/ipa/ipa-sra-15.c: Likewise.
	* gcc.dg/ipa/ipa-sra-16.c: Likewise.
	* gcc.dg/ipa/ipa-sra-17.c: Likewise.
	* gcc.dg/ipa/ipa-sra-18.c: Likewise.
	* gcc.dg/ipa/ipa-sra-19.c: Likewise.
	* gcc.dg/ipa/ipa-sra-20.c: Likewise.
	* gcc.dg/ipa/ipa-sra-21.c: Likewise.
	* gcc.dg/ipa/ipa-sra-22.c: Likewise.
	* gcc.dg/sso/ipa-sra-1.c: Likewise.
	* g++.dg/ipa/ipa-sra-2.C: Likewise.
	* g++.dg/ipa/ipa-sra-3.C: Likewise.
	* gcc.dg/tree-ssa/ipa-cp-1.c: Make return value used.
	* g++.dg/ipa/devirt-19.C: Add missing return, add -fipa-cp-clone
	option.
	* g++.dg/lto/devirt-19_0.C: Add -fipa-cp-clone option.
	* gcc.dg/ipa/ipa-sra-2.c: Removed.
	* gcc.dg/ipa/ipa-sra-6.c: Likewise.

From-SVN: r275982
This commit is contained in:
Martin Jambor 2019-09-20 00:25:04 +02:00 committed by Martin Jambor
parent 6889a3acfe
commit ff6686d2e5
67 changed files with 8029 additions and 3174 deletions

View File

@ -1,3 +1,101 @@
2019-09-20 Martin Jambor <mjambor@suse.cz>
* coretypes.h (cgraph_edge): Declare.
* ipa-param-manipulation.c: Rewrite.
* ipa-param-manipulation.h: Likewise.
* Makefile.in (GTFILES): Added ipa-param-manipulation.h and ipa-sra.c.
(OBJS): Added ipa-sra.o.
* cgraph.h (ipa_replace_map): Removed fields old_tree, replace_p
and ref_p, added fields param_adjustments and performed_splits.
(struct cgraph_clone_info): Remove ags_to_skip and
combined_args_to_skip, new field param_adjustments.
(cgraph_node::create_clone): Changed parameters to use
ipa_param_adjustments.
(cgraph_node::create_virtual_clone): Likewise.
(cgraph_node::create_virtual_clone_with_body): Likewise.
(tree_function_versioning): Likewise.
(cgraph_build_function_type_skip_args): Removed.
* cgraph.c (cgraph_edge::redirect_call_stmt_to_callee): Convert to
using ipa_param_adjustments.
(clone_of_p): Likewise.
* cgraphclones.c (cgraph_build_function_type_skip_args): Removed.
(build_function_decl_skip_args): Likewise.
(duplicate_thunk_for_node): Adjust parameters using
ipa_param_body_adjustments, copy param_adjustments instead of
args_to_skip.
(cgraph_node::create_clone): Convert to using ipa_param_adjustments.
(cgraph_node::create_virtual_clone): Likewise.
(cgraph_node::create_version_clone_with_body): Likewise.
(cgraph_materialize_clone): Likewise.
(symbol_table::materialize_all_clones): Likewise.
* ipa-fnsummary.c (ipa_fn_summary_t::duplicate): Simplify
ipa_replace_map check.
* ipa-cp.c (get_replacement_map): Do not initialize removed fields.
(initialize_node_lattices): Make aware that some parameters might have
already been removed.
(want_remove_some_param_p): New function.
(create_specialized_node): Convert to using ipa_param_adjustments and
deal with possibly pre-existing adjustments.
* lto-cgraph.c (output_cgraph_opt_summary_p): Likewise.
(output_node_opt_summary): Do not stream removed fields. Stream
parameter adjustments instead of argumetns to skip.
(input_node_opt_summary): Likewise.
(input_node_opt_summary): Likewise.
* lto-section-in.c (lto_section_name): Added ipa-sra section.
* lto-streamer.h (lto_section_type): Likewise.
* tree-inline.h (copy_body_data): New fields killed_new_ssa_names and
param_body_adjs.
(copy_decl_to_var): Declare.
* tree-inline.c (update_clone_info): Do not remap old_tree.
(remap_gimple_stmt): Use ipa_param_body_adjustments to modify gimple
statements, walk all extra generated statements and remap their
operands.
(redirect_all_calls): Add killed SSA names to a hash set.
(remap_ssa_name): Do not remap killed SSA names.
(copy_arguments_for_versioning): Renames to copy_arguments_nochange,
half of functionality moved to ipa_param_body_adjustments.
(copy_decl_to_var): Make exported.
(copy_body): Destroy killed_new_ssa_names hash set.
(expand_call_inline): Remap performed splits.
(update_clone_info): Likewise.
(tree_function_versioning): Simplify tree_map processing. Updated to
accept ipa_param_adjustments and use ipa_param_body_adjustments.
* omp-simd-clone.c (simd_clone_vector_of_formal_parm_types): Adjust
for the new interface.
(simd_clone_clauses_extract): Likewise, make args an auto_vec.
(simd_clone_compute_base_data_type): Likewise.
(simd_clone_init_simd_arrays): Adjust for the new interface.
(simd_clone_adjust_argument_types): Likewise.
(struct modify_stmt_info): Likewise.
(ipa_simd_modify_stmt_ops): Likewise.
(ipa_simd_modify_function_body): Likewise.
(simd_clone_adjust): Likewise.
* tree-sra.c: Removed IPA-SRA. Include tree-sra.h.
(type_internals_preclude_sra_p): Make public.
* tree-sra.h: New file.
* ipa-inline-transform.c (save_inline_function_body): Update to
refelct new tree_function_versioning signature.
* ipa-prop.c (adjust_agg_replacement_values): Use a helper from
ipa_param_adjustments to get current parameter indices.
(ipcp_modif_dom_walker::before_dom_children): Likewise.
(ipcp_update_bits): Likewise.
(ipcp_update_vr): Likewise.
* ipa-split.c (split_function): Convert to using ipa_param_adjustments.
* ipa-sra.c: New file.
* multiple_target.c (create_target_clone): Update to reflet new type
of create_version_clone_with_body.
* trans-mem.c (ipa_tm_create_version): Update to reflect new type of
tree_function_versioning.
(modify_function): Update to reflect new type of
tree_function_versioning.
* params.def (PARAM_IPA_SRA_MAX_REPLACEMENTS): New.
* passes.def: Remove old IPA-SRA and add new one.
* tree-pass.h (make_pass_early_ipa_sra): Remove declaration.
(make_pass_ipa_sra): Declare.
* dbgcnt.def: Remove eipa_sra. Added ipa_sra_params and
ipa_sra_retvalues.
* doc/invoke.texi (ipa-sra-max-replacements): New.
2019-09-19 Martin Sebor <msebor@redhat.com>
PR middle-end/91631

View File

@ -1368,6 +1368,7 @@ OBJS = \
init-regs.o \
internal-fn.o \
ipa-cp.o \
ipa-sra.o \
ipa-devirt.o \
ipa-fnsummary.o \
ipa-polymorphic-call.o \
@ -2527,7 +2528,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
$(srcdir)/reload.h $(srcdir)/caller-save.c $(srcdir)/symtab.c \
$(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
$(srcdir)/ipa-prop.c $(srcdir)/ipa-cp.c $(srcdir)/ipa-utils.h \
$(srcdir)/dbxout.c \
$(srcdir)/ipa-param-manipulation.h $(srcdir)/ipa-sra.c $(srcdir)/dbxout.c \
$(srcdir)/signop.h \
$(srcdir)/dwarf2out.h \
$(srcdir)/dwarf2asm.c \

View File

@ -1342,7 +1342,7 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
if (flag_checking && decl)
{
cgraph_node *node = cgraph_node::get (decl);
gcc_assert (!node || !node->clone.combined_args_to_skip);
gcc_assert (!node || !node->clone.param_adjustments);
}
if (symtab->dump_file)
@ -1350,25 +1350,36 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
fprintf (symtab->dump_file, "updating call of %s -> %s: ",
e->caller->dump_name (), e->callee->dump_name ());
print_gimple_stmt (symtab->dump_file, e->call_stmt, 0, dump_flags);
if (e->callee->clone.combined_args_to_skip)
if (e->callee->clone.param_adjustments)
e->callee->clone.param_adjustments->dump (symtab->dump_file);
unsigned performed_len
= vec_safe_length (e->caller->clone.performed_splits);
if (performed_len > 0)
fprintf (symtab->dump_file, "Performed splits records:\n");
for (unsigned i = 0; i < performed_len; i++)
{
fprintf (symtab->dump_file, " combined args to skip: ");
dump_bitmap (symtab->dump_file,
e->callee->clone.combined_args_to_skip);
ipa_param_performed_split *sm
= &(*e->caller->clone.performed_splits)[i];
print_node_brief (symtab->dump_file, " dummy_decl: ", sm->dummy_decl,
TDF_UID);
fprintf (symtab->dump_file, ", unit_offset: %u\n", sm->unit_offset);
}
}
if (e->callee->clone.combined_args_to_skip)
if (ipa_param_adjustments *padjs = e->callee->clone.param_adjustments)
{
int lp_nr;
/* We need to defer cleaning EH info on the new statement to
fixup-cfg. We may not have dominator information at this point
and thus would end up with unreachable blocks and have no way
to communicate that we need to run CFG cleanup then. */
int lp_nr = lookup_stmt_eh_lp (e->call_stmt);
if (lp_nr != 0)
remove_stmt_from_eh_lp (e->call_stmt);
new_stmt = e->call_stmt;
if (e->callee->clone.combined_args_to_skip)
new_stmt
= gimple_call_copy_skip_args (new_stmt,
e->callee->clone.combined_args_to_skip);
tree old_fntype = gimple_call_fntype (e->call_stmt);
gimple_call_set_fndecl (new_stmt, e->callee->decl);
new_stmt = padjs->modify_call (e->call_stmt,
e->caller->clone.performed_splits,
e->callee->decl, false);
cgraph_node *origin = e->callee;
while (origin->clone_of)
origin = origin->clone_of;
@ -1379,92 +1390,12 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
gimple_call_set_fntype (new_stmt, TREE_TYPE (e->callee->decl));
else
{
bitmap skip = e->callee->clone.combined_args_to_skip;
tree t = cgraph_build_function_type_skip_args (old_fntype, skip,
false);
gimple_call_set_fntype (new_stmt, t);
tree new_fntype = padjs->build_new_function_type (old_fntype, true);
gimple_call_set_fntype (new_stmt, new_fntype);
}
if (gimple_vdef (new_stmt)
&& TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
gsi = gsi_for_stmt (e->call_stmt);
/* For optimized away parameters, add on the caller side
before the call
DEBUG D#X => parm_Y(D)
stmts and associate D#X with parm in decl_debug_args_lookup
vector to say for debug info that if parameter parm had been passed,
it would have value parm_Y(D). */
if (e->callee->clone.combined_args_to_skip && MAY_HAVE_DEBUG_BIND_STMTS)
{
vec<tree, va_gc> **debug_args
= decl_debug_args_lookup (e->callee->decl);
tree old_decl = gimple_call_fndecl (e->call_stmt);
if (debug_args && old_decl)
{
tree parm;
unsigned i = 0, num;
unsigned len = vec_safe_length (*debug_args);
unsigned nargs = gimple_call_num_args (e->call_stmt);
for (parm = DECL_ARGUMENTS (old_decl), num = 0;
parm && num < nargs;
parm = DECL_CHAIN (parm), num++)
if (bitmap_bit_p (e->callee->clone.combined_args_to_skip, num)
&& is_gimple_reg (parm))
{
unsigned last = i;
while (i < len && (**debug_args)[i] != DECL_ORIGIN (parm))
i += 2;
if (i >= len)
{
i = 0;
while (i < last
&& (**debug_args)[i] != DECL_ORIGIN (parm))
i += 2;
if (i >= last)
continue;
}
tree ddecl = (**debug_args)[i + 1];
tree arg = gimple_call_arg (e->call_stmt, num);
if (!useless_type_conversion_p (TREE_TYPE (ddecl),
TREE_TYPE (arg)))
{
tree rhs1;
if (!fold_convertible_p (TREE_TYPE (ddecl), arg))
continue;
if (TREE_CODE (arg) == SSA_NAME
&& gimple_assign_cast_p (SSA_NAME_DEF_STMT (arg))
&& (rhs1
= gimple_assign_rhs1 (SSA_NAME_DEF_STMT (arg)))
&& useless_type_conversion_p (TREE_TYPE (ddecl),
TREE_TYPE (rhs1)))
arg = rhs1;
else
arg = fold_convert (TREE_TYPE (ddecl), arg);
}
gimple *def_temp
= gimple_build_debug_bind (ddecl, unshare_expr (arg),
e->call_stmt);
gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
}
}
}
gsi_replace (&gsi, new_stmt, false);
/* We need to defer cleaning EH info on the new statement to
fixup-cfg. We may not have dominator information at this point
and thus would end up with unreachable blocks and have no way
to communicate that we need to run CFG cleanup then. */
lp_nr = lookup_stmt_eh_lp (e->call_stmt);
if (lp_nr != 0)
{
remove_stmt_from_eh_lp (e->call_stmt);
add_stmt_to_eh_lp (new_stmt, lp_nr);
}
add_stmt_to_eh_lp (new_stmt, lp_nr);
}
else
{
@ -3014,8 +2945,8 @@ clone_of_p (cgraph_node *node, cgraph_node *node2)
return true;
node = node->callees->callee->ultimate_alias_target ();
if (!node2->clone.args_to_skip
|| !bitmap_bit_p (node2->clone.args_to_skip, 0))
if (!node2->clone.param_adjustments
|| node2->clone.param_adjustments->first_param_intact_p ())
return false;
if (node2->former_clone_of == node->decl)
return true;

View File

@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
#include "profile-count.h"
#include "ipa-ref.h"
#include "plugin-api.h"
#include "ipa-param-manipulation.h"
extern void debuginfo_early_init (void);
extern void debuginfo_init (void);
@ -740,23 +741,31 @@ struct GTY(()) cgraph_global_info {
will be replaced by another tree while versioning. */
struct GTY(()) ipa_replace_map
{
/* The tree that will be replaced. */
tree old_tree;
/* The new (replacing) tree. */
tree new_tree;
/* Parameter number to replace, when old_tree is NULL. */
int parm_num;
/* True when a substitution should be done, false otherwise. */
bool replace_p;
/* True when we replace a reference to old_tree. */
bool ref_p;
};
struct GTY(()) cgraph_clone_info
{
/* Constants discovered by IPA-CP, i.e. which parameter should be replaced
with what. */
vec<ipa_replace_map *, va_gc> *tree_map;
bitmap args_to_skip;
bitmap combined_args_to_skip;
/* Parameter modification that IPA-SRA decided to perform. */
ipa_param_adjustments *param_adjustments;
/* Lists of dummy-decl and offset pairs representing split formal parameters
in the caller. Offsets of all new replacements are enumerated, those
coming from the same original parameter have the same dummy decl stored
along with them.
Dummy decls sit in call statement arguments followed by new parameter
decls (or their SSA names) in between (caller) clone materialization and
call redirection. Redirection then recognizes the dummy variable and
together with the stored offsets can reconstruct what exactly the new
parameter decls represent and can leave in place only those that the
callee expects. */
vec<ipa_param_performed_split, va_gc> *performed_splits;
};
enum cgraph_simd_clone_arg_type
@ -976,15 +985,16 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
vec<cgraph_edge *> redirect_callers,
bool call_duplication_hook,
cgraph_node *new_inlined_to,
bitmap args_to_skip, const char *suffix = NULL);
ipa_param_adjustments *param_adjustments,
const char *suffix = NULL);
/* Create callgraph node clone with new declaration. The actual body will be
copied later at compilation stage. The name of the new clone will be
constructed from the name of the original node, SUFFIX and NUM_SUFFIX. */
cgraph_node *create_virtual_clone (vec<cgraph_edge *> redirect_callers,
vec<ipa_replace_map *, va_gc> *tree_map,
bitmap args_to_skip, const char * suffix,
unsigned num_suffix);
ipa_param_adjustments *param_adjustments,
const char * suffix, unsigned num_suffix);
/* cgraph node being removed from symbol table; see if its entry can be
replaced by other inline clone. */
@ -1033,9 +1043,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
Return the new version's cgraph node. */
cgraph_node *create_version_clone_with_body
(vec<cgraph_edge *> redirect_callers,
vec<ipa_replace_map *, va_gc> *tree_map, bitmap args_to_skip,
bool skip_return, bitmap bbs_to_copy, basic_block new_entry_block,
const char *clone_name, tree target_attributes = NULL_TREE);
vec<ipa_replace_map *, va_gc> *tree_map,
ipa_param_adjustments *param_adjustments,
bitmap bbs_to_copy, basic_block new_entry_block, const char *clone_name,
tree target_attributes = NULL_TREE);
/* Insert a new cgraph_function_version_info node into cgraph_fnver_htab
corresponding to cgraph_node. */
@ -2459,14 +2470,12 @@ tree clone_function_name (tree decl, const char *suffix,
tree clone_function_name (tree decl, const char *suffix);
void tree_function_versioning (tree, tree, vec<ipa_replace_map *, va_gc> *,
bool, bitmap, bool, bitmap, basic_block);
ipa_param_adjustments *,
bool, bitmap, basic_block);
void dump_callgraph_transformation (const cgraph_node *original,
const cgraph_node *clone,
const char *suffix);
tree cgraph_build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
bool skip_return);
/* In cgraphbuild.c */
int compute_call_stmt_bb_frequency (tree, basic_block bb);
void record_references_in_initializer (tree, bool);

View File

@ -142,96 +142,6 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
return new_edge;
}
/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP and the
return value if SKIP_RETURN is true. */
tree
cgraph_build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
bool skip_return)
{
tree new_type = NULL;
tree args, new_args = NULL;
tree new_reversed;
int i = 0;
for (args = TYPE_ARG_TYPES (orig_type); args && args != void_list_node;
args = TREE_CHAIN (args), i++)
if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
new_args = tree_cons (NULL_TREE, TREE_VALUE (args), new_args);
new_reversed = nreverse (new_args);
if (args)
{
if (new_reversed)
TREE_CHAIN (new_args) = void_list_node;
else
new_reversed = void_list_node;
}
/* Use copy_node to preserve as much as possible from original type
(debug info, attribute lists etc.)
Exception is METHOD_TYPEs must have THIS argument.
When we are asked to remove it, we need to build new FUNCTION_TYPE
instead. */
if (TREE_CODE (orig_type) != METHOD_TYPE
|| !args_to_skip
|| !bitmap_bit_p (args_to_skip, 0))
{
new_type = build_distinct_type_copy (orig_type);
TYPE_ARG_TYPES (new_type) = new_reversed;
}
else
{
new_type
= build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
new_reversed));
TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
}
if (skip_return)
TREE_TYPE (new_type) = void_type_node;
return new_type;
}
/* Build variant of function decl ORIG_DECL skipping ARGS_TO_SKIP and the
return value if SKIP_RETURN is true.
Arguments from DECL_ARGUMENTS list can't be removed now, since they are
linked by TREE_CHAIN directly. The caller is responsible for eliminating
them when they are being duplicated (i.e. copy_arguments_for_versioning). */
static tree
build_function_decl_skip_args (tree orig_decl, bitmap args_to_skip,
bool skip_return)
{
tree new_decl = copy_node (orig_decl);
tree new_type;
new_type = TREE_TYPE (orig_decl);
if (prototype_p (new_type)
|| (skip_return && !VOID_TYPE_P (TREE_TYPE (new_type))))
new_type
= cgraph_build_function_type_skip_args (new_type, args_to_skip,
skip_return);
TREE_TYPE (new_decl) = new_type;
/* For declarations setting DECL_VINDEX (i.e. methods)
we expect first argument to be THIS pointer. */
if (args_to_skip && bitmap_bit_p (args_to_skip, 0))
DECL_VINDEX (new_decl) = NULL_TREE;
/* When signature changes, we need to clear builtin info. */
if (fndecl_built_in_p (new_decl)
&& args_to_skip
&& !bitmap_empty_p (args_to_skip))
set_decl_built_in_function (new_decl, NOT_BUILT_IN, 0);
/* The FE might have information and assumptions about the other
arguments. */
DECL_LANG_SPECIFIC (new_decl) = NULL;
return new_decl;
}
/* Set flags of NEW_NODE and its decl. NEW_NODE is a newly created private
clone or its thunk. */
@ -281,35 +191,21 @@ duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
return cs->caller;
tree new_decl;
if (!node->clone.args_to_skip)
new_decl = copy_node (thunk->decl);
else
if (node->clone.param_adjustments)
{
/* We do not need to duplicate this_adjusting thunks if we have removed
this. */
if (thunk->thunk.this_adjusting
&& bitmap_bit_p (node->clone.args_to_skip, 0))
&& !node->clone.param_adjustments->first_param_intact_p ())
return node;
new_decl = build_function_decl_skip_args (thunk->decl,
node->clone.args_to_skip,
false);
new_decl = copy_node (thunk->decl);
ipa_param_body_adjustments body_adj (node->clone.param_adjustments,
new_decl);
body_adj.modify_formal_parameters ();
}
tree *link = &DECL_ARGUMENTS (new_decl);
int i = 0;
for (tree pd = DECL_ARGUMENTS (thunk->decl); pd; pd = DECL_CHAIN (pd), i++)
{
if (!node->clone.args_to_skip
|| !bitmap_bit_p (node->clone.args_to_skip, i))
{
tree nd = copy_node (pd);
DECL_CONTEXT (nd) = new_decl;
*link = nd;
link = &DECL_CHAIN (nd);
}
}
*link = NULL_TREE;
else
new_decl = copy_node (thunk->decl);
gcc_checking_assert (!DECL_STRUCT_FUNCTION (new_decl));
gcc_checking_assert (!DECL_INITIAL (new_decl));
@ -331,8 +227,7 @@ duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
new_thunk->thunk = thunk->thunk;
new_thunk->unique_name = in_lto_p;
new_thunk->former_clone_of = thunk->decl;
new_thunk->clone.args_to_skip = node->clone.args_to_skip;
new_thunk->clone.combined_args_to_skip = node->clone.combined_args_to_skip;
new_thunk->clone.param_adjustments = node->clone.param_adjustments;
cgraph_edge *e = new_thunk->create_edge (node, NULL, new_thunk->count);
symtab->call_edge_duplication_hooks (thunk->callees, e);
@ -415,7 +310,11 @@ dump_callgraph_transformation (const cgraph_node *original,
If the new node is being inlined into another one, NEW_INLINED_TO should be
the outline function the new one is (even indirectly) inlined to. All hooks
will see this in node's global.inlined_to, when invoked. Can be NULL if the
node is not inlined. */
node is not inlined.
If PARAM_ADJUSTMENTS is non-NULL, the parameter manipulation information
will be overwritten by the new structure. Otherwise the new node will
share parameter manipulation information with the original node. */
cgraph_node *
cgraph_node::create_clone (tree new_decl, profile_count prof_count,
@ -423,7 +322,8 @@ cgraph_node::create_clone (tree new_decl, profile_count prof_count,
vec<cgraph_edge *> redirect_callers,
bool call_duplication_hook,
cgraph_node *new_inlined_to,
bitmap args_to_skip, const char *suffix)
ipa_param_adjustments *param_adjustments,
const char *suffix)
{
cgraph_node *new_node = symtab->create_empty ();
cgraph_edge *e;
@ -467,19 +367,13 @@ cgraph_node::create_clone (tree new_decl, profile_count prof_count,
new_node->merged_comdat = merged_comdat;
new_node->thunk = thunk;
new_node->clone.tree_map = NULL;
new_node->clone.args_to_skip = args_to_skip;
new_node->split_part = split_part;
if (!args_to_skip)
new_node->clone.combined_args_to_skip = clone.combined_args_to_skip;
else if (clone.combined_args_to_skip)
{
new_node->clone.combined_args_to_skip = BITMAP_GGC_ALLOC ();
bitmap_ior (new_node->clone.combined_args_to_skip,
clone.combined_args_to_skip, args_to_skip);
}
if (param_adjustments)
new_node->clone.param_adjustments = param_adjustments;
else
new_node->clone.combined_args_to_skip = args_to_skip;
new_node->clone.param_adjustments = clone.param_adjustments;
new_node->clone.tree_map = NULL;
new_node->clone.performed_splits = vec_safe_copy (clone.performed_splits);
new_node->split_part = split_part;
FOR_EACH_VEC_ELT (redirect_callers, i, e)
{
@ -621,8 +515,8 @@ clone_function_name (tree decl, const char *suffix)
cgraph_node *
cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
vec<ipa_replace_map *, va_gc> *tree_map,
bitmap args_to_skip, const char * suffix,
unsigned num_suffix)
ipa_param_adjustments *param_adjustments,
const char * suffix, unsigned num_suffix)
{
tree old_decl = decl;
cgraph_node *new_node = NULL;
@ -632,13 +526,16 @@ cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
char *name;
gcc_checking_assert (local.versionable);
gcc_assert (local.can_change_signature || !args_to_skip);
/* TODO: It would be nice if we could recognize that param_adjustments do not
actually perform any changes, but at the moment let's require it simply
does not exist. */
gcc_assert (local.can_change_signature || !param_adjustments);
/* Make a new FUNCTION_DECL tree node */
if (!args_to_skip)
if (!param_adjustments)
new_decl = copy_node (old_decl);
else
new_decl = build_function_decl_skip_args (old_decl, args_to_skip, false);
new_decl = param_adjustments->adjust_decl (old_decl);
/* These pointers represent function body and will be populated only when clone
is materialized. */
@ -662,7 +559,8 @@ cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
SET_DECL_RTL (new_decl, NULL);
new_node = create_clone (new_decl, count, false,
redirect_callers, false, NULL, args_to_skip, suffix);
redirect_callers, false, NULL, param_adjustments,
suffix);
/* Update the properties.
Make clone visible only within this translation unit. Make sure
@ -1021,9 +919,10 @@ cgraph_node::create_version_clone (tree new_decl,
cgraph_node *
cgraph_node::create_version_clone_with_body
(vec<cgraph_edge *> redirect_callers,
vec<ipa_replace_map *, va_gc> *tree_map, bitmap args_to_skip,
bool skip_return, bitmap bbs_to_copy, basic_block new_entry_block,
const char *suffix, tree target_attributes)
vec<ipa_replace_map *, va_gc> *tree_map,
ipa_param_adjustments *param_adjustments,
bitmap bbs_to_copy, basic_block new_entry_block, const char *suffix,
tree target_attributes)
{
tree old_decl = decl;
cgraph_node *new_version_node = NULL;
@ -1032,14 +931,16 @@ cgraph_node::create_version_clone_with_body
if (!tree_versionable_function_p (old_decl))
return NULL;
gcc_assert (local.can_change_signature || !args_to_skip);
/* TODO: Restore an assert that we do not change signature if
local.can_change_signature is false. We cannot just check that
param_adjustments is NULL because unfortunately ipa-split removes return
values from such functions. */
/* Make a new FUNCTION_DECL tree node for the new version. */
if (!args_to_skip && !skip_return)
new_decl = copy_node (old_decl);
if (param_adjustments)
new_decl = param_adjustments->adjust_decl (old_decl);
else
new_decl
= build_function_decl_skip_args (old_decl, args_to_skip, skip_return);
new_decl = copy_node (old_decl);
/* Generate a new name for the new version. */
DECL_NAME (new_decl) = clone_function_name_numbered (old_decl, suffix);
@ -1076,8 +977,8 @@ cgraph_node::create_version_clone_with_body
new_version_node->ipa_transforms_to_apply
= ipa_transforms_to_apply.copy ();
/* Copy the OLD_VERSION_NODE function tree to the new version. */
tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
skip_return, bbs_to_copy, new_entry_block);
tree_function_versioning (old_decl, new_decl, tree_map, param_adjustments,
false, bbs_to_copy, new_entry_block);
/* Update the new version's properties.
Make The new version visible only within this translation unit. Make sure
@ -1117,9 +1018,8 @@ cgraph_materialize_clone (cgraph_node *node)
node->former_clone_of = node->clone_of->former_clone_of;
/* Copy the OLD_VERSION_NODE function tree to the new version. */
tree_function_versioning (node->clone_of->decl, node->decl,
node->clone.tree_map, true,
node->clone.args_to_skip, false,
NULL, NULL);
node->clone.tree_map, node->clone.param_adjustments,
true, NULL, NULL);
if (symtab->dump_file)
{
dump_function_to_file (node->clone_of->decl, symtab->dump_file,
@ -1194,28 +1094,15 @@ symbol_table::materialize_all_clones (void)
{
ipa_replace_map *replace_info;
replace_info = (*node->clone.tree_map)[i];
print_generic_expr (symtab->dump_file,
replace_info->old_tree);
fprintf (symtab->dump_file, " -> ");
fprintf (symtab->dump_file, "%i -> ",
(*node->clone.tree_map)[i]->parm_num);
print_generic_expr (symtab->dump_file,
replace_info->new_tree);
fprintf (symtab->dump_file, "%s%s;",
replace_info->replace_p ? "(replace)":"",
replace_info->ref_p ? "(ref)":"");
}
fprintf (symtab->dump_file, "\n");
}
if (node->clone.args_to_skip)
{
fprintf (symtab->dump_file, " args_to_skip: ");
dump_bitmap (symtab->dump_file,
node->clone.args_to_skip);
}
if (node->clone.args_to_skip)
{
fprintf (symtab->dump_file, " combined_args_to_skip:");
dump_bitmap (symtab->dump_file, node->clone.combined_args_to_skip);
}
if (node->clone.param_adjustments)
node->clone.param_adjustments->dump (symtab->dump_file);
}
cgraph_materialize_clone (node);
stabilized = false;

View File

@ -141,6 +141,7 @@ struct gomp_teams;
struct symtab_node;
struct cgraph_node;
struct varpool_node;
struct cgraph_edge;
union section;
typedef union section section;

View File

@ -156,7 +156,6 @@ DEBUG_COUNTER (df_byte_scan)
DEBUG_COUNTER (dse)
DEBUG_COUNTER (dse1)
DEBUG_COUNTER (dse2)
DEBUG_COUNTER (eipa_sra)
DEBUG_COUNTER (gcse2_delete)
DEBUG_COUNTER (global_alloc_at_func)
DEBUG_COUNTER (global_alloc_at_reg)
@ -168,6 +167,8 @@ DEBUG_COUNTER (if_after_combine)
DEBUG_COUNTER (if_after_reload)
DEBUG_COUNTER (if_conversion)
DEBUG_COUNTER (if_conversion_tree)
DEBUG_COUNTER (ipa_sra_params)
DEBUG_COUNTER (ipa_sra_retvalues)
DEBUG_COUNTER (ira_move)
DEBUG_COUNTER (local_alloc_for_sched)
DEBUG_COUNTER (merged_ipa_icf)

View File

@ -11870,6 +11870,11 @@ parameters only when their cumulative size is less or equal to
@option{ipa-sra-ptr-growth-factor} times the size of the original
pointer parameter.
@item ipa-sra-max-replacements
Maximum pieces of an aggregate that IPA-SRA tracks. As a
consequence, it is also the maximum number of replacements of a formal
parameter.
@item sra-max-scalarization-size-Ospeed
@itemx sra-max-scalarization-size-Osize
The two Scalar Reduction of Aggregates passes (SRA and IPA-SRA) aim to

View File

@ -1184,7 +1184,10 @@ initialize_node_lattices (struct cgraph_node *node)
int i;
gcc_checking_assert (node->has_gimple_body_p ());
if (node->local.local)
if (!ipa_get_param_count (info))
disable = true;
else if (node->local.local)
{
int caller_count = 0;
node->call_for_symbol_thunks_and_aliases (count_callers, &caller_count,
@ -1206,32 +1209,72 @@ initialize_node_lattices (struct cgraph_node *node)
disable = true;
}
for (i = 0; i < ipa_get_param_count (info); i++)
if (dump_file && (dump_flags & TDF_DETAILS)
&& !node->alias && !node->thunk.thunk_p)
{
class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
plats->m_value_range.init ();
fprintf (dump_file, "Initializing lattices of %s\n",
node->dump_name ());
if (disable || variable)
fprintf (dump_file, " Marking all lattices as %s\n",
disable ? "BOTTOM" : "VARIABLE");
}
if (disable || variable)
auto_vec<bool, 16> surviving_params;
bool pre_modified = false;
if (!disable && node->clone.param_adjustments)
{
for (i = 0; i < ipa_get_param_count (info); i++)
{
class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
if (disable)
{
plats->itself.set_to_bottom ();
plats->ctxlat.set_to_bottom ();
set_agg_lats_to_bottom (plats);
plats->bits_lattice.set_to_bottom ();
plats->m_value_range.set_to_bottom ();
}
else
set_all_contains_variable (plats);
}
/* At the moment all IPA optimizations should use the number of
parameters of the prevailing decl as the m_always_copy_start.
Handling any other value would complicate the code below, so for the
time bing let's only assert it is so. */
gcc_assert ((node->clone.param_adjustments->m_always_copy_start
== ipa_get_param_count (info))
|| node->clone.param_adjustments->m_always_copy_start < 0);
pre_modified = true;
node->clone.param_adjustments->get_surviving_params (&surviving_params);
if (dump_file && (dump_flags & TDF_DETAILS)
&& !node->alias && !node->thunk.thunk_p)
fprintf (dump_file, "Marking all lattices of %s as %s\n",
node->dump_name (), disable ? "BOTTOM" : "VARIABLE");
{
bool first = true;
for (int j = 0; j < ipa_get_param_count (info); j++)
{
if (j < (int) surviving_params.length ()
&& surviving_params[j])
continue;
if (first)
{
fprintf (dump_file,
" The following parameters are dead on arrival:");
first = false;
}
fprintf (dump_file, " %u", j);
}
if (!first)
fprintf (dump_file, "\n");
}
}
for (i = 0; i < ipa_get_param_count (info); i++)
{
ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
if (disable
|| (pre_modified && (surviving_params.length () <= (unsigned) i
|| !surviving_params[i])))
{
plats->itself.set_to_bottom ();
plats->ctxlat.set_to_bottom ();
set_agg_lats_to_bottom (plats);
plats->bits_lattice.set_to_bottom ();
plats->m_value_range.set_to_bottom ();
}
else
{
plats->m_value_range.init ();
if (variable)
set_all_contains_variable (plats);
}
}
for (ie = node->indirect_calls; ie; ie = ie->next_callee)
@ -3654,12 +3697,8 @@ get_replacement_map (class ipa_node_params *info, tree value, int parm_num)
print_generic_expr (dump_file, value);
fprintf (dump_file, "\n");
}
replace_map->old_tree = NULL;
replace_map->parm_num = parm_num;
replace_map->new_tree = value;
replace_map->replace_p = true;
replace_map->ref_p = false;
return replace_map;
}
@ -3797,6 +3836,35 @@ update_specialized_profile (struct cgraph_node *new_node,
dump_profile_updates (orig_node, new_node);
}
/* Return true if we would like to remove a parameter from NODE when cloning it
with KNOWN_CSTS scalar constants. */
static bool
want_remove_some_param_p (cgraph_node *node, vec<tree> known_csts)
{
auto_vec<bool, 16> surviving;
bool filled_vec = false;
ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
for (i = 0; i < count; i++)
{
if (!known_csts[i] && ipa_is_param_used (info, i))
continue;
if (!filled_vec)
{
if (!node->clone.param_adjustments)
return true;
node->clone.param_adjustments->get_surviving_params (&surviving);
filled_vec = true;
}
if (surviving.length() < (unsigned) i && surviving[i])
return true;
}
return false;
}
/* Create a specialized version of NODE with known constants in KNOWN_CSTS,
known contexts in KNOWN_CONTEXTS and known aggregate values in AGGVALS and
redirect all edges in CALLERS to it. */
@ -3810,31 +3878,65 @@ create_specialized_node (struct cgraph_node *node,
{
class ipa_node_params *new_info, *info = IPA_NODE_REF (node);
vec<ipa_replace_map *, va_gc> *replace_trees = NULL;
vec<ipa_adjusted_param, va_gc> *new_params = NULL;
struct ipa_agg_replacement_value *av;
struct cgraph_node *new_node;
int i, count = ipa_get_param_count (info);
bitmap args_to_skip;
ipa_param_adjustments *old_adjustments = node->clone.param_adjustments;
ipa_param_adjustments *new_adjustments;
gcc_assert (!info->ipcp_orig_node);
gcc_assert (node->local.can_change_signature
|| !old_adjustments);
if (node->local.can_change_signature)
if (old_adjustments)
{
args_to_skip = BITMAP_GGC_ALLOC ();
for (i = 0; i < count; i++)
/* At the moment all IPA optimizations should use the number of
parameters of the prevailing decl as the m_always_copy_start.
Handling any other value would complicate the code below, so for the
time bing let's only assert it is so. */
gcc_assert (old_adjustments->m_always_copy_start == count
|| old_adjustments->m_always_copy_start < 0);
int old_adj_count = vec_safe_length (old_adjustments->m_adj_params);
for (i = 0; i < old_adj_count; i++)
{
tree t = known_csts[i];
ipa_adjusted_param *old_adj = &(*old_adjustments->m_adj_params)[i];
if (!node->local.can_change_signature
|| old_adj->op != IPA_PARAM_OP_COPY
|| (!known_csts[old_adj->base_index]
&& ipa_is_param_used (info, old_adj->base_index)))
{
ipa_adjusted_param new_adj = *old_adj;
if (t || !ipa_is_param_used (info, i))
bitmap_set_bit (args_to_skip, i);
new_adj.prev_clone_adjustment = true;
new_adj.prev_clone_index = i;
vec_safe_push (new_params, new_adj);
}
}
bool skip_return = old_adjustments->m_skip_return;
new_adjustments = (new (ggc_alloc <ipa_param_adjustments> ())
ipa_param_adjustments (new_params, count,
skip_return));
}
else if (node->local.can_change_signature
&& want_remove_some_param_p (node, known_csts))
{
ipa_adjusted_param adj;
memset (&adj, 0, sizeof (adj));
adj.op = IPA_PARAM_OP_COPY;
for (i = 0; i < count; i++)
if (!known_csts[i] && ipa_is_param_used (info, i))
{
adj.base_index = i;
adj.prev_clone_index = i;
vec_safe_push (new_params, adj);
}
new_adjustments = (new (ggc_alloc <ipa_param_adjustments> ())
ipa_param_adjustments (new_params, count, false));
}
else
{
args_to_skip = NULL;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " cannot change function signature\n");
}
new_adjustments = NULL;
replace_trees = vec_safe_copy (node->clone.tree_map);
for (i = 0; i < count; i++)
{
tree t = known_csts[i];
@ -3863,7 +3965,7 @@ create_specialized_node (struct cgraph_node *node,
IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (
node->decl)));
new_node = node->create_virtual_clone (callers, replace_trees,
args_to_skip, "constprop",
new_adjustments, "constprop",
suffix_counter);
suffix_counter++;

View File

@ -616,9 +616,7 @@ ipa_fn_summary_t::duplicate (cgraph_node *src,
for (j = 0; vec_safe_iterate (dst->clone.tree_map, j, &r); j++)
{
if (((!r->old_tree && r->parm_num == i)
|| (r->old_tree && r->old_tree == ipa_get_param (parms_info, i)))
&& r->replace_p && !r->ref_p)
if (r->parm_num == i)
{
known_vals[i] = r->new_tree;
break;

View File

@ -617,8 +617,7 @@ save_inline_function_body (struct cgraph_node *node)
/* Copy the OLD_VERSION_NODE function tree to the new version. */
tree_function_versioning (node->decl, first_clone->decl,
NULL, true, NULL, false,
NULL, NULL);
NULL, NULL, true, NULL, NULL);
/* The function will be short lived and removed after we inline all the clones,
but make it internal so we won't confuse ourself. */

File diff suppressed because it is too large Load Diff

View File

@ -16,101 +16,416 @@ for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
<http://www.gnu.org/licenses/>.
This file defines classes and other data structures that are used to manipulate
the prototype of a function, especially to create, remove or split its formal
parameters, but also to remove its return value, and also its call statements
correspondingly.
The most basic one is a vector of structures ipa_adjusted_param. It is simply
a description how the new parameters should look like after the transformation
in what way they relate to the previous ones (if in any). Such relation to an
old parameter can be an outright copy or an IPA-SRA replacement. If an old
parameter is not listed or otherwise mentioned, it is removed as unused or at
least unnecessary. Note that this most basic structure does not work for
modifying calls of functions with variable number of arguments.
Class ipa_param_adjustments is only a little more than a thin encapsulation of
a vector of ipa_param_adjustments. Along with this vector it contains an index
of the first potential vararg argument and a boolean flag whether the return
value should be removed or not. Moreover, the class contains method
modify_call which can transform a call statement so that it correctly calls a
modified function. These two data structures were designed to have a small
memory footprint because they are allocated for each clone of a call graph node
that has its prototype changed and live until the end of IPA clone
materialization and call redirection phase.
On the other hand, class ipa_param_body_adjustments can afford to allocate more
data because its life span is much smaller, it is allocated and destroyed in
the course of materialization of each single clone that needs it or only when a
particular pass needs to change a function it is operating on. This class has
various methods required to change function declaration and the body of the
function according to instructions given either by class ipa_param_adjustments
or only a vector of ipa_adjusted_params.
When these classes are used in the context of call graph clone materialization
and subsequent call statement redirection - which is the point at which we
modify arguments in call statements - they need to cooperate with each other in
order to handle what we refer to as transitive (IPA-SRA) splits. These are
situations when a formal parameter of one function is split into several
smaller ones and some of them are then passed on in a call to another function
because the formal parameter of this callee has also been split.
Consider a simple example:
struct S {int a, b, c;};
struct Z {int x; S s;};
foo (S s)
{
use (s.b);
}
bar (Z z)
{
use (z.s.a);
foo (z.s);
}
baz ()
{
bar (*global);
}
Both bar and foo would have their parameter split. Foo would receive one
replacement representing s.b. Function bar would see its parameter split into
one replacement representing z.s.a and another representing z.s.b which would
be passed on to foo. It would be a so called transitive split IPA-SRA
replacement, one which is passed in a call as an actual argument to another
IPA-SRA replacement in another function.
Note that the call chain the example can be arbitrarily long and recursive and
that any function in it can be cloned by another IPA pass and any number of
adjacent functions in the call chain can be inlined into each other. Call
redirection takes place only after bodies of the function have been modified by
all of the above.
Call redirection has to be able to find the right decl or SSA_NAME that
corresponds to the transitive split in the caller. The SSA names are assigned
right after clone materialization/ modification and cannot be "added"
afterwards. Moreover, if the caller has been inlined the SSA_NAMEs in question
no longer belong to PARM_DECLs but to VAR_DECLs, indistinguishable from any
others.
Therefore, when clone materialization finds a call statement which it knows is
a part of a transitive split, it will modify it into:
foo (DUMMY_Z_VAR.s, repl_for_a, repl_for_b, <rest of original arguments>);
It will also store {DUMMY_S_VAR, 32} and {DUMMY_S_VAR, 64} representing offsets
of z.s.a and z.s.b (assuming a 32-bit int) into foo's cgraph node
clone->performed_splits vector (which is storing structures of type
ipa_param_performed_split also defined in this header file).
Call redirection will identify that expression DUMMY_Z_VAR.s is based on a
variable stored in performed_splits vector and learn that the following
arguments, already in SSA form, represent offsets 32 and 64 in a split original
parameter. It subtracts offset of DUMMY_Z_VAR.s from 32 and 64 and arrives at
offsets 0 and 32 within callee's original parameter. At this point it also
knows from the call graph that only the bit with offset 32 is needed and so
changes the call statement into final:
bar (repl_for_b, <rest of original arguments>); */
#ifndef IPA_PARAM_MANIPULATION_H
#define IPA_PARAM_MANIPULATION_H
/* Operation to be performed for the parameter in ipa_parm_adjustment
below. */
enum ipa_parm_op {
IPA_PARM_OP_NONE,
/* This describes a brand new parameter.
The field `type' should be set to the new type, `arg_prefix'
should be set to the string prefix for the new DECL_NAME, and
`new_decl' will ultimately hold the newly created argument. */
IPA_PARM_OP_NEW,
/* This new parameter is an unmodified parameter at index base_index. */
IPA_PARM_OP_COPY,
/* This adjustment describes a parameter that is about to be removed
completely. Most users will probably need to book keep those so that they
don't leave behinfd any non default def ssa names belonging to them. */
IPA_PARM_OP_REMOVE
/* Indices into ipa_param_prefixes to identify a human-readable prefix for newly
synthesized parameters. Keep in sync with the array. */
enum ipa_param_name_prefix_indices
{
IPA_PARAM_PREFIX_SYNTH,
IPA_PARAM_PREFIX_ISRA,
IPA_PARAM_PREFIX_SIMD,
IPA_PARAM_PREFIX_MASK,
IPA_PARAM_PREFIX_COUNT
};
/* Structure to describe transformations of formal parameters and actual
arguments. Each instance describes one new parameter and they are meant to
be stored in a vector. Additionally, most users will probably want to store
adjustments about parameters that are being removed altogether so that SSA
names belonging to them can be replaced by SSA names of an artificial
variable. */
struct ipa_parm_adjustment
{
/* The original PARM_DECL itself, helpful for processing of the body of the
function itself. Intended for traversing function bodies.
ipa_modify_formal_parameters, ipa_modify_call_arguments and
ipa_combine_adjustments ignore this and use base_index.
ipa_modify_formal_parameters actually sets this. */
tree base;
/* We do not support manipulating functions with more than
1<<IPA_PARAM_MAX_INDEX_BITS parameters. */
#define IPA_PARAM_MAX_INDEX_BITS 16
/* Type of the new parameter. However, if by_ref is true, the real type will
be a pointer to this type. */
/* Operation to be performed for the parameter in ipa_parm_adjustment
below. */
enum ipa_parm_op
{
/* Do not use or you will trigger an assert. */
IPA_PARAM_OP_UNDEFINED,
/* This new parameter is an unmodified parameter at index base_index. */
IPA_PARAM_OP_COPY,
/* This describes a brand new parameter. If it somehow relates to any
original parameters, the user needs to manage the transition itself. */
IPA_PARAM_OP_NEW,
/* Split parameter as indicated by fields base_index, offset and type. */
IPA_PARAM_OP_SPLIT
};
/* Structure that describes one parameter of a function after transformation.
Omitted parameters will be removed. */
struct GTY(()) ipa_adjusted_param
{
/* Type of the new parameter. Required for all operations except
IPA_PARM_OP_COPY when the original type will be preserved. */
tree type;
/* Alias refrerence type to be used in MEM_REFs when adjusting caller
arguments. */
/* Alias reference type to be used in MEM_REFs when adjusting caller
arguments. Required for IPA_PARM_OP_SPLIT operation. */
tree alias_ptr_type;
/* The new declaration when creating/replacing a parameter. Created
by ipa_modify_formal_parameters, useful for functions modifying
the body accordingly. For brand new arguments, this is the newly
created argument. */
tree new_decl;
/* New declaration of a substitute variable that we may use to replace all
non-default-def ssa names when a parm decl is going away. */
tree new_ssa_base;
/* This holds the prefix to be used for the new DECL_NAME. */
const char *arg_prefix;
/* Offset into the original parameter (for the cases when the new parameter
is a component of an original one). */
poly_int64_pod offset;
is a component of an original one). Required for IPA_PARM_OP_SPLIT
operation. */
unsigned unit_offset;
/* Zero based index of the original parameter this one is based on. */
int base_index;
/* Zero based index of the original parameter this one is based on. Required
for IPA_PARAM_OP_COPY and IPA_PARAM_OP_SPLIT, users of IPA_PARAM_OP_NEW
only need to specify it if they use replacement lookup provided by
ipa_param_body_adjustments. */
unsigned base_index : IPA_PARAM_MAX_INDEX_BITS;
/* Whether this parameter is a new parameter, a copy of an old one,
or one about to be removed. */
enum ipa_parm_op op;
/* Zero based index of the parameter this one is based on in the previous
clone. If there is no previous clone, it must be equal to base_index. */
unsigned prev_clone_index : IPA_PARAM_MAX_INDEX_BITS;
/* Specify the operation, if any, to be performed on the parameter. */
enum ipa_parm_op op : 2;
/* If set, this structure describes a parameter copied over from a previous
IPA clone, any transformations are thus not to be re-done. */
unsigned prev_clone_adjustment : 1;
/* Index into ipa_param_prefixes specifying a prefix to be used with
DECL_NAMEs of newly synthesized parameters. */
unsigned param_prefix_index : 2;
/* Storage order of the original parameter (for the cases when the new
parameter is a component of an original one). */
unsigned reverse : 1;
/* The parameter is to be passed by reference. */
unsigned by_ref : 1;
/* A bit free for the user. */
unsigned user_flag : 1;
};
typedef vec<ipa_parm_adjustment> ipa_parm_adjustment_vec;
void ipa_dump_adjusted_parameters (FILE *f,
vec<ipa_adjusted_param, va_gc> *adj_params);
vec<tree> ipa_get_vector_of_formal_parms (tree fndecl);
vec<tree> ipa_get_vector_of_formal_parm_types (tree fntype);
void ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec);
void ipa_modify_call_arguments (struct cgraph_edge *, gcall *,
ipa_parm_adjustment_vec);
ipa_parm_adjustment_vec ipa_combine_adjustments (ipa_parm_adjustment_vec,
ipa_parm_adjustment_vec);
void ipa_dump_param_adjustments (FILE *, ipa_parm_adjustment_vec, tree);
/* Structure to remember the split performed on a node so that edge redirection
(i.e. splitting arguments of call statements) know how split formal
parameters of the caller are represented. */
bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec);
ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *,
ipa_parm_adjustment_vec,
bool);
struct GTY(()) ipa_param_performed_split
{
/* The dummy VAR_DECL that was created instead of the split parameter that
sits in the call in the meantime between clone materialization and call
redirection. All entries in a vector of performed splits that correspond
to the same dumy decl must be grouped together. */
tree dummy_decl;
/* Offset into the original parameter. */
unsigned unit_offset;
};
/* Class used to record planned modifications to parameters of a function and
also to perform necessary modifications at the caller side at the gimple
level. Used to describe all cgraph node clones that have their parameters
changed, therefore the class should only have a small memory footprint. */
class GTY(()) ipa_param_adjustments
{
public:
/* Constructor from NEW_PARAMS showing how new parameters should look like
plus copying any pre-existing actual arguments starting from argument
with index ALWAYS_COPY_START (if non-negative, negative means do not copy
anything beyond what is described in NEW_PARAMS), and SKIP_RETURN, which
indicates that the function should return void after transformation. */
ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
int always_copy_start, bool skip_return)
: m_adj_params (new_params), m_always_copy_start (always_copy_start),
m_skip_return (skip_return)
{}
/* Modify a call statement arguments (and possibly remove the return value)
as described in the data fields of this class. */
gcall *modify_call (gcall *stmt,
vec<ipa_param_performed_split, va_gc> *performed_splits,
tree callee_decl, bool update_references);
/* Return if the first parameter is left intact. */
bool first_param_intact_p ();
/* Build a function type corresponding to the modified call. */
tree build_new_function_type (tree old_type, bool type_is_original_p);
/* Build a declaration corresponding to the target of the modified call. */
tree adjust_decl (tree orig_decl);
/* Fill a vector marking which parameters are intact by the described
modifications. */
void get_surviving_params (vec<bool> *surviving_params);
/* Fill a vector with new indices of surviving original parameters. */
void get_updated_indices (vec<int> *new_indices);
void dump (FILE *f);
void debug ();
/* How the known part of arguments should look like. */
vec<ipa_adjusted_param, va_gc> *m_adj_params;
/* If non-negative, copy any arguments starting at this offset without any
modifications so that functions with variable number of arguments can be
modified. This number should be equal to the number of original forma
parameters. */
int m_always_copy_start;
/* If true, make the function not return any value. */
bool m_skip_return;
private:
ipa_param_adjustments () {}
void init (vec<tree> *cur_params);
int get_max_base_index ();
bool method2func_p (tree orig_type);
};
/* Structure used to map expressions accessing split or replaced parameters to
new PARM_DECLs. */
struct ipa_param_body_replacement
{
/* The old decl of the original parameter. */
tree base;
/* The new decl it should be replaced with. */
tree repl;
/* When modifying clones during IPA clone materialization, this is a dummy
decl used to mark calls in which we need to apply transitive splitting,
these dummy delcls are inserted as arguments to such calls and then
followed by all the replacements with offset info stored in
ipa_param_performed_split.
Users of ipa_param_body_adjustments that modify standalone functions
outside of IPA clone materialization can use this field for their internal
purposes. */
tree dummy;
/* The offset within BASE that REPL represents. */
unsigned unit_offset;
};
struct ipa_replace_map;
/* Class used when actually performing adjustments to formal parameters of a
function to map accesses that need to be replaced to replacements. The
class attempts to work in two very different sets of circumstances: as a
part of tree-inine.c's tree_function_versioning machinery to clone functions
(when M_ID is not NULL) and in s standalone fashion, modifying an existing
function in place (when M_ID is NULL). While a lot of stuff handled in a
unified way in both modes, there are many aspects of the processs that
requires distinct paths. */
class ipa_param_body_adjustments
{
public:
/* Constructor to use from within tree-inline. */
ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
tree fndecl, tree old_fndecl,
struct copy_body_data *id, tree *vars,
vec<ipa_replace_map *, va_gc> *tree_map);
/* Constructor to use for modifying a function outside of tree-inline from an
instance of ipa_param_adjustments. */
ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
tree fndecl);
/* Constructor to use for modifying a function outside of tree-inline from a
simple vector of desired parameter modification. */
ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params,
tree fndecl);
/* The do-it-all function for modifying a function outside of
tree-inline. */
bool perform_cfun_body_modifications ();
/* Change the PARM_DECLs. */
void modify_formal_parameters ();
/* Register a replacement decl for the transformation done in APM. */
void register_replacement (ipa_adjusted_param *apm, tree replacement,
tree dummy = NULL_TREE);
/* Lookup a replacement for a given offset within a given parameter. */
tree lookup_replacement (tree base, unsigned unit_offset);
/* Lookup a replacement for an expression, if there is one. */
ipa_param_body_replacement *get_expr_replacement (tree expr,
bool ignore_default_def);
/* Lookup the new base for surviving names previously belonging to a
parameter. */
tree get_replacement_ssa_base (tree old_decl);
/* Modify a statement. */
bool modify_gimple_stmt (gimple **stmt, gimple_seq *extra_stmts);
/* Return the new chain of parameters. */
tree get_new_param_chain ();
/* Pointers to data structures defining how the function should be
modified. */
vec<ipa_adjusted_param, va_gc> *m_adj_params;
ipa_param_adjustments *m_adjustments;
/* Vector of old parameter declarations that must have their debug bind
statements re-mapped and debug decls created. */
auto_vec<tree, 16> m_reset_debug_decls;
/* Set to true if there are any IPA_PARAM_OP_SPLIT adjustments among stored
adjustments. */
bool m_split_modifications_p;
private:
void common_initialization (tree old_fndecl, tree *vars,
vec<ipa_replace_map *, va_gc> *tree_map);
unsigned get_base_index (ipa_adjusted_param *apm);
ipa_param_body_replacement *lookup_replacement_1 (tree base,
unsigned unit_offset);
tree replace_removed_params_ssa_names (tree old_name, gimple *stmt);
bool modify_expression (tree *expr_p, bool convert);
bool modify_assignment (gimple *stmt, gimple_seq *extra_stmts);
bool modify_call_stmt (gcall **stmt_p);
bool modify_cfun_body ();
void reset_debug_stmts ();
/* Declaration of the function that is being transformed. */
tree m_fndecl;
/* If non-NULL, the tree-inline master data structure guiding materialization
of the current clone. */
struct copy_body_data *m_id;
/* Vector of old parameter declarations (before changing them). */
auto_vec<tree, 16> m_oparms;
/* Vector of parameter declarations the function will have after
transformation. */
auto_vec<tree, 16> m_new_decls;
/* If the function type has non-NULL TYPE_ARG_TYPES, this is the vector of
these types after transformation, otherwise an empty one. */
auto_vec<tree, 16> m_new_types;
/* Vector of structures telling how to replace old parameters in in the
function body. TODO: Even though there usually be only few, but should we
use a hash? */
auto_vec<ipa_param_body_replacement, 16> m_replacements;
/* Vector for remapping SSA_BASES from old parameter declarations that are
being removed as a part of the transformation. Before a new VAR_DECL is
created, it holds the old PARM_DECL, once the variable is built it is
stored here. */
auto_vec<tree> m_removed_decls;
/* Hash to quickly lookup the item in m_removed_decls given the old decl. */
hash_map<tree, unsigned> m_removed_map;
/* True iff the transformed function is a class method that is about to loose
its this pointer and must be converted to a normal function. */
bool m_method2func;
};
void push_function_arg_decls (vec<tree> *args, tree fndecl);
void push_function_arg_types (vec<tree> *types, tree fntype);
#endif /* IPA_PARAM_MANIPULATION_H */

View File

@ -4851,31 +4851,24 @@ adjust_agg_replacement_values (struct cgraph_node *node,
struct ipa_agg_replacement_value *aggval)
{
struct ipa_agg_replacement_value *v;
int i, c = 0, d = 0, *adj;
if (!node->clone.combined_args_to_skip)
if (!node->clone.param_adjustments)
return;
auto_vec<int, 16> new_indices;
node->clone.param_adjustments->get_updated_indices (&new_indices);
for (v = aggval; v; v = v->next)
{
gcc_assert (v->index >= 0);
if (c < v->index)
c = v->index;
gcc_checking_assert (v->index >= 0);
if ((unsigned) v->index < new_indices.length ())
v->index = new_indices[v->index];
else
/* This can happen if we know about a constant passed by reference by
an argument which is never actually used for anything, let alone
loading that constant. */
v->index = -1;
}
c++;
adj = XALLOCAVEC (int, c);
for (i = 0; i < c; i++)
if (bitmap_bit_p (node->clone.combined_args_to_skip, i))
{
adj[i] = -1;
d++;
}
else
adj[i] = i - d;
for (v = aggval; v; v = v->next)
v->index = adj[v->index];
}
/* Dominator walker driving the ipcp modification phase. */
@ -5001,24 +4994,41 @@ ipcp_modif_dom_walker::before_dom_children (basic_block bb)
static void
ipcp_update_bits (struct cgraph_node *node)
{
tree parm = DECL_ARGUMENTS (node->decl);
tree next_parm = parm;
ipcp_transformation *ts = ipcp_get_transformation_summary (node);
if (!ts || vec_safe_length (ts->bits) == 0)
return;
vec<ipa_bits *, va_gc> &bits = *ts->bits;
unsigned count = bits.length ();
if (!count)
return;
for (unsigned i = 0; i < count; ++i, parm = next_parm)
auto_vec<int, 16> new_indices;
bool need_remapping = false;
if (node->clone.param_adjustments)
{
if (node->clone.combined_args_to_skip
&& bitmap_bit_p (node->clone.combined_args_to_skip, i))
continue;
node->clone.param_adjustments->get_updated_indices (&new_indices);
need_remapping = true;
}
auto_vec <tree, 16> parm_decls;
push_function_arg_decls (&parm_decls, node->decl);
for (unsigned i = 0; i < count; ++i)
{
tree parm;
if (need_remapping)
{
if (i >= new_indices.length ())
continue;
int idx = new_indices[i];
if (idx < 0)
continue;
parm = parm_decls[idx];
}
else
parm = parm_decls[i];
gcc_checking_assert (parm);
next_parm = DECL_CHAIN (parm);
if (!bits[i]
|| !(INTEGRAL_TYPE_P (TREE_TYPE (parm))
@ -5093,22 +5103,42 @@ ipcp_update_bits (struct cgraph_node *node)
static void
ipcp_update_vr (struct cgraph_node *node)
{
tree fndecl = node->decl;
tree parm = DECL_ARGUMENTS (fndecl);
tree next_parm = parm;
ipcp_transformation *ts = ipcp_get_transformation_summary (node);
if (!ts || vec_safe_length (ts->m_vr) == 0)
return;
const vec<ipa_vr, va_gc> &vr = *ts->m_vr;
unsigned count = vr.length ();
if (!count)
return;
for (unsigned i = 0; i < count; ++i, parm = next_parm)
auto_vec<int, 16> new_indices;
bool need_remapping = false;
if (node->clone.param_adjustments)
{
if (node->clone.combined_args_to_skip
&& bitmap_bit_p (node->clone.combined_args_to_skip, i))
continue;
node->clone.param_adjustments->get_updated_indices (&new_indices);
need_remapping = true;
}
auto_vec <tree, 16> parm_decls;
push_function_arg_decls (&parm_decls, node->decl);
for (unsigned i = 0; i < count; ++i)
{
tree parm;
int remapped_idx;
if (need_remapping)
{
if (i >= new_indices.length ())
continue;
remapped_idx = new_indices[i];
if (remapped_idx < 0)
continue;
}
else
remapped_idx = i;
parm = parm_decls[remapped_idx];
gcc_checking_assert (parm);
next_parm = DECL_CHAIN (parm);
tree ddef = ssa_default_def (DECL_STRUCT_FUNCTION (node->decl), parm);
if (!ddef || !is_gimple_reg (parm))
@ -5123,7 +5153,8 @@ ipcp_update_vr (struct cgraph_node *node)
{
if (dump_file)
{
fprintf (dump_file, "Setting value range of param %u ", i);
fprintf (dump_file, "Setting value range of param %u "
"(now %i) ", i, remapped_idx);
fprintf (dump_file, "%s[",
(vr[i].type == VR_ANTI_RANGE) ? "~" : "");
print_decs (vr[i].min, dump_file);

View File

@ -1326,13 +1326,38 @@ split_function (basic_block return_bb, class split_point *split_point,
}
}
ipa_param_adjustments *adjustments;
bool skip_return = (!split_part_return_p
|| !split_point->split_part_set_retval);
/* TODO: Perhaps get rid of args_to_skip entirely, after we make sure the
debug info generation and discrepancy avoiding works well too. */
if ((args_to_skip && !bitmap_empty_p (args_to_skip))
|| skip_return)
{
vec<ipa_adjusted_param, va_gc> *new_params = NULL;
unsigned j;
for (parm = DECL_ARGUMENTS (current_function_decl), j = 0;
parm; parm = DECL_CHAIN (parm), j++)
if (!args_to_skip || !bitmap_bit_p (args_to_skip, j))
{
ipa_adjusted_param adj;
memset (&adj, 0, sizeof (adj));
adj.op = IPA_PARAM_OP_COPY;
adj.base_index = j;
adj.prev_clone_index = j;
vec_safe_push (new_params, adj);
}
adjustments = new ipa_param_adjustments (new_params, j, skip_return);
}
else
adjustments = NULL;
/* Now create the actual clone. */
cgraph_edge::rebuild_edges ();
node = cur_node->create_version_clone_with_body
(vNULL, NULL, args_to_skip,
!split_part_return_p || !split_point->split_part_set_retval,
(vNULL, NULL, adjustments,
split_point->split_bbs, split_point->entry_bb, "part");
delete adjustments;
node->split_part = true;
if (cur_node->same_comdat_group)
@ -1469,6 +1494,7 @@ split_function (basic_block return_bb, class split_point *split_point,
= gimple_build_debug_bind (ddecl, unshare_expr (arg), call);
gsi_insert_after (&gsi, def_temp, GSI_NEW_STMT);
}
BITMAP_FREE (args_to_skip);
}
/* We avoid address being taken on any variable used by split part,

4049
gcc/ipa-sra.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1804,8 +1804,7 @@ output_cgraph_opt_summary_p (struct cgraph_node *node)
{
return ((node->clone_of || node->former_clone_of)
&& (node->clone.tree_map
|| node->clone.args_to_skip
|| node->clone.combined_args_to_skip));
|| node->clone.param_adjustments));
}
/* Output optimization summary for EDGE to OB. */
@ -1822,42 +1821,53 @@ output_node_opt_summary (struct output_block *ob,
struct cgraph_node *node,
lto_symtab_encoder_t encoder)
{
unsigned int index;
bitmap_iterator bi;
struct ipa_replace_map *map;
struct bitpack_d bp;
int i;
struct cgraph_edge *e;
if (node->clone.args_to_skip)
/* TODO: Should this code be moved to ipa-param-manipulation? */
struct bitpack_d bp;
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, (node->clone.param_adjustments != NULL), 1);
streamer_write_bitpack (&bp);
if (ipa_param_adjustments *adjustments = node->clone.param_adjustments)
{
streamer_write_uhwi (ob, bitmap_count_bits (node->clone.args_to_skip));
EXECUTE_IF_SET_IN_BITMAP (node->clone.args_to_skip, 0, index, bi)
streamer_write_uhwi (ob, index);
streamer_write_uhwi (ob, vec_safe_length (adjustments->m_adj_params));
ipa_adjusted_param *adj;
FOR_EACH_VEC_SAFE_ELT (adjustments->m_adj_params, i, adj)
{
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, adj->base_index, IPA_PARAM_MAX_INDEX_BITS);
bp_pack_value (&bp, adj->prev_clone_index, IPA_PARAM_MAX_INDEX_BITS);
bp_pack_value (&bp, adj->op, 2);
bp_pack_value (&bp, adj->param_prefix_index, 2);
bp_pack_value (&bp, adj->prev_clone_adjustment, 1);
bp_pack_value (&bp, adj->reverse, 1);
bp_pack_value (&bp, adj->user_flag, 1);
streamer_write_bitpack (&bp);
if (adj->op == IPA_PARAM_OP_SPLIT
|| adj->op == IPA_PARAM_OP_NEW)
{
stream_write_tree (ob, adj->type, true);
if (adj->op == IPA_PARAM_OP_SPLIT)
{
stream_write_tree (ob, adj->alias_ptr_type, true);
streamer_write_uhwi (ob, adj->unit_offset);
}
}
}
streamer_write_hwi (ob, adjustments->m_always_copy_start);
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, node->clone.param_adjustments->m_skip_return, 1);
streamer_write_bitpack (&bp);
}
else
streamer_write_uhwi (ob, 0);
if (node->clone.combined_args_to_skip)
{
streamer_write_uhwi (ob, bitmap_count_bits (node->clone.combined_args_to_skip));
EXECUTE_IF_SET_IN_BITMAP (node->clone.combined_args_to_skip, 0, index, bi)
streamer_write_uhwi (ob, index);
}
else
streamer_write_uhwi (ob, 0);
streamer_write_uhwi (ob, vec_safe_length (node->clone.tree_map));
FOR_EACH_VEC_SAFE_ELT (node->clone.tree_map, i, map)
{
/* At the moment we assume all old trees to be PARM_DECLs, because we have no
mechanism to store function local declarations into summaries. */
gcc_assert (!map->old_tree);
streamer_write_uhwi (ob, map->parm_num);
gcc_assert (EXPR_LOCATION (map->new_tree) == UNKNOWN_LOCATION);
stream_write_tree (ob, map->new_tree, true);
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, map->replace_p, 1);
bp_pack_value (&bp, map->ref_p, 1);
streamer_write_bitpack (&bp);
}
if (lto_symtab_encoder_in_partition_p (encoder, node))
@ -1922,26 +1932,49 @@ input_node_opt_summary (struct cgraph_node *node,
{
int i;
int count;
int bit;
struct bitpack_d bp;
struct cgraph_edge *e;
count = streamer_read_uhwi (ib_main);
if (count)
node->clone.args_to_skip = BITMAP_GGC_ALLOC ();
for (i = 0; i < count; i++)
/* TODO: Should this code be moved to ipa-param-manipulation? */
struct bitpack_d bp;
bp = streamer_read_bitpack (ib_main);
bool have_adjustments = bp_unpack_value (&bp, 1);
if (have_adjustments)
{
bit = streamer_read_uhwi (ib_main);
bitmap_set_bit (node->clone.args_to_skip, bit);
}
count = streamer_read_uhwi (ib_main);
if (count)
node->clone.combined_args_to_skip = BITMAP_GGC_ALLOC ();
for (i = 0; i < count; i++)
{
bit = streamer_read_uhwi (ib_main);
bitmap_set_bit (node->clone.combined_args_to_skip, bit);
count = streamer_read_uhwi (ib_main);
vec<ipa_adjusted_param, va_gc> *new_params = NULL;
for (i = 0; i < count; i++)
{
ipa_adjusted_param adj;
memset (&adj, 0, sizeof (adj));
bp = streamer_read_bitpack (ib_main);
adj.base_index = bp_unpack_value (&bp, IPA_PARAM_MAX_INDEX_BITS);
adj.prev_clone_index
= bp_unpack_value (&bp, IPA_PARAM_MAX_INDEX_BITS);
adj.op = (enum ipa_parm_op) bp_unpack_value (&bp, 2);
adj.param_prefix_index = bp_unpack_value (&bp, 2);
adj.prev_clone_adjustment = bp_unpack_value (&bp, 1);
adj.reverse = bp_unpack_value (&bp, 1);
adj.user_flag = bp_unpack_value (&bp, 1);
if (adj.op == IPA_PARAM_OP_SPLIT
|| adj.op == IPA_PARAM_OP_NEW)
{
adj.type = stream_read_tree (ib_main, data_in);
if (adj.op == IPA_PARAM_OP_SPLIT)
{
adj.alias_ptr_type = stream_read_tree (ib_main, data_in);
adj.unit_offset = streamer_read_uhwi (ib_main);
}
}
vec_safe_push (new_params, adj);
}
int always_copy_start = streamer_read_hwi (ib_main);
bp = streamer_read_bitpack (ib_main);
bool skip_return = bp_unpack_value (&bp, 1);
node->clone.param_adjustments
= (new (ggc_alloc <ipa_param_adjustments> ())
ipa_param_adjustments (new_params, always_copy_start, skip_return));
}
count = streamer_read_uhwi (ib_main);
for (i = 0; i < count; i++)
{
@ -1949,11 +1982,7 @@ input_node_opt_summary (struct cgraph_node *node,
vec_safe_push (node->clone.tree_map, map);
map->parm_num = streamer_read_uhwi (ib_main);
map->old_tree = NULL;
map->new_tree = stream_read_tree (ib_main, data_in);
bp = streamer_read_bitpack (ib_main);
map->replace_p = bp_unpack_value (&bp, 1);
map->ref_p = bp_unpack_value (&bp, 1);
}
for (e = node->callees; e; e = e->next_callee)
input_edge_opt_summary (e, ib_main);

View File

@ -53,7 +53,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
"offload_table",
"mode_table",
"hsa",
"lto"
"lto",
"ipa-sra"
};
/* Hooks so that the ipa passes can call into the lto front end to get

View File

@ -235,6 +235,7 @@ enum lto_section_type
LTO_section_mode_table,
LTO_section_ipa_hsa,
LTO_section_lto,
LTO_section_ipa_sra,
LTO_N_SECTION_TYPES /* Must be last. */
};

View File

@ -311,9 +311,8 @@ create_target_clone (cgraph_node *node, bool definition, char *name,
if (definition)
{
new_node = node->create_version_clone_with_body (vNULL, NULL,
NULL, false,
NULL, NULL,
name, attributes);
NULL, NULL,
NULL, name, attributes);
if (new_node == NULL)
return NULL;
new_node->force_output = true;

View File

@ -86,21 +86,23 @@ simd_clone_struct_copy (struct cgraph_simd_clone *to,
* sizeof (struct cgraph_simd_clone_arg))));
}
/* Return vector of parameter types of function FNDECL. This uses
TYPE_ARG_TYPES if available, otherwise falls back to types of
/* Fill an empty vector ARGS with parameter types of function FNDECL. This
uses TYPE_ARG_TYPES if available, otherwise falls back to types of
DECL_ARGUMENTS types. */
static vec<tree>
simd_clone_vector_of_formal_parm_types (tree fndecl)
static void
simd_clone_vector_of_formal_parm_types (vec<tree> *args, tree fndecl)
{
if (TYPE_ARG_TYPES (TREE_TYPE (fndecl)))
return ipa_get_vector_of_formal_parm_types (TREE_TYPE (fndecl));
vec<tree> args = ipa_get_vector_of_formal_parms (fndecl);
{
push_function_arg_types (args, TREE_TYPE (fndecl));
return;
}
push_function_arg_decls (args, fndecl);
unsigned int i;
tree arg;
FOR_EACH_VEC_ELT (args, i, arg)
args[i] = TREE_TYPE (args[i]);
return args;
FOR_EACH_VEC_ELT (*args, i, arg)
(*args)[i] = TREE_TYPE ((*args)[i]);
}
/* Given a simd function in NODE, extract the simd specific
@ -113,7 +115,8 @@ static struct cgraph_simd_clone *
simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
bool *inbranch_specified)
{
vec<tree> args = simd_clone_vector_of_formal_parm_types (node->decl);
auto_vec<tree> args;
simd_clone_vector_of_formal_parm_types (&args, node->decl);
tree t;
int n;
*inbranch_specified = false;
@ -192,14 +195,12 @@ simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
{
warning_at (OMP_CLAUSE_LOCATION (t), 0,
"ignoring large linear step");
args.release ();
return NULL;
}
else if (integer_zerop (step))
{
warning_at (OMP_CLAUSE_LOCATION (t), 0,
"ignoring zero linear step");
args.release ();
return NULL;
}
else
@ -263,7 +264,6 @@ simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
warning_at (DECL_SOURCE_LOCATION (node->decl), 0,
"ignoring %<#pragma omp declare simd%> on function "
"with %<_Atomic%> qualified return type");
args.release ();
return NULL;
}
@ -278,7 +278,6 @@ simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
return NULL;
}
args.release ();
return clone_info;
}
@ -303,14 +302,14 @@ simd_clone_compute_base_data_type (struct cgraph_node *node,
such parameter. */
else
{
vec<tree> map = simd_clone_vector_of_formal_parm_types (fndecl);
auto_vec<tree> map;
simd_clone_vector_of_formal_parm_types (&map, fndecl);
for (unsigned int i = 0; i < clone_info->nargs; ++i)
if (clone_info->args[i].arg_type == SIMD_CLONE_ARG_TYPE_VECTOR)
{
type = map[i];
break;
}
map.release ();
}
/* c) If the characteristic data type determined by a) or b) above
@ -441,7 +440,7 @@ simd_clone_create (struct cgraph_node *old_node)
return NULL;
old_node->get_body ();
new_node = old_node->create_version_clone_with_body (vNULL, NULL, NULL,
false, NULL, NULL,
NULL, NULL,
"simdclone");
}
else
@ -561,31 +560,33 @@ create_tmp_simd_array (const char *prefix, tree type, int simdlen)
NODE is the function whose arguments are to be adjusted.
Returns an adjustment vector that will be filled describing how the
argument types will be adjusted. */
If NODE does not represent function definition, returns NULL. Otherwise
returns an adjustment class that will be filled describing how the argument
declarations will be remapped. New arguments which are not to be remapped
are marked with USER_FLAG. */
static ipa_parm_adjustment_vec
static ipa_param_body_adjustments *
simd_clone_adjust_argument_types (struct cgraph_node *node)
{
vec<tree> args;
ipa_parm_adjustment_vec adjustments;
auto_vec<tree> args;
if (node->definition)
args = ipa_get_vector_of_formal_parms (node->decl);
push_function_arg_decls (&args, node->decl);
else
args = simd_clone_vector_of_formal_parm_types (node->decl);
adjustments.create (args.length ());
unsigned i, j, veclen;
struct ipa_parm_adjustment adj;
simd_clone_vector_of_formal_parm_types (&args, node->decl);
struct cgraph_simd_clone *sc = node->simdclone;
vec<ipa_adjusted_param, va_gc> *new_params = NULL;
vec_safe_reserve (new_params, sc->nargs);
unsigned i, j, veclen;
for (i = 0; i < sc->nargs; ++i)
{
ipa_adjusted_param adj;
memset (&adj, 0, sizeof (adj));
tree parm = args[i];
tree parm_type = node->definition ? TREE_TYPE (parm) : parm;
adj.base_index = i;
adj.base = parm;
adj.prev_clone_index = i;
sc->args[i].orig_arg = node->definition ? parm : NULL_TREE;
sc->args[i].orig_type = parm_type;
@ -594,7 +595,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
{
default:
/* No adjustment necessary for scalar arguments. */
adj.op = IPA_PARM_OP_COPY;
adj.op = IPA_PARAM_OP_COPY;
break;
case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP:
case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP:
@ -603,7 +604,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
= create_tmp_simd_array (IDENTIFIER_POINTER (DECL_NAME (parm)),
TREE_TYPE (parm_type),
sc->simdlen);
adj.op = IPA_PARM_OP_COPY;
adj.op = IPA_PARAM_OP_COPY;
break;
case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP:
case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP:
@ -615,7 +616,8 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
veclen /= GET_MODE_BITSIZE (SCALAR_TYPE_MODE (parm_type));
if (veclen > sc->simdlen)
veclen = sc->simdlen;
adj.arg_prefix = "simd";
adj.op = IPA_PARAM_OP_NEW;
adj.param_prefix_index = IPA_PARAM_PREFIX_SIMD;
if (POINTER_TYPE_P (parm_type))
adj.type = build_vector_type (pointer_sized_int_node, veclen);
else
@ -623,13 +625,15 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
sc->args[i].vector_type = adj.type;
for (j = veclen; j < sc->simdlen; j += veclen)
{
adjustments.safe_push (adj);
vec_safe_push (new_params, adj);
if (j == veclen)
{
memset (&adj, 0, sizeof (adj));
adj.op = IPA_PARM_OP_NEW;
adj.arg_prefix = "simd";
adj.op = IPA_PARAM_OP_NEW;
adj.user_flag = 1;
adj.param_prefix_index = IPA_PARAM_PREFIX_SIMD;
adj.base_index = i;
adj.prev_clone_index = i;
adj.type = sc->args[i].vector_type;
}
}
@ -640,18 +644,20 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
? IDENTIFIER_POINTER (DECL_NAME (parm))
: NULL, parm_type, sc->simdlen);
}
adjustments.safe_push (adj);
vec_safe_push (new_params, adj);
}
if (sc->inbranch)
{
tree base_type = simd_clone_compute_base_data_type (sc->origin, sc);
ipa_adjusted_param adj;
memset (&adj, 0, sizeof (adj));
adj.op = IPA_PARM_OP_NEW;
adj.arg_prefix = "mask";
adj.op = IPA_PARAM_OP_NEW;
adj.user_flag = 1;
adj.param_prefix_index = IPA_PARAM_PREFIX_MASK;
adj.base_index = i;
adj.prev_clone_index = i;
if (INTEGRAL_TYPE_P (base_type) || POINTER_TYPE_P (base_type))
veclen = sc->vecsize_int;
else
@ -666,10 +672,10 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
adj.type = build_vector_type (pointer_sized_int_node, veclen);
else
adj.type = build_vector_type (base_type, veclen);
adjustments.safe_push (adj);
vec_safe_push (new_params, adj);
for (j = veclen; j < sc->simdlen; j += veclen)
adjustments.safe_push (adj);
vec_safe_push (new_params, adj);
/* We have previously allocated one extra entry for the mask. Use
it and fill it. */
@ -694,7 +700,13 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
}
if (node->definition)
ipa_modify_formal_parameters (node->decl, adjustments);
{
ipa_param_body_adjustments *adjustments
= new ipa_param_body_adjustments (new_params, node->decl);
adjustments->modify_formal_parameters ();
return adjustments;
}
else
{
tree new_arg_types = NULL_TREE, new_reversed;
@ -703,12 +715,12 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
last_parm_void = true;
gcc_assert (TYPE_ARG_TYPES (TREE_TYPE (node->decl)));
j = adjustments.length ();
j = vec_safe_length (new_params);
for (i = 0; i < j; i++)
{
struct ipa_parm_adjustment *adj = &adjustments[i];
struct ipa_adjusted_param *adj = &(*new_params)[i];
tree ptype;
if (adj->op == IPA_PARM_OP_COPY)
if (adj->op == IPA_PARAM_OP_COPY)
ptype = args[adj->base_index];
else
ptype = adj->type;
@ -723,10 +735,8 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
new_reversed = void_list_node;
}
TYPE_ARG_TYPES (TREE_TYPE (node->decl)) = new_reversed;
adjustments.release ();
return NULL;
}
args.release ();
return adjustments;
}
/* Initialize and copy the function arguments in NODE to their
@ -735,7 +745,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
static gimple_seq
simd_clone_init_simd_arrays (struct cgraph_node *node,
ipa_parm_adjustment_vec adjustments)
ipa_param_body_adjustments *adjustments)
{
gimple_seq seq = NULL;
unsigned i = 0, j = 0, k;
@ -744,7 +754,7 @@ simd_clone_init_simd_arrays (struct cgraph_node *node,
arg;
arg = DECL_CHAIN (arg), i++, j++)
{
if (adjustments[j].op == IPA_PARM_OP_COPY
if ((*adjustments->m_adj_params)[j].op == IPA_PARAM_OP_COPY
|| POINTER_TYPE_P (TREE_TYPE (arg)))
continue;
@ -809,7 +819,7 @@ simd_clone_init_simd_arrays (struct cgraph_node *node,
/* Callback info for ipa_simd_modify_stmt_ops below. */
struct modify_stmt_info {
ipa_parm_adjustment_vec adjustments;
ipa_param_body_adjustments *adjustments;
gimple *stmt;
/* True if the parent statement was modified by
ipa_simd_modify_stmt_ops. */
@ -829,15 +839,26 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
tree *orig_tp = tp;
if (TREE_CODE (*tp) == ADDR_EXPR)
tp = &TREE_OPERAND (*tp, 0);
struct ipa_parm_adjustment *cand = NULL;
if (TREE_CODE (*tp) == BIT_FIELD_REF
|| TREE_CODE (*tp) == IMAGPART_EXPR
|| TREE_CODE (*tp) == REALPART_EXPR)
tp = &TREE_OPERAND (*tp, 0);
tree repl = NULL_TREE;
ipa_param_body_replacement *pbr = NULL;
if (TREE_CODE (*tp) == PARM_DECL)
cand = ipa_get_adjustment_candidate (&tp, NULL, info->adjustments, true);
{
pbr = info->adjustments->get_expr_replacement (*tp, true);
if (pbr)
repl = pbr->repl;
}
else if (TYPE_P (*tp))
*walk_subtrees = 0;
tree repl = NULL_TREE;
if (cand)
repl = unshare_expr (cand->new_decl);
if (repl)
repl = unshare_expr (repl);
else
{
if (tp != orig_tp)
@ -861,13 +882,13 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
if (tp != orig_tp)
{
if (gimple_code (info->stmt) == GIMPLE_PHI
&& cand
&& pbr
&& TREE_CODE (*orig_tp) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (*orig_tp, 0)) == PARM_DECL
&& cand->alias_ptr_type)
&& pbr->dummy)
{
gcc_assert (TREE_CODE (cand->alias_ptr_type) == SSA_NAME);
*orig_tp = cand->alias_ptr_type;
gcc_assert (TREE_CODE (pbr->dummy) == SSA_NAME);
*orig_tp = pbr->dummy;
info->modified = true;
return NULL_TREE;
}
@ -893,10 +914,13 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
{
gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
/* Cache SSA_NAME for next time. */
if (cand
if (pbr
&& TREE_CODE (*orig_tp) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (*orig_tp, 0)) == PARM_DECL)
cand->alias_ptr_type = repl;
{
gcc_assert (!pbr->dummy);
pbr->dummy = repl;
}
}
else
gsi = gsi_for_stmt (info->stmt);
@ -926,70 +950,56 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
static void
ipa_simd_modify_function_body (struct cgraph_node *node,
ipa_parm_adjustment_vec adjustments,
ipa_param_body_adjustments *adjustments,
tree retval_array, tree iter)
{
basic_block bb;
unsigned int i, j, l;
unsigned int i, j;
/* Re-use the adjustments array, but this time use it to replace
every function argument use to an offset into the corresponding
simd_array. */
/* Register replacements for every function argument use to an offset into
the corresponding simd_array. */
for (i = 0, j = 0; i < node->simdclone->nargs; ++i, ++j)
{
if (!node->simdclone->args[i].vector_arg)
if (!node->simdclone->args[i].vector_arg
|| (*adjustments->m_adj_params)[j].user_flag)
continue;
tree basetype = TREE_TYPE (node->simdclone->args[i].orig_arg);
tree vectype = TREE_TYPE (node->simdclone->args[i].vector_arg);
adjustments[j].new_decl
= build4 (ARRAY_REF,
basetype,
node->simdclone->args[i].simd_array,
iter,
NULL_TREE, NULL_TREE);
if (adjustments[j].op == IPA_PARM_OP_NONE
&& simd_clone_subparts (vectype) < node->simdclone->simdlen)
tree r = build4 (ARRAY_REF, basetype, node->simdclone->args[i].simd_array,
iter, NULL_TREE, NULL_TREE);
adjustments->register_replacement (&(*adjustments->m_adj_params)[j], r);
if (simd_clone_subparts (vectype) < node->simdclone->simdlen)
j += node->simdclone->simdlen / simd_clone_subparts (vectype) - 1;
}
l = adjustments.length ();
tree name;
FOR_EACH_SSA_NAME (i, name, cfun)
{
tree base_var;
if (SSA_NAME_VAR (name)
&& TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL)
&& TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL
&& (base_var
= adjustments->get_replacement_ssa_base (SSA_NAME_VAR (name))))
{
for (j = 0; j < l; j++)
if (SSA_NAME_VAR (name) == adjustments[j].base
&& adjustments[j].new_decl)
{
tree base_var;
if (adjustments[j].new_ssa_base == NULL_TREE)
{
base_var
= copy_var_decl (adjustments[j].base,
DECL_NAME (adjustments[j].base),
TREE_TYPE (adjustments[j].base));
adjustments[j].new_ssa_base = base_var;
}
else
base_var = adjustments[j].new_ssa_base;
if (SSA_NAME_IS_DEFAULT_DEF (name))
{
bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
gimple_stmt_iterator gsi = gsi_after_labels (bb);
tree new_decl = unshare_expr (adjustments[j].new_decl);
set_ssa_default_def (cfun, adjustments[j].base, NULL_TREE);
SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
SSA_NAME_IS_DEFAULT_DEF (name) = 0;
gimple *stmt = gimple_build_assign (name, new_decl);
gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
}
else
SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
}
if (SSA_NAME_IS_DEFAULT_DEF (name))
{
tree old_decl = SSA_NAME_VAR (name);
bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
gimple_stmt_iterator gsi = gsi_after_labels (bb);
tree repl = adjustments->lookup_replacement (old_decl, 0);
gcc_checking_assert (repl);
repl = unshare_expr (repl);
set_ssa_default_def (cfun, old_decl, NULL_TREE);
SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
SSA_NAME_IS_DEFAULT_DEF (name) = 0;
gimple *stmt = gimple_build_assign (name, repl);
gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
}
else
SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
}
}
@ -1162,8 +1172,9 @@ simd_clone_adjust (struct cgraph_node *node)
targetm.simd_clone.adjust (node);
tree retval = simd_clone_adjust_return_type (node);
ipa_parm_adjustment_vec adjustments
ipa_param_body_adjustments *adjustments
= simd_clone_adjust_argument_types (node);
gcc_assert (adjustments);
push_gimplify_context ();
@ -1175,7 +1186,7 @@ simd_clone_adjust (struct cgraph_node *node)
tree iter1 = make_ssa_name (iter);
tree iter2 = NULL_TREE;
ipa_simd_modify_function_body (node, adjustments, retval, iter1);
adjustments.release ();
delete adjustments;
/* Initialize the iteration variable. */
basic_block entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));

View File

@ -1062,6 +1062,13 @@ DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTOR,
"that ipa-sra replaces a pointer to an aggregate with.",
2, 0, 0)
DEFPARAM (PARAM_IPA_SRA_MAX_REPLACEMENTS,
"ipa-sra-max-replacements",
"Maximum pieces that IPA-SRA tracks per formal parameter, as "
"a consequence, also the maximum number of replacements of a formal "
"parameter.",
8, 0, 16)
DEFPARAM (PARAM_TM_MAX_AGGREGATE_SIZE,
"tm-max-aggregate-size",
"Size in bytes after which thread-local aggregates should be "

View File

@ -89,7 +89,6 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_dse);
NEXT_PASS (pass_cd_dce);
NEXT_PASS (pass_phiopt, true /* early_p */);
NEXT_PASS (pass_early_ipa_sra);
NEXT_PASS (pass_tail_recursion);
NEXT_PASS (pass_convert_switch);
NEXT_PASS (pass_cleanup_eh);
@ -148,6 +147,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_ipa_icf);
NEXT_PASS (pass_ipa_devirt);
NEXT_PASS (pass_ipa_cp);
NEXT_PASS (pass_ipa_sra);
NEXT_PASS (pass_ipa_cdtor_merge);
NEXT_PASS (pass_ipa_hsa);
NEXT_PASS (pass_ipa_fn_summary);

View File

@ -1,3 +1,43 @@
2019-09-20 Martin Jambor <mjambor@suse.cz>
* g++.dg/ipa/pr81248.C: Adjust dg-options and dump-scan.
* gcc.dg/ipa/ipa-sra-1.c: Likewise.
* gcc.dg/ipa/ipa-sra-10.c: Likewise.
* gcc.dg/ipa/ipa-sra-11.c: Likewise.
* gcc.dg/ipa/ipa-sra-3.c: Likewise.
* gcc.dg/ipa/ipa-sra-4.c: Likewise.
* gcc.dg/ipa/ipa-sra-5.c: Likewise.
* gcc.dg/ipa/ipacost-2.c: Disable ipa-sra.
* gcc.dg/ipa/ipcp-agg-9.c: Likewise.
* gcc.dg/ipa/pr78121.c: Adjust scan pattern.
* gcc.dg/ipa/vrp1.c: Likewise.
* gcc.dg/ipa/vrp2.c: Likewise.
* gcc.dg/ipa/vrp3.c: Likewise.
* gcc.dg/ipa/vrp7.c: Likewise.
* gcc.dg/ipa/vrp8.c: Likewise.
* gcc.dg/noreorder.c: use noipa attribute instead of noinline.
* gcc.dg/ipa/20040703-wpa.c: New test.
* gcc.dg/ipa/ipa-sra-12.c: New test.
* gcc.dg/ipa/ipa-sra-13.c: Likewise.
* gcc.dg/ipa/ipa-sra-14.c: Likewise.
* gcc.dg/ipa/ipa-sra-15.c: Likewise.
* gcc.dg/ipa/ipa-sra-16.c: Likewise.
* gcc.dg/ipa/ipa-sra-17.c: Likewise.
* gcc.dg/ipa/ipa-sra-18.c: Likewise.
* gcc.dg/ipa/ipa-sra-19.c: Likewise.
* gcc.dg/ipa/ipa-sra-20.c: Likewise.
* gcc.dg/ipa/ipa-sra-21.c: Likewise.
* gcc.dg/ipa/ipa-sra-22.c: Likewise.
* gcc.dg/sso/ipa-sra-1.c: Likewise.
* g++.dg/ipa/ipa-sra-2.C: Likewise.
* g++.dg/ipa/ipa-sra-3.C: Likewise.
* gcc.dg/tree-ssa/ipa-cp-1.c: Make return value used.
* g++.dg/ipa/devirt-19.C: Add missing return, add -fipa-cp-clone
option.
* g++.dg/lto/devirt-19_0.C: Add -fipa-cp-clone option.
* gcc.dg/ipa/ipa-sra-2.c: Removed.
* gcc.dg/ipa/ipa-sra-6.c: Likewise.
2019-09-19 Martin Sebor <msebor@redhat.com>
PR middle-end/91631

View File

@ -2,7 +2,7 @@
Previously we were failing by considering CLOBBER statement to be
a type change. */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-ipa-cp" } */
/* { dg-options "-O2 -fdump-ipa-cp -fipa-cp-clone" } */
/* { dg-additional-options "-Wno-return-type" } */
struct A {
@ -23,9 +23,12 @@ public:
C<int, int> b;
template <typename T, typename M> const M &C<T, M>::m_fn2(const T &) {
A a = _map.m_fn2();
a == _map.m_fn1();
m_fn1();
static M m;
return m;
}
void fn1() { b.m_fn2(0); }

View File

@ -0,0 +1,46 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra" } */
void fn1(int *, int *, double *, int *, double *);
int a, c, d, e, f, g, k;
double *b;
double h, i;
void fn2(int *p1, int *p2, double *p3) {
int l = 0, j, q, r;
double m, n, o, p, s, t, u;
--p3;
for (; a;) {
if (c) {
++*p2;
goto L170;
}
m = n = b[c];
p = t = m;
for (; j; ++j) {
u = 1.;
if (k) {
s = o;
u = -1.;
}
}
i = u * p;
L60:
p3[1] = s;
for (; d;)
goto L60;
fn1(&f, &g, &h, &l, &p3[1]);
o = p3[1];
L100:
o *= i;
if (e)
goto L100;
L170:;
}
if (*p1)
for (;;) {
if (r)
q = *p2;
d = q - j;
r = j;
}
}

View File

@ -0,0 +1,19 @@
/* { dg-do compile { target c++11 } } */
/* { dg-options "-O2 -fipa-sra" } */
class a {
void b();
char16_t c;
char16_t d;
};
void e(a);
void g();
void a::b() {
char16_t f = d;
e(*this);
for (;;) {
g();
if (f)
break;
}
}

View File

@ -0,0 +1,9 @@
/* { dg-options "-O2 -fipa-sra -fno-inline -fno-ipa-cp" } */
char *a() __attribute__((__malloc__));
static char *b() {
char *c = a();
return c;
}
int d() { b(); return 4; }

View File

@ -1,5 +1,5 @@
// { dg-do compile { target c++17 } }
// { dg-options "-O2 -fdump-tree-eipa_sra" }
// { dg-options "-O2 -fdump-ipa-sra" }
#include <type_traits>
@ -37,4 +37,4 @@ int main() {
f(n2);
}
// { dg-final { scan-tree-dump-times "Adjusting call" 2 "eipa_sra" } }
// { dg-final { scan-ipa-dump "Will split parameter 0" "sra" } }

View File

@ -1,5 +1,5 @@
/* { dg-lto-do link } */
/* { dg-lto-options { "-O2 -fdump-ipa-cp -Wno-return-type -flto -r -nostdlib" } } */
/* { dg-lto-options { "-O2 -fdump-ipa-cp -fipa-cp-clone -Wno-return-type -flto -r -nostdlib" } } */
/* { dg-extra-ld-options "-flinker-output=nolto-rel -flto=auto" } */
#include "../ipa/devirt-19.C"
/* { dg-final { scan-wpa-ipa-dump-times "Discovered a virtual call to a known target" 1 "cp" } } */

View File

@ -0,0 +1,151 @@
/* With -fwhole-program this is an excelent testcase for inlining IPA-SRAed
functions into each other. */
/* { dg-do run } */
/* { dg-options "-O2 -w -fno-ipa-cp -fwhole-program" } */
/* { dg-require-effective-target int32plus } */
#define PART_PRECISION (sizeof (cpp_num_part) * 8)
typedef unsigned int cpp_num_part;
typedef struct cpp_num cpp_num;
struct cpp_num
{
cpp_num_part high;
cpp_num_part low;
int unsignedp; /* True if value should be treated as unsigned. */
int overflow; /* True if the most recent calculation overflowed. */
};
static int
num_positive (cpp_num num, unsigned int precision)
{
if (precision > PART_PRECISION)
{
precision -= PART_PRECISION;
return (num.high & (cpp_num_part) 1 << (precision - 1)) == 0;
}
return (num.low & (cpp_num_part) 1 << (precision - 1)) == 0;
}
static cpp_num
num_trim (cpp_num num, unsigned int precision)
{
if (precision > PART_PRECISION)
{
precision -= PART_PRECISION;
if (precision < PART_PRECISION)
num.high &= ((cpp_num_part) 1 << precision) - 1;
}
else
{
if (precision < PART_PRECISION)
num.low &= ((cpp_num_part) 1 << precision) - 1;
num.high = 0;
}
return num;
}
/* Shift NUM, of width PRECISION, right by N bits. */
static cpp_num
num_rshift (cpp_num num, unsigned int precision, unsigned int n)
{
cpp_num_part sign_mask;
int x = num_positive (num, precision);
if (num.unsignedp || x)
sign_mask = 0;
else
sign_mask = ~(cpp_num_part) 0;
if (n >= precision)
num.high = num.low = sign_mask;
else
{
/* Sign-extend. */
if (precision < PART_PRECISION)
num.high = sign_mask, num.low |= sign_mask << precision;
else if (precision < 2 * PART_PRECISION)
num.high |= sign_mask << (precision - PART_PRECISION);
if (n >= PART_PRECISION)
{
n -= PART_PRECISION;
num.low = num.high;
num.high = sign_mask;
}
if (n)
{
num.low = (num.low >> n) | (num.high << (PART_PRECISION - n));
num.high = (num.high >> n) | (sign_mask << (PART_PRECISION - n));
}
}
num = num_trim (num, precision);
num.overflow = 0;
return num;
}
#define num_zerop(num) ((num.low | num.high) == 0)
#define num_eq(num1, num2) (num1.low == num2.low && num1.high == num2.high)
cpp_num
num_lshift (cpp_num num, unsigned int precision, unsigned int n)
{
if (n >= precision)
{
num.overflow = !num.unsignedp && !num_zerop (num);
num.high = num.low = 0;
}
else
{
cpp_num orig;
unsigned int m = n;
orig = num;
if (m >= PART_PRECISION)
{
m -= PART_PRECISION;
num.high = num.low;
num.low = 0;
}
if (m)
{
num.high = (num.high << m) | (num.low >> (PART_PRECISION - m));
num.low <<= m;
}
num = num_trim (num, precision);
if (num.unsignedp)
num.overflow = 0;
else
{
cpp_num maybe_orig = num_rshift (num, precision, n);
num.overflow = !num_eq (orig, maybe_orig);
}
}
return num;
}
unsigned int precision = 64;
unsigned int n = 16;
cpp_num num = { 0, 3, 0, 0 };
int main()
{
cpp_num res = num_lshift (num, 64, n);
if (res.low != 0x30000)
abort ();
if (res.high != 0)
abort ();
if (res.overflow != 0)
abort ();
exit (0);
}

View File

@ -1,5 +1,5 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra-details" } */
struct bovid
{
@ -36,4 +36,4 @@ main (int argc, char *argv[])
return 0;
}
/* { dg-final { scan-tree-dump-times "About to replace expr" 2 "eipa_sra" } } */
/* { dg-final { scan-ipa-dump "Will split parameter" "sra" } } */

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
/* { dg-options "-O2 -fno-ipa-cp -fipa-sra -fdump-ipa-sra" } */
extern void consume (int);
extern int glob, glob1, glob2;
@ -31,4 +31,4 @@ bar (int a)
return 0;
}
/* { dg-final { scan-tree-dump-times "replacing an SSA name of a removed param" 4 "eipa_sra" } } */
/* { dg-final { scan-ipa-dump "Will remove parameter 0" "sra" } } */

View File

@ -1,5 +1,5 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra-details" } */
struct bovid
{
@ -36,4 +36,4 @@ main (int argc, char *argv[])
return 0;
}
/* { dg-final { scan-tree-dump-not "About to replace expr" "eipa_sra" } } */
/* { dg-final { scan-ipa-dump-not "Will split parameter" "sra" } } */

View File

@ -0,0 +1,50 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra" } */
/* Check of a simple and transitive structure split. */
struct S
{
float red;
void *blue;
int green;
};
void __attribute__((noipa))
check (float r, int g, int g2)
{
if (r < 7.39 || r > 7.41
|| g != 6 || g2 != 6)
__builtin_abort ();
}
static void
__attribute__((noinline))
foo (struct S s)
{
check (s.red, s.green, s.green);
}
static void
__attribute__((noinline))
bar (struct S s)
{
foo (s);
}
int
main (int argc, char *argv[])
{
struct S s;
s.red = 7.4;
s.green = 6;
s.blue = &s;
bar (s);
return 0;
}
/* { dg-final { scan-ipa-dump-times "Will split parameter" 2 "sra" } } */
/* { dg-final { scan-ipa-dump-times "component at byte offset" 4 "sra" } } */

View File

@ -0,0 +1,49 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra" } */
/* Check of a by-reference structure split. */
struct S
{
float red;
void *blue;
int green;
};
void __attribute__((noipa))
check (float r, int g, int g2)
{
if (r < 7.39 || r > 7.41
|| g != 6 || g2 != 6)
__builtin_abort ();
}
static void
__attribute__((noinline))
foo (struct S *s)
{
check (s->red, s->green, s->green);
}
static void
__attribute__((noinline))
bar (struct S *s)
{
foo (s);
}
int
main (int argc, char *argv[])
{
struct S s;
s.red = 7.4;
s.green = 6;
s.blue = &s;
bar (&s);
return 0;
}
/* { dg-final { scan-ipa-dump-times "Will split parameter" 2 "sra" } } */
/* { dg-final { scan-ipa-dump-times "component at byte offset" 4 "sra" } } */

View File

@ -0,0 +1,60 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra" } */
/* Check of a transitive recursive structure split. */
struct S
{
float red;
void *blue;
int green;
};
static int done = 0;
void __attribute__((noipa))
check (float r, int g, int g2)
{
if (r < 7.39 || r > 7.41
|| g != 6 || g2 != 6)
__builtin_abort ();
}
static void __attribute__((noinline)) bar (struct S s);
static void
__attribute__((noinline))
foo (struct S s)
{
if (!done)
{
done = 1;
bar (s);
}
check (s.red, s.green, s.green);
}
static void
__attribute__((noinline))
bar (struct S s)
{
foo (s);
}
int
main (int argc, char *argv[])
{
struct S s;
s.red = 7.4;
s.green = 6;
s.blue = &s;
bar (s);
return 0;
}
/* { dg-final { scan-ipa-dump-times "Will split parameter" 2 "sra" } } */
/* { dg-final { scan-ipa-dump-times "component at byte offset" 4 "sra" } } */

View File

@ -0,0 +1,61 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra" } */
/* Check of a recursive by-reference structure split. The recursive functions
have to be pure right from the start, otherwise the current AA would detect
possible modification of data. */
struct S
{
float red;
void *blue;
int green;
};
void __attribute__((noipa))
check (float r, int g, int g2)
{
if (r < 7.39 || r > 7.41
|| g != 6 || g2 != 6)
__builtin_abort ();
return;
}
static int __attribute__((noinline, pure)) bar (struct S *s, int rec);
static int
__attribute__((noinline, pure))
foo (struct S *s , int rec)
{
int t = 0;
if (rec)
t = bar (s, 0);
check (s->red, s->green, s->green);
return t;
}
static int
__attribute__((noinline, pure))
bar (struct S *s, int rec)
{
int t = foo (s, rec);
return t + t;
}
volatile int g;
int
main (int argc, char *argv[])
{
struct S s;
s.red = 7.4;
s.green = 6;
s.blue = &s;
g = bar (&s, 1);
return 0;
}
/* { dg-final { scan-ipa-dump-times "Will split parameter" 2 "sra" } } */
/* { dg-final { scan-ipa-dump-times "component at byte offset" 4 "sra" } } */

View File

@ -0,0 +1,74 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra -fdump-tree-optimized" } */
/* Testing removal of unused parameters in recursive calls. */
extern int work_1 (int);
extern int work_2 (int);
static int __attribute__((noinline))
foo (int l, int w1, int w2, int useless, int useless2);
static int __attribute__((noinline))
bar_1 (int l, int w1, int w2, int useless, int useless2)
{
return work_1 (w1) + foo (l, w1, w2, useless2, useless);
}
static int __attribute__((noinline))
baz_1 (int useless, int useless2, int l, int w1, int w2)
{
return bar_1 (l, w1, w2, useless, useless2);
}
static int __attribute__((noinline))
bax_1 (int l, int w1, int w2, int useless, int useless2)
{
return baz_1 (useless, useless2, l, w1, w2);
}
static int __attribute__((noinline))
bar_2 (int l, int w1, int w2, int useless, int useless2)
{
return foo (l, w1, w2, useless2 + 5, useless);
}
static int __attribute__((noinline))
baz_2 (int useless, int useless2, int l, int w1, int w2)
{
return bar_2 (l, w1, w2, useless, useless2);
}
static int __attribute__((noinline))
bax_2 (int l, int w1, int w2, int useless, int useless2)
{
return work_2 (w2) + baz_2 (useless, useless2, l, w1, w2);
}
static int __attribute__((noinline))
foo (int l, int w1, int w2, int useless, int useless2)
{
int r = 0;
if (!l)
return r;
if (l % 2)
r = bax_1 (l - 1, w1, w2, useless, useless2);
else
r = bax_2 (l - 1, w1, w2, useless, useless2);
return r;
}
int
entry (int l, int w1, int w2, int noneed, int noneed2)
{
return foo (l, w2, w2, noneed2, noneed2 + 4);
}
/* { dg-final { scan-ipa-dump-times "Will remove parameter" 14 "sra" } } */
/* { dg-final { scan-tree-dump-not "useless" "optimized"} } */

View File

@ -0,0 +1,102 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-ipa-sra -fdump-tree-optimized" } */
#define DOIT
#define DONT
extern int extern_leaf (int);
/* ----- 1 ----- */
#ifdef DOIT
static int __attribute__((noinline))
whee_1 (int i, int j)
{
return extern_leaf (i * j) + 1;
}
static int foo_1 (int i, int j);
static int __attribute__((noinline))
baz_1 (int i, int j)
{
int a = 5;
if (j)
a = foo_1 (i, j - 1);
return whee_1 (i, j) + a + 1;
}
static int __attribute__((noinline))
bar_1 (int i, int j)
{
return baz_1 (i, j) + 1;
}
static int __attribute__((noinline))
foo_1 (int i, int j)
{
return bar_1 (i, j) + 1;
}
static int __attribute__((noinline))
inter_1 (int i, int j)
{
return foo_1 (i, j) + 1;
}
#endif
/* ----- 2 ----- */
#ifdef DONT
static int __attribute__((noinline))
whee_2 (int i, int j)
{
return extern_leaf (i * j) + 2;
}
static int foo_2 (int i, int j);
static int __attribute__((noinline))
baz_2 (int i, int j)
{
int a = 6;
if (j)
a = foo_2 (i, j - 1);
return whee_2 (i, j) + a + 2;
}
static int __attribute__((noinline))
bar_2 (int i, int j)
{
return baz_2 (i, j) + 2;
}
static int __attribute__((noinline))
foo_2 (int i, int j)
{
return bar_2 (i, j) + 2;
}
#endif
/* ----- entries ----- */
#ifdef DOIT
int
entry_1 (int i, int j)
{
inter_1 (i, j);
return i + j + 1;
}
#endif
#ifdef DONT
int
entry_2 (int i, int j)
{
#ifdef DOIT
inter_1 (i, j);
#endif
return i + j + bar_2 (i, j);
}
#endif
/* { dg-final { scan-ipa-dump-times "Will remove return value" 5 "sra" } } */
/* { dg-final { scan-tree-dump-times "return;" 5 "optimized"} } */

View File

@ -0,0 +1,49 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-ipa-sra" } */
struct S
{
long a, b;
};
extern void leaf_a (int );
extern void leaf_b (int, int);
extern void leaf_c (int, int);
extern void leaf_sa (struct S);
static void baz (int i, int j, int k, int l, struct S a, struct S b);
extern int gi;
static void __attribute__((noinline))
foo (int i, int j, int k, int l, struct S a, struct S b)
{
gi += l;
baz (i, j, k, l, a, b);
}
static void __attribute__((noinline))
bar (int i, int j, int k, int l, struct S a, struct S b)
{
foo (i, j, k, l, a, b);
leaf_sa (b);
}
static void __attribute__((noinline))
baz (int i, int j, int k, int l, struct S a, struct S b)
{
if (--k)
bar (i, j, k, l, a, b);
leaf_b (i, k);
}
void
entry (int i, int j, int k, int l, struct S a, struct S b)
{
foo (i, j, k, l, a, b);
}
/* { dg-final { scan-ipa-dump-times "Will remove parameter 1" 3 "sra" } } */
/* { dg-final { scan-ipa-dump-times "Will remove parameter 4" 3 "sra" } } */

View File

@ -0,0 +1,31 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
typedef int __attribute__((__vector_size__(16))) vectype;
vectype dk();
vectype k();
int b;
vectype *j;
inline int c(vectype *d) {
vectype e;
vectype f;
vectype g = *d;
vectype h = g;
vectype i = h;
f = i == dk();
e = f == b;
k(e);
}
static void m(vectype *d) {
int l = c(d);
if (l)
c(j);
}
void o(void) {
vectype n;
m(&n);
}

View File

@ -1,51 +0,0 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
struct bovid
{
float red;
int green;
void *blue;
};
static int
__attribute__((noinline))
ox (struct bovid *cow)
{
cow->red = cow->red + cow->green + cow->green;
return 0;
}
int something;
static int
__attribute__((noinline))
ox_improved (struct bovid *calf)
{
if (something > 0)
calf->red = calf->red + calf->green;
else
calf->red = calf->green + 87;
something = 77;
return 0;
}
int main (int argc, char *argv[])
{
struct bovid cow;
cow.red = 7.4;
cow.green = 6;
cow.blue = &cow;
ox (&cow);
ox_improved (&cow);
return 0;
}
/* { dg-final { scan-tree-dump "About to replace expr cow_.*D.->red with \\*ISRA" "eipa_sra" } } */
/* { dg-final { scan-tree-dump "About to replace expr cow_.*D.->green with ISRA" "eipa_sra" } } */
/* { dg-final { scan-tree-dump "About to replace expr calf_.*D.->red with \\*ISRA" "eipa_sra" } } */
/* { dg-final { scan-tree-dump "About to replace expr calf_.*D.->green with ISRA" "eipa_sra" } } */

View File

@ -0,0 +1,38 @@
/* { dg-do compile } */
/* { dg-options "-O1 -fipa-sra" } */
typedef struct {
int a;
} b;
typedef struct {
double c;
double a;
} d;
typedef struct {
d e;
d f;
} g;
g h;
b i, m;
int j, k, l, n, o;
static b q(d s) {
int r = s.c ?: 0;
if (r)
if (j)
l = j - 2;
o = k;
n = l;
i = m;
return m;
}
static void t(g s) {
{
d p = s.e;
int r = p.c ?: 0;
if (r) {
l = j - 2;
}
}
b f = q(s.f);
}
void main() { t(h); }

View File

@ -0,0 +1,33 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
typedef int a;
typedef int b;
int c, e;
void i();
void n(d, ab, f, ae, af, action, ag, ah, ai, g, h, aj, ak, al, j, k, am, an, ao,
l, m) int action,
ag;
int f, ae, af;
int ah, ai;
int j, k;
int l, m;
a aj, am;
int ak, al, an, ao, g, h;
char d, ab;
{
if (c)
i(e);
}
void o(d, ab, action, at, ag, g, h, aj, ak, al, au, av, am, an, ao, aw, ax, ay,
az, ba, bb, ai) int action,
ag;
int at, ai;
int au, av, aw, ax;
b ay, ba;
int az, bb;
int g, h;
int ak, al, an, ao;
a aj, am;
char d, ab;
{ n(); }

View File

@ -0,0 +1,56 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra" } */
struct W
{
int a, b;
};
union U
{
struct W w;
long l;
};
struct Z
{
int k;
union U u;
};
struct S
{
int i, j;
struct Z z;
char buf[64];
};
struct W gw;
static long
__attribute__((noinline))
foo (struct Z z)
{
return z.u.l;
}
static long
__attribute__((noinline))
bar (struct S s)
{
if (s.i > 100)
return s.z.u.w.a;
else
return foo (s.z);
}
volatile long g;
long
entry (struct S *p)
{
struct S s = *p;
return bar (s) | 2;
}

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
/* { dg-options "-O2 -fno-ipa-cp -fipa-sra -fdump-ipa-sra" } */
struct bovid
{
@ -34,5 +34,6 @@ void caller (void)
return;
}
/* { dg-final { scan-tree-dump "base: z, remove_param" "eipa_sra" } } */
/* { dg-final { scan-tree-dump "base: calf, remove_param" "eipa_sra" } } */
/* { dg-final { scan-ipa-dump "Will split parameter 0" "sra" } } */
/* { dg-final { scan-ipa-dump "Will remove parameter 1" "sra" } } */
/* { dg-final { scan-ipa-dump "Will remove parameter 2" "sra" } } */

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
/* { dg-options "-O2 -fipa-sra -fno-ipa-pure-const -fdump-ipa-sra" } */
static int
__attribute__((noinline))
@ -61,7 +61,5 @@ void caller (void)
return;
}
/* { dg-final { scan-tree-dump "About to replace expr \\*i_.*D. with ISRA" "eipa_sra" } } */
/* { dg-final { scan-tree-dump "About to replace expr \\*l_.*D. with ISRA" "eipa_sra" } } */
/* { dg-final { scan-tree-dump-times "About to replace expr \*j_.*D. with ISRA" 0 "eipa_sra" } } */
/* { dg-final { scan-tree-dump-times "About to replace expr \*k_.*D. with ISRA" 0 "eipa_sra" } } */
/* { dg-final { scan-ipa-dump-times "Will split parameter" 2 "sra" } } */

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-details" } */
/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra" } */
static int *
__attribute__((noinline,used))
@ -16,4 +16,4 @@ int *caller (void)
return ox (&a, &b);
}
/* { dg-final { scan-tree-dump-times "base: j, remove_param" 0 "eipa_sra" } } */
/* { dg-final { scan-ipa-dump-times "Will split parameter" 0 "sra" } } */

View File

@ -1,33 +0,0 @@
/* { dg-do compile } */
/* { dg-options "-O2 -fipa-sra -fdump-tree-eipa_sra-slim" } */
/* { dg-require-effective-target non_strict_align } */
struct bovid
{
float a;
int b;
struct bovid *next;
};
static int
__attribute__((noinline))
foo (struct bovid *cow, int i)
{
i++;
if (cow->next)
foo (cow->next, i);
return i;
}
int main (int argc, char *argv[])
{
struct bovid cow;
cow.a = 7.4;
cow.b = 6;
cow.next = (struct bovid *) 0;
return foo (&cow, 0);
}
/* { dg-final { scan-tree-dump-times "foo " 1 "eipa_sra" } } */

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf" } */
/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fno-ipa-sra -fdump-tree-optimized -fno-ipa-icf" } */
/* { dg-add-options bind_pic_locally } */
int array[100];
@ -72,7 +72,7 @@ main()
}
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of i_can_be_propagated_fully2" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of i_can_be_propagated_fully/" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of i_can_be_propagated_fully\[./\]" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-not "Creating a specialized node of i_can_not_be_propagated_fully2" "cp" } } */
/* { dg-final { scan-ipa-dump-not "Creating a specialized node of i_can_not_be_propagated_fully/" "cp" } } */
/* { dg-final { scan-tree-dump-not "i_can_be_propagated_fully \\(" "optimized" } } */

View File

@ -1,6 +1,6 @@
/* Verify that IPA-CP can make edges direct based on aggregate contents. */
/* { dg-do compile } */
/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp -fdump-ipa-inline" } */
/* { dg-options "-O3 -fno-early-inlining -fno-ipa-sra -fdump-ipa-cp -fdump-ipa-inline" } */
struct S
{

View File

@ -13,4 +13,4 @@ static void fn1(c) unsigned char c;
void fn3() { fn1 (267); }
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\\[11, 35\\\]" 1 "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\(now 0\\) \\\[11, 35\\\]" "cp" } } */

View File

@ -28,5 +28,5 @@ int main ()
return 0;
}
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\\[6," "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\\[0, 999\\\]" "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\(now 0\\) \\\[6," "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\(now 0\\) \\\[0, 999\\\]" "cp" } } */

View File

@ -31,5 +31,5 @@ int main ()
return 0;
}
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\\[4," "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\\[0, 11\\\]" "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\(now 0\\) \\\[4," "cp" } } */
/* { dg-final { scan-ipa-dump "Setting value range of param 0 \\(now 0\\) \\\[0, 11\\\]" "cp" } } */

View File

@ -27,4 +27,4 @@ int main ()
return 0;
}
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\\[0, 9\\\]" 2 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\(now 0\\) \\\[0, 9\\\]" 2 "cp" } } */

View File

@ -29,4 +29,4 @@ int main ()
return 0;
}
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\\[-10, 9\\\]" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\(now 0\\) \\\[-10, 9\\\]" 1 "cp" } } */

View File

@ -39,4 +39,4 @@ main ()
return 0;
}
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\\[-10, 9\\\]" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Setting value range of param 0 \\(now 0\\) \\\[-10, 9\\\]" 1 "cp" } } */

View File

@ -13,7 +13,7 @@ static int func2(void);
asm("firstasm");
NOREORDER __attribute__((noinline)) int bozo(void)
NOREORDER __attribute__((noipa)) int bozo(void)
{
f2(3);
func2();
@ -21,14 +21,14 @@ NOREORDER __attribute__((noinline)) int bozo(void)
asm("jukjuk");
NOREORDER __attribute__((noinline)) static int func1(void)
NOREORDER __attribute__((noipa)) static int func1(void)
{
f2(1);
}
asm("barbar");
NOREORDER __attribute__((noinline)) static int func2(void)
NOREORDER __attribute__((noipa)) static int func2(void)
{
func1();
}

View File

@ -0,0 +1,57 @@
/* { dg-do run } */
/* { dg-options "-O2 -fipa-sra" } */
struct __attribute__((scalar_storage_order("little-endian"))) LE
{
int i;
int j;
};
struct __attribute__((scalar_storage_order("big-endian"))) BE
{
int i;
int j;
};
struct LE gle;
struct BE gbe;
#define VAL 0x12345678
void __attribute__((noipa))
fill (void)
{
gle.i = VAL;
gle.j = 0xdeadbeef;
gbe.i = VAL;
gbe.j = 0x11223344;
}
static int __attribute__((noinline))
readLE (struct LE p)
{
return p.i;
}
static int __attribute__((noinline))
readBE (struct BE p)
{
return p.i;
}
int
main (int argc, char *argv[])
{
int r;
fill ();
r = readLE (gle);
if (r != VAL)
__builtin_abort ();
r = readBE (gbe);
if (r != VAL)
__builtin_abort ();
return 0;
}

View File

@ -14,7 +14,7 @@ very_long_function(int a)
int
blah ()
{
very_long_function (1);
return very_long_function (1);
}
/* One appearance for dump, one self recursive call and one call from main. */
/* { dg-final { scan-tree-dump-times "very_long_function.constprop \\(\\)" 3 "optimized"} } */

View File

@ -5009,8 +5009,7 @@ ipa_tm_create_version (struct cgraph_node *old_node)
}
tree_function_versioning (old_decl, new_decl,
NULL, false, NULL,
false, NULL, NULL);
NULL, NULL, false, NULL, NULL);
}
record_tm_clone_pair (old_decl, new_decl);

View File

@ -130,7 +130,6 @@ static void copy_bind_expr (tree *, int *, copy_body_data *);
static void declare_inline_vars (tree, tree);
static void remap_save_expr (tree *, hash_map<tree, tree> *, int *);
static void prepend_lexical_block (tree current_block, tree new_block);
static tree copy_decl_to_var (tree, copy_body_data *);
static tree copy_result_decl_to_var (tree, copy_body_data *);
static tree copy_decl_maybe_to_var (tree, copy_body_data *);
static gimple_seq remap_gimple_stmt (gimple *, copy_body_data *);
@ -192,7 +191,21 @@ remap_ssa_name (tree name, copy_body_data *id)
n = id->decl_map->get (name);
if (n)
return unshare_expr (*n);
{
/* WHen we perform edge redirection as part of CFG copy, IPA-SRA can
remove an unused LHS from a call statement. Such LHS can however
still appear in debug statements, but their value is lost in this
function and we do not want to map them. */
if (id->killed_new_ssa_names
&& id->killed_new_ssa_names->contains (*n))
{
gcc_assert (processing_debug_stmt);
processing_debug_stmt = -1;
return name;
}
return unshare_expr (*n);
}
if (processing_debug_stmt)
{
@ -1902,6 +1915,21 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
gcc_assert (n);
gimple_set_block (copy, *n);
}
if (id->param_body_adjs)
{
gimple_seq extra_stmts = NULL;
id->param_body_adjs->modify_gimple_stmt (&copy, &extra_stmts);
if (!gimple_seq_empty_p (extra_stmts))
{
memset (&wi, 0, sizeof (wi));
wi.info = id;
for (gimple_stmt_iterator egsi = gsi_start (extra_stmts);
!gsi_end_p (egsi);
gsi_next (&egsi))
walk_gimple_op (gsi_stmt (egsi), remap_gimple_op_r, &wi);
gimple_seq_add_seq (&stmts, extra_stmts);
}
}
if (id->reset_location)
gimple_set_location (copy, input_location);
@ -2865,10 +2893,24 @@ redirect_all_calls (copy_body_data * id, basic_block bb)
gimple *stmt = gsi_stmt (si);
if (is_gimple_call (stmt))
{
tree old_lhs = gimple_call_lhs (stmt);
struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
if (edge)
{
edge->redirect_call_stmt_to_callee ();
gimple *new_stmt = edge->redirect_call_stmt_to_callee ();
/* If IPA-SRA transformation, run as part of edge redirection,
removed the LHS because it is unused, save it to
killed_new_ssa_names so that we can prune it from debug
statements. */
if (old_lhs
&& TREE_CODE (old_lhs) == SSA_NAME
&& !gimple_call_lhs (new_stmt))
{
if (!id->killed_new_ssa_names)
id->killed_new_ssa_names = new hash_set<tree> (16);
id->killed_new_ssa_names->add (old_lhs);
}
if (stmt == last && id->call_stmt && maybe_clean_eh_stmt (stmt))
gimple_purge_dead_eh_edges (bb);
}
@ -3189,6 +3231,8 @@ copy_body (copy_body_data *id,
body = copy_cfg_body (id, entry_block_map, exit_block_map,
new_entry);
copy_debug_stmts (id);
delete id->killed_new_ssa_names;
id->killed_new_ssa_names = NULL;
return body;
}
@ -4908,6 +4952,38 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id)
/* Add local vars in this inlined callee to caller. */
add_local_variables (id->src_cfun, cfun, id);
if (id->src_node->clone.performed_splits)
{
/* Any calls from the inlined function will be turned into calls from the
function we inline into. We must preserve notes about how to split
parameters such calls should be redirected/updated. */
unsigned len = vec_safe_length (id->src_node->clone.performed_splits);
for (unsigned i = 0; i < len; i++)
{
ipa_param_performed_split ps
= (*id->src_node->clone.performed_splits)[i];
ps.dummy_decl = remap_decl (ps.dummy_decl, id);
vec_safe_push (id->dst_node->clone.performed_splits, ps);
}
if (flag_checking)
{
len = vec_safe_length (id->dst_node->clone.performed_splits);
for (unsigned i = 0; i < len; i++)
{
ipa_param_performed_split *ps1
= &(*id->dst_node->clone.performed_splits)[i];
for (unsigned j = i + 1; j < len; j++)
{
ipa_param_performed_split *ps2
= &(*id->dst_node->clone.performed_splits)[j];
gcc_assert (ps1->dummy_decl != ps2->dummy_decl
|| ps1->unit_offset != ps2->unit_offset);
}
}
}
}
if (dump_enabled_p ())
{
char buf[128];
@ -5733,7 +5809,11 @@ copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
return copy;
}
static tree
/* Create a new VAR_DECL that is indentical in all respect to DECL except that
DECL can be either a VAR_DECL, a PARM_DECL or RESULT_DECL. The original
DECL must come from ID->src_fn and the copy will be part of ID->dst_fn. */
tree
copy_decl_to_var (tree decl, copy_body_data *id)
{
tree copy, type;
@ -5816,38 +5896,24 @@ copy_decl_maybe_to_var (tree decl, copy_body_data *id)
return copy_decl_no_change (decl, id);
}
/* Return a copy of the function's argument tree. */
/* Return a copy of the function's argument tree without any modifications. */
static tree
copy_arguments_for_versioning (tree orig_parm, copy_body_data * id,
bitmap args_to_skip, tree *vars)
copy_arguments_nochange (tree orig_parm, copy_body_data * id)
{
tree arg, *parg;
tree new_parm = NULL;
int i = 0;
parg = &new_parm;
for (arg = orig_parm; arg; arg = DECL_CHAIN (arg), i++)
if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
{
tree new_tree = remap_decl (arg, id);
if (TREE_CODE (new_tree) != PARM_DECL)
new_tree = id->copy_decl (arg, id);
lang_hooks.dup_lang_specific_decl (new_tree);
*parg = new_tree;
parg = &DECL_CHAIN (new_tree);
}
else if (!id->decl_map->get (arg))
{
/* Make an equivalent VAR_DECL. If the argument was used
as temporary variable later in function, the uses will be
replaced by local variable. */
tree var = copy_decl_to_var (arg, id);
insert_decl_map (id, arg, var);
/* Declare this new variable. */
DECL_CHAIN (var) = *vars;
*vars = var;
}
for (arg = orig_parm; arg; arg = DECL_CHAIN (arg))
{
tree new_tree = remap_decl (arg, id);
if (TREE_CODE (new_tree) != PARM_DECL)
new_tree = id->copy_decl (arg, id);
lang_hooks.dup_lang_specific_decl (new_tree);
*parg = new_tree;
parg = &DECL_CHAIN (new_tree);
}
return new_parm;
}
@ -5883,6 +5949,18 @@ tree_versionable_function_p (tree fndecl)
static void
update_clone_info (copy_body_data * id)
{
vec<ipa_param_performed_split, va_gc> *cur_performed_splits
= id->dst_node->clone.performed_splits;
if (cur_performed_splits)
{
unsigned len = cur_performed_splits->length ();
for (unsigned i = 0; i < len; i++)
{
ipa_param_performed_split *ps = &(*cur_performed_splits)[i];
ps->dummy_decl = remap_decl (ps->dummy_decl, id);
}
}
struct cgraph_node *node;
if (!id->dst_node->clones)
return;
@ -5896,10 +5974,55 @@ update_clone_info (copy_body_data * id)
{
struct ipa_replace_map *replace_info;
replace_info = (*node->clone.tree_map)[i];
walk_tree (&replace_info->old_tree, copy_tree_body_r, id, NULL);
walk_tree (&replace_info->new_tree, copy_tree_body_r, id, NULL);
}
}
if (node->clone.performed_splits)
{
unsigned len = vec_safe_length (node->clone.performed_splits);
for (unsigned i = 0; i < len; i++)
{
ipa_param_performed_split *ps
= &(*node->clone.performed_splits)[i];
ps->dummy_decl = remap_decl (ps->dummy_decl, id);
}
}
if (unsigned len = vec_safe_length (cur_performed_splits))
{
/* We do not want to add current performed splits when we are saving
a copy of function body for later during inlining, that would just
duplicate all entries. So let's have a look whether anything
referring to the first dummy_decl is present. */
unsigned dst_len = vec_safe_length (node->clone.performed_splits);
ipa_param_performed_split *first = &(*cur_performed_splits)[0];
for (unsigned i = 0; i < dst_len; i++)
if ((*node->clone.performed_splits)[i].dummy_decl
== first->dummy_decl)
{
len = 0;
break;
}
for (unsigned i = 0; i < len; i++)
vec_safe_push (node->clone.performed_splits,
(*cur_performed_splits)[i]);
if (flag_checking)
{
for (unsigned i = 0; i < dst_len; i++)
{
ipa_param_performed_split *ps1
= &(*node->clone.performed_splits)[i];
for (unsigned j = i + 1; j < dst_len; j++)
{
ipa_param_performed_split *ps2
= &(*node->clone.performed_splits)[j];
gcc_assert (ps1->dummy_decl != ps2->dummy_decl
|| ps1->unit_offset != ps2->unit_offset);
}
}
}
}
if (node->clones)
node = node->clones;
else if (node->next_sibling_clone)
@ -5921,19 +6044,18 @@ update_clone_info (copy_body_data * id)
tree with another tree while duplicating the function's
body, TREE_MAP represents the mapping between these
trees. If UPDATE_CLONES is set, the call_stmt fields
of edges of clones of the function will be updated.
of edges of clones of the function will be updated.
If non-NULL ARGS_TO_SKIP determine function parameters to remove
from new version.
If SKIP_RETURN is true, the new version will return void.
If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
If non-NULL PARAM_ADJUSTMENTS determines how function prototype (i.e. the
function parameters and return value) should be modified).
If non-NULL BLOCKS_TO_COPY determine what basic blocks to copy.
If non_NULL NEW_ENTRY determine new entry BB of the clone.
*/
void
tree_function_versioning (tree old_decl, tree new_decl,
vec<ipa_replace_map *, va_gc> *tree_map,
bool update_clones, bitmap args_to_skip,
bool skip_return, bitmap blocks_to_copy,
ipa_param_adjustments *param_adjustments,
bool update_clones, bitmap blocks_to_copy,
basic_block new_entry)
{
struct cgraph_node *old_version_node;
@ -5945,7 +6067,6 @@ tree_function_versioning (tree old_decl, tree new_decl,
basic_block old_entry_block, bb;
auto_vec<gimple *, 10> init_stmts;
tree vars = NULL_TREE;
bitmap debug_args_to_skip = args_to_skip;
gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL
&& TREE_CODE (new_decl) == FUNCTION_DECL);
@ -6021,96 +6142,78 @@ tree_function_versioning (tree old_decl, tree new_decl,
DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl
= copy_static_chain (p, &id);
auto_vec<int, 16> new_param_indices;
ipa_param_adjustments *old_param_adjustments
= old_version_node->clone.param_adjustments;
if (old_param_adjustments)
old_param_adjustments->get_updated_indices (&new_param_indices);
/* If there's a tree_map, prepare for substitution. */
if (tree_map)
for (i = 0; i < tree_map->length (); i++)
{
gimple *init;
replace_info = (*tree_map)[i];
if (replace_info->replace_p)
{
int parm_num = -1;
if (!replace_info->old_tree)
{
int p = replace_info->parm_num;
tree parm;
tree req_type, new_type;
for (parm = DECL_ARGUMENTS (old_decl); p;
parm = DECL_CHAIN (parm))
p--;
replace_info->old_tree = parm;
parm_num = replace_info->parm_num;
req_type = TREE_TYPE (parm);
new_type = TREE_TYPE (replace_info->new_tree);
if (!useless_type_conversion_p (req_type, new_type))
{
if (fold_convertible_p (req_type, replace_info->new_tree))
replace_info->new_tree
= fold_build1 (NOP_EXPR, req_type,
replace_info->new_tree);
else if (TYPE_SIZE (req_type) == TYPE_SIZE (new_type))
replace_info->new_tree
= fold_build1 (VIEW_CONVERT_EXPR, req_type,
replace_info->new_tree);
else
{
if (dump_file)
{
fprintf (dump_file, " const ");
print_generic_expr (dump_file,
replace_info->new_tree);
fprintf (dump_file,
" can't be converted to param ");
print_generic_expr (dump_file, parm);
fprintf (dump_file, "\n");
}
replace_info->old_tree = NULL;
}
}
}
int p = replace_info->parm_num;
if (old_param_adjustments)
p = new_param_indices[p];
tree parm;
tree req_type, new_type;
for (parm = DECL_ARGUMENTS (old_decl); p;
parm = DECL_CHAIN (parm))
p--;
tree old_tree = parm;
req_type = TREE_TYPE (parm);
new_type = TREE_TYPE (replace_info->new_tree);
if (!useless_type_conversion_p (req_type, new_type))
{
if (fold_convertible_p (req_type, replace_info->new_tree))
replace_info->new_tree
= fold_build1 (NOP_EXPR, req_type, replace_info->new_tree);
else if (TYPE_SIZE (req_type) == TYPE_SIZE (new_type))
replace_info->new_tree
= fold_build1 (VIEW_CONVERT_EXPR, req_type,
replace_info->new_tree);
else
gcc_assert (TREE_CODE (replace_info->old_tree) == PARM_DECL);
if (replace_info->old_tree)
{
init = setup_one_parameter (&id, replace_info->old_tree,
replace_info->new_tree, id.src_fn,
NULL,
&vars);
if (init)
init_stmts.safe_push (init);
if (MAY_HAVE_DEBUG_BIND_STMTS && args_to_skip)
if (dump_file)
{
if (parm_num == -1)
{
tree parm;
int p;
for (parm = DECL_ARGUMENTS (old_decl), p = 0; parm;
parm = DECL_CHAIN (parm), p++)
if (parm == replace_info->old_tree)
{
parm_num = p;
break;
}
}
if (parm_num != -1)
{
if (debug_args_to_skip == args_to_skip)
{
debug_args_to_skip = BITMAP_ALLOC (NULL);
bitmap_copy (debug_args_to_skip, args_to_skip);
}
bitmap_clear_bit (debug_args_to_skip, parm_num);
}
fprintf (dump_file, " const ");
print_generic_expr (dump_file,
replace_info->new_tree);
fprintf (dump_file,
" can't be converted to param ");
print_generic_expr (dump_file, parm);
fprintf (dump_file, "\n");
}
old_tree = NULL;
}
}
if (old_tree)
{
init = setup_one_parameter (&id, old_tree, replace_info->new_tree,
id.src_fn, NULL, &vars);
if (init)
init_stmts.safe_push (init);
}
}
/* Copy the function's arguments. */
if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
ipa_param_body_adjustments *param_body_adjs = NULL;
if (param_adjustments)
{
param_body_adjs = new ipa_param_body_adjustments (param_adjustments,
new_decl, old_decl,
&id, &vars, tree_map);
id.param_body_adjs = param_body_adjs;
DECL_ARGUMENTS (new_decl) = param_body_adjs->get_new_param_chain ();
}
else if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
DECL_ARGUMENTS (new_decl)
= copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id,
args_to_skip, &vars);
= copy_arguments_nochange (DECL_ARGUMENTS (old_decl), &id);
DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id);
BLOCK_SUPERCONTEXT (DECL_INITIAL (new_decl)) = new_decl;
@ -6123,12 +6226,19 @@ tree_function_versioning (tree old_decl, tree new_decl,
if (DECL_RESULT (old_decl) == NULL_TREE)
;
else if (skip_return && !VOID_TYPE_P (TREE_TYPE (DECL_RESULT (old_decl))))
else if (param_adjustments && param_adjustments->m_skip_return
&& !VOID_TYPE_P (TREE_TYPE (DECL_RESULT (old_decl))))
{
tree resdecl_repl = copy_result_decl_to_var (DECL_RESULT (old_decl),
&id);
declare_inline_vars (NULL, resdecl_repl);
insert_decl_map (&id, DECL_RESULT (old_decl), resdecl_repl);
DECL_RESULT (new_decl)
= build_decl (DECL_SOURCE_LOCATION (DECL_RESULT (old_decl)),
RESULT_DECL, NULL_TREE, void_type_node);
DECL_CONTEXT (DECL_RESULT (new_decl)) = new_decl;
DECL_IS_MALLOC (new_decl) = false;
cfun->returns_struct = 0;
cfun->returns_pcc_struct = 0;
}
@ -6221,29 +6331,30 @@ tree_function_versioning (tree old_decl, tree new_decl,
}
}
if (debug_args_to_skip && MAY_HAVE_DEBUG_BIND_STMTS)
if (param_body_adjs && MAY_HAVE_DEBUG_BIND_STMTS)
{
tree parm;
vec<tree, va_gc> **debug_args = NULL;
unsigned int len = 0;
for (parm = DECL_ARGUMENTS (old_decl), i = 0;
parm; parm = DECL_CHAIN (parm), i++)
if (bitmap_bit_p (debug_args_to_skip, i) && is_gimple_reg (parm))
{
tree ddecl;
unsigned reset_len = param_body_adjs->m_reset_debug_decls.length ();
if (debug_args == NULL)
{
debug_args = decl_debug_args_insert (new_decl);
len = vec_safe_length (*debug_args);
}
ddecl = make_node (DEBUG_EXPR_DECL);
DECL_ARTIFICIAL (ddecl) = 1;
TREE_TYPE (ddecl) = TREE_TYPE (parm);
SET_DECL_MODE (ddecl, DECL_MODE (parm));
vec_safe_push (*debug_args, DECL_ORIGIN (parm));
vec_safe_push (*debug_args, ddecl);
}
for (i = 0; i < reset_len; i++)
{
tree parm = param_body_adjs->m_reset_debug_decls[i];
gcc_assert (is_gimple_reg (parm));
tree ddecl;
if (debug_args == NULL)
{
debug_args = decl_debug_args_insert (new_decl);
len = vec_safe_length (*debug_args);
}
ddecl = make_node (DEBUG_EXPR_DECL);
DECL_ARTIFICIAL (ddecl) = 1;
TREE_TYPE (ddecl) = TREE_TYPE (parm);
SET_DECL_MODE (ddecl, DECL_MODE (parm));
vec_safe_push (*debug_args, DECL_ORIGIN (parm));
vec_safe_push (*debug_args, ddecl);
}
if (debug_args != NULL)
{
/* On the callee side, add
@ -6269,7 +6380,7 @@ tree_function_versioning (tree old_decl, tree new_decl,
if (var == NULL_TREE)
break;
vexpr = make_node (DEBUG_EXPR_DECL);
parm = (**debug_args)[i];
tree parm = (**debug_args)[i];
DECL_ARTIFICIAL (vexpr) = 1;
TREE_TYPE (vexpr) = TREE_TYPE (parm);
SET_DECL_MODE (vexpr, DECL_MODE (parm));
@ -6281,9 +6392,7 @@ tree_function_versioning (tree old_decl, tree new_decl,
while (i > len);
}
}
if (debug_args_to_skip && debug_args_to_skip != args_to_skip)
BITMAP_FREE (debug_args_to_skip);
delete param_body_adjs;
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);

View File

@ -172,6 +172,15 @@ struct copy_body_data
outside of the inlined function, this should be the number
of basic blocks in the caller before inlining. Zero otherwise. */
int add_clobbers_to_eh_landing_pads;
/* Class managing changes to function parameters and return value planned
during IPA stage. */
class ipa_param_body_adjustments *param_body_adjs;
/* Hash set of SSA names that have been killed during call graph edge
redirection and should not be introduced into debug statements or NULL if no
SSA_NAME was deleted during redirections happened. */
hash_set <tree> *killed_new_ssa_names;
};
/* Weights of constructions for estimate_num_insns. */
@ -240,6 +249,7 @@ extern bool debug_find_tree (tree, tree);
extern tree copy_fn (tree, tree&, tree&);
extern const char *copy_forbidden (struct function *fun);
extern tree copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy);
extern tree copy_decl_to_var (tree, copy_body_data *);
/* This is in tree-inline.c since the routine uses
data structures from the inliner. */

View File

@ -355,7 +355,6 @@ extern gimple_opt_pass *make_pass_early_tree_profile (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_cleanup_eh (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_sra (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_sra_early (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_early_ipa_sra (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tail_recursion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tail_calls (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_fix_loops (gcc::context *ctxt);
@ -500,6 +499,7 @@ extern ipa_opt_pass_d *make_pass_ipa_inline (gcc::context *ctxt);
extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
extern simple_ipa_opt_pass *make_pass_ipa_free_fn_summary (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);

File diff suppressed because it is too large Load Diff

31
gcc/tree-sra.h Normal file
View File

@ -0,0 +1,31 @@
/* Scalar Replacement of Aggregates (SRA) converts some structure
references into scalar references, exposing them to the scalar
optimizers.
Copyright (C) 2019 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
bool type_internals_preclude_sra_p (tree type, const char **msg);
/* Return true iff TYPE is stdarg va_list type (which early SRA and IPA-SRA
should leave alone). */
static inline bool
is_va_list_type (tree type)
{
return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node);
}