* rtl.h (validate_subreg): Declare.
* emit-rtl.c (validate_subreg): New.
(gen_rtx_SUBREG): Use it.
* simplify-rtx.c (simplify_subreg): Likewise.
(simplify_gen_subreg): Likewise. Remove duplicate asserts.
* expr.c (emit_move_insn_1): Tidy complex move code. Use memory
fallback whenever gen_realpart/gen_imagpart would not be able to
create SUBREGs.
From-SVN: r91126
+2004-11-23 Richard Henderson <rth@redhat.com>
+
+ * rtl.h (validate_subreg): Declare.
+ * emit-rtl.c (validate_subreg): New.
+ (gen_rtx_SUBREG): Use it.
+ * simplify-rtx.c (simplify_subreg): Likewise.
+ (simplify_gen_subreg): Likewise. Remove duplicate asserts.
+ * expr.c (emit_move_insn_1): Tidy complex move code. Use memory
+ fallback whenever gen_realpart/gen_imagpart would not be able to
+ create SUBREGs.
+
2004-11-23 Richard Henderson <rth@redhat.com>
* expmed.c (extract_bit_field): Use simplify_gen_subreg instead of
return mem;
}
-rtx
-gen_rtx_SUBREG (enum machine_mode mode, rtx reg, int offset)
+/* We want to create (subreg:OMODE (obj:IMODE) OFFSET). Return true if
+ this construct would be valid, and false otherwise. */
+
+bool
+validate_subreg (enum machine_mode omode, enum machine_mode imode,
+ rtx reg, unsigned int offset)
{
- /* This is the most common failure type.
- Catch it early so we can see who does it. */
- gcc_assert (!(offset % GET_MODE_SIZE (mode)));
+ unsigned int isize = GET_MODE_SIZE (imode);
+ unsigned int osize = GET_MODE_SIZE (omode);
- /* This check isn't usable right now because combine will
- throw arbitrary crap like a CALL into a SUBREG in
- gen_lowpart_for_combine so we must just eat it. */
-#if 0
- /* Check for this too. */
- gcc_assert (offset < GET_MODE_SIZE (GET_MODE (reg)));
+ /* All subregs must be aligned. */
+ if (offset % osize != 0)
+ return false;
+
+ /* The subreg offset cannot be outside the inner object. */
+ if (offset >= isize)
+ return false;
+
+ /* ??? This should not be here. Temporarily continue to allow word_mode
+ subregs of anything. The most common offender is (subreg:SI (reg:DF)).
+ Generally, backends are doing something sketchy but it'll take time to
+ fix them all. */
+ if (omode == word_mode)
+ ;
+ /* ??? Similarly, e.g. with (subreg:DF (reg:TI)). Though store_bit_field
+ is the culprit here, and not the backends. */
+ else if (osize >= UNITS_PER_WORD && isize >= osize)
+ ;
+ /* Allow component subregs of complex and vector. Though given the below
+ extraction rules, it's not always clear what that means. */
+ else if ((COMPLEX_MODE_P (imode) || VECTOR_MODE_P (imode))
+ && GET_MODE_INNER (imode) == omode)
+ ;
+ /* ??? x86 sse code makes heavy use of *paradoxical* vector subregs,
+ i.e. (subreg:V4SF (reg:SF) 0). This surely isn't the cleanest way to
+ represent this. It's questionable if this ought to be represented at
+ all -- why can't this all be hidden in post-reload splitters that make
+ arbitrarily mode changes to the registers themselves. */
+ else if (VECTOR_MODE_P (omode) && GET_MODE_INNER (omode) == imode)
+ ;
+ /* Subregs involving floating point modes are not allowed to
+ change size. Therefore (subreg:DI (reg:DF) 0) is fine, but
+ (subreg:SI (reg:DF) 0) isn't. */
+ else if (FLOAT_MODE_P (imode) || FLOAT_MODE_P (omode))
+ {
+ if (isize != osize)
+ return false;
+ }
+
+ /* Paradoxical subregs must have offset zero. */
+ if (osize > isize)
+ return offset == 0;
+
+ /* This is a normal subreg. Verify that the offset is representable. */
+
+ /* For hard registers, we already have most of these rules collected in
+ subreg_offset_representable_p. */
+ if (reg && REG_P (reg) && HARD_REGISTER_P (reg))
+ {
+ unsigned int regno = REGNO (reg);
+
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ if ((COMPLEX_MODE_P (imode) || VECTOR_MODE_P (imode))
+ && GET_MODE_INNER (imode) == omode)
+ ;
+ else if (REG_CANNOT_CHANGE_MODE_P (regno, imode, omode))
+ return false;
#endif
+
+ return subreg_offset_representable_p (regno, imode, offset, omode);
+ }
+
+ /* For pseudo registers, we want most of the same checks. Namely:
+ If the register no larger than a word, the subreg must be lowpart.
+ If the register is larger than a word, the subreg must be the lowpart
+ of a subword. A subreg does *not* perform arbitrary bit extraction.
+ Given that we've already checked mode/offset alignment, we only have
+ to check subword subregs here. */
+ if (osize < UNITS_PER_WORD)
+ {
+ enum machine_mode wmode = isize > UNITS_PER_WORD ? word_mode : imode;
+ unsigned int low_off = subreg_lowpart_offset (omode, wmode);
+ if (offset % UNITS_PER_WORD != low_off)
+ return false;
+ }
+ return true;
+}
+
+rtx
+gen_rtx_SUBREG (enum machine_mode mode, rtx reg, int offset)
+{
+ gcc_assert (validate_subreg (mode, GET_MODE (reg), reg, offset));
return gen_rtx_raw_SUBREG (mode, reg, offset);
}
{
enum machine_mode mode = GET_MODE (x);
enum machine_mode submode;
- enum mode_class class = GET_MODE_CLASS (mode);
gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
/* Expand complex moves by moving real part and imag part, if possible. */
- else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
+ else if (COMPLEX_MODE_P (mode)
&& BLKmode != (submode = GET_MODE_INNER (mode))
&& (mov_optab->handlers[(int) submode].insn_code
!= CODE_FOR_nothing))
{
+ unsigned int modesize = GET_MODE_SIZE (mode);
+ unsigned int submodesize = GET_MODE_SIZE (submode);
+
/* Don't split destination if it is a stack push. */
- int stack = push_operand (x, GET_MODE (x));
+ int stack = push_operand (x, mode);
#ifdef PUSH_ROUNDING
/* In case we output to the stack, but the size is smaller than the
machine can push exactly, we need to use move instructions. */
- if (stack
- && (PUSH_ROUNDING (GET_MODE_SIZE (submode))
- != GET_MODE_SIZE (submode)))
+ if (stack && PUSH_ROUNDING (submodesize) != submodesize)
{
rtx temp;
HOST_WIDE_INT offset1, offset2;
add_optab,
#endif
stack_pointer_rtx,
- GEN_INT
- (PUSH_ROUNDING
- (GET_MODE_SIZE (GET_MODE (x)))),
+ GEN_INT (PUSH_ROUNDING (modesize)),
stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
if (temp != stack_pointer_rtx)
#ifdef STACK_GROWS_DOWNWARD
offset1 = 0;
- offset2 = GET_MODE_SIZE (submode);
+ offset2 = submodesize;
#else
- offset1 = -PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)));
- offset2 = (-PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)))
- + GET_MODE_SIZE (submode));
+ offset1 = -PUSH_ROUNDING (modesize);
+ offset2 = -PUSH_ROUNDING (modesize) + submodesize;
#endif
emit_move_insn (change_address (x, submode,
memory and reload. FIXME, we should see about using extract and
insert on integer registers, but complex short and complex char
variables should be rarely used. */
- if (GET_MODE_BITSIZE (mode) < 2 * BITS_PER_WORD
- && (reload_in_progress | reload_completed) == 0)
+ if ((reload_in_progress | reload_completed) == 0
+ && (!validate_subreg (submode, mode, NULL, submodesize)
+ || !validate_subreg (submode, mode, NULL, 0)))
{
- int packed_dest_p
- = (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER);
- int packed_src_p
- = (REG_P (y) && REGNO (y) < FIRST_PSEUDO_REGISTER);
-
- if (packed_dest_p || packed_src_p)
+ if (REG_P (x) || REG_P (y))
{
- enum mode_class reg_class = ((class == MODE_COMPLEX_FLOAT)
- ? MODE_FLOAT : MODE_INT);
-
+ rtx mem, cmem;
enum machine_mode reg_mode
- = mode_for_size (GET_MODE_BITSIZE (mode), reg_class, 1);
+ = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 1);
+
+ gcc_assert (reg_mode != BLKmode);
+
+ mem = assign_stack_temp (reg_mode, modesize, 0);
+ cmem = adjust_address (mem, mode, 0);
- if (reg_mode != BLKmode)
+ if (REG_P (x))
+ {
+ rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
+ emit_move_insn_1 (cmem, y);
+ return emit_move_insn_1 (sreg, mem);
+ }
+ else
{
- rtx mem = assign_stack_temp (reg_mode,
- GET_MODE_SIZE (mode), 0);
- rtx cmem = adjust_address (mem, mode, 0);
-
- if (packed_dest_p)
- {
- rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
-
- emit_move_insn_1 (cmem, y);
- return emit_move_insn_1 (sreg, mem);
- }
- else
- {
- rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
-
- emit_move_insn_1 (mem, sreg);
- return emit_move_insn_1 (x, cmem);
- }
+ rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
+ emit_move_insn_1 (mem, sreg);
+ return emit_move_insn_1 (x, cmem);
}
}
}
extern void delete_insn_chain_and_edges (rtx, rtx);
extern rtx gen_lowpart_SUBREG (enum machine_mode, rtx);
extern rtx gen_const_mem (enum machine_mode, rtx);
+extern bool validate_subreg (enum machine_mode, enum machine_mode,
+ rtx, unsigned int);
/* In combine.c */
extern int combine_instructions (rtx, unsigned int);
}
/* Recurse for further possible simplifications. */
- newx = simplify_subreg (outermode, SUBREG_REG (op),
- GET_MODE (SUBREG_REG (op)),
- final_offset);
+ newx = simplify_subreg (outermode, SUBREG_REG (op), innermostmode,
+ final_offset);
if (newx)
return newx;
- return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset);
+ if (validate_subreg (outermode, innermostmode,
+ SUBREG_REG (op), final_offset))
+ return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset);
+ return NULL_RTX;
}
/* SUBREG of a hard register => just change the register number
res = simplify_subreg (outermode, part, GET_MODE (part), final_offset);
if (res)
return res;
- /* We can at least simplify it by referring directly to the
- relevant part. */
- return gen_rtx_SUBREG (outermode, part, final_offset);
+ if (validate_subreg (outermode, GET_MODE (part), part, final_offset))
+ return gen_rtx_SUBREG (outermode, part, final_offset);
+ return NULL_RTX;
}
/* Optimize SUBREG truncations of zero and sign extended values. */
enum machine_mode innermode, unsigned int byte)
{
rtx newx;
- /* Little bit of sanity checking. */
- gcc_assert (innermode != VOIDmode);
- gcc_assert (outermode != VOIDmode);
- gcc_assert (innermode != BLKmode);
- gcc_assert (outermode != BLKmode);
-
- gcc_assert (GET_MODE (op) == innermode
- || GET_MODE (op) == VOIDmode);
-
- gcc_assert ((byte % GET_MODE_SIZE (outermode)) == 0);
- gcc_assert (byte < GET_MODE_SIZE (innermode));
newx = simplify_subreg (outermode, op, innermode, byte);
if (newx)
|| (REG_P (op) && REGNO (op) < FIRST_PSEUDO_REGISTER))
return NULL_RTX;
- return gen_rtx_SUBREG (outermode, op, byte);
+ if (validate_subreg (outermode, innermode, op, byte))
+ return gen_rtx_SUBREG (outermode, op, byte);
+
+ return NULL_RTX;
}
+
/* Simplify X, an rtx expression.
Return the simplified expression or NULL if no simplifications