--- 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 - 0x8000 is
sfp - 0x8000 - 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 (- 0x8000 - 4, SImode).
Since gen_int_mode ( , SImode) truncates the constant to 32-bit,
it returns (const_int 2147483644 [0x7ffc]).
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 [0x7ffc]))
(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 0x7ffc + 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 0x8008))
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 0x8008 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 0x8008)) instead
of the above set insn.
--- ORIG/trunk/gcc/reload1.c2006-11-19 10:41:46.0 +0900
+++ LOCAL/trunk/gcc/reload1.c 2006-12-04 08:29:43.0 +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.0 +0900
+++ LOCAL/trunk/gcc/config/sh/sh.c 2006-12-04 22:57:42.0 +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