diff options
author | Ian Lance Taylor <iant@golang.org> | 2021-09-13 10:37:49 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2021-09-13 10:37:49 -0700 |
commit | e252b51ccde010cbd2a146485d8045103cd99533 (patch) | |
tree | e060f101cdc32bf5e520de8e5275db9d4236b74c /gcc/ipa-param-manipulation.c | |
parent | f10c7c4596dda99d2ee872c995ae4aeda65adbdf (diff) | |
parent | 104c05c5284b7822d770ee51a7d91946c7e56d50 (diff) | |
download | gcc-e252b51ccde010cbd2a146485d8045103cd99533.zip gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.gz gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.bz2 |
Merge from trunk revision 104c05c5284b7822d770ee51a7d91946c7e56d50.
Diffstat (limited to 'gcc/ipa-param-manipulation.c')
-rw-r--r-- | gcc/ipa-param-manipulation.c | 902 |
1 files changed, 547 insertions, 355 deletions
diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c index 132bb24..26b02d7 100644 --- a/gcc/ipa-param-manipulation.c +++ b/gcc/ipa-param-manipulation.c @@ -62,6 +62,80 @@ static const char *ipa_param_op_names[IPA_PARAM_PREFIX_COUNT] "IPA_PARAM_OP_NEW", "IPA_PARAM_OP_SPLIT"}; +/* Structure to hold declarations representing pass-through IPA-SRA splits. In + essence, it tells new index for a combination of original index and + offset. */ + +struct pass_through_split_map +{ + /* Original argument index. */ + unsigned base_index; + /* Offset of the split part in the original argument. */ + unsigned unit_offset; + /* Index of the split part in the call statement - where clone + materialization put it. */ + int new_index; +}; + +/* Information about some call statements that needs to be conveyed from clone + materialization to edge redirection. */ + +class ipa_edge_modification_info +{ + public: + ipa_edge_modification_info () + {} + + /* Mapping of original argument indices to where those arguments sit in the + call statement now or to a negative index if they were removed. */ + auto_vec<int> index_map; + /* Information about ISRA replacements put into the call statement at the + clone materialization stages. */ + auto_vec<pass_through_split_map> pass_through_map; + /* Necessary adjustment to ipa_param_adjustments::m_always_copy_start when + redirecting the call. */ + int always_copy_delta = 0; +}; + +/* Class for storing and retrieving summaries about cal statement + modifications. */ + +class ipa_edge_modification_sum + : public call_summary <ipa_edge_modification_info *> +{ + public: + ipa_edge_modification_sum (symbol_table *table) + : call_summary<ipa_edge_modification_info *> (table) + { + } + + /* Hook that is called by summary when an edge is duplicated. */ + + virtual void duplicate (cgraph_edge *, + cgraph_edge *, + ipa_edge_modification_info *old_info, + ipa_edge_modification_info *new_info) + { + new_info->index_map.safe_splice (old_info->index_map); + new_info->pass_through_map.safe_splice (old_info->pass_through_map); + new_info->always_copy_delta = old_info->always_copy_delta; + } +}; + +/* Call summary to store information about edges which have had their arguments + partially modified already. */ + +static ipa_edge_modification_sum *ipa_edge_modifications; + +/* Fail compilation if CS has any summary associated with it in + ipa_edge_modifications. */ + +DEBUG_FUNCTION void +ipa_verify_edge_has_no_modifications (cgraph_edge *cs) +{ + gcc_assert (!ipa_edge_modifications || !ipa_edge_modifications->get (cs)); +} + /* Fill an empty vector ARGS with PARM_DECLs representing formal parameters of FNDECL. The function should not be called during LTO WPA phase except for thunks (or functions with bodies streamed in). */ @@ -459,147 +533,46 @@ isra_get_ref_base_and_offset (tree expr, tree *base_p, unsigned *unit_offset_p) return true; } -/* Return true if EXPR describes a transitive split (i.e. one that happened for - both the caller and the callee) as recorded in PERFORMED_SPLITS. In that - case, store index of the respective record in PERFORMED_SPLITS into - *SM_IDX_P and the unit offset from all handled components in EXPR into - *UNIT_OFFSET_P. */ - -static bool -transitive_split_p (vec<ipa_param_performed_split, va_gc> *performed_splits, - tree expr, unsigned *sm_idx_p, unsigned *unit_offset_p) -{ - tree base; - if (!isra_get_ref_base_and_offset (expr, &base, unit_offset_p)) - return false; - - if (TREE_CODE (base) == SSA_NAME) - { - base = SSA_NAME_VAR (base); - if (!base) - return false; - } +/* Modify actual arguments of a function call in statement currently belonging + to CS, and make it call CS->callee->decl. Return the new statement that + replaced the old one. When invoked, cfun and current_function_decl have to + be set to the caller. */ - unsigned len = vec_safe_length (performed_splits); - for (unsigned i = 0 ; i < len; i++) - { - ipa_param_performed_split *sm = &(*performed_splits)[i]; - if (sm->dummy_decl == base) - { - *sm_idx_p = i; - return true; - } - } - return false; -} - -/* Structure to hold declarations representing transitive IPA-SRA splits. In - essence, if we need to pass UNIT_OFFSET of a parameter which originally has - number BASE_INDEX, we should pass down REPL. */ - -struct transitive_split_map +gcall * +ipa_param_adjustments::modify_call (cgraph_edge *cs, + bool update_references) { - tree repl; - unsigned base_index; - unsigned unit_offset; -}; - -/* If call STMT contains any parameters representing transitive splits as - described by PERFORMED_SPLITS, return the number of extra parameters that - were addded during clone materialization and fill in INDEX_MAP with adjusted - indices of corresponding original parameters and TRANS_MAP with description - of all transitive replacement descriptions. Otherwise return zero. */ + gcall *stmt = cs->call_stmt; + tree callee_decl = cs->callee->decl; -static unsigned -init_transitive_splits (vec<ipa_param_performed_split, va_gc> *performed_splits, - gcall *stmt, vec <unsigned> *index_map, - auto_vec <transitive_split_map> *trans_map) -{ - unsigned phony_arguments = 0; - unsigned stmt_idx = 0, base_index = 0; - unsigned nargs = gimple_call_num_args (stmt); - while (stmt_idx < nargs) + ipa_edge_modification_info *mod_info + = ipa_edge_modifications ? ipa_edge_modifications->get (cs) : NULL; + if (mod_info && symtab->dump_file) { - unsigned unit_offset_delta; - tree base_arg = gimple_call_arg (stmt, stmt_idx); - - if (phony_arguments > 0) - index_map->safe_push (stmt_idx); - - unsigned sm_idx; - stmt_idx++; - if (transitive_split_p (performed_splits, base_arg, &sm_idx, - &unit_offset_delta)) - { - if (phony_arguments == 0) - /* We have optimistically avoided constructing index_map do far but - now it is clear it will be necessary, so let's create the easy - bit we skipped until now. */ - for (unsigned k = 0; k < stmt_idx; k++) - index_map->safe_push (k); - - tree dummy = (*performed_splits)[sm_idx].dummy_decl; - for (unsigned j = sm_idx; j < performed_splits->length (); j++) - { - ipa_param_performed_split *caller_split - = &(*performed_splits)[j]; - if (caller_split->dummy_decl != dummy) - break; - - tree arg = gimple_call_arg (stmt, stmt_idx); - struct transitive_split_map tsm; - tsm.repl = arg; - tsm.base_index = base_index; - if (caller_split->unit_offset >= unit_offset_delta) - { - tsm.unit_offset - = (caller_split->unit_offset - unit_offset_delta); - trans_map->safe_push (tsm); - } - - phony_arguments++; - stmt_idx++; - } - } - base_index++; + fprintf (symtab->dump_file, "Information about pre-exiting " + "modifications.\n Index map:"); + unsigned idx_len = mod_info->index_map.length (); + for (unsigned i = 0; i < idx_len; i++) + fprintf (symtab->dump_file, " %i", mod_info->index_map[i]); + fprintf (symtab->dump_file, "\n Pass-through split map: "); + unsigned ptm_len = mod_info->pass_through_map.length (); + for (unsigned i = 0; i < ptm_len; i++) + fprintf (symtab->dump_file, + " (base_index: %u, offset: %u, new_index: %i)", + mod_info->pass_through_map[i].base_index, + mod_info->pass_through_map[i].unit_offset, + mod_info->pass_through_map[i].new_index); + fprintf (symtab->dump_file, "\n Always-copy delta: %i\n", + mod_info->always_copy_delta); } - return phony_arguments; -} - -/* Modify actual arguments of a function call in statement STMT, assuming it - calls CALLEE_DECL. CALLER_ADJ must be the description of parameter - adjustments of the caller or NULL if there are none. Return the new - statement that replaced the old one. When invoked, cfun and - current_function_decl have to be set to the caller. */ -gcall * -ipa_param_adjustments::modify_call (gcall *stmt, - vec<ipa_param_performed_split, - va_gc> *performed_splits, - tree callee_decl, bool update_references) -{ unsigned len = vec_safe_length (m_adj_params); auto_vec<tree, 16> vargs (len); - tree old_decl = gimple_call_fndecl (stmt); unsigned old_nargs = gimple_call_num_args (stmt); + unsigned orig_nargs = mod_info ? mod_info->index_map.length () : old_nargs; auto_vec<bool, 16> kept (old_nargs); kept.quick_grow_cleared (old_nargs); - auto_vec <unsigned, 16> index_map; - auto_vec <transitive_split_map> trans_map; - bool transitive_remapping = false; - - if (performed_splits) - { - unsigned removed = init_transitive_splits (performed_splits, - stmt, &index_map, &trans_map); - if (removed > 0) - { - transitive_remapping = true; - old_nargs -= removed; - } - } - cgraph_node *current_node = cgraph_node::get (current_function_decl); if (update_references) current_node->remove_stmt_references (stmt); @@ -612,13 +585,16 @@ ipa_param_adjustments::modify_call (gcall *stmt, ipa_adjusted_param *apm = &(*m_adj_params)[i]; if (apm->op == IPA_PARAM_OP_COPY) { - unsigned index = apm->base_index; - if (index >= old_nargs) + int index = apm->base_index; + if ((unsigned) index >= orig_nargs) /* Can happen if the original call has argument mismatch, ignore. */ continue; - if (transitive_remapping) - index = index_map[apm->base_index]; + if (mod_info) + { + index = mod_info->index_map[apm->base_index]; + gcc_assert (index >= 0); + } tree arg = gimple_call_arg (stmt, index); @@ -636,14 +612,17 @@ ipa_param_adjustments::modify_call (gcall *stmt, materialization. */ gcc_assert (apm->op == IPA_PARAM_OP_SPLIT); - /* We have to handle transitive changes differently using the maps we - have created before. So look into them first. */ + /* We have to handle pass-through changes differently using the map + clone materialziation might have left behind. */ tree repl = NULL_TREE; - for (unsigned j = 0; j < trans_map.length (); j++) - if (trans_map[j].base_index == apm->base_index - && trans_map[j].unit_offset == apm->unit_offset) + unsigned ptm_len = mod_info ? mod_info->pass_through_map.length () : 0; + for (unsigned j = 0; j < ptm_len; j++) + if (mod_info->pass_through_map[j].base_index == apm->base_index + && mod_info->pass_through_map[j].unit_offset == apm->unit_offset) { - repl = trans_map[j].repl; + int repl_idx = mod_info->pass_through_map[j].new_index; + gcc_assert (repl_idx >= 0); + repl = gimple_call_arg (stmt, repl_idx); break; } if (repl) @@ -652,12 +631,15 @@ ipa_param_adjustments::modify_call (gcall *stmt, continue; } - unsigned index = apm->base_index; - if (index >= old_nargs) + int index = apm->base_index; + if ((unsigned) index >= orig_nargs) /* Can happen if the original call has argument mismatch, ignore. */ continue; - if (transitive_remapping) - index = index_map[apm->base_index]; + if (mod_info) + { + index = mod_info->index_map[apm->base_index]; + gcc_assert (index >= 0); + } tree base = gimple_call_arg (stmt, index); /* We create a new parameter out of the value of the old one, we can @@ -773,8 +755,16 @@ ipa_param_adjustments::modify_call (gcall *stmt, } if (m_always_copy_start >= 0) - for (unsigned i = m_always_copy_start; i < old_nargs; i++) - vargs.safe_push (gimple_call_arg (stmt, i)); + { + int always_copy_start = m_always_copy_start; + if (mod_info) + { + always_copy_start += mod_info->always_copy_delta; + gcc_assert (always_copy_start >= 0); + } + for (unsigned i = always_copy_start; i < old_nargs; i++) + vargs.safe_push (gimple_call_arg (stmt, i)); + } /* For optimized away parameters, add on the caller side before the call @@ -782,6 +772,7 @@ ipa_param_adjustments::modify_call (gcall *stmt, 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). */ + tree old_decl = gimple_call_fndecl (stmt); if (MAY_HAVE_DEBUG_BIND_STMTS && old_decl && callee_decl) { vec<tree, va_gc> **debug_args = NULL; @@ -799,13 +790,17 @@ ipa_param_adjustments::modify_call (gcall *stmt, { if (!is_gimple_reg (old_parm) || kept[i]) continue; - tree origin = DECL_ORIGIN (old_parm); tree arg; - if (transitive_remapping) - arg = gimple_call_arg (stmt, index_map[i]); + if (mod_info) + { + if (mod_info->index_map[i] < 0) + continue; + arg = gimple_call_arg (stmt, mod_info->index_map[i]); + } else arg = gimple_call_arg (stmt, i); + tree origin = DECL_ORIGIN (old_parm); if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg))) { if (!fold_convertible_p (TREE_TYPE (origin), arg)) @@ -856,6 +851,7 @@ ipa_param_adjustments::modify_call (gcall *stmt, gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs); + tree ssa_to_remove = NULL; if (tree lhs = gimple_call_lhs (stmt)) { if (!m_skip_return) @@ -880,6 +876,7 @@ ipa_param_adjustments::modify_call (gcall *stmt, } update_stmt (using_stmt); } + ssa_to_remove = lhs; } } @@ -898,6 +895,8 @@ ipa_param_adjustments::modify_call (gcall *stmt, fprintf (dump_file, "\n"); } gsi_replace (&gsi, new_stmt, true); + if (ssa_to_remove) + release_ssa_name (ssa_to_remove); if (update_references) do { @@ -905,6 +904,9 @@ ipa_param_adjustments::modify_call (gcall *stmt, gsi_prev (&gsi); } while (gsi_stmt (gsi) != gsi_stmt (prev_gsi)); + + if (mod_info) + ipa_edge_modifications->remove (cs); return new_stmt; } @@ -927,13 +929,11 @@ ipa_param_adjustments::debug () dump (stderr); } -/* Register that REPLACEMENT should replace parameter described in APM and - optionally as DUMMY to mark transitive splits across calls. */ +/* Register that REPLACEMENT should replace parameter described in APM. */ void ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm, - tree replacement, - tree dummy) + tree replacement) { gcc_checking_assert (apm->op == IPA_PARAM_OP_SPLIT || apm->op == IPA_PARAM_OP_NEW); @@ -941,7 +941,7 @@ ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm, ipa_param_body_replacement psr; psr.base = m_oparms[apm->prev_clone_index]; psr.repl = replacement; - psr.dummy = dummy; + psr.dummy = NULL_TREE; psr.unit_offset = apm->unit_offset; m_replacements.safe_push (psr); } @@ -970,6 +970,84 @@ ipa_param_body_adjustments::carry_over_param (tree t) return new_parm; } +/* Populate m_dead_stmts given that DEAD_PARAM is going to be removed without + any replacement or splitting. REPL is the replacement VAR_SECL to base any + remaining uses of a removed parameter on. */ + +void +ipa_param_body_adjustments::mark_dead_statements (tree dead_param) +{ + /* Current IPA analyses which remove unused parameters never remove a + non-gimple register ones which have any use except as parameters in other + calls, so we can safely leve them as they are. */ + if (!is_gimple_reg (dead_param)) + return; + tree parm_ddef = ssa_default_def (m_id->src_cfun, dead_param); + if (!parm_ddef || has_zero_uses (parm_ddef)) + return; + + auto_vec<tree, 4> stack; + m_dead_ssas.add (parm_ddef); + stack.safe_push (parm_ddef); + while (!stack.is_empty ()) + { + imm_use_iterator imm_iter; + use_operand_p use_p; + tree t = stack.pop (); + + insert_decl_map (m_id, t, error_mark_node); + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, t) + { + gimple *stmt = USE_STMT (use_p); + + /* Calls containing dead arguments cannot be deleted, + modify_call_stmt will instead remove just the argument later on. + If isra_track_scalar_value_uses in ipa-sra.c is extended to look + through const functions, we will need to do so here too. */ + if (is_gimple_call (stmt) + || (m_id->blocks_to_copy + && !bitmap_bit_p (m_id->blocks_to_copy, + gimple_bb (stmt)->index))) + continue; + + if (is_gimple_debug (stmt)) + { + m_dead_stmts.add (stmt); + gcc_assert (gimple_debug_bind_p (stmt)); + } + else if (gimple_code (stmt) == GIMPLE_PHI) + { + gphi *phi = as_a <gphi *> (stmt); + int ix = PHI_ARG_INDEX_FROM_USE (use_p); + + if (!m_id->blocks_to_copy + || bitmap_bit_p (m_id->blocks_to_copy, + gimple_phi_arg_edge (phi, ix)->src->index)) + { + m_dead_stmts.add (phi); + tree res = gimple_phi_result (phi); + if (!m_dead_ssas.add (res)) + stack.safe_push (res); + } + } + else if (is_gimple_assign (stmt)) + { + m_dead_stmts.add (stmt); + if (!gimple_clobber_p (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + gcc_assert (TREE_CODE (lhs) == SSA_NAME); + if (!m_dead_ssas.add (lhs)) + stack.safe_push (lhs); + } + } + else + /* IPA-SRA does not analyze other types of statements. */ + gcc_unreachable (); + } + } +} + /* Common initialization performed by all ipa_param_body_adjustments constructors. OLD_FNDECL is the declaration we take original arguments from, (it may be the same as M_FNDECL). VARS, if non-NULL, is a pointer to @@ -1003,9 +1081,9 @@ ipa_param_body_adjustments::common_initialization (tree old_fndecl, auto_vec<bool, 16> kept; kept.reserve_exact (m_oparms.length ()); kept.quick_grow_cleared (m_oparms.length ()); - auto_vec<tree, 16> isra_dummy_decls; - isra_dummy_decls.reserve_exact (m_oparms.length ()); - isra_dummy_decls.quick_grow_cleared (m_oparms.length ()); + auto_vec<bool, 16> split; + split.reserve_exact (m_oparms.length ()); + split.quick_grow_cleared (m_oparms.length ()); unsigned adj_len = vec_safe_length (m_adj_params); m_method2func = ((TREE_CODE (TREE_TYPE (m_fndecl)) == METHOD_TYPE) @@ -1051,35 +1129,8 @@ ipa_param_body_adjustments::common_initialization (tree old_fndecl, if (apm->op == IPA_PARAM_OP_SPLIT) { m_split_modifications_p = true; - - if (m_id) - { - tree dummy_decl; - if (!isra_dummy_decls[prev_index]) - { - dummy_decl = copy_decl_to_var (m_oparms[prev_index], - m_id); - /* Any attempt to remap this dummy in this particular - instance of clone materialization should yield - itself. */ - insert_decl_map (m_id, dummy_decl, dummy_decl); - - DECL_CHAIN (dummy_decl) = *vars; - *vars = dummy_decl; - isra_dummy_decls[prev_index] = dummy_decl; - } - else - dummy_decl = isra_dummy_decls[prev_index]; - - register_replacement (apm, new_parm, dummy_decl); - ipa_param_performed_split ps; - ps.dummy_decl = dummy_decl; - ps.unit_offset = apm->unit_offset; - vec_safe_push (clone_info::get_create - (m_id->dst_node)->performed_splits, ps); - } - else - register_replacement (apm, new_parm); + split[prev_index] = true; + register_replacement (apm, new_parm); } } else @@ -1106,13 +1157,16 @@ ipa_param_body_adjustments::common_initialization (tree old_fndecl, { if (!m_id->decl_map->get (m_oparms[i])) { - /* TODO: Perhaps at least aggregate-type params could re-use - their isra_dummy_decl here? */ tree var = copy_decl_to_var (m_oparms[i], m_id); insert_decl_map (m_id, m_oparms[i], var); /* Declare this new variable. */ DECL_CHAIN (var) = *vars; *vars = var; + + /* If this is not a split but a real removal, init hash sets + that will guide what not to copy to the new body. */ + if (!split[i]) + mark_dead_statements (m_oparms[i]); } } else @@ -1169,9 +1223,10 @@ ipa_param_body_adjustments ::ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params, tree fndecl) : m_adj_params (adj_params), m_adjustments (NULL), m_reset_debug_decls (), - m_split_modifications_p (false), m_fndecl (fndecl), m_id (NULL), - m_oparms (), m_new_decls (), m_new_types (), m_replacements (), - m_removed_decls (), m_removed_map (), m_method2func (false) + m_split_modifications_p (false), m_dead_stmts (), m_dead_ssas (), + m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls (), + m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (), + m_method2func (false) { common_initialization (fndecl, NULL, NULL); } @@ -1185,9 +1240,9 @@ ipa_param_body_adjustments ::ipa_param_body_adjustments (ipa_param_adjustments *adjustments, tree fndecl) : m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments), - m_reset_debug_decls (), m_split_modifications_p (false), m_fndecl (fndecl), - m_id (NULL), m_oparms (), m_new_decls (), m_new_types (), - m_replacements (), m_removed_decls (), m_removed_map (), + m_reset_debug_decls (), m_split_modifications_p (false), m_dead_stmts (), + m_dead_ssas (), m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls (), + m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (), m_method2func (false) { common_initialization (fndecl, NULL, NULL); @@ -1208,9 +1263,10 @@ ipa_param_body_adjustments copy_body_data *id, tree *vars, vec<ipa_replace_map *, va_gc> *tree_map) : m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments), - m_reset_debug_decls (), m_split_modifications_p (false), m_fndecl (fndecl), - m_id (id), m_oparms (), m_new_decls (), m_new_types (), m_replacements (), - m_removed_decls (), m_removed_map (), m_method2func (false) + m_reset_debug_decls (), m_split_modifications_p (false), m_dead_stmts (), + m_dead_ssas (),m_fndecl (fndecl), m_id (id), m_oparms (), m_new_decls (), + m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (), + m_method2func (false) { common_initialization (old_fndecl, vars, tree_map); } @@ -1498,218 +1554,339 @@ ipa_param_body_adjustments::modify_assignment (gimple *stmt, return any; } -/* Data passed to remap_split_decl_to_dummy through walk_tree. */ - -struct simple_tree_swap_info -{ - /* Change FROM to TO. */ - tree from, to; - /* And set DONE to true when doing so. */ - bool done; -}; +/* Record information about what modifications to call arguments have already + been done by clone materialization into a summary describing CS. The + information is stored in NEW_INDEX_MAP, NEW_PT_MAP and NEW_ALWAYS_COPY_DELTA + and correspond to equivalent fields in ipa_edge_modification_info. Return + the edge summary. */ -/* Simple remapper to remap a split parameter to the same expression based on a - special dummy decl so that edge redirections can detect transitive splitting - and finish them. */ +static ipa_edge_modification_info * +record_argument_state_1 (cgraph_edge *cs, const vec<int> &new_index_map, + const vec<pass_through_split_map> &new_pt_map, + int new_always_copy_delta) -static tree -remap_split_decl_to_dummy (tree *tp, int *walk_subtrees, void *data) { - tree t = *tp; + ipa_edge_modification_info *sum = ipa_edge_modifications->get_create (cs); - if (DECL_P (t) || TREE_CODE (t) == SSA_NAME) + unsigned len = sum->pass_through_map.length (); + for (unsigned i = 0; i < len; i++) { - struct simple_tree_swap_info *swapinfo - = (struct simple_tree_swap_info *) data; - if (t == swapinfo->from - || (TREE_CODE (t) == SSA_NAME - && SSA_NAME_VAR (t) == swapinfo->from)) + unsigned oldnew = sum->pass_through_map[i].new_index; + sum->pass_through_map[i].new_index = new_index_map[oldnew]; + } + + len = sum->index_map.length (); + if (len > 0) + { + unsigned nptlen = new_pt_map.length (); + for (unsigned j = 0; j < nptlen; j++) { - *tp = swapinfo->to; - swapinfo->done = true; + int inverse = -1; + for (unsigned i = 0; i < len ; i++) + if ((unsigned) sum->index_map[i] == new_pt_map[j].base_index) + { + inverse = i; + break; + } + gcc_assert (inverse >= 0); + pass_through_split_map ptm_item; + + ptm_item.base_index = inverse; + ptm_item.unit_offset = new_pt_map[j].unit_offset; + ptm_item.new_index = new_pt_map[j].new_index; + sum->pass_through_map.safe_push (ptm_item); + } + + for (unsigned i = 0; i < len; i++) + { + int idx = sum->index_map[i]; + if (idx < 0) + continue; + sum->index_map[i] = new_index_map[idx]; } - *walk_subtrees = 0; } - else if (TYPE_P (t)) - *walk_subtrees = 0; else - *walk_subtrees = 1; - return NULL_TREE; + { + sum->pass_through_map.safe_splice (new_pt_map); + sum->index_map.safe_splice (new_index_map); + } + sum->always_copy_delta += new_always_copy_delta; + return sum; } +/* Record information about what modifications to call arguments have already + been done by clone materialization into a summary of an edge describing the + call in this clone and all its clones. NEW_INDEX_MAP, NEW_PT_MAP and + NEW_ALWAYS_COPY_DELTA have the same meaning as record_argument_state_1. + + In order to associate the info with the right edge summaries, we need + address of the ORIG_STMT in the function from which we are cloning (because + the edges have not yet been re-assigned to the new statement that has just + been created) and ID, the structure governing function body copying. */ + +static void +record_argument_state (copy_body_data *id, gimple *orig_stmt, + const vec<int> &new_index_map, + const vec<pass_through_split_map> &new_pt_map, + int new_always_copy_delta) +{ + if (!ipa_edge_modifications) + ipa_edge_modifications = new ipa_edge_modification_sum (symtab); + + struct cgraph_node *this_node = id->dst_node; + ipa_edge_modification_info *first_sum = NULL; + cgraph_edge *cs = this_node->get_edge (orig_stmt); + if (cs) + first_sum = record_argument_state_1 (cs, new_index_map, new_pt_map, + new_always_copy_delta); + else + gcc_assert (this_node->clones); + + if (!this_node->clones) + return; + for (cgraph_node *subclone = this_node->clones; subclone != this_node;) + { + cs = subclone->get_edge (orig_stmt); + if (cs) + { + if (!first_sum) + first_sum = record_argument_state_1 (cs, new_index_map, new_pt_map, + new_always_copy_delta); + else + { + ipa_edge_modification_info *s2 + = ipa_edge_modifications->get_create (cs); + s2->index_map.truncate (0); + s2->index_map.safe_splice (first_sum->index_map); + s2->pass_through_map.truncate (0); + s2->pass_through_map.safe_splice (first_sum->pass_through_map); + s2->always_copy_delta = first_sum->always_copy_delta; + } + } + else + gcc_assert (subclone->clones); + + if (subclone->clones) + subclone = subclone->clones; + else if (subclone->next_sibling_clone) + subclone = subclone->next_sibling_clone; + else + { + while (subclone != this_node && !subclone->next_sibling_clone) + subclone = subclone->clone_of; + if (subclone != this_node) + subclone = subclone->next_sibling_clone; + } + } +} /* If the call statement pointed at by STMT_P contains any expressions that need to replaced with a different one as noted by ADJUSTMENTS, do so. f the statement needs to be rebuilt, do so. Return true if any modifications have - been performed. + been performed. ORIG_STMT, if not NULL, is the original statement in the + function that is being cloned from, which at this point can be used to look + up call_graph edges. If the method is invoked as a part of IPA clone materialization and if any - parameter split is transitive, i.e. it applies to the functin that is being - modified and also to the callee of the statement, replace the parameter - passed to old callee with an equivalent expression based on a dummy decl - followed by PARM_DECLs representing the actual replacements. The actual - replacements will be then converted into SSA_NAMEs and then - ipa_param_adjustments::modify_call will find the appropriate ones and leave - only those in the call. */ + parameter split is pass-through, i.e. it applies to the functin that is + being modified and also to the callee of the statement, replace the + parameter passed to old callee with all of the replacement a callee might + possibly want and record the performed argument modifications in + ipa_edge_modifications. Likewise if any argument has already been left out + because it is not necessary. */ bool -ipa_param_body_adjustments::modify_call_stmt (gcall **stmt_p) +ipa_param_body_adjustments::modify_call_stmt (gcall **stmt_p, + gimple *orig_stmt) { - gcall *stmt = *stmt_p; auto_vec <unsigned, 4> pass_through_args; auto_vec <unsigned, 4> pass_through_pbr_indices; + auto_vec <HOST_WIDE_INT, 4> pass_through_offsets; + gcall *stmt = *stmt_p; + unsigned nargs = gimple_call_num_args (stmt); + bool recreate = false; - if (m_split_modifications_p && m_id) + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) { - for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) - { - tree t = gimple_call_arg (stmt, i); - gcc_assert (TREE_CODE (t) != BIT_FIELD_REF - && TREE_CODE (t) != IMAGPART_EXPR - && TREE_CODE (t) != REALPART_EXPR); + tree t = gimple_call_arg (stmt, i); + gcc_assert (TREE_CODE (t) != BIT_FIELD_REF + && TREE_CODE (t) != IMAGPART_EXPR + && TREE_CODE (t) != REALPART_EXPR); - tree base; - unsigned unit_offset; - if (!isra_get_ref_base_and_offset (t, &base, &unit_offset)) - continue; + if (TREE_CODE (t) == SSA_NAME + && m_dead_ssas.contains (t)) + recreate = true; - bool by_ref = false; - if (TREE_CODE (base) == SSA_NAME) - { - if (!SSA_NAME_IS_DEFAULT_DEF (base)) - continue; - base = SSA_NAME_VAR (base); - gcc_checking_assert (base); - by_ref = true; - } - if (TREE_CODE (base) != PARM_DECL) + if (!m_split_modifications_p) + continue; + + tree base; + unsigned agg_arg_offset; + if (!isra_get_ref_base_and_offset (t, &base, &agg_arg_offset)) + continue; + + bool by_ref = false; + if (TREE_CODE (base) == SSA_NAME) + { + if (!SSA_NAME_IS_DEFAULT_DEF (base)) continue; + base = SSA_NAME_VAR (base); + gcc_checking_assert (base); + by_ref = true; + } + if (TREE_CODE (base) != PARM_DECL) + continue; - bool base_among_replacements = false; - unsigned j, repl_list_len = m_replacements.length (); - for (j = 0; j < repl_list_len; j++) + bool base_among_replacements = false; + unsigned j, repl_list_len = m_replacements.length (); + for (j = 0; j < repl_list_len; j++) + { + ipa_param_body_replacement *pbr = &m_replacements[j]; + if (pbr->base == base) { - ipa_param_body_replacement *pbr = &m_replacements[j]; - if (pbr->base == base) - { - base_among_replacements = true; - break; - } + base_among_replacements = true; + break; } - if (!base_among_replacements) - continue; + } + if (!base_among_replacements) + continue; - /* We still have to distinguish between an end-use that we have to - transform now and a pass-through, which happens in the following - two cases. */ + /* We still have to distinguish between an end-use that we have to + transform now and a pass-through, which happens in the following + two cases. */ - /* TODO: After we adjust ptr_parm_has_nonarg_uses to also consider - &MEM_REF[ssa_name + offset], we will also have to detect that case - here. */ + /* TODO: After we adjust ptr_parm_has_nonarg_uses to also consider + &MEM_REF[ssa_name + offset], we will also have to detect that case + here. */ - if (TREE_CODE (t) == SSA_NAME - && SSA_NAME_IS_DEFAULT_DEF (t) - && SSA_NAME_VAR (t) - && TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL) + if (TREE_CODE (t) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (t) + && SSA_NAME_VAR (t) + && TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL) + { + /* This must be a by_reference pass-through. */ + recreate = true; + gcc_assert (POINTER_TYPE_P (TREE_TYPE (t))); + pass_through_args.safe_push (i); + pass_through_pbr_indices.safe_push (j); + pass_through_offsets.safe_push (agg_arg_offset); + } + else if (!by_ref && AGGREGATE_TYPE_P (TREE_TYPE (t))) + { + /* Currently IPA-SRA guarantees the aggregate access type + exactly matches in this case. So if it does not match, it is + a pass-through argument that will be sorted out at edge + redirection time. */ + ipa_param_body_replacement *pbr + = lookup_replacement_1 (base, agg_arg_offset); + + if (!pbr + || (TYPE_MAIN_VARIANT (TREE_TYPE (t)) + != TYPE_MAIN_VARIANT (TREE_TYPE (pbr->repl)))) { - /* This must be a by_reference pass-through. */ - gcc_assert (POINTER_TYPE_P (TREE_TYPE (t))); + recreate = true; pass_through_args.safe_push (i); pass_through_pbr_indices.safe_push (j); - } - else if (!by_ref && AGGREGATE_TYPE_P (TREE_TYPE (t))) - { - /* Currently IPA-SRA guarantees the aggregate access type - exactly matches in this case. So if it does not match, it is - a pass-through argument that will be sorted out at edge - redirection time. */ - ipa_param_body_replacement *pbr - = lookup_replacement_1 (base, unit_offset); - - if (!pbr - || (TYPE_MAIN_VARIANT (TREE_TYPE (t)) - != TYPE_MAIN_VARIANT (TREE_TYPE (pbr->repl)))) - { - pass_through_args.safe_push (i); - pass_through_pbr_indices.safe_push (j); - } + pass_through_offsets.safe_push (agg_arg_offset); } } } - unsigned nargs = gimple_call_num_args (stmt); - if (!pass_through_args.is_empty ()) + if (!recreate) { - auto_vec<tree, 16> vargs; - unsigned pt_idx = 0; + /* No need to rebuild the statement, let's just modify arguments + and the LHS if/as appropriate. */ + bool modified = false; for (unsigned i = 0; i < nargs; i++) { - if (pt_idx < pass_through_args.length () - && i == pass_through_args[pt_idx]) + tree *t = gimple_call_arg_ptr (stmt, i); + modified |= modify_expression (t, true); + } + if (gimple_call_lhs (stmt)) + { + tree *t = gimple_call_lhs_ptr (stmt); + modified |= modify_expression (t, false); + } + return modified; + } + + auto_vec<int, 16> index_map; + auto_vec<pass_through_split_map, 4> pass_through_map; + auto_vec<tree, 16> vargs; + int always_copy_delta = 0; + unsigned pt_idx = 0; + int new_arg_idx = 0; + for (unsigned i = 0; i < nargs; i++) + { + if (pt_idx < pass_through_args.length () + && i == pass_through_args[pt_idx]) + { + unsigned j = pass_through_pbr_indices[pt_idx]; + unsigned agg_arg_offset = pass_through_offsets[pt_idx]; + pt_idx++; + always_copy_delta--; + tree base = m_replacements[j].base; + + /* In order to be put into SSA form, we have to push all replacements + pertaining to this parameter as parameters to the call statement. + Edge redirection will need to use edge summary to weed out the + unnecessary ones. */ + unsigned repl_list_len = m_replacements.length (); + for (; j < repl_list_len; j++) { - unsigned j = pass_through_pbr_indices[pt_idx]; - pt_idx++; - tree base = m_replacements[j].base; - - /* Map base will get mapped to the special transitive-isra marker - dummy decl. */ - struct simple_tree_swap_info swapinfo; - swapinfo.from = base; - swapinfo.to = m_replacements[j].dummy; - swapinfo.done = false; - tree arg = gimple_call_arg (stmt, i); - walk_tree (&arg, remap_split_decl_to_dummy, &swapinfo, NULL); - gcc_assert (swapinfo.done); - vargs.safe_push (arg); - /* Now let's push all replacements pertaining to this parameter - so that all gimple register ones get correct SSA_NAMES. Edge - redirection will weed out the dummy argument as well as all - unused replacements later. */ - unsigned int repl_list_len = m_replacements.length (); - for (; j < repl_list_len; j++) - { - if (m_replacements[j].base != base) - break; - vargs.safe_push (m_replacements[j].repl); - } + if (m_replacements[j].base != base) + break; + if (m_replacements[j].unit_offset < agg_arg_offset) + continue; + pass_through_split_map pt_map; + pt_map.base_index = i; + pt_map.unit_offset + = m_replacements[j].unit_offset - agg_arg_offset; + pt_map.new_index = new_arg_idx; + pass_through_map.safe_push (pt_map); + vargs.safe_push (m_replacements[j].repl); + new_arg_idx++; + always_copy_delta++; + } + index_map.safe_push (-1); + } + else + { + tree t = gimple_call_arg (stmt, i); + if (TREE_CODE (t) == SSA_NAME + && m_dead_ssas.contains (t)) + { + always_copy_delta--; + index_map.safe_push (-1); } else { - tree t = gimple_call_arg (stmt, i); modify_expression (&t, true); vargs.safe_push (t); + index_map.safe_push (new_arg_idx); + new_arg_idx++; } } - gcall *new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs); - if (gimple_has_location (stmt)) - gimple_set_location (new_stmt, gimple_location (stmt)); - gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); - gimple_call_copy_flags (new_stmt, stmt); - if (tree lhs = gimple_call_lhs (stmt)) - { - modify_expression (&lhs, false); - gimple_call_set_lhs (new_stmt, lhs); - } - *stmt_p = new_stmt; - return true; } - /* Otherwise, no need to rebuild the statement, let's just modify arguments - and the LHS if/as appropriate. */ - bool modified = false; - for (unsigned i = 0; i < nargs; i++) - { - tree *t = gimple_call_arg_ptr (stmt, i); - modified |= modify_expression (t, true); - } - - if (gimple_call_lhs (stmt)) + gcall *new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs); + if (gimple_has_location (stmt)) + gimple_set_location (new_stmt, gimple_location (stmt)); + gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); + gimple_call_copy_flags (new_stmt, stmt); + if (tree lhs = gimple_call_lhs (stmt)) { - tree *t = gimple_call_lhs_ptr (stmt); - modified |= modify_expression (t, false); + modify_expression (&lhs, false); + /* Avoid adjusting SSA_NAME_DEF_STMT of a SSA lhs, SSA names + have not yet been remapped. */ + *gimple_call_lhs_ptr (new_stmt) = lhs; } + *stmt_p = new_stmt; - return modified; + if (orig_stmt) + record_argument_state (m_id, orig_stmt, index_map, pass_through_map, + always_copy_delta); + return true; } /* If the statement STMT contains any expressions that need to replaced with a @@ -1720,7 +1897,8 @@ ipa_param_body_adjustments::modify_call_stmt (gcall **stmt_p) bool ipa_param_body_adjustments::modify_gimple_stmt (gimple **stmt, - gimple_seq *extra_stmts) + gimple_seq *extra_stmts, + gimple *orig_stmt) { bool modified = false; tree *t; @@ -1740,7 +1918,7 @@ ipa_param_body_adjustments::modify_gimple_stmt (gimple **stmt, break; case GIMPLE_CALL: - modified |= modify_call_stmt ((gcall **) stmt); + modified |= modify_call_stmt ((gcall **) stmt, orig_stmt); break; case GIMPLE_ASM: @@ -1797,7 +1975,7 @@ ipa_param_body_adjustments::modify_cfun_body () gimple *stmt = gsi_stmt (gsi); gimple *stmt_copy = stmt; gimple_seq extra_stmts = NULL; - bool modified = modify_gimple_stmt (&stmt, &extra_stmts); + bool modified = modify_gimple_stmt (&stmt, &extra_stmts, NULL); if (stmt != stmt_copy) { gcc_checking_assert (modified); @@ -1946,3 +2124,17 @@ ipa_param_body_adjustments::perform_cfun_body_modifications () return cfg_changed; } + +/* Deallocate summaries which otherwise stay alive until the end of + compilation. */ + +void +ipa_edge_modifications_finalize () +{ + if (!ipa_edge_modifications) + return; + delete ipa_edge_modifications; + ipa_edge_modifications = NULL; +} + + |