#include "flags.h"
#include "dbgcnt.h"
#include "tree-inline.h"
+#include "gimple-pretty-print.h"
/* Enumeration of all aggregate reductions we can do. */
enum sra_mode { SRA_MODE_EARLY_IPA, /* early call regularization */
/* Return true iff TYPE is a RECORD_TYPE with fields that are either of gimple
register types or (recursively) records with only these two kinds of fields.
It also returns false if any of these records has a zero-size field as its
- last field. */
+ last field or has a bit-field. */
static bool
type_consists_of_records_p (tree type)
{
tree ft = TREE_TYPE (fld);
+ if (DECL_BIT_FIELD (fld))
+ return false;
+
if (!is_gimple_reg_type (ft)
&& !type_consists_of_records_p (ft))
return false;
/* Create total_scalarization accesses for all scalar type fields in DECL that
must be of a RECORD_TYPE conforming to type_consists_of_records_p. BASE
must be the top-most VAR_DECL representing the variable, OFFSET must be the
- offset of DECL within BASE. */
+ offset of DECL within BASE. REF must be the memory reference expression for
+ the given decl. */
static void
-completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset)
+completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset,
+ tree ref)
{
tree fld, decl_type = TREE_TYPE (decl);
{
HOST_WIDE_INT pos = offset + int_bit_position (fld);
tree ft = TREE_TYPE (fld);
+ tree nref = build3 (COMPONENT_REF, TREE_TYPE (fld), ref, fld,
+ NULL_TREE);
if (is_gimple_reg_type (ft))
{
struct access *access;
HOST_WIDE_INT size;
- tree expr;
- bool ok;
size = tree_low_cst (DECL_SIZE (fld), 1);
- expr = base;
- ok = build_ref_for_offset (&expr, TREE_TYPE (base), pos,
- ft, false);
- gcc_assert (ok);
-
access = create_access_1 (base, pos, size);
- access->expr = expr;
+ access->expr = nref;
access->type = ft;
access->total_scalarization = 1;
/* Accesses for intraprocedural SRA can have their stmt NULL. */
}
else
- completely_scalarize_record (base, fld, pos);
+ completely_scalarize_record (base, fld, pos, nref);
}
}
<= max_total_scalarization_size)
&& type_consists_of_records_p (TREE_TYPE (var)))
{
- completely_scalarize_record (var, var, 0);
+ completely_scalarize_record (var, var, 0, var);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Will attempt to totally scalarize ");
}
}
-/* Create a new suitable default definition SSA_NAME and replace all uses of
- SSA with it, RACC is access describing the uninitialized part of an
- aggregate that is being loaded. */
+/* Create and return a new suitable default definition SSA_NAME for RACC which
+ is an access describing an uninitialized part of an aggregate that is being
+ loaded. */
-static void
-replace_uses_with_default_def_ssa_name (tree ssa, struct access *racc)
+static tree
+get_repl_default_def_ssa_name (struct access *racc)
{
tree repl, decl;
set_default_def (decl, repl);
}
- replace_uses_by (ssa, repl);
+ return repl;
}
/* Examine both sides of the assignment statement pointed to by STMT, replace
{
if (!racc->grp_to_be_replaced && !racc->grp_unscalarized_data)
{
- if (racc->first_child)
- generate_subtree_copies (racc->first_child, lhs,
- racc->offset, 0, 0, gsi,
- false, false);
- gcc_assert (*stmt == gsi_stmt (*gsi));
- if (TREE_CODE (lhs) == SSA_NAME)
- replace_uses_with_default_def_ssa_name (lhs, racc);
+ if (dump_file)
+ {
+ fprintf (dump_file, "Removing load: ");
+ print_gimple_stmt (dump_file, *stmt, 0, 0);
+ }
- unlink_stmt_vdef (*stmt);
- gsi_remove (gsi, true);
- sra_stats.deleted++;
- return SRA_AM_REMOVED;
+ 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);
+
+ gcc_assert (*stmt == gsi_stmt (*gsi));
+ unlink_stmt_vdef (*stmt);
+ gsi_remove (gsi, true);
+ sra_stats.deleted++;
+ return SRA_AM_REMOVED;
+ }
}
else if (racc->first_child)
generate_subtree_copies (racc->first_child, lhs,
}
/* Traverse the function body and all modifications as decided in
- analyze_all_variable_accesses. */
+ analyze_all_variable_accesses. Return true iff the CFG has been
+ changed. */
-static void
+static bool
sra_modify_function_body (void)
{
+ bool cfg_changed = false;
basic_block bb;
FOR_EACH_BB (bb)
if (modified)
{
update_stmt (stmt);
- maybe_clean_eh_stmt (stmt);
+ if (maybe_clean_eh_stmt (stmt)
+ && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+ cfg_changed = true;
}
if (!deleted)
gsi_next (&gsi);
}
}
+
+ return cfg_changed;
}
/* Generate statements initializing scalar replacements of parts of function
if (!analyze_all_variable_accesses ())
goto out;
- sra_modify_function_body ();
+ if (sra_modify_function_body ())
+ ret = TODO_update_ssa | TODO_cleanup_cfg;
+ else
+ ret = TODO_update_ssa;
initialize_parameter_reductions ();
statistics_counter_event (cfun, "Scalar replacements created",
statistics_counter_event (cfun, "Separate LHS and RHS handling",
sra_stats.separate_lhs_rhs_handling);
- ret = TODO_update_ssa;
-
out:
sra_deinitialize ();
return ret;
if (TREE_CODE (type) == FUNCTION_TYPE
|| TYPE_VOLATILE (type)
+ || (TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_NONALIASED_COMPONENT (type))
|| !is_gimple_reg (parm)
|| is_va_list_type (type)
|| ptr_parm_has_direct_uses (parm))
}
/* Traverse the function body and all modifications as described in
- ADJUSTMENTS. */
+ ADJUSTMENTS. Return true iff the CFG has been changed. */
-static void
+static bool
ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments)
{
+ bool cfg_changed = false;
basic_block bb;
FOR_EACH_BB (bb)
{
gimple_stmt_iterator gsi;
- bool bb_changed = false;
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
replace_removed_params_ssa_names (gsi_stmt (gsi), adjustments);
if (modified)
{
- bb_changed = true;
update_stmt (stmt);
- maybe_clean_eh_stmt (stmt);
+ if (maybe_clean_eh_stmt (stmt)
+ && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+ cfg_changed = true;
}
gsi_next (&gsi);
}
- if (bb_changed)
- gimple_purge_dead_eh_edges (bb);
}
+
+ return cfg_changed;
}
/* Call gimple_debug_bind_reset_value on all debug statements describing
}
/* Perform all the modification required in IPA-SRA for NODE to have parameters
- as given in ADJUSTMENTS. */
+ as given in ADJUSTMENTS. Return true iff the CFG has been changed. */
-static void
+static bool
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;
push_cfun (DECL_STRUCT_FUNCTION (new_node->decl));
ipa_modify_formal_parameters (current_function_decl, adjustments, "ISRA");
- ipa_sra_modify_function_body (adjustments);
+ cfg_changed = ipa_sra_modify_function_body (adjustments);
sra_ipa_reset_debug_stmts (adjustments);
convert_callers (new_node, node->decl, adjustments);
cgraph_make_node_local (new_node);
- return;
+ return cfg_changed;
}
/* Return false the function is apparently unsuitable for IPA-SRA based on it's
if (!tree_versionable_function_p (node->decl))
{
if (dump_file)
- fprintf (dump_file, "Function not local to this compilation unit.\n");
+ fprintf (dump_file, "Function is not versionable.\n");
return false;
}
if (dump_file)
ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl);
- modify_function (node, adjustments);
+ if (modify_function (node, adjustments))
+ ret = TODO_update_ssa | TODO_cleanup_cfg;
+ else
+ ret = TODO_update_ssa;
VEC_free (ipa_parm_adjustment_t, heap, adjustments);
- ret = TODO_update_ssa;
statistics_counter_event (cfun, "Unused parameters deleted",
sra_stats.deleted_unused_parameters);
static bool
ipa_early_sra_gate (void)
{
- return flag_ipa_sra;
+ return flag_ipa_sra && dbg_cnt (eipa_sra);
}
struct gimple_opt_pass pass_early_ipa_sra =