+
+
+/* Return true iff PARM (which must be a parm_decl) is an unused scalar
+ parameter. */
+
+static bool
+is_unused_scalar_param (tree parm)
+{
+ tree name;
+ return (is_gimple_reg (parm)
+ && (!(name = gimple_default_def (cfun, parm))
+ || has_zero_uses (name)));
+}
+
+/* Scan immediate uses of a default definition SSA name of a parameter PARM and
+ examine whether there are any direct or otherwise infeasible ones. If so,
+ return true, otherwise return false. PARM must be a gimple register with a
+ non-NULL default definition. */
+
+static bool
+ptr_parm_has_direct_uses (tree parm)
+{
+ imm_use_iterator ui;
+ gimple stmt;
+ tree name = gimple_default_def (cfun, parm);
+ bool ret = false;
+
+ FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+ {
+ if (gimple_assign_single_p (stmt))
+ {
+ tree rhs = gimple_assign_rhs1 (stmt);
+ if (rhs == name)
+ ret = true;
+ else if (TREE_CODE (rhs) == ADDR_EXPR)
+ {
+ do
+ {
+ rhs = TREE_OPERAND (rhs, 0);
+ }
+ while (handled_component_p (rhs));
+ if (INDIRECT_REF_P (rhs) && TREE_OPERAND (rhs, 0) == name)
+ ret = true;
+ }
+ }
+ else if (gimple_code (stmt) == GIMPLE_RETURN)
+ {
+ tree t = gimple_return_retval (stmt);
+ if (t == name)
+ ret = true;
+ }
+ else if (is_gimple_call (stmt))
+ {
+ unsigned i;
+ for (i = 0; i < gimple_call_num_args (stmt); i++)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ if (arg == name)
+ {
+ ret = true;
+ break;
+ }
+ }
+ }
+ else if (!is_gimple_debug (stmt))
+ ret = true;
+
+ if (ret)
+ BREAK_FROM_IMM_USE_STMT (ui);
+ }
+
+ return ret;
+}
+
+/* Identify candidates for reduction for IPA-SRA based on their type and mark
+ them in candidate_bitmap. Note that these do not necessarily include
+ parameter which are unused and thus can be removed. Return true iff any
+ such candidate has been found. */
+
+static bool
+find_param_candidates (void)
+{
+ tree parm;
+ int count = 0;
+ bool ret = false;
+
+ for (parm = DECL_ARGUMENTS (current_function_decl);
+ parm;
+ parm = TREE_CHAIN (parm))
+ {
+ tree type = TREE_TYPE (parm);
+
+ count++;
+
+ if (TREE_THIS_VOLATILE (parm)
+ || TREE_ADDRESSABLE (parm)
+ || is_va_list_type (type))
+ continue;
+
+ if (is_unused_scalar_param (parm))
+ {
+ ret = true;
+ continue;
+ }
+
+ if (POINTER_TYPE_P (type))
+ {
+ type = TREE_TYPE (type);
+
+ if (TREE_CODE (type) == FUNCTION_TYPE
+ || TYPE_VOLATILE (type)
+ || !is_gimple_reg (parm)
+ || is_va_list_type (type)
+ || ptr_parm_has_direct_uses (parm))
+ continue;
+ }
+ else if (!AGGREGATE_TYPE_P (type))
+ continue;
+
+ if (!COMPLETE_TYPE_P (type)
+ || !host_integerp (TYPE_SIZE (type), 1)
+ || tree_low_cst (TYPE_SIZE (type), 1) == 0
+ || (AGGREGATE_TYPE_P (type)
+ && type_internals_preclude_sra_p (type)))
+ continue;
+
+ bitmap_set_bit (candidate_bitmap, DECL_UID (parm));
+ ret = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Candidate (%d): ", DECL_UID (parm));
+ print_generic_expr (dump_file, parm, 0);
+ fprintf (dump_file, "\n");
+ }
+ }
+
+ func_param_count = count;
+ return ret;
+}
+
+/* Callback of walk_aliased_vdefs, marks the access passed as DATA as
+ maybe_modified. */
+
+static bool
+mark_maybe_modified (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct access *repr = (struct access *) data;
+
+ repr->grp_maybe_modified = 1;
+ return true;
+}
+
+/* Analyze what representatives (in linked lists accessible from
+ REPRESENTATIVES) can be modified by side effects of statements in the
+ current function. */
+
+static void
+analyze_modified_params (VEC (access_p, heap) *representatives)
+{
+ int i;
+
+ for (i = 0; i < func_param_count; i++)
+ {
+ struct access *repr;
+
+ for (repr = VEC_index (access_p, representatives, i);
+ repr;
+ repr = repr->next_grp)
+ {
+ struct access *access;
+ bitmap visited;
+ ao_ref ar;
+
+ if (no_accesses_p (repr))
+ continue;
+ if (!POINTER_TYPE_P (TREE_TYPE (repr->base))
+ || repr->grp_maybe_modified)
+ continue;
+
+ ao_ref_init (&ar, repr->expr);
+ visited = BITMAP_ALLOC (NULL);
+ for (access = repr; access; access = access->next_sibling)
+ {
+ /* All accesses are read ones, otherwise grp_maybe_modified would
+ be trivially set. */
+ walk_aliased_vdefs (&ar, gimple_vuse (access->stmt),
+ mark_maybe_modified, repr, &visited);
+ if (repr->grp_maybe_modified)
+ break;
+ }
+ BITMAP_FREE (visited);
+ }
+ }
+}
+
+/* Propagate distances in bb_dereferences in the opposite direction than the
+ control flow edges, in each step storing the maximum of the current value
+ and the minimum of all successors. These steps are repeated until the table
+ stabilizes. Note that BBs which might terminate the functions (according to
+ final_bbs bitmap) never updated in this way. */
+
+static void
+propagate_dereference_distances (void)
+{
+ VEC (basic_block, heap) *queue;
+ basic_block bb;
+
+ queue = VEC_alloc (basic_block, heap, last_basic_block_for_function (cfun));
+ VEC_quick_push (basic_block, queue, ENTRY_BLOCK_PTR);
+ FOR_EACH_BB (bb)
+ {
+ VEC_quick_push (basic_block, queue, bb);
+ bb->aux = bb;
+ }
+
+ while (!VEC_empty (basic_block, queue))
+ {
+ edge_iterator ei;
+ edge e;
+ bool change = false;
+ int i;
+
+ bb = VEC_pop (basic_block, queue);
+ bb->aux = NULL;
+
+ if (bitmap_bit_p (final_bbs, bb->index))
+ continue;
+
+ for (i = 0; i < func_param_count; i++)
+ {
+ int idx = bb->index * func_param_count + i;
+ bool first = true;
+ HOST_WIDE_INT inh = 0;
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ int succ_idx = e->dest->index * func_param_count + i;
+
+ if (e->src == EXIT_BLOCK_PTR)
+ continue;
+
+ if (first)
+ {
+ first = false;
+ inh = bb_dereferences [succ_idx];
+ }
+ else if (bb_dereferences [succ_idx] < inh)
+ inh = bb_dereferences [succ_idx];
+ }
+
+ if (!first && bb_dereferences[idx] < inh)
+ {
+ bb_dereferences[idx] = inh;
+ change = true;
+ }
+ }
+
+ if (change && !bitmap_bit_p (final_bbs, bb->index))
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ if (e->src->aux)
+ continue;
+
+ e->src->aux = e->src;
+ VEC_quick_push (basic_block, queue, e->src);
+ }
+ }
+
+ VEC_free (basic_block, heap, queue);
+}
+
+/* Dump a dereferences TABLE with heading STR to file F. */
+
+static void
+dump_dereferences_table (FILE *f, const char *str, HOST_WIDE_INT *table)
+{
+ basic_block bb;
+
+ fprintf (dump_file, str);
+ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
+ {
+ fprintf (f, "%4i %i ", bb->index, bitmap_bit_p (final_bbs, bb->index));
+ if (bb != EXIT_BLOCK_PTR)
+ {
+ int i;
+ for (i = 0; i < func_param_count; i++)
+ {
+ int idx = bb->index * func_param_count + i;
+ fprintf (f, " %4" HOST_WIDE_INT_PRINT "d", table[idx]);
+ }
+ }
+ fprintf (f, "\n");
+ }
+ fprintf (dump_file, "\n");
+}
+
+/* Determine what (parts of) parameters passed by reference that are not
+ assigned to are not certainly dereferenced in this function and thus the
+ dereferencing cannot be safely moved to the caller without potentially
+ introducing a segfault. Mark such REPRESENTATIVES as
+ grp_not_necessarilly_dereferenced.
+
+ The dereferenced maximum "distance," i.e. the offset + size of the accessed
+ part is calculated rather than simple booleans are calculated for each
+ pointer parameter to handle cases when only a fraction of the whole
+ aggregate is allocated (see testsuite/gcc.c-torture/execute/ipa-sra-2.c for
+ an example).
+
+ The maximum dereference distances for each pointer parameter and BB are
+ already stored in bb_dereference. This routine simply propagates these
+ values upwards by propagate_dereference_distances and then compares the
+ distances of individual parameters in the ENTRY BB to the equivalent
+ distances of each representative of a (fraction of a) parameter. */
+
+static void
+analyze_caller_dereference_legality (VEC (access_p, heap) *representatives)
+{
+ int i;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_dereferences_table (dump_file,
+ "Dereference table before propagation:\n",
+ bb_dereferences);
+
+ propagate_dereference_distances ();
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ dump_dereferences_table (dump_file,
+ "Dereference table after propagation:\n",
+ bb_dereferences);
+
+ for (i = 0; i < func_param_count; i++)
+ {
+ struct access *repr = VEC_index (access_p, representatives, i);
+ int idx = ENTRY_BLOCK_PTR->index * func_param_count + i;
+
+ if (!repr || no_accesses_p (repr))
+ continue;
+
+ do
+ {
+ if ((repr->offset + repr->size) > bb_dereferences[idx])
+ repr->grp_not_necessarilly_dereferenced = 1;
+ repr = repr->next_grp;
+ }
+ while (repr);
+ }
+}
+
+/* Return the representative access for the parameter declaration PARM if it is
+ a scalar passed by reference which is not written to and the pointer value
+ is not used directly. Thus, if it is legal to dereference it in the caller
+ and we can rule out modifications through aliases, such parameter should be
+ turned into one passed by value. Return NULL otherwise. */
+
+static struct access *
+unmodified_by_ref_scalar_representative (tree parm)
+{
+ int i, access_count;
+ struct access *repr;
+ VEC (access_p, heap) *access_vec;
+
+ access_vec = get_base_access_vector (parm);
+ gcc_assert (access_vec);
+ repr = VEC_index (access_p, access_vec, 0);
+ if (repr->write)
+ return NULL;
+ repr->group_representative = repr;
+
+ access_count = VEC_length (access_p, access_vec);
+ for (i = 1; i < access_count; i++)
+ {
+ struct access *access = VEC_index (access_p, access_vec, i);
+ if (access->write)
+ return NULL;
+ access->group_representative = repr;
+ access->next_sibling = repr->next_sibling;
+ repr->next_sibling = access;
+ }
+
+ repr->grp_read = 1;
+ repr->grp_scalar_ptr = 1;
+ return repr;
+}
+
+/* Return true iff this access precludes IPA-SRA of the parameter it is
+ associated with. */
+
+static bool
+access_precludes_ipa_sra_p (struct access *access)
+{
+ /* Avoid issues such as the second simple testcase in PR 42025. The problem
+ is incompatible assign in a call statement (and possibly even in asm
+ statements). This can be relaxed by using a new temporary but only for
+ non-TREE_ADDRESSABLE types and is probably not worth the complexity. (In
+ intraprocedural SRA we deal with this by keeping the old aggregate around,
+ something we cannot do in IPA-SRA.) */
+ if (access->write
+ && (is_gimple_call (access->stmt)
+ || gimple_code (access->stmt) == GIMPLE_ASM))
+ return true;
+
+ return false;
+}
+
+
+/* Sort collected accesses for parameter PARM, identify representatives for
+ each accessed region and link them together. Return NULL if there are
+ different but overlapping accesses, return the special ptr value meaning
+ there are no accesses for this parameter if that is the case and return the
+ first representative otherwise. Set *RO_GRP if there is a group of accesses
+ with only read (i.e. no write) accesses. */
+
+static struct access *
+splice_param_accesses (tree parm, bool *ro_grp)
+{
+ int i, j, access_count, group_count;
+ int agg_size, total_size = 0;
+ struct access *access, *res, **prev_acc_ptr = &res;
+ VEC (access_p, heap) *access_vec;
+
+ access_vec = get_base_access_vector (parm);
+ if (!access_vec)
+ return &no_accesses_representant;
+ access_count = VEC_length (access_p, access_vec);
+
+ qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
+ compare_access_positions);
+
+ i = 0;
+ total_size = 0;
+ group_count = 0;
+ while (i < access_count)
+ {
+ bool modification;
+ access = VEC_index (access_p, access_vec, i);
+ modification = access->write;
+ if (access_precludes_ipa_sra_p (access))
+ return NULL;
+
+ /* Access is about to become group representative unless we find some
+ nasty overlap which would preclude us from breaking this parameter
+ apart. */
+
+ j = i + 1;
+ while (j < access_count)
+ {
+ struct access *ac2 = VEC_index (access_p, access_vec, j);
+ if (ac2->offset != access->offset)
+ {
+ /* All or nothing law for parameters. */
+ if (access->offset + access->size > ac2->offset)
+ return NULL;
+ else
+ break;
+ }
+ else if (ac2->size != access->size)
+ return NULL;
+
+ if (access_precludes_ipa_sra_p (ac2))
+ return NULL;
+
+ modification |= ac2->write;
+ ac2->group_representative = access;
+ ac2->next_sibling = access->next_sibling;
+ access->next_sibling = ac2;
+ j++;
+ }
+
+ group_count++;
+ access->grp_maybe_modified = modification;
+ if (!modification)
+ *ro_grp = true;
+ *prev_acc_ptr = access;
+ prev_acc_ptr = &access->next_grp;
+ total_size += access->size;
+ i = j;
+ }
+
+ if (POINTER_TYPE_P (TREE_TYPE (parm)))
+ agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))), 1);
+ else
+ agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (parm)), 1);
+ if (total_size >= agg_size)
+ return NULL;
+
+ gcc_assert (group_count > 0);
+ return res;
+}
+
+/* Decide whether parameters with representative accesses given by REPR should
+ be reduced into components. */
+
+static int
+decide_one_param_reduction (struct access *repr)
+{
+ int total_size, cur_parm_size, agg_size, new_param_count, parm_size_limit;
+ bool by_ref;
+ tree parm;
+
+ parm = repr->base;
+ cur_parm_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (parm)), 1);
+ gcc_assert (cur_parm_size > 0);
+
+ if (POINTER_TYPE_P (TREE_TYPE (parm)))
+ {
+ by_ref = true;
+ agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))), 1);
+ }
+ else
+ {
+ by_ref = false;
+ agg_size = cur_parm_size;
+ }
+
+ if (dump_file)
+ {
+ struct access *acc;
+ fprintf (dump_file, "Evaluating PARAM group sizes for ");
+ print_generic_expr (dump_file, parm, 0);
+ fprintf (dump_file, " (UID: %u): \n", DECL_UID (parm));
+ for (acc = repr; acc; acc = acc->next_grp)
+ dump_access (dump_file, acc, true);
+ }
+
+ total_size = 0;
+ new_param_count = 0;
+
+ for (; repr; repr = repr->next_grp)
+ {
+ gcc_assert (parm == repr->base);
+ new_param_count++;
+
+ if (!by_ref || (!repr->grp_maybe_modified
+ && !repr->grp_not_necessarilly_dereferenced))
+ total_size += repr->size;
+ else
+ total_size += cur_parm_size;
+ }
+
+ gcc_assert (new_param_count > 0);
+
+ if (optimize_function_for_size_p (cfun))
+ parm_size_limit = cur_parm_size;
+ else
+ parm_size_limit = (PARAM_VALUE (PARAM_IPA_SRA_PTR_GROWTH_FACTOR)
+ * cur_parm_size);
+
+ if (total_size < agg_size
+ && total_size <= parm_size_limit)
+ {
+ if (dump_file)
+ fprintf (dump_file, " ....will be split into %i components\n",
+ new_param_count);
+ return new_param_count;
+ }
+ else
+ return 0;
+}
+
+/* The order of the following enums is important, we need to do extra work for
+ UNUSED_PARAMS, BY_VAL_ACCESSES and UNMODIF_BY_REF_ACCESSES. */
+enum ipa_splicing_result { NO_GOOD_ACCESS, UNUSED_PARAMS, BY_VAL_ACCESSES,
+ MODIF_BY_REF_ACCESSES, UNMODIF_BY_REF_ACCESSES };
+
+/* Identify representatives of all accesses to all candidate parameters for
+ IPA-SRA. Return result based on what representatives have been found. */
+
+static enum ipa_splicing_result
+splice_all_param_accesses (VEC (access_p, heap) **representatives)
+{
+ enum ipa_splicing_result result = NO_GOOD_ACCESS;
+ tree parm;
+ struct access *repr;
+
+ *representatives = VEC_alloc (access_p, heap, func_param_count);
+
+ for (parm = DECL_ARGUMENTS (current_function_decl);
+ parm;
+ parm = TREE_CHAIN (parm))
+ {
+ if (is_unused_scalar_param (parm))
+ {
+ VEC_quick_push (access_p, *representatives,
+ &no_accesses_representant);
+ if (result == NO_GOOD_ACCESS)
+ result = UNUSED_PARAMS;
+ }
+ else if (POINTER_TYPE_P (TREE_TYPE (parm))
+ && is_gimple_reg_type (TREE_TYPE (TREE_TYPE (parm)))
+ && bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+ {
+ repr = unmodified_by_ref_scalar_representative (parm);
+ VEC_quick_push (access_p, *representatives, repr);
+ if (repr)
+ result = UNMODIF_BY_REF_ACCESSES;
+ }
+ else if (bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+ {
+ bool ro_grp = false;
+ repr = splice_param_accesses (parm, &ro_grp);
+ VEC_quick_push (access_p, *representatives, repr);
+
+ if (repr && !no_accesses_p (repr))
+ {
+ if (POINTER_TYPE_P (TREE_TYPE (parm)))
+ {
+ if (ro_grp)
+ result = UNMODIF_BY_REF_ACCESSES;
+ else if (result < MODIF_BY_REF_ACCESSES)
+ result = MODIF_BY_REF_ACCESSES;
+ }
+ else if (result < BY_VAL_ACCESSES)
+ result = BY_VAL_ACCESSES;
+ }
+ else if (no_accesses_p (repr) && (result == NO_GOOD_ACCESS))
+ result = UNUSED_PARAMS;
+ }
+ else
+ VEC_quick_push (access_p, *representatives, NULL);
+ }
+
+ if (result == NO_GOOD_ACCESS)
+ {
+ VEC_free (access_p, heap, *representatives);
+ *representatives = NULL;
+ return NO_GOOD_ACCESS;
+ }
+
+ return result;
+}
+
+/* Return the index of BASE in PARMS. Abort if it is not found. */
+
+static inline int
+get_param_index (tree base, VEC(tree, heap) *parms)
+{
+ int i, len;
+
+ len = VEC_length (tree, parms);
+ for (i = 0; i < len; i++)
+ if (VEC_index (tree, parms, i) == base)
+ return i;
+ gcc_unreachable ();
+}
+
+/* Convert the decisions made at the representative level into compact
+ parameter adjustments. REPRESENTATIVES are pointers to first
+ representatives of each param accesses, ADJUSTMENTS_COUNT is the expected
+ final number of adjustments. */
+
+static ipa_parm_adjustment_vec
+turn_representatives_into_adjustments (VEC (access_p, heap) *representatives,
+ int adjustments_count)
+{
+ VEC (tree, heap) *parms;
+ ipa_parm_adjustment_vec adjustments;
+ tree parm;
+ int i;
+
+ gcc_assert (adjustments_count > 0);
+ parms = ipa_get_vector_of_formal_parms (current_function_decl);
+ adjustments = VEC_alloc (ipa_parm_adjustment_t, heap, adjustments_count);
+ parm = DECL_ARGUMENTS (current_function_decl);
+ for (i = 0; i < func_param_count; i++, parm = TREE_CHAIN (parm))
+ {
+ struct access *repr = VEC_index (access_p, representatives, i);
+
+ if (!repr || no_accesses_p (repr))
+ {
+ struct ipa_parm_adjustment *adj;
+
+ adj = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL);
+ memset (adj, 0, sizeof (*adj));
+ adj->base_index = get_param_index (parm, parms);
+ adj->base = parm;
+ if (!repr)
+ adj->copy_param = 1;
+ else
+ adj->remove_param = 1;
+ }
+ else
+ {
+ struct ipa_parm_adjustment *adj;
+ int index = get_param_index (parm, parms);
+
+ for (; repr; repr = repr->next_grp)
+ {
+ adj = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL);
+ memset (adj, 0, sizeof (*adj));
+ gcc_assert (repr->base == parm);
+ adj->base_index = index;
+ adj->base = repr->base;
+ adj->type = repr->type;
+ adj->offset = repr->offset;
+ adj->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
+ && (repr->grp_maybe_modified
+ || repr->grp_not_necessarilly_dereferenced));
+
+ }
+ }
+ }
+ VEC_free (tree, heap, parms);
+ return adjustments;
+}
+
+/* Analyze the collected accesses and produce a plan what to do with the
+ parameters in the form of adjustments, NULL meaning nothing. */
+
+static ipa_parm_adjustment_vec
+analyze_all_param_acesses (void)
+{
+ enum ipa_splicing_result repr_state;
+ bool proceed = false;
+ int i, adjustments_count = 0;
+ VEC (access_p, heap) *representatives;
+ ipa_parm_adjustment_vec adjustments;
+
+ repr_state = splice_all_param_accesses (&representatives);
+ if (repr_state == NO_GOOD_ACCESS)
+ return NULL;
+
+ /* If there are any parameters passed by reference which are not modified
+ directly, we need to check whether they can be modified indirectly. */
+ if (repr_state == UNMODIF_BY_REF_ACCESSES)
+ {
+ analyze_caller_dereference_legality (representatives);
+ analyze_modified_params (representatives);
+ }
+
+ for (i = 0; i < func_param_count; i++)
+ {
+ struct access *repr = VEC_index (access_p, representatives, i);
+
+ if (repr && !no_accesses_p (repr))
+ {
+ if (repr->grp_scalar_ptr)
+ {
+ adjustments_count++;
+ if (repr->grp_not_necessarilly_dereferenced
+ || repr->grp_maybe_modified)
+ VEC_replace (access_p, representatives, i, NULL);
+ else
+ {
+ proceed = true;
+ sra_stats.scalar_by_ref_to_by_val++;
+ }
+ }
+ else
+ {
+ int new_components = decide_one_param_reduction (repr);
+
+ if (new_components == 0)
+ {
+ VEC_replace (access_p, representatives, i, NULL);
+ adjustments_count++;
+ }
+ else
+ {
+ adjustments_count += new_components;
+ sra_stats.aggregate_params_reduced++;
+ sra_stats.param_reductions_created += new_components;
+ proceed = true;
+ }
+ }
+ }
+ else
+ {
+ if (no_accesses_p (repr))
+ {
+ proceed = true;
+ sra_stats.deleted_unused_parameters++;
+ }
+ adjustments_count++;
+ }
+ }
+
+ if (!proceed && dump_file)
+ fprintf (dump_file, "NOT proceeding to change params.\n");
+
+ if (proceed)
+ adjustments = turn_representatives_into_adjustments (representatives,
+ adjustments_count);
+ else
+ adjustments = NULL;
+
+ VEC_free (access_p, heap, representatives);
+ return adjustments;
+}
+
+/* If a parameter replacement identified by ADJ does not yet exist in the form
+ of declaration, create it and record it, otherwise return the previously
+ created one. */
+
+static tree
+get_replaced_param_substitute (struct ipa_parm_adjustment *adj)
+{
+ tree repl;
+ if (!adj->new_ssa_base)
+ {
+ char *pretty_name = make_fancy_name (adj->base);
+
+ repl = create_tmp_var (TREE_TYPE (adj->base), "ISR");
+ if (TREE_CODE (TREE_TYPE (repl)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (repl)) == VECTOR_TYPE)
+ DECL_GIMPLE_REG_P (repl) = 1;
+ DECL_NAME (repl) = get_identifier (pretty_name);
+ obstack_free (&name_obstack, pretty_name);
+
+ get_var_ann (repl);
+ add_referenced_var (repl);
+ adj->new_ssa_base = repl;
+ }
+ else
+ repl = adj->new_ssa_base;
+ return repl;
+}
+
+/* Find the first adjustment for a particular parameter BASE in a vector of
+ ADJUSTMENTS which is not a copy_param. Return NULL if there is no such
+ adjustment. */
+
+static struct ipa_parm_adjustment *
+get_adjustment_for_base (ipa_parm_adjustment_vec adjustments, tree base)
+{
+ int i, len;
+
+ len = VEC_length (ipa_parm_adjustment_t, adjustments);
+ for (i = 0; i < len; i++)
+ {
+ struct ipa_parm_adjustment *adj;
+
+ adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+ if (!adj->copy_param && adj->base == base)
+ return adj;
+ }
+
+ return NULL;
+}
+
+/* Callback for scan_function. If the statement STMT defines an SSA_NAME of a
+ parameter which is to be removed because its value is not used, replace the
+ SSA_NAME with a one relating to a created VAR_DECL and replace all of its
+ uses too and return true (update_stmt is then issued for the statement by
+ the caller). DATA is a pointer to an adjustments vector. */
+
+static bool
+replace_removed_params_ssa_names (gimple stmt, void *data)
+{
+ VEC (ipa_parm_adjustment_t, heap) *adjustments;
+ struct ipa_parm_adjustment *adj;
+ tree lhs, decl, repl, name;
+
+ adjustments = (VEC (ipa_parm_adjustment_t, heap) *) data;
+ if (gimple_code (stmt) == GIMPLE_PHI)
+ lhs = gimple_phi_result (stmt);
+ else if (is_gimple_assign (stmt))
+ lhs = gimple_assign_lhs (stmt);
+ else if (is_gimple_call (stmt))
+ lhs = gimple_call_lhs (stmt);
+ else
+ gcc_unreachable ();
+
+ if (TREE_CODE (lhs) != SSA_NAME)
+ return false;
+ decl = SSA_NAME_VAR (lhs);
+ if (TREE_CODE (decl) != PARM_DECL)
+ return false;
+
+ adj = get_adjustment_for_base (adjustments, decl);
+ if (!adj)
+ return false;
+
+ repl = get_replaced_param_substitute (adj);
+ name = make_ssa_name (repl, stmt);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "replacing an SSA name of a removed param ");
+ print_generic_expr (dump_file, lhs, 0);
+ fprintf (dump_file, " with ");
+ print_generic_expr (dump_file, name, 0);
+ fprintf (dump_file, "\n");
+ }
+
+ if (is_gimple_assign (stmt))
+ gimple_assign_set_lhs (stmt, name);
+ else if (is_gimple_call (stmt))
+ gimple_call_set_lhs (stmt, name);
+ else
+ gimple_phi_set_result (stmt, name);
+
+ replace_uses_by (lhs, name);
+ return true;
+}
+
+/* Callback for scan_function and helper to sra_ipa_modify_assign. If the
+ expression *EXPR should be replaced by a reduction of a parameter, do so.
+ DATA is a pointer to a vector of adjustments. DONT_CONVERT specifies
+ whether the function should care about type incompatibility the current and
+ new expressions. If it is true, the function will leave incompatibility
+ issues to the caller.
+
+ When called directly by scan_function, DONT_CONVERT is true when the EXPR is
+ a write (LHS) expression. */
+
+static bool
+sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
+ bool dont_convert, void *data)
+{
+ ipa_parm_adjustment_vec adjustments;
+ int i, len;
+ struct ipa_parm_adjustment *adj, *cand = NULL;
+ HOST_WIDE_INT offset, size, max_size;
+ tree base, src;
+
+ adjustments = (VEC (ipa_parm_adjustment_t, heap) *) data;
+ len = VEC_length (ipa_parm_adjustment_t, adjustments);
+
+ if (TREE_CODE (*expr) == BIT_FIELD_REF
+ || TREE_CODE (*expr) == IMAGPART_EXPR
+ || TREE_CODE (*expr) == REALPART_EXPR)
+ {
+ expr = &TREE_OPERAND (*expr, 0);
+ dont_convert = false;
+ }
+
+ base = get_ref_base_and_extent (*expr, &offset, &size, &max_size);
+ if (!base || size == -1 || max_size == -1)
+ return false;
+
+ if (INDIRECT_REF_P (base))
+ base = TREE_OPERAND (base, 0);
+
+ base = get_ssa_base_param (base);
+ if (!base || TREE_CODE (base) != PARM_DECL)
+ return false;
+
+ for (i = 0; i < len; i++)
+ {
+ adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+
+ if (adj->base == base &&
+ (adj->offset == offset || adj->remove_param))
+ {
+ cand = adj;
+ break;
+ }
+ }
+ if (!cand || cand->copy_param || cand->remove_param)
+ return false;
+
+ if (cand->by_ref)
+ {
+ tree folded;
+ src = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (cand->reduction)),
+ cand->reduction);
+ folded = gimple_fold_indirect_ref (src);
+ if (folded)
+ src = folded;
+ }
+ else
+ src = cand->reduction;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "About to replace expr ");
+ print_generic_expr (dump_file, *expr, 0);
+ fprintf (dump_file, " with ");
+ print_generic_expr (dump_file, src, 0);
+ fprintf (dump_file, "\n");
+ }
+
+ if (!dont_convert
+ && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type))
+ {
+ tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src);
+ *expr = vce;
+ }
+ else
+ *expr = src;
+ return true;
+}
+
+/* Callback for scan_function to process assign statements. Performs
+ essentially the same function like sra_ipa_modify_expr. */
+
+static enum scan_assign_result
+sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi, void *data)
+{
+ gimple stmt = *stmt_ptr;
+ tree *lhs_p, *rhs_p;
+ bool any;
+
+ if (!gimple_assign_single_p (stmt))
+ return SRA_SA_NONE;
+
+ rhs_p = gimple_assign_rhs1_ptr (stmt);
+ lhs_p = gimple_assign_lhs_ptr (stmt);
+
+ any = sra_ipa_modify_expr (rhs_p, gsi, true, data);
+ any |= sra_ipa_modify_expr (lhs_p, gsi, true, data);
+ if (any)
+ {
+ tree new_rhs = NULL_TREE;
+
+ if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p)))
+ new_rhs = fold_build1_loc (gimple_location (stmt), VIEW_CONVERT_EXPR,
+ TREE_TYPE (*lhs_p), *rhs_p);
+ else if (REFERENCE_CLASS_P (*rhs_p)
+ && is_gimple_reg_type (TREE_TYPE (*lhs_p))
+ && !is_gimple_reg (*lhs_p))
+ /* This can happen when an assignment in between two single field
+ structures is turned into an assignment in between two pointers to
+ scalars (PR 42237). */
+ new_rhs = *rhs_p;
+
+ if (new_rhs)
+ {
+ tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE,
+ true, GSI_SAME_STMT);
+
+ gimple_assign_set_rhs_from_tree (gsi, tmp);
+ }
+
+ return SRA_SA_PROCESSED;
+ }
+
+ return SRA_SA_NONE;
+}
+
+/* Call gimple_debug_bind_reset_value on all debug statements describing
+ gimple register parameters that are being removed or replaced. */
+
+static void
+sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
+{
+ int i, len;
+
+ len = VEC_length (ipa_parm_adjustment_t, adjustments);
+ for (i = 0; i < len; i++)
+ {
+ struct ipa_parm_adjustment *adj;
+ imm_use_iterator ui;
+ gimple stmt;
+ tree name;
+
+ adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+ if (adj->copy_param || !is_gimple_reg (adj->base))
+ continue;
+ name = gimple_default_def (cfun, adj->base);
+ if (!name)
+ continue;
+ FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+ {
+ /* All other users must have been removed by scan_function. */
+ gcc_assert (is_gimple_debug (stmt));
+ gimple_debug_bind_reset_value (stmt);
+ update_stmt (stmt);
+ }
+ }
+}
+
+/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS. */
+
+static void
+convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
+{
+ tree old_cur_fndecl = current_function_decl;
+ struct cgraph_edge *cs;
+ basic_block this_block;
+ bitmap recomputed_callers = BITMAP_ALLOC (NULL);
+
+ for (cs = node->callers; cs; cs = cs->next_caller)
+ {
+ current_function_decl = cs->caller->decl;
+ push_cfun (DECL_STRUCT_FUNCTION (cs->caller->decl));
+
+ if (dump_file)
+ fprintf (dump_file, "Adjusting call (%i -> %i) %s -> %s\n",
+ cs->caller->uid, cs->callee->uid,
+ cgraph_node_name (cs->caller),
+ cgraph_node_name (cs->callee));
+
+ ipa_modify_call_arguments (cs, cs->call_stmt, adjustments);
+
+ pop_cfun ();
+ }
+
+ for (cs = node->callers; cs; cs = cs->next_caller)
+ if (!bitmap_bit_p (recomputed_callers, cs->caller->uid))
+ {
+ compute_inline_parameters (cs->caller);
+ bitmap_set_bit (recomputed_callers, cs->caller->uid);
+ }
+ BITMAP_FREE (recomputed_callers);
+
+ current_function_decl = old_cur_fndecl;
+ FOR_EACH_BB (this_block)
+ {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ tree call_fndecl;
+ if (gimple_code (stmt) != GIMPLE_CALL)
+ continue;
+ call_fndecl = gimple_call_fndecl (stmt);
+ if (call_fndecl && cgraph_get_node (call_fndecl) == node)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Adjusting recursive call");
+ ipa_modify_call_arguments (NULL, stmt, adjustments);
+ }
+ }
+ }
+
+ return;
+}
+
+/* Perform all the modification required in IPA-SRA for NODE to have parameters
+ as given in ADJUSTMENTS. */
+
+static void
+modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
+{
+ struct cgraph_node *alias;
+ for (alias = node->same_body; alias; alias = alias->next)
+ ipa_modify_formal_parameters (alias->decl, adjustments, "ISRA");
+ /* current_function_decl must be handled last, after same_body aliases,
+ as following functions will use what it computed. */
+ ipa_modify_formal_parameters (current_function_decl, adjustments, "ISRA");
+ scan_function (sra_ipa_modify_expr, sra_ipa_modify_assign,
+ replace_removed_params_ssa_names, false, adjustments);
+ sra_ipa_reset_debug_stmts (adjustments);
+ convert_callers (node, adjustments);
+ cgraph_make_node_local (node);
+ return;
+}
+
+/* Return false the function is apparently unsuitable for IPA-SRA based on it's
+ attributes, return true otherwise. NODE is the cgraph node of the current
+ function. */
+
+static bool
+ipa_sra_preliminary_function_checks (struct cgraph_node *node)
+{
+ if (!cgraph_node_can_be_local_p (node))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function not local to this compilation unit.\n");
+ return false;
+ }
+
+ if (DECL_VIRTUAL_P (current_function_decl))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function is a virtual method.\n");
+ return false;
+ }
+
+ if ((DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl))
+ && node->global.size >= MAX_INLINE_INSNS_AUTO)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function too big to be made truly local.\n");
+ return false;
+ }
+
+ if (!node->callers)
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Function has no callers in this compilation unit.\n");
+ return false;
+ }
+
+ if (cfun->stdarg)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function uses stdarg. \n");
+ return false;
+ }
+
+ return true;
+}
+
+/* Perform early interprocedural SRA. */
+
+static unsigned int
+ipa_early_sra (void)
+{
+ struct cgraph_node *node = cgraph_node (current_function_decl);
+ ipa_parm_adjustment_vec adjustments;
+ int ret = 0;
+
+ if (!ipa_sra_preliminary_function_checks (node))
+ return 0;
+
+ sra_initialize ();
+ sra_mode = SRA_MODE_EARLY_IPA;
+
+ if (!find_param_candidates ())
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function has no IPA-SRA candidates.\n");
+ goto simple_out;
+ }
+
+ bb_dereferences = XCNEWVEC (HOST_WIDE_INT,
+ func_param_count
+ * last_basic_block_for_function (cfun));
+ final_bbs = BITMAP_ALLOC (NULL);
+
+ scan_function (build_access_from_expr, build_accesses_from_assign,
+ NULL, true, NULL);
+ if (encountered_apply_args)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function calls __builtin_apply_args().\n");
+ goto out;
+ }
+
+ adjustments = analyze_all_param_acesses ();
+ if (!adjustments)
+ goto out;
+ if (dump_file)
+ ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl);
+
+ modify_function (node, adjustments);
+ VEC_free (ipa_parm_adjustment_t, heap, adjustments);
+ ret = TODO_update_ssa;
+
+ statistics_counter_event (cfun, "Unused parameters deleted",
+ sra_stats.deleted_unused_parameters);
+ statistics_counter_event (cfun, "Scalar parameters converted to by-value",
+ sra_stats.scalar_by_ref_to_by_val);
+ statistics_counter_event (cfun, "Aggregate parameters broken up",
+ sra_stats.aggregate_params_reduced);
+ statistics_counter_event (cfun, "Aggregate parameter components created",
+ sra_stats.param_reductions_created);
+
+ out:
+ BITMAP_FREE (final_bbs);
+ free (bb_dereferences);
+ simple_out:
+ sra_deinitialize ();
+ return ret;
+}
+
+/* Return if early ipa sra shall be performed. */
+static bool
+ipa_early_sra_gate (void)
+{
+ return flag_ipa_sra;
+}
+
+struct gimple_opt_pass pass_early_ipa_sra =
+{
+ {
+ GIMPLE_PASS,
+ "eipa_sra", /* name */
+ ipa_early_sra_gate, /* gate */
+ ipa_early_sra, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_IPA_SRA, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func | TODO_dump_cgraph /* todo_flags_finish */
+ }
+};
+
+