diff --git a/gcc/ext-dce.cc b/gcc/ext-dce.cc index 0ece37726c7..649d39fadf9 100644 --- a/gcc/ext-dce.cc +++ b/gcc/ext-dce.cc @@ -941,6 +941,38 @@ ext_dce_process_bb (basic_block bb) } } +/* SUBREG_PROMOTED_VAR_P is set by the gimple->rtl optimizers and + is usually helpful. However, in some cases setting the value when + it not strictly needed can cause this pass to miss optimizations. + + Specifically consider (set (mem) (subreg (reg))). If set in that + case it will cause more bit groups to be live for REG than would + be strictly necessary which in turn can inhibit extension removal. + + So do a pass over the IL wiping the SUBREG_PROMOTED_VAR_P when it + is obviously not needed. */ + +static void +maybe_clear_subreg_promoted_p (void) +{ + for (rtx_insn *insn = get_insns(); insn; insn = NEXT_INSN (insn)) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + rtx set = single_set (insn); + if (!set) + continue; + + /* There may be other cases where we should clear, but for + now, this is the only known case where it causes problems. */ + if (MEM_P (SET_DEST (set)) && SUBREG_P (SET_SRC (set)) + && GET_MODE (SET_DEST (set)) <= GET_MODE (SUBREG_REG (SET_SRC (set)))) + SUBREG_PROMOTED_VAR_P (SET_SRC (set)) = 0; + } +} + + /* We optimize away sign/zero extensions in this pass and replace them with SUBREGs indicating certain bits are don't cares. @@ -1077,6 +1109,9 @@ static bool ext_dce_rd_confluence_n (edge) { return true; } void ext_dce_execute (void) { + /* Some settings of SUBREG_PROMOTED_VAR_P are actively harmful + to this pass. Clear it for those cases. */ + maybe_clear_subreg_promoted_p (); df_analyze (); ext_dce_init (); diff --git a/gcc/testsuite/gcc.target/riscv/ext-dce-1.c b/gcc/testsuite/gcc.target/riscv/ext-dce-1.c new file mode 100644 index 00000000000..295d956ef55 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/ext-dce-1.c @@ -0,0 +1,44 @@ +/* { dg-options "-O2 -fdump-rtl-ext_dce" } */ +typedef unsigned char __uint8_t; +typedef unsigned int __uint32_t; +typedef __uint8_t uint8_t; +typedef __uint32_t uint32_t; +static inline void +unaligned_write32le(uint8_t *buf, uint32_t num) +{ + buf[0] = num; + buf[1] = num >> 8; + buf[2] = num >> 16; + buf[3] = num >> 24; + return; +} +typedef struct { + uint32_t dict_size; +} lzma_options_lzma; +typedef void lzma_coder; +typedef struct lzma_next_coder_s lzma_next_coder; +struct lzma_next_coder_s { + lzma_coder *coder; +}; +struct lzma_coder_s { + uint8_t header[(1 + 4 + 8)]; +}; + +void +alone_encoder_init(lzma_next_coder *next, const lzma_options_lzma *options) +{ + uint32_t d = options->dict_size - 1; + d |= d >> 2; +#if 0 + d |= d >> 3; + d |= d >> 4; + d |= d >> 8; + d |= d >> 16; +#endif + if (d != (4294967295U)) + ++d; + unaligned_write32le(((struct lzma_coder_s*)next->coder)->header + 1, d); +} + +/* { dg-final { scan-rtl-dump "Successfully transformed to:" "ext_dce" } } */ +