Hi Jean, > Do you have a link to that patch? So that I can see if it applies for me ?
See patch below. I put in some comments to try and explain what I have attempted to do. The hash SET generation, to track potential candidates in replacing a register USE, needed some tweaking. This function was not tracking register copies if the associated INSN had a NOTE attached that associated the register with a constant. Also, only the last register or constant in an assignment chain was being added to the hash SET. It may be profitable to have a closer register copy in the assignment chain as a potential replacement. Before the actual replacing operation, the cost of temporary replacement is determined using rtx_cost. A cost cannot be reliably formed if we come across jump INSNs because they may alter jumps or successors. The default replacement is used in this case. Rahul Index: gcse.c =================================================================== --- gcse.c (revision 141156) +++ gcse.c (working copy) @@ -520,7 +520,7 @@ static void record_set_info (rtx, const_rtx, void *); static void compute_sets (void); static void hash_scan_insn (rtx, struct hash_table *, int); -static void hash_scan_set (rtx, rtx, struct hash_table *); +static void hash_scan_set (rtx, rtx, struct hash_table *, bool); static void hash_scan_clobber (rtx, rtx, struct hash_table *); static void hash_scan_call (rtx, rtx, struct hash_table *); static int want_to_gcse_p (rtx); @@ -560,7 +560,7 @@ static void compute_cprop_data (void); static void find_used_regs (rtx *, void *); static int try_replace_reg (rtx, rtx, rtx); -static struct expr *find_avail_set (int, rtx); +static struct expr *find_avail_set (int, rtx, bool); static int cprop_jump (basic_block, rtx, rtx, rtx, rtx); static void mems_conflict_for_gcse_p (rtx, const_rtx, void *); static int load_killed_in_block_p (const_basic_block, int, const_rtx, int); @@ -1671,11 +1671,11 @@ expression one). */ static void -hash_scan_set (rtx pat, rtx insn, struct hash_table *table) +hash_scan_set (rtx pat, rtx insn, struct hash_table *table, bool use_note) { rtx src = SET_SRC (pat); rtx dest = SET_DEST (pat); - rtx note; + rtx note = NULL_RTX; if (GET_CODE (src) == CALL) hash_scan_call (src, insn, table); @@ -1685,16 +1685,21 @@ unsigned int regno = REGNO (dest); rtx tmp; - /* See if a REG_NOTE shows this equivalent to a simpler expression. - This allows us to do a single GCSE pass and still eliminate - redundant constants, addresses or other expressions that are - constructed with multiple instructions. */ - note = find_reg_equal_equiv_note (insn); - if (note != 0 - && (table->set_p - ? gcse_constant_p (XEXP (note, 0)) - : want_to_gcse_p (XEXP (note, 0)))) - src = XEXP (note, 0), pat = gen_rtx_SET (VOIDmode, dest, src); + /* Do not substitute insn SET_SRC for note if caller uses + uses use_note = false. */ + if (use_note) + { + /* See if a REG_NOTE shows this equivalent to a simpler expression. + This allows us to do a single GCSE pass and still eliminate + redundant constants, addresses or other expressions that are + constructed with multiple instructions. */ + note = find_reg_equal_equiv_note (insn); + if (note != 0 + && (table->set_p + ? gcse_constant_p (XEXP (note, 0)) + : want_to_gcse_p (XEXP (note, 0)))) + src = XEXP (note, 0), pat = gen_rtx_SET (VOIDmode, dest, src); + } /* Only record sets of pseudo-regs in the hash table. */ if (! table->set_p @@ -1835,14 +1840,30 @@ what's been modified. */ if (GET_CODE (pat) == SET) - hash_scan_set (pat, insn, table); + { + /* If we're hashing SETs for constant/copy propagation + also hash a SET ignoring any insn notes. */ + if (table->set_p) + { + hash_scan_set (pat, insn, table, false); + } + hash_scan_set (pat, insn, table, true); + } else if (GET_CODE (pat) == PARALLEL) for (i = 0; i < XVECLEN (pat, 0); i++) { rtx x = XVECEXP (pat, 0, i); if (GET_CODE (x) == SET) - hash_scan_set (x, insn, table); + { + /* If we're hashing SETs for constant/copy propagation + also hash a SET ignoring any insn notes. */ + if (table->set_p) + { + hash_scan_set (x, insn, table, false); + } + hash_scan_set (x, insn, table, true); + } else if (GET_CODE (x) == CLOBBER) hash_scan_clobber (x, insn, table); else if (GET_CODE (x) == CALL) @@ -2071,7 +2092,7 @@ if (table->set_p && implicit_sets[current_bb->index] != NULL_RTX) hash_scan_set (implicit_sets[current_bb->index], - BB_HEAD (current_bb), table); + BB_HEAD (current_bb), table, true); /* The next pass builds the hash table. */ in_libcall_block = 0; @@ -2701,7 +2722,7 @@ NULL no such set is found. */ static struct expr * -find_avail_set (int regno, rtx insn) +find_avail_set (int regno, rtx insn, bool follow_chain) { /* SET1 contains the last set found that can be returned to the caller for use in a substitution. */ @@ -2716,7 +2737,7 @@ This can not happen since the set of (reg Y) would have killed the set of (reg X) making it unavailable at the start of this block. */ - while (1) + do { rtx src; struct expr *set = lookup_set (regno, &set_hash_table); @@ -2758,6 +2779,7 @@ and see if we have an available copy into SRC. */ regno = REGNO (src); } + while (follow_chain); /* SET1 holds the last set that was available and anticipatable at INSN. */ @@ -2944,7 +2966,12 @@ unsigned int regno = REGNO (reg_used->reg_rtx); rtx pat, src; struct expr *set; + int set_cost = INT_MAX; + rtx src_first; + struct expr *set_first; + int set_first_cost = INT_MAX; + /* Ignore registers created by GCSE. We do this because ... */ if (regno >= max_gcse_regno) @@ -2957,10 +2984,72 @@ /* Find an assignment that sets reg_used and is available at the start of the block. */ - set = find_avail_set (regno, insn); + set = find_avail_set (regno, insn, true); if (! set) continue; + src = SET_SRC (set->expr); + + /* If possible, determine the cost of substituting the + last definition in the target insn */ + rtx insn_set = make_insn_raw (copy_insn (PATTERN (insn))); + if (NONJUMP_INSN_P (insn_set) + && try_replace_reg (reg_used->reg_rtx, src, insn_set)) + { + set_cost = rtx_cost (PATTERN (insn_set), + GET_CODE (PATTERN (insn_set))); + + /* Bias heavily for last definition if we managed to + delete target insn with this substitution */ + if (INSN_DELETED_P (insn_set)) + set_cost = 0; + } + /* Might be a alter_jumps and jump insn case which we neglect + in costing this option */ + /* Find the first definition that sets reg_used + i.e. not following the assignment chain */ + set_first = find_avail_set (regno, insn, false); + + /* If we have a closest definition available, cost the replacement of + first definition (set_first) Vs last definition (in the assignment chain) */ + if (set_first + /* If we cannot deduce the cost of propagating constants into + jump insns, go with constant propagation */ + && (!alter_jumps + || !((any_condjump_p (insn) && onlyjump_p (insn)) + || (NEXT_INSN (insn) && any_condjump_p (NEXT_INSN (insn)) + && onlyjump_p (NEXT_INSN (insn)))))) + { + src_first = SET_SRC (set_first->expr); + + if (REG_P (src_first) + && REGNO (src_first) >= FIRST_PSEUDO_REGISTER + && REGNO (src_first) != regno) + { + rtx insn_set_first = make_insn_raw (copy_insn (PATTERN (insn))); + if (try_replace_reg (reg_used->reg_rtx, src_first, insn_set_first)) + { + set_first_cost = rtx_cost (PATTERN (insn_set_first), + GET_CODE (PATTERN (insn_set_first))); + } + } + + if (set_first_cost < set_cost) + { + set = set_first; + set_cost = set_first_cost; + + if (dump_file) + { + fprintf (dump_file, "cprop_insn prefer "); + print_inline_rtx (dump_file, SET_SRC (set_first->expr), 2); + fprintf (dump_file, " cost %d over ", set_first_cost); + print_inline_rtx (dump_file, SET_SRC (set->expr), 2); + fprintf (dump_file, " cost %d\n", set_cost); + } + } + } + pat = set->expr; /* ??? We might be able to handle PARALLELs. Later. */ gcc_assert (GET_CODE (pat) == SET);