+
+/* Return true if STMT is of the form 'mem_ref = RHS', where 'mem_ref'
+ is a non-volatile pointer dereference, a structure reference or a
+ reference to a single _DECL. Ignore volatile memory references
+ because they are not interesting for the optimizers. */
+
+bool
+stmt_makes_single_store (gimple stmt)
+{
+ tree lhs;
+
+ if (gimple_code (stmt) != GIMPLE_ASSIGN
+ && gimple_code (stmt) != GIMPLE_CALL)
+ return false;
+
+ if (!gimple_vdef (stmt))
+ return false;
+
+ lhs = gimple_get_lhs (stmt);
+
+ /* A call statement may have a null LHS. */
+ if (!lhs)
+ return false;
+
+ return (!TREE_THIS_VOLATILE (lhs)
+ && (DECL_P (lhs)
+ || REFERENCE_CLASS_P (lhs)));
+}
+
+
+/* Propagation statistics. */
+struct prop_stats_d
+{
+ long num_const_prop;
+ long num_copy_prop;
+ long num_stmts_folded;
+ long num_dce;
+};
+
+static struct prop_stats_d prop_stats;
+
+/* Replace USE references in statement STMT with the values stored in
+ PROP_VALUE. Return true if at least one reference was replaced. */
+
+static bool
+replace_uses_in (gimple stmt, prop_value_t *prop_value)
+{
+ bool replaced = false;
+ use_operand_p use;
+ ssa_op_iter iter;
+
+ FOR_EACH_SSA_USE_OPERAND (use, stmt, iter, SSA_OP_USE)
+ {
+ tree tuse = USE_FROM_PTR (use);
+ tree val = prop_value[SSA_NAME_VERSION (tuse)].value;
+
+ if (val == tuse || val == NULL_TREE)
+ continue;
+
+ if (gimple_code (stmt) == GIMPLE_ASM
+ && !may_propagate_copy_into_asm (tuse))
+ continue;
+
+ if (!may_propagate_copy (tuse, val))
+ continue;
+
+ if (TREE_CODE (val) != SSA_NAME)
+ prop_stats.num_const_prop++;
+ else
+ prop_stats.num_copy_prop++;
+
+ propagate_value (use, val);
+
+ replaced = true;
+ }
+
+ return replaced;
+}
+
+
+/* Replace propagated values into all the arguments for PHI using the
+ values from PROP_VALUE. */
+
+static void
+replace_phi_args_in (gimple phi, prop_value_t *prop_value)
+{
+ size_t i;
+ bool replaced = false;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Folding PHI node: ");
+ print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
+ }
+
+ for (i = 0; i < gimple_phi_num_args (phi); i++)
+ {
+ tree arg = gimple_phi_arg_def (phi, i);
+
+ if (TREE_CODE (arg) == SSA_NAME)
+ {
+ tree val = prop_value[SSA_NAME_VERSION (arg)].value;
+
+ if (val && val != arg && may_propagate_copy (arg, val))
+ {
+ if (TREE_CODE (val) != SSA_NAME)
+ prop_stats.num_const_prop++;
+ else
+ prop_stats.num_copy_prop++;
+
+ propagate_value (PHI_ARG_DEF_PTR (phi, i), val);
+ replaced = true;
+
+ /* If we propagated a copy and this argument flows
+ through an abnormal edge, update the replacement
+ accordingly. */
+ if (TREE_CODE (val) == SSA_NAME
+ && gimple_phi_arg_edge (phi, i)->flags & EDGE_ABNORMAL)
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (val) = 1;
+ }
+ }
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ if (!replaced)
+ fprintf (dump_file, "No folding possible\n");
+ else
+ {
+ fprintf (dump_file, "Folded into: ");
+ print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
+ fprintf (dump_file, "\n");
+ }
+ }
+}
+
+
+/* Perform final substitution and folding of propagated values.
+
+ PROP_VALUE[I] contains the single value that should be substituted
+ at every use of SSA name N_I. If PROP_VALUE is NULL, no values are
+ substituted.
+
+ If FOLD_FN is non-NULL the function will be invoked on all statements
+ before propagating values for pass specific simplification.
+
+ Return TRUE when something changed. */
+
+bool
+substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
+{
+ basic_block bb;
+ bool something_changed = false;
+
+ if (prop_value == NULL && !fold_fn)
+ return false;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nSubstituting values and folding statements\n\n");
+
+ memset (&prop_stats, 0, sizeof (prop_stats));
+
+ /* Substitute values in every statement of every basic block. */
+ FOR_EACH_BB (bb)
+ {
+ gimple_stmt_iterator i;
+
+ /* Propagate known values into PHI nodes. */
+ if (prop_value)
+ for (i = gsi_start_phis (bb); !gsi_end_p (i); gsi_next (&i))
+ replace_phi_args_in (gsi_stmt (i), prop_value);
+
+ /* Propagate known values into stmts. Do a backward walk to expose
+ more trivially deletable stmts. */
+ for (i = gsi_last_bb (bb); !gsi_end_p (i);)
+ {
+ bool did_replace;
+ gimple stmt = gsi_stmt (i);
+ gimple old_stmt;
+ enum gimple_code code = gimple_code (stmt);
+ gimple_stmt_iterator oldi;
+
+ oldi = i;
+ gsi_prev (&i);
+
+ /* Ignore ASSERT_EXPRs. They are used by VRP to generate
+ range information for names and they are discarded
+ afterwards. */
+
+ if (code == GIMPLE_ASSIGN
+ && TREE_CODE (gimple_assign_rhs1 (stmt)) == ASSERT_EXPR)
+ continue;
+
+ /* No point propagating into a stmt whose result is not used,
+ but instead we might be able to remove a trivially dead stmt. */
+ if (gimple_get_lhs (stmt)
+ && TREE_CODE (gimple_get_lhs (stmt)) == SSA_NAME
+ && has_zero_uses (gimple_get_lhs (stmt))
+ && !stmt_could_throw_p (stmt)
+ && !gimple_has_side_effects (stmt))
+ {
+ gimple_stmt_iterator i2;
+
+ if (dump_file && dump_flags & TDF_DETAILS)
+ {
+ fprintf (dump_file, "Removing dead stmt ");
+ print_gimple_stmt (dump_file, stmt, 0, 0);
+ fprintf (dump_file, "\n");
+ }
+ prop_stats.num_dce++;
+ i2 = gsi_for_stmt (stmt);
+ gsi_remove (&i2, true);
+ release_defs (stmt);
+ continue;
+ }
+
+ /* Replace the statement with its folded version and mark it
+ folded. */
+ did_replace = false;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Folding statement: ");
+ print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+ }
+
+ old_stmt = stmt;
+
+ /* Some statements may be simplified using propagator
+ specific information. Do this before propagating
+ into the stmt to not disturb pass specific information. */
+ if (fold_fn
+ && (*fold_fn)(&oldi))
+ {
+ did_replace = true;
+ prop_stats.num_stmts_folded++;
+ }
+
+ /* Only replace real uses if we couldn't fold the
+ statement using value range information. */
+ if (prop_value
+ && !did_replace)
+ did_replace |= replace_uses_in (stmt, prop_value);
+
+ /* If we made a replacement, fold the statement. */
+ if (did_replace)
+ fold_stmt (&oldi);
+
+ /* Now cleanup. */
+ if (did_replace)
+ {
+ stmt = gsi_stmt (oldi);
+
+ /* If we cleaned up EH information from the statement,
+ remove EH edges. */
+ if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
+ gimple_purge_dead_eh_edges (bb);
+
+ if (is_gimple_assign (stmt)
+ && (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
+ == GIMPLE_SINGLE_RHS))
+ {
+ tree rhs = gimple_assign_rhs1 (stmt);
+
+ if (TREE_CODE (rhs) == ADDR_EXPR)
+ recompute_tree_invariant_for_addr_expr (rhs);
+ }
+
+ /* Determine what needs to be done to update the SSA form. */
+ update_stmt (stmt);
+ if (!is_gimple_debug (stmt))
+ something_changed = true;
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ if (did_replace)
+ {
+ fprintf (dump_file, "Folded into: ");
+ print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+ fprintf (dump_file, "\n");
+ }
+ else
+ fprintf (dump_file, "Not folded\n");
+ }
+ }
+ }
+
+ statistics_counter_event (cfun, "Constants propagated",
+ prop_stats.num_const_prop);
+ statistics_counter_event (cfun, "Copies propagated",
+ prop_stats.num_copy_prop);
+ statistics_counter_event (cfun, "Statements folded",
+ prop_stats.num_stmts_folded);
+ statistics_counter_event (cfun, "Statements deleted",
+ prop_stats.num_dce);
+ return something_changed;
+}
+