------- Comment #2 from kkojima at gcc dot gnu dot org 2006-12-06 00:40 ------- I've looked at what is going on. The variable "block" is placed at sfp - 4 where sfp is the software frame pointer. Then the expression (unsigned long) buf - 0x80000000 is sfp - 0x80000000 - 4. The cse pass folds the constant part of this expression with simplify_const_binary_operation. As simplify_const_binary_operation computes it as 64-bit HOST_WIDE_INT constant and returns gen_int_mode (- 0x80000000 - 4, SImode). Since gen_int_mode ( , SImode) truncates the constant to 32-bit, it returns (const_int 2147483644 [0x7ffffffc]). Thus cse makes a REG_EQUAL note below:
(insn 30 29 32 3 (set (reg:SI 168) (plus:SI (reg/f:SI 159 [ buf.1 ]) (reg:SI 167))) 35 {*addsi3_compact} (nil) (expr_list:REG_EQUAL (plus:SI (reg/f:SI 153 sfp) (const_int 2147483644 [0x7ffffffc])) (nil))) When reloading, eliminate_regs_in_insn tries to replace sfp with sp + 12 where sp is the hard stack pointer register and changes the above insn 30 to the insns (set (hard-reg:SI) (const_int 0x7ffffffc + 12)) (set (hard-reg':SI) (plus:SI (hard-reg:SI) (reg:SI sp))) Since eliminate_regs_in_insn computes the constant offset as HOST_WIDE_INT with ignoring the mode of destination, the first insn of the insns is (set (hard-reg:SI) (const_int 0x80000008)) However, movsi_ie which is the constant load pattern of SH checks it source with general_movsrc_operand which is essentially general_operand predicate and 0x80000008 is out of range as a SImode constant. Thus the above set insn fails to be recognised. I thought that if eliminate_regs_in_insn took account of the mode of the destination, then it'll generate (set (hard-reg:SI) (const_int 0xffffffff80000008)) instead of the above set insn. --- ORIG/trunk/gcc/reload1.c 2006-11-19 10:41:46.000000000 +0900 +++ LOCAL/trunk/gcc/reload1.c 2006-12-04 08:29:43.000000000 +0900 @@ -3093,7 +3093,15 @@ eliminate_regs_in_insn (rtx insn, int re if (ep->from_rtx == reg && ep->can_eliminate) { rtx to_rtx = ep->to_rtx; + unsigned int width = GET_MODE_BITSIZE (GET_MODE (reg)); + offset += ep->offset; + if (width < HOST_BITS_PER_WIDE_INT) + { + offset &= ((HOST_WIDE_INT) 1 << width) - 1; + if (offset & ((HOST_WIDE_INT) 1 << (width - 1))) + offset |= ((HOST_WIDE_INT) (-1) << width); + } if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG) to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)), I'm not sure that this is in the right direction, and it seems to be too invasive anyway. SH target modifies the operands of move insns in prepare_move_operands if needed. I guess the it'd be appropriate to handle this issue. Here is a patch in testing now: --- ORIG/trunk/gcc/config/sh/sh.c 2006-12-03 10:58:32.000000000 +0900 +++ LOCAL/trunk/gcc/config/sh/sh.c 2006-12-04 22:57:42.000000000 +0900 @@ -1345,6 +1345,20 @@ prepare_move_operands (rtx operands[], e } } + /* Reload might make move insn from const_int to a register of + which mode is narrower than HOST_WIDE_INT where the value of + const_int is out of range for that mode. See PR 29599. */ + if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT + && GET_CODE (operands[1]) == CONST_INT) + { + unsigned int width = GET_MODE_BITSIZE (mode); + HOST_WIDE_INT val = INTVAL (operands[1]); + + val &= ((HOST_WIDE_INT) 1 << width) - 1; + if (val & ((HOST_WIDE_INT) 1 << (width - 1))) + operands[1] = GEN_INT (((HOST_WIDE_INT) (-1) << width) | val); + } + return 0; } -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29599