and global constant/copy propagation for GNU compiler.
Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
/* TODO
- reordering of memory allocation and freeing to be more space efficient
\f
/* Hash table support. */
-/* For each register, the cuid of the first/last insn in the block to set it,
- or -1 if not set. */
+/* For each register, the cuid of the first/last insn in the block
+ that set it, or -1 if not set. */
#define NEVER_SET -1
-static int *reg_first_set;
-static int *reg_last_set;
+
+struct reg_avail_info
+{
+ int last_bb;
+ int first_set;
+ int last_set;
+};
+
+static struct reg_avail_info *reg_avail_info;
+static int current_bb;
/* See whether X, the source of a set, is something we want to consider for
switch (code)
{
case REG:
- if (avail_p)
- return (reg_last_set[REGNO (x)] == NEVER_SET
- || reg_last_set[REGNO (x)] < INSN_CUID (insn));
- else
- return (reg_first_set[REGNO (x)] == NEVER_SET
- || reg_first_set[REGNO (x)] >= INSN_CUID (insn));
+ {
+ struct reg_avail_info *info = ®_avail_info[REGNO (x)];
+
+ if (info->last_bb != current_bb)
+ return 1;
+ if (avail_p)
+ return info->last_set < INSN_CUID (insn);
+ else
+ return info->first_set >= INSN_CUID (insn);
+ }
case MEM:
- if (load_killed_in_block_p (BLOCK_FOR_INSN (insn), INSN_CUID (insn),
+ if (load_killed_in_block_p (BASIC_BLOCK (current_bb), INSN_CUID (insn),
x, avail_p))
return 0;
else
this insn. */
int antic_p = oprs_anticipatable_p (src, insn) && single_set (insn);
/* An expression is not available if its operands are
- subsequently modified, including this insn. */
- int avail_p = oprs_available_p (src, insn);
+ subsequently modified, including this insn. It's also not
+ available if this is a branch, because we can't insert
+ a set after the branch. */
+ int avail_p = (oprs_available_p (src, insn)
+ && ! JUMP_P (insn));
insert_expr_in_table (src, GET_MODE (dest), insn, antic_p, avail_p);
}
/* Record register first/last/block set information for REGNO in INSN.
- reg_first_set records the first place in the block where the register
+ first_set records the first place in the block where the register
is set and is used to compute "anticipatability".
- reg_last_set records the last place in the block where the register
+ last_set records the last place in the block where the register
is set and is used to compute "availability".
+ last_bb records the block for which first_set and last_set are
+ valid, as a quick test to invalidate them.
+
reg_set_in_block records whether the register is set in the block
and is used to compute "transparency". */
rtx insn;
int regno;
{
- if (reg_first_set[regno] == NEVER_SET)
- reg_first_set[regno] = INSN_CUID (insn);
+ struct reg_avail_info *info = ®_avail_info[regno];
+ int cuid = INSN_CUID (insn);
- reg_last_set[regno] = INSN_CUID (insn);
- SET_BIT (reg_set_in_block[BLOCK_NUM (insn)], regno);
+ info->last_set = cuid;
+ if (info->last_bb != current_bb)
+ {
+ info->last_bb = current_bb;
+ info->first_set = cuid;
+ SET_BIT (reg_set_in_block[current_bb], regno);
+ }
}
rtx insn;
{
/* load_killed_in_block_p will handle the case of calls clobbering
- everything. */
+ everything. */
modify_mem_list[BLOCK_NUM (insn)] =
alloc_INSN_LIST (insn, modify_mem_list[BLOCK_NUM (insn)]);
{
/* Note that traversals of this loop (other than for free-ing)
will break after encountering a CALL_INSN. So, there's no
- need to insert a pair of items, as canon_list_insert does. */
+ need to insert a pair of items, as canon_list_insert does. */
canon_modify_mem_list[BLOCK_NUM (insn)] =
alloc_INSN_LIST (insn, canon_modify_mem_list[BLOCK_NUM (insn)]);
}
compute_hash_table (set_p)
int set_p;
{
- int bb;
+ unsigned int i;
/* While we compute the hash table we also compute a bit array of which
registers are set in which blocks.
}
}
/* Some working arrays used to track first and last set in each block. */
- /* ??? One could use alloca here, but at some size a threshold is crossed
- beyond which one should use malloc. Are we at that threshold here? */
- reg_first_set = (int *) gmalloc (max_gcse_regno * sizeof (int));
- reg_last_set = (int *) gmalloc (max_gcse_regno * sizeof (int));
+ reg_avail_info = (struct reg_avail_info*)
+ gmalloc (max_gcse_regno * sizeof (struct reg_avail_info));
- for (bb = 0; bb < n_basic_blocks; bb++)
+ for (i = 0; i < max_gcse_regno; ++i)
+ reg_avail_info[i].last_bb = NEVER_SET;
+
+ for (current_bb = 0; current_bb < n_basic_blocks; current_bb++)
{
rtx insn;
unsigned int regno;
int in_libcall_block;
- unsigned int i;
/* First pass over the instructions records information used to
determine when registers and memory are first and last set.
??? hard-reg reg_set_in_block computation
could be moved to compute_sets since they currently don't change. */
- for (i = 0; i < max_gcse_regno; i++)
- reg_first_set[i] = reg_last_set[i] = NEVER_SET;
-
-
- for (insn = BLOCK_HEAD (bb);
- insn && insn != NEXT_INSN (BLOCK_END (bb));
+ for (insn = BLOCK_HEAD (current_bb);
+ insn && insn != NEXT_INSN (BLOCK_END (current_bb));
insn = NEXT_INSN (insn))
{
-#ifdef NON_SAVING_SETJMP
- if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- {
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- record_last_reg_set_info (insn, regno);
- continue;
- }
-#endif
-
if (! INSN_P (insn))
continue;
if (GET_CODE (insn) == CALL_INSN)
{
+ bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP
+ && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+ clobbers_all = true;
+#endif
+
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+ if (clobbers_all
+ || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
record_last_reg_set_info (insn, regno);
- if (! CONST_CALL_P (insn))
- record_last_mem_set_info (insn);
+ mark_call (insn);
}
note_stores (PATTERN (insn), record_last_set_info, insn);
/* The next pass builds the hash table. */
- for (insn = BLOCK_HEAD (bb), in_libcall_block = 0;
- insn && insn != NEXT_INSN (BLOCK_END (bb));
+ for (insn = BLOCK_HEAD (current_bb), in_libcall_block = 0;
+ insn && insn != NEXT_INSN (BLOCK_END (current_bb));
insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
- in_libcall_block = 1;
- else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
- in_libcall_block = 0;
- hash_scan_insn (insn, set_p, in_libcall_block);
+ in_libcall_block = 1;
+ else if (set_p && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ in_libcall_block = 0;
+ hash_scan_insn (insn, set_p, in_libcall_block);
+ if (!set_p && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ in_libcall_block = 0;
}
}
- free (reg_first_set);
- free (reg_last_set);
-
- /* Catch bugs early. */
- reg_first_set = reg_last_set = 0;
+ free (reg_avail_info);
+ reg_avail_info = NULL;
}
/* Allocate space for the set hash table.
mark_call (insn)
rtx insn;
{
- if (! CONST_CALL_P (insn))
+ if (! CONST_OR_PURE_CALL_P (insn))
record_last_mem_set_info (insn);
}
}
/* This wrapper for expr_reaches_here_p_work() is to ensure that any
- memory allocated for that function is returned. */
+ memory allocated for that function is returned. */
static int
expr_reaches_here_p (occr, expr, bb, check_self_loop)
note = find_reg_equal_equiv_note (insn);
- /* We may win even when propagating constants into notes. */
+ /* We may win even when propagating constants into notes. */
if (note)
find_used_regs (&XEXP (note, 0), NULL);
struct expr *set;
/* Ignore registers created by GCSE.
- We do this because ... */
+ We do this because ... */
if (regno >= max_gcse_regno)
continue;
}
/* The wrapper for pre_expr_reaches_here_work that ensures that any
- memory allocated for that function is returned. */
+ memory allocated for that function is returned. */
static int
pre_expr_reaches_here_p (occr_bb, expr, bb)
We no longer ignore such label references (see LABEL_REF handling in
mark_jump_label for additional information). */
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL, XEXP (x, 0),
+ REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, XEXP (x, 0),
REG_NOTES (insn));
if (LABEL_P (XEXP (x, 0)))
LABEL_NUSES (XEXP (x, 0))++;
insn && insn != PREV_INSN (BLOCK_HEAD (bb));
insn = PREV_INSN (insn))
{
-#ifdef NON_SAVING_SETJMP
- if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- {
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- SET_BIT (reg_set_in_block[bb], regno);
- continue;
- }
-#endif
- /* Ignore anything that is not a normal insn. */
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ /* Ignore anything that is not a normal insn. */
+ if (! INSN_P (insn))
continue;
if (GET_CODE (insn) == CALL_INSN)
{
+ bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP
+ && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+ clobbers_all = true;
+#endif
+
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+ if (clobbers_all
+ || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
SET_BIT (reg_set_in_block[bb], regno);
}
int i,j;
int ret = 0;
+ if (!x)
+ return 0;
+
if (GET_CODE (x) == SET)
x = SET_SRC (x);
if (GET_CODE (insn) == CALL_INSN)
{
- if (CONST_CALL_P (insn))
+ if (CONST_OR_PURE_CALL_P (insn))
return 0;
else
return 1;
{
if (store_killed_after (ptr->pattern, BLOCK_HEAD (b), BASIC_BLOCK (b)))
{
- /* The anticipatable expression is not killed if it's gen'd. */
+ /* The anticipatable expression is not killed if it's gen'd. */
/*
We leave this check out for now. If we have a code sequence
in a block which looks like: