On Wed, 14 May 2014, Richard Biener wrote: > > This makes FRE/PRE substitute values into all uses instead of leaving > copies and initializations from constants in the IL which requires > a copyprop pass to clean up things (which we usually place directly > after FRE/PRE). > > This should open the possibility to remove some of the passes, > respectively the two copy-prop passes right after the two FREs > and the copyprop pass we do early in loop opts. > > The patch exposes a weakness in invariant motion cost computation > and thus gfortran.dg/vect/fast-math-vect-8.f90 fails for me > on i?86 (but not on x86_64). > > I want to revisit some of the ???s and uglinesses in the patch, > but this is a state that passes bootstrap and regtest otherwise > and thus ready. > > Any comments?
Ok, so here goes the final variant I am about to commit. Bootstrapped and tested on x86_64-unknown-linux-gnu. Richard. 2014-06-13 Richard Biener <rguent...@suse.de> * tree-ssa-pre.c (eliminate_dom_walker::before_dom_children): Rewrite to propagate the VN result into all uses where possible and to remove stmts becoming dead because of that. (eliminate): Generalize stmt removal handling, remove in reverse dominator order to support proper debug stmt generation. Update stmts before removing stmts. * tree-ssa-propagate.c (propagate_tree_value): Remove bogus assert. * c-c++-common/pr46562-2.c: Adjust. * g++.dg/tree-ssa/pr8781.C: Likewise. * gcc.dg/tree-ssa/ssa-fre-24.c: Likewise. * gcc.dg/tree-ssa/ssa-fre-25.c: Likewise. * gcc.dg/tree-ssa/ssa-fre-32.c: Likewise. * gcc.dg/tree-ssa/ssa-fre-39.c: Likewise. * gcc.dg/tree-ssa/ssa-pre-16.c: Likewise. Index: trunk/gcc/tree-ssa-pre.c =================================================================== *** trunk.orig/gcc/tree-ssa-pre.c 2014-06-12 13:05:07.459653680 +0200 --- trunk/gcc/tree-ssa-pre.c 2014-06-13 09:48:28.646517479 +0200 *************** eliminate_dom_walker::before_dom_childre *** 4012,4131 **** /* Mark new bb. */ el_avail_stack.safe_push (NULL_TREE); ! /* If this block is not reachable do nothing. */ ! edge_iterator ei; ! edge e; ! FOR_EACH_EDGE (e, ei, b->preds) ! if (e->flags & EDGE_EXECUTABLE) ! break; ! if (!e) ! return; for (gsi = gsi_start_phis (b); !gsi_end_p (gsi);) { ! gimple stmt, phi = gsi_stmt (gsi); ! tree sprime = NULL_TREE, res = PHI_RESULT (phi); ! gimple_stmt_iterator gsi2; ! ! /* We want to perform redundant PHI elimination. Do so by ! replacing the PHI with a single copy if possible. ! Do not touch inserted, single-argument or virtual PHIs. */ ! if (gimple_phi_num_args (phi) == 1 ! || virtual_operand_p (res)) ! { ! gsi_next (&gsi); ! continue; ! } ! sprime = eliminate_avail (res); ! if (!sprime ! || sprime == res) { - eliminate_push_avail (res); gsi_next (&gsi); continue; } - else if (is_gimple_min_invariant (sprime)) - { - if (!useless_type_conversion_p (TREE_TYPE (res), - TREE_TYPE (sprime))) - sprime = fold_convert (TREE_TYPE (res), sprime); - } ! if (dump_file && (dump_flags & TDF_DETAILS)) { ! fprintf (dump_file, "Replaced redundant PHI node defining "); ! print_generic_expr (dump_file, res, 0); ! fprintf (dump_file, " with "); ! print_generic_expr (dump_file, sprime, 0); ! fprintf (dump_file, "\n"); ! } ! remove_phi_node (&gsi, false); ! if (inserted_exprs ! && !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res)) ! && TREE_CODE (sprime) == SSA_NAME) ! gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true); ! ! if (!useless_type_conversion_p (TREE_TYPE (res), TREE_TYPE (sprime))) ! sprime = fold_convert (TREE_TYPE (res), sprime); ! stmt = gimple_build_assign (res, sprime); ! gimple_set_plf (stmt, NECESSARY, gimple_plf (phi, NECESSARY)); ! ! gsi2 = gsi_after_labels (b); ! gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT); ! /* Queue the copy for eventual removal. */ ! el_to_remove.safe_push (stmt); ! /* If we inserted this PHI node ourself, it's not an elimination. */ ! if (inserted_exprs ! && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res))) ! pre_stats.phis--; ! else ! pre_stats.eliminations++; ! } ! for (gsi = gsi_start_bb (b); !gsi_end_p (gsi); gsi_next (&gsi)) ! { ! tree lhs = NULL_TREE; ! tree rhs = NULL_TREE; ! stmt = gsi_stmt (gsi); ! ! if (gimple_has_lhs (stmt)) ! lhs = gimple_get_lhs (stmt); ! if (gimple_assign_single_p (stmt)) ! rhs = gimple_assign_rhs1 (stmt); ! /* Lookup the RHS of the expression, see if we have an ! available computation for it. If so, replace the RHS with ! the available computation. */ ! if (gimple_has_lhs (stmt) ! && TREE_CODE (lhs) == SSA_NAME ! && !gimple_has_volatile_ops (stmt)) ! { ! tree sprime; ! gimple orig_stmt = stmt; ! sprime = eliminate_avail (lhs); ! /* If there is no usable leader mark lhs as leader for its value. */ ! if (!sprime) ! eliminate_push_avail (lhs); /* See PR43491. Do not replace a global register variable when it is a the RHS of an assignment. Do replace local register variables since gcc does not guarantee a local variable will be allocated in register. ! Do not perform copy propagation or undo constant propagation. */ ! if (gimple_assign_single_p (stmt) ! && (TREE_CODE (rhs) == SSA_NAME ! || is_gimple_min_invariant (rhs) ! || (TREE_CODE (rhs) == VAR_DECL ! && is_global_var (rhs) ! && DECL_HARD_REGISTER (rhs)))) ! continue; ! if (!sprime) { /* If there is no existing usable leader but SCCVN thinks --- 4012,4104 ---- /* Mark new bb. */ el_avail_stack.safe_push (NULL_TREE); ! /* ??? If we do nothing for unreachable blocks then this will confuse ! tailmerging. Eventually we can reduce its reliance on SCCVN now ! that we fully copy/constant-propagate (most) things. */ for (gsi = gsi_start_phis (b); !gsi_end_p (gsi);) { ! gimple phi = gsi_stmt (gsi); ! tree res = PHI_RESULT (phi); ! if (virtual_operand_p (res)) { gsi_next (&gsi); continue; } ! tree sprime = eliminate_avail (res); ! if (sprime ! && sprime != res) { ! if (dump_file && (dump_flags & TDF_DETAILS)) ! { ! fprintf (dump_file, "Replaced redundant PHI node defining "); ! print_generic_expr (dump_file, res, 0); ! fprintf (dump_file, " with "); ! print_generic_expr (dump_file, sprime, 0); ! fprintf (dump_file, "\n"); ! } ! /* If we inserted this PHI node ourself, it's not an elimination. */ ! if (inserted_exprs ! && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res))) ! pre_stats.phis--; ! else ! pre_stats.eliminations++; ! /* If we will propagate into all uses don't bother to do ! anything. */ ! if (may_propagate_copy (res, sprime)) ! { ! /* Mark the PHI for removal. */ ! el_to_remove.safe_push (phi); ! gsi_next (&gsi); ! continue; ! } ! remove_phi_node (&gsi, false); ! if (inserted_exprs ! && !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res)) ! && TREE_CODE (sprime) == SSA_NAME) ! gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true); ! if (!useless_type_conversion_p (TREE_TYPE (res), TREE_TYPE (sprime))) ! sprime = fold_convert (TREE_TYPE (res), sprime); ! gimple stmt = gimple_build_assign (res, sprime); ! /* ??? It cannot yet be necessary (DOM walk). */ ! gimple_set_plf (stmt, NECESSARY, gimple_plf (phi, NECESSARY)); ! gimple_stmt_iterator gsi2 = gsi_after_labels (b); ! gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT); ! continue; ! } ! eliminate_push_avail (res); ! gsi_next (&gsi); ! } + for (gsi = gsi_start_bb (b); !gsi_end_p (gsi); gsi_next (&gsi)) + { + tree sprime = NULL_TREE; + stmt = gsi_stmt (gsi); + tree lhs = gimple_get_lhs (stmt); + if (lhs && TREE_CODE (lhs) == SSA_NAME + && !gimple_has_volatile_ops (stmt) /* See PR43491. Do not replace a global register variable when it is a the RHS of an assignment. Do replace local register variables since gcc does not guarantee a local variable will be allocated in register. ! ??? The fix isn't effective here. This should instead ! be ensured by not value-numbering them the same but treating ! them like volatiles? */ ! && !(gimple_assign_single_p (stmt) ! && (TREE_CODE (gimple_assign_rhs1 (stmt)) == VAR_DECL ! && DECL_HARD_REGISTER (gimple_assign_rhs1 (stmt)) ! && is_global_var (gimple_assign_rhs1 (stmt))))) ! { ! sprime = eliminate_avail (lhs); if (!sprime) { /* If there is no existing usable leader but SCCVN thinks *************** eliminate_dom_walker::before_dom_childre *** 4139,4246 **** && (sprime = eliminate_insert (&gsi, val)) != NULL_TREE) eliminate_push_avail (sprime); } - else if (is_gimple_min_invariant (sprime)) - { - /* If there is no existing leader but SCCVN knows this - value is constant, use that constant. */ - if (!useless_type_conversion_p (TREE_TYPE (lhs), - TREE_TYPE (sprime))) - sprime = fold_convert (TREE_TYPE (lhs), sprime); ! if (dump_file && (dump_flags & TDF_DETAILS)) ! { ! fprintf (dump_file, "Replaced "); ! print_gimple_expr (dump_file, stmt, 0, 0); ! fprintf (dump_file, " with "); ! print_generic_expr (dump_file, sprime, 0); ! fprintf (dump_file, " in "); ! print_gimple_stmt (dump_file, stmt, 0, 0); ! } ! pre_stats.eliminations++; ! ! tree vdef = gimple_vdef (stmt); ! tree vuse = gimple_vuse (stmt); ! propagate_tree_value_into_stmt (&gsi, sprime); ! stmt = gsi_stmt (gsi); ! update_stmt (stmt); ! if (vdef != gimple_vdef (stmt)) ! VN_INFO (vdef)->valnum = vuse; ! ! /* If we removed EH side-effects from the statement, clean ! its EH information. */ ! if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt)) ! { ! bitmap_set_bit (need_eh_cleanup, ! gimple_bb (stmt)->index); ! if (dump_file && (dump_flags & TDF_DETAILS)) ! fprintf (dump_file, " Removed EH side-effects.\n"); ! } ! continue; } if (sprime ! && sprime != lhs ! && (rhs == NULL_TREE ! || TREE_CODE (rhs) != SSA_NAME ! || may_propagate_copy (rhs, sprime))) { ! bool can_make_abnormal_goto ! = is_gimple_call (stmt) ! && stmt_can_make_abnormal_goto (stmt); ! ! gcc_assert (sprime != rhs); ! ! /* Inhibit the use of an inserted PHI on a loop header when ! the address of the memory reference is a simple induction ! variable. In other cases the vectorizer won't do anything ! anyway (either it's loop invariant or a complicated ! expression). */ ! if (do_pre ! && flag_tree_loop_vectorize ! && gimple_assign_single_p (stmt) ! && TREE_CODE (sprime) == SSA_NAME ! && loop_outer (b->loop_father)) ! { ! gimple def_stmt = SSA_NAME_DEF_STMT (sprime); ! basic_block def_bb = gimple_bb (def_stmt); ! if (gimple_code (def_stmt) == GIMPLE_PHI ! && b->loop_father->header == def_bb ! && has_zero_uses (sprime)) { ! ssa_op_iter iter; ! tree op; ! bool found = false; ! FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE) { ! affine_iv iv; ! def_bb = gimple_bb (SSA_NAME_DEF_STMT (op)); ! if (def_bb ! && flow_bb_inside_loop_p (b->loop_father, ! def_bb) ! && simple_iv (b->loop_father, ! b->loop_father, op, &iv, true)) ! { ! found = true; ! break; ! } } ! if (found) { ! if (dump_file && (dump_flags & TDF_DETAILS)) ! { ! fprintf (dump_file, "Not replacing "); ! print_gimple_expr (dump_file, stmt, 0, 0); ! fprintf (dump_file, " with "); ! print_generic_expr (dump_file, sprime, 0); ! fprintf (dump_file, " which would add a loop" ! " carried dependence to loop %d\n", ! b->loop_father->num); ! } ! continue; } } } if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Replaced "); --- 4112,4240 ---- && (sprime = eliminate_insert (&gsi, val)) != NULL_TREE) eliminate_push_avail (sprime); } ! /* If this now constitutes a copy duplicate points-to ! and range info appropriately. This is especially ! important for inserted code. See tree-ssa-copy.c ! for similar code. */ ! if (sprime ! && TREE_CODE (sprime) == SSA_NAME) ! { ! basic_block sprime_b = gimple_bb (SSA_NAME_DEF_STMT (sprime)); ! if (POINTER_TYPE_P (TREE_TYPE (lhs)) ! && SSA_NAME_PTR_INFO (lhs) ! && !SSA_NAME_PTR_INFO (sprime)) ! { ! duplicate_ssa_name_ptr_info (sprime, ! SSA_NAME_PTR_INFO (lhs)); ! if (b != sprime_b) ! mark_ptr_info_alignment_unknown ! (SSA_NAME_PTR_INFO (sprime)); ! } ! else if (!POINTER_TYPE_P (TREE_TYPE (lhs)) ! && SSA_NAME_RANGE_INFO (lhs) ! && !SSA_NAME_RANGE_INFO (sprime) ! && b == sprime_b) ! duplicate_ssa_name_range_info (sprime, ! SSA_NAME_RANGE_TYPE (lhs), ! SSA_NAME_RANGE_INFO (lhs)); } + /* Inhibit the use of an inserted PHI on a loop header when + the address of the memory reference is a simple induction + variable. In other cases the vectorizer won't do anything + anyway (either it's loop invariant or a complicated + expression). */ if (sprime ! && TREE_CODE (sprime) == SSA_NAME ! && do_pre ! && flag_tree_loop_vectorize ! && loop_outer (b->loop_father) ! && has_zero_uses (sprime) ! && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime)) ! && gimple_assign_load_p (stmt)) { ! gimple def_stmt = SSA_NAME_DEF_STMT (sprime); ! basic_block def_bb = gimple_bb (def_stmt); ! if (gimple_code (def_stmt) == GIMPLE_PHI ! && b->loop_father->header == def_bb) ! { ! ssa_op_iter iter; ! tree op; ! bool found = false; ! FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE) { ! affine_iv iv; ! def_bb = gimple_bb (SSA_NAME_DEF_STMT (op)); ! if (def_bb ! && flow_bb_inside_loop_p (b->loop_father, def_bb) ! && simple_iv (b->loop_father, ! b->loop_father, op, &iv, true)) { ! found = true; ! break; } ! } ! if (found) ! { ! if (dump_file && (dump_flags & TDF_DETAILS)) { ! fprintf (dump_file, "Not replacing "); ! print_gimple_expr (dump_file, stmt, 0, 0); ! fprintf (dump_file, " with "); ! print_generic_expr (dump_file, sprime, 0); ! fprintf (dump_file, " which would add a loop" ! " carried dependence to loop %d\n", ! b->loop_father->num); } + /* Don't keep sprime available. */ + eliminate_push_avail (lhs); + sprime = NULL_TREE; + } + } + } + + if (sprime) + { + /* If we can propagate the value computed for LHS into + all uses don't bother doing anything with this stmt. */ + if (may_propagate_copy (lhs, sprime)) + { + /* Mark it for removal. */ + el_to_remove.safe_push (stmt); + + /* ??? Don't count copy/constant propagations. */ + if (gimple_assign_single_p (stmt) + && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME + || gimple_assign_rhs1 (stmt) == sprime)) + continue; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Replaced "); + print_gimple_expr (dump_file, stmt, 0, 0); + fprintf (dump_file, " with "); + print_generic_expr (dump_file, sprime, 0); + fprintf (dump_file, " in all uses of "); + print_gimple_stmt (dump_file, stmt, 0, 0); } + + pre_stats.eliminations++; + continue; } + /* If this is an assignment from our leader (which + happens in the case the value-number is a constant) + then there is nothing to do. */ + if (gimple_assign_single_p (stmt) + && sprime == gimple_assign_rhs1 (stmt)) + continue; + + /* Else replace its RHS. */ + bool can_make_abnormal_goto + = is_gimple_call (stmt) + && stmt_can_make_abnormal_goto (stmt); + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Replaced "); *************** eliminate_dom_walker::before_dom_childre *** 4254,4269 **** if (TREE_CODE (sprime) == SSA_NAME) gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true); - /* We need to make sure the new and old types actually match, - which may require adding a simple cast, which fold_convert - will do for us. */ - if ((!rhs || TREE_CODE (rhs) != SSA_NAME) - && !useless_type_conversion_p (gimple_expr_type (stmt), - TREE_TYPE (sprime))) - sprime = fold_convert (gimple_expr_type (stmt), sprime); pre_stats.eliminations++; ! tree vdef = gimple_vdef (stmt); tree vuse = gimple_vuse (stmt); propagate_tree_value_into_stmt (&gsi, sprime); --- 4248,4259 ---- if (TREE_CODE (sprime) == SSA_NAME) gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true); pre_stats.eliminations++; ! gimple orig_stmt = stmt; ! if (!useless_type_conversion_p (TREE_TYPE (lhs), ! TREE_TYPE (sprime))) ! sprime = fold_convert (TREE_TYPE (lhs), sprime); tree vdef = gimple_vdef (stmt); tree vuse = gimple_vuse (stmt); propagate_tree_value_into_stmt (&gsi, sprime); *************** eliminate_dom_walker::before_dom_childre *** 4291,4425 **** if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " Removed AB side-effects.\n"); } } } /* If the statement is a scalar store, see if the expression ! has the same value number as its rhs. If so, the store is ! dead. */ ! else if (gimple_assign_single_p (stmt) ! && !gimple_has_volatile_ops (stmt) ! && !is_gimple_reg (gimple_assign_lhs (stmt)) ! && (TREE_CODE (rhs) == SSA_NAME ! || is_gimple_min_invariant (rhs))) ! { ! tree val; ! val = vn_reference_lookup (gimple_assign_lhs (stmt), ! gimple_vuse (stmt), VN_WALK, NULL); ! if (TREE_CODE (rhs) == SSA_NAME) ! rhs = VN_INFO (rhs)->valnum; ! if (val ! && operand_equal_p (val, rhs, 0)) ! { ! if (dump_file && (dump_flags & TDF_DETAILS)) ! { ! fprintf (dump_file, "Deleted redundant store "); ! print_gimple_stmt (dump_file, stmt, 0, 0); ! } ! /* Queue stmt for removal. */ ! el_to_remove.safe_push (stmt); ! } ! } ! /* Visit COND_EXPRs and fold the comparison with the ! available value-numbers. */ ! else if (gimple_code (stmt) == GIMPLE_COND) { ! tree op0 = gimple_cond_lhs (stmt); ! tree op1 = gimple_cond_rhs (stmt); ! tree result; ! ! if (TREE_CODE (op0) == SSA_NAME) ! op0 = VN_INFO (op0)->valnum; ! if (TREE_CODE (op1) == SSA_NAME) ! op1 = VN_INFO (op1)->valnum; ! result = fold_binary (gimple_cond_code (stmt), boolean_type_node, ! op0, op1); ! if (result && TREE_CODE (result) == INTEGER_CST) ! { ! if (integer_zerop (result)) ! gimple_cond_make_false (stmt); ! else ! gimple_cond_make_true (stmt); ! update_stmt (stmt); ! el_todo = TODO_cleanup_cfg; } } /* Visit indirect calls and turn them into direct calls if ! possible. */ if (is_gimple_call (stmt)) { ! tree orig_fn = gimple_call_fn (stmt); ! tree fn; ! if (!orig_fn) ! continue; ! if (TREE_CODE (orig_fn) == SSA_NAME) ! fn = VN_INFO (orig_fn)->valnum; ! else if (TREE_CODE (orig_fn) == OBJ_TYPE_REF ! && TREE_CODE (OBJ_TYPE_REF_EXPR (orig_fn)) == SSA_NAME) { ! fn = VN_INFO (OBJ_TYPE_REF_EXPR (orig_fn))->valnum; ! if (!gimple_call_addr_fndecl (fn)) { ! fn = ipa_intraprocedural_devirtualization (stmt); ! if (fn) ! fn = build_fold_addr_expr (fn); } } ! else ! continue; ! if (gimple_call_addr_fndecl (fn) != NULL_TREE ! && useless_type_conversion_p (TREE_TYPE (orig_fn), ! TREE_TYPE (fn)) ! && dbg_cnt (devirt)) ! { ! bool can_make_abnormal_goto ! = stmt_can_make_abnormal_goto (stmt); ! bool was_noreturn = gimple_call_noreturn_p (stmt); ! ! if (dump_enabled_p ()) ! { ! location_t loc = gimple_location (stmt); ! dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, ! "converting indirect call to function %s\n", ! cgraph_get_node (gimple_call_addr_fndecl (fn))->name ()); ! } ! ! gimple_call_set_fn (stmt, fn); ! tree vdef = gimple_vdef (stmt); ! tree vuse = gimple_vuse (stmt); ! update_stmt (stmt); ! if (vdef != gimple_vdef (stmt)) ! VN_INFO (vdef)->valnum = vuse; /* When changing a call into a noreturn call, cfg cleanup is needed to fix up the noreturn call. */ if (!was_noreturn && gimple_call_noreturn_p (stmt)) el_todo |= TODO_cleanup_cfg; ! /* If we removed EH side-effects from the statement, clean ! its EH information. */ ! if (maybe_clean_or_replace_eh_stmt (stmt, stmt)) ! { ! bitmap_set_bit (need_eh_cleanup, ! gimple_bb (stmt)->index); ! if (dump_file && (dump_flags & TDF_DETAILS)) ! fprintf (dump_file, " Removed EH side-effects.\n"); ! } ! ! /* Likewise for AB side-effects. */ ! if (can_make_abnormal_goto ! && !stmt_can_make_abnormal_goto (stmt)) ! { ! bitmap_set_bit (need_ab_cleanup, ! gimple_bb (stmt)->index); ! if (dump_file && (dump_flags & TDF_DETAILS)) ! fprintf (dump_file, " Removed AB side-effects.\n"); ! } ! /* Changing an indirect call to a direct call may ! have exposed different semantics. This may ! require an SSA update. */ ! el_todo |= TODO_update_ssa_only_virtuals; } } } --- 4281,4463 ---- if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " Removed AB side-effects.\n"); } + + continue; } } + /* If the statement is a scalar store, see if the expression ! has the same value number as its rhs. If so, the store is ! dead. */ ! if (gimple_assign_single_p (stmt) ! && !gimple_has_volatile_ops (stmt) ! && !is_gimple_reg (gimple_assign_lhs (stmt)) ! && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME ! || is_gimple_min_invariant (gimple_assign_rhs1 (stmt)))) ! { ! tree val; ! tree rhs = gimple_assign_rhs1 (stmt); ! val = vn_reference_lookup (gimple_assign_lhs (stmt), ! gimple_vuse (stmt), VN_WALK, NULL); ! if (TREE_CODE (rhs) == SSA_NAME) ! rhs = VN_INFO (rhs)->valnum; ! if (val ! && operand_equal_p (val, rhs, 0)) ! { ! if (dump_file && (dump_flags & TDF_DETAILS)) ! { ! fprintf (dump_file, "Deleted redundant store "); ! print_gimple_stmt (dump_file, stmt, 0, 0); ! } ! /* Queue stmt for removal. */ ! el_to_remove.safe_push (stmt); ! continue; ! } ! } ! ! bool can_make_abnormal_goto = stmt_can_make_abnormal_goto (stmt); ! bool was_noreturn = (is_gimple_call (stmt) ! && gimple_call_noreturn_p (stmt)); ! tree vdef = gimple_vdef (stmt); ! tree vuse = gimple_vuse (stmt); ! ! /* If we didn't replace the whole stmt (or propagate the result ! into all uses), replace all uses on this stmt with their ! leaders. */ ! use_operand_p use_p; ! ssa_op_iter iter; ! FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE) { ! tree use = USE_FROM_PTR (use_p); ! /* ??? The call code above leaves stmt operands un-updated. */ ! if (TREE_CODE (use) != SSA_NAME) ! continue; ! tree sprime = eliminate_avail (use); ! if (sprime && sprime != use ! && may_propagate_copy (use, sprime) ! /* We substitute into debug stmts to avoid excessive ! debug temporaries created by removed stmts, but we need ! to avoid doing so for inserted sprimes as we never want ! to create debug temporaries for them. */ ! && (!inserted_exprs ! || TREE_CODE (sprime) != SSA_NAME ! || !is_gimple_debug (stmt) ! || !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime)))) ! { ! propagate_value (use_p, sprime); ! gimple_set_modified (stmt, true); ! if (TREE_CODE (sprime) == SSA_NAME ! && !is_gimple_debug (stmt)) ! gimple_set_plf (SSA_NAME_DEF_STMT (sprime), ! NECESSARY, true); } } + /* Visit indirect calls and turn them into direct calls if ! possible using the devirtualization machinery. */ if (is_gimple_call (stmt)) { ! tree fn = gimple_call_fn (stmt); ! if (fn ! && TREE_CODE (fn) == OBJ_TYPE_REF ! && TREE_CODE (OBJ_TYPE_REF_EXPR (fn)) == SSA_NAME) { ! fn = ipa_intraprocedural_devirtualization (stmt); ! if (fn && dbg_cnt (devirt)) { ! if (dump_enabled_p ()) ! { ! location_t loc = gimple_location (stmt); ! dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, ! "converting indirect call to " ! "function %s\n", ! cgraph_get_node (fn)->name ()); ! } ! gimple_call_set_fndecl (stmt, fn); ! gimple_set_modified (stmt, true); } } ! } + if (gimple_modified_p (stmt)) + { + /* If a formerly non-invariant ADDR_EXPR is turned into an + invariant one it was on a separate stmt. */ + if (gimple_assign_single_p (stmt) + && TREE_CODE (gimple_assign_rhs1 (stmt)) == ADDR_EXPR) + recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt)); + gimple old_stmt = stmt; + if (is_gimple_call (stmt)) + { + /* ??? Only fold calls inplace for now, this may create new + SSA names which in turn will confuse free_scc_vn SSA name + release code. */ + fold_stmt_inplace (&gsi); /* When changing a call into a noreturn call, cfg cleanup is needed to fix up the noreturn call. */ if (!was_noreturn && gimple_call_noreturn_p (stmt)) el_todo |= TODO_cleanup_cfg; + } + else + { + fold_stmt (&gsi); + stmt = gsi_stmt (gsi); + if ((gimple_code (stmt) == GIMPLE_COND + && (gimple_cond_true_p (stmt) + || gimple_cond_false_p (stmt))) + || (gimple_code (stmt) == GIMPLE_SWITCH + && TREE_CODE (gimple_switch_index (stmt)) == INTEGER_CST)) + el_todo |= TODO_cleanup_cfg; + } + /* If we removed EH side-effects from the statement, clean + its EH information. */ + if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt)) + { + bitmap_set_bit (need_eh_cleanup, + gimple_bb (stmt)->index); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " Removed EH side-effects.\n"); + } + /* Likewise for AB side-effects. */ + if (can_make_abnormal_goto + && !stmt_can_make_abnormal_goto (stmt)) + { + bitmap_set_bit (need_ab_cleanup, + gimple_bb (stmt)->index); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " Removed AB side-effects.\n"); + } + update_stmt (stmt); + if (vdef != gimple_vdef (stmt)) + VN_INFO (vdef)->valnum = vuse; + } ! /* Make the new value available - for fully redundant LHS we ! continue with the next stmt above. */ ! if (lhs && TREE_CODE (lhs) == SSA_NAME) ! eliminate_push_avail (lhs); ! } ! /* Replace destination PHI arguments. */ ! edge_iterator ei; ! edge e; ! FOR_EACH_EDGE (e, ei, b->succs) ! { ! for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) ! { ! gimple phi = gsi_stmt (gsi); ! use_operand_p use_p = PHI_ARG_DEF_PTR_FROM_EDGE (phi, e); ! tree arg = USE_FROM_PTR (use_p); ! if (TREE_CODE (arg) != SSA_NAME ! || virtual_operand_p (arg)) ! continue; ! tree sprime = eliminate_avail (arg); ! if (sprime && may_propagate_copy (arg, sprime)) ! { ! propagate_value (use_p, sprime); ! if (TREE_CODE (sprime) == SSA_NAME) ! gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true); } } } *************** eliminate (bool do_pre) *** 4442,4448 **** { gimple_stmt_iterator gsi; gimple stmt; - unsigned i; need_eh_cleanup = BITMAP_ALLOC (NULL); need_ab_cleanup = BITMAP_ALLOC (NULL); --- 4480,4485 ---- *************** eliminate (bool do_pre) *** 4460,4500 **** /* We cannot remove stmts during BB walk, especially not release SSA names there as this confuses the VN machinery. The stmts ending ! up in el_to_remove are either stores or simple copies. */ ! FOR_EACH_VEC_ELT (el_to_remove, i, stmt) { ! tree lhs = gimple_assign_lhs (stmt); ! tree rhs = gimple_assign_rhs1 (stmt); ! use_operand_p use_p; ! gimple use_stmt; ! /* If there is a single use only, propagate the equivalency ! instead of keeping the copy. */ ! if (TREE_CODE (lhs) == SSA_NAME ! && TREE_CODE (rhs) == SSA_NAME ! && single_imm_use (lhs, &use_p, &use_stmt) ! && may_propagate_copy (USE_FROM_PTR (use_p), rhs)) { ! SET_USE (use_p, rhs); ! update_stmt (use_stmt); ! if (inserted_exprs ! && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (lhs)) ! && TREE_CODE (rhs) == SSA_NAME) ! gimple_set_plf (SSA_NAME_DEF_STMT (rhs), NECESSARY, true); } ! /* If this is a store or a now unused copy, remove it. */ ! if (TREE_CODE (lhs) != SSA_NAME ! || has_zero_uses (lhs)) { basic_block bb = gimple_bb (stmt); - gsi = gsi_for_stmt (stmt); unlink_stmt_vdef (stmt); if (gsi_remove (&gsi, true)) bitmap_set_bit (need_eh_cleanup, bb->index); - if (inserted_exprs - && TREE_CODE (lhs) == SSA_NAME) - bitmap_clear_bit (inserted_exprs, SSA_NAME_VERSION (lhs)); release_defs (stmt); } } --- 4497,4533 ---- /* We cannot remove stmts during BB walk, especially not release SSA names there as this confuses the VN machinery. The stmts ending ! up in el_to_remove are either stores or simple copies. ! Remove stmts in reverse order to make debug stmt creation possible. */ ! while (!el_to_remove.is_empty ()) { ! stmt = el_to_remove.pop (); ! if (dump_file && (dump_flags & TDF_DETAILS)) { ! fprintf (dump_file, "Removing dead stmt "); ! print_gimple_stmt (dump_file, stmt, 0, 0); } ! tree lhs; ! if (gimple_code (stmt) == GIMPLE_PHI) ! lhs = gimple_phi_result (stmt); ! else ! lhs = gimple_get_lhs (stmt); ! ! if (inserted_exprs ! && TREE_CODE (lhs) == SSA_NAME) ! bitmap_clear_bit (inserted_exprs, SSA_NAME_VERSION (lhs)); ! ! gsi = gsi_for_stmt (stmt); ! if (gimple_code (stmt) == GIMPLE_PHI) ! remove_phi_node (&gsi, true); ! else { basic_block bb = gimple_bb (stmt); unlink_stmt_vdef (stmt); if (gsi_remove (&gsi, true)) bitmap_set_bit (need_eh_cleanup, bb->index); release_defs (stmt); } } Index: trunk/gcc/testsuite/c-c++-common/pr46562-2.c =================================================================== *** trunk.orig/gcc/testsuite/c-c++-common/pr46562-2.c 2014-06-12 13:05:44.962651098 +0200 --- trunk/gcc/testsuite/c-c++-common/pr46562-2.c 2014-06-12 13:05:49.149650810 +0200 *************** int foo(void) *** 9,13 **** return *p; } ! /* { dg-final { scan-tree-dump "= 0;" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ --- 9,13 ---- return *p; } ! /* { dg-final { scan-tree-dump "return 0;" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ Index: trunk/gcc/tree-ssa-propagate.c =================================================================== *** trunk.orig/gcc/tree-ssa-propagate.c 2014-06-12 13:05:44.962651098 +0200 --- trunk/gcc/tree-ssa-propagate.c 2014-06-12 13:05:49.149650810 +0200 *************** replace_exp (use_operand_p op_p, tree va *** 1410,1420 **** void propagate_tree_value (tree *op_p, tree val) { - gcc_checking_assert (!(TREE_CODE (val) == SSA_NAME - && *op_p - && TREE_CODE (*op_p) == SSA_NAME - && !may_propagate_copy (*op_p, val))); - if (TREE_CODE (val) == SSA_NAME) *op_p = val; else --- 1410,1415 ---- Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-24.c =================================================================== *** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-24.c 2014-06-12 13:05:44.963651098 +0200 --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-24.c 2014-06-12 13:05:49.150650810 +0200 *************** int bazzoo (void) *** 30,34 **** return b.i; } ! /* { dg-final { scan-tree-dump-times "= 0;" 5 "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ --- 30,34 ---- return b.i; } ! /* { dg-final { scan-tree-dump-times "return 0;" 4 "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-25.c =================================================================== *** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-25.c 2014-06-12 13:05:44.963651098 +0200 --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-25.c 2014-06-12 13:05:49.150650810 +0200 *************** int foo (struct X *p) *** 14,18 **** /* We should optimize this to return 0. */ ! /* { dg-final { scan-tree-dump "= 0;" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ --- 14,18 ---- /* We should optimize this to return 0. */ ! /* { dg-final { scan-tree-dump "return 0;" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-32.c =================================================================== *** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-32.c 2014-06-12 13:05:44.963651098 +0200 --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-32.c 2014-06-12 13:05:49.150650810 +0200 *************** bar (_Complex float x) *** 23,28 **** return z; } ! /* We should CSE all the way to replace the final assignment to z with x. */ ! /* { dg-final { scan-tree-dump-times "with x_1\\\(D\\\) in z" 3 "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ --- 23,28 ---- return z; } ! /* We should CSE all the way to replace the return value with x. */ ! /* { dg-final { scan-tree-dump-times "return x_\\d\+\\(D\\);" 2 "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-16.c =================================================================== *** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-16.c 2014-06-12 13:05:44.964651098 +0200 --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-16.c 2014-06-12 13:05:49.150650810 +0200 *************** int foo(int k, int *x) *** 11,15 **** } while (++j<k); return res; } ! /* { dg-final { scan-tree-dump-times "Eliminated: 1" 1 "pre"} } */ /* { dg-final { cleanup-tree-dump "pre" } } */ --- 11,15 ---- } while (++j<k); return res; } ! /* { dg-final { scan-tree-dump-times "Eliminated: 2" 1 "pre"} } */ /* { dg-final { cleanup-tree-dump "pre" } } */ Index: trunk/gcc/testsuite/g++.dg/tree-ssa/pr8781.C =================================================================== *** trunk.orig/gcc/testsuite/g++.dg/tree-ssa/pr8781.C 2014-06-12 13:05:44.964651098 +0200 --- trunk/gcc/testsuite/g++.dg/tree-ssa/pr8781.C 2014-06-12 13:06:08.487649479 +0200 *************** *** 1,5 **** /* { dg-do compile } */ ! /* { dg-options "-O -fno-tree-sra -fdump-tree-fre1-details" } */ int f(); --- 1,5 ---- /* { dg-do compile } */ ! /* { dg-options "-O -fno-tree-sra -fdump-tree-fre1" } */ int f(); *************** int x() *** 24,28 **** /* We should optimize this to a direct call. */ ! /* { dg-final { scan-tree-dump "converting indirect call to function int f()" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ --- 24,28 ---- /* We should optimize this to a direct call. */ ! /* { dg-final { scan-tree-dump-times "= f \\(\\);" 1 "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-39.c =================================================================== *** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-39.c 2014-06-12 13:05:44.964651098 +0200 --- trunk/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-39.c 2014-06-12 13:05:49.151650810 +0200 *************** int foo (int i) *** 15,19 **** /* We should be able to value-number the final assignment to k to 1. */ ! /* { dg-final { scan-tree-dump "k_. = 1;" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */ --- 15,19 ---- /* We should be able to value-number the final assignment to k to 1. */ ! /* { dg-final { scan-tree-dump "return 1;" "fre1" } } */ /* { dg-final { cleanup-tree-dump "fre1" } } */