This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug rtl-optimization/49884] New: get_last_value in combine ignores register mode


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: unassigned@gcc.gnu.org
        ReportedBy: Paulo.Matos@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.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]