https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120550
--- Comment #6 from Jeffrey A. Law <law at gcc dot gnu.org> --- So this is an ext-dce bug, it just isn't obvious. ext-dce removes the extension in this insn: (insn 26 24 29 3 (set (reg:DI 141 [ pretmp_16 ]) (zero_extend:DI (subreg:QI (reg:DI 160) 0))) "j.c":8:5 126 {*zero_extendqidi2_internal} (expr_list:REG_DEAD (reg:DI 160) (expr_list:REG_EQUAL (zero_extend:DI (mem/c:QI (symbol_ref:DI ("u") [flags 0x86] <var_decl 0x7ffff7202d10 u>) [0 u+0 S1 A8])) (nil)))) I've traced the values around the CFG and the removal is valid. But there's a REG_EQUAL note that gets left around. So after ext-dce we have: (insn 26 24 29 3 (set (reg:DI 141 [ pretmp_16 ]) (reg:DI 160)) "j.c":8:5 277 {*movdi_64bit} (expr_list:REG_DEAD (reg:DI 160) (expr_list:REG_EQUAL (zero_extend:DI (mem/c:QI (symbol_ref:DI ("u") [flags 0x86] <var_decl 0x7ffff7202d10 u>) [0 u+0 S1 A8])) (nil)))) That's the bug. Combine later kicks in and sees the REG_EQUAL note and adjust the nonzero bits for (reg 141) in the expected way, but they don't accurately reflect the value in (reg 141). Combine later uses the incorrect nonzero_bits and eliminates a different (and necessary) extension. The fix is simple. When ext-dce makes a change it can just wipe the REG_EQUAL note. We could try and be selective about the notes we remove, but I doubt it's worth the effort and analysis to be selective.