/* Emit RTL for the GCC expander.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GCC.
return rt_val;
}
\f
+/* Return the number of bytes between the start of an OUTER_MODE
+ in-memory value and the start of an INNER_MODE in-memory value,
+ given that the former is a lowpart of the latter. It may be a
+ paradoxical lowpart, in which case the offset will be negative
+ on big-endian targets. */
+
+int
+byte_lowpart_offset (enum machine_mode outer_mode,
+ enum machine_mode inner_mode)
+{
+ if (GET_MODE_SIZE (outer_mode) < GET_MODE_SIZE (inner_mode))
+ return subreg_lowpart_offset (outer_mode, inner_mode);
+ else
+ return -subreg_lowpart_offset (inner_mode, outer_mode);
+}
+\f
/* Generate a REG rtx for a new pseudo register of mode MODE.
This pseudo is assigned the next sequential register number. */
return val;
}
-/* Update NEW with the same attributes as REG, but offsetted by OFFSET.
- Do the big endian correction if needed. */
+/* Update NEW with the same attributes as REG, but with OFFSET added
+ to the REG_OFFSET. */
static void
update_reg_offset (rtx new, rtx reg, int offset)
{
- tree decl;
- HOST_WIDE_INT var_size;
-
- /* PR middle-end/14084
- The problem appears when a variable is stored in a larger register
- and later it is used in the original mode or some mode in between
- or some part of variable is accessed.
-
- On little endian machines there is no problem because
- the REG_OFFSET of the start of the variable is the same when
- accessed in any mode (it is 0).
-
- However, this is not true on big endian machines.
- The offset of the start of the variable is different when accessed
- in different modes.
- When we are taking a part of the REG we have to change the OFFSET
- from offset WRT size of mode of REG to offset WRT size of variable.
-
- If we would not do the big endian correction the resulting REG_OFFSET
- would be larger than the size of the DECL.
-
- Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
-
- REG.mode MODE DECL size old offset new offset description
- DI SI 4 4 0 int32 in SImode
- DI SI 1 4 0 char in SImode
- DI QI 1 7 0 char in QImode
- DI QI 4 5 1 1st element in QImode
- of char[4]
- DI HI 4 6 2 1st element in HImode
- of int16[2]
-
- If the size of DECL is equal or greater than the size of REG
- we can't do this correction because the register holds the
- whole variable or a part of the variable and thus the REG_OFFSET
- is already correct. */
-
- decl = REG_EXPR (reg);
- if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
- && decl != NULL
- && offset > 0
- && GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (GET_MODE (new))
- && ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
- && var_size < GET_MODE_SIZE (GET_MODE (reg))))
- {
- int offset_le;
-
- /* Convert machine endian to little endian WRT size of mode of REG. */
- if (WORDS_BIG_ENDIAN)
- offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
- / UNITS_PER_WORD) * UNITS_PER_WORD;
- else
- offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
-
- if (BYTES_BIG_ENDIAN)
- offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
- % UNITS_PER_WORD);
- else
- offset_le += offset % UNITS_PER_WORD;
-
- if (offset_le >= var_size)
- {
- /* MODE is wider than the variable so the new reg will cover
- the whole variable so the resulting OFFSET should be 0. */
- offset = 0;
- }
- else
- {
- /* Convert little endian to machine endian WRT size of variable. */
- if (WORDS_BIG_ENDIAN)
- offset = ((var_size - 1 - offset_le)
- / UNITS_PER_WORD) * UNITS_PER_WORD;
- else
- offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
-
- if (BYTES_BIG_ENDIAN)
- offset += ((var_size - 1 - offset_le)
- % UNITS_PER_WORD);
- else
- offset += offset_le % UNITS_PER_WORD;
- }
- }
-
REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
REG_OFFSET (reg) + offset);
}
-/* Generate a register with same attributes as REG, but offsetted by
- OFFSET. */
+/* Generate a register with same attributes as REG, but with OFFSET
+ added to the REG_OFFSET. */
rtx
gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno,
}
/* Generate a new pseudo-register with the same attributes as REG, but
- offsetted by OFFSET. */
+ with OFFSET added to the REG_OFFSET. */
rtx
gen_reg_rtx_offset (rtx reg, enum machine_mode mode, int offset)
return new;
}
-/* Set the decl for MEM to DECL. */
+/* Adjust REG in-place so that it has mode MODE. It is assumed that the
+ new register is a (possibly paradoxical) lowpart of the old one. */
void
-set_reg_attrs_from_mem (rtx reg, rtx mem)
+adjust_reg_mode (rtx reg, enum machine_mode mode)
{
- if (MEM_OFFSET (mem) && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
+ update_reg_offset (reg, reg, byte_lowpart_offset (mode, GET_MODE (reg)));
+ PUT_MODE (reg, mode);
+}
+
+/* Copy REG's attributes from X, if X has any attributes. If REG and X
+ have different modes, REG is a (possibly paradoxical) lowpart of X. */
+
+void
+set_reg_attrs_from_value (rtx reg, rtx x)
+{
+ int offset;
+
+ offset = byte_lowpart_offset (GET_MODE (reg), GET_MODE (x));
+ if (MEM_P (x) && MEM_OFFSET (x) && GET_CODE (MEM_OFFSET (x)) == CONST_INT)
REG_ATTRS (reg)
- = get_reg_attrs (MEM_EXPR (mem), INTVAL (MEM_OFFSET (mem)));
+ = get_reg_attrs (MEM_EXPR (x), INTVAL (MEM_OFFSET (x)) + offset);
+ if (REG_P (x) && REG_ATTRS (x))
+ update_reg_offset (reg, x, offset);
}
/* Set the register attributes for registers contained in PARM_RTX.
set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
{
if (REG_P (parm_rtx))
- set_reg_attrs_from_mem (parm_rtx, mem);
+ set_reg_attrs_from_value (parm_rtx, mem);
else if (GET_CODE (parm_rtx) == PARALLEL)
{
/* Check for a NULL entry in the first slot, used to indicate that the
}
}
-/* Assign the RTX X to declaration T. */
-void
-set_decl_rtl (tree t, rtx x)
-{
- DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
+/* Set the REG_ATTRS for registers in value X, given that X represents
+ decl T. */
- if (!x)
- return;
- /* For register, we maintain the reverse information too. */
- if (REG_P (x))
- REG_ATTRS (x) = get_reg_attrs (t, 0);
- else if (GET_CODE (x) == SUBREG)
- REG_ATTRS (SUBREG_REG (x))
- = get_reg_attrs (t, -SUBREG_BYTE (x));
- if (GET_CODE (x) == CONCAT)
- {
- if (REG_P (XEXP (x, 0)))
- REG_ATTRS (XEXP (x, 0)) = get_reg_attrs (t, 0);
- if (REG_P (XEXP (x, 1)))
- REG_ATTRS (XEXP (x, 1))
- = get_reg_attrs (t, GET_MODE_UNIT_SIZE (GET_MODE (XEXP (x, 0))));
- }
- if (GET_CODE (x) == PARALLEL)
+static void
+set_reg_attrs_for_decl_rtl (tree t, rtx x)
+{
+ if (GET_CODE (x) == SUBREG)
{
- int i;
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- rtx y = XVECEXP (x, 0, i);
- if (REG_P (XEXP (y, 0)))
- REG_ATTRS (XEXP (y, 0)) = get_reg_attrs (t, INTVAL (XEXP (y, 1)));
- }
+ gcc_assert (subreg_lowpart_p (x));
+ x = SUBREG_REG (x);
}
-}
-
-/* Assign the RTX X to parameter declaration T. */
-void
-set_decl_incoming_rtl (tree t, rtx x)
-{
- DECL_INCOMING_RTL (t) = x;
-
- if (!x)
- return;
- /* For register, we maintain the reverse information too. */
if (REG_P (x))
- REG_ATTRS (x) = get_reg_attrs (t, 0);
- else if (GET_CODE (x) == SUBREG)
- REG_ATTRS (SUBREG_REG (x))
- = get_reg_attrs (t, -SUBREG_BYTE (x));
+ REG_ATTRS (x)
+ = get_reg_attrs (t, byte_lowpart_offset (GET_MODE (x),
+ DECL_MODE (t)));
if (GET_CODE (x) == CONCAT)
{
if (REG_P (XEXP (x, 0)))
}
}
+/* Assign the RTX X to declaration T. */
+
+void
+set_decl_rtl (tree t, rtx x)
+{
+ DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
+ if (x)
+ set_reg_attrs_for_decl_rtl (t, x);
+}
+
+/* Assign the RTX X to parameter declaration T. BY_REFERENCE_P is true
+ if the ABI requires the parameter to be passed by reference. */
+
+void
+set_decl_incoming_rtl (tree t, rtx x, bool by_reference_p)
+{
+ DECL_INCOMING_RTL (t) = x;
+ if (x && !by_reference_p)
+ set_reg_attrs_for_decl_rtl (t, x);
+}
+
/* Identify REG (which may be a CONCAT) as a user register. */
void
subreg_highpart_offset (outermode, innermode));
}
-/* Return offset in bytes to get OUTERMODE low part
- of the value in mode INNERMODE stored in memory in target format. */
+/* Return the SUBREG_BYTE for an OUTERMODE lowpart of an INNERMODE value. */
unsigned int
subreg_lowpart_offset (enum machine_mode outermode, enum machine_mode innermode)
set_mem_attributes_minus_bitpos (ref, t, objectp, 0);
}
-/* Set the decl for MEM to DECL. */
+/* Set MEM to the decl that REG refers to. */
void
set_mem_attrs_from_reg (rtx mem, rtx reg)
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
+ TODO_dump_func | TODO_verify_rtl_sharing, /* todo_flags_finish */
0 /* letter */
};
set_used_flags (DECL_RTL (t));
/* Now process sub-blocks. */
- for (t = BLOCK_SUBBLOCKS (blk); t; t = TREE_CHAIN (t))
+ for (t = BLOCK_SUBBLOCKS (blk); t; t = BLOCK_CHAIN (t))
set_used_decls (t);
}
/* Return the next insn. If it is a SEQUENCE, return the first insn
of the sequence. */
-#define NEXT_INSN_BODY do { \
- if (insn) \
- { \
- insn = NEXT_INSN (insn); \
- if (insn && NONJUMP_INSN_P (insn) \
- && GET_CODE (PATTERN (insn)) == SEQUENCE) \
- insn = XVECEXP (PATTERN (insn), 0, 0); \
- } \
- return insn; \
-} while (0)
-
rtx
next_insn (rtx insn)
{
- NEXT_INSN_BODY;
-}
+ if (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn && NONJUMP_INSN_P (insn)
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+ }
-const_rtx
-const_next_insn (const_rtx insn)
-{
- NEXT_INSN_BODY;
+ return insn;
}
-#undef NEXT_INSN_BODY
-
/* Return the previous insn. If it is a SEQUENCE, return the last insn
of the sequence. */
-#define PREVIOUS_INSN_BODY do { \
- if (insn) \
- { \
- insn = PREV_INSN (insn); \
- if (insn && NONJUMP_INSN_P (insn) \
- && GET_CODE (PATTERN (insn)) == SEQUENCE) \
- insn = XVECEXP (PATTERN (insn), 0, XVECLEN (PATTERN (insn), 0) - 1); \
- } \
- return insn; \
-} while (0)
-
rtx
previous_insn (rtx insn)
{
- PREVIOUS_INSN_BODY;
-}
+ if (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn && NONJUMP_INSN_P (insn)
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, XVECLEN (PATTERN (insn), 0) - 1);
+ }
-const_rtx
-const_previous_insn (const_rtx insn)
-{
- PREVIOUS_INSN_BODY;
+ return insn;
}
-#undef PREVIOUS_INSN_BODY
-
/* Return the next insn after INSN that is not a NOTE. This routine does not
look inside SEQUENCEs. */
-#define NEXT_NONNOTE_INSN_BODY do { \
- while (insn) \
- { \
- insn = NEXT_INSN (insn); \
- if (insn == 0 || !NOTE_P (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
next_nonnote_insn (rtx insn)
{
- NEXT_NONNOTE_INSN_BODY;
-}
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || !NOTE_P (insn))
+ break;
+ }
-const_rtx
-const_next_nonnote_insn (const_rtx insn)
-{
- NEXT_NONNOTE_INSN_BODY;
+ return insn;
}
-#undef NEXT_NONNOTE_INSN_BODY
-
/* Return the previous insn before INSN that is not a NOTE. This routine does
not look inside SEQUENCEs. */
-#define PREV_NONNOTE_INSN_BODY do { \
- while (insn) \
- { \
- insn = PREV_INSN (insn); \
- if (insn == 0 || !NOTE_P (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
prev_nonnote_insn (rtx insn)
{
- PREV_NONNOTE_INSN_BODY;
-}
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || !NOTE_P (insn))
+ break;
+ }
-const_rtx
-const_prev_nonnote_insn (const_rtx insn)
-{
- PREV_NONNOTE_INSN_BODY;
+ return insn;
}
-#undef PREV_NONNOTE_INSN_BODY
-
/* Return the next INSN, CALL_INSN or JUMP_INSN after INSN;
or 0, if there is none. This routine does not look inside
SEQUENCEs. */
-#define NEXT_REAL_INSN_BODY do { \
- while (insn) \
- { \
- insn = NEXT_INSN (insn); \
- if (insn == 0 || INSN_P (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
next_real_insn (rtx insn)
{
- NEXT_REAL_INSN_BODY;
-}
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || INSN_P (insn))
+ break;
+ }
-const_rtx
-const_next_real_insn (const_rtx insn)
-{
- NEXT_REAL_INSN_BODY;
+ return insn;
}
-#undef NEXT_REAL_INSN_BODY
-
/* Return the last INSN, CALL_INSN or JUMP_INSN before INSN;
or 0, if there is none. This routine does not look inside
SEQUENCEs. */
-#define PREV_REAL_INSN_BODY do { \
- while (insn) \
- { \
- insn = PREV_INSN (insn); \
- if (insn == 0 || INSN_P (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
prev_real_insn (rtx insn)
{
- PREV_REAL_INSN_BODY;
-}
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || INSN_P (insn))
+ break;
+ }
-const_rtx
-const_prev_real_insn (const_rtx insn)
-{
- PREV_REAL_INSN_BODY;
+ return insn;
}
-#undef PREV_REAL_INSN_BODY
-
/* Return the last CALL_INSN in the current list, or 0 if there is none.
This routine does not look inside SEQUENCEs. */
&& GET_CODE (PATTERN (insn)) != CLOBBER))));
}
-#define NEXT_ACTIVE_INSN_BODY do { \
- while (insn) \
- { \
- insn = NEXT_INSN (insn); \
- if (insn == 0 || active_insn_p (insn)) \
- break; \
- } \
- return insn;\
-} while (0)
-
rtx
next_active_insn (rtx insn)
{
- NEXT_ACTIVE_INSN_BODY;
-}
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || active_insn_p (insn))
+ break;
+ }
-const_rtx
-const_next_active_insn (const_rtx insn)
-{
- NEXT_ACTIVE_INSN_BODY;
+ return insn;
}
-#undef NEXT_ACTIVE_INSN_BODY
-
/* Find the last insn before INSN that really does something. This routine
does not look inside SEQUENCEs. Until reload has completed, this is the
same as prev_real_insn. */
-#define PREV_ACTIVE_INSN_BODY do { \
- while (insn) \
- { \
- insn = PREV_INSN (insn);\
- if (insn == 0 || active_insn_p (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
prev_active_insn (rtx insn)
{
- PREV_ACTIVE_INSN_BODY;
-}
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || active_insn_p (insn))
+ break;
+ }
-const_rtx
-const_prev_active_insn (const_rtx insn)
-{
- PREV_ACTIVE_INSN_BODY;
+ return insn;
}
-#undef PREV_ACTIVE_INSN_BODY
-
/* Return the next CODE_LABEL after the insn INSN, or 0 if there is none. */
-#define NEXT_LABEL_BODY do { \
- while (insn) \
- { \
- insn = NEXT_INSN (insn); \
- if (insn == 0 || LABEL_P (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
next_label (rtx insn)
{
- NEXT_LABEL_BODY;
-}
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || LABEL_P (insn))
+ break;
+ }
-const_rtx
-const_next_label (const_rtx insn)
-{
- NEXT_LABEL_BODY;
+ return insn;
}
-#undef NEXT_LABEL_BODY
-
/* Return the last CODE_LABEL before the insn INSN, or 0 if there is none. */
-#define PREV_LABEL_BODY do { \
- while (insn) \
- { \
- insn = PREV_INSN (insn); \
- if (insn == 0 || LABEL_P (insn)) \
- break; \
- } \
- return insn; \
-} while (0)
-
rtx
prev_label (rtx insn)
{
- PREV_LABEL_BODY;
-}
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || LABEL_P (insn))
+ break;
+ }
-const_rtx
-const_prev_label (const_rtx insn)
-{
- PREV_LABEL_BODY;
+ return insn;
}
-#undef PREV_LABEL_BODY
-
/* Return the last label to mark the same position as LABEL. Return null
if LABEL itself is null. */
rtx before = PREV_INSN (trial);
rtx after = NEXT_INSN (trial);
int has_barrier = 0;
- rtx tem, note_retval;
+ rtx tem, note_retval, note_libcall;
rtx note, seq;
int probability;
rtx insn_last, insn;
XEXP (note_retval, 0) = insn_last;
break;
+ case REG_RETVAL:
+ /* Relink the insns with REG_LIBCALL note and with REG_RETVAL note
+ after split. */
+ REG_NOTES (insn_last)
+ = gen_rtx_INSN_LIST (REG_RETVAL,
+ XEXP (note, 0),
+ REG_NOTES (insn_last));
+
+ note_libcall = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL);
+ XEXP (note_libcall, 0) = insn_last;
+ break;
+
default:
break;
}
/* If there are LABELS inside the split insns increment the
usage count so we don't delete the label. */
- if (NONJUMP_INSN_P (trial))
+ if (INSN_P (trial))
{
insn = insn_last;
while (insn != NULL_RTX)
{
+ /* JUMP_P insns have already been "marked" above. */
if (NONJUMP_INSN_P (insn))
mark_label_nuses (PATTERN (insn));
void
force_next_line_note (void)
{
-#ifdef USE_MAPPED_LOCATION
last_location = -1;
-#else
- last_location.line = -1;
-#endif
}
/* Place a note of KIND on insn INSN with DATUM as the datum. If a
which may be duplicated by the basic block reordering code. */
RTX_FRAME_RELATED_P (new) = RTX_FRAME_RELATED_P (insn);
- /* Copy all REG_NOTES except REG_LABEL since mark_jump_label will
- make them. */
+ /* Copy all REG_NOTES except REG_LABEL_OPERAND since mark_jump_label
+ will make them. REG_LABEL_TARGETs are created there too, but are
+ supposed to be sticky, so we copy them. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
- if (REG_NOTE_KIND (link) != REG_LABEL)
+ if (REG_NOTE_KIND (link) != REG_LABEL_OPERAND)
{
if (GET_CODE (link) == EXPR_LIST)
REG_NOTES (new)