#include "tm.h"
#include "tree.h"
#include "gimple.h"
+#include "cgraph.h"
#include "tree-flow.h"
#include "ipa-prop.h"
#include "diagnostic.h"
#include "flags.h"
/* Enumeration of all aggregate reductions we can do. */
-enum sra_mode { SRA_MODE_EARLY_INTRA, /* early intraprocedural SRA */
- SRA_MODE_INTRA }; /* late intraprocedural SRA */
+enum sra_mode { SRA_MODE_EARLY_IPA, /* early call regularization */
+ SRA_MODE_EARLY_INTRA, /* early intraprocedural SRA */
+ SRA_MODE_INTRA }; /* late intraprocedural SRA */
/* Global variable describing which aggregate reduction we are performing at
the moment. */
HOST_WIDE_INT size;
tree base;
- /* Expression. */
+ /* Expression. It is context dependent so do not use it to create new
+ expressions to access the original aggregate. See PR 42154 for a
+ testcase. */
tree expr;
/* Type. */
tree type;
+ /* The statement this access belongs to. */
+ gimple stmt;
+
/* Next group representative for this aggregate. */
struct access *next_grp;
points to the first one. */
struct access *first_child;
- /* Pointer to the next sibling in the access tree as described above. */
+ /* In intraprocedural SRA, pointer to the next sibling in the access tree as
+ described above. In IPA-SRA this is a pointer to the next access
+ belonging to the same group (having the same representative). */
struct access *next_sibling;
/* Pointers to the first and last element in the linked list of assign
/* Is this access currently in the work queue? */
unsigned grp_queued : 1;
+
/* Does this group contain a write access? This flag is propagated down the
access tree. */
unsigned grp_write : 1;
+
/* Does this group contain a read access? This flag is propagated down the
access tree. */
unsigned grp_read : 1;
+
/* Other passes of the analysis use this bit to make function
analyze_access_subtree create scalar replacements for this group if
possible. */
unsigned grp_hint : 1;
+
/* Is the subtree rooted in this access fully covered by scalar
replacements? */
unsigned grp_covered : 1;
+
/* If set to true, this access and all below it in an access tree must not be
scalarized. */
unsigned grp_unscalarizable_region : 1;
+
/* Whether data have been written to parts of the aggregate covered by this
access which is not to be scalarized. This flag is propagated up in the
access tree. */
unsigned grp_unscalarized_data : 1;
+
/* Does this access and/or group contain a write access through a
BIT_FIELD_REF? */
unsigned grp_partial_lhs : 1;
+ /* Does this group contain accesses to different types? (I.e. through a union
+ or a similar mechanism). */
+ unsigned grp_different_types : 1;
+
/* Set when a scalar replacement should be created for this variable. We do
the decision and creation at different places because create_tmp_var
cannot be called from within FOR_EACH_REFERENCED_VAR. */
unsigned grp_to_be_replaced : 1;
+
+ /* Is it possible that the group refers to data which might be (directly or
+ otherwise) modified? */
+ unsigned grp_maybe_modified : 1;
+
+ /* Set when this is a representative of a pointer to scalar (i.e. by
+ reference) parameter which we consider for turning into a plain scalar
+ (i.e. a by value parameter). */
+ unsigned grp_scalar_ptr : 1;
+
+ /* Set when we discover that this pointer is not safe to dereference in the
+ caller. */
+ unsigned grp_not_necessarilly_dereferenced : 1;
};
typedef struct access *access_p;
/* Base (tree) -> Vector (VEC(access_p,heap) *) map. */
static struct pointer_map_t *base_access_vec;
-/* Bitmap of bases (candidates). */
+/* Bitmap of candidates. */
static bitmap candidate_bitmap;
+
/* Obstack for creation of fancy names. */
static struct obstack name_obstack;
propagated to their assignment counterparts. */
static struct access *work_queue_head;
+/* Number of parameters of the analyzed function when doing early ipa SRA. */
+static int func_param_count;
+
+/* scan_function sets the following to true if it encounters a call to
+ __builtin_apply_args. */
+static bool encountered_apply_args;
+
+/* This is a table in which for each basic block and parameter there is a
+ distance (offset + size) in that parameter which is dereferenced and
+ accessed in that BB. */
+static HOST_WIDE_INT *bb_dereferences;
+/* Bitmap of BBs that can cause the function to "stop" progressing by
+ returning, throwing externally, looping infinitely or calling a function
+ which might abort etc.. */
+static bitmap final_bbs;
+
+/* Representative of no accesses at all. */
+static struct access no_accesses_representant;
+
+/* Predicate to test the special value. */
+
+static inline bool
+no_accesses_p (struct access *access)
+{
+ return access == &no_accesses_representant;
+}
+
/* Dump contents of ACCESS to file F in a human friendly way. If GRP is true,
representative fields are dumped, otherwise those which only describe the
individual access are. */
static struct
{
+ /* Number of processed aggregates is readily available in
+ analyze_all_variable_accesses and so is not stored here. */
+
/* Number of created scalar replacements. */
int replacements;
references. */
int separate_lhs_rhs_handling;
- /* Number of processed aggregates is readily available in
- analyze_all_variable_accesses and so is not stored here. */
+ /* Number of parameters that were removed because they were unused. */
+ int deleted_unused_parameters;
+
+ /* Number of scalars passed as parameters by reference that have been
+ converted to be passed by value. */
+ int scalar_by_ref_to_by_val;
+
+ /* Number of aggregate parameters that were replaced by one or more of their
+ components. */
+ int aggregate_params_reduced;
+
+ /* Numbber of components created when splitting aggregate parameters. */
+ int param_reductions_created;
} sra_stats;
static void
fprintf (f, ", grp_write = %d, grp_read = %d, grp_hint = %d, "
"grp_covered = %d, grp_unscalarizable_region = %d, "
"grp_unscalarized_data = %d, grp_partial_lhs = %d, "
- "grp_to_be_replaced = %d\n",
+ "grp_different_types = %d, grp_to_be_replaced = %d, "
+ "grp_maybe_modified = %d, "
+ "grp_not_necessarilly_dereferenced = %d\n",
access->grp_write, access->grp_read, access->grp_hint,
access->grp_covered, access->grp_unscalarizable_region,
access->grp_unscalarized_data, access->grp_partial_lhs,
- access->grp_to_be_replaced);
+ access->grp_different_types, access->grp_to_be_replaced,
+ access->grp_maybe_modified,
+ access->grp_not_necessarilly_dereferenced);
else
fprintf (f, ", write = %d, grp_partial_lhs = %d\n", access->write,
access->grp_partial_lhs);
link_pool = create_alloc_pool ("SRA links", sizeof (struct assign_link), 16);
base_access_vec = pointer_map_create ();
memset (&sra_stats, 0, sizeof (sra_stats));
+ encountered_apply_args = false;
}
/* Hook fed to pointer_map_traverse, deallocate stored vectors. */
}
}
+/* If T is an SSA_NAME, return NULL if it is not a default def or return its
+ base variable if it is. Return T if it is not an SSA_NAME. */
+
+static tree
+get_ssa_base_param (tree t)
+{
+ if (TREE_CODE (t) == SSA_NAME)
+ {
+ if (SSA_NAME_IS_DEFAULT_DEF (t))
+ return SSA_NAME_VAR (t);
+ else
+ return NULL_TREE;
+ }
+ return t;
+}
+
+/* Mark a dereference of BASE of distance DIST in a basic block tht STMT
+ belongs to, unless the BB has already been marked as a potentially
+ final. */
+
+static void
+mark_parm_dereference (tree base, HOST_WIDE_INT dist, gimple stmt)
+{
+ basic_block bb = gimple_bb (stmt);
+ int idx, parm_index = 0;
+ tree parm;
+
+ if (bitmap_bit_p (final_bbs, bb->index))
+ return;
+
+ for (parm = DECL_ARGUMENTS (current_function_decl);
+ parm && parm != base;
+ parm = TREE_CHAIN (parm))
+ parm_index++;
+
+ gcc_assert (parm_index < func_param_count);
+
+ idx = bb->index * func_param_count + parm_index;
+ if (bb_dereferences[idx] < dist)
+ bb_dereferences[idx] = dist;
+}
+
/* Create and insert access for EXPR. Return created access, or NULL if it is
not possible. */
static struct access *
-create_access (tree expr, bool write)
+create_access (tree expr, gimple stmt, bool write)
{
struct access *access;
void **slot;
VEC (access_p,heap) *vec;
HOST_WIDE_INT offset, size, max_size;
tree base = expr;
- bool unscalarizable_region = false;
+ bool ptr, unscalarizable_region = false;
base = get_ref_base_and_extent (expr, &offset, &size, &max_size);
+ if (sra_mode == SRA_MODE_EARLY_IPA && INDIRECT_REF_P (base))
+ {
+ base = get_ssa_base_param (TREE_OPERAND (base, 0));
+ if (!base)
+ return NULL;
+ ptr = true;
+ }
+ else
+ ptr = false;
+
if (!DECL_P (base) || !bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
return NULL;
- if (size != max_size)
+ if (sra_mode == SRA_MODE_EARLY_IPA)
{
- size = max_size;
- unscalarizable_region = true;
- }
+ if (size < 0 || size != max_size)
+ {
+ disqualify_candidate (base, "Encountered a variable sized access.");
+ return NULL;
+ }
+ if ((offset % BITS_PER_UNIT) != 0 || (size % BITS_PER_UNIT) != 0)
+ {
+ disqualify_candidate (base,
+ "Encountered an acces not aligned to a byte.");
+ return NULL;
+ }
- if (size < 0)
+ if (ptr)
+ mark_parm_dereference (base, offset + size, stmt);
+ }
+ else
{
- disqualify_candidate (base, "Encountered an unconstrained access.");
- return NULL;
+ if (size != max_size)
+ {
+ size = max_size;
+ unscalarizable_region = true;
+ }
+ if (size < 0)
+ {
+ disqualify_candidate (base, "Encountered an unconstrained access.");
+ return NULL;
+ }
}
access = (struct access *) pool_alloc (access_pool);
access->type = TREE_TYPE (expr);
access->write = write;
access->grp_unscalarizable_region = unscalarizable_region;
+ access->stmt = stmt;
slot = pointer_map_contains (base_access_vec, base);
if (slot)
while (handled_component_p (t))
t = TREE_OPERAND (t, 0);
- if (DECL_P (t))
+ if (sra_mode == SRA_MODE_EARLY_IPA)
+ {
+ if (INDIRECT_REF_P (t))
+ t = TREE_OPERAND (t, 0);
+ t = get_ssa_base_param (t);
+ }
+
+ if (t && DECL_P (t))
disqualify_candidate (t, reason);
}
created. */
static struct access *
-build_access_from_expr_1 (tree *expr_ptr, bool write)
+build_access_from_expr_1 (tree *expr_ptr, gimple stmt, bool write)
{
struct access *ret = NULL;
tree expr = *expr_ptr;
switch (TREE_CODE (expr))
{
+ case INDIRECT_REF:
+ if (sra_mode != SRA_MODE_EARLY_IPA)
+ return NULL;
+ /* fall through */
case VAR_DECL:
case PARM_DECL:
case RESULT_DECL:
case COMPONENT_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
- ret = create_access (expr, write);
+ ret = create_access (expr, stmt, write);
break;
default:
gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED, bool write,
void *data ATTRIBUTE_UNUSED)
{
- return build_access_from_expr_1 (expr_ptr, write) != NULL;
+ return build_access_from_expr_1 (expr_ptr, gsi_stmt (*gsi), write) != NULL;
}
/* Disqualify LHS and RHS for scalarization if STMT must end its basic block in
static bool
disqualify_ops_if_throwing_stmt (gimple stmt, tree lhs, tree rhs)
{
- if (stmt_can_throw_internal (stmt) || stmt_ends_bb_p (stmt))
+ if ((sra_mode == SRA_MODE_EARLY_INTRA || sra_mode == SRA_MODE_INTRA)
+ && (stmt_can_throw_internal (stmt) || stmt_ends_bb_p (stmt)))
{
disqualify_base_of_expr (lhs, "LHS of a throwing stmt.");
if (rhs)
if (disqualify_ops_if_throwing_stmt (stmt, *lhs_ptr, *rhs_ptr))
return SRA_SA_NONE;
- racc = build_access_from_expr_1 (rhs_ptr, false);
- lacc = build_access_from_expr_1 (lhs_ptr, true);
+ racc = build_access_from_expr_1 (rhs_ptr, stmt, false);
+ lacc = build_access_from_expr_1 (lhs_ptr, stmt, true);
if (lacc && racc
+ && (sra_mode == SRA_MODE_EARLY_INTRA || sra_mode == SRA_MODE_INTRA)
&& !lacc->grp_unscalarizable_region
&& !racc->grp_unscalarizable_region
&& AGGREGATE_TYPE_P (TREE_TYPE (*lhs_ptr))
those deemed entirely unsuitable for some reason (all operands in such
statements and expression are removed from candidate_bitmap). SCAN_ASSIGN
is a callback called on all assign statements, HANDLE_SSA_DEFS is a callback
- called on assign statements and those call statements which have a lhs and
- it is the only callback which can be NULL. ANALYSIS_STAGE is true when
- running in the analysis stage of a pass and thus no statement is being
- modified. DATA is a pointer passed to all callbacks. If any single
- callback returns true, this function also returns true, otherwise it returns
- false. */
+ called on assign statements and those call statements which have a lhs, it
+ can be NULL. ANALYSIS_STAGE is true when running in the analysis stage of a
+ pass and thus no statement is being modified. DATA is a pointer passed to
+ all callbacks. If any single callback returns true, this function also
+ returns true, otherwise it returns false. */
static bool
scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
{
bool bb_changed = false;
+ if (handle_ssa_defs)
+ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ ret |= handle_ssa_defs (gsi_stmt (gsi), data);
+
gsi = gsi_start_bb (bb);
while (!gsi_end_p (gsi))
{
enum scan_assign_result assign_result;
bool any = false, deleted = false;
+ if (analysis_stage && final_bbs && stmt_can_throw_external (stmt))
+ bitmap_set_bit (final_bbs, bb->index);
switch (gimple_code (stmt))
{
case GIMPLE_RETURN:
t = gimple_return_retval_ptr (stmt);
if (*t != NULL_TREE)
any |= scan_expr (t, &gsi, false, data);
+ if (analysis_stage && final_bbs)
+ bitmap_set_bit (final_bbs, bb->index);
break;
case GIMPLE_ASSIGN:
any |= scan_expr (argp, &gsi, false, data);
}
+ if (analysis_stage)
+ {
+ tree dest = gimple_call_fndecl (stmt);
+ int flags = gimple_call_flags (stmt);
+
+ if (dest
+ && DECL_BUILT_IN_CLASS (dest) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (dest) == BUILT_IN_APPLY_ARGS)
+ encountered_apply_args = true;
+
+ if (final_bbs
+ && (flags & (ECF_CONST | ECF_PURE)) == 0)
+ bitmap_set_bit (final_bbs, bb->index);
+ }
+
if (gimple_call_lhs (stmt))
{
tree *lhs_ptr = gimple_call_lhs_ptr (stmt);
break;
case GIMPLE_ASM:
-
if (analysis_stage)
- walk_stmt_load_store_addr_ops (stmt, NULL, NULL, NULL,
- asm_visit_addr);
+ {
+ walk_stmt_load_store_addr_ops (stmt, NULL, NULL, NULL,
+ asm_visit_addr);
+ if (final_bbs)
+ bitmap_set_bit (final_bbs, bb->index);
+ }
for (i = 0; i < gimple_asm_ninputs (stmt); i++)
{
tree *op = &TREE_VALUE (gimple_asm_input_op (stmt, i));
tree *op = &TREE_VALUE (gimple_asm_output_op (stmt, i));
any |= scan_expr (op, &gsi, true, data);
}
+ break;
default:
break;
if (any)
{
ret = true;
- bb_changed = true;
if (!analysis_stage)
{
+ bb_changed = true;
update_stmt (stmt);
- if (!stmt_could_throw_p (stmt))
- remove_stmt_from_eh_region (stmt);
+ maybe_clean_eh_stmt (stmt);
}
}
if (deleted)
ret = true;
}
}
- if (!analysis_stage && bb_changed)
+ if (!analysis_stage && bb_changed && sra_mode == SRA_MODE_EARLY_IPA)
gimple_purge_dead_eh_edges (bb);
}
{
/* Put any non-aggregate type before any aggregate type. */
if (!is_gimple_reg_type (f1->type)
- && is_gimple_reg_type (f2->type))
+ && is_gimple_reg_type (f2->type))
return 1;
else if (is_gimple_reg_type (f1->type)
&& !is_gimple_reg_type (f2->type))
return -1;
+ /* Put any complex or vector type before any other scalar type. */
+ else if (TREE_CODE (f1->type) != COMPLEX_TYPE
+ && TREE_CODE (f1->type) != VECTOR_TYPE
+ && (TREE_CODE (f2->type) == COMPLEX_TYPE
+ || TREE_CODE (f2->type) == VECTOR_TYPE))
+ return 1;
+ else if ((TREE_CODE (f1->type) == COMPLEX_TYPE
+ || TREE_CODE (f1->type) == VECTOR_TYPE)
+ && TREE_CODE (f2->type) != COMPLEX_TYPE
+ && TREE_CODE (f2->type) != VECTOR_TYPE)
+ return -1;
/* Put the integral type with the bigger precision first. */
else if (INTEGRAL_TYPE_P (f1->type)
- && INTEGRAL_TYPE_P (f2->type))
+ && INTEGRAL_TYPE_P (f2->type))
return TYPE_PRECISION (f1->type) > TYPE_PRECISION (f2->type) ? -1 : 1;
/* Put any integral type with non-full precision last. */
else if (INTEGRAL_TYPE_P (f1->type)
while (1)
{
tree fld;
- tree tr_size, index;
+ tree tr_size, index, minidx;
HOST_WIDE_INT el_size;
if (offset == 0 && exp_type
case UNION_TYPE:
case QUAL_UNION_TYPE:
case RECORD_TYPE:
- /* Some ADA records are half-unions, treat all of them the same. */
for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
{
HOST_WIDE_INT pos, size;
pos = int_bit_position (fld);
gcc_assert (TREE_CODE (type) == RECORD_TYPE || pos == 0);
- size = tree_low_cst (DECL_SIZE (fld), 1);
+ tr_size = DECL_SIZE (fld);
+ if (!tr_size || !host_integerp (tr_size, 1))
+ continue;
+ size = tree_low_cst (tr_size, 1);
if (pos > offset || (pos + size) <= offset)
continue;
return false;
el_size = tree_low_cst (tr_size, 1);
+ minidx = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
+ if (TREE_CODE (minidx) != INTEGER_CST)
+ return false;
if (res)
{
index = build_int_cst (TYPE_DOMAIN (type), offset / el_size);
- if (!integer_zerop (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
- index = int_const_binop (PLUS_EXPR, index,
- TYPE_MIN_VALUE (TYPE_DOMAIN (type)),
- 0);
+ if (!integer_zerop (minidx))
+ index = int_const_binop (PLUS_EXPR, index, minidx, 0);
*res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index,
NULL_TREE, NULL_TREE);
}
/* Construct an expression that would reference a part of aggregate *EXPR of
type TYPE at the given OFFSET of the type EXP_TYPE. If EXPR is NULL, the
function only determines whether it can build such a reference without
- actually doing it.
+ actually doing it, otherwise, the tree it points to is unshared first and
+ then used as a base for furhter sub-references.
FIXME: Eventually this should be replaced with
maybe_fold_offset_to_reference() from tree-ssa-ccp.c but that requires a
{
location_t loc = expr ? EXPR_LOCATION (*expr) : UNKNOWN_LOCATION;
+ if (expr)
+ *expr = unshare_expr (*expr);
+
if (allow_ptr && POINTER_TYPE_P (type))
{
type = TREE_TYPE (type);
return build_ref_for_offset_1 (expr, type, offset, exp_type);
}
+/* Return true iff TYPE is stdarg va_list type. */
+
+static inline bool
+is_va_list_type (tree type)
+{
+ return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node);
+}
+
/* The very first phase of intraprocedural SRA. It marks in candidate_bitmap
those with type which is suitable for scalarization. */
|| !COMPLETE_TYPE_P (type)
|| !host_integerp (TYPE_SIZE (type), 1)
|| tree_low_cst (TYPE_SIZE (type), 1) == 0
- || type_internals_preclude_sra_p (type))
+ || type_internals_preclude_sra_p (type)
+ /* Fix for PR 41089. tree-stdarg.c needs to have va_lists intact but
+ we also want to schedule it rather late. Thus we ignore it in
+ the early pass. */
+ || (sra_mode == SRA_MODE_EARLY_INTRA
+ && is_va_list_type (type)))
continue;
bitmap_set_bit (candidate_bitmap, DECL_UID (var));
bool grp_read = !access->write;
bool multiple_reads = false;
bool grp_partial_lhs = access->grp_partial_lhs;
+ bool grp_different_types = false;
bool first_scalar = is_gimple_reg_type (access->type);
bool unscalarizable_region = access->grp_unscalarizable_region;
grp_read = true;
}
grp_partial_lhs |= ac2->grp_partial_lhs;
+ grp_different_types |= !types_compatible_p (access->type, ac2->type);
unscalarizable_region |= ac2->grp_unscalarizable_region;
relink_to_new_repr (access, ac2);
access->grp_read = grp_read;
access->grp_hint = multiple_reads;
access->grp_partial_lhs = grp_partial_lhs;
+ access->grp_different_types = grp_different_types;
access->grp_unscalarizable_region = unscalarizable_region;
if (access->first_link)
add_access_to_work_queue (access);
}
}
+/* Return true if expr contains some ARRAY_REFs into a variable bounded
+ array. */
+
+static bool
+expr_with_var_bounded_array_refs_p (tree expr)
+{
+ while (handled_component_p (expr))
+ {
+ if (TREE_CODE (expr) == ARRAY_REF
+ && !host_integerp (array_ref_low_bound (expr), 0))
+ return true;
+ expr = TREE_OPERAND (expr, 0);
+ }
+ return false;
+}
+
/* Analyze the subtree of accesses rooted in ROOT, scheduling replacements when
both seeming beneficial and when ALLOW_REPLACEMENTS allows it. Also set
all sorts of access flags appropriately along the way, notably always ser
if (root->grp_unscalarizable_region)
allow_replacements = false;
+ if (allow_replacements && expr_with_var_bounded_array_refs_p (root->expr))
+ allow_replacements = false;
+
for (child = root->first_child; child; child = child->next_sibling)
{
if (!hole && child->offset < covered_to)
/* Create a new child access of PARENT, with all properties just like MODEL
except for its offset and with its grp_write false and grp_read true.
- Return the new access. Note that this access is created long after all
- splicing and sorting, it's not located in any access vector and is
- automatically a representative of its group. */
+ Return the new access or NULL if it cannot be created. Note that this access
+ is created long after all splicing and sorting, it's not located in any
+ access vector and is automatically a representative of its group. */
static struct access *
create_artificial_child_access (struct access *parent, struct access *model,
{
struct access *access;
struct access **child;
- bool ok;
+ tree expr = parent->base;;
gcc_assert (!model->grp_unscalarizable_region);
+ if (!build_ref_for_offset (&expr, TREE_TYPE (expr), new_offset,
+ model->type, false))
+ return NULL;
+
access = (struct access *) pool_alloc (access_pool);
memset (access, 0, sizeof (struct access));
access->base = parent->base;
+ access->expr = expr;
access->offset = new_offset;
access->size = model->size;
access->type = model->type;
access->grp_write = true;
access->grp_read = false;
- access->expr = access->base;
- ok = build_ref_for_offset (&access->expr, TREE_TYPE (access->expr),
- new_offset, access->type, false);
- gcc_assert (ok);
child = &parent->first_child;
while (*child && (*child)->offset < new_offset)
/* Propagate all subaccesses of RACC across an assignment link to LACC. Return
true if any new subaccess was created. Additionally, if RACC is a scalar
- access but LACC is not, change the type of the latter. */
+ access but LACC is not, change the type of the latter, if possible. */
static bool
-propagate_subacesses_accross_link (struct access *lacc, struct access *racc)
+propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
{
struct access *rchild;
HOST_WIDE_INT norm_delta = lacc->offset - racc->offset;
if (!lacc->first_child && !racc->first_child
&& is_gimple_reg_type (racc->type))
{
- bool ok;
+ tree t = lacc->base;
- lacc->expr = lacc->base;
- ok = build_ref_for_offset (&lacc->expr, TREE_TYPE (lacc->expr),
- lacc->offset, racc->type, false);
- gcc_assert (ok);
- lacc->type = racc->type;
+ if (build_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, racc->type,
+ false))
+ {
+ lacc->expr = t;
+ lacc->type = racc->type;
+ }
return false;
}
rchild->grp_hint = 1;
new_acc->grp_hint |= new_acc->grp_read;
if (rchild->first_child)
- ret |= propagate_subacesses_accross_link (new_acc, rchild);
+ ret |= propagate_subaccesses_across_link (new_acc, rchild);
}
continue;
}
rchild->grp_hint = 1;
new_acc = create_artificial_child_access (lacc, rchild, norm_offset);
- if (racc->first_child)
- propagate_subacesses_accross_link (new_acc, rchild);
-
- ret = true;
+ if (new_acc)
+ {
+ ret = true;
+ if (racc->first_child)
+ propagate_subaccesses_across_link (new_acc, rchild);
+ }
}
return ret;
if (!bitmap_bit_p (candidate_bitmap, DECL_UID (lacc->base)))
continue;
lacc = lacc->group_representative;
- if (propagate_subacesses_accross_link (lacc, racc)
+ if (propagate_subaccesses_across_link (lacc, racc)
&& lacc->first_link)
add_access_to_work_queue (lacc);
}
{
do
{
- tree expr = unshare_expr (agg);
+ tree expr = agg;
if (chunk_size && access->offset >= start_offset + chunk_size)
return;
access expression to extract the scalar component afterwards.
This happens if scalarizing a function return value or parameter
like in gcc.c-torture/execute/20041124-1.c, 20050316-1.c and
- gcc.c-torture/compile/20011217-1.c. */
- if (!is_gimple_reg_type (type))
+ gcc.c-torture/compile/20011217-1.c.
+
+ We also want to use this when accessing a complex or vector which can
+ be accessed as a different type too, potentially creating a need for
+ type conversion (see PR42196). */
+ if (!is_gimple_reg_type (type)
+ || (access->grp_different_types
+ && (TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == VECTOR_TYPE)))
{
- gimple stmt;
+ tree ref = access->base;
+ bool ok;
+
+ ok = build_ref_for_offset (&ref, TREE_TYPE (ref),
+ access->offset, access->type, false);
+ gcc_assert (ok);
+
if (write)
{
- tree ref = unshare_expr (access->expr);
+ gimple stmt;
+
if (access->grp_partial_lhs)
ref = force_gimple_operand_gsi (gsi, ref, true, NULL_TREE,
false, GSI_NEW_STMT);
}
else
{
+ gimple stmt;
+
if (access->grp_partial_lhs)
repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
true, GSI_SAME_STMT);
- stmt = gimple_build_assign (unshare_expr (access->expr), repl);
+ stmt = gimple_build_assign (ref, repl);
gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
}
}
}
else
{
- bool repl_found;
-
/* No suitable access on the right hand side, need to load from
the aggregate. See if we have to update it first... */
if (*refreshed == SRA_UDH_NONE)
lhs, old_gsi);
if (*refreshed == SRA_UDH_LEFT)
- rhs = unshare_expr (lacc->expr);
+ {
+ bool repl_found;
+
+ rhs = lacc->base;
+ repl_found = build_ref_for_offset (&rhs, TREE_TYPE (rhs),
+ lacc->offset, lacc->type,
+ false);
+ gcc_assert (repl_found);
+ }
else
{
- rhs = unshare_expr (top_racc->base);
+ bool repl_found;
+
+ rhs = top_racc->base;
repl_found = build_ref_for_offset (&rhs,
TREE_TYPE (top_racc->base),
offset, lacc->type, false);
if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))
&& !access_has_children_p (lacc))
{
- tree expr = unshare_expr (lhs);
+ tree expr = lhs;
if (build_ref_for_offset (&expr, TREE_TYPE (lhs), 0,
TREE_TYPE (rhs), false))
{
else if (AGGREGATE_TYPE_P (TREE_TYPE (rhs))
&& !access_has_children_p (racc))
{
- tree expr = unshare_expr (rhs);
+ tree expr = rhs;
if (build_ref_for_offset (&expr, TREE_TYPE (rhs), 0,
TREE_TYPE (lhs), false))
rhs = expr;
if (!analyze_all_variable_accesses ())
goto out;
- scan_function (sra_modify_expr, sra_modify_assign, NULL,
- false, NULL);
+ scan_function (sra_modify_expr, sra_modify_assign, NULL, false, NULL);
initialize_parameter_reductions ();
statistics_counter_event (cfun, "Scalar replacements created",
}
};
-
struct gimple_opt_pass pass_sra =
{
{
| TODO_verify_ssa /* todo_flags_finish */
}
};
+
+
+/* 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);
+ if (gimple_code (stmt) == GIMPLE_CALL
+ && gimple_call_fndecl (stmt) == node->decl)
+ {
+ 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)
+{
+ 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 */
+ }
+};
+
+