-/* Analyze RTL for C-Compiler
+/* Analyze RTL for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software
+ Foundation, Inc.
This file is part of GCC.
#include "function.h"
/* Forward declarations */
-static int global_reg_mentioned_p_1 (rtx *, void *);
static void set_of_1 (rtx, rtx, void *);
static bool covers_regno_p (rtx, unsigned int);
static bool covers_regno_no_parallel_p (rtx, unsigned int);
and set by `-m...' switches. Must be defined in rtlanal.c. */
int target_flags;
+
+/* Truncation narrows the mode from SOURCE mode to DESTINATION mode.
+ If TARGET_MODE_REP_EXTENDED (DESTINATION, DESTINATION_REP) is
+ SIGN_EXTEND then while narrowing we also have to enforce the
+ representation and sign-extend the value to mode DESTINATION_REP.
+
+ If the value is already sign-extended to DESTINATION_REP mode we
+ can just switch to DESTINATION mode on it. For each pair of
+ integral modes SOURCE and DESTINATION, when truncating from SOURCE
+ to DESTINATION, NUM_SIGN_BIT_COPIES_IN_REP[SOURCE][DESTINATION]
+ contains the number of high-order bits in SOURCE that have to be
+ copies of the sign-bit so that we can do this mode-switch to
+ DESTINATION. */
+
+static unsigned int
+num_sign_bit_copies_in_rep[MAX_MODE_INT + 1][MAX_MODE_INT + 1];
\f
/* Return 1 if the value of X is unstable
(would be different at a different point in the program).
return 0;
}
-/* Return 0 if the use of X as an address in a MEM can cause a trap. */
+/* Return nonzero if the use of X as an address in a MEM can cause a trap.
+ MODE is the mode of the MEM (not that of X) and UNALIGNED_MEMS controls
+ whether nonzero is returned for unaligned memory accesses on strict
+ alignment machines. */
-int
-rtx_addr_can_trap_p (rtx x)
+static int
+rtx_addr_can_trap_p_1 (rtx x, enum machine_mode mode, bool unaligned_mems)
{
enum rtx_code code = GET_CODE (x);
return 1;
case CONST:
- return rtx_addr_can_trap_p (XEXP (x, 0));
+ return rtx_addr_can_trap_p_1 (XEXP (x, 0), mode, unaligned_mems);
case PLUS:
- /* An address is assumed not to trap if it is an address that can't
- trap plus a constant integer or it is the pic register plus a
- constant. */
- return ! ((! rtx_addr_can_trap_p (XEXP (x, 0))
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
- || (XEXP (x, 0) == pic_offset_table_rtx
- && CONSTANT_P (XEXP (x, 1))));
+ /* An address is assumed not to trap if:
+ - it is an address that can't trap plus a constant integer,
+ with the proper remainder modulo the mode size if we are
+ considering unaligned memory references. */
+ if (!rtx_addr_can_trap_p_1 (XEXP (x, 0), mode, unaligned_mems)
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ HOST_WIDE_INT offset;
+
+ if (!STRICT_ALIGNMENT
+ || !unaligned_mems
+ || GET_MODE_SIZE (mode) == 0)
+ return 0;
+
+ offset = INTVAL (XEXP (x, 1));
+
+#ifdef SPARC_STACK_BOUNDARY_HACK
+ /* ??? The SPARC port may claim a STACK_BOUNDARY higher than
+ the real alignment of %sp. However, when it does this, the
+ alignment of %sp+STACK_POINTER_OFFSET is STACK_BOUNDARY. */
+ if (SPARC_STACK_BOUNDARY_HACK
+ && (XEXP (x, 0) == stack_pointer_rtx
+ || XEXP (x, 0) == hard_frame_pointer_rtx))
+ offset -= STACK_POINTER_OFFSET;
+#endif
+
+ return offset % GET_MODE_SIZE (mode) != 0;
+ }
+
+ /* - or it is the pic register plus a constant. */
+ if (XEXP (x, 0) == pic_offset_table_rtx && CONSTANT_P (XEXP (x, 1)))
+ return 0;
+
+ return 1;
case LO_SUM:
case PRE_MODIFY:
- return rtx_addr_can_trap_p (XEXP (x, 1));
+ return rtx_addr_can_trap_p_1 (XEXP (x, 1), mode, unaligned_mems);
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
case POST_MODIFY:
- return rtx_addr_can_trap_p (XEXP (x, 0));
+ return rtx_addr_can_trap_p_1 (XEXP (x, 0), mode, unaligned_mems);
default:
break;
return 1;
}
+/* Return nonzero if the use of X as an address in a MEM can cause a trap. */
+
+int
+rtx_addr_can_trap_p (rtx x)
+{
+ return rtx_addr_can_trap_p_1 (x, VOIDmode, false);
+}
+
/* Return true if X is an address that is known to not be zero. */
bool
return 0;
}
\f
-/* A subroutine of global_reg_mentioned_p, returns 1 if *LOC mentions
- a global register. */
-
-static int
-global_reg_mentioned_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
-{
- int regno;
- rtx x = *loc;
-
- if (! x)
- return 0;
-
- switch (GET_CODE (x))
- {
- case SUBREG:
- if (REG_P (SUBREG_REG (x)))
- {
- if (REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
- && global_regs[subreg_regno (x)])
- return 1;
- return 0;
- }
- break;
-
- case REG:
- regno = REGNO (x);
- if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
- return 1;
- return 0;
-
- case SCRATCH:
- case PC:
- case CC0:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST:
- case LABEL_REF:
- return 0;
-
- case CALL:
- /* A non-constant call might use a global register. */
- return 1;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/* Returns nonzero if X mentions a global register. */
-
-int
-global_reg_mentioned_p (rtx x)
-{
- if (INSN_P (x))
- {
- if (CALL_P (x))
- {
- if (! CONST_OR_PURE_CALL_P (x))
- return 1;
- x = CALL_INSN_FUNCTION_USAGE (x);
- if (x == 0)
- return 0;
- }
- else
- x = PATTERN (x);
- }
-
- return for_each_rtx (&x, global_reg_mentioned_p_1, NULL);
-}
-\f
/* Return the number of places FIND appears within X. If COUNT_DEST is
zero, we do not count occurrences inside the destination of a SET. */
for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& (reg_overlap_mentioned_p (reg, PATTERN (insn))
- || (CALL_P (insn)
- && (find_reg_fusage (insn, USE, reg)
- || find_reg_fusage (insn, CLOBBER, reg)))))
+ || (CALL_P (insn) && find_reg_fusage (insn, USE, reg))))
return 1;
return 0;
}
{
rtx link;
+ gcc_assert (insn);
+
/* Ignore anything that is not an INSN, JUMP_INSN or CALL_INSN. */
if (! INSN_P (insn))
return 0;
return 0;
}
\f
-/* Return nonzero if evaluating rtx X might cause a trap. */
+enum may_trap_p_flags
+{
+ MTP_UNALIGNED_MEMS = 1,
+ MTP_AFTER_MOVE = 2
+};
+/* Return nonzero if evaluating rtx X might cause a trap.
+ (FLAGS & MTP_UNALIGNED_MEMS) controls whether nonzero is returned for
+ unaligned memory accesses on strict alignment machines. If
+ (FLAGS & AFTER_MOVE) is true, returns nonzero even in case the expression
+ cannot trap at its current location, but it might become trapping if moved
+ elsewhere. */
-int
-may_trap_p (rtx x)
+static int
+may_trap_p_1 (rtx x, unsigned flags)
{
int i;
enum rtx_code code;
const char *fmt;
+ bool unaligned_mems = (flags & MTP_UNALIGNED_MEMS) != 0;
if (x == 0)
return 0;
/* Memory ref can trap unless it's a static var or a stack slot. */
case MEM:
- if (MEM_NOTRAP_P (x))
+ if (/* MEM_NOTRAP_P only relates to the actual position of the memory
+ reference; moving it out of condition might cause its address
+ become invalid. */
+ !(flags & MTP_AFTER_MOVE)
+ && MEM_NOTRAP_P (x)
+ && (!STRICT_ALIGNMENT || !unaligned_mems))
return 0;
- return rtx_addr_can_trap_p (XEXP (x, 0));
+ return
+ rtx_addr_can_trap_p_1 (XEXP (x, 0), GET_MODE (x), unaligned_mems);
/* Division by a non-constant might trap. */
case DIV:
case UMOD:
if (HONOR_SNANS (GET_MODE (x)))
return 1;
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ if (SCALAR_FLOAT_MODE_P (GET_MODE (x)))
return flag_trapping_math;
if (!CONSTANT_P (XEXP (x, 1)) || (XEXP (x, 1) == const0_rtx))
return 1;
case NEG:
case ABS:
+ case SUBREG:
/* These operations don't trap even with floating point. */
break;
default:
/* Any floating arithmetic may trap. */
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
+ if (SCALAR_FLOAT_MODE_P (GET_MODE (x))
&& flag_trapping_math)
return 1;
}
{
if (fmt[i] == 'e')
{
- if (may_trap_p (XEXP (x, i)))
+ if (may_trap_p_1 (XEXP (x, i), flags))
return 1;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
- if (may_trap_p (XVECEXP (x, i, j)))
+ if (may_trap_p_1 (XVECEXP (x, i, j), flags))
return 1;
}
}
return 0;
}
+
+/* Return nonzero if evaluating rtx X might cause a trap. */
+
+int
+may_trap_p (rtx x)
+{
+ return may_trap_p_1 (x, 0);
+}
+
+/* Return nonzero if evaluating rtx X might cause a trap, when the expression
+ is moved from its current location by some optimization. */
+
+int
+may_trap_after_code_motion_p (rtx x)
+{
+ return may_trap_p_1 (x, MTP_AFTER_MOVE);
+}
+
+/* Same as above, but additionally return nonzero if evaluating rtx X might
+ cause a fault. We define a fault for the purpose of this function as a
+ erroneous execution condition that cannot be encountered during the normal
+ execution of a valid program; the typical example is an unaligned memory
+ access on a strict alignment machine. The compiler guarantees that it
+ doesn't generate code that will fault from a valid program, but this
+ guarantee doesn't mean anything for individual instructions. Consider
+ the following example:
+
+ struct S { int d; union { char *cp; int *ip; }; };
+
+ int foo(struct S *s)
+ {
+ if (s->d == 1)
+ return *s->ip;
+ else
+ return *s->cp;
+ }
+
+ on a strict alignment machine. In a valid program, foo will never be
+ invoked on a structure for which d is equal to 1 and the underlying
+ unique field of the union not aligned on a 4-byte boundary, but the
+ expression *s->ip might cause a fault if considered individually.
+
+ At the RTL level, potentially problematic expressions will almost always
+ verify may_trap_p; for example, the above dereference can be emitted as
+ (mem:SI (reg:P)) and this expression is may_trap_p for a generic register.
+ However, suppose that foo is inlined in a caller that causes s->cp to
+ point to a local character variable and guarantees that s->d is not set
+ to 1; foo may have been effectively translated into pseudo-RTL as:
+
+ if ((reg:SI) == 1)
+ (set (reg:SI) (mem:SI (%fp - 7)))
+ else
+ (set (reg:QI) (mem:QI (%fp - 7)))
+
+ Now (mem:SI (%fp - 7)) is considered as not may_trap_p since it is a
+ memory reference to a stack slot, but it will certainly cause a fault
+ on a strict alignment machine. */
+
+int
+may_trap_or_fault_p (rtx x)
+{
+ return may_trap_p_1 (x, MTP_UNALIGNED_MEMS);
+}
\f
/* Return nonzero if X contains a comparison that is not either EQ or NE,
i.e., an inequality. */
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.
-
- 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
- is not called.
-
- If REPLACE_DEST is 1, replacements are also done in destinations;
- otherwise, only sources are replaced. */
-
-rtx
-replace_regs (rtx x, rtx *reg_map, unsigned int nregs, int replace_dest)
-{
- enum rtx_code code;
- int i;
- const char *fmt;
-
- if (x == 0)
- return x;
-
- code = GET_CODE (x);
- switch (code)
- {
- case SCRATCH:
- case PC:
- case CC0:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_VECTOR:
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- return x;
-
- case REG:
- /* Verify that the register has an entry before trying to access it. */
- if (REGNO (x) < nregs && reg_map[REGNO (x)] != 0)
- {
- /* SUBREGs can't be shared. Always return a copy to ensure that if
- this replacement occurs more than once then each instance will
- get distinct rtx. */
- if (GET_CODE (reg_map[REGNO (x)]) == SUBREG)
- return copy_rtx (reg_map[REGNO (x)]);
- return reg_map[REGNO (x)];
- }
- return x;
-
- case SUBREG:
- /* Prevent making nested SUBREGs. */
- if (REG_P (SUBREG_REG (x)) && REGNO (SUBREG_REG (x)) < nregs
- && reg_map[REGNO (SUBREG_REG (x))] != 0
- && GET_CODE (reg_map[REGNO (SUBREG_REG (x))]) == SUBREG)
- {
- rtx map_val = reg_map[REGNO (SUBREG_REG (x))];
- return simplify_gen_subreg (GET_MODE (x), map_val,
- GET_MODE (SUBREG_REG (x)),
- SUBREG_BYTE (x));
- }
- break;
-
- case SET:
- if (replace_dest)
- SET_DEST (x) = replace_regs (SET_DEST (x), reg_map, nregs, 0);
-
- else if (MEM_P (SET_DEST (x))
- || GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
- /* Even if we are not to replace destinations, replace register if it
- is CONTAINED in destination (destination is memory or
- STRICT_LOW_PART). */
- XEXP (SET_DEST (x), 0) = replace_regs (XEXP (SET_DEST (x), 0),
- reg_map, nregs, 0);
- else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
- /* Similarly, for ZERO_EXTRACT we replace all operands. */
- break;
-
- SET_SRC (x) = replace_regs (SET_SRC (x), reg_map, nregs, 0);
- return x;
-
- default:
- break;
- }
-
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- XEXP (x, i) = replace_regs (XEXP (x, i), reg_map, nregs, replace_dest);
- else if (fmt[i] == 'E')
- {
- int j;
- for (j = 0; j < XVECLEN (x, i); j++)
- XVECEXP (x, i, j) = replace_regs (XVECEXP (x, i, j), reg_map,
- nregs, replace_dest);
- }
- }
- 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. */
return 0;
}
-/* Return 1 if the sequence of instructions beginning with FROM and up
- 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.
-
- For now, this function only checks that the region contains whole
- exception regions, but it could be extended to check additional
- conditions as well. */
-
-int
-insns_safe_to_move_p (rtx from, rtx to, rtx *new_to)
-{
- int eh_region_count = 0;
- int past_to_p = 0;
- rtx r = from;
-
- /* By default, assume the end of the region will be what was
- suggested. */
- if (new_to)
- *new_to = to;
-
- while (r)
- {
- if (NOTE_P (r))
- {
- switch (NOTE_LINE_NUMBER (r))
- {
- case NOTE_INSN_EH_REGION_BEG:
- ++eh_region_count;
- break;
-
- case NOTE_INSN_EH_REGION_END:
- if (eh_region_count == 0)
- /* This sequence of instructions contains the end of
- an exception region, but not he beginning. Moving
- it will cause chaos. */
- return 0;
-
- --eh_region_count;
- break;
-
- default:
- break;
- }
- }
- else if (past_to_p)
- /* If we've passed TO, and we see a non-note instruction, we
- can't extend the sequence to a movable sequence. */
- return 0;
-
- if (r == to)
- {
- if (!new_to)
- /* 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;
- }
-
- /* It's OK to move the sequence if there were matched sets of
- exception region notes. */
- if (past_to_p && eh_region_count == 0)
- {
- *new_to = r;
- return 1;
- }
-
- /* Go to the next instruction. */
- r = NEXT_INSN (r);
- }
-
- return 0;
-}
-
/* Return nonzero if IN contains a piece of rtl that has the address LOC. */
int
loc_mentioned_in_p (rtx *loc, rtx in)
total = COSTS_N_INSNS (7);
break;
case USE:
- /* Used in loop.c and combine.c as a marker. */
+ /* Used in combine.c as a marker. */
total = 0;
break;
default:
rtx op0, op1;
int reverse_code = 0;
enum machine_mode mode;
+ basic_block bb = BLOCK_FOR_INSN (insn);
code = GET_CODE (cond);
mode = GET_MODE (cond);
if ((prev = prev_nonnote_insn (prev)) == 0
|| !NONJUMP_INSN_P (prev)
- || FIND_REG_INC_NOTE (prev, NULL_RTX))
+ || FIND_REG_INC_NOTE (prev, NULL_RTX)
+ /* In cfglayout mode, there do not have to be labels at the
+ beginning of a block, or jumps at the end, so the previous
+ conditions would not stop us when we reach bb boundary. */
+ || BLOCK_FOR_INSN (prev) != bb)
break;
set = set_of (op0, prev);
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == LT
- && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
+ && SCALAR_FLOAT_MODE_P (inner_mode)
&& (fsfv = FLOAT_STORE_FLAG_VALUE (inner_mode),
REAL_VALUE_NEGATIVE (fsfv)))
#endif
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == GE
- && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
+ && SCALAR_FLOAT_MODE_P (inner_mode)
&& (fsfv = FLOAT_STORE_FLAG_VALUE (inner_mode),
REAL_VALUE_NEGATIVE (fsfv)))
#endif
allow_cc_mode, valid_at_insn_p);
}
+/* Initialize the table NUM_SIGN_BIT_COPIES_IN_REP based on
+ TARGET_MODE_REP_EXTENDED.
+
+ Note that we assume that the property of
+ TARGET_MODE_REP_EXTENDED(B, C) is sticky to the integral modes
+ narrower than mode B. I.e., if A is a mode narrower than B then in
+ order to be able to operate on it in mode B, mode A needs to
+ satisfy the requirements set by the representation of mode B. */
+
+static void
+init_num_sign_bit_copies_in_rep (void)
+{
+ enum machine_mode mode, in_mode;
+
+ for (in_mode = GET_CLASS_NARROWEST_MODE (MODE_INT); in_mode != VOIDmode;
+ in_mode = GET_MODE_WIDER_MODE (mode))
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != in_mode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ enum machine_mode i;
+
+ /* Currently, it is assumed that TARGET_MODE_REP_EXTENDED
+ extends to the next widest mode. */
+ gcc_assert (targetm.mode_rep_extended (mode, in_mode) == UNKNOWN
+ || GET_MODE_WIDER_MODE (mode) == in_mode);
+
+ /* We are in in_mode. Count how many bits outside of mode
+ have to be copies of the sign-bit. */
+ for (i = mode; i != in_mode; i = GET_MODE_WIDER_MODE (i))
+ {
+ enum machine_mode wider = GET_MODE_WIDER_MODE (i);
+
+ if (targetm.mode_rep_extended (i, wider) == SIGN_EXTEND
+ /* We can only check sign-bit copies starting from the
+ top-bit. In order to be able to check the bits we
+ have already seen we pretend that subsequent bits
+ have to be sign-bit copies too. */
+ || num_sign_bit_copies_in_rep [in_mode][mode])
+ num_sign_bit_copies_in_rep [in_mode][mode]
+ += GET_MODE_BITSIZE (wider) - GET_MODE_BITSIZE (i);
+ }
+ }
+}
+
+/* Suppose that truncation from the machine mode of X to MODE is not a
+ no-op. See if there is anything special about X so that we can
+ assume it already contains a truncated value of MODE. */
+
+bool
+truncated_to_mode (enum machine_mode mode, rtx x)
+{
+ /* This register has already been used in MODE without explicit
+ truncation. */
+ if (REG_P (x) && rtl_hooks.reg_truncated_to_mode (mode, x))
+ return true;
+
+ /* See if we already satisfy the requirements of MODE. If yes we
+ can just switch to MODE. */
+ if (num_sign_bit_copies_in_rep[GET_MODE (x)][mode]
+ && (num_sign_bit_copies (x, GET_MODE (x))
+ >= num_sign_bit_copies_in_rep[GET_MODE (x)][mode] + 1))
+ return true;
+
+ return false;
+}
\f
/* Initialize non_rtx_starting_operands, which is used to speed up
for_each_rtx. */
const char *first = strpbrk (format, "eEV");
non_rtx_starting_operands[i] = first ? first - format : -1;
}
+
+ init_num_sign_bit_copies_in_rep ();
}
+\f
+/* Check whether this is a constant pool constant. */
+bool
+constant_pool_constant_p (rtx x)
+{
+ x = avoid_constant_pool_reference (x);
+ return GET_CODE (x) == CONST_DOUBLE;
+}
+