#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "hard-reg-set.h"
#include "recog.h"
#include "tm_p.h"
#include "flags.h"
+#include "basic-block.h"
+#include "real.h"
/* Forward declarations */
static int global_reg_mentioned_p_1 PARAMS ((rtx *, void *));
static void set_of_1 PARAMS ((rtx, rtx, void *));
static void insn_dependent_p_1 PARAMS ((rtx, rtx, void *));
+static int rtx_referenced_p_1 PARAMS ((rtx *, void *));
static int computed_jump_p_1 PARAMS ((rtx));
static void parms_set PARAMS ((rtx, rtx, void *));
+static bool hoist_test_store PARAMS ((rtx, rtx, regset));
+static void hoist_update_store PARAMS ((rtx, rtx *, rtx, rtx));
/* Bit flags that specify the machine subtype we are compiling for.
Bits are tested using macros TARGET_... defined in the tm.h file
case LABEL_REF:
return 0;
+ case ADDRESSOF:
+ /* This will resolve to some offset from the frame pointer. */
+ return 0;
+
case REG:
/* Note that we have to test for the actual rtx used for the frame
and arg pointers and not just the register number in case we have
during alias analysis. */
return (! for_alias && rtx_varies_p (XEXP (x, 0), for_alias))
|| rtx_varies_p (XEXP (x, 1), for_alias);
-
+
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
return 1;
case LABEL_REF:
return 0;
+ case ADDRESSOF:
+ /* This will resolve to some offset from the frame pointer. */
+ return 0;
+
case REG:
/* As in rtx_varies_p, we have to use the actual rtx, not reg number. */
if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
return 1;
}
-/* Return 1 if X refers to a memory location whose address
+/* Return true if X is an address that is known to not be zero. */
+
+bool
+nonzero_address_p (x)
+ rtx x;
+{
+ enum rtx_code code = GET_CODE (x);
+
+ switch (code)
+ {
+ case SYMBOL_REF:
+ return !SYMBOL_REF_WEAK (x);
+
+ case LABEL_REF:
+ return true;
+
+ case ADDRESSOF:
+ /* This will resolve to some offset from the frame pointer. */
+ return true;
+
+ case REG:
+ /* As in rtx_varies_p, we have to use the actual rtx, not reg number. */
+ if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+ || x == stack_pointer_rtx
+ || (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM]))
+ return true;
+ /* All of the virtual frame registers are stack references. */
+ if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
+ && REGNO (x) <= LAST_VIRTUAL_REGISTER)
+ return true;
+ return false;
+
+ case CONST:
+ return nonzero_address_p (XEXP (x, 0));
+
+ case PLUS:
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ /* Pointers aren't allowed to wrap. If we've got a register
+ that is known to be a pointer, and a positive offset, then
+ the composite can't be zero. */
+ if (INTVAL (XEXP (x, 1)) > 0
+ && REG_P (XEXP (x, 0))
+ && REG_POINTER (XEXP (x, 0)))
+ return true;
+
+ return nonzero_address_p (XEXP (x, 0));
+ }
+ /* Handle PIC references. */
+ else if (XEXP (x, 0) == pic_offset_table_rtx
+ && CONSTANT_P (XEXP (x, 1)))
+ return true;
+ return false;
+
+ case PRE_MODIFY:
+ /* Similar to the above; allow positive offsets. Further, since
+ auto-inc is only allowed in memories, the register must be a
+ pointer. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) > 0)
+ return true;
+ return nonzero_address_p (XEXP (x, 0));
+
+ case PRE_INC:
+ /* Similarly. Further, the offset is always positive. */
+ return true;
+
+ case PRE_DEC:
+ case POST_DEC:
+ case POST_INC:
+ case POST_MODIFY:
+ return nonzero_address_p (XEXP (x, 0));
+
+ case LO_SUM:
+ return nonzero_address_p (XEXP (x, 1));
+
+ default:
+ break;
+ }
+
+ /* If it isn't one of the case above, might be zero. */
+ return false;
+}
+
+/* Return 1 if X refers to a memory location whose address
cannot be compared reliably with constant addresses,
- or if X refers to a BLKmode memory object.
+ or if X refers to a BLKmode memory object.
FOR_ALIAS is nonzero if we are called from alias analysis; if it is
zero, we are slightly more conservative. */
into the jump table. If the offset cannot be determined, then return
NULL_RTX.
- If EARLIEST is non-zero, it is a pointer to a place where the earliest
+ If EARLIEST is nonzero, it is a pointer to a place where the earliest
insn used in locating the offset was found. */
rtx
rtx old_y;
int i;
- if (GET_CODE (insn) != JUMP_INSN
- || ! (label = JUMP_LABEL (insn))
- || ! (table = NEXT_INSN (label))
- || GET_CODE (table) != JUMP_INSN
- || (GET_CODE (PATTERN (table)) != ADDR_VEC
- && GET_CODE (PATTERN (table)) != ADDR_DIFF_VEC)
- || ! (set = single_set (insn)))
+ if (!tablejump_p (insn, &label, &table) || !(set = single_set (insn)))
return NULL_RTX;
x = SET_SRC (set);
return 0;
}
-/* Returns non-zero if X mentions a global register. */
+/* Returns nonzero if X mentions a global register. */
int
global_reg_mentioned_p (x)
rtx x;
{
-
if (INSN_P (x))
{
if (GET_CODE (x) == CALL_INSN)
x = CALL_INSN_FUNCTION_USAGE (x);
if (x == 0)
return 0;
- }
+ }
else
- x = PATTERN (x);
+ x = PATTERN (x);
}
return for_each_rtx (&x, global_reg_mentioned_p_1, NULL);
case CONST_DOUBLE:
/* These are kept unique for a given value. */
return 0;
-
+
default:
break;
}
if (reg_referenced_p (x, XVECEXP (body, 0, i)))
return 1;
return 0;
-
+
case CLOBBER:
if (GET_CODE (XEXP (body, 0)) == MEM)
if (reg_overlap_mentioned_p (x, XEXP (XEXP (body, 0), 0)))
reg_set_p (reg, insn)
rtx reg, insn;
{
- rtx body = insn;
-
/* We can be passed an insn or part of one. If we are passed an insn,
check if a side-effect of the insn clobbers REG. */
- if (INSN_P (insn))
- {
- if (FIND_REG_INC_NOTE (insn, reg)
+ if (INSN_P (insn)
+ && (FIND_REG_INC_NOTE (insn, reg)
|| (GET_CODE (insn) == CALL_INSN
/* We'd like to test call_used_regs here, but rtlanal.c can't
reference that variable due to its use in genattrtab. So
&& ((GET_CODE (reg) == REG
&& REGNO (reg) < FIRST_PSEUDO_REGISTER)
|| GET_CODE (reg) == MEM
- || find_reg_fusage (insn, CLOBBER, reg))))
- return 1;
-
- body = PATTERN (insn);
- }
+ || find_reg_fusage (insn, CLOBBER, reg)))))
+ return 1;
return set_of (reg, insn) != NULL_RTX;
}
case REG:
return reg_set_between_p (x, start, end);
-
+
default:
break;
}
/* Similar to reg_set_between_p, but check all registers in X. Return 0
only if none of them are modified between START and END. Return 1 if
- X contains a MEM; this routine does not perform any memory aliasing. */
+ X contains a MEM; this routine does usememory aliasing. */
int
modified_between_p (x, start, end)
enum rtx_code code = GET_CODE (x);
const char *fmt;
int i, j;
+ rtx insn;
+
+ if (start == end)
+ return 0;
switch (code)
{
return 1;
case MEM:
- /* If the memory is not constant, assume it is modified. If it is
- constant, we still have to check the address. */
- if (! RTX_UNCHANGING_P (x))
+ if (RTX_UNCHANGING_P (x))
+ return 0;
+ if (modified_between_p (XEXP (x, 0), start, end))
return 1;
+ for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn))
+ if (memory_modified_in_insn_p (x, insn))
+ return 1;
+ return 0;
break;
case REG:
return reg_set_between_p (x, start, end);
-
+
default:
break;
}
/* Similar to reg_set_p, but check all registers in X. Return 0 only if none
of them are modified in INSN. Return 1 if X contains a MEM; this routine
- does not perform any memory aliasing. */
+ does use memory aliasing. */
int
modified_in_p (x, insn)
return 1;
case MEM:
- /* If the memory is not constant, assume it is modified. If it is
- constant, we still have to check the address. */
- if (! RTX_UNCHANGING_P (x))
+ if (RTX_UNCHANGING_P (x))
+ return 0;
+ if (modified_in_p (XEXP (x, 0), insn))
return 1;
+ if (memory_modified_in_insn_p (x, insn))
+ return 1;
+ return 0;
break;
case REG:
{
int found;
int i;
-
+
/* INSN must be an insn. */
if (! INSN_P (insn))
return 0;
found = 1;
}
}
-
+
/* Either zero or one SET. */
return 0;
}
rtx src = SET_SRC (set);
rtx dst = SET_DEST (set);
- if (side_effects_p (src) || side_effects_p (dst))
- return 0;
-
- if (GET_CODE (dst) == MEM && GET_CODE (src) == MEM)
- return rtx_equal_p (dst, src);
-
if (dst == pc_rtx && src == pc_rtx)
return 1;
+ if (GET_CODE (dst) == MEM && GET_CODE (src) == MEM)
+ return rtx_equal_p (dst, src) && !side_effects_p (dst);
+
if (GET_CODE (dst) == SIGN_EXTRACT
|| GET_CODE (dst) == ZERO_EXTRACT)
return rtx_equal_p (XEXP (dst, 0), src)
- && ! BYTES_BIG_ENDIAN && XEXP (dst, 2) == const0_rtx;
+ && ! BYTES_BIG_ENDIAN && XEXP (dst, 2) == const0_rtx
+ && !side_effects_p (src);
if (GET_CODE (dst) == STRICT_LOW_PART)
dst = XEXP (dst, 0);
return src;
}
}
-
+
/* If set in non-simple way, we don't have a value. */
if (reg_set_p (x, p))
break;
}
return x;
-}
+}
\f
/* Return nonzero if register in range [REGNO, ENDREGNO)
appears either explicitly or implicitly in X
return 1;
return (endregno > x_regno
- && regno < x_regno + (x_regno < FIRST_PSEUDO_REGISTER
+ && regno < x_regno + (x_regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (x_regno, GET_MODE (x))
: 1));
else if (fmt[i] == 'E')
{
int j;
- for (j = XVECLEN (x, i) - 1; j >=0; j--)
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (loc != &XVECEXP (x, i, j)
&& refers_to_regno_p (regno, endregno, XVECEXP (x, i, j), loc))
return 1;
unsigned int regno, endregno;
/* Overly conservative. */
- if (GET_CODE (x) == STRICT_LOW_PART)
+ if (GET_CODE (x) == STRICT_LOW_PART
+ || GET_CODE (x) == ZERO_EXTRACT
+ || GET_CODE (x) == SIGN_EXTRACT)
x = XEXP (x, 0);
/* If either argument is a constant, then modifying X can not affect IN. */
If the item being stored in or clobbered is a SUBREG of a hard register,
the SUBREG will be passed. */
-
+
void
note_stores (x, fun, data)
rtx x;
if (GET_CODE (pattern) == SET)
{
- rtx dest = SET_DEST (PATTERN (insn));
-
+ rtx dest = SET_DEST (pattern);
+
/* A value is totally replaced if it is the destination or the
destination is a SUBREG of REGNO that does not change the number of
words in it. */
find_reg_equal_equiv_note (insn)
rtx insn;
{
- rtx note;
+ rtx link;
- if (single_set (insn) == 0)
+ if (!INSN_P (insn))
return 0;
- else if ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != 0)
- return note;
- else
- return find_reg_note (insn, REG_EQUAL, NULL_RTX);
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_EQUAL
+ || REG_NOTE_KIND (link) == REG_EQUIV)
+ {
+ if (single_set (insn) == 0)
+ return 0;
+ return link;
+ }
+ return NULL;
}
/* Return true if DATUM, or any overlap of DATUM, of kind CODE is found
rtx link;
for (link = CALL_INSN_FUNCTION_USAGE (insn);
- link;
+ link;
link = XEXP (link, 1))
- if (GET_CODE (XEXP (link, 0)) == code
+ if (GET_CODE (XEXP (link, 0)) == code
&& rtx_equal_p (datum, XEXP (XEXP (link, 0), 0)))
- return 1;
+ return 1;
}
else
{
to pseudo registers, so don't bother checking. */
if (regno < FIRST_PSEUDO_REGISTER)
- {
+ {
unsigned int end_regno
= regno + HARD_REGNO_NREGS (regno, GET_MODE (datum));
unsigned int i;
for (i = regno; i < end_regno; i++)
if (find_regno_fusage (insn, code, i))
return 1;
- }
+ }
}
return 0;
case REG:
case SCRATCH:
case CLOBBER:
- case ASM_INPUT:
case ADDR_VEC:
case ADDR_DIFF_VEC:
case CALL:
/* case TRAP_IF: This isn't clear yet. */
return 1;
+ case ASM_INPUT:
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
return 1;
{
const char *fmt = GET_RTX_FORMAT (code);
int i;
-
+
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
case REG:
case SCRATCH:
case CLOBBER:
- case ASM_INPUT:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return 0;
- case CALL:
case UNSPEC_VOLATILE:
- /* case TRAP_IF: This isn't clear yet. */
return 1;
case MEM:
+ case ASM_INPUT:
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
return 1;
{
const char *fmt = GET_RTX_FORMAT (code);
int i;
-
+
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
case PC:
case REG:
case SCRATCH:
- case ASM_INPUT:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return 0;
return 1;
case MEM:
+ case ASM_INPUT:
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
return 1;
{
const char *fmt = GET_RTX_FORMAT (code);
int i;
-
+
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
/* Memory ref can trap unless it's a static var or a stack slot. */
case MEM:
+ if (MEM_NOTRAP_P (x))
+ return 0;
return rtx_addr_can_trap_p (XEXP (x, 0));
/* Division by a non-constant might trap. */
case MOD:
case UDIV:
case UMOD:
+ if (HONOR_SNANS (GET_MODE (x)))
+ return 1;
if (! CONSTANT_P (XEXP (x, 1))
|| (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
&& flag_trapping_math))
when COMPARE is used, though many targets do make this distinction.
For instance, sparc uses CCFPE for compares which generate exceptions
and CCFP for compares which do not generate exceptions. */
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ if (HONOR_NANS (GET_MODE (x)))
return 1;
/* But often the compare has some CC mode, so check operand
modes as well. */
- if (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_FLOAT
- || GET_MODE_CLASS (GET_MODE (XEXP (x, 1))) == MODE_FLOAT)
+ if (HONOR_NANS (GET_MODE (XEXP (x, 0)))
+ || HONOR_NANS (GET_MODE (XEXP (x, 1))))
+ return 1;
+ break;
+
+ case EQ:
+ case NE:
+ if (HONOR_SNANS (GET_MODE (x)))
+ return 1;
+ /* Often comparison is CC mode, so check operand modes. */
+ if (HONOR_SNANS (GET_MODE (XEXP (x, 0)))
+ || HONOR_SNANS (GET_MODE (XEXP (x, 1))))
+ return 1;
+ break;
+
+ case FIX:
+ /* Conversion of floating point might trap. */
+ if (flag_trapping_math && HONOR_NANS (GET_MODE (XEXP (x, 0))))
return 1;
break;
case GE:
case GEU:
return 1;
-
+
default:
break;
}
return 1;
}
}
-
+
return 0;
}
\f
}
return x;
-}
+}
\f
/* Throughout the rtx X, replace many registers according to REG_MAP.
Return the replacement for X (which may be X with altered contents).
REG_MAP[R] is the replacement for register R, or 0 for don't replace.
- NREGS is the length of REG_MAP; regs >= NREGS are not mapped.
+ NREGS is the length of REG_MAP; regs >= NREGS are not mapped.
We only support REG_MAP entries of REG or SUBREG. Also, hard registers
should not be mapped to pseudos or vice versa since validate_change
{
rtx map_val = reg_map[REGNO (SUBREG_REG (x))];
return simplify_gen_subreg (GET_MODE (x), map_val,
- GET_MODE (SUBREG_REG (x)),
+ GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x));
}
break;
SET_SRC (x) = replace_regs (SET_SRC (x), reg_map, nregs, 0);
return x;
-
+
default:
break;
}
return x;
}
+/* Replace occurrences of the old label in *X with the new one.
+ DATA is a REPLACE_LABEL_DATA containing the old and new labels. */
+
+int
+replace_label (x, data)
+ rtx *x;
+ void *data;
+{
+ rtx l = *x;
+ rtx tmp;
+ rtx old_label = ((replace_label_data *) data)->r1;
+ rtx new_label = ((replace_label_data *) data)->r2;
+ bool update_label_nuses = ((replace_label_data *) data)->update_label_nuses;
+
+ if (l == NULL_RTX)
+ return 0;
+
+ if (GET_CODE (l) == MEM
+ && (tmp = XEXP (l, 0)) != NULL_RTX
+ && GET_CODE (tmp) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (tmp))
+ {
+ rtx c = get_pool_constant (tmp);
+ if (rtx_referenced_p (old_label, c))
+ {
+ rtx new_c, new_l;
+ replace_label_data *d = (replace_label_data *) data;
+
+ /* Create a copy of constant C; replace the label inside
+ but do not update LABEL_NUSES because uses in constant pool
+ are not counted. */
+ new_c = copy_rtx (c);
+ d->update_label_nuses = false;
+ for_each_rtx (&new_c, replace_label, data);
+ d->update_label_nuses = update_label_nuses;
+
+ /* Add the new constant NEW_C to constant pool and replace
+ the old reference to constant by new reference. */
+ new_l = force_const_mem (get_pool_mode (tmp), new_c);
+ *x = replace_rtx (l, l, new_l);
+ }
+ return 0;
+ }
+
+ /* If this is a JUMP_INSN, then we also need to fix the JUMP_LABEL
+ field. This is not handled by for_each_rtx because it doesn't
+ handle unprinted ('0') fields. */
+ if (GET_CODE (l) == JUMP_INSN && JUMP_LABEL (l) == old_label)
+ JUMP_LABEL (l) = new_label;
+
+ if ((GET_CODE (l) == LABEL_REF
+ || GET_CODE (l) == INSN_LIST)
+ && XEXP (l, 0) == old_label)
+ {
+ XEXP (l, 0) = new_label;
+ if (update_label_nuses)
+ {
+ ++LABEL_NUSES (new_label);
+ --LABEL_NUSES (old_label);
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+/* When *BODY is equal to X or X is directly referenced by *BODY
+ return nonzero, thus FOR_EACH_RTX stops traversing and returns nonzero
+ too, otherwise FOR_EACH_RTX continues traversing *BODY. */
+
+static int
+rtx_referenced_p_1 (body, x)
+ rtx *body;
+ void *x;
+{
+ rtx y = (rtx) x;
+
+ if (*body == NULL_RTX)
+ return y == NULL_RTX;
+
+ /* Return true if a label_ref *BODY refers to label Y. */
+ if (GET_CODE (*body) == LABEL_REF && GET_CODE (y) == CODE_LABEL)
+ return XEXP (*body, 0) == y;
+
+ /* If *BODY is a reference to pool constant traverse the constant. */
+ if (GET_CODE (*body) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (*body))
+ return rtx_referenced_p (y, get_pool_constant (*body));
+
+ /* By default, compare the RTL expressions. */
+ return rtx_equal_p (*body, y);
+}
+
+/* Return true if X is referenced in BODY. */
+
+int
+rtx_referenced_p (x, body)
+ rtx x;
+ rtx body;
+{
+ return for_each_rtx (&body, rtx_referenced_p_1, x);
+}
+
+/* If INSN is a jump to jumptable insn rturn true and store the label (which
+ INSN jumps to) to *LABEL and the tablejump insn to *TABLE.
+ LABEL and TABLE may be NULL. */
+
+bool
+tablejump_p (insn, label, table)
+ rtx insn;
+ rtx *label;
+ rtx *table;
+{
+ rtx l, t;
+
+ if (onlyjump_p (insn)
+ && (l = JUMP_LABEL (insn)) != NULL_RTX
+ && (t = NEXT_INSN (l)) != NULL_RTX
+ && GET_CODE (t) == JUMP_INSN
+ && (GET_CODE (PATTERN (t)) == ADDR_VEC
+ || GET_CODE (PATTERN (t)) == ADDR_DIFF_VEC))
+ {
+ if (label)
+ *label = l;
+ if (table)
+ *table = t;
+ return true;
+ }
+ return false;
+}
+
/* A subroutine of computed_jump_p, return 1 if X contains a REG or MEM or
constant that is not in the constant pool and not in the condition
of an IF_THEN_ELSE. */
sub-expression (including X itself). F is also passed the DATA.
If F returns -1, do not traverse sub-expressions, but continue
traversing the rest of the tree. If F ever returns any other
- non-zero value, stop the traversal, and return the value returned
+ nonzero value, stop the traversal, and return the value returned
by F. Otherwise, return 0. This function does not traverse inside
tree structure that contains RTX_EXPRs, or into sub-expressions
whose format code is `0' since it is not known whether or not those
length = GET_RTX_LENGTH (GET_CODE (*x));
format = GET_RTX_FORMAT (GET_CODE (*x));
- for (i = 0; i < length; ++i)
+ for (i = 0; i < length; ++i)
{
- switch (format[i])
+ switch (format[i])
{
case 'e':
result = for_each_rtx (&XEXP (*x, i), f, data);
case 'V':
case 'E':
- if (XVEC (*x, i) != 0)
+ if (XVEC (*x, i) != 0)
{
int j;
for (j = 0; j < XVECLEN (*x, i); ++j)
return result;
}
}
- break;
+ break;
default:
/* Nothing to do. */
to and including TO is safe to move. If NEW_TO is non-NULL, and
the sequence is not already safe to move, but can be easily
extended to a sequence which is safe, then NEW_TO will point to the
- end of the extended sequence.
-
+ end of the extended sequence.
+
For now, this function only checks that the region contains whole
exception regions, but it could be extended to check additional
conditions as well. */
/* It's OK to move the sequence if there were matched sets of
exception region notes. */
return eh_region_count == 0;
-
+
past_to_p = 1;
}
/* Go to the next instruction. */
r = NEXT_INSN (r);
}
-
+
return 0;
}
-/* Return non-zero if IN contains a piece of rtl that has the address LOC */
+/* Return nonzero if IN contains a piece of rtl that has the address LOC */
int
loc_mentioned_in_p (loc, in)
rtx *loc, in;
nregs_xmode = HARD_REGNO_NREGS (xregno, xmode);
nregs_ymode = HARD_REGNO_NREGS (xregno, ymode);
+
+ /* If this is a big endian paradoxical subreg, which uses more actual
+ hard registers than the original register, we must return a negative
+ offset so that we find the proper highpart of the register. */
+ if (offset == 0
+ && nregs_ymode > nregs_xmode
+ && (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
+ ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
+ return nregs_xmode - nregs_ymode;
+
if (offset == 0 || nregs_xmode == nregs_ymode)
return 0;
-
+
/* size of ymode must not be greater than the size of xmode. */
mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
if (mode_multiple == 0)
return (y_offset / (mode_multiple / nregs_multiple)) * nregs_ymode;
}
+/* This function returns true when the offset is representable via
+ subreg_offset in the given regno.
+ xregno - A regno of an inner hard subreg_reg (or what will become one).
+ xmode - The mode of xregno.
+ offset - The byte offset.
+ ymode - The mode of a top level SUBREG (or what may become one).
+ RETURN - The regno offset which would be used. */
+bool
+subreg_offset_representable_p (xregno, xmode, offset, ymode)
+ unsigned int xregno;
+ enum machine_mode xmode;
+ unsigned int offset;
+ enum machine_mode ymode;
+{
+ int nregs_xmode, nregs_ymode;
+ int mode_multiple, nregs_multiple;
+ int y_offset;
+
+ if (xregno >= FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ nregs_xmode = HARD_REGNO_NREGS (xregno, xmode);
+ nregs_ymode = HARD_REGNO_NREGS (xregno, ymode);
+
+ /* paradoxical subregs are always valid. */
+ if (offset == 0
+ && nregs_ymode > nregs_xmode
+ && (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
+ ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
+ return true;
+
+ /* Lowpart subregs are always valid. */
+ if (offset == subreg_lowpart_offset (ymode, xmode))
+ return true;
+
+#ifdef ENABLE_CHECKING
+ /* This should always pass, otherwise we don't know how to verify the
+ constraint.
+
+ These conditions may be relaxed but subreg_offset would need to be
+ redesigned. */
+ if (GET_MODE_SIZE (xmode) % GET_MODE_SIZE (ymode)
+ || GET_MODE_SIZE (ymode) % nregs_ymode
+ || (GET_MODE_BITSIZE (mode_for_size (GET_MODE_BITSIZE (xmode)
+ / nregs_xmode,
+ MODE_INT, 0))
+ != GET_MODE_BITSIZE (xmode) / nregs_xmode)
+ || nregs_xmode % nregs_ymode)
+ abort ();
+#endif
+
+ /* The XMODE value can be seen as an vector of NREGS_XMODE
+ values. The subreg must represent an lowpart of given field.
+ Compute what field it is. */
+ offset -= subreg_lowpart_offset (ymode,
+ mode_for_size (GET_MODE_BITSIZE (xmode)
+ / nregs_xmode,
+ MODE_INT, 0));
+
+ /* size of ymode must not be greater than the size of xmode. */
+ mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
+ if (mode_multiple == 0)
+ abort ();
+
+ y_offset = offset / GET_MODE_SIZE (ymode);
+ nregs_multiple = nregs_xmode / nregs_ymode;
+#ifdef ENABLE_CHECKING
+ if (offset % GET_MODE_SIZE (ymode)
+ || mode_multiple % nregs_multiple)
+ abort ();
+#endif
+ return (!(y_offset % (mode_multiple / nregs_multiple)));
+}
+
/* Return the final regno that a subreg expression refers to. */
-unsigned int
+unsigned int
subreg_regno (x)
rtx x;
{
rtx subreg = SUBREG_REG (x);
int regno = REGNO (subreg);
- ret = regno + subreg_regno_offset (regno,
- GET_MODE (subreg),
+ ret = regno + subreg_regno_offset (regno,
+ GET_MODE (subreg),
SUBREG_BYTE (x),
GET_MODE (x));
return ret;
}
}
-/* Look backward for first parameter to be loaded.
+/* Look backward for first parameter to be loaded.
Do not skip BOUNDARY. */
rtx
find_first_parameter_load (call_insn, boundary)
}
if (INSN_P (before))
- note_stores (PATTERN (before), parms_set, &parm);
+ note_stores (PATTERN (before), parms_set, &parm);
}
return before;
}
-/* Return true if we should avoid inserting code between INSN and preceeding
+/* Return true if we should avoid inserting code between INSN and preceding
call instruction. */
bool
if (INSN_P (insn) && (set = single_set (insn)) != NULL)
{
if (GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
&& fixed_regs[REGNO (SET_DEST (set))]
&& general_operand (SET_SRC (set), VOIDmode))
return true;
}
return false;
}
+
+/* Return true when store to register X can be hoisted to the place
+ with LIVE registers (can be NULL). Value VAL contains destination
+ whose value will be used. */
+
+static bool
+hoist_test_store (x, val, live)
+ rtx x, val;
+ regset live;
+{
+ if (GET_CODE (x) == SCRATCH)
+ return true;
+
+ if (rtx_equal_p (x, val))
+ return true;
+
+ /* Allow subreg of X in case it is not writting just part of multireg pseudo.
+ Then we would need to update all users to care hoisting the store too.
+ Caller may represent that by specifying whole subreg as val. */
+
+ if (GET_CODE (x) == SUBREG && rtx_equal_p (SUBREG_REG (x), val))
+ {
+ if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) > UNITS_PER_WORD
+ && GET_MODE_BITSIZE (GET_MODE (x)) <
+ GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))))
+ return false;
+ return true;
+ }
+ if (GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+
+ /* Anything except register store is not hoistable. This includes the
+ partial stores to registers. */
+
+ if (!REG_P (x))
+ return false;
+
+ /* Pseudo registers can be allways replaced by another pseudo to avoid
+ the side effect, for hard register we must ensure that they are dead.
+ Eventually we may want to add code to try turn pseudos to hards, but it
+ is unlikely useful. */
+
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (x);
+ int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
+
+ if (!live)
+ return false;
+ if (REGNO_REG_SET_P (live, regno))
+ return false;
+ while (--n > 0)
+ if (REGNO_REG_SET_P (live, regno + n))
+ return false;
+ }
+ return true;
+}
+
+
+/* Return true if INSN can be hoisted to place with LIVE hard registers
+ (LIVE can be NULL when unknown). VAL is expected to be stored by the insn
+ and used by the hoisting pass. */
+
+bool
+can_hoist_insn_p (insn, val, live)
+ rtx insn, val;
+ regset live;
+{
+ rtx pat = PATTERN (insn);
+ int i;
+
+ /* It probably does not worth the complexity to handle multiple
+ set stores. */
+ if (!single_set (insn))
+ return false;
+ /* We can move CALL_INSN, but we need to check that all caller clobbered
+ regs are dead. */
+ if (GET_CODE (insn) == CALL_INSN)
+ return false;
+ /* In future we will handle hoisting of libcall sequences, but
+ give up for now. */
+ if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ return false;
+ switch (GET_CODE (pat))
+ {
+ case SET:
+ if (!hoist_test_store (SET_DEST (pat), val, live))
+ return false;
+ break;
+ case USE:
+ /* USES do have sick semantics, so do not move them. */
+ return false;
+ break;
+ case CLOBBER:
+ if (!hoist_test_store (XEXP (pat, 0), val, live))
+ return false;
+ break;
+ case PARALLEL:
+ for (i = 0; i < XVECLEN (pat, 0); i++)
+ {
+ rtx x = XVECEXP (pat, 0, i);
+ switch (GET_CODE (x))
+ {
+ case SET:
+ if (!hoist_test_store (SET_DEST (x), val, live))
+ return false;
+ break;
+ case USE:
+ /* We need to fix callers to really ensure availability
+ of all values inisn uses, but for now it is safe to prohibit
+ hoisting of any insn having such a hidden uses. */
+ return false;
+ break;
+ case CLOBBER:
+ if (!hoist_test_store (SET_DEST (x), val, live))
+ return false;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ abort ();
+ }
+ return true;
+}
+
+/* Update store after hoisting - replace all stores to pseudo registers
+ by new ones to avoid clobbering of values except for store to VAL that will
+ be updated to NEW. */
+
+static void
+hoist_update_store (insn, xp, val, new)
+ rtx insn, *xp, val, new;
+{
+ rtx x = *xp;
+
+ if (GET_CODE (x) == SCRATCH)
+ return;
+
+ if (GET_CODE (x) == SUBREG && SUBREG_REG (x) == val)
+ validate_change (insn, xp,
+ simplify_gen_subreg (GET_MODE (x), new, GET_MODE (new),
+ SUBREG_BYTE (x)), 1);
+ if (rtx_equal_p (x, val))
+ {
+ validate_change (insn, xp, new, 1);
+ return;
+ }
+ if (GET_CODE (x) == SUBREG)
+ {
+ xp = &SUBREG_REG (x);
+ x = *xp;
+ }
+
+ if (!REG_P (x))
+ abort ();
+
+ /* We've verified that hard registers are dead, so we may keep the side
+ effect. Otherwise replace it by new pseudo. */
+ if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ validate_change (insn, xp, gen_reg_rtx (GET_MODE (x)), 1);
+ REG_NOTES (insn)
+ = alloc_EXPR_LIST (REG_UNUSED, *xp, REG_NOTES (insn));
+}
+
+/* Create a copy of INSN after AFTER replacing store of VAL to NEW
+ and each other side effect to pseudo register by new pseudo register. */
+
+rtx
+hoist_insn_after (insn, after, val, new)
+ rtx insn, after, val, new;
+{
+ rtx pat;
+ int i;
+ rtx note;
+
+ insn = emit_copy_of_insn_after (insn, after);
+ pat = PATTERN (insn);
+
+ /* Remove REG_UNUSED notes as we will re-emit them. */
+ while ((note = find_reg_note (insn, REG_UNUSED, NULL_RTX)))
+ remove_note (insn, note);
+
+ /* To get this working callers must ensure to move everything referenced
+ by REG_EQUAL/REG_EQUIV notes too. Lets remove them, it is probably
+ easier. */
+ while ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX)))
+ remove_note (insn, note);
+ while ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)))
+ remove_note (insn, note);
+
+ /* Remove REG_DEAD notes as they might not be valid anymore in case
+ we create redundancy. */
+ while ((note = find_reg_note (insn, REG_DEAD, NULL_RTX)))
+ remove_note (insn, note);
+ switch (GET_CODE (pat))
+ {
+ case SET:
+ hoist_update_store (insn, &SET_DEST (pat), val, new);
+ break;
+ case USE:
+ break;
+ case CLOBBER:
+ hoist_update_store (insn, &XEXP (pat, 0), val, new);
+ break;
+ case PARALLEL:
+ for (i = 0; i < XVECLEN (pat, 0); i++)
+ {
+ rtx x = XVECEXP (pat, 0, i);
+ switch (GET_CODE (x))
+ {
+ case SET:
+ hoist_update_store (insn, &SET_DEST (x), val, new);
+ break;
+ case USE:
+ break;
+ case CLOBBER:
+ hoist_update_store (insn, &SET_DEST (x), val, new);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ abort ();
+ }
+ if (!apply_change_group ())
+ abort ();
+
+ return insn;
+}
+
+rtx
+hoist_insn_to_edge (insn, e, val, new)
+ rtx insn, val, new;
+ edge e;
+{
+ rtx new_insn;
+
+ /* We cannot insert instructions on an abnormal critical edge.
+ It will be easier to find the culprit if we die now. */
+ if ((e->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (e))
+ abort ();
+
+ /* Do not use emit_insn_on_edge as we want to preserve notes and similar
+ stuff. We also emit CALL_INSNS and firends. */
+ if (e->insns == NULL_RTX)
+ {
+ start_sequence ();
+ emit_note (NULL, NOTE_INSN_DELETED);
+ }
+ else
+ push_to_sequence (e->insns);
+
+ new_insn = hoist_insn_after (insn, get_last_insn (), val, new);
+
+ e->insns = get_insns ();
+ end_sequence ();
+ return new_insn;
+}