#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "hard-reg-set.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));
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
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 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.
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)
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;
}
/* 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:
/* 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:
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);
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;
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
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
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;
case REG:
case SCRATCH:
case CLOBBER:
- 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;
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;
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;
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
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;
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;
/* 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 usefull. */
+ is unlikely useful. */
if (REGNO (x) < FIRST_PSEUDO_REGISTER)
{
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 hiden uses. */
+ hoisting of any insn having such a hidden uses. */
return false;
break;
case CLOBBER: