From cad6f7d0500211029f717912048103063ea93df5 Mon Sep 17 00:00:00 2001 From: Bernd Schmidt Date: Mon, 5 Oct 1998 18:39:23 -0600 Subject: [PATCH] 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 --- gcc/ChangeLog | 44 ++++++++++++ gcc/Makefile.in | 4 +- gcc/global.c | 138 +++++++++++++++++++++++++++++++++++++- gcc/reload.h | 75 +++++++++++++++++++++ gcc/reload1.c | 47 ++++++++++++- gcc/stupid.c | 175 ++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 455 insertions(+), 28 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 + + * 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 * 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; -/* Element N is suid of insn where life span of pseudo reg N begins. */ +/* Likewise, but point to the insn_chain structure of the insn at which + the reg dies. */ +static struct insn_chain **reg_where_dead_chain; -static int *reg_where_born; +/* 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; + +/* 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); } } }