/* Emit RTL for the GNU C-Compiler expander.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "tree.h"
/* This is *not* reset after each function. It gives each CODE_LABEL
in the entire compilation a unique label number. */
-static int label_num = 1;
+static GTY(()) int label_num = 1;
/* Highest label number in current function.
Zero means use the value of label_num instead.
REAL_VALUE_TYPE dconst1;
REAL_VALUE_TYPE dconst2;
REAL_VALUE_TYPE dconstm1;
+REAL_VALUE_TYPE dconstm2;
+REAL_VALUE_TYPE dconsthalf;
/* All references to the following fixed hard registers go through
these unique rtl objects. On machines where the frame-pointer and
static GTY ((if_marked ("ggc_marked_p"), param_is (struct mem_attrs)))
htab_t mem_attrs_htab;
+/* A hash table storing register attribute structures. */
+static GTY ((if_marked ("ggc_marked_p"), param_is (struct reg_attrs)))
+ htab_t reg_attrs_htab;
+
/* A hash table storing all CONST_DOUBLEs. */
static GTY ((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
htab_t const_double_htab;
static mem_attrs *get_mem_attrs PARAMS ((HOST_WIDE_INT, tree, rtx,
rtx, unsigned int,
enum machine_mode));
+static hashval_t reg_attrs_htab_hash PARAMS ((const void *));
+static int reg_attrs_htab_eq PARAMS ((const void *,
+ const void *));
+static reg_attrs *get_reg_attrs PARAMS ((tree, int));
static tree component_ref_for_mem_expr PARAMS ((tree));
static rtx gen_const_vector_0 PARAMS ((enum machine_mode));
if (GET_MODE (value) == VOIDmode)
h = CONST_DOUBLE_LOW (value) ^ CONST_DOUBLE_HIGH (value);
else
- h = real_hash (CONST_DOUBLE_REAL_VALUE (value));
+ {
+ h = real_hash (CONST_DOUBLE_REAL_VALUE (value));
+ /* MODE is used in the comparison, so it should be in the hash. */
+ h ^= GET_MODE (value);
+ }
return h;
}
return *slot;
}
+/* Returns a hash code for X (which is a really a reg_attrs *). */
+
+static hashval_t
+reg_attrs_htab_hash (x)
+ const void *x;
+{
+ reg_attrs *p = (reg_attrs *) x;
+
+ return ((p->offset * 1000) ^ (long) p->decl);
+}
+
+/* Returns non-zero if the value represented by X (which is really a
+ reg_attrs *) is the same as that given by Y (which is also really a
+ reg_attrs *). */
+
+static int
+reg_attrs_htab_eq (x, y)
+ const void *x;
+ const void *y;
+{
+ reg_attrs *p = (reg_attrs *) x;
+ reg_attrs *q = (reg_attrs *) y;
+
+ return (p->decl == q->decl && p->offset == q->offset);
+}
+/* Allocate a new reg_attrs structure and insert it into the hash table if
+ one identical to it is not already in the table. We are doing this for
+ MEM of mode MODE. */
+
+static reg_attrs *
+get_reg_attrs (decl, offset)
+ tree decl;
+ int offset;
+{
+ reg_attrs attrs;
+ void **slot;
+
+ /* If everything is the default, we can just return zero. */
+ if (decl == 0 && offset == 0)
+ return 0;
+
+ attrs.decl = decl;
+ attrs.offset = offset;
+
+ slot = htab_find_slot (reg_attrs_htab, &attrs, INSERT);
+ if (*slot == 0)
+ {
+ *slot = ggc_alloc (sizeof (reg_attrs));
+ memcpy (*slot, &attrs, sizeof (reg_attrs));
+ }
+
+ return *slot;
+}
+
/* Generate a new REG rtx. Make sure ORIGINAL_REGNO is set properly, and
don't attempt to share with the various global pieces of rtl (such as
frame_pointer_rtx). */
if (regno == RETURN_ADDRESS_POINTER_REGNUM)
return return_address_pointer_rtx;
#endif
- if (regno == PIC_OFFSET_TABLE_REGNUM
+ if (regno == (unsigned) PIC_OFFSET_TABLE_REGNUM
&& fixed_regs[PIC_OFFSET_TABLE_REGNUM])
return pic_offset_table_rtx;
if (regno == STACK_POINTER_REGNUM)
return gen_rtx_CONCAT (mode, realpart, imagpart);
}
- /* Make sure regno_pointer_align, regno_decl, and regno_reg_rtx are large
+ /* Make sure regno_pointer_align, and regno_reg_rtx are large
enough to have an element for this pseudo reg number. */
if (reg_rtx_no == f->emit->regno_pointer_align_length)
int old_size = f->emit->regno_pointer_align_length;
char *new;
rtx *new1;
- tree *new2;
new = ggc_realloc (f->emit->regno_pointer_align, old_size * 2);
memset (new + old_size, 0, old_size);
memset (new1 + old_size, 0, old_size * sizeof (rtx));
regno_reg_rtx = new1;
- new2 = (tree *) ggc_realloc (f->emit->regno_decl,
- old_size * 2 * sizeof (tree));
- memset (new2 + old_size, 0, old_size * sizeof (tree));
- f->emit->regno_decl = new2;
-
f->emit->regno_pointer_align_length = old_size * 2;
}
return val;
}
+/* Generate an register with same attributes as REG,
+ but offsetted by OFFSET. */
+
+rtx
+gen_rtx_REG_offset (reg, mode, regno, offset)
+ enum machine_mode mode;
+ unsigned int regno;
+ int offset;
+ rtx reg;
+{
+ rtx new = gen_rtx_REG (mode, regno);
+ REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
+ REG_OFFSET (reg) + offset);
+ return new;
+}
+
+/* Set the decl for MEM to DECL. */
+
+void
+set_reg_attrs_from_mem (reg, mem)
+ rtx reg;
+ rtx mem;
+{
+ if (MEM_OFFSET (mem) && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
+ REG_ATTRS (reg)
+ = get_reg_attrs (MEM_EXPR (mem), INTVAL (MEM_OFFSET (mem)));
+}
+
+/* Set the register attributes for registers contained in PARM_RTX.
+ Use needed values from memory attributes of MEM. */
+
+void
+set_reg_attrs_for_parm (parm_rtx, mem)
+ rtx parm_rtx;
+ rtx mem;
+{
+ if (GET_CODE (parm_rtx) == REG)
+ set_reg_attrs_from_mem (parm_rtx, mem);
+ else if (GET_CODE (parm_rtx) == PARALLEL)
+ {
+ /* Check for a NULL entry in the first slot, used to indicate that the
+ parameter goes both on the stack and in registers. */
+ int i = XEXP (XVECEXP (parm_rtx, 0, 0), 0) ? 0 : 1;
+ for (; i < XVECLEN (parm_rtx, 0); i++)
+ {
+ rtx x = XVECEXP (parm_rtx, 0, i);
+ if (GET_CODE (XEXP (x, 0)) == REG)
+ REG_ATTRS (XEXP (x, 0))
+ = get_reg_attrs (MEM_EXPR (mem),
+ INTVAL (XEXP (x, 1)));
+ }
+ }
+}
+
+/* Assign the RTX X to declaration T. */
+void
+set_decl_rtl (t, x)
+ tree t;
+ rtx x;
+{
+ DECL_CHECK (t)->decl.rtl = x;
+
+ if (!x)
+ return;
+ /* For register, we maitain the reverse information too. */
+ if (GET_CODE (x) == REG)
+ 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)
+ {
+ 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)));
+ }
+ }
+}
+
/* Identify REG (which may be a CONCAT) as a user register. */
void
{
/* The only additional case we can do is MEM. */
int offset = 0;
+
+ /* The following exposes the use of "x" to CSE. */
+ if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD
+ && SCALAR_INT_MODE_P (GET_MODE (x))
+ && ! no_new_pseudos)
+ return gen_lowpart (mode, force_reg (GET_MODE (x), x));
+
if (WORDS_BIG_ENDIAN)
offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
return result;
}
-/* Like gen_highpart_mode, but accept mode of EXP operand in case EXP can
+/* Like gen_highpart, but accept mode of EXP operand in case EXP can
be VOIDmode constant. */
rtx
gen_highpart_mode (outermode, innermode, exp)
HOST_WIDE_INT ioff = tree_low_cst (off_tree, 1);
HOST_WIDE_INT aoff = (ioff & -ioff) * BITS_PER_UNIT;
align = DECL_ALIGN (t);
- if (aoff && aoff < align)
+ if (aoff && (unsigned HOST_WIDE_INT) aoff < align)
align = aoff;
offset = GEN_INT (ioff);
apply_bitpos = bitpos;
set_mem_attributes_minus_bitpos (ref, t, objectp, 0);
}
+/* Set the decl for MEM to DECL. */
+
+void
+set_mem_attrs_from_reg (mem, reg)
+ rtx mem;
+ rtx reg;
+{
+ MEM_ATTRS (mem)
+ = get_mem_attrs (MEM_ALIAS_SET (mem), REG_EXPR (reg),
+ GEN_INT (REG_OFFSET (reg)),
+ MEM_SIZE (mem), MEM_ALIGN (mem), GET_MODE (mem));
+}
+
/* Set the alias set of MEM to SET. */
void
rtx tem;
rtx note, seq;
int probability;
+ rtx insn_last, insn;
+ int njumps = 0;
if (any_condjump_p (trial)
&& (note = find_reg_note (trial, REG_BR_PROB, 0)))
after = NEXT_INSN (after);
}
- if (seq)
+ if (!seq)
+ return trial;
+
+ /* Avoid infinite loop if any insn of the result matches
+ the original pattern. */
+ insn_last = seq;
+ while (1)
{
- /* Sometimes there will be only one insn in that list, this case will
- normally arise only when we want it in turn to be split (SFmode on
- the 29k is an example). */
- if (NEXT_INSN (seq) != NULL_RTX)
- {
- rtx insn_last, insn;
- int njumps = 0;
+ if (INSN_P (insn_last)
+ && rtx_equal_p (PATTERN (insn_last), pat))
+ return trial;
+ if (!NEXT_INSN (insn_last))
+ break;
+ insn_last = NEXT_INSN (insn_last);
+ }
- /* Avoid infinite loop if any insn of the result matches
- the original pattern. */
- insn_last = seq;
- while (1)
+ /* Mark labels. */
+ for (insn = insn_last; insn ; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ mark_jump_label (PATTERN (insn), insn, 0);
+ njumps++;
+ if (probability != -1
+ && any_condjump_p (insn)
+ && !find_reg_note (insn, REG_BR_PROB, 0))
{
- if (INSN_P (insn_last)
- && rtx_equal_p (PATTERN (insn_last), pat))
- return trial;
- if (NEXT_INSN (insn_last) == NULL_RTX)
- break;
- insn_last = NEXT_INSN (insn_last);
+ /* We can preserve the REG_BR_PROB notes only if exactly
+ one jump is created, otherwise the machine description
+ is responsible for this step using
+ split_branch_probability variable. */
+ if (njumps != 1)
+ abort ();
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_BR_PROB,
+ GEN_INT (probability),
+ REG_NOTES (insn));
}
+ }
+ }
- /* Mark labels. */
+ /* If we are splitting a CALL_INSN, look for the CALL_INSN
+ in SEQ and copy our CALL_INSN_FUNCTION_USAGE to it. */
+ if (GET_CODE (trial) == CALL_INSN)
+ {
+ for (insn = insn_last; insn ; insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ CALL_INSN_FUNCTION_USAGE (insn)
+ = CALL_INSN_FUNCTION_USAGE (trial);
+ SIBLING_CALL_P (insn) = SIBLING_CALL_P (trial);
+ }
+ }
+
+ /* Copy notes, particularly those related to the CFG. */
+ for (note = REG_NOTES (trial); note; note = XEXP (note, 1))
+ {
+ switch (REG_NOTE_KIND (note))
+ {
+ case REG_EH_REGION:
insn = insn_last;
while (insn != NULL_RTX)
{
- if (GET_CODE (insn) == JUMP_INSN)
- {
- mark_jump_label (PATTERN (insn), insn, 0);
- njumps++;
- if (probability != -1
- && any_condjump_p (insn)
- && !find_reg_note (insn, REG_BR_PROB, 0))
- {
- /* We can preserve the REG_BR_PROB notes only if exactly
- one jump is created, otherwise the machine description
- is responsible for this step using
- split_branch_probability variable. */
- if (njumps != 1)
- abort ();
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_BR_PROB,
- GEN_INT (probability),
- REG_NOTES (insn));
- }
- }
-
+ if (GET_CODE (insn) == CALL_INSN
+ || (flag_non_call_exceptions
+ && may_trap_p (PATTERN (insn))))
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_EH_REGION,
+ XEXP (note, 0),
+ REG_NOTES (insn));
insn = PREV_INSN (insn);
}
+ break;
- /* If we are splitting a CALL_INSN, look for the CALL_INSN
- in SEQ and copy our CALL_INSN_FUNCTION_USAGE to it. */
- if (GET_CODE (trial) == CALL_INSN)
+ case REG_NORETURN:
+ case REG_SETJMP:
+ case REG_ALWAYS_RETURN:
+ insn = insn_last;
+ while (insn != NULL_RTX)
{
- insn = insn_last;
- while (insn != NULL_RTX)
- {
- if (GET_CODE (insn) == CALL_INSN)
- CALL_INSN_FUNCTION_USAGE (insn)
- = CALL_INSN_FUNCTION_USAGE (trial);
-
- insn = PREV_INSN (insn);
- }
+ if (GET_CODE (insn) == CALL_INSN)
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
+ XEXP (note, 0),
+ REG_NOTES (insn));
+ insn = PREV_INSN (insn);
}
+ break;
- /* Copy notes, particularly those related to the CFG. */
- for (note = REG_NOTES (trial); note; note = XEXP (note, 1))
+ case REG_NON_LOCAL_GOTO:
+ insn = insn_last;
+ while (insn != NULL_RTX)
{
- switch (REG_NOTE_KIND (note))
- {
- case REG_EH_REGION:
- insn = insn_last;
- while (insn != NULL_RTX)
- {
- if (GET_CODE (insn) == CALL_INSN
- || (flag_non_call_exceptions
- && may_trap_p (PATTERN (insn))))
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_EH_REGION,
- XEXP (note, 0),
- REG_NOTES (insn));
- insn = PREV_INSN (insn);
- }
- break;
-
- case REG_NORETURN:
- case REG_SETJMP:
- case REG_ALWAYS_RETURN:
- insn = insn_last;
- while (insn != NULL_RTX)
- {
- if (GET_CODE (insn) == CALL_INSN)
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
- XEXP (note, 0),
- REG_NOTES (insn));
- insn = PREV_INSN (insn);
- }
- break;
-
- case REG_NON_LOCAL_GOTO:
- insn = insn_last;
- while (insn != NULL_RTX)
- {
- if (GET_CODE (insn) == JUMP_INSN)
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
- XEXP (note, 0),
- REG_NOTES (insn));
- insn = PREV_INSN (insn);
- }
- break;
-
- default:
- break;
- }
+ if (GET_CODE (insn) == JUMP_INSN)
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
+ XEXP (note, 0),
+ REG_NOTES (insn));
+ insn = PREV_INSN (insn);
}
+ break;
- /* If there are LABELS inside the split insns increment the
- usage count so we don't delete the label. */
- if (GET_CODE (trial) == INSN)
- {
- insn = insn_last;
- while (insn != NULL_RTX)
- {
- if (GET_CODE (insn) == INSN)
- mark_label_nuses (PATTERN (insn));
+ default:
+ break;
+ }
+ }
- insn = PREV_INSN (insn);
- }
- }
+ /* If there are LABELS inside the split insns increment the
+ usage count so we don't delete the label. */
+ if (GET_CODE (trial) == INSN)
+ {
+ insn = insn_last;
+ while (insn != NULL_RTX)
+ {
+ if (GET_CODE (insn) == INSN)
+ mark_label_nuses (PATTERN (insn));
- tem = emit_insn_after_scope (seq, trial, INSN_SCOPE (trial));
+ insn = PREV_INSN (insn);
+ }
+ }
- delete_insn (trial);
- if (has_barrier)
- emit_barrier_after (tem);
+ tem = emit_insn_after_scope (seq, trial, INSN_SCOPE (trial));
- /* Recursively call try_split for each new insn created; by the
- time control returns here that insn will be fully split, so
- set LAST and continue from the insn after the one returned.
- We can't use next_active_insn here since AFTER may be a note.
- Ignore deleted insns, which can be occur if not optimizing. */
- for (tem = NEXT_INSN (before); tem != after; tem = NEXT_INSN (tem))
- if (! INSN_DELETED_P (tem) && INSN_P (tem))
- tem = try_split (PATTERN (tem), tem, 1);
- }
- /* Avoid infinite loop if the result matches the original pattern. */
- else if (rtx_equal_p (PATTERN (seq), pat))
- return trial;
- else
- {
- PATTERN (trial) = PATTERN (seq);
- INSN_CODE (trial) = -1;
- try_split (PATTERN (trial), trial, last);
- }
+ delete_insn (trial);
+ if (has_barrier)
+ emit_barrier_after (tem);
- /* Return either the first or the last insn, depending on which was
- requested. */
- return last
- ? (after ? PREV_INSN (after) : last_insn)
- : NEXT_INSN (before);
- }
+ /* Recursively call try_split for each new insn created; by the
+ time control returns here that insn will be fully split, so
+ set LAST and continue from the insn after the one returned.
+ We can't use next_active_insn here since AFTER may be a note.
+ Ignore deleted insns, which can be occur if not optimizing. */
+ for (tem = NEXT_INSN (before); tem != after; tem = NEXT_INSN (tem))
+ if (! INSN_DELETED_P (tem) && INSN_P (tem))
+ tem = try_split (PATTERN (tem), tem, 1);
- return trial;
+ /* Return either the first or the last insn, depending on which was
+ requested. */
+ return last
+ ? (after ? PREV_INSN (after) : last_insn)
+ : NEXT_INSN (before);
}
\f
/* Make and return an INSN rtx, initializing all its slots.
* sizeof (unsigned char));
regno_reg_rtx
- = (rtx *) ggc_alloc_cleared (f->emit->regno_pointer_align_length
- * sizeof (rtx));
-
- f->emit->regno_decl
- = (tree *) ggc_alloc_cleared (f->emit->regno_pointer_align_length
- * sizeof (tree));
+ = (rtx *) ggc_alloc (f->emit->regno_pointer_align_length * sizeof (rtx));
/* Put copies of all the hard registers into regno_reg_rtx. */
memcpy (regno_reg_rtx,
/* Initialize the CONST_INT, CONST_DOUBLE, and memory attribute hash
tables. */
- const_int_htab = htab_create (37, const_int_htab_hash,
- const_int_htab_eq, NULL);
+ const_int_htab = htab_create_ggc (37, const_int_htab_hash,
+ const_int_htab_eq, NULL);
- const_double_htab = htab_create (37, const_double_htab_hash,
- const_double_htab_eq, NULL);
+ const_double_htab = htab_create_ggc (37, const_double_htab_hash,
+ const_double_htab_eq, NULL);
- mem_attrs_htab = htab_create (37, mem_attrs_htab_hash,
- mem_attrs_htab_eq, NULL);
+ mem_attrs_htab = htab_create_ggc (37, mem_attrs_htab_hash,
+ mem_attrs_htab_eq, NULL);
+ reg_attrs_htab = htab_create_ggc (37, reg_attrs_htab_hash,
+ reg_attrs_htab_eq, NULL);
no_line_numbers = ! line_numbers;
REAL_VALUE_FROM_INT (dconst1, 1, 0, double_mode);
REAL_VALUE_FROM_INT (dconst2, 2, 0, double_mode);
REAL_VALUE_FROM_INT (dconstm1, -1, -1, double_mode);
+ REAL_VALUE_FROM_INT (dconstm2, -2, -1, double_mode);
+
+ dconsthalf = dconst1;
+ dconsthalf.exp--;
for (i = 0; i <= 2; i++)
{
#endif
#endif
- if (PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
+ if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
pic_offset_table_rtx = gen_raw_REG (Pmode, PIC_OFFSET_TABLE_REGNUM);
}
\f
XEXP (note1, 0) = p;
XEXP (note2, 0) = new;
}
+ INSN_CODE (new) = INSN_CODE (insn);
return new;
}