http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49884
Summary: get_last_value in combine ignores register mode Product: gcc Version: 4.6.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: rtl-optimization AssignedTo: unassig...@gcc.gnu.org ReportedBy: paulo.ma...@csr.com Created attachment 24853 --> http://gcc.gnu.org/bugzilla/attachment.cgi?id=24853 Insn list right before combine when compiling test with gcc-4.6.1 and -Os I have noticed this with GCC 4.3.4, GCC 4.4.4, GCC 4.5.2 and GCC 4.6.1. The problem was triggered by a change in our backend and none of our testsuites have ever caught it. Only by analysing the generated code we noticed something was wrong when compiling: unsigned long val; int swap_magic(void) { unsigned int low = val; unsigned int high = (val >> 16); unsigned long swapped = ((unsigned long)low << 16) | (unsigned long)high; if(swapped == 0x12345678) return 1; else return 0; } This happens in our port with that has the following types: int : QImode (1 word, 16 bits) unsigned long : HImode (2 words, 32 bits) and the following constraints: - a shift can only be done in HImode over registers AH and AL (AH holds the 16 most significant bits and AL holds the 16 least significant bits). So, our backend expand a shift over an unsigned long like: (define_expand "ashlhi3" [ (use (match_operand:HI 0 "nonimmediate_operand" "")) (use (match_operand:HI 1 "general_operand" "")) (use (match_operand:QI 2 "general_operand" "")) ] "" { xap_expand_HIoperation(ASHIFT, operands); DONE; }) (define_insn "*ashlhi3" [ (set (reg:HI RAH) (ashift:HI (reg:HI RAH) (match_operand:QI 0 "general_operand" "cwmi"))) ] "" "asl\\t%f0" [(set_attr "cc" "set_c")] ) and xap_expand_HIoperation for ASHIFT does: rtx regAH_HI = gen_rtx_REG(HImode, RAH); emit_insn(gen_movhi(regAH_HI, operands[1])); emit_insn(gen_rtx_SET(VOIDmode, regAH_HI, gen_rtx_ASHIFT(HImode, regAH_HI, operands[2]))); emit_insn(gen_movhi(operands[0], regAH_HI)); The ior in HImode, which also uses AH:HImode, splits into two QImode iors. So, we arrive at combine (after dce pass) with the following (simplified) insn list: regQI 37 <- memQI val regQI 38 <- memQI val + 1 regQI AH <- const_int 0 regQI AL <- regQI 38 regHI AH <- ashift regHI AH, 16 regQI 45 <- regQI AH ... cc0 <- compare regQI 45, const_int 0x1234 if_then_else cc0 != const_int 0, jump to label 27, pc [the full insn list is attached] The problem is that combine assumes the jump is always true because it thinks that regQI 45 is zero! The problem stems from get_last_value when applied to regHI AH in the shift insn. get_last_value when applied to regHI AH during the ashift instruction returns const_int 0. Should return 0. We don't know the value! We know that regQI AH was set to zero but the value of regHI AH needs to take AL into consideration so we really don't know it. get_last_value should take the mode of the register into consideration when returning the value it was last set to. If the last set value was set in a different mode, then we can say nothing about the value in the register.