http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51933
--- Comment #1 from Jakub Jelinek <jakub at gcc dot gnu.org> 2012-01-21 15:24:26 UTC --- Consider following 3 routines: unsigned long foo (unsigned short *s, int i) { unsigned short x = s[i]; if (x < 0x211) return (unsigned char) x; asm volatile (""); return 6; } unsigned long bar (unsigned short *s, int i) { unsigned short x = s[i]; if (x < 0x211) return (unsigned char) x; asm volatile (""); return x; } unsigned long baz (unsigned short *s, int i) { unsigned short x = s[i]; if (x < 0x211) return x; asm volatile (""); return (unsigned char) x; } In foo ree doesn't optimize away the QImode -> DImode zero extension, because merge_def_and_ext checks: && GET_MODE (SET_DEST (*sub_rtx)) == ext_src_mode and in this case ext_src_mode is QImode, but SET_DEST has HImode. In bar ree doesn't optimize it away either, first merge_def_and_ext_checks is called with the QImode ext_src_mode (which doesn't match) and only afterwards on the HImode -> DImode extension, which is optimized (that is correct). In baz (which is the same as bar in #c0 testcase) unfortunately merge_def_and_ext is first called on the HImode -> DImode extension, which is optimized (that is fine) by turning the HImode load into a DImode zero_extend load from HImode and removing the HImode -> DImode extension. But then when make_defs_and_copies_lists is called on the QImode -> DImode extension, we reach: /* Initialize the work list. */ if (!get_defs (extend_insn, src_reg, &work_list)) { VEC_free (rtx, heap, work_list); /* The number of defs being equal to zero can only mean that all the definitions have been previously merged. */ return 2; } and because the definition has been changed already. But nothing performs the ext_src_mode check that used to be performed otherwise. So we either need to do the src mode checking earlier when we haven't started modifying insns (in add_removable_extension?), or we'd need to look even at the newly added defs and see what mode they extend from.