diff options
Diffstat (limited to 'gcc/ext-dce.cc')
-rw-r--r-- | gcc/ext-dce.cc | 120 |
1 files changed, 105 insertions, 15 deletions
diff --git a/gcc/ext-dce.cc b/gcc/ext-dce.cc index a034395..afe7afe 100644 --- a/gcc/ext-dce.cc +++ b/gcc/ext-dce.cc @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "print-rtl.h" #include "dbgcnt.h" #include "diagnostic-core.h" +#include "target.h" /* These should probably move into a C++ class. */ static vec<bitmap_head> livein; @@ -441,6 +442,11 @@ ext_dce_try_optimize_insn (rtx_insn *insn, rtx set) print_rtl_single (dump_file, new_pattern); fprintf (dump_file, "\n"); } + + /* INSN may have a REG_EQUAL note indicating that the value was + sign or zero extended. That note is no longer valid since we've + just removed the extension. Just wipe the notes. */ + remove_reg_equal_equiv_notes (insn, false); } else { @@ -754,23 +760,19 @@ ext_dce_process_uses (rtx_insn *insn, rtx obj, y = XEXP (y, 0); else if (SUBREG_P (y) && SUBREG_BYTE (y).is_constant ()) { - /* We really want to know the outer code here, ie do we - have (ANY_EXTEND (SUBREG ...)) as we need to know if - the extension matches the SUBREG_PROMOTED state. In - that case optimizers can turn the extension into a - simple copy. Which means that bits outside the - SUBREG's mode are actually live. - - We don't want to mark those bits live unnecessarily - as that inhibits extension elimination in important - cases such as those in Coremark. So we need that - outer code. */ + /* If !TRULY_NOOP_TRUNCATION_MODES_P, the mode + change performed by Y would normally need to be a + TRUNCATE rather than a SUBREG. It is probably the + guarantee provided by SUBREG_PROMOTED_VAR_P that + allows the SUBREG in Y as an exception. We must + therefore preserve that guarantee and treat the + upper bits of the inner register as live + regardless of the outer code. See PR 120050. */ if (!REG_P (SUBREG_REG (y)) || (SUBREG_PROMOTED_VAR_P (y) - && ((GET_CODE (SET_SRC (x)) == SIGN_EXTEND - && SUBREG_PROMOTED_SIGNED_P (y)) - || (GET_CODE (SET_SRC (x)) == ZERO_EXTEND - && SUBREG_PROMOTED_UNSIGNED_P (y))))) + && (!TRULY_NOOP_TRUNCATION_MODES_P ( + GET_MODE (y), + GET_MODE (SUBREG_REG (y)))))) break; bit = subreg_lsb (y).to_constant (); @@ -974,6 +976,81 @@ maybe_clear_subreg_promoted_p (void) } } +/* Walk the IL and build the transitive closure of all the REGs tied + together by copies where either the source or destination is + marked in CHANGED_PSEUDOS. */ + +static void +expand_changed_pseudos (void) +{ + /* Build a vector of registers related by a copy. This is meant to + speed up the next step by avoiding full IL walks. */ + struct copy_pair { rtx first; rtx second; }; + auto_vec<copy_pair> pairs; + for (rtx_insn *insn = get_insns(); insn; insn = NEXT_INSN (insn)) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + rtx pat = PATTERN (insn); + + /* Simple copies to a REG from another REG or SUBREG of a REG. */ + if (GET_CODE (pat) == SET + && REG_P (SET_DEST (pat)) + && (REG_P (SET_SRC (pat)) + || (SUBREG_P (SET_SRC (pat)) + && REG_P (SUBREG_REG (SET_SRC (pat)))))) + { + rtx src = (REG_P (SET_SRC (pat)) + ? SET_SRC (pat) + : SUBREG_REG (SET_SRC (pat))); + pairs.safe_push ({ SET_DEST (pat), src }); + } + + /* Simple copies to a REG from another REG or SUBREG of a REG + held inside a PARALLEL. */ + if (GET_CODE (pat) == PARALLEL) + { + for (int i = XVECLEN (pat, 0) - 1; i >= 0; i--) + { + rtx elem = XVECEXP (pat, 0, i); + + if (GET_CODE (elem) == SET + && REG_P (SET_DEST (elem)) + && (REG_P (SET_SRC (elem)) + || (SUBREG_P (SET_SRC (elem)) + && REG_P (SUBREG_REG (SET_SRC (elem)))))) + { + rtx src = (REG_P (SET_SRC (elem)) + ? SET_SRC (elem) + : SUBREG_REG (SET_SRC (elem))); + pairs.safe_push ({ SET_DEST (elem), src }); + } + } + continue; + } + } + + /* Now we have a vector with copy pairs. Iterate over that list + updating CHANGED_PSEUDOS as we go. Eliminate copies from the + list as we go as they don't need further processing. */ + bool changed = true; + while (changed) + { + changed = false; + unsigned int i; + copy_pair *p; + FOR_EACH_VEC_ELT (pairs, i, p) + { + if (bitmap_bit_p (changed_pseudos, REGNO (p->second)) + && bitmap_set_bit (changed_pseudos, REGNO (p->first))) + { + pairs.unordered_remove (i); + changed = true; + } + } + } +} /* We optimize away sign/zero extensions in this pass and replace them with SUBREGs indicating certain bits are don't cares. @@ -986,6 +1063,19 @@ maybe_clear_subreg_promoted_p (void) static void reset_subreg_promoted_p (void) { + /* This pass eliminates zero/sign extensions on pseudo regs found + in CHANGED_PSEUDOS. Elimination of those extensions changes if + the pseudos are known to hold values extended to wider modes + via SUBREG_PROMOTED_VAR. So we wipe the SUBREG_PROMOTED_VAR + state on all affected pseudos. + + But that is insufficient. We might have a copy from one REG + to another (possibly with the source register wrapped with a + SUBREG). We need to wipe SUBREG_PROMOTED_VAR on the transitive + closure of the original CHANGED_PSEUDOS and registers they're + connected to via copies. So expand the set. */ + expand_changed_pseudos (); + /* If we removed an extension, that changed the promoted state of the destination of that extension. Thus we need to go find any SUBREGs that reference that pseudo and adjust their |