From 4c649323e35b4334684c43c13f55f8dcdd1e849a Mon Sep 17 00:00:00 2001 From: Jeffrey A Law Date: Sat, 27 Jun 1998 15:51:49 +0000 Subject: [PATCH] flow.c (count_reg_sets): New function. * flow.c (count_reg_sets): New function. (count_reg_sets_1, count_ref_references): Likewise. (recompute_reg_usage): Likewise. * rtl.h (recompute_reg_usage): Add prototype. * toplev.c (rest_of_compilation): Call recompute_reg_usage just before local register allocation. From-SVN: r20756 --- gcc/ChangeLog | 9 ++ gcc/flow.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/rtl.h | 2 + gcc/toplev.c | 1 + 4 files changed, 293 insertions(+) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index eb762b13bf7..d3cb6c1fdf1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +Sat Jun 27 16:45:42 1998 Jeffrey A Law (law@cygnus.com) + + * flow.c (count_reg_sets): New function. + (count_reg_sets_1, count_ref_references): Likewise. + (recompute_reg_usage): Likewise. + * rtl.h (recompute_reg_usage): Add prototype. + * toplev.c (rest_of_compilation): Call recompute_reg_usage just + before local register allocation. + Sat Jun 27 13:15:30 1998 Richard Henderson * alpha.md (negsf, negdf): Revert Jan 22 change. diff --git a/gcc/flow.c b/gcc/flow.c index f8ce8689d37..f78e171e0e5 100644 --- a/gcc/flow.c +++ b/gcc/flow.c @@ -284,6 +284,9 @@ static int_list_ptr add_int_list_node PROTO ((int_list_block **, int_list **, int)); static void init_regset_vector PROTO ((regset *, int, struct obstack *)); +static void count_reg_sets_1 PROTO ((rtx)); +static void count_reg_sets PROTO ((rtx)); +static void count_reg_references PROTO ((rtx)); /* Find basic blocks of the current function. F is the first insn of the function and NREGS the number of register numbers @@ -3997,3 +4000,281 @@ compute_dominators (dominators, post_dominators, s_preds, s_succs) free (temp_bitmap); } + +/* Count for a single SET rtx, X. */ + +static void +count_reg_sets_1 (x) + rtx x; +{ + register int regno; + register rtx reg = SET_DEST (x); + + /* Find the register that's set/clobbered. */ + while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT + || GET_CODE (reg) == SIGN_EXTRACT + || GET_CODE (reg) == STRICT_LOW_PART) + reg = XEXP (reg, 0); + + if (GET_CODE (reg) == REG) + { + regno = REGNO (reg); + if (regno >= FIRST_PSEUDO_REGISTER) + { + /* Count (weighted) references, stores, etc. This counts a + register twice if it is modified, but that is correct. */ + REG_N_SETS (regno)++; + + REG_N_REFS (regno) += loop_depth; + } + } +} + +/* Increment REG_N_SETS for each SET or CLOBBER found in X; also increment + REG_N_REFS by the current loop depth for each SET or CLOBBER found. */ + +static void +count_reg_sets (x) + rtx x; +{ + register RTX_CODE code = GET_CODE (x); + + if (code == SET || code == CLOBBER) + count_reg_sets_1 (x); + else if (code == PARALLEL) + { + register int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + code = GET_CODE (XVECEXP (x, 0, i)); + if (code == SET || code == CLOBBER) + count_reg_sets_1 (XVECEXP (x, 0, i)); + } + } +} + +/* Increment REG_N_REFS by the current loop depth each register reference + found in X. */ + +static void +count_reg_references (x) + rtx x; +{ + register RTX_CODE code; + register int regno; + int i; + + retry: + code = GET_CODE (x); + switch (code) + { + case LABEL_REF: + case SYMBOL_REF: + case CONST_INT: + case CONST: + case CONST_DOUBLE: + case PC: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case ASM_INPUT: + return; + +#ifdef HAVE_cc0 + case CC0: + return; +#endif + + case CLOBBER: + /* If we are clobbering a MEM, mark any registers inside the address + as being used. */ + if (GET_CODE (XEXP (x, 0)) == MEM) + count_reg_references (XEXP (XEXP (x, 0), 0)); + return; + + case SUBREG: + /* While we're here, optimize this case. */ + x = SUBREG_REG (x); + + /* In case the SUBREG is not of a register, don't optimize */ + if (GET_CODE (x) != REG) + { + count_reg_references (x); + return; + } + + /* ... fall through ... */ + + case REG: + if (REGNO (x) >= FIRST_PSEUDO_REGISTER) + REG_N_REFS (REGNO (x)) += loop_depth; + return; + + case SET: + { + register rtx testreg = SET_DEST (x); + int mark_dest = 0; + + /* If storing into MEM, don't show it as being used. But do + show the address as being used. */ + if (GET_CODE (testreg) == MEM) + { + count_reg_references (XEXP (testreg, 0)); + count_reg_references (SET_SRC (x)); + return; + } + + /* Storing in STRICT_LOW_PART is like storing in a reg + in that this SET might be dead, so ignore it in TESTREG. + but in some other ways it is like using the reg. + + Storing in a SUBREG or a bit field is like storing the entire + register in that if the register's value is not used + then this SET is not needed. */ + while (GET_CODE (testreg) == STRICT_LOW_PART + || GET_CODE (testreg) == ZERO_EXTRACT + || GET_CODE (testreg) == SIGN_EXTRACT + || GET_CODE (testreg) == SUBREG) + { + /* Modifying a single register in an alternate mode + does not use any of the old value. But these other + ways of storing in a register do use the old value. */ + if (GET_CODE (testreg) == SUBREG + && !(REG_SIZE (SUBREG_REG (testreg)) > REG_SIZE (testreg))) + ; + else + mark_dest = 1; + + testreg = XEXP (testreg, 0); + } + + /* If this is a store into a register, + recursively scan the value being stored. */ + + if (GET_CODE (testreg) == REG) + { + count_reg_references (SET_SRC (x)); + if (mark_dest) + count_reg_references (SET_DEST (x)); + return; + } + } + break; + + default: + break; + } + + /* Recursively scan the operands of this expression. */ + + { + register char *fmt = GET_RTX_FORMAT (code); + register int i; + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + /* Tail recursive case: save a function call level. */ + if (i == 0) + { + x = XEXP (x, 0); + goto retry; + } + count_reg_references (XEXP (x, i)); + } + else if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + count_reg_references (XVECEXP (x, i, j)); + } + } + } +} + +/* Recompute register set/reference counts immediately prior to register + allocation. + + This avoids problems with set/reference counts changing to/from values + which have special meanings to the register allocators. + + Additionally, the reference counts are the primary component used by the + register allocators to prioritize pseudos for allocation to hard regs. + More accurate reference counts generally lead to better register allocation. + + It might be worthwhile to update REG_LIVE_LENGTH, REG_BASIC_BLOCK and + possibly other information which is used by the register allocators. */ + +int +recompute_reg_usage (f) + rtx f; +{ + rtx insn; + int i, max_reg; + + /* Clear out the old data. */ + max_reg = max_reg_num (); + for (i = FIRST_PSEUDO_REGISTER; i < max_reg; i++) + { + REG_N_SETS (i) = 0; + REG_N_REFS (i) = 0; + } + + /* Scan each insn in the chain and count how many times each register is + set/used. */ + loop_depth = 1; + for (insn = f; insn; insn = NEXT_INSN (insn)) + { + /* Keep track of loop depth. */ + if (GET_CODE (insn) == NOTE) + { + /* Look for loop boundaries. */ + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) + loop_depth--; + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) + loop_depth++; + + /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error. + Abort now rather than setting register status incorrectly. */ + if (loop_depth == 0) + abort (); + } + else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + rtx links; + + /* This call will increment REG_N_SETS for each SET or CLOBBER + of a register in INSN. It will also increment REG_N_REFS + by the loop depth for each set of a register in INSN. */ + count_reg_sets (PATTERN (insn)); + + /* count_reg_sets does not detect autoincrement address modes, so + detect them here by looking at the notes attached to INSN. */ + for (links = REG_NOTES (insn); links; links = XEXP (links, 1)) + { + if (REG_NOTE_KIND (links) == REG_INC) + /* Count (weighted) references, stores, etc. This counts a + register twice if it is modified, but that is correct. */ + REG_N_SETS (REGNO (XEXP (links, 0)))++; + } + + /* This call will increment REG_N_REFS by the current loop depth for + each reference to a register in INSN. */ + count_reg_references (PATTERN (insn)); + + /* count_reg_references will not include counts for arguments to + function calls, so detect them here by examining the + CALL_INSN_FUNCTION_USAGE data. */ + if (GET_CODE (insn) == CALL_INSN) + { + rtx note; + + for (note = CALL_INSN_FUNCTION_USAGE (insn); + note; + note = XEXP (note, 1)) + if (GET_CODE (XEXP (note, 0)) == USE) + count_reg_references (SET_DEST (XEXP (note, 0))); + } + } + } +} diff --git a/gcc/rtl.h b/gcc/rtl.h index af85d823738..d281cbef2d1 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -1407,6 +1407,7 @@ extern void stupid_life_analysis PROTO ((rtx, int, FILE *)); /* In flow.c */ extern void allocate_for_life_analysis PROTO ((void)); +extern int recompute_reg_usage PROTO ((rtx)); #ifdef BUFSIZ extern void dump_flow_info PROTO ((FILE *)); #endif @@ -1543,4 +1544,5 @@ extern void init_alias_analysis PROTO ((void)); extern void end_alias_analysis PROTO ((void)); extern void record_base_value PROTO ((int, rtx, int)); + #endif /* _RTL_H */ diff --git a/gcc/toplev.c b/gcc/toplev.c index d51dc60eb95..b1987336cb1 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -3481,6 +3481,7 @@ rest_of_compilation (decl) if (!obey_regdecls) TIMEVAR (local_alloc_time, { + recompute_reg_usage (insns); regclass (insns, max_reg_num ()); local_alloc (); }); -- 2.30.2