aboutsummaryrefslogtreecommitdiff
path: root/gcc/ipa-param-manipulation.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ipa-param-manipulation.c')
-rw-r--r--gcc/ipa-param-manipulation.c2093
1 files changed, 1620 insertions, 473 deletions
diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c
index 1af6d05..7f52e9c 100644
--- a/gcc/ipa-param-manipulation.c
+++ b/gcc/ipa-param-manipulation.c
@@ -22,170 +22,201 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "backend.h"
-#include "rtl.h"
#include "tree.h"
#include "gimple.h"
#include "ssa.h"
#include "cgraph.h"
#include "fold-const.h"
+#include "tree-eh.h"
#include "stor-layout.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "gimplify-me.h"
+#include "tree-cfg.h"
#include "tree-dfa.h"
#include "ipa-param-manipulation.h"
#include "print-tree.h"
#include "gimple-pretty-print.h"
#include "builtins.h"
+#include "tree-ssa.h"
+#include "tree-inline.h"
-/* Return a heap allocated vector containing formal parameters of FNDECL. */
-vec<tree>
-ipa_get_vector_of_formal_parms (tree fndecl)
+/* Actual prefixes of different newly synthetized parameters. Keep in sync
+ with IPA_PARAM_PREFIX_* defines. */
+
+static const char *ipa_param_prefixes[IPA_PARAM_PREFIX_COUNT]
+ = {"SYNTH",
+ "ISRA",
+ "simd",
+ "mask"};
+
+/* Names of parameters for dumping. Keep in sync with enum ipa_parm_op. */
+
+static const char *ipa_param_op_names[IPA_PARAM_PREFIX_COUNT]
+ = {"IPA_PARAM_OP_UNDEFINED",
+ "IPA_PARAM_OP_COPY",
+ "IPA_PARAM_OP_NEW",
+ "IPA_PARAM_OP_SPLIT"};
+
+/* 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). */
+
+void
+push_function_arg_decls (vec<tree> *args, tree fndecl)
{
- vec<tree> args;
int count;
tree parm;
- gcc_assert (!flag_wpa);
+ /* Safety check that we do not attempt to use the function in WPA, except
+ when the function is a thunk and then we have DECL_ARGUMENTS or when we
+ have already explicitely loaded its body. */
+ gcc_assert (!flag_wpa
+ || DECL_ARGUMENTS (fndecl)
+ || gimple_has_body_p (fndecl));
count = 0;
for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm))
count++;
- args.create (count);
+ args->reserve_exact (count);
for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm))
- args.quick_push (parm);
-
- return args;
+ args->quick_push (parm);
}
-/* Return a heap allocated vector containing types of formal parameters of
+/* Fill an empty vector TYPES with trees representing formal parameters of
function type FNTYPE. */
-vec<tree>
-ipa_get_vector_of_formal_parm_types (tree fntype)
+void
+push_function_arg_types (vec<tree> *types, tree fntype)
{
- vec<tree> types;
int count = 0;
tree t;
for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t))
count++;
- types.create (count);
+ types->reserve_exact (count);
for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t))
- types.quick_push (TREE_VALUE (t));
-
- return types;
+ types->quick_push (TREE_VALUE (t));
}
-/* Modify the function declaration FNDECL and its type according to the plan in
- ADJUSTMENTS. It also sets base fields of individual adjustments structures
- to reflect the actual parameters being modified which are determined by the
- base_index field. */
+/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human
+ friendly way, assuming they are meant to be applied to FNDECL. */
void
-ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments)
-{
- vec<tree> oparms = ipa_get_vector_of_formal_parms (fndecl);
- tree orig_type = TREE_TYPE (fndecl);
- tree old_arg_types = TYPE_ARG_TYPES (orig_type);
-
- /* The following test is an ugly hack, some functions simply don't have any
- arguments in their type. This is probably a bug but well... */
- bool care_for_types = (old_arg_types != NULL_TREE);
- bool last_parm_void;
- vec<tree> otypes;
- if (care_for_types)
- {
- last_parm_void = (TREE_VALUE (tree_last (old_arg_types))
- == void_type_node);
- otypes = ipa_get_vector_of_formal_parm_types (orig_type);
- if (last_parm_void)
- gcc_assert (oparms.length () + 1 == otypes.length ());
- else
- gcc_assert (oparms.length () == otypes.length ());
- }
- else
- {
- last_parm_void = false;
- otypes.create (0);
- }
+ipa_dump_adjusted_parameters (FILE *f,
+ vec<ipa_adjusted_param, va_gc> *adj_params)
+{
+ unsigned i, len = vec_safe_length (adj_params);
+ bool first = true;
- int len = adjustments.length ();
- tree *link = &DECL_ARGUMENTS (fndecl);
- tree new_arg_types = NULL;
- for (int i = 0; i < len; i++)
+ fprintf (f, " IPA adjusted parameters: ");
+ for (i = 0; i < len; i++)
{
- struct ipa_parm_adjustment *adj;
- gcc_assert (link);
+ struct ipa_adjusted_param *apm;
+ apm = &(*adj_params)[i];
- adj = &adjustments[i];
- tree parm;
- if (adj->op == IPA_PARM_OP_NEW)
- parm = NULL;
+ if (!first)
+ fprintf (f, " ");
else
- parm = oparms[adj->base_index];
- adj->base = parm;
+ first = false;
- if (adj->op == IPA_PARM_OP_COPY)
- {
- if (care_for_types)
- new_arg_types = tree_cons (NULL_TREE, otypes[adj->base_index],
- new_arg_types);
- *link = parm;
- link = &DECL_CHAIN (parm);
- }
- else if (adj->op != IPA_PARM_OP_REMOVE)
+ fprintf (f, "%i. %s %s", i, ipa_param_op_names[apm->op],
+ apm->prev_clone_adjustment ? "prev_clone_adjustment " : "");
+ switch (apm->op)
{
- tree new_parm;
- tree ptype;
-
- if (adj->by_ref)
- ptype = build_pointer_type (adj->type);
- else
- {
- ptype = adj->type;
- if (is_gimple_reg_type (ptype)
- && TYPE_MODE (ptype) != BLKmode)
- {
- unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ptype));
- if (TYPE_ALIGN (ptype) != malign)
- ptype = build_aligned_type (ptype, malign);
- }
- }
+ case IPA_PARAM_OP_UNDEFINED:
+ break;
- if (care_for_types)
- new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types);
+ case IPA_PARAM_OP_COPY:
+ fprintf (f, ", base_index: %u", apm->base_index);
+ fprintf (f, ", prev_clone_index: %u", apm->prev_clone_index);
+ break;
- new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE,
- ptype);
- const char *prefix = adj->arg_prefix ? adj->arg_prefix : "SYNTH";
- DECL_NAME (new_parm) = create_tmp_var_name (prefix);
- DECL_ARTIFICIAL (new_parm) = 1;
- DECL_ARG_TYPE (new_parm) = ptype;
- DECL_CONTEXT (new_parm) = fndecl;
- TREE_USED (new_parm) = 1;
- DECL_IGNORED_P (new_parm) = 1;
- layout_decl (new_parm, 0);
+ case IPA_PARAM_OP_SPLIT:
+ fprintf (f, ", offset: %u", apm->unit_offset);
+ /* fall-through */
+ case IPA_PARAM_OP_NEW:
+ fprintf (f, ", base_index: %u", apm->base_index);
+ fprintf (f, ", prev_clone_index: %u", apm->prev_clone_index);
+ print_node_brief (f, ", type: ", apm->type, 0);
+ print_node_brief (f, ", alias type: ", apm->alias_ptr_type, 0);
+ fprintf (f, " prefix: %s",
+ ipa_param_prefixes[apm->param_prefix_index]);
+ if (apm->reverse)
+ fprintf (f, ", reverse-sso");
+ break;
+ }
+ fprintf (f, "\n");
+ }
+}
- if (adj->op == IPA_PARM_OP_NEW)
- adj->base = NULL;
- else
- adj->base = parm;
- adj->new_decl = new_parm;
+/* Fill NEW_TYPES with types of a function after its current OTYPES have been
+ modified as described in ADJ_PARAMS. When USE_PREV_INDICES is true, use
+ prev_clone_index from ADJ_PARAMS as opposed to base_index when the parameter
+ is false. */
- *link = new_parm;
- link = &DECL_CHAIN (new_parm);
+static void
+fill_vector_of_new_param_types (vec<tree> *new_types, vec<tree> *otypes,
+ vec<ipa_adjusted_param, va_gc> *adj_params,
+ bool use_prev_indices)
+{
+ unsigned adj_len = vec_safe_length (adj_params);
+ new_types->reserve_exact (adj_len);
+ for (unsigned i = 0; i < adj_len ; i++)
+ {
+ ipa_adjusted_param *apm = &(*adj_params)[i];
+ if (apm->op == IPA_PARAM_OP_COPY)
+ {
+ unsigned index
+ = use_prev_indices ? apm->prev_clone_index : apm->base_index;
+ /* The following needs to be handled gracefully because of type
+ mismatches. This happens with LTO but apparently also in Fortran
+ with -fcoarray=lib -O2 -lcaf_single -latomic. */
+ if (index >= otypes->length ())
+ continue;
+ new_types->quick_push ((*otypes)[index]);
}
+ else if (apm->op == IPA_PARAM_OP_NEW
+ || apm->op == IPA_PARAM_OP_SPLIT)
+ {
+ tree ntype = apm->type;
+ if (is_gimple_reg_type (ntype)
+ && TYPE_MODE (ntype) != BLKmode)
+ {
+ unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ntype));
+ if (TYPE_ALIGN (ntype) != malign)
+ ntype = build_aligned_type (ntype, malign);
+ }
+ new_types->quick_push (ntype);
+ }
+ else
+ gcc_unreachable ();
}
+}
- *link = NULL_TREE;
+/* Build and return a function type just like ORIG_TYPE but with parameter
+ types given in NEW_PARAM_TYPES - which can be NULL if, but only if,
+ ORIG_TYPE itself has NULL TREE_ARG_TYPEs. If METHOD2FUNC is true, also make
+ it a FUNCTION_TYPE instead of FUNCTION_TYPE. */
- tree new_reversed = NULL;
- if (care_for_types)
+static tree
+build_adjusted_function_type (tree orig_type, vec<tree> *new_param_types,
+ bool method2func, bool skip_return)
+{
+ tree new_arg_types = NULL;
+ if (TYPE_ARG_TYPES (orig_type))
{
- new_reversed = nreverse (new_arg_types);
+ gcc_checking_assert (new_param_types);
+ bool last_parm_void = (TREE_VALUE (tree_last (TYPE_ARG_TYPES (orig_type)))
+ == void_type_node);
+ unsigned len = new_param_types->length ();
+ for (unsigned i = 0; i < len; i++)
+ new_arg_types = tree_cons (NULL_TREE, (*new_param_types)[i],
+ new_arg_types);
+
+ tree new_reversed = nreverse (new_arg_types);
if (last_parm_void)
{
if (new_reversed)
@@ -193,224 +224,568 @@ ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments)
else
new_reversed = void_list_node;
}
+ new_arg_types = new_reversed;
}
- /* 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. */
+ /* Use build_distinct_type_copy to preserve as much as possible from original
+ type (debug info, attribute lists etc.). The one exception is
+ METHOD_TYPEs which must have THIS argument and when we are asked to remove
+ it, we need to build new FUNCTION_TYPE instead. */
tree new_type = NULL;
- if (TREE_CODE (orig_type) != METHOD_TYPE
- || (adjustments[0].op == IPA_PARM_OP_COPY
- && adjustments[0].base_index == 0))
+ if (method2func)
+ {
+ tree ret_type;
+ if (skip_return)
+ ret_type = void_type_node;
+ else
+ ret_type = TREE_TYPE (orig_type);
+
+ new_type
+ = build_distinct_type_copy (build_function_type (ret_type,
+ new_arg_types));
+ TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
+ }
+ else
{
new_type = build_distinct_type_copy (orig_type);
- TYPE_ARG_TYPES (new_type) = new_reversed;
+ TYPE_ARG_TYPES (new_type) = new_arg_types;
+ if (skip_return)
+ TREE_TYPE (new_type) = void_type_node;
+ }
+
+ return new_type;
+}
+
+/* Return the maximum index in any IPA_PARAM_OP_COPY adjustment or -1 if there
+ is none. */
+
+int
+ipa_param_adjustments::get_max_base_index ()
+{
+ unsigned adj_len = vec_safe_length (m_adj_params);
+ int max_index = -1;
+ for (unsigned i = 0; i < adj_len ; i++)
+ {
+ ipa_adjusted_param *apm = &(*m_adj_params)[i];
+ if (apm->op == IPA_PARAM_OP_COPY
+ && max_index < apm->base_index)
+ max_index = apm->base_index;
+ }
+ return max_index;
+}
+
+
+/* Fill SURVIVING_PARAMS with an array of bools where each one says whether a
+ parameter that originally was at that position still survives in the given
+ clone or is removed/replaced. If the final array is smaller than an index
+ of an original parameter, that parameter also did not survive. That a
+ parameter survives does not mean it has the same index as before. */
+
+void
+ipa_param_adjustments::get_surviving_params (vec<bool> *surviving_params)
+{
+ unsigned adj_len = vec_safe_length (m_adj_params);
+ int max_index = get_max_base_index ();
+
+ if (max_index < 0)
+ return;
+ surviving_params->reserve_exact (max_index + 1);
+ surviving_params->quick_grow_cleared (max_index + 1);
+ for (unsigned i = 0; i < adj_len ; i++)
+ {
+ ipa_adjusted_param *apm = &(*m_adj_params)[i];
+ if (apm->op == IPA_PARAM_OP_COPY)
+ (*surviving_params)[apm->base_index] = true;
+ }
+}
+
+/* Fill NEW_INDICES with new indices of each surviving parameter or -1 for
+ those which do not survive. Any parameter outside of lenght of the vector
+ does not survive. There is currently no support for a parameter to be
+ copied to two distinct new parameters. */
+
+void
+ipa_param_adjustments::get_updated_indices (vec<int> *new_indices)
+{
+ unsigned adj_len = vec_safe_length (m_adj_params);
+ int max_index = get_max_base_index ();
+
+ if (max_index < 0)
+ return;
+ unsigned res_len = max_index + 1;
+ new_indices->reserve_exact (res_len);
+ for (unsigned i = 0; i < res_len ; i++)
+ new_indices->quick_push (-1);
+ for (unsigned i = 0; i < adj_len ; i++)
+ {
+ ipa_adjusted_param *apm = &(*m_adj_params)[i];
+ if (apm->op == IPA_PARAM_OP_COPY)
+ (*new_indices)[apm->base_index] = i;
+ }
+}
+
+/* Return true if the first parameter (assuming there was one) survives the
+ transformation intact and remains the first one. */
+
+bool
+ipa_param_adjustments::first_param_intact_p ()
+{
+ return (!vec_safe_is_empty (m_adj_params)
+ && (*m_adj_params)[0].op == IPA_PARAM_OP_COPY
+ && (*m_adj_params)[0].base_index == 0);
+}
+
+/* Return true if we have to change what has formerly been a method into a
+ function. */
+
+bool
+ipa_param_adjustments::method2func_p (tree orig_type)
+{
+ return ((TREE_CODE (orig_type) == METHOD_TYPE) && !first_param_intact_p ());
+}
+
+/* Given function type OLD_TYPE, return a new type derived from it after
+ performing all atored modifications. TYPE_ORIGINAL_P should be true when
+ OLD_TYPE refers to the type before any IPA transformations, as opposed to a
+ type that can be an intermediate one in between various IPA
+ transformations. */
+
+tree
+ipa_param_adjustments::build_new_function_type (tree old_type,
+ bool type_original_p)
+{
+ auto_vec<tree,16> new_param_types, *new_param_types_p;
+ if (prototype_p (old_type))
+ {
+ auto_vec<tree, 16> otypes;
+ push_function_arg_types (&otypes, old_type);
+ fill_vector_of_new_param_types (&new_param_types, &otypes, m_adj_params,
+ !type_original_p);
+ new_param_types_p = &new_param_types;
}
else
+ new_param_types_p = NULL;
+
+ return build_adjusted_function_type (old_type, new_param_types_p,
+ method2func_p (old_type), m_skip_return);
+}
+
+/* Build variant of function decl ORIG_DECL which has no return value if
+ M_SKIP_RETURN is true and, if ORIG_DECL's types or parameters is known, has
+ this type adjusted as indicated in M_ADJ_PARAMS. Arguments from
+ DECL_ARGUMENTS list are not processed now, since they are linked by
+ TREE_CHAIN directly and not accessible in LTO during WPA. The caller is
+ responsible for eliminating them when clones are properly materialized. */
+
+tree
+ipa_param_adjustments::adjust_decl (tree orig_decl)
+{
+ tree new_decl = copy_node (orig_decl);
+ tree orig_type = TREE_TYPE (orig_decl);
+ if (prototype_p (orig_type)
+ || (m_skip_return && !VOID_TYPE_P (TREE_TYPE (orig_type))))
{
- new_type
- = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
- new_reversed));
- TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
- DECL_VINDEX (fndecl) = NULL_TREE;
+ tree new_type = build_new_function_type (orig_type, false);
+ TREE_TYPE (new_decl) = new_type;
}
+ if (method2func_p (orig_type))
+ DECL_VINDEX (new_decl) = NULL_TREE;
/* When signature changes, we need to clear builtin info. */
- if (fndecl_built_in_p (fndecl))
- set_decl_built_in_function (fndecl, NOT_BUILT_IN, 0);
+ if (fndecl_built_in_p (new_decl))
+ set_decl_built_in_function (new_decl, NOT_BUILT_IN, 0);
+
+ DECL_VIRTUAL_P (new_decl) = 0;
+ DECL_LANG_SPECIFIC (new_decl) = NULL;
- TREE_TYPE (fndecl) = new_type;
- DECL_VIRTUAL_P (fndecl) = 0;
- DECL_LANG_SPECIFIC (fndecl) = NULL;
- otypes.release ();
- oparms.release ();
+ return new_decl;
}
-/* Modify actual arguments of a function call CS as indicated in ADJUSTMENTS.
- If this is a directly recursive call, CS must be NULL. Otherwise it must
- contain the corresponding call graph edge. */
+/* Wrapper around get_base_ref_and_offset for cases interesting for IPA-SRA
+ transformations. Return true if EXPR has an interesting form and fill in
+ *BASE_P and *UNIT_OFFSET_P with the appropriate info. */
-void
-ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
- ipa_parm_adjustment_vec adjustments)
-{
- struct cgraph_node *current_node = cgraph_node::get (current_function_decl);
- vec<tree> vargs;
- vec<tree, va_gc> **debug_args = NULL;
- gcall *new_stmt;
- gimple_stmt_iterator gsi, prev_gsi;
- tree callee_decl;
- int i, len;
+static bool
+isra_get_ref_base_and_offset (tree expr, tree *base_p, unsigned *unit_offset_p)
+{
+ HOST_WIDE_INT offset, size;
+ bool reverse;
+ tree base
+ = get_ref_base_and_extent_hwi (expr, &offset, &size, &reverse);
+ if (!base || size < 0)
+ return false;
- len = adjustments.length ();
- vargs.create (len);
- callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl;
- current_node->remove_stmt_references (stmt);
+ if ((offset % BITS_PER_UNIT) != 0)
+ return false;
- gsi = gsi_for_stmt (stmt);
- prev_gsi = gsi;
- gsi_prev (&prev_gsi);
- for (i = 0; i < len; i++)
+ if (TREE_CODE (base) == MEM_REF)
{
- struct ipa_parm_adjustment *adj;
+ poly_int64 plmoff = mem_ref_offset (base).force_shwi ();
+ HOST_WIDE_INT moff;
+ bool is_cst = plmoff.is_constant (&moff);
+ if (!is_cst)
+ return false;
+ offset += moff * BITS_PER_UNIT;
+ base = TREE_OPERAND (base, 0);
+ }
- adj = &adjustments[i];
+ if (offset < 0 || (offset / BITS_PER_UNIT) > UINT_MAX)
+ return false;
- if (adj->op == IPA_PARM_OP_COPY)
- {
- tree arg = gimple_call_arg (stmt, adj->base_index);
+ *base_p = base;
+ *unit_offset_p = offset / BITS_PER_UNIT;
+ return true;
+}
- vargs.quick_push (arg);
+/* 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;
+ }
+
+ 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;
}
- else if (adj->op != IPA_PARM_OP_REMOVE)
+ }
+ 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
+{
+ 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. */
+
+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)
+ {
+ 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))
{
- tree expr, base, off;
- location_t loc;
- unsigned int deref_align = 0;
- bool deref_base = false;
-
- /* We create a new parameter out of the value of the old one, we can
- do the following kind of transformations:
-
- - A scalar passed by reference is converted to a scalar passed by
- value. (adj->by_ref is false and the type of the original
- actual argument is a pointer to a scalar).
-
- - A part of an aggregate is passed instead of the whole aggregate.
- The part can be passed either by value or by reference, this is
- determined by value of adj->by_ref. Moreover, the code below
- handles both situations when the original aggregate is passed by
- value (its type is not a pointer) and when it is passed by
- reference (it is a pointer to an aggregate).
-
- When the new argument is passed by reference (adj->by_ref is true)
- it must be a part of an aggregate and therefore we form it by
- simply taking the address of a reference inside the original
- aggregate. */
-
- poly_int64 byte_offset = exact_div (adj->offset, BITS_PER_UNIT);
- base = gimple_call_arg (stmt, adj->base_index);
- loc = gimple_location (stmt);
-
- if (TREE_CODE (base) != ADDR_EXPR
- && POINTER_TYPE_P (TREE_TYPE (base)))
- off = build_int_cst (adj->alias_ptr_type, byte_offset);
- else
+ 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++)
{
- poly_int64 base_offset;
- tree prev_base;
- bool addrof;
+ ipa_param_performed_split *caller_split
+ = &(*performed_splits)[j];
+ if (caller_split->dummy_decl != dummy)
+ break;
- if (TREE_CODE (base) == ADDR_EXPR)
- {
- base = TREE_OPERAND (base, 0);
- addrof = true;
- }
- else
- addrof = false;
- prev_base = base;
- base = get_addr_base_and_unit_offset (base, &base_offset);
- /* Aggregate arguments can have non-invariant addresses. */
- if (!base)
- {
- base = build_fold_addr_expr (prev_base);
- off = build_int_cst (adj->alias_ptr_type, byte_offset);
- }
- else if (TREE_CODE (base) == MEM_REF)
- {
- if (!addrof)
- {
- deref_base = true;
- deref_align = TYPE_ALIGN (TREE_TYPE (base));
- }
- off = build_int_cst (adj->alias_ptr_type,
- base_offset + byte_offset);
- off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1),
- off);
- base = TREE_OPERAND (base, 0);
- }
- else
+ 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)
{
- off = build_int_cst (adj->alias_ptr_type,
- base_offset + byte_offset);
- base = build_fold_addr_expr (base);
+ tsm.unit_offset
+ = (caller_split->unit_offset - unit_offset_delta);
+ trans_map->safe_push (tsm);
}
+
+ phony_arguments++;
+ stmt_idx++;
}
+ }
+ base_index++;
+ }
+ return phony_arguments;
+}
- if (!adj->by_ref)
+/* 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);
+ 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);
+
+ gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+ gimple_stmt_iterator prev_gsi = gsi;
+ gsi_prev (&prev_gsi);
+ for (unsigned i = 0; i < len; i++)
+ {
+ ipa_adjusted_param *apm = &(*m_adj_params)[i];
+ if (apm->op == IPA_PARAM_OP_COPY)
+ {
+ unsigned index = apm->base_index;
+ if (index >= old_nargs)
+ /* Can happen if the original call has argument mismatch,
+ ignore. */
+ continue;
+ if (transitive_remapping)
+ index = index_map[apm->base_index];
+
+ tree arg = gimple_call_arg (stmt, index);
+
+ vargs.quick_push (arg);
+ kept[index] = true;
+ continue;
+ }
+
+ /* At the moment the only user of IPA_PARAM_OP_NEW modifies calls itself.
+ If we ever want to support it during WPA IPA stage, we'll need a
+ mechanism to call into the IPA passes that introduced them. Currently
+ we simply mandate that IPA infrastructure understands all argument
+ modifications. Remember, edge redirection/modification is done only
+ once, not in steps for each pass modifying the callee like clone
+ 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. */
+ 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)
+ {
+ repl = trans_map[j].repl;
+ break;
+ }
+ if (repl)
+ {
+ vargs.quick_push (repl);
+ continue;
+ }
+
+ unsigned index = apm->base_index;
+ if (index >= old_nargs)
+ /* Can happen if the original call has argument mismatch, ignore. */
+ continue;
+ if (transitive_remapping)
+ index = index_map[apm->base_index];
+ tree base = gimple_call_arg (stmt, index);
+
+ /* We create a new parameter out of the value of the old one, we can
+ do the following kind of transformations:
+
+ - A scalar passed by reference, potentially as a part of a larger
+ aggregate, is converted to a scalar passed by value.
+
+ - A part of an aggregate is passed instead of the whole aggregate. */
+
+ location_t loc = gimple_location (stmt);
+ tree off;
+ bool deref_base = false;
+ unsigned int deref_align = 0;
+ if (TREE_CODE (base) != ADDR_EXPR
+ && POINTER_TYPE_P (TREE_TYPE (base)))
+ off = build_int_cst (apm->alias_ptr_type, apm->unit_offset);
+ else
+ {
+ bool addrof;
+ if (TREE_CODE (base) == ADDR_EXPR)
{
- tree type = adj->type;
- unsigned int align;
- unsigned HOST_WIDE_INT misalign;
+ base = TREE_OPERAND (base, 0);
+ addrof = true;
+ }
+ else
+ addrof = false;
- if (deref_base)
- {
- align = deref_align;
- misalign = 0;
- }
- else
- {
- get_pointer_alignment_1 (base, &align, &misalign);
- if (TYPE_ALIGN (type) > align)
- align = TYPE_ALIGN (type);
- }
- misalign += (offset_int::from (wi::to_wide (off),
- SIGNED).to_short_addr ()
- * BITS_PER_UNIT);
- misalign = misalign & (align - 1);
- if (misalign != 0)
- align = least_bit_hwi (misalign);
- if (align < TYPE_ALIGN (type))
- type = build_aligned_type (type, align);
- base = force_gimple_operand_gsi (&gsi, base,
- true, NULL, true, GSI_SAME_STMT);
- expr = fold_build2_loc (loc, MEM_REF, type, base, off);
- REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse;
- /* If expr is not a valid gimple call argument emit
- a load into a temporary. */
- if (is_gimple_reg_type (TREE_TYPE (expr)))
+ tree prev_base = base;
+ poly_int64 base_offset;
+ base = get_addr_base_and_unit_offset (base, &base_offset);
+
+ /* Aggregate arguments can have non-invariant addresses. */
+ if (!base)
+ {
+ base = build_fold_addr_expr (prev_base);
+ off = build_int_cst (apm->alias_ptr_type, apm->unit_offset);
+ }
+ else if (TREE_CODE (base) == MEM_REF)
+ {
+ if (!addrof)
{
- gimple *tem = gimple_build_assign (NULL_TREE, expr);
- if (gimple_in_ssa_p (cfun))
- {
- gimple_set_vuse (tem, gimple_vuse (stmt));
- expr = make_ssa_name (TREE_TYPE (expr), tem);
- }
- else
- expr = create_tmp_reg (TREE_TYPE (expr));
- gimple_assign_set_lhs (tem, expr);
- gimple_set_location (tem, loc);
- gsi_insert_before (&gsi, tem, GSI_SAME_STMT);
+ deref_base = true;
+ deref_align = TYPE_ALIGN (TREE_TYPE (base));
}
+ off = build_int_cst (apm->alias_ptr_type,
+ base_offset + apm->unit_offset);
+ off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1),
+ off);
+ base = TREE_OPERAND (base, 0);
}
else
{
- expr = fold_build2_loc (loc, MEM_REF, adj->type, base, off);
- REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse;
- expr = build_fold_addr_expr (expr);
- expr = force_gimple_operand_gsi (&gsi, expr,
- true, NULL, true, GSI_SAME_STMT);
+ off = build_int_cst (apm->alias_ptr_type,
+ base_offset + apm->unit_offset);
+ base = build_fold_addr_expr (base);
}
- vargs.quick_push (expr);
}
- if (adj->op != IPA_PARM_OP_COPY && MAY_HAVE_DEBUG_BIND_STMTS)
+
+ tree type = apm->type;
+ unsigned int align;
+ unsigned HOST_WIDE_INT misalign;
+
+ if (deref_base)
{
- unsigned int ix;
- tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg;
- gimple *def_temp;
+ align = deref_align;
+ misalign = 0;
+ }
+ else
+ {
+ get_pointer_alignment_1 (base, &align, &misalign);
+ /* All users must make sure that we can be optimistic when it
+ comes to alignment in this case (by inspecting the final users
+ of these new parameters). */
+ if (TYPE_ALIGN (type) > align)
+ align = TYPE_ALIGN (type);
+ }
+ misalign
+ += (offset_int::from (wi::to_wide (off), SIGNED).to_short_addr ()
+ * BITS_PER_UNIT);
+ misalign = misalign & (align - 1);
+ if (misalign != 0)
+ align = least_bit_hwi (misalign);
+ if (align < TYPE_ALIGN (type))
+ type = build_aligned_type (type, align);
+ base = force_gimple_operand_gsi (&gsi, base,
+ true, NULL, true, GSI_SAME_STMT);
+ tree expr = fold_build2_loc (loc, MEM_REF, type, base, off);
+ REF_REVERSE_STORAGE_ORDER (expr) = apm->reverse;
+ /* If expr is not a valid gimple call argument emit
+ a load into a temporary. */
+ if (is_gimple_reg_type (TREE_TYPE (expr)))
+ {
+ gimple *tem = gimple_build_assign (NULL_TREE, expr);
+ if (gimple_in_ssa_p (cfun))
+ {
+ gimple_set_vuse (tem, gimple_vuse (stmt));
+ expr = make_ssa_name (TREE_TYPE (expr), tem);
+ }
+ else
+ expr = create_tmp_reg (TREE_TYPE (expr));
+ gimple_assign_set_lhs (tem, expr);
+ gsi_insert_before (&gsi, tem, GSI_SAME_STMT);
+ }
+ vargs.quick_push (expr);
+ }
+
+ 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));
+
+ /* 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 (MAY_HAVE_DEBUG_BIND_STMTS && old_decl && callee_decl)
+ {
+ vec<tree, va_gc> **debug_args = NULL;
+ unsigned i = 0;
+ for (tree old_parm = DECL_ARGUMENTS (old_decl);
+ old_parm && i < old_nargs && ((int) i) < m_always_copy_start;
+ old_parm = DECL_CHAIN (old_parm), i++)
+ {
+ if (!is_gimple_reg (old_parm) || kept[i])
+ continue;
+ tree origin = DECL_ORIGIN (old_parm);
+ tree arg = gimple_call_arg (stmt, i);
- arg = gimple_call_arg (stmt, adj->base_index);
if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg)))
{
if (!fold_convertible_p (TREE_TYPE (origin), arg))
continue;
- arg = fold_convert_loc (gimple_location (stmt),
- TREE_TYPE (origin), arg);
+ tree rhs1;
+ 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 (origin),
+ TREE_TYPE (rhs1)))
+ arg = rhs1;
+ else
+ arg = fold_convert_loc (gimple_location (stmt),
+ TREE_TYPE (origin), arg);
}
if (debug_args == NULL)
debug_args = decl_debug_args_insert (callee_decl);
+ unsigned int ix;
+ tree ddecl = NULL_TREE;
for (ix = 0; vec_safe_iterate (*debug_args, ix, &ddecl); ix += 2)
if (ddecl == origin)
{
@@ -427,7 +802,8 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
vec_safe_push (*debug_args, origin);
vec_safe_push (*debug_args, ddecl);
}
- def_temp = gimple_build_debug_bind (ddecl, unshare_expr (arg), stmt);
+ gimple *def_temp = gimple_build_debug_bind (ddecl,
+ unshare_expr (arg), stmt);
gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
}
}
@@ -438,10 +814,34 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
print_gimple_stmt (dump_file, gsi_stmt (gsi), 0);
}
- new_stmt = gimple_build_call_vec (callee_decl, vargs);
- vargs.release ();
- if (gimple_call_lhs (stmt))
- gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
+ gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
+
+ if (tree lhs = gimple_call_lhs (stmt))
+ {
+ if (!m_skip_return)
+ gimple_call_set_lhs (new_stmt, lhs);
+ else if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ /* LHS should now by a default-def SSA. Unfortunately default-def
+ SSA_NAMEs need a backing variable (or at least some code examining
+ SSAs assumes it is non-NULL). So we either have to re-use the
+ decl we have at hand or introdice a new one. */
+ tree repl = create_tmp_var (TREE_TYPE (lhs), "removed_return");
+ repl = get_or_create_ssa_default_def (cfun, repl);
+ SSA_NAME_IS_DEFAULT_DEF (repl) = true;
+ imm_use_iterator ui;
+ use_operand_p use_p;
+ gimple *using_stmt;
+ FOR_EACH_IMM_USE_STMT (using_stmt, ui, lhs)
+ {
+ FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+ {
+ SET_USE (use_p, repl);
+ }
+ update_stmt (using_stmt);
+ }
+ }
+ }
gimple_set_block (new_stmt, gimple_block (stmt));
if (gimple_has_location (stmt))
@@ -458,120 +858,398 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
fprintf (dump_file, "\n");
}
gsi_replace (&gsi, new_stmt, true);
- if (cs)
- cs->set_call_stmt (new_stmt);
- do
- {
- current_node->record_stmt_references (gsi_stmt (gsi));
- gsi_prev (&gsi);
- }
- while (gsi_stmt (gsi) != gsi_stmt (prev_gsi));
+ if (update_references)
+ do
+ {
+ current_node->record_stmt_references (gsi_stmt (gsi));
+ gsi_prev (&gsi);
+ }
+ while (gsi_stmt (gsi) != gsi_stmt (prev_gsi));
+ return new_stmt;
}
-/* Return true iff BASE_INDEX is in ADJUSTMENTS more than once. */
+/* Dump information contained in the object in textual form to F. */
-static bool
-index_in_adjustments_multiple_times_p (int base_index,
- ipa_parm_adjustment_vec adjustments)
+void
+ipa_param_adjustments::dump (FILE *f)
{
- int i, len = adjustments.length ();
- bool one = false;
+ fprintf (f, " m_always_copy_start: %i\n", m_always_copy_start);
+ ipa_dump_adjusted_parameters (f, m_adj_params);
+ if (m_skip_return)
+ fprintf (f, " Will SKIP return.\n");
+}
- for (i = 0; i < len; i++)
- {
- struct ipa_parm_adjustment *adj;
- adj = &adjustments[i];
+/* Dump information contained in the object in textual form to stderr. */
- if (adj->base_index == base_index)
- {
- if (one)
- return true;
- else
- one = true;
- }
- }
- return false;
+void
+ipa_param_adjustments::debug ()
+{
+ dump (stderr);
}
-/* Return adjustments that should have the same effect on function parameters
- and call arguments as if they were first changed according to adjustments in
- INNER and then by adjustments in OUTER. */
+/* Register that REPLACEMENT should replace parameter described in APM and
+ optionally as DUMMY to mark transitive splits accross calls. */
-ipa_parm_adjustment_vec
-ipa_combine_adjustments (ipa_parm_adjustment_vec inner,
- ipa_parm_adjustment_vec outer)
+void
+ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm,
+ tree replacement,
+ tree dummy)
{
- int i, outlen = outer.length ();
- int inlen = inner.length ();
- int removals = 0;
- ipa_parm_adjustment_vec adjustments, tmp;
+ gcc_checking_assert (apm->op == IPA_PARAM_OP_SPLIT
+ || apm->op == IPA_PARAM_OP_NEW);
+ gcc_checking_assert (!apm->prev_clone_adjustment);
+ ipa_param_body_replacement psr;
+ psr.base = m_oparms[apm->prev_clone_index];
+ psr.repl = replacement;
+ psr.dummy = dummy;
+ psr.unit_offset = apm->unit_offset;
+ m_replacements.safe_push (psr);
+}
+
+/* Copy or not, as appropriate given ID, a pre-existing PARM_DECL T so that
+ it can be included in the parameters of the modified function. */
- tmp.create (inlen);
- for (i = 0; i < inlen; i++)
+static tree
+carry_over_param (tree t, struct copy_body_data *id)
+{
+ tree new_parm;
+ if (id)
{
- struct ipa_parm_adjustment *n;
- n = &inner[i];
+ new_parm = remap_decl (t, id);
+ if (TREE_CODE (new_parm) != PARM_DECL)
+ new_parm = id->copy_decl (t, id);
+ }
+ else
+ new_parm = t;
+ return new_parm;
+}
- if (n->op == IPA_PARM_OP_REMOVE)
- removals++;
- else
+/* 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
+ a chained list of new local variables. TREE_MAP is the IPA-CP produced
+ mapping of trees to constants.
+
+ The function is rather long but it really onlu initializes all data members
+ of the class. It creates new param DECLs, finds their new types, */
+
+void
+ipa_param_body_adjustments::common_initialization (tree old_fndecl,
+ tree *vars,
+ vec<ipa_replace_map *,
+ va_gc> *tree_map)
+{
+ push_function_arg_decls (&m_oparms, old_fndecl);
+ auto_vec<tree,16> otypes;
+ if (TYPE_ARG_TYPES (TREE_TYPE (old_fndecl)) != NULL_TREE)
+ push_function_arg_types (&otypes, TREE_TYPE (old_fndecl));
+ else
+ {
+ auto_vec<tree,16> oparms;
+ push_function_arg_decls (&oparms, old_fndecl);
+ unsigned ocount = oparms.length ();
+ otypes.reserve_exact (ocount);
+ for (unsigned i = 0; i < ocount; i++)
+ otypes.quick_push (TREE_TYPE (oparms[i]));
+ }
+ fill_vector_of_new_param_types (&m_new_types, &otypes, m_adj_params, true);
+
+ 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 ());
+
+ unsigned adj_len = vec_safe_length (m_adj_params);
+ m_method2func = ((TREE_CODE (TREE_TYPE (m_fndecl)) == METHOD_TYPE)
+ && (adj_len == 0
+ || (*m_adj_params)[0].op != IPA_PARAM_OP_COPY
+ || (*m_adj_params)[0].base_index != 0));
+
+ /* The main job of the this function is to go over the vector of adjusted
+ parameters and create declarations or find corresponding old ones and push
+ them to m_new_decls. For IPA-SRA replacements it also creates
+ corresponding m_id->dst_node->clone.performed_splits entries. */
+
+ m_new_decls.reserve_exact (adj_len);
+ for (unsigned i = 0; i < adj_len ; i++)
+ {
+ ipa_adjusted_param *apm = &(*m_adj_params)[i];
+ unsigned prev_index = apm->prev_clone_index;
+ tree new_parm;
+ if (apm->op == IPA_PARAM_OP_COPY
+ || apm->prev_clone_adjustment)
{
- /* FIXME: Handling of new arguments are not implemented yet. */
- gcc_assert (n->op != IPA_PARM_OP_NEW);
- tmp.quick_push (*n);
+ kept[prev_index] = true;
+ new_parm = carry_over_param (m_oparms[prev_index], m_id);
+ m_new_decls.quick_push (new_parm);
}
+ else if (apm->op == IPA_PARAM_OP_NEW
+ || apm->op == IPA_PARAM_OP_SPLIT)
+ {
+ tree new_type = m_new_types[i];
+ gcc_checking_assert (new_type);
+ new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE,
+ new_type);
+ const char *prefix = ipa_param_prefixes[apm->param_prefix_index];
+ DECL_NAME (new_parm) = create_tmp_var_name (prefix);
+ DECL_ARTIFICIAL (new_parm) = 1;
+ DECL_ARG_TYPE (new_parm) = new_type;
+ DECL_CONTEXT (new_parm) = m_fndecl;
+ TREE_USED (new_parm) = 1;
+ DECL_IGNORED_P (new_parm) = 1;
+ /* We assume all newly created arguments are not addressable. */
+ if (TREE_CODE (new_type) == COMPLEX_TYPE
+ || TREE_CODE (new_type) == VECTOR_TYPE)
+ DECL_GIMPLE_REG_P (new_parm) = 1;
+ layout_decl (new_parm, 0);
+ m_new_decls.quick_push (new_parm);
+
+ 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 (m_id->dst_node->clone.performed_splits, ps);
+ }
+ else
+ register_replacement (apm, new_parm);
+ }
+ }
+ else
+ gcc_unreachable ();
}
- adjustments.create (outlen + removals);
- for (i = 0; i < outlen; i++)
+
+ /* As part of body modifications, we will also have to replace remaining uses
+ of remaining uses of removed PARM_DECLs (which do not however use the
+ initial value) with their VAR_DECL copies.
+
+ We do this differently with and without m_id. With m_id, we rely on its
+ mapping and create a replacement straight away. Without it, we have our
+ own mechanism for which we have to populate m_removed_decls vector. Just
+ don't mix them, that is why you should not call
+ replace_removed_params_ssa_names or perform_cfun_body_modifications when
+ you construct with ID not equal to NULL. */
+
+ unsigned op_len = m_oparms.length ();
+ for (unsigned i = 0; i < op_len; i++)
+ if (!kept[i])
+ {
+ if (m_id)
+ {
+ 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;
+ }
+ }
+ else
+ {
+ m_removed_decls.safe_push (m_oparms[i]);
+ m_removed_map.put (m_oparms[i], m_removed_decls.length () - 1);
+ }
+ }
+
+ if (!MAY_HAVE_DEBUG_STMTS)
+ return;
+
+ /* Finally, when generating debug info, we fill vector m_reset_debug_decls
+ with removed parameters declarations. We do this in order to re-map their
+ debug bind statements and create debug decls for them. */
+
+ if (tree_map)
{
- struct ipa_parm_adjustment r;
- struct ipa_parm_adjustment *out = &outer[i];
- struct ipa_parm_adjustment *in = &tmp[out->base_index];
+ /* Do not output debuginfo for parameter declarations as if they vanished
+ when they were in fact replaced by a constant. */
+ auto_vec <int, 16> index_mapping;
+ bool need_remap = false;
- memset (&r, 0, sizeof (r));
- gcc_assert (in->op != IPA_PARM_OP_REMOVE);
- if (out->op == IPA_PARM_OP_REMOVE)
+ if (m_id && m_id->src_node->clone.param_adjustments)
{
- if (!index_in_adjustments_multiple_times_p (in->base_index, tmp))
- {
- r.op = IPA_PARM_OP_REMOVE;
- adjustments.quick_push (r);
- }
- continue;
+ ipa_param_adjustments *prev_adjustments
+ = m_id->src_node->clone.param_adjustments;
+ prev_adjustments->get_updated_indices (&index_mapping);
+ need_remap = true;
}
- else
+
+ for (unsigned i = 0; i < tree_map->length (); i++)
{
- /* FIXME: Handling of new arguments are not implemented yet. */
- gcc_assert (out->op != IPA_PARM_OP_NEW);
+ int parm_num = (*tree_map)[i]->parm_num;
+ gcc_assert (parm_num >= 0);
+ if (need_remap)
+ parm_num = index_mapping[parm_num];
+ kept[parm_num] = true;
}
+ }
+
+ for (unsigned i = 0; i < op_len; i++)
+ if (!kept[i] && is_gimple_reg (m_oparms[i]))
+ m_reset_debug_decls.safe_push (m_oparms[i]);
+}
- r.base_index = in->base_index;
- r.type = out->type;
+/* Constructor of ipa_param_body_adjustments from a simple list of
+ modifications to parameters listed in ADJ_PARAMS which will prepare ground
+ for modification of parameters of fndecl. Return value of the function will
+ not be removed and the object will assume it does not run as a part of
+ tree-function_versioning. */
+
+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)
+{
+ common_initialization (fndecl, NULL, NULL);
+}
- /* FIXME: Create nonlocal value too. */
+/* Constructor of ipa_param_body_adjustments from ipa_param_adjustments in
+ ADJUSTMENTS which will prepare ground for modification of parameters of
+ fndecl. The object will assume it does not run as a part of
+ tree-function_versioning. */
+
+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_method2func (false)
+{
+ common_initialization (fndecl, NULL, NULL);
+}
- if (in->op == IPA_PARM_OP_COPY && out->op == IPA_PARM_OP_COPY)
- r.op = IPA_PARM_OP_COPY;
- else if (in->op == IPA_PARM_OP_COPY)
- r.offset = out->offset;
- else if (out->op == IPA_PARM_OP_COPY)
- r.offset = in->offset;
- else
- r.offset = in->offset + out->offset;
- adjustments.quick_push (r);
+/* Constructor of ipa_param_body_adjustments which sets it up as a part of
+ running tree_function_versioning. Planned modifications to the function are
+ in ADJUSTMENTS. FNDECL designates the new function clone which is being
+ modified. OLD_FNDECL is the function of which FNDECL is a clone (and which
+ at the time of invocation still share DECL_ARGUMENTS). ID is the
+ copy_body_data structure driving the wholy body copying process. VARS is a
+ pointer to the head of the list of new local variables, TREE_MAP is the map
+ that drives tree substitution in the cloning process. */
+
+ipa_param_body_adjustments
+::ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
+ tree fndecl, tree old_fndecl,
+ 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)
+{
+ common_initialization (old_fndecl, vars, tree_map);
+}
+
+/* Chain new param decls up and return them. */
+
+tree
+ipa_param_body_adjustments::get_new_param_chain ()
+{
+ tree result;
+ tree *link = &result;
+
+ unsigned len = vec_safe_length (m_adj_params);
+ for (unsigned i = 0; i < len; i++)
+ {
+ tree new_decl = m_new_decls[i];
+ *link = new_decl;
+ link = &DECL_CHAIN (new_decl);
}
+ *link = NULL_TREE;
+ return result;
+}
+
+/* Modify the function parameters FNDECL and its type according to the plan in
+ ADJUSTMENTS. This function needs to be called when the decl has not already
+ been processed with ipa_param_adjustments::adjust_decl, otherwise just
+ seting DECL_ARGUMENTS to whatever get_new_param_chain will do is enough. */
+
+void
+ipa_param_body_adjustments::modify_formal_parameters ()
+{
+ tree orig_type = TREE_TYPE (m_fndecl);
+ DECL_ARGUMENTS (m_fndecl) = get_new_param_chain ();
+
+ /* When signature changes, we need to clear builtin info. */
+ if (fndecl_built_in_p (m_fndecl))
+ set_decl_built_in_function (m_fndecl, NOT_BUILT_IN, 0);
+
+ /* At this point, removing return value is only implemented when going
+ through tree_function_versioning, not when modifying function body
+ directly. */
+ gcc_assert (!m_adjustments || !m_adjustments->m_skip_return);
+ tree new_type = build_adjusted_function_type (orig_type, &m_new_types,
+ m_method2func, false);
+
+ TREE_TYPE (m_fndecl) = new_type;
+ DECL_VIRTUAL_P (m_fndecl) = 0;
+ DECL_LANG_SPECIFIC (m_fndecl) = NULL;
+ if (m_method2func)
+ DECL_VINDEX (m_fndecl) = NULL_TREE;
+}
- for (i = 0; i < inlen; i++)
+/* Given BASE and UNIT_OFFSET, find the corresponding record among replacement
+ structures. */
+
+ipa_param_body_replacement *
+ipa_param_body_adjustments::lookup_replacement_1 (tree base,
+ unsigned unit_offset)
+{
+ unsigned int len = m_replacements.length ();
+ for (unsigned i = 0; i < len; i++)
{
- struct ipa_parm_adjustment *n = &inner[i];
+ ipa_param_body_replacement *pbr = &m_replacements[i];
- if (n->op == IPA_PARM_OP_REMOVE)
- adjustments.quick_push (*n);
+ if (pbr->base == base
+ && (pbr->unit_offset == unit_offset))
+ return pbr;
}
+ return NULL;
+}
- tmp.release ();
- return adjustments;
+/* Given BASE and UNIT_OFFSET, find the corresponding replacement expression
+ and return it, assuming it is known it does not hold value by reference or
+ in reverse storage order. */
+
+tree
+ipa_param_body_adjustments::lookup_replacement (tree base, unsigned unit_offset)
+{
+ ipa_param_body_replacement *pbr = lookup_replacement_1 (base, unit_offset);
+ if (!pbr)
+ return NULL;
+ return pbr->repl;
}
/* If T is an SSA_NAME, return NULL if it is not a default def or
@@ -592,165 +1270,634 @@ get_ssa_base_param (tree t, bool ignore_default_def)
return t;
}
-/* Given an expression, return an adjustment entry specifying the
- transformation to be done on EXPR. If no suitable adjustment entry
- was found, returns NULL.
+/* Given an expression, return the structure describing how it should be
+ replaced if it accesses a part of a split parameter or NULL otherwise.
- If IGNORE_DEFAULT_DEF is set, consider SSA_NAMEs which are not a
- default def, otherwise bail on them.
+ Do not free the result, it will be deallocated when the object is destroyed.
- If CONVERT is non-NULL, this function will set *CONVERT if the
- expression provided is a component reference. ADJUSTMENTS is the
- adjustments vector. */
+ If IGNORE_DEFAULT_DEF is cleared, consider only SSA_NAMEs of PARM_DECLs
+ which are default definitions, if set, consider all SSA_NAMEs of
+ PARM_DECLs. */
-ipa_parm_adjustment *
-ipa_get_adjustment_candidate (tree **expr, bool *convert,
- ipa_parm_adjustment_vec adjustments,
- bool ignore_default_def)
+ipa_param_body_replacement *
+ipa_param_body_adjustments::get_expr_replacement (tree expr,
+ bool ignore_default_def)
{
- if (TREE_CODE (**expr) == BIT_FIELD_REF
- || TREE_CODE (**expr) == IMAGPART_EXPR
- || TREE_CODE (**expr) == REALPART_EXPR)
- {
- *expr = &TREE_OPERAND (**expr, 0);
- if (convert)
- *convert = true;
- }
+ tree base;
+ unsigned unit_offset;
- poly_int64 offset, size, max_size;
- bool reverse;
- tree base
- = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse);
- if (!base || !known_size_p (size) || !known_size_p (max_size))
+ if (!isra_get_ref_base_and_offset (expr, &base, &unit_offset))
return NULL;
- if (TREE_CODE (base) == MEM_REF)
- {
- offset += mem_ref_offset (base).force_shwi () * BITS_PER_UNIT;
- base = TREE_OPERAND (base, 0);
- }
-
base = get_ssa_base_param (base, ignore_default_def);
if (!base || TREE_CODE (base) != PARM_DECL)
return NULL;
+ return lookup_replacement_1 (base, unit_offset);
+}
- struct ipa_parm_adjustment *cand = NULL;
- unsigned int len = adjustments.length ();
- for (unsigned i = 0; i < len; i++)
- {
- struct ipa_parm_adjustment *adj = &adjustments[i];
+/* Given OLD_DECL, which is a PARM_DECL of a parameter that is being removed
+ (which includes it being split or replaced), return a new variable that
+ should be used for any SSA names that will remain in the function that
+ previously belonged to OLD_DECL. */
- if (adj->base == base
- && (known_eq (adj->offset, offset) || adj->op == IPA_PARM_OP_REMOVE))
- {
- cand = adj;
- break;
- }
+tree
+ipa_param_body_adjustments::get_replacement_ssa_base (tree old_decl)
+{
+ unsigned *idx = m_removed_map.get (old_decl);
+ if (!idx)
+ return NULL;
+
+ tree repl;
+ if (TREE_CODE (m_removed_decls[*idx]) == PARM_DECL)
+ {
+ gcc_assert (m_removed_decls[*idx] == old_decl);
+ repl = copy_var_decl (old_decl, DECL_NAME (old_decl),
+ TREE_TYPE (old_decl));
+ m_removed_decls[*idx] = repl;
}
+ else
+ repl = m_removed_decls[*idx];
+ return repl;
+}
+
+/* If OLD_NAME, which is being defined by statement STMT, is an SSA_NAME of a
+ parameter which is to be removed because its value is not used, create a new
+ SSA_NAME relating to a replacement VAR_DECL, replace all uses of the
+ original with it and return it. If there is no need to re-map, return NULL.
+ ADJUSTMENTS is a pointer to a vector of IPA-SRA adjustments. */
+
+tree
+ipa_param_body_adjustments::replace_removed_params_ssa_names (tree old_name,
+ gimple *stmt)
+{
+ gcc_assert (!m_id);
+ if (TREE_CODE (old_name) != SSA_NAME)
+ return NULL;
+
+ tree decl = SSA_NAME_VAR (old_name);
+ if (decl == NULL_TREE
+ || TREE_CODE (decl) != PARM_DECL)
+ return NULL;
- if (!cand || cand->op == IPA_PARM_OP_COPY || cand->op == IPA_PARM_OP_REMOVE)
+ tree repl = get_replacement_ssa_base (decl);
+ if (!repl)
return NULL;
- return cand;
+
+ tree new_name = make_ssa_name (repl, stmt);
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_name)
+ = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (old_name);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "replacing an SSA name of a removed param ");
+ print_generic_expr (dump_file, old_name);
+ fprintf (dump_file, " with ");
+ print_generic_expr (dump_file, new_name);
+ fprintf (dump_file, "\n");
+ }
+
+ replace_uses_by (old_name, new_name);
+ return new_name;
}
-/* If the expression *EXPR should be replaced by a reduction of a parameter, do
- so. ADJUSTMENTS is a pointer to a vector of adjustments. CONVERT
- specifies whether the function should care about type incompatibility the
- current and new expressions. If it is false, the function will leave
- incompatibility issues to the caller. Return true iff the expression
- was modified. */
+/* If the expression *EXPR_P should be replaced, do so. CONVERT specifies
+ whether the function should care about type incompatibility of the current
+ and new expressions. If it is false, the function will leave
+ incompatibility issues to the caller - note that when the function
+ encounters a BIT_FIELD_REF, IMAGPART_EXPR or REALPART_EXPR, it will modify
+ their bases instead of the expressions themselves and then also performs any
+ necessary conversions. */
bool
-ipa_modify_expr (tree *expr, bool convert,
- ipa_parm_adjustment_vec adjustments)
+ipa_param_body_adjustments::modify_expression (tree *expr_p, bool convert)
{
- struct ipa_parm_adjustment *cand
- = ipa_get_adjustment_candidate (&expr, &convert, adjustments, false);
- if (!cand)
- return false;
+ tree expr = *expr_p;
- tree src;
- if (cand->by_ref)
+ if (TREE_CODE (expr) == BIT_FIELD_REF
+ || TREE_CODE (expr) == IMAGPART_EXPR
+ || TREE_CODE (expr) == REALPART_EXPR)
{
- src = build_simple_mem_ref (cand->new_decl);
- REF_REVERSE_STORAGE_ORDER (src) = cand->reverse;
+ expr_p = &TREE_OPERAND (expr, 0);
+ expr = *expr_p;
+ convert = true;
}
- else
- src = cand->new_decl;
+ ipa_param_body_replacement *pbr = get_expr_replacement (expr, false);
+ if (!pbr)
+ return false;
+
+ tree repl = pbr->repl;
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "About to replace expr ");
- print_generic_expr (dump_file, *expr);
+ print_generic_expr (dump_file, expr);
fprintf (dump_file, " with ");
- print_generic_expr (dump_file, src);
+ print_generic_expr (dump_file, repl);
fprintf (dump_file, "\n");
}
- if (convert && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type))
+ if (convert && !useless_type_conversion_p (TREE_TYPE (expr),
+ TREE_TYPE (repl)))
{
- tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src);
- *expr = vce;
+ tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expr), repl);
+ *expr_p = vce;
}
else
- *expr = src;
+ *expr_p = repl;
return true;
}
-/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human
- friendly way, assuming they are meant to be applied to FNDECL. */
+/* If the assignment statement STMT contains any expressions that need to
+ replaced with a different one as noted by ADJUSTMENTS, do so. Handle any
+ potential type incompatibilities. If any conversion sttements have to be
+ pre-pended to STMT, they will be added to EXTRA_STMTS. Return true iff the
+ statement was modified. */
-void
-ipa_dump_param_adjustments (FILE *file, ipa_parm_adjustment_vec adjustments,
- tree fndecl)
+bool
+ipa_param_body_adjustments::modify_assignment (gimple *stmt,
+ gimple_seq *extra_stmts)
{
- int i, len = adjustments.length ();
- bool first = true;
- vec<tree> parms = ipa_get_vector_of_formal_parms (fndecl);
+ tree *lhs_p, *rhs_p;
+ bool any;
- fprintf (file, "IPA param adjustments: ");
- for (i = 0; i < len; i++)
- {
- struct ipa_parm_adjustment *adj;
- adj = &adjustments[i];
+ if (!gimple_assign_single_p (stmt))
+ return false;
- if (!first)
- fprintf (file, " ");
+ rhs_p = gimple_assign_rhs1_ptr (stmt);
+ lhs_p = gimple_assign_lhs_ptr (stmt);
+
+ any = modify_expression (lhs_p, false);
+ any |= modify_expression (rhs_p, false);
+ if (any
+ && !useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p)))
+ {
+ if (TREE_CODE (*rhs_p) == CONSTRUCTOR)
+ {
+ /* V_C_Es of constructors can cause trouble (PR 42714). */
+ if (is_gimple_reg_type (TREE_TYPE (*lhs_p)))
+ *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p));
+ else
+ *rhs_p = build_constructor (TREE_TYPE (*lhs_p),
+ NULL);
+ }
else
- first = false;
+ {
+ tree new_rhs = fold_build1_loc (gimple_location (stmt),
+ VIEW_CONVERT_EXPR, TREE_TYPE (*lhs_p),
+ *rhs_p);
+ tree tmp = force_gimple_operand (new_rhs, extra_stmts, true,
+ NULL_TREE);
+ gimple_assign_set_rhs1 (stmt, tmp);
+ }
+ return true;
+ }
+
+ 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;
+};
+
+/* 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. */
- fprintf (file, "%i. base_index: %i - ", i, adj->base_index);
- print_generic_expr (file, parms[adj->base_index]);
- if (adj->base)
+static tree
+remap_split_decl_to_dummy (tree *tp, int *walk_subtrees, void *data)
+{
+ tree t = *tp;
+
+ if (DECL_P (t) || TREE_CODE (t) == SSA_NAME)
+ {
+ 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))
{
- fprintf (file, ", base: ");
- print_generic_expr (file, adj->base);
+ *tp = swapinfo->to;
+ swapinfo->done = true;
+ }
+ *walk_subtrees = 0;
+ }
+ else if (TYPE_P (t))
+ *walk_subtrees = 0;
+ else
+ *walk_subtrees = 1;
+ return NULL_TREE;
+}
+
+
+/* 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.
+
+ 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. */
+
+bool
+ipa_param_body_adjustments::modify_call_stmt (gcall **stmt_p)
+{
+ gcall *stmt = *stmt_p;
+ auto_vec <unsigned, 4> pass_through_args;
+ auto_vec <unsigned, 4> pass_through_pbr_indices;
+
+ if (m_split_modifications_p && m_id)
+ {
+ 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 base;
+ unsigned unit_offset;
+ if (!isra_get_ref_base_and_offset (t, &base, &unit_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++)
+ {
+ ipa_param_body_replacement *pbr = &m_replacements[j];
+ if (pbr->base == base)
+ {
+ base_among_replacements = true;
+ break;
+ }
+ }
+ 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. */
+
+ /* 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)
+ {
+ /* This must be a by_reference pass-through. */
+ gcc_assert (POINTER_TYPE_P (TREE_TYPE (t)));
+ 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);
+ }
+ }
}
- if (adj->new_decl)
+ }
+
+ unsigned nargs = gimple_call_num_args (stmt);
+ if (!pass_through_args.is_empty ())
+ {
+ auto_vec<tree, 16> vargs;
+ unsigned pt_idx = 0;
+ for (unsigned i = 0; i < nargs; i++)
{
- fprintf (file, ", new_decl: ");
- print_generic_expr (file, adj->new_decl);
+ if (pt_idx < pass_through_args.length ()
+ && i == pass_through_args[pt_idx])
+ {
+ 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);
+ }
+ }
+ else
+ {
+ tree t = gimple_call_arg (stmt, i);
+ modify_expression (&t, true);
+ vargs.safe_push (t);
+ }
}
- if (adj->new_ssa_base)
+ gcall *new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs);
+ gimple_call_set_chain (new_stmt, gimple_call_chain (stmt));
+ gimple_call_copy_flags (new_stmt, stmt);
+ if (tree lhs = gimple_call_lhs (stmt))
{
- fprintf (file, ", new_ssa_base: ");
- print_generic_expr (file, adj->new_ssa_base);
+ modify_expression (&lhs, false);
+ gimple_call_set_lhs (new_stmt, lhs);
}
+ *stmt_p = new_stmt;
+ return true;
+ }
- if (adj->op == IPA_PARM_OP_COPY)
- fprintf (file, ", copy_param");
- else if (adj->op == IPA_PARM_OP_REMOVE)
- fprintf (file, ", remove_param");
- else
+ /* 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))
+ {
+ tree *t = gimple_call_lhs_ptr (stmt);
+ modified |= modify_expression (t, false);
+ }
+
+ return modified;
+}
+
+/* If the statement STMT contains any expressions that need to replaced with a
+ different one as noted by ADJUSTMENTS, do so. Handle any potential type
+ incompatibilities. If any conversion sttements have to be pre-pended to
+ STMT, they will be added to EXTRA_STMTS. Return true iff the statement was
+ modified. */
+
+bool
+ipa_param_body_adjustments::modify_gimple_stmt (gimple **stmt,
+ gimple_seq *extra_stmts)
+{
+ bool modified = false;
+ tree *t;
+
+ switch (gimple_code (*stmt))
+ {
+ case GIMPLE_RETURN:
+ t = gimple_return_retval_ptr (as_a <greturn *> (*stmt));
+ if (m_adjustments && m_adjustments->m_skip_return)
+ *t = NULL_TREE;
+ else if (*t != NULL_TREE)
+ modified |= modify_expression (t, true);
+ break;
+
+ case GIMPLE_ASSIGN:
+ modified |= modify_assignment (*stmt, extra_stmts);
+ break;
+
+ case GIMPLE_CALL:
+ modified |= modify_call_stmt ((gcall **) stmt);
+ break;
+
+ case GIMPLE_ASM:
+ {
+ gasm *asm_stmt = as_a <gasm *> (*stmt);
+ for (unsigned i = 0; i < gimple_asm_ninputs (asm_stmt); i++)
+ {
+ t = &TREE_VALUE (gimple_asm_input_op (asm_stmt, i));
+ modified |= modify_expression (t, true);
+ }
+ for (unsigned i = 0; i < gimple_asm_noutputs (asm_stmt); i++)
+ {
+ t = &TREE_VALUE (gimple_asm_output_op (asm_stmt, i));
+ modified |= modify_expression (t, false);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return modified;
+}
+
+
+/* Traverse body of the current function and perform the requested adjustments
+ on its statements. Return true iff the CFG has been changed. */
+
+bool
+ipa_param_body_adjustments::modify_cfun_body ()
+{
+ bool cfg_changed = false;
+ basic_block bb;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gphi *phi = as_a <gphi *> (gsi_stmt (gsi));
+ tree new_lhs, old_lhs = gimple_phi_result (phi);
+ new_lhs = replace_removed_params_ssa_names (old_lhs, phi);
+ if (new_lhs)
+ {
+ gimple_phi_set_result (phi, new_lhs);
+ release_ssa_name (old_lhs);
+ }
+ }
+
+ gsi = gsi_start_bb (bb);
+ while (!gsi_end_p (gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ gimple *stmt_copy = stmt;
+ gimple_seq extra_stmts = NULL;
+ bool modified = modify_gimple_stmt (&stmt, &extra_stmts);
+ if (stmt != stmt_copy)
+ {
+ gcc_checking_assert (modified);
+ gsi_replace (&gsi, stmt, false);
+ }
+ if (!gimple_seq_empty_p (extra_stmts))
+ gsi_insert_seq_before (&gsi, extra_stmts, GSI_SAME_STMT);
+
+ def_operand_p defp;
+ ssa_op_iter iter;
+ FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF)
+ {
+ tree old_def = DEF_FROM_PTR (defp);
+ if (tree new_def = replace_removed_params_ssa_names (old_def,
+ stmt))
+ {
+ SET_DEF (defp, new_def);
+ release_ssa_name (old_def);
+ modified = true;
+ }
+ }
+
+ if (modified)
+ {
+ update_stmt (stmt);
+ if (maybe_clean_eh_stmt (stmt)
+ && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+ cfg_changed = true;
+ }
+ gsi_next (&gsi);
+ }
+ }
+
+ return cfg_changed;
+}
+
+/* Call gimple_debug_bind_reset_value on all debug statements describing
+ gimple register parameters that are being removed or replaced. */
+
+void
+ipa_param_body_adjustments::reset_debug_stmts ()
+{
+ int i, len;
+ gimple_stmt_iterator *gsip = NULL, gsi;
+
+ if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun)))
+ {
+ gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+ gsip = &gsi;
+ }
+ len = m_reset_debug_decls.length ();
+ for (i = 0; i < len; i++)
+ {
+ imm_use_iterator ui;
+ gimple *stmt;
+ gdebug *def_temp;
+ tree name, vexpr, copy = NULL_TREE;
+ use_operand_p use_p;
+ tree decl = m_reset_debug_decls[i];
+
+ gcc_checking_assert (is_gimple_reg (decl));
+ name = ssa_default_def (cfun, decl);
+ vexpr = NULL;
+ if (name)
+ FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+ {
+ if (gimple_clobber_p (stmt))
+ {
+ gimple_stmt_iterator cgsi = gsi_for_stmt (stmt);
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&cgsi, true);
+ release_defs (stmt);
+ continue;
+ }
+ /* All other users must have been removed by function body
+ modification. */
+ gcc_assert (is_gimple_debug (stmt));
+ if (vexpr == NULL && gsip != NULL)
+ {
+ vexpr = make_node (DEBUG_EXPR_DECL);
+ def_temp = gimple_build_debug_source_bind (vexpr, decl, NULL);
+ DECL_ARTIFICIAL (vexpr) = 1;
+ TREE_TYPE (vexpr) = TREE_TYPE (name);
+ SET_DECL_MODE (vexpr, DECL_MODE (decl));
+ gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
+ }
+ if (vexpr)
+ {
+ FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+ SET_USE (use_p, vexpr);
+ }
+ else
+ gimple_debug_bind_reset_value (stmt);
+ update_stmt (stmt);
+ }
+ /* Create a VAR_DECL for debug info purposes. */
+ if (!DECL_IGNORED_P (decl))
{
- fprintf (file, ", offset ");
- print_dec (adj->offset, file);
+ copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
+ VAR_DECL, DECL_NAME (decl),
+ TREE_TYPE (decl));
+ if (DECL_PT_UID_SET_P (decl))
+ SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
+ TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
+ TREE_READONLY (copy) = TREE_READONLY (decl);
+ TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
+ DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl);
+ DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl);
+ DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl);
+ DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
+ DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
+ SET_DECL_RTL (copy, 0);
+ TREE_USED (copy) = 1;
+ DECL_CONTEXT (copy) = current_function_decl;
+ add_local_decl (cfun, copy);
+ DECL_CHAIN (copy)
+ = BLOCK_VARS (DECL_INITIAL (current_function_decl));
+ BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy;
+ }
+ if (gsip != NULL && copy && target_for_debug_bind (decl))
+ {
+ gcc_assert (TREE_CODE (decl) == PARM_DECL);
+ if (vexpr)
+ def_temp = gimple_build_debug_bind (copy, vexpr, NULL);
+ else
+ def_temp = gimple_build_debug_source_bind (copy, decl,
+ NULL);
+ gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
}
- if (adj->by_ref)
- fprintf (file, ", by_ref");
- print_node_brief (file, ", type: ", adj->type, 0);
- fprintf (file, "\n");
}
- parms.release ();
+}
+
+/* Perform all necessary body changes to change signature, body and debug info
+ of fun according to adjustments passed at construction. Return true if CFG
+ was changed in any way. The main entry point for modification of standalone
+ functions that is not part of IPA clone materialization. */
+
+bool
+ipa_param_body_adjustments::perform_cfun_body_modifications ()
+{
+ bool cfg_changed;
+ modify_formal_parameters ();
+ cfg_changed = modify_cfun_body ();
+ reset_debug_stmts ();
+
+ return cfg_changed;
}