summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorBernd Schmidt <crux@pool.informatik.rwth-aachen.de>1998-10-05 18:39:23 -0600
committerJeff Law <law@gcc.gnu.org>1998-10-05 18:39:23 -0600
commitcad6f7d0500211029f717912048103063ea93df5 (patch)
tree8d139251935aba1755bfaa2510b2f95b82ebf53f /gcc
parent0eaae86cabfda78baec29e96a461f6bc52f60e6e (diff)
downloadgcc-cad6f7d0500211029f717912048103063ea93df5.tar.gz
Makefile.in (stupid.o): Update dependencies.
* Makefile.in (stupid.o): Update dependencies. (global.o): Likewise. * global.c: Include reload.h (reg_becomes_live): New function. (reg_dies): New function. (build_insn_chain): New function. (global_alloc): Call build_insn_chain before calling reload. * reload.h (struct needs): New structure definition. (struct insn_chain): Likewise. (reload_insn_chain): Declare variable. (new_insn_chain): Declare function. * reload1.c (reload_startobj): New variable. (reload_insn_chain): New variable. (unused_insn_chains): New variable. (new_insn_chain): New function. (init_reload): Initialize reload_startobj, not reload_firstobj. (reload): Initialize reload_firstobj. Before returning, free everything on the reload_obstack. * stupid.c: Include insn-config.h, reload.h and basic-block.h. (reg_where_dead_chain, reg_where_born_exact, reg_where_born_clobber, current_chain): New variables. (reg_where_born): Delete variable. (REG_WHERE_BORN): New macro. (find_clobbered_regs): New function. (stupid_life_analysis): Don't allocate/free reg_where_born. Allocate and free reg_where_born_exact, reg_where_born_clobber, reg_where_dead_chain. Use REG_WHERE_BORN instead of reg_where_born. While processing the insns, build the reload_insn_chain with information about register lifetimes. (stupid_reg_compare): Use REG_WHERE_BORN instead of reg_where_born. (stupid_mark_refs): Replace arg INSN with arg CHAIN. All callers changed. Compute and information about birth and death of pseudo registers in reg_where_dead_chain, reg_where_born_exact and reg_where_born_clobber. Delete code to set elements of reg_where_born. From-SVN: r22862
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog44
-rw-r--r--gcc/Makefile.in4
-rw-r--r--gcc/global.c138
-rw-r--r--gcc/reload.h75
-rw-r--r--gcc/reload1.c47
-rw-r--r--gcc/stupid.c173
6 files changed, 454 insertions, 27 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 6ddce689a54..e50a261eb31 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,47 @@
+Tue Oct 6 01:36:00 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
+
+ * Makefile.in (stupid.o): Update dependencies.
+ (global.o): Likewise.
+
+ * global.c: Include reload.h
+ (reg_becomes_live): New function.
+ (reg_dies): New function.
+ (build_insn_chain): New function.
+ (global_alloc): Call build_insn_chain before calling reload.
+
+ * reload.h (struct needs): New structure definition.
+ (struct insn_chain): Likewise.
+ (reload_insn_chain): Declare variable.
+ (new_insn_chain): Declare function.
+
+
+ * reload1.c (reload_startobj): New variable.
+ (reload_insn_chain): New variable.
+ (unused_insn_chains): New variable.
+ (new_insn_chain): New function.
+ (init_reload): Initialize reload_startobj, not reload_firstobj.
+ (reload): Initialize reload_firstobj.
+ Before returning, free everything on the reload_obstack.
+
+ * stupid.c: Include insn-config.h, reload.h and basic-block.h.
+ (reg_where_dead_chain, reg_where_born_exact, reg_where_born_clobber,
+ current_chain): New variables.
+ (reg_where_born): Delete variable.
+ (REG_WHERE_BORN): New macro.
+ (find_clobbered_regs): New function.
+ (stupid_life_analysis): Don't allocate/free reg_where_born.
+ Allocate and free reg_where_born_exact, reg_where_born_clobber,
+ reg_where_dead_chain.
+ Use REG_WHERE_BORN instead of reg_where_born.
+ While processing the insns, build the reload_insn_chain with
+ information about register lifetimes.
+ (stupid_reg_compare): Use REG_WHERE_BORN instead of reg_where_born.
+ (stupid_mark_refs): Replace arg INSN with arg CHAIN. All callers
+ changed.
+ Compute and information about birth and death of pseudo registers in
+ reg_where_dead_chain, reg_where_born_exact and reg_where_born_clobber.
+ Delete code to set elements of reg_where_born.
+
Mon Oct 5 22:34:30 1998 Alexandre Petit-Bianco <apbianco@cygnus.com>
* tree.def (GOTO_EXPR): Modified documentation.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 8898140cfc7..e97246df4ba 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1461,7 +1461,7 @@ jump.o : jump.c $(CONFIG_H) system.h $(RTL_H) flags.h hard-reg-set.h $(REGS_H) \
insn-config.h insn-flags.h $(RECOG_H) $(EXPR_H) real.h except.h \
toplev.h
stupid.o : stupid.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h \
- flags.h toplev.h
+ $(BASIC_BLOCK_H) insn-config.h reload.h flags.h toplev.h
cse.o : cse.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h output.h
@@ -1487,7 +1487,7 @@ local-alloc.o : local-alloc.c $(CONFIG_H) system.h $(RTL_H) flags.h \
insn-attr.h toplev.h
bitmap.o : bitmap.c $(CONFIG_H) system.h $(RTL_H) flags.h $(BASIC_BLOCK_H) \
$(REGS_H)
-global.o : global.c $(CONFIG_H) system.h $(RTL_H) flags.h \
+global.o : global.c $(CONFIG_H) system.h $(RTL_H) flags.h reload.h \
$(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h insn-config.h output.h toplev.h
varray.o : varray.c $(CONFIG_H) system.h varray.h $(RTL_H) $(TREE_H) bitmap.h
diff --git a/gcc/global.c b/gcc/global.c
index e7edb6d36ac..04a836c0003 100644
--- a/gcc/global.c
+++ b/gcc/global.c
@@ -29,6 +29,7 @@ Boston, MA 02111-1307, USA. */
#include "basic-block.h"
#include "regs.h"
#include "insn-config.h"
+#include "reload.h"
#include "output.h"
#include "toplev.h"
@@ -268,6 +269,9 @@ static void mark_reg_death PROTO((rtx));
static void mark_reg_live_nc PROTO((int, enum machine_mode));
static void set_preference PROTO((rtx, rtx));
static void dump_conflicts PROTO((FILE *));
+static void reg_becomes_live PROTO((rtx, rtx));
+static void reg_dies PROTO((int, enum machine_mode));
+static void build_insn_chain PROTO((rtx));
/* Perform allocation of pseudo-registers not allocated by local_alloc.
FILE is a file to output debugging information on,
@@ -573,7 +577,10 @@ global_alloc (file)
for the sake of debugging information. */
if (n_basic_blocks > 0)
#endif
- retval = reload (get_insns (), 1, file);
+ {
+ build_insn_chain (get_insns ());
+ retval = reload (get_insns (), 1, file);
+ }
free (conflicts);
return retval;
@@ -1648,6 +1655,135 @@ mark_elimination (from, to)
}
}
+/* Used for communication between the following functions. Holds the
+ current life information. */
+static regset live_relevant_regs;
+
+/* Record in live_relevant_regs that register REG became live. This
+ is called via note_stores. */
+static void
+reg_becomes_live (reg, setter)
+ rtx reg;
+ rtx setter ATTRIBUTE_UNUSED;
+{
+ int regno;
+
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ if (GET_CODE (reg) != REG)
+ return;
+
+ regno = REGNO (reg);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int nregs = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (nregs-- > 0)
+ SET_REGNO_REG_SET (live_relevant_regs, regno++);
+ }
+ else if (reg_renumber[regno] >= 0)
+ SET_REGNO_REG_SET (live_relevant_regs, regno);
+}
+
+/* Record in live_relevant_regs that register REGNO died. */
+static void
+reg_dies (regno, mode)
+ int regno;
+ enum machine_mode mode;
+{
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int nregs = HARD_REGNO_NREGS (regno, mode);
+ while (nregs-- > 0)
+ CLEAR_REGNO_REG_SET (live_relevant_regs, regno++);
+ }
+ else
+ CLEAR_REGNO_REG_SET (live_relevant_regs, regno);
+}
+
+/* Walk the insns of the current function and build reload_insn_chain,
+ and record register life information. */
+static void
+build_insn_chain (first)
+ rtx first;
+{
+ struct insn_chain **p = &reload_insn_chain;
+ struct insn_chain *prev = 0;
+ int b = 0;
+
+ live_relevant_regs = ALLOCA_REG_SET ();
+
+ for (; first; first = NEXT_INSN (first))
+ {
+ struct insn_chain *c;
+
+ if (first == basic_block_head[b])
+ {
+ int i;
+ CLEAR_REG_SET (live_relevant_regs);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (REGNO_REG_SET_P (basic_block_live_at_start[b], i)
+ && ! TEST_HARD_REG_BIT (eliminable_regset, i))
+ SET_REGNO_REG_SET (live_relevant_regs, i);
+
+ for (; i < max_regno; i++)
+ if (reg_renumber[i] >= 0
+ && REGNO_REG_SET_P (basic_block_live_at_start[b], i))
+ SET_REGNO_REG_SET (live_relevant_regs, i);
+ }
+
+ if (GET_CODE (first) != NOTE && GET_CODE (first) != BARRIER)
+ {
+ c = new_insn_chain ();
+ c->prev = prev;
+ prev = c;
+ *p = c;
+ p = &c->next;
+ c->insn = first;
+ c->block = b;
+
+ COPY_REG_SET (c->live_before, live_relevant_regs);
+
+ if (GET_RTX_CLASS (GET_CODE (first)) == 'i')
+ {
+ rtx link;
+
+ /* Mark the death of everything that dies in this instruction. */
+
+ for (link = REG_NOTES (first); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD
+ && GET_CODE (XEXP (link, 0)) == REG)
+ reg_dies (REGNO (XEXP (link, 0)), GET_MODE (XEXP (link, 0)));
+
+ /* Mark everything born in this instruction as live. */
+
+ note_stores (PATTERN (first), reg_becomes_live);
+ }
+
+ /* Remember which registers are live at the end of the insn, before
+ killing those with REG_UNUSED notes. */
+ COPY_REG_SET (c->live_after, live_relevant_regs);
+
+ if (GET_RTX_CLASS (GET_CODE (first)) == 'i')
+ {
+ rtx link;
+
+ /* Mark anything that is set in this insn and then unused as dying. */
+
+ for (link = REG_NOTES (first); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_UNUSED
+ && GET_CODE (XEXP (link, 0)) == REG)
+ reg_dies (REGNO (XEXP (link, 0)), GET_MODE (XEXP (link, 0)));
+ }
+ }
+
+ if (first == basic_block_end[b])
+ b++;
+ }
+ FREE_REG_SET (live_relevant_regs);
+ *p = 0;
+}
+
/* Print debugging trace information if -greg switch is given,
showing the information on which the allocation decisions are based. */
diff --git a/gcc/reload.h b/gcc/reload.h
index d99b0c128a5..58f6be3a101 100644
--- a/gcc/reload.h
+++ b/gcc/reload.h
@@ -142,6 +142,81 @@ extern enum insn_code reload_in_optab[];
extern enum insn_code reload_out_optab[];
#endif
+struct needs
+{
+ /* [0] is normal, [1] is nongroup. */
+ short regs[2][N_REG_CLASSES];
+ short groups[N_REG_CLASSES];
+};
+
+#if defined SET_HARD_REG_BIT && defined CLEAR_REG_SET
+/* This structure describes instructions which are relevant for reload.
+ Apart from all regular insns, this also includes CODE_LABELs, since they
+ must be examined for register elimination. */
+struct insn_chain
+{
+ /* Links to the neighbour instructions. */
+ struct insn_chain *next, *prev;
+
+ /* Link through a chains set up by calculate_needs_all_insns, containing
+ all insns that need reloading. */
+ struct insn_chain *next_need_reload;
+
+ /* The basic block this insn is in. */
+ int block;
+ /* The rtx of the insn. */
+ rtx insn;
+ /* Register life information: record all live hard registers, and all
+ live pseudos that have a hard register.
+ This information is recorded for the point immediately before the insn
+ (in live_before), and for the point within the insn at which all
+ outputs have just been written to (in live_after). */
+ regset live_before;
+ regset live_after;
+
+ /* For each class, size of group of consecutive regs
+ that is needed for the reloads of this class. */
+ char group_size[N_REG_CLASSES];
+ /* For each class, the machine mode which requires consecutive
+ groups of regs of that class.
+ If two different modes ever require groups of one class,
+ they must be the same size and equally restrictive for that class,
+ otherwise we can't handle the complexity. */
+ enum machine_mode group_mode[N_REG_CLASSES];
+
+ /* Indicates if a register was counted against the need for
+ groups. 0 means it can count against max_nongroup instead. */
+ HARD_REG_SET counted_for_groups;
+
+ /* Indicates if a register was counted against the need for
+ non-groups. 0 means it can become part of a new group.
+ During choose_reload_regs, 1 here means don't use this reg
+ as part of a group, even if it seems to be otherwise ok. */
+ HARD_REG_SET counted_for_nongroups;
+
+ /* Indicates which registers have already been used for spills. */
+ HARD_REG_SET used_spill_regs;
+
+ /* Describe the needs for reload registers of this insn. */
+ struct needs need;
+
+ /* Nonzero if find_reloads said the insn requires reloading. */
+ unsigned int need_reload:1;
+ /* Nonzero if eliminate_regs_in_insn said it requires eliminations. */
+ unsigned int need_elim:1;
+ /* Nonzero if this insn was inserted by perform_caller_saves. */
+ unsigned int is_caller_save_insn:1;
+};
+
+/* A chain of insn_chain structures to describe all non-note insns in
+ a function. */
+extern struct insn_chain *reload_insn_chain;
+
+/* Allocate a new insn_chain structure. */
+extern struct insn_chain *new_insn_chain PROTO((void));
+
+#endif
+
/* Functions from reload.c: */
/* Return a memory location that will be used to copy X in mode MODE.
diff --git a/gcc/reload1.c b/gcc/reload1.c
index 6b11bb95646..8b15e0e8e8d 100644
--- a/gcc/reload1.c
+++ b/gcc/reload1.c
@@ -32,9 +32,9 @@ Boston, MA 02111-1307, USA. */
#include "flags.h"
#include "expr.h"
#include "regs.h"
+#include "basic-block.h"
#include "reload.h"
#include "recog.h"
-#include "basic-block.h"
#include "output.h"
#include "real.h"
#include "toplev.h"
@@ -279,6 +279,13 @@ enum insn_code reload_out_optab[NUM_MACHINE_MODES];
insn. */
struct obstack reload_obstack;
+
+/* Points to the beginning of the reload_obstack. All insn_chain structures
+ are allocated first. */
+char *reload_startobj;
+
+/* The point after all insn_chain structures. Used to quickly deallocate
+ memory used while processing one insn. */
char *reload_firstobj;
#define obstack_chunk_alloc xmalloc
@@ -286,6 +293,10 @@ char *reload_firstobj;
/* List of labels that must never be deleted. */
extern rtx forced_labels;
+
+/* List of insn_chain instructions, one for every insn that reload needs to
+ examine. */
+struct insn_chain *reload_insn_chain;
/* This structure is used to record information about register eliminations.
Each array entry describes one possible way of eliminating a register
@@ -461,7 +472,7 @@ init_reload ()
/* Initialize obstack for our rtl allocation. */
gcc_obstack_init (&reload_obstack);
- reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0);
+ reload_startobj = (char *) obstack_alloc (&reload_obstack, 0);
/* Decide which register class should be used when reloading
addresses. If we are using SMALL_REGISTER_CLASSES, and any
@@ -522,6 +533,32 @@ init_reload ()
}
}
+/* List of insn chains that are currently unused. */
+static struct insn_chain *unused_insn_chains = 0;
+
+/* Allocate an empty insn_chain structure. */
+struct insn_chain *
+new_insn_chain ()
+{
+ struct insn_chain *c;
+
+ if (unused_insn_chains == 0)
+ {
+ c = obstack_alloc (&reload_obstack, sizeof (struct insn_chain));
+ c->live_before = OBSTACK_ALLOC_REG_SET (&reload_obstack);
+ c->live_after = OBSTACK_ALLOC_REG_SET (&reload_obstack);
+ }
+ else
+ {
+ c = unused_insn_chains;
+ unused_insn_chains = c->next;
+ }
+ c->is_caller_save_insn = 0;
+ c->need_reload = 0;
+ c->need_elim = 0;
+ return c;
+}
+
/* Global variables used by reload and its subroutines. */
/* Set during calculate_needs if an insn needs reloading. */
@@ -605,6 +642,8 @@ reload (first, global, dumpfile)
failure = 0;
+ reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0);
+
/* Enable find_equiv_reg to distinguish insns made by reload. */
reload_first_uid = get_max_uid ();
@@ -1217,7 +1256,9 @@ reload (first, global, dumpfile)
if (size > STACK_CHECK_MAX_FRAME_SIZE)
warning ("frame size too large for reliable stack checking");
}
-
+
+ obstack_free (&reload_obstack, reload_startobj);
+
/* Indicate that we no longer have known memory locations or constants. */
reg_equiv_constant = 0;
reg_equiv_memory_loc = 0;
diff --git a/gcc/stupid.c b/gcc/stupid.c
index dbb0ffeccc9..cd1329f83d7 100644
--- a/gcc/stupid.c
+++ b/gcc/stupid.c
@@ -47,7 +47,10 @@ Boston, MA 02111-1307, USA. */
#include "rtl.h"
#include "hard-reg-set.h"
+#include "basic-block.h"
#include "regs.h"
+#include "insn-config.h"
+#include "reload.h"
#include "flags.h"
#include "toplev.h"
@@ -77,9 +80,21 @@ static int last_setjmp_suid;
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. */
@@ -111,7 +126,43 @@ static HARD_REG_SET *after_insn_hard_regs;
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));
+
+/* 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++);
+ }
+}
/* Stupid life analysis is for the case where only variables declared
`register' go in registers. For this case, we mark all
@@ -171,9 +222,15 @@ stupid_life_analysis (f, nregs, file)
reg_where_dead = (int *) xmalloc (nregs * sizeof (int));
bzero ((char *) reg_where_dead, nregs * sizeof (int));
- reg_where_born = (int *) xmalloc (nregs * sizeof (int));
- bzero ((char *) reg_where_born, nregs * sizeof (int));
+ reg_where_born_exact = (int *) xmalloc (nregs * sizeof (int));
+ bzero ((char *) reg_where_born_exact, nregs * sizeof (int));
+ reg_where_born_clobber = (int *) xmalloc (nregs * sizeof (int));
+ bzero ((char *) reg_where_born_clobber, nregs * sizeof (int));
+
+ reg_where_dead_chain = (struct insn_chain **) xmalloc (nregs * sizeof (struct insn_chain *));
+ bzero ((char *) reg_where_dead_chain, nregs * sizeof (struct insn_chain *));
+
reg_order = (int *) xmalloc (nregs * sizeof (int));
bzero ((char *) reg_order, nregs * sizeof (int));
@@ -210,11 +267,15 @@ stupid_life_analysis (f, nregs, file)
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. */
@@ -223,12 +284,27 @@ stupid_life_analysis (f, nregs, file)
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)
@@ -266,8 +342,23 @@ stupid_life_analysis (f, nregs, file)
/* 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;
}
@@ -289,8 +380,10 @@ stupid_life_analysis (f, nregs, file)
/* 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;
@@ -300,7 +393,7 @@ stupid_life_analysis (f, nregs, file)
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]);
@@ -309,18 +402,48 @@ stupid_life_analysis (f, nregs, file)
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);
+ 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);
@@ -336,8 +459,8 @@ stupid_reg_compare (r1p, r2p)
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;
@@ -470,12 +593,14 @@ stupid_find_reg (call_preserved, class, mode,
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 int regno, i;
+ rtx insn = chain->insn;
if (x == 0)
return;
@@ -530,7 +655,11 @@ stupid_mark_refs (x, insn)
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
@@ -573,9 +702,9 @@ stupid_mark_refs (x, 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;
}
@@ -608,12 +737,14 @@ stupid_mark_refs (x, insn)
{
/* 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;
@@ -625,12 +756,12 @@ stupid_mark_refs (x, insn)
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);
}
}
}