/* Dummy data flow analysis for GNU compiler in nonoptimizing mode.
- Copyright (C) 1987, 91, 94, 95, 96, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1987, 91, 94-96, 1998, 1999 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "rtl.h"
#include "hard-reg-set.h"
+#include "basic-block.h"
#include "regs.h"
+#include "function.h"
+#include "insn-config.h"
+#include "reload.h"
#include "flags.h"
+#include "toplev.h"
\f
/* Vector mapping INSN_UIDs to suids.
The suids are like uids but increase monotonically always.
static int *reg_where_dead;
+/* Likewise, but point to the insn_chain structure of the insn at which
+ the reg dies. */
+static struct insn_chain **reg_where_dead_chain;
+
/* Element N is suid of insn where life span of pseudo reg N begins. */
+static int *reg_where_born_exact;
+
+/* Element N is 1 if the birth of pseudo reg N is due to a CLOBBER,
+ 0 otherwise. */
+static int *reg_where_born_clobber;
-static int *reg_where_born;
+/* Return the suid of the insn where the register is born, or the suid
+ of the insn before if the birth is due to a CLOBBER. */
+#define REG_WHERE_BORN(N) \
+ (reg_where_born_exact[(N)] - reg_where_born_clobber[(N)])
/* Numbers of pseudo-regs to be allocated, highest priority first. */
static int stupid_reg_compare PROTO((const GENERIC_PTR,const GENERIC_PTR));
static int stupid_find_reg PROTO((int, enum reg_class, enum machine_mode,
int, int, int));
-static void stupid_mark_refs PROTO((rtx, rtx));
+static void stupid_mark_refs PROTO((rtx, struct insn_chain *));
+static void find_clobbered_regs PROTO((rtx, rtx));
+\f
+/* For communication between stupid_life_analysis and find_clobbered_regs. */
+static struct insn_chain *current_chain;
+
+/* This function, called via note_stores, marks any hard registers that are
+ clobbered in an insn as being live in the live_after and live_before fields
+ of the appropriate insn_chain structure. */
+
+static void
+find_clobbered_regs (reg, setter)
+ rtx reg, setter;
+{
+ int regno, nregs;
+ if (setter == 0 || GET_CODE (setter) != CLOBBER)
+ return;
+
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ if (GET_CODE (reg) != REG)
+ return;
+ regno = REGNO (reg);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ if (GET_MODE (reg) == VOIDmode)
+ abort ();
+ else
+ nregs = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (nregs-- > 0)
+ {
+ SET_REGNO_REG_SET (current_chain->live_after, regno);
+ SET_REGNO_REG_SET (current_chain->live_before, regno++);
+ }
+}
\f
/* Stupid life analysis is for the case where only variables declared
`register' go in registers. For this case, we mark all
bzero (regs_ever_live, sizeof regs_ever_live);
- regs_live = (char *) alloca (nregs);
+ regs_live = (char *) xmalloc (nregs);
/* First find the last real insn, and count the number of insns,
and assign insns their suids. */
i = INSN_UID (insn);
max_uid = i + 1;
- uid_suid = (int *) alloca ((i + 1) * sizeof (int));
+ uid_suid = (int *) xmalloc ((i + 1) * sizeof (int));
/* Compute the mapping from uids to suids.
Suids are numbers assigned to insns, like uids,
/* Allocate tables to record info about regs. */
- reg_where_dead = (int *) alloca (nregs * sizeof (int));
- bzero ((char *) reg_where_dead, nregs * sizeof (int));
-
- reg_where_born = (int *) alloca (nregs * sizeof (int));
- bzero ((char *) reg_where_born, nregs * sizeof (int));
-
- reg_order = (int *) alloca (nregs * sizeof (int));
- bzero ((char *) reg_order, nregs * sizeof (int));
-
- regs_change_size = (char *) alloca (nregs * sizeof (char));
- bzero ((char *) regs_change_size, nregs * sizeof (char));
-
- regs_crosses_setjmp = (char *) alloca (nregs * sizeof (char));
- bzero ((char *) regs_crosses_setjmp, nregs * sizeof (char));
+ reg_where_dead = (int *) xcalloc (nregs, sizeof (int));
+ reg_where_born_exact = (int *) xcalloc (nregs, sizeof (int));
+ reg_where_born_clobber = (int *) xcalloc (nregs, sizeof (int));
+ reg_where_dead_chain = (struct insn_chain **)
+ xcalloc (nregs, sizeof (struct insn_chain *));
+ reg_order = (int *) xcalloc (nregs, sizeof (int));
+ regs_change_size = (char *) xcalloc (nregs, sizeof (char));
+ regs_crosses_setjmp = (char *) xcalloc (nregs, sizeof (char));
/* Allocate the reg_renumber array */
allocate_reg_info (max_regno, FALSE, TRUE);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
reg_renumber[i] = i;
- after_insn_hard_regs
- = (HARD_REG_SET *) alloca (max_suid * sizeof (HARD_REG_SET));
-
- bzero ((char *) after_insn_hard_regs, max_suid * sizeof (HARD_REG_SET));
+ after_insn_hard_regs =
+ (HARD_REG_SET *) xcalloc (max_suid, sizeof (HARD_REG_SET));
/* Allocate and zero out many data structures
that will record the data from lifetime analysis. */
- allocate_for_life_analysis ();
+ allocate_reg_life_data ();
+ allocate_bb_life_data ();
for (i = 0; i < max_regno; i++)
REG_N_DEATHS (i) = 1;
Also find where each hard register is live
and record that info in after_insn_hard_regs.
regs_live[I] is 1 if hard reg I is live
- at the current point in the scan. */
+ at the current point in the scan.
+
+ Build reload_insn_chain while we're walking the insns. */
+ reload_insn_chain = 0;
for (insn = last; insn; insn = PREV_INSN (insn))
{
register HARD_REG_SET *p = after_insn_hard_regs + INSN_SUID (insn);
+ struct insn_chain *chain;
/* Copy the info in regs_live into the element of after_insn_hard_regs
for the current position in the rtl code. */
if (regs_live[i])
SET_HARD_REG_BIT (*p, i);
+ if (GET_CODE (insn) != NOTE && GET_CODE (insn) != BARRIER)
+ {
+ chain = new_insn_chain ();
+ if (reload_insn_chain)
+ reload_insn_chain->prev = chain;
+ chain->next = reload_insn_chain;
+ chain->prev = 0;
+ reload_insn_chain = chain;
+ chain->block = 0;
+ chain->insn = insn;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_live[i])
+ SET_REGNO_REG_SET (chain->live_before, i);
+ }
+
/* Update which hard regs are currently live
and also the birth and death suids of pseudo regs
based on the pattern of this insn. */
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
- stupid_mark_refs (PATTERN (insn), insn);
+ stupid_mark_refs (PATTERN (insn), chain);
if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
/* It is important that this be done after processing the insn's
pattern because we want the function result register to still
be live if it's also used to pass arguments. */
- stupid_mark_refs (CALL_INSN_FUNCTION_USAGE (insn), insn);
+ stupid_mark_refs (CALL_INSN_FUNCTION_USAGE (insn), chain);
+ }
+
+ if (GET_CODE (insn) != NOTE && GET_CODE (insn) != BARRIER)
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_live[i])
+ SET_REGNO_REG_SET (chain->live_after, i);
+
+ /* The regs_live array doesn't say anything about hard registers
+ clobbered by this insn. So we need an extra pass over the
+ pattern. */
+ current_chain = chain;
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ note_stores (PATTERN (insn), find_clobbered_regs);
}
+
if (GET_CODE (insn) == JUMP_INSN && computed_jump_p (insn))
current_function_has_computed_jump = 1;
}
/* Some regnos disappear from the rtl. Ignore them to avoid crash.
Also don't allocate registers that cross a setjmp, or live across
- a call if this function receives a nonlocal goto. */
+ a call if this function receives a nonlocal goto.
+ Also ignore registers we didn't see during the scan. */
if (regno_reg_rtx[r] == 0 || regs_crosses_setjmp[r]
+ || (reg_where_born_exact[r] == 0 && reg_where_dead[r] == 0)
|| (REG_N_CALLS_CROSSED (r) > 0
&& current_function_has_nonlocal_label))
continue;
reg_renumber[r] = stupid_find_reg (REG_N_CALLS_CROSSED (r),
reg_preferred_class (r),
PSEUDO_REGNO_MODE (r),
- reg_where_born[r],
+ REG_WHERE_BORN (r),
reg_where_dead[r],
regs_change_size[r]);
reg_renumber[r] = stupid_find_reg (REG_N_CALLS_CROSSED (r),
reg_alternate_class (r),
PSEUDO_REGNO_MODE (r),
- reg_where_born[r],
+ REG_WHERE_BORN (r),
reg_where_dead[r],
regs_change_size[r]);
}
+ /* Fill in the pseudo reg life information into the insn chain. */
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+ {
+ struct insn_chain *chain;
+ int regno;
+
+ regno = reg_renumber[i];
+ if (regno < 0)
+ continue;
+
+ chain = reg_where_dead_chain[i];
+ if (reg_where_dead[i] > INSN_SUID (chain->insn))
+ SET_REGNO_REG_SET (chain->live_after, i);
+
+ while (INSN_SUID (chain->insn) > reg_where_born_exact[i])
+ {
+ SET_REGNO_REG_SET (chain->live_before, i);
+ chain = chain->prev;
+ if (!chain)
+ break;
+ SET_REGNO_REG_SET (chain->live_after, i);
+ }
+
+ if (INSN_SUID (chain->insn) == reg_where_born_exact[i]
+ && reg_where_born_clobber[i])
+ SET_REGNO_REG_SET (chain->live_before, i);
+ }
+
if (file)
dump_flow_info (file);
+
+ free (regs_live);
+ free (uid_suid);
+ free (reg_where_dead);
+ free (reg_where_born_exact);
+ free (reg_where_born_clobber);
+ free (reg_where_dead_chain);
+ free (reg_order);
+ free (regs_change_size);
+ free (regs_crosses_setjmp);
+ free (after_insn_hard_regs);
}
/* Comparison function for qsort.
const GENERIC_PTR r2p;
{
register int r1 = *(int *)r1p, r2 = *(int *)r2p;
- register int len1 = reg_where_dead[r1] - reg_where_born[r1];
- register int len2 = reg_where_dead[r2] - reg_where_born[r2];
+ register int len1 = reg_where_dead[r1] - REG_WHERE_BORN (r1);
+ register int len2 = reg_where_dead[r2] - REG_WHERE_BORN (r2);
int tem;
tem = len2 - len1;
enum reg_class class;
enum machine_mode mode;
int born_insn, dead_insn;
- int changes_size;
+ int changes_size ATTRIBUTE_UNUSED;
{
register int i, ins;
#ifdef HARD_REG_SET
call_preserved ? call_used_reg_set : fixed_reg_set);
#ifdef ELIMINABLE_REGS
- for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+ for (i = 0; i < (int)(sizeof eliminables / sizeof eliminables[0]); i++)
SET_HARD_REG_BIT (used, eliminables[i].from);
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
SET_HARD_REG_BIT (used, HARD_FRAME_POINTER_REGNUM);
INSN is the current insn, supplied so we can find its suid. */
static void
-stupid_mark_refs (x, insn)
- rtx x, insn;
+stupid_mark_refs (x, chain)
+ rtx x;
+ struct insn_chain *chain;
{
register RTX_CODE code;
- register char *fmt;
+ register const char *fmt;
register int regno, i;
+ rtx insn = chain->insn;
if (x == 0)
return;
the clobbering insn. When setting, just after. */
int where_born = INSN_SUID (insn) - (code == CLOBBER);
- reg_where_born[regno] = where_born;
+ reg_where_born_exact[regno] = INSN_SUID (insn);
+ reg_where_born_clobber[regno] = (code == CLOBBER);
+
+ if (reg_where_dead_chain[regno] == 0)
+ reg_where_dead_chain[regno] = chain;
/* The reg must live at least one insn even
in it is never again used--because it has to go
if (last_setjmp_suid < reg_where_dead[regno])
regs_crosses_setjmp[regno] = 1;
- /* If this register is only used in this insn and is only
- set, mark it unused. We have to do this even when not
- optimizing so that MD patterns which count on this
- behavior (e.g., it not causing an output reload on
- an insn setting CC) will operate correctly. */
+ /* If this register is clobbered or it is only used in
+ this insn and is only set, mark it unused. We have
+ to do this even when not optimizing so that MD patterns
+ which count on this behavior (e.g., it not causing an
+ output reload on an insn setting CC) will operate
+ correctly. */
if (GET_CODE (SET_DEST (x)) == REG
- && REGNO_FIRST_UID (regno) == INSN_UID (insn)
- && REGNO_LAST_UID (regno) == INSN_UID (insn)
- && (code == CLOBBER || ! reg_mentioned_p (SET_DEST (x),
- SET_SRC (x))))
+ && (code == CLOBBER
+ || (REGNO_FIRST_UID (regno) == INSN_UID (insn)
+ && REGNO_LAST_UID (regno) == INSN_UID (insn)
+ && ! reg_mentioned_p (SET_DEST (x),
+ SET_SRC (x)))))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_UNUSED,
SET_DEST (x),
REG_NOTES (insn));
If setting a SUBREG, we treat the entire reg as *used*. */
if (code == SET)
{
- stupid_mark_refs (SET_SRC (x), insn);
+ stupid_mark_refs (SET_SRC (x), chain);
if (GET_CODE (SET_DEST (x)) != REG)
- stupid_mark_refs (SET_DEST (x), insn);
+ stupid_mark_refs (SET_DEST (x), chain);
}
return;
}
{
/* Pseudo reg: record first use, last use and number of uses. */
- reg_where_born[regno] = INSN_SUID (insn);
+ reg_where_born_exact[regno] = INSN_SUID (insn);
+ reg_where_born_clobber[regno] = 0;
REG_N_REFS (regno)++;
if (regs_live[regno] == 0)
{
regs_live[regno] = 1;
reg_where_dead[regno] = INSN_SUID (insn);
+ reg_where_dead_chain[regno] = chain;
}
}
return;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- stupid_mark_refs (XEXP (x, i), insn);
+ stupid_mark_refs (XEXP (x, i), chain);
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- stupid_mark_refs (XVECEXP (x, i, j), insn);
+ stupid_mark_refs (XVECEXP (x, i, j), chain);
}
}
}