/* Scalar Replacement of Aggregates (SRA) converts some structure
references into scalar references, exposing them to the scalar
optimizers.
- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 2008-2013 Free Software Foundation, Inc.
Contributed by Martin Jambor <mjambor@suse.cz>
This file is part of GCC.
#include "gimple.h"
#include "cgraph.h"
#include "tree-flow.h"
+#include "tree-pass.h"
#include "ipa-prop.h"
-#include "tree-pretty-print.h"
#include "statistics.h"
-#include "tree-dump.h"
-#include "timevar.h"
#include "params.h"
#include "target.h"
#include "flags.h"
/* Is this particular access write access? */
unsigned write : 1;
- /* Is this access an artificial one created to scalarize some record
- entirely? */
- unsigned total_scalarization : 1;
-
/* Is this access an access to a non-addressable field? */
unsigned non_addressable : 1;
is not propagated in the access tree in any direction. */
unsigned grp_scalar_write : 1;
+ /* Is this access an artificial one created to scalarize some record
+ entirely? */
+ unsigned grp_total_scalarization : 1;
+
/* Other passes of the analysis use this bit to make function
analyze_access_subtree create scalar replacements for this group if
possible. */
BIT_FIELD_REF? */
unsigned grp_partial_lhs : 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. */
+ /* Set when a scalar replacement should be created for this variable. */
unsigned grp_to_be_replaced : 1;
+ /* Set when we want a replacement for the sole purpose of having it in
+ generated debug statements. */
+ unsigned grp_to_be_debug_replaced : 1;
+
/* Should TREE_NO_WARNING of a replacement be set? */
unsigned grp_no_warning : 1;
typedef struct access *access_p;
-DEF_VEC_P (access_p);
-DEF_VEC_ALLOC_P (access_p, heap);
/* Alloc pool for allocating access structures. */
static alloc_pool access_pool;
/* Alloc pool for allocating assign link structures. */
static alloc_pool link_pool;
-/* Base (tree) -> Vector (VEC(access_p,heap) *) map. */
+/* Base (tree) -> Vector (vec<access_p> *) map. */
static struct pointer_map_t *base_access_vec;
-/* Bitmap of candidates. */
+/* Set of candidates. */
static bitmap candidate_bitmap;
+static htab_t candidates;
+
+/* For a candidate UID return the candidates decl. */
+
+static inline tree
+candidate (unsigned uid)
+{
+ struct tree_decl_minimal t;
+ t.uid = uid;
+ return (tree) htab_find_with_hash (candidates, &t, uid);
+}
/* Bitmap of candidates which we should try to entirely scalarize away and
those which cannot be (because they are and need be used as a whole). */
fprintf (f, ", type = ");
print_generic_expr (f, access->type, 0);
if (grp)
- fprintf (f, ", total_scalarization = %d, grp_read = %d, grp_write = %d, "
- "grp_assignment_read = %d, grp_assignment_write = %d, "
- "grp_scalar_read = %d, grp_scalar_write = %d, "
+ fprintf (f, ", grp_read = %d, grp_write = %d, grp_assignment_read = %d, "
+ "grp_assignment_write = %d, grp_scalar_read = %d, "
+ "grp_scalar_write = %d, grp_total_scalarization = %d, "
"grp_hint = %d, grp_covered = %d, "
"grp_unscalarizable_region = %d, grp_unscalarized_data = %d, "
"grp_partial_lhs = %d, grp_to_be_replaced = %d, "
- "grp_maybe_modified = %d, "
+ "grp_to_be_debug_replaced = %d, grp_maybe_modified = %d, "
"grp_not_necessarilly_dereferenced = %d\n",
- access->total_scalarization, access->grp_read, access->grp_write,
- access->grp_assignment_read, access->grp_assignment_write,
- access->grp_scalar_read, access->grp_scalar_write,
+ access->grp_read, access->grp_write, access->grp_assignment_read,
+ access->grp_assignment_write, access->grp_scalar_read,
+ access->grp_scalar_write, access->grp_total_scalarization,
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_maybe_modified,
+ access->grp_to_be_debug_replaced, access->grp_maybe_modified,
access->grp_not_necessarilly_dereferenced);
else
- fprintf (f, ", write = %d, total_scalarization = %d, "
+ fprintf (f, ", write = %d, grp_total_scalarization = %d, "
"grp_partial_lhs = %d\n",
- access->write, access->total_scalarization,
+ access->write, access->grp_total_scalarization,
access->grp_partial_lhs);
}
return acc && acc->first_child;
}
+/* Return true iff ACC is (partly) covered by at least one replacement. */
+
+static bool
+access_has_replacements_p (struct access *acc)
+{
+ struct access *child;
+ if (acc->grp_to_be_replaced)
+ return true;
+ for (child = acc->first_child; child; child = child->next_sibling)
+ if (access_has_replacements_p (child))
+ return true;
+ return false;
+}
+
/* Return a vector of pointers to accesses for the variable given in BASE or
NULL if there is none. */
-static VEC (access_p, heap) *
+static vec<access_p> *
get_base_access_vector (tree base)
{
void **slot;
if (!slot)
return NULL;
else
- return *(VEC (access_p, heap) **) slot;
+ return *(vec<access_p> **) slot;
}
/* Find an access with required OFFSET and SIZE in a subtree of accesses rooted
static struct access *
get_first_repr_for_decl (tree base)
{
- VEC (access_p, heap) *access_vec;
+ vec<access_p> *access_vec;
access_vec = get_base_access_vector (base);
if (!access_vec)
return NULL;
- return VEC_index (access_p, access_vec, 0);
+ return (*access_vec)[0];
}
/* Find an access representative for the variable BASE and given OFFSET and
sra_initialize (void)
{
candidate_bitmap = BITMAP_ALLOC (NULL);
+ candidates = htab_create (vec_safe_length (cfun->local_decls) / 2,
+ uid_decl_map_hash, uid_decl_map_eq, NULL);
should_scalarize_away_bitmap = BITMAP_ALLOC (NULL);
cannot_scalarize_away_bitmap = BITMAP_ALLOC (NULL);
gcc_obstack_init (&name_obstack);
delete_base_accesses (const void *key ATTRIBUTE_UNUSED, void **value,
void *data ATTRIBUTE_UNUSED)
{
- VEC (access_p, heap) *access_vec;
- access_vec = (VEC (access_p, heap) *) *value;
- VEC_free (access_p, heap, access_vec);
-
+ vec<access_p> *access_vec = (vec<access_p> *) *value;
+ vec_free (access_vec);
return true;
}
sra_deinitialize (void)
{
BITMAP_FREE (candidate_bitmap);
+ htab_delete (candidates);
BITMAP_FREE (should_scalarize_away_bitmap);
BITMAP_FREE (cannot_scalarize_away_bitmap);
free_alloc_pool (access_pool);
static void
disqualify_candidate (tree decl, const char *reason)
{
- bitmap_clear_bit (candidate_bitmap, DECL_UID (decl));
+ if (bitmap_clear_bit (candidate_bitmap, DECL_UID (decl)))
+ htab_clear_slot (candidates,
+ htab_find_slot_with_hash (candidates, decl,
+ DECL_UID (decl), NO_INSERT));
if (dump_file && (dump_flags & TDF_DETAILS))
{
scalarization. */
static bool
-type_internals_preclude_sra_p (tree type)
+type_internals_preclude_sra_p (tree type, const char **msg)
{
tree fld;
tree et;
{
tree ft = TREE_TYPE (fld);
- if (TREE_THIS_VOLATILE (fld)
- || !DECL_FIELD_OFFSET (fld) || !DECL_SIZE (fld)
- || !host_integerp (DECL_FIELD_OFFSET (fld), 1)
- || !host_integerp (DECL_SIZE (fld), 1)
- || (AGGREGATE_TYPE_P (ft)
- && int_bit_position (fld) % BITS_PER_UNIT != 0))
- return true;
-
+ if (TREE_THIS_VOLATILE (fld))
+ {
+ *msg = "volatile structure field";
+ return true;
+ }
+ if (!DECL_FIELD_OFFSET (fld))
+ {
+ *msg = "no structure field offset";
+ return true;
+ }
+ if (!DECL_SIZE (fld))
+ {
+ *msg = "zero structure field size";
+ return true;
+ }
+ if (!host_integerp (DECL_FIELD_OFFSET (fld), 1))
+ {
+ *msg = "structure field offset not fixed";
+ return true;
+ }
+ if (!host_integerp (DECL_SIZE (fld), 1))
+ {
+ *msg = "structure field size not fixed";
+ return true;
+ }
+ if (!host_integerp (bit_position (fld), 0))
+ {
+ *msg = "structure field size too big";
+ return true;
+ }
if (AGGREGATE_TYPE_P (ft)
- && type_internals_preclude_sra_p (ft))
+ && int_bit_position (fld) % BITS_PER_UNIT != 0)
+ {
+ *msg = "structure field is bit field";
+ return true;
+ }
+
+ if (AGGREGATE_TYPE_P (ft) && type_internals_preclude_sra_p (ft, msg))
return true;
}
case ARRAY_TYPE:
et = TREE_TYPE (type);
- if (AGGREGATE_TYPE_P (et))
- return type_internals_preclude_sra_p (et);
- else
- return false;
+ if (TYPE_VOLATILE (et))
+ {
+ *msg = "element type is volatile";
+ return true;
+ }
+
+ if (AGGREGATE_TYPE_P (et) && type_internals_preclude_sra_p (et, msg))
+ return true;
+
+ return false;
default:
return false;
static struct access *
create_access_1 (tree base, HOST_WIDE_INT offset, HOST_WIDE_INT size)
{
- VEC (access_p, heap) *vec;
+ vec<access_p> *v;
struct access *access;
void **slot;
slot = pointer_map_contains (base_access_vec, base);
if (slot)
- vec = (VEC (access_p, heap) *) *slot;
+ v = (vec<access_p> *) *slot;
else
- vec = VEC_alloc (access_p, heap, 32);
+ vec_alloc (v, 32);
- VEC_safe_push (access_p, heap, vec, access);
+ v->safe_push (access);
- *((struct VEC (access_p,heap) **)
- pointer_map_insert (base_access_vec, base)) = vec;
+ *((vec<access_p> **)
+ pointer_map_insert (base_access_vec, base)) = v;
return access;
}
access = create_access_1 (base, pos, size);
access->expr = nref;
access->type = ft;
- access->total_scalarization = 1;
+ access->grp_total_scalarization = 1;
/* Accesses for intraprocedural SRA can have their stmt NULL. */
}
else
}
}
+/* Create total_scalarization accesses for all scalar type fields in VAR and
+ for VAR a a whole. VAR must be of a RECORD_TYPE conforming to
+ type_consists_of_records_p. */
+
+static void
+completely_scalarize_var (tree var)
+{
+ HOST_WIDE_INT size = tree_low_cst (DECL_SIZE (var), 1);
+ struct access *access;
+
+ access = create_access_1 (var, 0, size);
+ access->expr = var;
+ access->type = TREE_TYPE (var);
+ access->grp_total_scalarization = 1;
+
+ completely_scalarize_record (var, var, 0, var);
+}
/* Search the given tree for a declaration by skipping handled components and
exclude it from the candidates. */
return false;
}
-/* Scan expressions occuring in STMT, create access structures for all accesses
+/* Scan expressions occurring in STMT, create access structures for all accesses
to candidates for scalarization and remove those candidates which occur in
statements or expressions that prevent them from being split apart. Return
true if any access has been inserted. */
tree lhs, rhs;
struct access *lacc, *racc;
- if (!gimple_assign_single_p (stmt))
+ if (!gimple_assign_single_p (stmt)
+ /* Scope clobbers don't influence scalarization. */
+ || gimple_clobber_p (stmt))
return false;
lhs = gimple_assign_lhs (stmt);
&& !lacc->grp_unscalarizable_region
&& !racc->grp_unscalarizable_region
&& AGGREGATE_TYPE_P (TREE_TYPE (lhs))
- /* FIXME: Turn the following line into an assert after PR 40058 is
- fixed. */
&& lacc->size == racc->size
&& useless_type_conversion_p (lacc->type, racc->type))
{
EXP_TYPE at the given OFFSET. If BASE is something for which
get_addr_base_and_unit_offset returns NULL, gsi must be non-NULL and is used
to insert new statements either before or below the current one as specified
- by INSERT_AFTER. This function is not capable of handling bitfields. */
+ by INSERT_AFTER. This function is not capable of handling bitfields.
+
+ BASE must be either a declaration or a memory reference that has correct
+ alignment ifformation embeded in it (e.g. a pre-existing one in SRA). */
tree
build_ref_for_offset (location_t loc, tree base, HOST_WIDE_INT offset,
tree prev_base = base;
tree off;
HOST_WIDE_INT base_offset;
+ unsigned HOST_WIDE_INT misalign;
+ unsigned int align;
gcc_checking_assert (offset % BITS_PER_UNIT == 0);
-
+ get_object_alignment_1 (base, &align, &misalign);
base = get_addr_base_and_unit_offset (base, &base_offset);
/* get_addr_base_and_unit_offset returns NULL for references with a variable
tree tmp, addr;
gcc_checking_assert (gsi);
- tmp = create_tmp_reg (build_pointer_type (TREE_TYPE (prev_base)), NULL);
- add_referenced_var (tmp);
- tmp = make_ssa_name (tmp, NULL);
+ tmp = make_ssa_name (build_pointer_type (TREE_TYPE (prev_base)), NULL);
addr = build_fold_addr_expr (unshare_expr (prev_base));
STRIP_USELESS_TYPE_CONVERSION (addr);
stmt = gimple_build_assign (tmp, addr);
gimple_set_location (stmt, loc);
- SSA_NAME_DEF_STMT (tmp) = stmt;
if (insert_after)
gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
else
gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
- update_stmt (stmt);
off = build_int_cst (reference_alias_ptr_type (prev_base),
offset / BITS_PER_UNIT);
{
off = build_int_cst (TREE_TYPE (TREE_OPERAND (base, 1)),
base_offset + offset / BITS_PER_UNIT);
- off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), off, 0);
+ off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), off);
base = unshare_expr (TREE_OPERAND (base, 0));
}
else
base = build_fold_addr_expr (unshare_expr (base));
}
+ misalign = (misalign + offset) & (align - 1);
+ if (misalign != 0)
+ align = (misalign & -misalign);
+ if (align < TYPE_ALIGN (exp_type))
+ exp_type = build_aligned_type (exp_type, align);
+
return fold_build2_loc (loc, MEM_REF, exp_type, base, off);
}
/* Construct a memory reference to a part of an aggregate BASE at the given
OFFSET and of the same type as MODEL. In case this is a reference to a
- component, the function will replicate the last COMPONENT_REF of model's
+ bit-field, the function will replicate the last component_ref of model's
expr to access it. GSI and INSERT_AFTER have the same meaning as in
build_ref_for_offset. */
struct access *model, gimple_stmt_iterator *gsi,
bool insert_after)
{
- if (TREE_CODE (model->expr) == COMPONENT_REF)
+ if (TREE_CODE (model->expr) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (model->expr, 1)))
{
- tree t, exp_type;
- offset -= int_bit_position (TREE_OPERAND (model->expr, 1));
+ /* This access represents a bit-field. */
+ tree t, exp_type, fld = TREE_OPERAND (model->expr, 1);
+
+ offset -= int_bit_position (fld);
exp_type = TREE_TYPE (TREE_OPERAND (model->expr, 0));
t = build_ref_for_offset (loc, base, offset, exp_type, gsi, insert_after);
- return fold_build3_loc (loc, COMPONENT_REF, model->type, t,
- TREE_OPERAND (model->expr, 1), NULL_TREE);
+ return fold_build3_loc (loc, COMPONENT_REF, TREE_TYPE (fld), t, fld,
+ NULL_TREE);
}
else
return build_ref_for_offset (loc, base, offset, model->type,
gsi, insert_after);
}
+/* Attempt to build a memory reference that we could but into a gimple
+ debug_bind statement. Similar to build_ref_for_model but punts if it has to
+ create statements and return s NULL instead. This function also ignores
+ alignment issues and so its results should never end up in non-debug
+ statements. */
+
+static tree
+build_debug_ref_for_model (location_t loc, tree base, HOST_WIDE_INT offset,
+ struct access *model)
+{
+ HOST_WIDE_INT base_offset;
+ tree off;
+
+ if (TREE_CODE (model->expr) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (model->expr, 1)))
+ return NULL_TREE;
+
+ base = get_addr_base_and_unit_offset (base, &base_offset);
+ if (!base)
+ return NULL_TREE;
+ if (TREE_CODE (base) == MEM_REF)
+ {
+ off = build_int_cst (TREE_TYPE (TREE_OPERAND (base, 1)),
+ base_offset + offset / BITS_PER_UNIT);
+ off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), off);
+ base = unshare_expr (TREE_OPERAND (base, 0));
+ }
+ else
+ {
+ off = build_int_cst (reference_alias_ptr_type (base),
+ base_offset + offset / BITS_PER_UNIT);
+ base = build_fold_addr_expr (unshare_expr (base));
+ }
+
+ return fold_build2_loc (loc, MEM_REF, model->type, base, off);
+}
+
/* Construct a memory reference consisting of component_refs and array_refs to
a part of an aggregate *RES (which is of type TYPE). The requested part
should have type EXP_TYPE at be the given OFFSET. This function might not
for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
{
HOST_WIDE_INT pos, size;
- tree expr, *expr_ptr;
+ tree tr_pos, expr, *expr_ptr;
if (TREE_CODE (fld) != FIELD_DECL)
continue;
- pos = int_bit_position (fld);
+ tr_pos = bit_position (fld);
+ if (!tr_pos || !host_integerp (tr_pos, 1))
+ continue;
+ pos = TREE_INT_CST_LOW (tr_pos);
gcc_assert (TREE_CODE (type) == RECORD_TYPE || pos == 0);
tr_size = DECL_SIZE (fld);
if (!tr_size || !host_integerp (tr_size, 1))
continue;
- size = tree_low_cst (tr_size, 1);
+ size = TREE_INT_CST_LOW (tr_size);
if (size == 0)
{
if (pos != offset)
return false;
index = build_int_cst (TYPE_DOMAIN (type), offset / el_size);
if (!integer_zerop (minidx))
- index = int_const_binop (PLUS_EXPR, index, minidx, 0);
+ index = int_const_binop (PLUS_EXPR, index, minidx);
*res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index,
NULL_TREE, NULL_TREE);
offset = offset % el_size;
return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node);
}
+/* Print message to dump file why a variable was rejected. */
+
+static void
+reject (tree var, const char *msg)
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Rejected (%d): %s: ", DECL_UID (var), msg);
+ print_generic_expr (dump_file, var, 0);
+ fprintf (dump_file, "\n");
+ }
+}
+
+/* Return true if VAR is a candidate for SRA. */
+
+static bool
+maybe_add_sra_candidate (tree var)
+{
+ tree type = TREE_TYPE (var);
+ const char *msg;
+ void **slot;
+
+ if (!AGGREGATE_TYPE_P (type))
+ {
+ reject (var, "not aggregate");
+ return false;
+ }
+ if (needs_to_live_in_memory (var))
+ {
+ reject (var, "needs to live in memory");
+ return false;
+ }
+ if (TREE_THIS_VOLATILE (var))
+ {
+ reject (var, "is volatile");
+ return false;
+ }
+ if (!COMPLETE_TYPE_P (type))
+ {
+ reject (var, "has incomplete type");
+ return false;
+ }
+ if (!host_integerp (TYPE_SIZE (type), 1))
+ {
+ reject (var, "type size not fixed");
+ return false;
+ }
+ if (tree_low_cst (TYPE_SIZE (type), 1) == 0)
+ {
+ reject (var, "type size is zero");
+ return false;
+ }
+ if (type_internals_preclude_sra_p (type, &msg))
+ {
+ reject (var, msg);
+ return false;
+ }
+ if (/* 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)))
+ {
+ reject (var, "is va_list");
+ return false;
+ }
+
+ bitmap_set_bit (candidate_bitmap, DECL_UID (var));
+ slot = htab_find_slot_with_hash (candidates, var, DECL_UID (var), INSERT);
+ *slot = (void *) var;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Candidate (%d): ", DECL_UID (var));
+ print_generic_expr (dump_file, var, 0);
+ fprintf (dump_file, "\n");
+ }
+
+ return true;
+}
+
/* The very first phase of intraprocedural SRA. It marks in candidate_bitmap
those with type which is suitable for scalarization. */
static bool
find_var_candidates (void)
{
- tree var, type;
- referenced_var_iterator rvi;
+ tree var, parm;
+ unsigned int i;
bool ret = false;
- FOR_EACH_REFERENCED_VAR (cfun, var, rvi)
+ for (parm = DECL_ARGUMENTS (current_function_decl);
+ parm;
+ parm = DECL_CHAIN (parm))
+ ret |= maybe_add_sra_candidate (parm);
+
+ FOR_EACH_LOCAL_DECL (cfun, i, var)
{
- if (TREE_CODE (var) != VAR_DECL && TREE_CODE (var) != PARM_DECL)
+ if (TREE_CODE (var) != VAR_DECL)
continue;
- type = TREE_TYPE (var);
-
- if (!AGGREGATE_TYPE_P (type)
- || needs_to_live_in_memory (var)
- || TREE_THIS_VOLATILE (var)
- || !COMPLETE_TYPE_P (type)
- || !host_integerp (TYPE_SIZE (type), 1)
- || tree_low_cst (TYPE_SIZE (type), 1) == 0
- || 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));
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Candidate (%d): ", DECL_UID (var));
- print_generic_expr (dump_file, var, 0);
- fprintf (dump_file, "\n");
- }
- ret = true;
+ ret |= maybe_add_sra_candidate (var);
}
return ret;
{
int i, j, access_count;
struct access *res, **prev_acc_ptr = &res;
- VEC (access_p, heap) *access_vec;
+ vec<access_p> *access_vec;
bool first = true;
HOST_WIDE_INT low = -1, high = 0;
access_vec = get_base_access_vector (var);
if (!access_vec)
return NULL;
- access_count = VEC_length (access_p, access_vec);
+ access_count = access_vec->length ();
/* Sort by <OFFSET, SIZE>. */
- VEC_qsort (access_p, access_vec, compare_access_positions);
+ access_vec->qsort (compare_access_positions);
i = 0;
while (i < access_count)
{
- struct access *access = VEC_index (access_p, access_vec, i);
+ struct access *access = (*access_vec)[i];
bool grp_write = access->write;
bool grp_read = !access->write;
bool grp_scalar_write = access->write
bool grp_assignment_read = access->grp_assignment_read;
bool grp_assignment_write = access->grp_assignment_write;
bool multiple_scalar_reads = false;
- bool total_scalarization = access->total_scalarization;
+ bool total_scalarization = access->grp_total_scalarization;
bool grp_partial_lhs = access->grp_partial_lhs;
bool first_scalar = is_gimple_reg_type (access->type);
bool unscalarizable_region = access->grp_unscalarizable_region;
j = i + 1;
while (j < access_count)
{
- struct access *ac2 = VEC_index (access_p, access_vec, j);
+ struct access *ac2 = (*access_vec)[j];
if (ac2->offset != access->offset || ac2->size != access->size)
break;
if (ac2->write)
grp_assignment_write |= ac2->grp_assignment_write;
grp_partial_lhs |= ac2->grp_partial_lhs;
unscalarizable_region |= ac2->grp_unscalarizable_region;
- total_scalarization |= ac2->total_scalarization;
+ total_scalarization |= ac2->grp_total_scalarization;
relink_to_new_repr (access, ac2);
/* If there are both aggregate-type and scalar-type accesses with
access->grp_assignment_read = grp_assignment_read;
access->grp_assignment_write = grp_assignment_write;
access->grp_hint = multiple_scalar_reads || total_scalarization;
+ access->grp_total_scalarization = total_scalarization;
access->grp_partial_lhs = grp_partial_lhs;
access->grp_unscalarizable_region = unscalarizable_region;
if (access->first_link)
prev_acc_ptr = &access->next_grp;
}
- gcc_assert (res == VEC_index (access_p, access_vec, 0));
+ gcc_assert (res == (*access_vec)[0]);
return res;
}
ACCESS->replacement. */
static tree
-create_access_replacement (struct access *access, bool rename)
+create_access_replacement (struct access *access)
{
tree repl;
- repl = create_tmp_var (access->type, "SR");
- get_var_ann (repl);
- add_referenced_var (repl);
- if (rename)
- mark_sym_for_renaming (repl);
-
- if (!access->grp_partial_lhs
- && (TREE_CODE (access->type) == COMPLEX_TYPE
- || TREE_CODE (access->type) == VECTOR_TYPE))
- DECL_GIMPLE_REG_P (repl) = 1;
+ if (access->grp_to_be_debug_replaced)
+ {
+ repl = create_tmp_var_raw (access->type, NULL);
+ DECL_CONTEXT (repl) = current_function_decl;
+ }
+ else
+ repl = create_tmp_var (access->type, "SR");
+ if (TREE_CODE (access->type) == COMPLEX_TYPE
+ || TREE_CODE (access->type) == VECTOR_TYPE)
+ {
+ if (!access->grp_partial_lhs)
+ DECL_GIMPLE_REG_P (repl) = 1;
+ }
+ else if (access->grp_partial_lhs
+ && is_gimple_reg_type (access->type))
+ TREE_ADDRESSABLE (repl) = 1;
DECL_SOURCE_LOCATION (repl) = DECL_SOURCE_LOCATION (access->base);
DECL_ARTIFICIAL (repl) = 1;
{
char *pretty_name = make_fancy_name (access->expr);
tree debug_expr = unshare_expr (access->expr), d;
+ bool fail = false;
DECL_NAME (repl) = get_identifier (pretty_name);
obstack_free (&name_obstack, pretty_name);
used SSA_NAMEs and thus they could be freed. All debug info
generation cares is whether something is constant or variable
and that get_ref_base_and_extent works properly on the
- expression. */
- for (d = debug_expr; handled_component_p (d); d = TREE_OPERAND (d, 0))
+ expression. It cannot handle accesses at a non-constant offset
+ though, so just give up in those cases. */
+ for (d = debug_expr;
+ !fail && (handled_component_p (d) || TREE_CODE (d) == MEM_REF);
+ d = TREE_OPERAND (d, 0))
switch (TREE_CODE (d))
{
case ARRAY_REF:
case ARRAY_RANGE_REF:
if (TREE_OPERAND (d, 1)
- && TREE_CODE (TREE_OPERAND (d, 1)) == SSA_NAME)
- TREE_OPERAND (d, 1) = SSA_NAME_VAR (TREE_OPERAND (d, 1));
+ && TREE_CODE (TREE_OPERAND (d, 1)) != INTEGER_CST)
+ fail = true;
if (TREE_OPERAND (d, 3)
- && TREE_CODE (TREE_OPERAND (d, 3)) == SSA_NAME)
- TREE_OPERAND (d, 3) = SSA_NAME_VAR (TREE_OPERAND (d, 3));
+ && TREE_CODE (TREE_OPERAND (d, 3)) != INTEGER_CST)
+ fail = true;
/* FALLTHRU */
case COMPONENT_REF:
if (TREE_OPERAND (d, 2)
- && TREE_CODE (TREE_OPERAND (d, 2)) == SSA_NAME)
- TREE_OPERAND (d, 2) = SSA_NAME_VAR (TREE_OPERAND (d, 2));
+ && TREE_CODE (TREE_OPERAND (d, 2)) != INTEGER_CST)
+ fail = true;
+ break;
+ case MEM_REF:
+ if (TREE_CODE (TREE_OPERAND (d, 0)) != ADDR_EXPR)
+ fail = true;
+ else
+ d = TREE_OPERAND (d, 0);
break;
default:
break;
}
- SET_DECL_DEBUG_EXPR (repl, debug_expr);
- DECL_DEBUG_EXPR_IS_FROM (repl) = 1;
+ if (!fail)
+ {
+ SET_DECL_DEBUG_EXPR (repl, debug_expr);
+ DECL_DEBUG_EXPR_IS_FROM (repl) = 1;
+ }
if (access->grp_no_warning)
TREE_NO_WARNING (repl) = 1;
else
if (dump_file)
{
- fprintf (dump_file, "Created a replacement for ");
- print_generic_expr (dump_file, access->base, 0);
- fprintf (dump_file, " offset: %u, size: %u: ",
- (unsigned) access->offset, (unsigned) access->size);
- print_generic_expr (dump_file, repl, 0);
- fprintf (dump_file, "\n");
+ if (access->grp_to_be_debug_replaced)
+ {
+ fprintf (dump_file, "Created a debug-only replacement for ");
+ print_generic_expr (dump_file, access->base, 0);
+ fprintf (dump_file, " offset: %u, size: %u\n",
+ (unsigned) access->offset, (unsigned) access->size);
+ }
+ else
+ {
+ fprintf (dump_file, "Created a replacement for ");
+ print_generic_expr (dump_file, access->base, 0);
+ fprintf (dump_file, " offset: %u, size: %u: ",
+ (unsigned) access->offset, (unsigned) access->size);
+ print_generic_expr (dump_file, repl, 0);
+ fprintf (dump_file, "\n");
+ }
}
sra_stats.replacements++;
static inline tree
get_access_replacement (struct access *access)
{
- gcc_assert (access->grp_to_be_replaced);
-
if (!access->replacement_decl)
- access->replacement_decl = create_access_replacement (access, true);
- return access->replacement_decl;
-}
-
-/* Return ACCESS scalar replacement, create it if it does not exist yet but do
- not mark it for renaming. */
-
-static inline tree
-get_unrenamed_access_replacement (struct access *access)
-{
- gcc_assert (!access->grp_to_be_replaced);
-
- if (!access->replacement_decl)
- access->replacement_decl = create_access_replacement (access, false);
+ access->replacement_decl = create_access_replacement (access);
return access->replacement_decl;
}
return false;
}
-enum mark_rw_status { SRA_MRRW_NOTHING, SRA_MRRW_DIRECT, SRA_MRRW_ASSIGN};
-
/* 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 set
1 1 1 1 Yes Any of the above yeses */
static bool
-analyze_access_subtree (struct access *root, bool allow_replacements,
- enum mark_rw_status mark_read,
- enum mark_rw_status mark_write)
+analyze_access_subtree (struct access *root, struct access *parent,
+ bool allow_replacements)
{
struct access *child;
HOST_WIDE_INT limit = root->offset + root->size;
bool scalar = is_gimple_reg_type (root->type);
bool hole = false, sth_created = false;
- if (root->grp_assignment_read)
- mark_read = SRA_MRRW_ASSIGN;
- else if (mark_read == SRA_MRRW_ASSIGN)
+ if (parent)
{
- root->grp_read = 1;
- root->grp_assignment_read = 1;
+ if (parent->grp_read)
+ root->grp_read = 1;
+ if (parent->grp_assignment_read)
+ root->grp_assignment_read = 1;
+ if (parent->grp_write)
+ root->grp_write = 1;
+ if (parent->grp_assignment_write)
+ root->grp_assignment_write = 1;
+ if (parent->grp_total_scalarization)
+ root->grp_total_scalarization = 1;
}
- else if (mark_read == SRA_MRRW_DIRECT)
- root->grp_read = 1;
- else if (root->grp_read)
- mark_read = SRA_MRRW_DIRECT;
-
- if (root->grp_assignment_write)
- mark_write = SRA_MRRW_ASSIGN;
- else if (mark_write == SRA_MRRW_ASSIGN)
- {
- root->grp_write = 1;
- root->grp_assignment_write = 1;
- }
- else if (mark_write == SRA_MRRW_DIRECT)
- root->grp_write = 1;
- else if (root->grp_write)
- mark_write = SRA_MRRW_DIRECT;
if (root->grp_unscalarizable_region)
allow_replacements = false;
for (child = root->first_child; child; child = child->next_sibling)
{
- if (!hole && child->offset < covered_to)
- hole = true;
- else
- covered_to += child->size;
-
- sth_created |= analyze_access_subtree (child,
- allow_replacements && !scalar,
- mark_read, mark_write);
+ hole |= covered_to < child->offset;
+ sth_created |= analyze_access_subtree (child, root,
+ allow_replacements && !scalar);
root->grp_unscalarized_data |= child->grp_unscalarized_data;
- hole |= !child->grp_covered;
+ root->grp_total_scalarization &= child->grp_total_scalarization;
+ if (child->grp_covered)
+ covered_to += child->size;
+ else
+ hole = true;
}
if (allow_replacements && scalar && !root->first_child
|| ((root->grp_scalar_read || root->grp_assignment_read)
&& (root->grp_scalar_write || root->grp_assignment_write))))
{
+ bool new_integer_type;
+ /* Always create access replacements that cover the whole access.
+ For integral types this means the precision has to match.
+ Avoid assumptions based on the integral type kind, too. */
+ if (INTEGRAL_TYPE_P (root->type)
+ && (TREE_CODE (root->type) != INTEGER_TYPE
+ || TYPE_PRECISION (root->type) != root->size)
+ /* But leave bitfield accesses alone. */
+ && (TREE_CODE (root->expr) != COMPONENT_REF
+ || !DECL_BIT_FIELD (TREE_OPERAND (root->expr, 1))))
+ {
+ tree rt = root->type;
+ gcc_assert ((root->offset % BITS_PER_UNIT) == 0
+ && (root->size % BITS_PER_UNIT) == 0);
+ root->type = build_nonstandard_integer_type (root->size,
+ TYPE_UNSIGNED (rt));
+ root->expr = build_ref_for_offset (UNKNOWN_LOCATION,
+ root->base, root->offset,
+ root->type, NULL, false);
+ new_integer_type = true;
+ }
+ else
+ new_integer_type = false;
+
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Marking ");
print_generic_expr (dump_file, root->base, 0);
- fprintf (dump_file, " offset: %u, size: %u: ",
+ fprintf (dump_file, " offset: %u, size: %u ",
(unsigned) root->offset, (unsigned) root->size);
- fprintf (dump_file, " to be replaced.\n");
+ fprintf (dump_file, " to be replaced%s.\n",
+ new_integer_type ? " with an integer": "");
}
root->grp_to_be_replaced = 1;
sth_created = true;
hole = false;
}
- else if (covered_to < limit)
- hole = true;
-
- if (sth_created && !hole)
+ else
{
- root->grp_covered = 1;
- return true;
+ if (allow_replacements
+ && scalar && !root->first_child
+ && (root->grp_scalar_write || root->grp_assignment_write)
+ && !bitmap_bit_p (cannot_scalarize_away_bitmap,
+ DECL_UID (root->base)))
+ {
+ gcc_checking_assert (!root->grp_scalar_read
+ && !root->grp_assignment_read);
+ sth_created = true;
+ if (MAY_HAVE_DEBUG_STMTS)
+ {
+ root->grp_to_be_debug_replaced = 1;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Marking ");
+ print_generic_expr (dump_file, root->base, 0);
+ fprintf (dump_file, " offset: %u, size: %u ",
+ (unsigned) root->offset, (unsigned) root->size);
+ fprintf (dump_file, " to be replaced with debug "
+ "statements.\n");
+ }
+ }
+ }
+
+ if (covered_to < limit)
+ hole = true;
+ if (scalar)
+ root->grp_total_scalarization = 0;
}
- if (root->grp_write || TREE_CODE (root->base) == PARM_DECL)
+
+ if (!hole || root->grp_total_scalarization)
+ root->grp_covered = 1;
+ else if (root->grp_write || TREE_CODE (root->base) == PARM_DECL)
root->grp_unscalarized_data = 1; /* not covered and written to */
- if (sth_created)
- return true;
- return false;
+ return sth_created;
}
/* Analyze all access trees linked by next_grp by the means of
while (access)
{
- if (analyze_access_subtree (access, true,
- SRA_MRRW_NOTHING, SRA_MRRW_NOTHING))
+ if (analyze_access_subtree (access, NULL, true))
ret = true;
access = access->next_grp;
}
|| racc->grp_unscalarizable_region)
return false;
- if (!lacc->first_child && !racc->first_child
- && is_gimple_reg_type (racc->type))
+ if (is_gimple_reg_type (racc->type))
{
- tree t = lacc->base;
-
- lacc->type = racc->type;
- if (build_user_friendly_ref_for_offset (&t, TREE_TYPE (t), lacc->offset,
- racc->type))
- lacc->expr = t;
- else
+ if (!lacc->first_child && !racc->first_child)
{
- lacc->expr = build_ref_for_model (EXPR_LOCATION (lacc->base),
- lacc->base, lacc->offset,
- racc, NULL, false);
- lacc->grp_no_warning = true;
+ tree t = lacc->base;
+
+ lacc->type = racc->type;
+ if (build_user_friendly_ref_for_offset (&t, TREE_TYPE (t),
+ lacc->offset, racc->type))
+ lacc->expr = t;
+ else
+ {
+ lacc->expr = build_ref_for_model (EXPR_LOCATION (lacc->base),
+ lacc->base, lacc->offset,
+ racc, NULL, false);
+ lacc->grp_no_warning = true;
+ }
}
return false;
}
if (bitmap_bit_p (should_scalarize_away_bitmap, i)
&& !bitmap_bit_p (cannot_scalarize_away_bitmap, i))
{
- tree var = referenced_var (i);
+ tree var = candidate (i);
if (TREE_CODE (var) == VAR_DECL
- && ((unsigned) tree_low_cst (TYPE_SIZE (TREE_TYPE (var)), 1)
- <= max_total_scalarization_size)
&& type_consists_of_records_p (TREE_TYPE (var)))
{
- completely_scalarize_record (var, var, 0, var);
- if (dump_file && (dump_flags & TDF_DETAILS))
+ if ((unsigned) tree_low_cst (TYPE_SIZE (TREE_TYPE (var)), 1)
+ <= max_total_scalarization_size)
+ {
+ completely_scalarize_var (var);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Will attempt to totally scalarize ");
+ print_generic_expr (dump_file, var, 0);
+ fprintf (dump_file, " (UID: %u): \n", DECL_UID (var));
+ }
+ }
+ else if (dump_file && (dump_flags & TDF_DETAILS))
{
- fprintf (dump_file, "Will attempt to totally scalarize ");
+ fprintf (dump_file, "Too big to totally scalarize: ");
print_generic_expr (dump_file, var, 0);
- fprintf (dump_file, " (UID: %u): \n", DECL_UID (var));
+ fprintf (dump_file, " (UID: %u)\n", DECL_UID (var));
}
}
}
bitmap_copy (tmp, candidate_bitmap);
EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
{
- tree var = referenced_var (i);
+ tree var = candidate (i);
struct access *access;
access = sort_and_splice_var_accesses (var);
bitmap_copy (tmp, candidate_bitmap);
EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
{
- tree var = referenced_var (i);
+ tree var = candidate (i);
struct access *access = get_first_repr_for_decl (var);
if (analyze_access_trees (access))
update_stmt (stmt);
sra_stats.subtree_copies++;
}
+ else if (write
+ && access->grp_to_be_debug_replaced
+ && (chunk_size == 0
+ || access->offset + access->size > start_offset))
+ {
+ gimple ds;
+ tree drhs = build_debug_ref_for_model (loc, agg,
+ access->offset - top_offset,
+ access);
+ ds = gimple_build_debug_bind (get_access_replacement (access),
+ drhs, gsi_stmt (*gsi));
+ if (insert_after)
+ gsi_insert_after (gsi, ds, GSI_NEW_STMT);
+ else
+ gsi_insert_before (gsi, ds, GSI_SAME_STMT);
+ }
if (access->first_child)
generate_subtree_copies (access->first_child, agg, top_offset,
update_stmt (stmt);
gimple_set_location (stmt, loc);
}
+ else if (access->grp_to_be_debug_replaced)
+ {
+ gimple ds = gimple_build_debug_bind (get_access_replacement (access),
+ build_zero_cst (access->type),
+ gsi_stmt (*gsi));
+ if (insert_after)
+ gsi_insert_after (gsi, ds, GSI_NEW_STMT);
+ else
+ gsi_insert_before (gsi, ds, GSI_SAME_STMT);
+ }
for (child = access->first_child; child; child = child->next_sibling)
init_subtree_with_zero (child, gsi, insert_after, loc);
*expr = repl;
sra_stats.exprs++;
}
+ else if (write && access->grp_to_be_debug_replaced)
+ {
+ gimple ds = gimple_build_debug_bind (get_access_replacement (access),
+ NULL_TREE,
+ gsi_stmt (*gsi));
+ gsi_insert_after (gsi, ds, GSI_NEW_STMT);
+ }
if (access->first_child)
{
location_t loc = gimple_location (gsi_stmt (*old_gsi));
for (lacc = lacc->first_child; lacc; lacc = lacc->next_sibling)
{
+ HOST_WIDE_INT offset = lacc->offset - left_offset + top_racc->offset;
+
if (lacc->grp_to_be_replaced)
{
struct access *racc;
- HOST_WIDE_INT offset = lacc->offset - left_offset + top_racc->offset;
gimple stmt;
tree rhs;
rhs = get_access_replacement (racc);
if (!useless_type_conversion_p (lacc->type, racc->type))
rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, lacc->type, rhs);
+
+ if (racc->grp_partial_lhs && lacc->grp_partial_lhs)
+ rhs = force_gimple_operand_gsi (old_gsi, rhs, true, NULL_TREE,
+ true, GSI_SAME_STMT);
}
else
{
else
rhs = build_ref_for_model (loc, top_racc->base, offset, lacc,
new_gsi, true);
+ if (lacc->grp_partial_lhs)
+ rhs = force_gimple_operand_gsi (new_gsi, rhs, true, NULL_TREE,
+ false, GSI_NEW_STMT);
}
stmt = gimple_build_assign (get_access_replacement (lacc), rhs);
update_stmt (stmt);
sra_stats.subreplacements++;
}
- else if (*refreshed == SRA_UDH_NONE
- && lacc->grp_read && !lacc->grp_covered)
- *refreshed = handle_unscalarized_data_in_subtree (top_racc,
- old_gsi);
+ else
+ {
+ if (*refreshed == SRA_UDH_NONE
+ && lacc->grp_read && !lacc->grp_covered)
+ *refreshed = handle_unscalarized_data_in_subtree (top_racc,
+ old_gsi);
+ if (lacc && lacc->grp_to_be_debug_replaced)
+ {
+ gimple ds;
+ tree drhs;
+ struct access *racc = find_access_in_subtree (top_racc, offset,
+ lacc->size);
+
+ if (racc && racc->grp_to_be_replaced)
+ drhs = get_access_replacement (racc);
+ else if (*refreshed == SRA_UDH_LEFT)
+ drhs = build_debug_ref_for_model (loc, lacc->base, lacc->offset,
+ lacc);
+ else if (*refreshed == SRA_UDH_RIGHT)
+ drhs = build_debug_ref_for_model (loc, top_racc->base, offset,
+ lacc);
+ else
+ drhs = NULL_TREE;
+ ds = gimple_build_debug_bind (get_access_replacement (lacc),
+ drhs, gsi_stmt (*old_gsi));
+ gsi_insert_after (new_gsi, ds, GSI_NEW_STMT);
+ }
+ }
if (lacc->first_child)
load_assign_lhs_subreplacements (lacc, top_racc, left_offset,
if (!acc)
return SRA_AM_NONE;
+ if (gimple_clobber_p (*stmt))
+ {
+ /* Remove clobbers of fully scalarized variables, otherwise
+ do nothing. */
+ if (acc->grp_covered)
+ {
+ unlink_stmt_vdef (*stmt);
+ gsi_remove (gsi, true);
+ release_defs (*stmt);
+ return SRA_AM_REMOVED;
+ }
+ else
+ return SRA_AM_NONE;
+ }
+
loc = gimple_location (*stmt);
- if (VEC_length (constructor_elt,
- CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0)
+ if (vec_safe_length (CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0)
{
/* I have never seen this code path trigger but if it can happen the
following should handle it gracefully. */
init_subtree_with_zero (acc, gsi, false, loc);
unlink_stmt_vdef (*stmt);
gsi_remove (gsi, true);
+ release_defs (*stmt);
return SRA_AM_REMOVED;
}
else
static tree
get_repl_default_def_ssa_name (struct access *racc)
{
- tree repl, decl;
-
- decl = get_unrenamed_access_replacement (racc);
-
- repl = gimple_default_def (cfun, decl);
- if (!repl)
- {
- repl = make_ssa_name (decl, gimple_build_nop ());
- set_default_def (decl, repl);
- }
-
- return repl;
+ return get_or_create_ssa_default_def (cfun, get_access_replacement (racc));
}
/* Return true if REF has a COMPONENT_REF with a bit-field field declaration
force_gimple_rhs = true;
sra_stats.exprs++;
}
+ else if (racc
+ && !racc->grp_unscalarized_data
+ && TREE_CODE (lhs) == SSA_NAME
+ && !access_has_replacements_p (racc))
+ {
+ rhs = get_repl_default_def_ssa_name (racc);
+ modify_this_stmt = true;
+ sra_stats.exprs++;
+ }
if (modify_this_stmt)
{
??? This should move to fold_stmt which we simply should
call after building a VIEW_CONVERT_EXPR here. */
if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))
- && !contains_bitfld_comp_ref_p (lhs)
- && !access_has_children_p (lacc))
+ && !contains_bitfld_comp_ref_p (lhs))
{
lhs = build_ref_for_model (loc, lhs, 0, racc, gsi, false);
gimple_assign_set_lhs (*stmt, lhs);
}
else if (AGGREGATE_TYPE_P (TREE_TYPE (rhs))
- && !contains_vce_or_bfcref_p (rhs)
- && !access_has_children_p (racc))
+ && !contains_vce_or_bfcref_p (rhs))
rhs = build_ref_for_model (loc, rhs, 0, lacc, gsi, false);
if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
}
}
+ if (lacc && lacc->grp_to_be_debug_replaced)
+ {
+ tree dlhs = get_access_replacement (lacc);
+ tree drhs = unshare_expr (rhs);
+ if (!useless_type_conversion_p (TREE_TYPE (dlhs), TREE_TYPE (drhs)))
+ {
+ if (AGGREGATE_TYPE_P (TREE_TYPE (drhs))
+ && !contains_vce_or_bfcref_p (drhs))
+ drhs = build_debug_ref_for_model (loc, drhs, 0, lacc);
+ if (drhs
+ && !useless_type_conversion_p (TREE_TYPE (dlhs),
+ TREE_TYPE (drhs)))
+ drhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR,
+ TREE_TYPE (dlhs), drhs);
+ }
+ gimple ds = gimple_build_debug_bind (dlhs, drhs, *stmt);
+ gsi_insert_before (gsi, ds, GSI_SAME_STMT);
+ }
+
/* From this point on, the function deals with assignments in between
aggregates when at least one has scalar reductions of some of its
components. There are three possible scenarios: Both the LHS and RHS have
there to do the copying and then load the scalar replacements of the LHS.
This is what the first branch does. */
- if (gimple_has_volatile_ops (*stmt)
+ if (modify_this_stmt
+ || gimple_has_volatile_ops (*stmt)
|| contains_vce_or_bfcref_p (rhs)
|| contains_vce_or_bfcref_p (lhs))
{
generate_subtree_copies (lacc->first_child, lacc->base, 0, 0, 0,
gsi, true, true, loc);
sra_stats.separate_lhs_rhs_handling++;
+
+ /* This gimplification must be done after generate_subtree_copies,
+ lest we insert the subtree copies in the middle of the gimplified
+ sequence. */
+ if (force_gimple_rhs)
+ rhs = force_gimple_operand_gsi (&orig_gsi, rhs, true, NULL_TREE,
+ true, GSI_SAME_STMT);
+ if (gimple_assign_rhs1 (*stmt) != rhs)
+ {
+ modify_this_stmt = true;
+ gimple_assign_set_rhs_from_tree (&orig_gsi, rhs);
+ gcc_assert (*stmt == gsi_stmt (orig_gsi));
+ }
+
+ return modify_this_stmt ? SRA_AM_MODIFIED : SRA_AM_NONE;
}
else
{
- if (access_has_children_p (lacc) && access_has_children_p (racc))
+ if (access_has_children_p (lacc)
+ && access_has_children_p (racc)
+ /* When an access represents an unscalarizable region, it usually
+ represents accesses with variable offset and thus must not be used
+ to generate new memory accesses. */
+ && !lacc->grp_unscalarizable_region
+ && !racc->grp_unscalarizable_region)
{
gimple_stmt_iterator orig_gsi = *gsi;
enum unscalarized_data_handling refreshed;
gsi_next (gsi);
unlink_stmt_vdef (*stmt);
gsi_remove (&orig_gsi, true);
+ release_defs (*stmt);
sra_stats.deleted++;
return SRA_AM_REMOVED;
}
}
else
{
- if (racc)
+ if (access_has_children_p (racc)
+ && !racc->grp_unscalarized_data)
{
- if (!racc->grp_to_be_replaced && !racc->grp_unscalarized_data)
+ if (dump_file)
{
- if (dump_file)
- {
- fprintf (dump_file, "Removing load: ");
- print_gimple_stmt (dump_file, *stmt, 0, 0);
- }
-
- if (TREE_CODE (lhs) == SSA_NAME)
- {
- rhs = get_repl_default_def_ssa_name (racc);
- if (!useless_type_conversion_p (TREE_TYPE (lhs),
- TREE_TYPE (rhs)))
- rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR,
- TREE_TYPE (lhs), rhs);
- }
- else
- {
- if (racc->first_child)
- generate_subtree_copies (racc->first_child, lhs,
- racc->offset, 0, 0, gsi,
- false, false, loc);
-
- gcc_assert (*stmt == gsi_stmt (*gsi));
- unlink_stmt_vdef (*stmt);
- gsi_remove (gsi, true);
- sra_stats.deleted++;
- return SRA_AM_REMOVED;
- }
+ fprintf (dump_file, "Removing load: ");
+ print_gimple_stmt (dump_file, *stmt, 0, 0);
}
- else if (racc->first_child)
- generate_subtree_copies (racc->first_child, lhs, racc->offset,
- 0, 0, gsi, false, true, loc);
+ generate_subtree_copies (racc->first_child, lhs,
+ racc->offset, 0, 0, gsi,
+ false, false, loc);
+ gcc_assert (*stmt == gsi_stmt (*gsi));
+ unlink_stmt_vdef (*stmt);
+ gsi_remove (gsi, true);
+ release_defs (*stmt);
+ sra_stats.deleted++;
+ return SRA_AM_REMOVED;
}
+ /* Restore the aggregate RHS from its components so the
+ prevailing aggregate copy does the right thing. */
+ if (access_has_children_p (racc))
+ generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
+ gsi, false, false, loc);
+ /* Re-load the components of the aggregate copy destination.
+ But use the RHS aggregate to load from to expose more
+ optimization opportunities. */
if (access_has_children_p (lacc))
generate_subtree_copies (lacc->first_child, rhs, lacc->offset,
0, 0, gsi, true, true, loc);
}
- }
- /* This gimplification must be done after generate_subtree_copies, lest we
- insert the subtree copies in the middle of the gimplified sequence. */
- if (force_gimple_rhs)
- rhs = force_gimple_operand_gsi (&orig_gsi, rhs, true, NULL_TREE,
- true, GSI_SAME_STMT);
- if (gimple_assign_rhs1 (*stmt) != rhs)
- {
- modify_this_stmt = true;
- gimple_assign_set_rhs_from_tree (&orig_gsi, rhs);
- gcc_assert (*stmt == gsi_stmt (orig_gsi));
+ return SRA_AM_NONE;
}
-
- return modify_this_stmt ? SRA_AM_MODIFIED : SRA_AM_NONE;
}
/* Traverse the function body and all modifications as decided in
gimple_seq seq = NULL;
tree parm;
+ gsi = gsi_start (seq);
for (parm = DECL_ARGUMENTS (current_function_decl);
parm;
parm = DECL_CHAIN (parm))
{
- VEC (access_p, heap) *access_vec;
+ vec<access_p> *access_vec;
struct access *access;
if (!bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
if (!access_vec)
continue;
- if (!seq)
- {
- seq = gimple_seq_alloc ();
- gsi = gsi_start (seq);
- }
-
- for (access = VEC_index (access_p, access_vec, 0);
+ for (access = (*access_vec)[0];
access;
access = access->next_grp)
generate_subtree_copies (access, parm, 0, 0, 0, &gsi, true, true,
EXPR_LOCATION (parm));
}
+ seq = gsi_seq (gsi);
if (seq)
gsi_insert_seq_on_edge_immediate (single_succ_edge (ENTRY_BLOCK_PTR), seq);
}
{
GIMPLE_PASS,
"esra", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
gate_intra_sra, /* gate */
early_intra_sra, /* execute */
NULL, /* sub */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func
- | TODO_update_ssa
+ TODO_update_ssa
| TODO_ggc_collect
| TODO_verify_ssa /* todo_flags_finish */
}
{
GIMPLE_PASS,
"sra", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
gate_intra_sra, /* gate */
late_intra_sra, /* execute */
NULL, /* sub */
0, /* properties_provided */
0, /* properties_destroyed */
TODO_update_address_taken, /* todo_flags_start */
- TODO_dump_func
- | TODO_update_ssa
+ TODO_update_ssa
| TODO_ggc_collect
| TODO_verify_ssa /* todo_flags_finish */
}
{
tree name;
return (is_gimple_reg (parm)
- && (!(name = gimple_default_def (cfun, parm))
+ && (!(name = ssa_default_def (cfun, parm))
|| has_zero_uses (name)));
}
{
imm_use_iterator ui;
gimple stmt;
- tree name = gimple_default_def (cfun, parm);
+ tree name = ssa_default_def (cfun, parm);
bool ret = false;
FOR_EACH_IMM_USE_STMT (stmt, ui, name)
&& TREE_OPERAND (lhs, 0) == name
&& integer_zerop (TREE_OPERAND (lhs, 1))
&& types_compatible_p (TREE_TYPE (lhs),
- TREE_TYPE (TREE_TYPE (name))))
+ TREE_TYPE (TREE_TYPE (name)))
+ && !TREE_THIS_VOLATILE (lhs))
uses_ok++;
}
if (gimple_assign_single_p (stmt))
&& TREE_OPERAND (rhs, 0) == name
&& integer_zerop (TREE_OPERAND (rhs, 1))
&& types_compatible_p (TREE_TYPE (rhs),
- TREE_TYPE (TREE_TYPE (name))))
+ TREE_TYPE (TREE_TYPE (name)))
+ && !TREE_THIS_VOLATILE (rhs))
uses_ok++;
}
else if (is_gimple_call (stmt))
&& TREE_OPERAND (arg, 0) == name
&& integer_zerop (TREE_OPERAND (arg, 1))
&& types_compatible_p (TREE_TYPE (arg),
- TREE_TYPE (TREE_TYPE (name))))
+ TREE_TYPE (TREE_TYPE (name)))
+ && !TREE_THIS_VOLATILE (arg))
uses_ok++;
}
}
tree parm;
int count = 0;
bool ret = false;
+ const char *msg;
for (parm = DECL_ARGUMENTS (current_function_decl);
parm;
parm = DECL_CHAIN (parm))
{
tree type = TREE_TYPE (parm);
+ void **slot;
count++;
|| !host_integerp (TYPE_SIZE (type), 1)
|| tree_low_cst (TYPE_SIZE (type), 1) == 0
|| (AGGREGATE_TYPE_P (type)
- && type_internals_preclude_sra_p (type)))
+ && type_internals_preclude_sra_p (type, &msg)))
continue;
bitmap_set_bit (candidate_bitmap, DECL_UID (parm));
+ slot = htab_find_slot_with_hash (candidates, parm,
+ DECL_UID (parm), INSERT);
+ *slot = (void *) parm;
+
ret = true;
if (dump_file && (dump_flags & TDF_DETAILS))
{
current function. */
static void
-analyze_modified_params (VEC (access_p, heap) *representatives)
+analyze_modified_params (vec<access_p> representatives)
{
int i;
{
struct access *repr;
- for (repr = VEC_index (access_p, representatives, i);
+ for (repr = representatives[i];
repr;
repr = repr->next_grp)
{
static void
propagate_dereference_distances (void)
{
- VEC (basic_block, heap) *queue;
+ vec<basic_block> 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);
+ queue.create (last_basic_block_for_function (cfun));
+ queue.quick_push (ENTRY_BLOCK_PTR);
FOR_EACH_BB (bb)
{
- VEC_quick_push (basic_block, queue, bb);
+ queue.quick_push (bb);
bb->aux = bb;
}
- while (!VEC_empty (basic_block, queue))
+ while (!queue.is_empty ())
{
edge_iterator ei;
edge e;
bool change = false;
int i;
- bb = VEC_pop (basic_block, queue);
+ bb = queue.pop ();
bb->aux = NULL;
if (bitmap_bit_p (final_bbs, bb->index))
continue;
e->src->aux = e->src;
- VEC_quick_push (basic_block, queue, e->src);
+ queue.quick_push (e->src);
}
}
- VEC_free (basic_block, heap, queue);
+ queue.release ();
}
/* Dump a dereferences TABLE with heading STR to file F. */
distances of each representative of a (fraction of a) parameter. */
static void
-analyze_caller_dereference_legality (VEC (access_p, heap) *representatives)
+analyze_caller_dereference_legality (vec<access_p> representatives)
{
int i;
for (i = 0; i < func_param_count; i++)
{
- struct access *repr = VEC_index (access_p, representatives, i);
+ struct access *repr = representatives[i];
int idx = ENTRY_BLOCK_PTR->index * func_param_count + i;
if (!repr || no_accesses_p (repr))
{
int i, access_count;
struct access *repr;
- VEC (access_p, heap) *access_vec;
+ vec<access_p> *access_vec;
access_vec = get_base_access_vector (parm);
gcc_assert (access_vec);
- repr = VEC_index (access_p, access_vec, 0);
+ repr = (*access_vec)[0];
if (repr->write)
return NULL;
repr->group_representative = repr;
- access_count = VEC_length (access_p, access_vec);
+ access_count = access_vec->length ();
for (i = 1; i < access_count; i++)
{
- struct access *access = VEC_index (access_p, access_vec, i);
+ struct access *access = (*access_vec)[i];
if (access->write)
return NULL;
access->group_representative = repr;
return repr;
}
-/* Return true iff this access precludes IPA-SRA of the parameter it is
- associated with. */
+/* Return true iff this ACCESS precludes IPA-SRA of the parameter it is
+ associated with. REQ_ALIGN is the minimum required alignment. */
static bool
-access_precludes_ipa_sra_p (struct access *access)
+access_precludes_ipa_sra_p (struct access *access, unsigned int req_align)
{
+ unsigned int exp_align;
/* 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
|| gimple_code (access->stmt) == GIMPLE_ASM))
return true;
+ exp_align = get_object_alignment (access->expr);
+ if (exp_align < req_align)
+ return true;
+
return false;
}
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;
+ vec<access_p> *access_vec;
access_vec = get_base_access_vector (parm);
if (!access_vec)
return &no_accesses_representant;
- access_count = VEC_length (access_p, access_vec);
+ access_count = access_vec->length ();
- VEC_qsort (access_p, access_vec, compare_access_positions);
+ access_vec->qsort (compare_access_positions);
i = 0;
total_size = 0;
{
bool modification;
tree a1_alias_type;
- access = VEC_index (access_p, access_vec, i);
+ access = (*access_vec)[i];
modification = access->write;
- if (access_precludes_ipa_sra_p (access))
+ if (access_precludes_ipa_sra_p (access, TYPE_ALIGN (access->type)))
return NULL;
a1_alias_type = reference_alias_ptr_type (access->expr);
j = i + 1;
while (j < access_count)
{
- struct access *ac2 = VEC_index (access_p, access_vec, j);
+ struct access *ac2 = (*access_vec)[j];
if (ac2->offset != access->offset)
{
/* All or nothing law for parameters. */
else if (ac2->size != access->size)
return NULL;
- if (access_precludes_ipa_sra_p (ac2)
+ if (access_precludes_ipa_sra_p (ac2, TYPE_ALIGN (access->type))
|| (ac2->type != access->type
&& (TREE_ADDRESSABLE (ac2->type)
|| TREE_ADDRESSABLE (access->type)))
if (by_ref && repr->non_addressable)
return 0;
+ /* Do not decompose a non-BLKmode param in a way that would
+ create BLKmode params. Especially for by-reference passing
+ (thus, pointer-type param) this is hardly worthwhile. */
+ if (DECL_MODE (parm) != BLKmode
+ && TYPE_MODE (repr->type) == BLKmode)
+ return 0;
+
if (!by_ref || (!repr->grp_maybe_modified
&& !repr->grp_not_necessarilly_dereferenced))
total_size += repr->size;
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)
+splice_all_param_accesses (vec<access_p> &representatives)
{
enum ipa_splicing_result result = NO_GOOD_ACCESS;
tree parm;
struct access *repr;
- *representatives = VEC_alloc (access_p, heap, func_param_count);
+ representatives.create (func_param_count);
for (parm = DECL_ARGUMENTS (current_function_decl);
parm;
{
if (is_unused_scalar_param (parm))
{
- VEC_quick_push (access_p, *representatives,
- &no_accesses_representant);
+ representatives.quick_push (&no_accesses_representant);
if (result == NO_GOOD_ACCESS)
result = UNUSED_PARAMS;
}
&& bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
{
repr = unmodified_by_ref_scalar_representative (parm);
- VEC_quick_push (access_p, *representatives, repr);
+ representatives.quick_push (repr);
if (repr)
result = UNMODIF_BY_REF_ACCESSES;
}
{
bool ro_grp = false;
repr = splice_param_accesses (parm, &ro_grp);
- VEC_quick_push (access_p, *representatives, repr);
+ representatives.quick_push (repr);
if (repr && !no_accesses_p (repr))
{
result = UNUSED_PARAMS;
}
else
- VEC_quick_push (access_p, *representatives, NULL);
+ representatives.quick_push (NULL);
}
if (result == NO_GOOD_ACCESS)
{
- VEC_free (access_p, heap, *representatives);
- *representatives = NULL;
+ representatives.release ();
return NO_GOOD_ACCESS;
}
/* 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)
+get_param_index (tree base, vec<tree> parms)
{
int i, len;
- len = VEC_length (tree, parms);
+ len = parms.length ();
for (i = 0; i < len; i++)
- if (VEC_index (tree, parms, i) == base)
+ if (parms[i] == base)
return i;
gcc_unreachable ();
}
final number of adjustments. */
static ipa_parm_adjustment_vec
-turn_representatives_into_adjustments (VEC (access_p, heap) *representatives,
+turn_representatives_into_adjustments (vec<access_p> representatives,
int adjustments_count)
{
- VEC (tree, heap) *parms;
+ vec<tree> 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);
+ adjustments.create (adjustments_count);
parm = DECL_ARGUMENTS (current_function_decl);
for (i = 0; i < func_param_count; i++, parm = DECL_CHAIN (parm))
{
- struct access *repr = VEC_index (access_p, representatives, i);
+ struct access *repr = representatives[i];
if (!repr || no_accesses_p (repr))
{
- struct ipa_parm_adjustment *adj;
+ 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;
+ memset (&adj, 0, sizeof (adj));
+ adj.base_index = get_param_index (parm, parms);
+ adj.base = parm;
if (!repr)
- adj->copy_param = 1;
+ adj.copy_param = 1;
else
- adj->remove_param = 1;
+ adj.remove_param = 1;
+ adjustments.quick_push (adj);
}
else
{
- struct ipa_parm_adjustment *adj;
+ 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));
+ memset (&adj, 0, sizeof (adj));
gcc_assert (repr->base == parm);
- adj->base_index = index;
- adj->base = repr->base;
- adj->type = repr->type;
- adj->alias_ptr_type = reference_alias_ptr_type (repr->expr);
- adj->offset = repr->offset;
- adj->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
- && (repr->grp_maybe_modified
- || repr->grp_not_necessarilly_dereferenced));
-
+ adj.base_index = index;
+ adj.base = repr->base;
+ adj.type = repr->type;
+ adj.alias_ptr_type = reference_alias_ptr_type (repr->expr);
+ adj.offset = repr->offset;
+ adj.by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
+ && (repr->grp_maybe_modified
+ || repr->grp_not_necessarilly_dereferenced));
+ adjustments.quick_push (adj);
}
}
}
- VEC_free (tree, heap, parms);
+ parms.release ();
return adjustments;
}
enum ipa_splicing_result repr_state;
bool proceed = false;
int i, adjustments_count = 0;
- VEC (access_p, heap) *representatives;
+ vec<access_p> representatives;
ipa_parm_adjustment_vec adjustments;
- repr_state = splice_all_param_accesses (&representatives);
+ repr_state = splice_all_param_accesses (representatives);
if (repr_state == NO_GOOD_ACCESS)
- return NULL;
+ return ipa_parm_adjustment_vec();
/* If there are any parameters passed by reference which are not modified
directly, we need to check whether they can be modified indirectly. */
for (i = 0; i < func_param_count; i++)
{
- struct access *repr = VEC_index (access_p, representatives, i);
+ struct access *repr = representatives[i];
if (repr && !no_accesses_p (repr))
{
adjustments_count++;
if (repr->grp_not_necessarilly_dereferenced
|| repr->grp_maybe_modified)
- VEC_replace (access_p, representatives, i, NULL);
+ representatives[i] = NULL;
else
{
proceed = true;
if (new_components == 0)
{
- VEC_replace (access_p, representatives, i, NULL);
+ representatives[i] = NULL;
adjustments_count++;
}
else
adjustments = turn_representatives_into_adjustments (representatives,
adjustments_count);
else
- adjustments = NULL;
+ adjustments = ipa_parm_adjustment_vec();
- VEC_free (access_p, heap, representatives);
+ representatives.release ();
return adjustments;
}
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
{
int i, len;
- len = VEC_length (ipa_parm_adjustment_t, adjustments);
+ len = adjustments.length ();
for (i = 0; i < len; i++)
{
struct ipa_parm_adjustment *adj;
- adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+ adj = &adjustments[i];
if (!adj->copy_param && adj->base == base)
return adj;
}
if (TREE_CODE (lhs) != SSA_NAME)
return false;
+
decl = SSA_NAME_VAR (lhs);
- if (TREE_CODE (decl) != PARM_DECL)
+ if (decl == NULL_TREE
+ || TREE_CODE (decl) != PARM_DECL)
return false;
adj = get_adjustment_for_base (adjustments, decl);
HOST_WIDE_INT offset, size, max_size;
tree base, src;
- len = VEC_length (ipa_parm_adjustment_t, adjustments);
+ len = adjustments.length ();
if (TREE_CODE (*expr) == BIT_FIELD_REF
|| TREE_CODE (*expr) == IMAGPART_EXPR
for (i = 0; i < len; i++)
{
- adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+ adj = &adjustments[i];
if (adj->base == base &&
(adj->offset == offset || adj->remove_param))
if (is_gimple_reg_type (TREE_TYPE (*lhs_p)))
*rhs_p = build_zero_cst (TREE_TYPE (*lhs_p));
else
- *rhs_p = build_constructor (TREE_TYPE (*lhs_p), 0);
+ *rhs_p = build_constructor (TREE_TYPE (*lhs_p),
+ NULL);
}
else
new_rhs = fold_build1_loc (gimple_location (stmt),
sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
{
int i, len;
+ gimple_stmt_iterator *gsip = NULL, gsi;
- len = VEC_length (ipa_parm_adjustment_t, adjustments);
+ if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR))
+ {
+ gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+ gsip = &gsi;
+ }
+ len = adjustments.length ();
for (i = 0; i < len; i++)
{
struct ipa_parm_adjustment *adj;
imm_use_iterator ui;
- gimple stmt;
- tree name;
+ gimple stmt, def_temp;
+ tree name, vexpr, copy = NULL_TREE;
+ use_operand_p use_p;
- adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+ adj = &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)
+ name = ssa_default_def (cfun, adj->base);
+ vexpr = NULL;
+ if (name)
+ FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+ {
+ /* All other users must have been removed by
+ ipa_sra_modify_function_body. */
+ gcc_assert (is_gimple_debug (stmt));
+ if (vexpr == NULL && gsip != NULL)
+ {
+ gcc_assert (TREE_CODE (adj->base) == PARM_DECL);
+ vexpr = make_node (DEBUG_EXPR_DECL);
+ def_temp = gimple_build_debug_source_bind (vexpr, adj->base,
+ NULL);
+ DECL_ARTIFICIAL (vexpr) = 1;
+ TREE_TYPE (vexpr) = TREE_TYPE (name);
+ DECL_MODE (vexpr) = DECL_MODE (adj->base);
+ gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
+ }
+ if (vexpr)
+ {
+ FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+ SET_USE (use_p, vexpr);
+ }
+ else
+ gimple_debug_bind_reset_value (stmt);
+ update_stmt (stmt);
+ }
+ /* Create a VAR_DECL for debug info purposes. */
+ if (!DECL_IGNORED_P (adj->base))
{
- /* All other users must have been removed by
- ipa_sra_modify_function_body. */
- gcc_assert (is_gimple_debug (stmt));
- gimple_debug_bind_reset_value (stmt);
- update_stmt (stmt);
+ copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
+ VAR_DECL, DECL_NAME (adj->base),
+ TREE_TYPE (adj->base));
+ if (DECL_PT_UID_SET_P (adj->base))
+ SET_DECL_PT_UID (copy, DECL_PT_UID (adj->base));
+ TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (adj->base);
+ TREE_READONLY (copy) = TREE_READONLY (adj->base);
+ TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (adj->base);
+ DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (adj->base);
+ DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (adj->base);
+ DECL_IGNORED_P (copy) = DECL_IGNORED_P (adj->base);
+ DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (adj->base);
+ DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
+ SET_DECL_RTL (copy, 0);
+ TREE_USED (copy) = 1;
+ DECL_CONTEXT (copy) = current_function_decl;
+ add_local_decl (cfun, copy);
+ DECL_CHAIN (copy) =
+ BLOCK_VARS (DECL_INITIAL (current_function_decl));
+ BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy;
+ }
+ if (gsip != NULL && copy && target_for_debug_bind (adj->base))
+ {
+ gcc_assert (TREE_CODE (adj->base) == PARM_DECL);
+ if (vexpr)
+ def_temp = gimple_build_debug_bind (copy, vexpr, NULL);
+ else
+ def_temp = gimple_build_debug_source_bind (copy, adj->base,
+ NULL);
+ gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
}
}
}
-/* Return true iff all callers have at least as many actual arguments as there
+/* Return false iff all callers have at least as many actual arguments as there
are formal parameters in the current function. */
static bool
-all_callers_have_enough_arguments_p (struct cgraph_node *node)
+not_all_callers_have_enough_arguments_p (struct cgraph_node *node,
+ void *data ATTRIBUTE_UNUSED)
{
struct cgraph_edge *cs;
for (cs = node->callers; cs; cs = cs->next_caller)
if (!callsite_has_enough_arguments_p (cs->call_stmt))
- return false;
+ return true;
- return true;
+ return false;
}
+/* Convert all callers of NODE. */
-/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS. */
-
-static void
-convert_callers (struct cgraph_node *node, tree old_decl,
- ipa_parm_adjustment_vec adjustments)
+static bool
+convert_callers_for_node (struct cgraph_node *node,
+ void *data)
{
- tree old_cur_fndecl = current_function_decl;
- struct cgraph_edge *cs;
- basic_block this_block;
+ ipa_parm_adjustment_vec *adjustments = (ipa_parm_adjustment_vec *) data;
bitmap recomputed_callers = BITMAP_ALLOC (NULL);
+ struct cgraph_edge *cs;
for (cs = node->callers; cs; cs = cs->next_caller)
{
- current_function_decl = cs->caller->decl;
- push_cfun (DECL_STRUCT_FUNCTION (cs->caller->decl));
+ push_cfun (DECL_STRUCT_FUNCTION (cs->caller->symbol.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));
+ xstrdup (cgraph_node_name (cs->caller)),
+ xstrdup (cgraph_node_name (cs->callee)));
- ipa_modify_call_arguments (cs, cs->call_stmt, adjustments);
+ ipa_modify_call_arguments (cs, cs->call_stmt, *adjustments);
pop_cfun ();
}
for (cs = node->callers; cs; cs = cs->next_caller)
if (bitmap_set_bit (recomputed_callers, cs->caller->uid)
- && gimple_in_ssa_p (DECL_STRUCT_FUNCTION (cs->caller->decl)))
- compute_inline_parameters (cs->caller);
+ && gimple_in_ssa_p (DECL_STRUCT_FUNCTION (cs->caller->symbol.decl)))
+ compute_inline_parameters (cs->caller, true);
BITMAP_FREE (recomputed_callers);
- current_function_decl = old_cur_fndecl;
+ return true;
+}
+
+/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS. */
+
+static void
+convert_callers (struct cgraph_node *node, tree old_decl,
+ ipa_parm_adjustment_vec adjustments)
+{
+ basic_block this_block;
+
+ cgraph_for_node_and_aliases (node, convert_callers_for_node,
+ &adjustments, false);
if (!encountered_recursive_call)
return;
{
if (dump_file)
fprintf (dump_file, "Adjusting recursive call");
- gimple_call_set_fndecl (stmt, node->decl);
+ gimple_call_set_fndecl (stmt, node->symbol.decl);
ipa_modify_call_arguments (NULL, stmt, adjustments);
}
}
modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
{
struct cgraph_node *new_node;
- struct cgraph_edge *cs;
bool cfg_changed;
- VEC (cgraph_edge_p, heap) * redirect_callers;
- int node_callers;
-
- node_callers = 0;
- for (cs = node->callers; cs != NULL; cs = cs->next_caller)
- node_callers++;
- redirect_callers = VEC_alloc (cgraph_edge_p, heap, node_callers);
- for (cs = node->callers; cs != NULL; cs = cs->next_caller)
- VEC_quick_push (cgraph_edge_p, redirect_callers, cs);
+ vec<cgraph_edge_p> redirect_callers = collect_callers_of_node (node);
rebuild_cgraph_edges ();
+ free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
- current_function_decl = NULL_TREE;
- new_node = cgraph_function_versioning (node, redirect_callers, NULL, NULL,
- NULL, NULL, "isra");
- current_function_decl = new_node->decl;
- push_cfun (DECL_STRUCT_FUNCTION (new_node->decl));
+ new_node = cgraph_function_versioning (node, redirect_callers,
+ NULL,
+ NULL, false, NULL, NULL, "isra");
+ redirect_callers.release ();
+ push_cfun (DECL_STRUCT_FUNCTION (new_node->symbol.decl));
ipa_modify_formal_parameters (current_function_decl, adjustments, "ISRA");
cfg_changed = ipa_sra_modify_function_body (adjustments);
sra_ipa_reset_debug_stmts (adjustments);
- convert_callers (new_node, node->decl, adjustments);
+ convert_callers (new_node, node->symbol.decl, adjustments);
cgraph_make_node_local (new_node);
return cfg_changed;
}
return false;
}
- if (!tree_versionable_function_p (node->decl))
+ if (!tree_versionable_function_p (node->symbol.decl))
{
if (dump_file)
fprintf (dump_file, "Function is not versionable.\n");
return false;
}
- if ((DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl))
+ if ((DECL_COMDAT (node->symbol.decl) || DECL_EXTERNAL (node->symbol.decl))
&& inline_summary(node)->size >= MAX_INLINE_INSNS_AUTO)
{
if (dump_file)
return false;
}
- if (TYPE_ATTRIBUTES (TREE_TYPE (node->decl)))
+ if (TYPE_ATTRIBUTES (TREE_TYPE (node->symbol.decl)))
return false;
return true;
goto simple_out;
}
- if (!all_callers_have_enough_arguments_p (node))
+ if (cgraph_for_node_and_aliases (node, not_all_callers_have_enough_arguments_p,
+ NULL, true))
{
if (dump_file)
fprintf (dump_file, "There are callers with insufficient number of "
}
adjustments = analyze_all_param_acesses ();
- if (!adjustments)
+ if (!adjustments.exists ())
goto out;
if (dump_file)
ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl);
ret = TODO_update_ssa | TODO_cleanup_cfg;
else
ret = TODO_update_ssa;
- VEC_free (ipa_parm_adjustment_t, heap, adjustments);
+ adjustments.release ();
statistics_counter_event (cfun, "Unused parameters deleted",
sra_stats.deleted_unused_parameters);
{
GIMPLE_PASS,
"eipa_sra", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
ipa_early_sra_gate, /* gate */
ipa_early_sra, /* execute */
NULL, /* sub */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func | TODO_dump_cgraph /* todo_flags_finish */
+ TODO_dump_symtab /* todo_flags_finish */
}
};
-
-