/* Emit RTL for the GNU C-Compiler expander.
- Copyright (C) 1987, 88, 92-99, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "expr.h"
#include "regs.h"
#include "hard-reg-set.h"
+#include "hashtab.h"
#include "insn-config.h"
#include "recog.h"
#include "real.h"
rtx const_int_rtx[MAX_SAVED_CONST_INT * 2 + 1];
+/* A hash table storing CONST_INTs whose absolute value is greater
+ than MAX_SAVED_CONST_INT. */
+
+static htab_t const_int_htab;
+
/* start_sequence and gen_sequence can make a lot of rtx expressions which are
shortly thrown away. We use two mechanisms to prevent this waste:
static rtx make_call_insn_raw PARAMS ((rtx));
static rtx find_line_note PARAMS ((rtx));
static void mark_sequence_stack PARAMS ((struct sequence_stack *));
+static void unshare_all_rtl_1 PARAMS ((rtx));
+static hashval_t const_int_htab_hash PARAMS ((const void *));
+static int const_int_htab_eq PARAMS ((const void *,
+ const void *));
+static int rtx_htab_mark_1 PARAMS ((void **, void *));
+static void rtx_htab_mark PARAMS ((void *));
+
\f
+/* Returns a hash code for X (which is a really a CONST_INT). */
+
+static hashval_t
+const_int_htab_hash (x)
+ const void *x;
+{
+ return (hashval_t) INTVAL ((const struct rtx_def *) x);
+}
+
+/* Returns non-zero if the value represented by X (which is really a
+ CONST_INT) is the same as that given by Y (which is really a
+ HOST_WIDE_INT *). */
+
+static int
+const_int_htab_eq (x, y)
+ const void *x;
+ const void *y;
+{
+ return (INTVAL ((const struct rtx_def *) x) == *((const HOST_WIDE_INT *) y));
+}
+
+/* Mark the hash-table element X (which is really a pointer to an
+ rtx). */
+
+static int
+rtx_htab_mark_1 (x, data)
+ void **x;
+ void *data ATTRIBUTE_UNUSED;
+{
+ ggc_mark_rtx (*x);
+ return 1;
+}
+
+/* Mark all the elements of HTAB (which is really an htab_t full of
+ rtxs). */
+
+static void
+rtx_htab_mark (htab)
+ void *htab;
+{
+ htab_traverse (*((htab_t *) htab), rtx_htab_mark_1, NULL);
+}
+
/* There are some RTL codes that require special attention; the generation
functions do the raw handling. If you add to this list, modify
special_rtx in gengenrtl.c as well. */
rtx
gen_rtx_CONST_INT (mode, arg)
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
HOST_WIDE_INT arg;
{
+ void **slot;
+
if (arg >= - MAX_SAVED_CONST_INT && arg <= MAX_SAVED_CONST_INT)
return const_int_rtx[arg + MAX_SAVED_CONST_INT];
return const_true_rtx;
#endif
- return gen_rtx_raw_CONST_INT (mode, arg);
+ /* Look up the CONST_INT in the hash table. */
+ slot = htab_find_slot_with_hash (const_int_htab, &arg, (hashval_t) arg, 1);
+ if (*slot == 0)
+ {
+ if (!ggc_p)
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ *slot = gen_rtx_raw_CONST_INT (VOIDmode, arg);
+ pop_obstacks ();
+ }
+ else
+ *slot = gen_rtx_raw_CONST_INT (VOIDmode, arg);
+ }
+
+ return (rtx) *slot;
}
-/* CONST_DOUBLEs needs special handling because its length is known
+/* CONST_DOUBLEs needs special handling because their length is known
only at run-time. */
+
rtx
gen_rtx_CONST_DOUBLE (mode, arg0, arg1, arg2)
enum machine_mode mode;
if (GET_CODE (x) != SUBREG)
abort ();
- return SUBREG_WORD (x) * UNITS_PER_WORD < GET_MODE_UNIT_SIZE (GET_MODE (SUBREG_REG (x)));
+ return ((unsigned int) SUBREG_WORD (x) * UNITS_PER_WORD
+ < GET_MODE_UNIT_SIZE (GET_MODE (SUBREG_REG (x))));
}
\f
/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a value,
rtx
operand_subword (op, i, validate_address, mode)
rtx op;
- int i;
+ unsigned int i;
int validate_address;
enum machine_mode mode;
{
return gen_rtx_SUBREG (word_mode, SUBREG_REG (op), i + SUBREG_WORD (op));
else if (GET_CODE (op) == CONCAT)
{
- int partwords = GET_MODE_UNIT_SIZE (GET_MODE (op)) / UNITS_PER_WORD;
+ unsigned int partwords
+ = GET_MODE_UNIT_SIZE (GET_MODE (op)) / UNITS_PER_WORD;
+
if (i < partwords)
return operand_subword (XEXP (op, 0), i, validate_address, mode);
return operand_subword (XEXP (op, 1), i - partwords,
are defined as returning one or two 32 bit values, respectively,
and not values of BITS_PER_WORD bits. */
#ifdef REAL_ARITHMETIC
-/* The output is some bits, the width of the target machine's word.
- A wider-word host can surely hold them in a CONST_INT. A narrower-word
- host can't. */
+ /* The output is some bits, the width of the target machine's word.
+ A wider-word host can surely hold them in a CONST_INT. A narrower-word
+ host can't. */
if (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD
&& GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_MODE_BITSIZE (mode) == 64
&& GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_MODE_BITSIZE (mode) > 64
&& GET_CODE (op) == CONST_DOUBLE)
- {
- long k[4];
- REAL_VALUE_TYPE rv;
+ {
+ long k[4];
+ REAL_VALUE_TYPE rv;
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, k);
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, k);
- if (BITS_PER_WORD == 32)
- {
- val = k[i];
- val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
- return GEN_INT (val);
- }
- else
- abort ();
- }
+ if (BITS_PER_WORD == 32)
+ {
+ val = k[i];
+ val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ return GEN_INT (val);
+ }
+#if HOST_BITS_PER_WIDE_INT >= 64
+ else if (BITS_PER_WORD >= 64 && i <= 1)
+ {
+ val = k[i*2 + ! WORDS_BIG_ENDIAN];
+ val = (((val & 0xffffffff) ^ 0x80000000) - 0x80000000) << 32;
+ val |= (HOST_WIDE_INT) k[i*2 + WORDS_BIG_ENDIAN] & 0xffffffff;
+ return GEN_INT (val);
+ }
+#endif
+ else
+ abort ();
+ }
#else /* no REAL_ARITHMETIC */
if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
&& HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
rtx
operand_subword_force (op, i, mode)
rtx op;
- int i;
+ unsigned int i;
enum machine_mode mode;
{
rtx result = operand_subword (op, i, 1, mode);
f->emit = NULL;
}
\f
-/* Go through all the RTL insn bodies and copy any invalid shared structure.
- It does not work to do this twice, because the mark bits set here
- are not cleared afterwards. */
+/* Go through all the RTL insn bodies and copy any invalid shared
+ structure. This routine should only be called once. */
void
-unshare_all_rtl (insn)
- register rtx insn;
+unshare_all_rtl (fndecl, insn)
+ tree fndecl;
+ rtx insn;
{
- for (; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CALL_INSN)
- {
- PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn));
- REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn));
- LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn));
- }
+ tree decl;
+
+ /* Make sure that virtual parameters are not shared. */
+ for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
+ copy_rtx_if_shared (DECL_RTL (decl));
+ /* Unshare just about everything else. */
+ unshare_all_rtl_1 (insn);
+
/* Make sure the addresses of stack slots found outside the insn chain
(such as, in DECL_RTL of a variable) are not shared
with the insn chain.
This special care is necessary when the stack slot MEM does not
actually appear in the insn chain. If it does appear, its address
is unshared from all else at that point. */
-
copy_rtx_if_shared (stack_slot_list);
}
+/* Go through all the RTL insn bodies and copy any invalid shared
+ structure, again. This is a fairly expensive thing to do so it
+ should be done sparingly. */
+
+void
+unshare_all_rtl_again (insn)
+ rtx insn;
+{
+ rtx p;
+ for (p = insn; p; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ {
+ reset_used_flags (PATTERN (p));
+ reset_used_flags (REG_NOTES (p));
+ reset_used_flags (LOG_LINKS (p));
+ }
+ unshare_all_rtl_1 (insn);
+}
+
+/* Go through all the RTL insn bodies and copy any invalid shared structure.
+ Assumes the mark bits are cleared at entry. */
+
+static void
+unshare_all_rtl_1 (insn)
+ rtx insn;
+{
+ for (; insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn));
+ REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn));
+ LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn));
+ }
+}
+
/* Mark ORIG as in use, and return a copy of it if it was already in use.
Recursively does the same for subexpressions. */
does not look inside SEQUENCEs. Until reload has completed, this is the
same as next_real_insn. */
+int
+active_insn_p (insn)
+ rtx insn;
+{
+ return (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
+ || (GET_CODE (insn) == INSN
+ && (! reload_completed
+ || (GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER))));
+}
+
rtx
next_active_insn (insn)
rtx insn;
while (insn)
{
insn = NEXT_INSN (insn);
- if (insn == 0
- || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
- || (GET_CODE (insn) == INSN
- && (! reload_completed
- || (GET_CODE (PATTERN (insn)) != USE
- && GET_CODE (PATTERN (insn)) != CLOBBER))))
+ if (insn == 0 || active_insn_p (insn))
break;
}
while (insn)
{
insn = PREV_INSN (insn);
- if (insn == 0
- || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
- || (GET_CODE (insn) == INSN
- && (! reload_completed
- || (GET_CODE (PATTERN (insn)) != USE
- && GET_CODE (PATTERN (insn)) != CLOBBER))))
+ if (insn == 0 || active_insn_p (insn))
break;
}
LOG_LINKS (insn) = NULL;
REG_NOTES (insn) = NULL;
+#ifdef ENABLE_RTL_CHECKING
+ if (insn
+ && GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (returnjump_p (insn)
+ || (GET_CODE (insn) == SET
+ && SET_DEST (insn) == pc_rtx)))
+ {
+ warning ("ICE: emit_insn used where emit_jump_insn needed:\n");
+ debug_rtx (insn);
+ }
+#endif
+
return insn;
}
rtx insn;
rtx next;
- /* Remove NOTE_INSN_DELETED notes. We must not remove the first
- instruction in the function because the compiler depends on the
- first instruction being a note. */
+ /* We must not remove the first instruction in the function because
+ the compiler depends on the first instruction being a note. */
for (insn = NEXT_INSN (get_insns ()); insn; insn = next)
{
/* Remember what's next. */
if (GET_CODE (insn) != NOTE)
continue;
+ /* By now, all notes indicating lexical blocks should have
+ NOTE_BLOCK filled in. */
+ if ((NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ && NOTE_BLOCK (insn) == NULL_TREE)
+ abort ();
+
+ /* Remove NOTE_INSN_DELETED notes. */
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
remove_insn (insn);
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ {
+ /* Scan back to see if there are any non-note instructions
+ between INSN and the beginning of this block. If not,
+ then there is no PC range in the generated code that will
+ actually be in this block, so there's no point in
+ remembering the existence of the block. */
+ rtx prev;
+
+ for (prev = PREV_INSN (insn); prev; prev = PREV_INSN (prev))
+ {
+ /* This block contains a real instruction. Note that we
+ don't include labels; if the only thing in the block
+ is a label, then there are still no PC values that
+ lie within the block. */
+ if (GET_RTX_CLASS (GET_CODE (prev)) == 'i')
+ break;
+
+ /* We're only interested in NOTEs. */
+ if (GET_CODE (prev) != NOTE)
+ continue;
+
+ if (NOTE_LINE_NUMBER (prev) == NOTE_INSN_BLOCK_BEG)
+ {
+ /* If the BLOCKs referred to by these notes don't
+ match, then something is wrong with our BLOCK
+ nesting structure. */
+ if (NOTE_BLOCK (prev) != NOTE_BLOCK (insn))
+ abort ();
+
+ /* Never delete the BLOCK for the outermost scope
+ of the function; we can refer to names from
+ that scope even if the block notes are messed up. */
+ if (! is_body_block (NOTE_BLOCK (insn)))
+ {
+ debug_ignore_block (NOTE_BLOCK (insn));
+
+ remove_insn (prev);
+ remove_insn (insn);
+ }
+ break;
+ }
+ else if (NOTE_LINE_NUMBER (prev) == NOTE_INSN_BLOCK_END)
+ /* There's a nested block. We need to leave the
+ current block in place since otherwise the debugger
+ wouldn't be able to show symbols from our block in
+ the nested block. */
+ break;
+ }
+ }
}
}
rtx
emit_line_note_after (file, line, after)
- char *file;
+ const char *file;
int line;
rtx after;
{
add_insn (insn);
}
-#ifdef ENABLE_RTL_CHECKING
- if (insn
- && (returnjump_p (insn)
- || (GET_CODE (insn) == SET
- && SET_DEST (insn) == pc_rtx)))
- {
- warning ("ICE: emit_insn used where emit_jump_insn needed:\n");
- debug_rtx (insn);
- }
-#endif
-
return insn;
}
rtx
emit_line_note (file, line)
- char *file;
+ const char *file;
int line;
{
set_file_and_line_for_stmt (file, line);
rtx
emit_note (file, line)
- char *file;
+ const char *file;
int line;
{
register rtx note;
rtx
emit_line_note_force (file, line)
- char *file;
+ const char *file;
int line;
{
last_linenum = -1;
last_insn = last;
}
+/* Set up the insn chain from a chain stort in FIRST to LAST. */
+
+void
+push_to_full_sequence (first, last)
+ rtx first, last;
+{
+ start_sequence ();
+ first_insn = first;
+ last_insn = last;
+ /* We really should have the end of the insn chain here. */
+ if (last && NEXT_INSN (last))
+ abort ();
+}
+
/* Set up the outer-level insn chain
as the current sequence, saving the previously current one. */
free (tem);
}
+/* This works like end_sequence, but records the old sequence in FIRST
+ and LAST. */
+
+void
+end_full_sequence (first, last)
+ rtx *first, *last;
+{
+ *first = first_insn;
+ *last = last_insn;
+ end_sequence();
+}
+
/* Return 1 if currently emitting into a sequence. */
int
REGNO_POINTER_FLAG (VIRTUAL_CFA_REGNUM) = 1;
#ifdef STACK_BOUNDARY
- REGNO_POINTER_ALIGN (STACK_POINTER_REGNUM) = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (FRAME_POINTER_REGNUM) = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM)
- = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (ARG_POINTER_REGNUM) = STACK_BOUNDARY / BITS_PER_UNIT;
-
- REGNO_POINTER_ALIGN (VIRTUAL_INCOMING_ARGS_REGNUM)
- = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (VIRTUAL_STACK_VARS_REGNUM)
- = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (VIRTUAL_STACK_DYNAMIC_REGNUM)
- = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (VIRTUAL_OUTGOING_ARGS_REGNUM)
- = STACK_BOUNDARY / BITS_PER_UNIT;
- REGNO_POINTER_ALIGN (VIRTUAL_CFA_REGNUM) = UNITS_PER_WORD;
+ REGNO_POINTER_ALIGN (STACK_POINTER_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (FRAME_POINTER_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (ARG_POINTER_REGNUM) = STACK_BOUNDARY;
+
+ REGNO_POINTER_ALIGN (VIRTUAL_INCOMING_ARGS_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (VIRTUAL_STACK_VARS_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (VIRTUAL_STACK_DYNAMIC_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (VIRTUAL_OUTGOING_ARGS_REGNUM) = STACK_BOUNDARY;
+ REGNO_POINTER_ALIGN (VIRTUAL_CFA_REGNUM) = BITS_PER_WORD;
#endif
#ifdef INIT_EXPANDERS
ggc_add_rtx_root (&static_chain_rtx, 1);
ggc_add_rtx_root (&static_chain_incoming_rtx, 1);
ggc_add_rtx_root (&return_address_pointer_rtx, 1);
+
+ /* Initialize the CONST_INT hash table. */
+ const_int_htab = htab_create (37, const_int_htab_hash,
+ const_int_htab_eq, NULL);
+ ggc_add_root (&const_int_htab, 1, sizeof (const_int_htab),
+ rtx_htab_mark);
}
\f
/* Query and clear/ restore no_line_numbers. This is used by the