diff options
author | Alan Modra <amodra@gmail.com> | 2017-02-17 09:26:51 +1030 |
---|---|---|
committer | Jeff Law <law@gcc.gnu.org> | 2017-02-16 15:56:51 -0700 |
commit | a72b242eacf3bc43375daff829e04bcce634ad22 (patch) | |
tree | 8292f5e8b39f99c7703446eee1a6e3e17e09c454 /gcc/ira.c | |
parent | 9b9ad3606debd14bd1e57306358f4fcab2c2b1db (diff) | |
download | gcc-a72b242eacf3bc43375daff829e04bcce634ad22.zip gcc-a72b242eacf3bc43375daff829e04bcce634ad22.tar.gz gcc-a72b242eacf3bc43375daff829e04bcce634ad22.tar.bz2 |
re PR rtl-optimization/79286 (ira and lra wrong code at -O2 and -Os on i686-linux)
2017-02-16 Alan Modra <amodra@gmail.com>
PR rtl-optimization/79286
* ira.c (def_dominates_uses): New function.
(update_equiv_regs): Don't create an equivalence for insns that
may trap where the register def does not dominate the use.
* gcc.c-torture/execute/pr79286.c: New.
From-SVN: r245521
Diffstat (limited to 'gcc/ira.c')
-rw-r--r-- | gcc/ira.c | 56 |
1 files changed, 54 insertions, 2 deletions
@@ -3300,6 +3300,49 @@ adjust_cleared_regs (rtx loc, const_rtx old_rtx ATTRIBUTE_UNUSED, void *data) return NULL_RTX; } +/* Given register REGNO is set only once, return true if the defining + insn dominates all uses. */ + +static bool +def_dominates_uses (int regno) +{ + df_ref def = DF_REG_DEF_CHAIN (regno); + + struct df_insn_info *def_info = DF_REF_INSN_INFO (def); + /* If this is an artificial def (eh handler regs, hard frame pointer + for non-local goto, regs defined on function entry) then def_info + is NULL and the reg is always live before any use. We might + reasonably return true in that case, but since the only call + of this function is currently here in ira.c when we are looking + at a defining insn we can't have an artificial def as that would + bump DF_REG_DEF_COUNT. */ + gcc_assert (DF_REG_DEF_COUNT (regno) == 1 && def_info != NULL); + + rtx_insn *def_insn = DF_REF_INSN (def); + basic_block def_bb = BLOCK_FOR_INSN (def_insn); + + for (df_ref use = DF_REG_USE_CHAIN (regno); + use; + use = DF_REF_NEXT_REG (use)) + { + struct df_insn_info *use_info = DF_REF_INSN_INFO (use); + /* Only check real uses, not artificial ones. */ + if (use_info) + { + rtx_insn *use_insn = DF_REF_INSN (use); + if (!DEBUG_INSN_P (use_insn)) + { + basic_block use_bb = BLOCK_FOR_INSN (use_insn); + if (use_bb != def_bb + ? !dominated_by_p (CDI_DOMINATORS, use_bb, def_bb) + : DF_INSN_INFO_LUID (use_info) < DF_INSN_INFO_LUID (def_info)) + return false; + } + } + } + return true; +} + /* Find registers that are equivalent to a single value throughout the compilation (either because they can be referenced in memory or are set once from a single constant). Lower their priority for a @@ -3498,9 +3541,18 @@ update_equiv_regs (void) = gen_rtx_INSN_LIST (VOIDmode, insn, reg_equiv[regno].init_insns); /* If this register is known to be equal to a constant, record that - it is always equivalent to the constant. */ + it is always equivalent to the constant. + Note that it is possible to have a register use before + the def in loops (see gcc.c-torture/execute/pr79286.c) + where the reg is undefined on first use. If the def insn + won't trap we can use it as an equivalence, effectively + choosing the "undefined" value for the reg to be the + same as the value set by the def. */ if (DF_REG_DEF_COUNT (regno) == 1 - && note && ! rtx_varies_p (XEXP (note, 0), 0)) + && note + && !rtx_varies_p (XEXP (note, 0), 0) + && (!may_trap_p (XEXP (note, 0)) + || def_dominates_uses (regno))) { rtx note_value = XEXP (note, 0); remove_note (insn, note); |