diff options
author | amylaar <amylaar@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-11-24 18:55:53 +0000 |
---|---|---|
committer | amylaar <amylaar@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-11-24 18:55:53 +0000 |
commit | 4d58fa46845bd30191722871f4df562b29c4a43b (patch) | |
tree | c4e97f2cd689b954ad779a8f0f2c7757912a91f8 | |
parent | cf63832997ae558d073cd83bd44284da7519b072 (diff) | |
download | gcc-4d58fa46845bd30191722871f4df562b29c4a43b.tar.gz |
PR target/21623:
* regclass.c (FORBIDDEN_INC_DEC_CLASSES): Remove
SECONDARY_INPUT_RELOAD_CLASS and SECONDARY_OUTPUT_RELOAD_CLASS tests.
(init_fake_stack_mems): Remove HAVE_SECONDARY_RELOADS test.
(memory_move_secondary_cost, init_reg_autoinc): Remove
SECONDARY_INPUT_RELOAD_CLASS / SECONDARY_OUTPUT_RELOAD_CLASS tests.
Replace SECONDARY_{IN,OUT}PUT_RELOAD_CLASS use with
secondary_reload_class call.
(copy_cost): Likewise. Add new parameter prev_sri. Changed all
callers.
* reload.c (entire file): Remove HAVE_SECONDARY_RELOADS checks.
(push_secondary_reload): Use secondary_reload target hook.
(secondary_reload_class, scratch_reload_class): New functions.
(push_reload): Remove SECONDARY_INPUT_RELOAD_CLASS and
SECONDARY_OUTPUT_RELOAD_CLASS tests. Replace
SECONDARY_{IN,OUT}PUT_RELOAD_CLASS use with secondary_reload_class call.
* reload.h (HAVE_SECONDARY_RELOADS): Don't define nor test.
(secondary_reload_class, scratch_reload_class): Declare.
* reload1.c: Include target.h.
(reload_adjust_reg_for_temp): New function.
(reload_adjust_reg_for_icode): Likewise.
(choose_reload_regs): Remove SECONDARY_INPUT_RELOAD_CLASS test.
Replace SECONDARY_INPUT_RELOAD_CLASS use with secondary_reload_class
call.
(emit_input_reload_insns): Likewise. Rewrite secondary reload checks
for inheritance. Support case when both secondary & tertiary reloads
are for intermediate registers.
(emit_output_reload_insns): Replace SECONDARY_OUTPUT_RELOAD_CLASS use
with secondary_reload_class call. Support case when both secondary
& tertiary reloads are for intermediate registers.
* target-def.h (TARGET_SECONDARY_RELOAD): Provide default definition.
(TARGET_INITIALIZER) Add TARGET_SECONDARY_RELOAD.
* target.h (secondary_reload_info): New struct / typedef.
(struct gcc_target): New member secondary_reload.
* targhooks.c Include reload.h, optabs.h and recog.h.
(default_secondary_reload): New function.
* targhooks.h (default_secondary_reload): Declare.
* doc/tm.texi: Document secondary_reload target hook. Update
description of SECONDARY_*RELOAD_CLASS and reload_{in,out}<mode>.
* doc/md.texi: Likewise.
* sh-protos.h (sh_secondary_reload): Declare.
* sh.c (TARGET_SECONDARY_RELOAD): Override.
(sh_secondary_reload): New function.
* sh.h (SECONDARY_INOUT_RELOAD_CLASS): Don't define.
(SECONDARY_OUTPUT_RELOAD_CLASS): Likewise.
(SECONDARY_INPUT_RELOAD_CLASS): Likewise.
(HAVE_SECONDARY_RELOADS): Define.
* sh.md (reload_indf): Rename to:
(reload_indf__frn).
(reload_outdf): Rename to:
(reload_outdf__RnFRm).
(reload_insf): Rename to:
(reload_insf__frn).
(reload_insi): Rename to:
(reload_insi__i_fpul).
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@107468 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r-- | gcc/ChangeLog | 59 | ||||
-rw-r--r-- | gcc/config/sh/sh-protos.h | 4 | ||||
-rw-r--r-- | gcc/config/sh/sh.c | 103 | ||||
-rw-r--r-- | gcc/config/sh/sh.h | 4 | ||||
-rw-r--r-- | gcc/config/sh/sh.md | 10 | ||||
-rw-r--r-- | gcc/doc/md.texi | 8 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 117 | ||||
-rw-r--r-- | gcc/optabs.c | 2 | ||||
-rw-r--r-- | gcc/regclass.c | 98 | ||||
-rw-r--r-- | gcc/reload.c | 255 | ||||
-rw-r--r-- | gcc/reload.h | 16 | ||||
-rw-r--r-- | gcc/reload1.c | 225 | ||||
-rw-r--r-- | gcc/target-def.h | 5 | ||||
-rw-r--r-- | gcc/target.h | 20 | ||||
-rw-r--r-- | gcc/targhooks.c | 86 | ||||
-rw-r--r-- | gcc/targhooks.h | 3 |
16 files changed, 693 insertions, 322 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bb00ed2ea7c..5d869abcd27 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,62 @@ +2005-11-24 J"orn Rennecke <joern.rennecke@st.com> + PR target/21623: + + * regclass.c (FORBIDDEN_INC_DEC_CLASSES): Remove + SECONDARY_INPUT_RELOAD_CLASS and SECONDARY_OUTPUT_RELOAD_CLASS tests. + (init_fake_stack_mems): Remove HAVE_SECONDARY_RELOADS test. + (memory_move_secondary_cost, init_reg_autoinc): Remove + SECONDARY_INPUT_RELOAD_CLASS / SECONDARY_OUTPUT_RELOAD_CLASS tests. + Replace SECONDARY_{IN,OUT}PUT_RELOAD_CLASS use with + secondary_reload_class call. + (copy_cost): Likewise. Add new parameter prev_sri. Changed all + callers. + * reload.c (entire file): Remove HAVE_SECONDARY_RELOADS checks. + (push_secondary_reload): Use secondary_reload target hook. + (secondary_reload_class, scratch_reload_class): New functions. + (push_reload): Remove SECONDARY_INPUT_RELOAD_CLASS and + SECONDARY_OUTPUT_RELOAD_CLASS tests. Replace + SECONDARY_{IN,OUT}PUT_RELOAD_CLASS use with secondary_reload_class call. + * reload.h (HAVE_SECONDARY_RELOADS): Don't define nor test. + (secondary_reload_class, scratch_reload_class): Declare. + * reload1.c: Include target.h. + (reload_adjust_reg_for_temp): New function. + (reload_adjust_reg_for_icode): Likewise. + (choose_reload_regs): Remove SECONDARY_INPUT_RELOAD_CLASS test. + Replace SECONDARY_INPUT_RELOAD_CLASS use with secondary_reload_class + call. + (emit_input_reload_insns): Likewise. Rewrite secondary reload checks + for inheritance. Support case when both secondary & tertiary reloads + are for intermediate registers. + (emit_output_reload_insns): Replace SECONDARY_OUTPUT_RELOAD_CLASS use + with secondary_reload_class call. Support case when both secondary + & tertiary reloads are for intermediate registers. + * target-def.h (TARGET_SECONDARY_RELOAD): Provide default definition. + (TARGET_INITIALIZER) Add TARGET_SECONDARY_RELOAD. + * target.h (secondary_reload_info): New struct / typedef. + (struct gcc_target): New member secondary_reload. + * targhooks.c Include reload.h, optabs.h and recog.h. + (default_secondary_reload): New function. + * targhooks.h (default_secondary_reload): Declare. + * doc/tm.texi: Document secondary_reload target hook. Update + description of SECONDARY_*RELOAD_CLASS and reload_{in,out}<mode>. + * doc/md.texi: Likewise. + + * sh-protos.h (sh_secondary_reload): Declare. + * sh.c (TARGET_SECONDARY_RELOAD): Override. + (sh_secondary_reload): New function. + * sh.h (SECONDARY_INOUT_RELOAD_CLASS): Don't define. + (SECONDARY_OUTPUT_RELOAD_CLASS): Likewise. + (SECONDARY_INPUT_RELOAD_CLASS): Likewise. + (HAVE_SECONDARY_RELOADS): Define. + * sh.md (reload_indf): Rename to: + (reload_indf__frn). + (reload_outdf): Rename to: + (reload_outdf__RnFRm). + (reload_insf): Rename to: + (reload_insf__frn). + (reload_insi): Rename to: + (reload_insi__i_fpul). + 2005-11-24 Uros Bizjak <uros@kss-loka.si> * configure.ac: Require at least texinfo 4.4. diff --git a/gcc/config/sh/sh-protos.h b/gcc/config/sh/sh-protos.h index 564c1bee2cf..a0661545b56 100644 --- a/gcc/config/sh/sh-protos.h +++ b/gcc/config/sh/sh-protos.h @@ -165,6 +165,10 @@ extern int shmedia_cleanup_truncate (rtx *, void *); extern int sh_contains_memref_p (rtx); extern rtx shmedia_prepare_call_address (rtx fnaddr, int is_sibcall); +struct secondary_reload_info; +extern enum reg_class sh_secondary_reload (bool, rtx, enum reg_class, + enum machine_mode, + struct secondary_reload_info *); #endif /* ! GCC_SH_PROTOS_H */ diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index e11d557d3c1..1bcf81d8bdc 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -500,6 +500,9 @@ static int hard_regs_intersect_p (HARD_REG_SET *, HARD_REG_SET *); #define TARGET_ADJUST_UNROLL_MAX sh_adjust_unroll_max #endif +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD sh_secondary_reload + struct gcc_target targetm = TARGET_INITIALIZER; /* Implement TARGET_HANDLE_OPTION. */ @@ -10672,6 +10675,106 @@ shmedia_prepare_call_address (rtx fnaddr, int is_sibcall) return fnaddr; } +enum reg_class +sh_secondary_reload (bool in_p, rtx x, enum reg_class class, + enum machine_mode mode, secondary_reload_info *sri) +{ + if (in_p) + { + if (REGCLASS_HAS_FP_REG (class) + && ! TARGET_SHMEDIA + && immediate_operand ((x), mode) + && ! ((fp_zero_operand (x) || fp_one_operand (x)) + && mode == SFmode && fldi_ok ())) + switch (mode) + { + case SFmode: + sri->icode = CODE_FOR_reload_insf__frn; + return NO_REGS; + case DFmode: + sri->icode = CODE_FOR_reload_indf__frn; + return NO_REGS; + case SImode: + /* ??? If we knew that we are in the appropriate mode - + single precision - we could use a reload pattern directly. */ + return FPUL_REGS; + default: + abort (); + } + if (class == FPUL_REGS + && ((GET_CODE (x) == REG + && (REGNO (x) == MACL_REG || REGNO (x) == MACH_REG + || REGNO (x) == T_REG)) + || GET_CODE (x) == PLUS)) + return GENERAL_REGS; + if (class == FPUL_REGS && immediate_operand (x, mode)) + { + if (GET_CODE (x) == CONST_INT && CONST_OK_FOR_I08 (INTVAL (x))) + return GENERAL_REGS; + sri->icode = CODE_FOR_reload_insi__i_fpul; + return NO_REGS; + } + if (class == FPSCR_REGS + && ((GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) + || (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PLUS))) + return GENERAL_REGS; + if (REGCLASS_HAS_FP_REG (class) + && TARGET_SHMEDIA + && immediate_operand (x, mode) + && x != CONST0_RTX (GET_MODE (x)) + && GET_MODE (x) != V4SFmode) + return GENERAL_REGS; + if ((mode == QImode || mode == HImode) + && TARGET_SHMEDIA && inqhi_operand (x, mode)) + { + sri->icode = ((mode == QImode) + ? CODE_FOR_reload_inqi : CODE_FOR_reload_inhi); + return NO_REGS; + } + if (TARGET_SHMEDIA && class == GENERAL_REGS + && (GET_CODE (x) == LABEL_REF || PIC_DIRECT_ADDR_P (x))) + return TARGET_REGS; + } /* end of input-only processing. */ + + if (((REGCLASS_HAS_FP_REG (class) + && (GET_CODE (x) == REG + && (GENERAL_OR_AP_REGISTER_P (REGNO (x)) + || (FP_REGISTER_P (REGNO (x)) && mode == SImode + && TARGET_FMOVD)))) + || (REGCLASS_HAS_GENERAL_REG (class) + && GET_CODE (x) == REG + && FP_REGISTER_P (REGNO (x)))) + && ! TARGET_SHMEDIA + && (mode == SFmode || mode == SImode)) + return FPUL_REGS; + if ((class == FPUL_REGS + || (REGCLASS_HAS_FP_REG (class) + && ! TARGET_SHMEDIA && mode == SImode)) + && (GET_CODE (x) == MEM + || (GET_CODE (x) == REG + && (REGNO (x) >= FIRST_PSEUDO_REGISTER + || REGNO (x) == T_REG + || system_reg_operand (x, VOIDmode))))) + { + if (class == FPUL_REGS) + return GENERAL_REGS; + return FPUL_REGS; + } + if ((class == TARGET_REGS + || (TARGET_SHMEDIA && class == SIBCALL_REGS)) + && !EXTRA_CONSTRAINT_Csy (x) + && (GET_CODE (x) != REG || ! GENERAL_REGISTER_P (REGNO (x)))) + return GENERAL_REGS; + if ((class == MAC_REGS || class == PR_REGS) + && GET_CODE (x) == REG && ! GENERAL_REGISTER_P (REGNO (x)) + && class != REGNO_REG_CLASS (REGNO (x))) + return GENERAL_REGS; + if (class != GENERAL_REGS && GET_CODE (x) == REG + && TARGET_REGISTER_P (REGNO (x))) + return GENERAL_REGS; + return NO_REGS; +} + enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT; /* This defines the storage for the variable part of a -mboard= option. diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h index 7f541fbda8b..44b9c93c1f7 100644 --- a/gcc/config/sh/sh.h +++ b/gcc/config/sh/sh.h @@ -1604,6 +1604,7 @@ extern enum reg_class reg_class_from_letter[]; ? GENERAL_REGS \ : (CLASS)) \ +#if 0 #define SECONDARY_INOUT_RELOAD_CLASS(CLASS,MODE,X,ELSE) \ ((((REGCLASS_HAS_FP_REG (CLASS) \ && (GET_CODE (X) == REG \ @@ -1675,6 +1676,9 @@ extern enum reg_class reg_class_from_letter[]; && (GET_CODE (X) == LABEL_REF || PIC_DIRECT_ADDR_P (X))) \ ? TARGET_REGS \ : SECONDARY_INOUT_RELOAD_CLASS((CLASS),(MODE),(X), NO_REGS)) +#else +#define HAVE_SECONDARY_RELOADS +#endif /* Return the maximum number of consecutive registers needed to represent mode MODE in a register of class CLASS. diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 42e05bce806..91e8ff834e8 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -5845,15 +5845,15 @@ label: (clobber (scratch:SI))])] "") -(define_expand "reload_indf" - [(parallel [(set (match_operand:DF 0 "register_operand" "=f") +(define_expand "reload_indf__frn" + [(parallel [(set (match_operand:DF 0 "register_operand" "=a") (match_operand:DF 1 "immediate_operand" "FQ")) (use (reg:PSI FPSCR_REG)) (clobber (match_operand:SI 2 "register_operand" "=&z"))])] "TARGET_SH1" "") -(define_expand "reload_outdf" +(define_expand "reload_outdf__RnFRm" [(parallel [(set (match_operand:DF 0 "register_operand" "=r,f") (match_operand:DF 1 "register_operand" "af,r")) (clobber (match_operand:SI 2 "register_operand" "=&y,y"))])] @@ -6475,7 +6475,7 @@ label: [(set_attr "length" "0") (set_attr "type" "nil")]) -(define_expand "reload_insf" +(define_expand "reload_insf__frn" [(parallel [(set (match_operand:SF 0 "register_operand" "=a") (match_operand:SF 1 "immediate_operand" "FQ")) (use (reg:PSI FPSCR_REG)) @@ -6483,7 +6483,7 @@ label: "TARGET_SH1" "") -(define_expand "reload_insi" +(define_expand "reload_insi__i_fpul" [(parallel [(set (match_operand:SI 0 "fpul_operand" "=y") (match_operand:SI 1 "immediate_operand" "i")) (clobber (match_operand:SI 2 "register_operand" "=&z"))])] diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 115de4a2aba..54ad659cb5b 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -2920,10 +2920,7 @@ If a scratch register is required to move an object to or from memory, it can be allocated using @code{gen_reg_rtx} prior to life analysis. If there are cases which need scratch registers during or after reload, -you must define @code{SECONDARY_INPUT_RELOAD_CLASS} and/or -@code{SECONDARY_OUTPUT_RELOAD_CLASS} to detect them, and provide -patterns @samp{reload_in@var{m}} or @samp{reload_out@var{m}} to handle -them. @xref{Register Classes}. +you must provide an appropriate secondary_reload target hook. @findex no_new_pseudos The global variable @code{no_new_pseudos} can be used to determine if it @@ -2953,6 +2950,9 @@ reload into a floating point register. @cindex @code{reload_out} instruction pattern @item @samp{reload_in@var{m}} @itemx @samp{reload_out@var{m}} +These named patterns have been obsoleted by the target hook +@code{secondary_reload}. + Like @samp{mov@var{m}}, but used when a scratch register is required to move between operand 0 and operand 1. Operand 2 describes the scratch register. See the discussion of the @code{SECONDARY_RELOAD_CLASS} diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 09279ec7af5..b736855aaca 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -2397,32 +2397,114 @@ Don't define this macro unless the target machine has limitations which require the macro to do something nontrivial. @end defmac -@defmac SECONDARY_RELOAD_CLASS (@var{class}, @var{mode}, @var{x}) -@defmacx SECONDARY_INPUT_RELOAD_CLASS (@var{class}, @var{mode}, @var{x}) -@defmacx SECONDARY_OUTPUT_RELOAD_CLASS (@var{class}, @var{mode}, @var{x}) +@deftypefn {Target Hook} enum reg_class TARGET_SECONDARY_RELOAD (bool @var{in_p}, rtx @var{x}, enum reg_class @var{reload_class}, enum machine_mode @var{reload_mode}, secondary_reload_info *@var{sri}) Many machines have some registers that cannot be copied directly to or from memory or even from other types of registers. An example is the @samp{MQ} register, which on most machines, can only be copied to or -from general registers, but not memory. Some machines allow copying all -registers to and from memory, but require a scratch register for stores -to some memory locations (e.g., those with symbolic address on the RT, -and those with certain symbolic address on the SPARC when compiling -PIC)@. In some cases, both an intermediate and a scratch register are -required. +from general registers, but not memory. Below, we shall be using the +term 'intermediate register' when a move operation cannot be performed +directly, but has to be done by copying the source into the intermediate +register first, and then copying the intermediate register to the +destination. An intermediate register always has the same mode as +source and destination. Since it holds the actual value being copied, +reload might apply optimizations to re-use an intermediate register +and eliding the copy from the source when it can determine that the +intermediate register still holds the required value. + +Another kind of secondary reload is required on some machines which +allow copying all registers to and from memory, but require a scratch +register for stores to some memory locations (e.g., those with symbolic +address on the RT, and those with certain symbolic address on the SPARC +when compiling PIC)@. Scratch registers need not have the same mode +as the value being copied, and usually hold a different value that +that being copied. Special patterns in the md file are needed to +describe how the copy is performed with the help of the scratch register; +these patterns also describe the number, register class(es) and mode(s) +of the scratch register(s). + +In some cases, both an intermediate and a scratch register are required. + +For input reloads, this target hook is called with nonzero @var{in_p}, +and @var{x} is an rtx that needs to be copied to a register in of class +@var{reload_class} in @var{reload_mode}. For output reloads, this target +hook is called with zero @var{in_p}, and a register of class @var{reload_mode} +needs to be copied to rtx @var{x} in @var{reload_mode}. + +If copying a register of @var{reload_class} from/to @var{x} requires +an intermediate register, the hook @code{secondary_reload} should +return the register class required for this intermediate register. +If no intermediate register is required, it should return NO_REGS. +If more than one intermediate register is required, describe the one +that is closest in the copy chain to the reload register. + +If scratch registers are needed, you also have to describe how to +perform the copy from/to the reload register to/from this +closest intermediate register. Or if no intermediate register is +required, but still a scratch register is needed, describe the +copy from/to the reload register to/from the reload operand @var{x}. + +You do this by setting @code{sri->icode} to the instruction code of a pattern +in the md file which performs the move. Operands 0 and 1 are the output +and input of this copy, respectively. Operands from operand 2 onward are +for scratch operands. These scratch operands must have a mode, and a +single-register-class +@c [later: or memory] +output constraint. + +When an intermediate register is used, the @code{secondary_reload} +hook will be called again to determine how to copy the intermediate +register to/from the reload operand @var{x}, so your hook must also +have code to handle the register class of the intermediate operand. + +@c [For later: maybe we'll allow multi-alternative reload patterns - +@c the port maintainer could name a mov<mode> pattern that has clobbers - +@c and match the constraints of input and output to determine the required +@c alternative. A restriction would be that constraints used to match +@c against reloads registers would have to be written as register class +@c constraints, or we need a new target macro / hook that tells us if an +@c arbitrary constraint can match an unknown register of a given class. +@c Such a macro / hook would also be useful in other places.] + + +@var{x} might be a pseudo-register or a @code{subreg} of a +pseudo-register, which could either be in a hard register or in memory. +Use @code{true_regnum} to find out; it will return @minus{}1 if the pseudo is +in memory and the hard register number if it is in a register. + +Scratch operands in memory (constraint @code{"=m"} / @code{"=&m"}) are +currently not supported. For the time being, you will have to continue +to use @code{SECONDARY_MEMORY_NEEDED} for that purpose. + +@code{copy_cost} also uses this target hook to find out how values are +copied. If you want it to include some extra cost for the need to allocate +(a) scratch register(s), set @code{sri->extra_cost} to the additional cost. +Or if two dependent moves are supposed to have a lower cost than the sum +of the individual moves due to expected fortuitous scheduling and/or special +forwarding logic, you can set @code{sri->extra_cost} to a negative amount. +@end deftypefn + +@defmac SECONDARY_RELOAD_CLASS (@var{class}, @var{mode}, @var{x}) +@defmacx SECONDARY_INPUT_RELOAD_CLASS (@var{class}, @var{mode}, @var{x}) +@defmacx SECONDARY_OUTPUT_RELOAD_CLASS (@var{class}, @var{mode}, @var{x}) +These macros are obsolete, new ports should use the target hook +@code{TARGET_SECONDARY_RELOAD} instead. -You should define these macros to indicate to the reload phase that it may +These are obsolete macros, replaced by the @code{TARGET_SECONDARY_RELOAD} +target hook. Older ports still define these macros to indicate to the +reload phase that it may need to allocate at least one register for a reload in addition to the register to contain the data. Specifically, if copying @var{x} to a register @var{class} in @var{mode} requires an intermediate register, -you should define @code{SECONDARY_INPUT_RELOAD_CLASS} to return the +you were supposed to define @code{SECONDARY_INPUT_RELOAD_CLASS} to return the largest register class all of whose registers can be used as intermediate registers or scratch registers. If copying a register @var{class} in @var{mode} to @var{x} requires an intermediate or scratch register, @code{SECONDARY_OUTPUT_RELOAD_CLASS} -should be defined to return the largest register class required. If the -requirements for input and output reloads are the same, the macro -@code{SECONDARY_RELOAD_CLASS} should be used instead of defining both +was supposed to be defined be defined to return the largest register +class required. If the +requirements for input and output reloads were the same, the macro +@code{SECONDARY_RELOAD_CLASS} should have been used instead of defining both macros identically. The values returned by these macros are often @code{GENERAL_REGS}. @@ -2432,14 +2514,15 @@ can be directly copied to or from a register of @var{class} in macro if it would always return @code{NO_REGS}. If a scratch register is required (either with or without an -intermediate register), you should define patterns for +intermediate register), you were supposed to define patterns for @samp{reload_in@var{m}} or @samp{reload_out@var{m}}, as required -(@pxref{Standard Names}. These patterns, which will normally be +(@pxref{Standard Names}. These patterns, which were normally implemented with a @code{define_expand}, should be similar to the @samp{mov@var{m}} patterns, except that operand 2 is the scratch register. -Define constraints for the reload register and scratch register that +These patterns need constraints for the reload register and scratch +register that contain a single register class. If the original reload register (whose class is @var{class}) can meet the constraint given in the pattern, the value returned by these macros is used for the class of the scratch diff --git a/gcc/optabs.c b/gcc/optabs.c index 2b0e02c8601..9ef0d5afbdb 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -5191,9 +5191,7 @@ init_optabs (void) sync_lock_test_and_set[i] = CODE_FOR_nothing; sync_lock_release[i] = CODE_FOR_nothing; -#ifdef HAVE_SECONDARY_RELOADS reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing; -#endif } /* Fill in the optabs with the insns we support. */ diff --git a/gcc/regclass.c b/gcc/regclass.c index d828953702e..6839f67f070 100644 --- a/gcc/regclass.c +++ b/gcc/regclass.c @@ -46,6 +46,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "ggc.h" #include "timevar.h" #include "hashtab.h" +#include "target.h" static void init_reg_sets_1 (void); static void init_reg_autoinc (void); @@ -53,12 +54,11 @@ static void init_reg_autoinc (void); /* If we have auto-increment or auto-decrement and we can have secondary reloads, we are not allowed to use classes requiring secondary reloads for pseudos auto-incremented since reload can't handle it. */ - -#ifdef AUTO_INC_DEC -#if defined(SECONDARY_INPUT_RELOAD_CLASS) || defined(SECONDARY_OUTPUT_RELOAD_CLASS) +/* We leave it to target hooks to decide if we have secondary reloads, so + assume that we might have them. */ +#if defined(AUTO_INC_DEC) /* */ #define FORBIDDEN_INC_DEC_CLASSES #endif -#endif /* Register tables used by many passes. */ @@ -597,17 +597,14 @@ init_regs (void) void init_fake_stack_mems (void) { -#ifdef HAVE_SECONDARY_RELOADS { int i; for (i = 0; i < MAX_MACHINE_MODE; i++) top_of_stack[i] = gen_rtx_MEM (i, stack_pointer_rtx); } -#endif } -#ifdef HAVE_SECONDARY_RELOADS /* Compute extra cost of moving registers to/from memory due to reloads. Only needed if secondary reloads are required for memory moves. */ @@ -622,22 +619,7 @@ memory_move_secondary_cost (enum machine_mode mode, enum reg_class class, int in rtx mem ATTRIBUTE_UNUSED = top_of_stack[(int) mode]; - if (in) - { -#ifdef SECONDARY_INPUT_RELOAD_CLASS - altclass = SECONDARY_INPUT_RELOAD_CLASS (class, mode, mem); -#else - altclass = NO_REGS; -#endif - } - else - { -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - altclass = SECONDARY_OUTPUT_RELOAD_CLASS (class, mode, mem); -#else - altclass = NO_REGS; -#endif - } + altclass = secondary_reload_class (in ? 1 : 0, class, mode, mem); if (altclass == NO_REGS) return 0; @@ -661,7 +643,6 @@ memory_move_secondary_cost (enum machine_mode mode, enum reg_class class, int in secondary reload. */ return memory_move_secondary_cost (mode, altclass, in) + partial_cost; } -#endif /* Return a machine mode that is legitimate for hard reg REGNO and large enough to save nregs. If we can't find one, return VOIDmode. @@ -878,7 +859,8 @@ static void dump_regclass (FILE *); static void record_reg_classes (int, int, rtx *, enum machine_mode *, const char **, rtx, struct costs *, struct reg_pref *); -static int copy_cost (rtx, enum machine_mode, enum reg_class, int); +static int copy_cost (rtx, enum machine_mode, enum reg_class, int, + secondary_reload_info *); static void record_address_regs (rtx, enum reg_class, int); #ifdef FORBIDDEN_INC_DEC_CLASSES static int auto_inc_dec_reg_p (rtx, enum machine_mode); @@ -1175,6 +1157,8 @@ init_reg_autoinc (void) m = (enum machine_mode) ((int) m + 1)) if (HARD_REGNO_MODE_OK (j, m)) { + enum reg_class base_class = MODE_BASE_REG_CLASS (VOIDmode); + PUT_MODE (r, m); /* If a register is not directly suitable for an @@ -1182,21 +1166,8 @@ init_reg_autoinc (void) requires secondary reloads, disallow its class from being used in such addresses. */ - if ((0 -#ifdef SECONDARY_RELOAD_CLASS - || (SECONDARY_RELOAD_CLASS (MODE_BASE_REG_CLASS (VOIDmode), m, r) - != NO_REGS) -#else -#ifdef SECONDARY_INPUT_RELOAD_CLASS - || (SECONDARY_INPUT_RELOAD_CLASS (MODE_BASE_REG_CLASS (VOIDmode), m, r) - != NO_REGS) -#endif -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - || (SECONDARY_OUTPUT_RELOAD_CLASS (MODE_BASE_REG_CLASS (VOIDmode), m, r) - != NO_REGS) -#endif -#endif - ) + if ((secondary_reload_class (1, base_class, m, r) + || secondary_reload_class (1, base_class, m, r)) && ! auto_inc_dec_reg_p (r, m)) forbidden_inc_dec_class[i] = 1; } @@ -1473,7 +1444,10 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, operand to the register used for the other operand. */ else if (classes[j] != NO_REGS) - alt_cost += copy_cost (op, mode, classes[j], 1), win = 1; + { + alt_cost += copy_cost (op, mode, classes[j], 1, NULL); + win = 1; + } } else if (!REG_P (ops[j]) || REGNO (ops[j]) < FIRST_PSEUDO_REGISTER) @@ -1491,7 +1465,7 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, operand. */ else - alt_cost += copy_cost (ops[j], mode, classes[j], 1); + alt_cost += copy_cost (ops[j], mode, classes[j], 1, NULL); } else { @@ -1777,10 +1751,10 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, else if (classes[i] != NO_REGS) { if (recog_data.operand_type[i] != OP_OUT) - alt_cost += copy_cost (op, mode, classes[i], 1); + alt_cost += copy_cost (op, mode, classes[i], 1, NULL); if (recog_data.operand_type[i] != OP_IN) - alt_cost += copy_cost (op, mode, classes[i], 0); + alt_cost += copy_cost (op, mode, classes[i], 0, NULL); } /* The only other way this alternative can be used is if this is a @@ -1878,12 +1852,11 @@ record_reg_classes (int n_alts, int n_ops, rtx *ops, X must not be a pseudo. */ static int -copy_cost (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED, - enum reg_class class, int to_p ATTRIBUTE_UNUSED) +copy_cost (rtx x, enum machine_mode mode, enum reg_class class, int to_p, + secondary_reload_info *prev_sri) { -#ifdef HAVE_SECONDARY_RELOADS enum reg_class secondary_class = NO_REGS; -#endif + secondary_reload_info sri; /* If X is a SCRATCH, there is actually nothing to move since we are assuming optimal allocation. */ @@ -1894,40 +1867,33 @@ copy_cost (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED, /* Get the class we will actually use for a reload. */ class = PREFERRED_RELOAD_CLASS (x, class); -#ifdef HAVE_SECONDARY_RELOADS - /* If we need a secondary reload (we assume here that we are using - the secondary reload as an intermediate, not a scratch register), the + /* If we need a secondary reload for an intermediate, the cost is that to load the input into the intermediate register, then - to copy them. We use a special value of TO_P to avoid recursion. */ + to copy it. */ -#ifdef SECONDARY_INPUT_RELOAD_CLASS - if (to_p == 1) - secondary_class = SECONDARY_INPUT_RELOAD_CLASS (class, mode, x); -#endif - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - if (! to_p) - secondary_class = SECONDARY_OUTPUT_RELOAD_CLASS (class, mode, x); -#endif + sri.prev_sri = prev_sri; + sri.extra_cost = 0; + secondary_class = targetm.secondary_reload (to_p, x, class, mode, &sri); if (secondary_class != NO_REGS) return (move_cost[mode][(int) secondary_class][(int) class] - + copy_cost (x, mode, secondary_class, 2)); -#endif /* HAVE_SECONDARY_RELOADS */ + + sri.extra_cost + + copy_cost (x, mode, secondary_class, to_p, &sri)); /* For memory, use the memory move cost, for (hard) registers, use the cost to move between the register classes, and use 2 for everything else (constants). */ if (MEM_P (x) || class == NO_REGS) - return MEMORY_MOVE_COST (mode, class, to_p); + return sri.extra_cost + MEMORY_MOVE_COST (mode, class, to_p); else if (REG_P (x)) - return move_cost[mode][(int) REGNO_REG_CLASS (REGNO (x))][(int) class]; + return (sri.extra_cost + + move_cost[mode][(int) REGNO_REG_CLASS (REGNO (x))][(int) class]); else /* If this is a constant, we may eventually want to call rtx_cost here. */ - return COSTS_N_INSNS (1); + return sri.extra_cost + COSTS_N_INSNS (1); } /* Record the pseudo registers we must reload into hard registers diff --git a/gcc/reload.c b/gcc/reload.c index 1c1a441c345..71882577686 100644 --- a/gcc/reload.c +++ b/gcc/reload.c @@ -242,11 +242,9 @@ static int output_reloadnum; ? RELOAD_FOR_OUTADDR_ADDRESS \ : (type))) -#ifdef HAVE_SECONDARY_RELOADS static int push_secondary_reload (int, rtx, int, int, enum reg_class, enum machine_mode, enum reload_type, - enum insn_code *); -#endif + enum insn_code *, secondary_reload_info *); static enum reg_class find_valid_class (enum machine_mode, enum machine_mode, int, unsigned int); static int reload_inner_reg_of_subreg (rtx, enum machine_mode, int); @@ -283,8 +281,6 @@ static int refers_to_mem_for_reload_p (rtx); static int refers_to_regno_for_reload_p (unsigned int, unsigned int, rtx, rtx *); -#ifdef HAVE_SECONDARY_RELOADS - /* Determine if any secondary reloads are needed for loading (if IN_P is nonzero) or storing (if IN_P is zero) X to or from a reload register of register class RELOAD_CLASS in mode RELOAD_MODE. If secondary reloads @@ -298,16 +294,18 @@ static int push_secondary_reload (int in_p, rtx x, int opnum, int optional, enum reg_class reload_class, enum machine_mode reload_mode, enum reload_type type, - enum insn_code *picode) + enum insn_code *picode, secondary_reload_info *prev_sri) { enum reg_class class = NO_REGS; + enum reg_class scratch_class; enum machine_mode mode = reload_mode; enum insn_code icode = CODE_FOR_nothing; - enum reg_class t_class = NO_REGS; - enum machine_mode t_mode = VOIDmode; - enum insn_code t_icode = CODE_FOR_nothing; + enum insn_code t_icode; enum reload_type secondary_type; int s_reload, t_reload = -1; + const char *scratch_constraint; + char letter; + secondary_reload_info sri; if (type == RELOAD_FOR_INPUT_ADDRESS || type == RELOAD_FOR_OUTPUT_ADDRESS @@ -339,36 +337,21 @@ push_secondary_reload (int in_p, rtx x, int opnum, int optional, && reg_equiv_mem[REGNO (x)] != 0) x = reg_equiv_mem[REGNO (x)]; -#ifdef SECONDARY_INPUT_RELOAD_CLASS - if (in_p) - class = SECONDARY_INPUT_RELOAD_CLASS (reload_class, reload_mode, x); -#endif - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - if (! in_p) - class = SECONDARY_OUTPUT_RELOAD_CLASS (reload_class, reload_mode, x); -#endif + sri.icode = CODE_FOR_nothing; + sri.prev_sri = prev_sri; + class = targetm.secondary_reload (in_p, x, reload_class, reload_mode, &sri); + icode = sri.icode; /* If we don't need any secondary registers, done. */ - if (class == NO_REGS) + if (class == NO_REGS && icode == CODE_FOR_nothing) return -1; - /* Get a possible insn to use. If the predicate doesn't accept X, don't - use the insn. */ + if (class != NO_REGS) + t_reload = push_secondary_reload (in_p, x, opnum, optional, class, + reload_mode, type, &t_icode, &sri); - icode = (in_p ? reload_in_optab[(int) reload_mode] - : reload_out_optab[(int) reload_mode]); - - if (icode != CODE_FOR_nothing - && insn_data[(int) icode].operand[in_p].predicate - && (! (insn_data[(int) icode].operand[in_p].predicate) (x, reload_mode))) - icode = CODE_FOR_nothing; - - /* If we will be using an insn, see if it can directly handle the reload - register we will be using. If it can, the secondary reload is for a - scratch register. If it can't, we will use the secondary reload for - an intermediate register and require a tertiary reload for the scratch - register. */ + /* If we will be using an insn, the secondary reload is for a + scratch register. */ if (icode != CODE_FOR_nothing) { @@ -377,45 +360,29 @@ push_secondary_reload (int in_p, rtx x, int opnum, int optional, in operand 1. Outputs should have an initial "=", which we must skip. */ - enum reg_class insn_class; - - if (insn_data[(int) icode].operand[!in_p].constraint[0] == 0) - insn_class = ALL_REGS; - else - { - const char *insn_constraint - = &insn_data[(int) icode].operand[!in_p].constraint[in_p]; - char insn_letter = *insn_constraint; - insn_class - = (insn_letter == 'r' ? GENERAL_REGS - : REG_CLASS_FROM_CONSTRAINT ((unsigned char) insn_letter, - insn_constraint)); - - gcc_assert (insn_class != NO_REGS); - gcc_assert (!in_p - || insn_data[(int) icode].operand[!in_p].constraint[0] - == '='); - } - - /* The scratch register's constraint must start with "=&". */ - gcc_assert (insn_data[(int) icode].operand[2].constraint[0] == '=' - && insn_data[(int) icode].operand[2].constraint[1] == '&'); - - if (reg_class_subset_p (reload_class, insn_class)) - mode = insn_data[(int) icode].operand[2].mode; - else - { - const char *t_constraint - = &insn_data[(int) icode].operand[2].constraint[2]; - char t_letter = *t_constraint; - class = insn_class; - t_mode = insn_data[(int) icode].operand[2].mode; - t_class = (t_letter == 'r' ? GENERAL_REGS - : REG_CLASS_FROM_CONSTRAINT ((unsigned char) t_letter, - t_constraint)); - t_icode = icode; - icode = CODE_FOR_nothing; - } + /* ??? It would be useful to be able to handle only two, or more than + three, operands, but for now we can only handle the case of having + exactly three: output, input and one temp/scratch. */ + gcc_assert (insn_data[(int) icode].n_operands == 3); + + /* ??? We currently have no way to represent a reload that needs + an icode to reload from an intermediate tertiaty reload register. + We should probably have a new field in struct reload to tag a + chain of scratch operand reloads onto. */ + gcc_assert (class == NO_REGS); + + scratch_constraint = insn_data[(int) icode].operand[2].constraint; + gcc_assert (*scratch_constraint == '='); + scratch_constraint++; + if (*scratch_constraint == '&') + scratch_constraint++; + letter = *scratch_constraint; + scratch_class = (letter == 'r' ? GENERAL_REGS + : REG_CLASS_FROM_CONSTRAINT ((unsigned char) letter, + insn_constraint)); + + class = scratch_class; + mode = insn_data[(int) icode].operand[2].mode; } /* This case isn't valid, so fail. Reload is allowed to use the same @@ -435,68 +402,6 @@ push_secondary_reload (int in_p, rtx x, int opnum, int optional, gcc_assert (!in_p || class != reload_class || icode != CODE_FOR_nothing || t_icode != CODE_FOR_nothing); - /* If we need a tertiary reload, see if we have one we can reuse or else - make a new one. */ - - if (t_class != NO_REGS) - { - for (t_reload = 0; t_reload < n_reloads; t_reload++) - if (rld[t_reload].secondary_p - && (reg_class_subset_p (t_class, rld[t_reload].class) - || reg_class_subset_p (rld[t_reload].class, t_class)) - && ((in_p && rld[t_reload].inmode == t_mode) - || (! in_p && rld[t_reload].outmode == t_mode)) - && ((in_p && (rld[t_reload].secondary_in_icode - == CODE_FOR_nothing)) - || (! in_p &&(rld[t_reload].secondary_out_icode - == CODE_FOR_nothing))) - && (SMALL_REGISTER_CLASS_P (t_class) || SMALL_REGISTER_CLASSES) - && MERGABLE_RELOADS (secondary_type, - rld[t_reload].when_needed, - opnum, rld[t_reload].opnum)) - { - if (in_p) - rld[t_reload].inmode = t_mode; - if (! in_p) - rld[t_reload].outmode = t_mode; - - if (reg_class_subset_p (t_class, rld[t_reload].class)) - rld[t_reload].class = t_class; - - rld[t_reload].opnum = MIN (rld[t_reload].opnum, opnum); - rld[t_reload].optional &= optional; - rld[t_reload].secondary_p = 1; - if (MERGE_TO_OTHER (secondary_type, rld[t_reload].when_needed, - opnum, rld[t_reload].opnum)) - rld[t_reload].when_needed = RELOAD_OTHER; - } - - if (t_reload == n_reloads) - { - /* We need to make a new tertiary reload for this register class. */ - rld[t_reload].in = rld[t_reload].out = 0; - rld[t_reload].class = t_class; - rld[t_reload].inmode = in_p ? t_mode : VOIDmode; - rld[t_reload].outmode = ! in_p ? t_mode : VOIDmode; - rld[t_reload].reg_rtx = 0; - rld[t_reload].optional = optional; - rld[t_reload].inc = 0; - /* Maybe we could combine these, but it seems too tricky. */ - rld[t_reload].nocombine = 1; - rld[t_reload].in_reg = 0; - rld[t_reload].out_reg = 0; - rld[t_reload].opnum = opnum; - rld[t_reload].when_needed = secondary_type; - rld[t_reload].secondary_in_reload = -1; - rld[t_reload].secondary_out_reload = -1; - rld[t_reload].secondary_in_icode = CODE_FOR_nothing; - rld[t_reload].secondary_out_icode = CODE_FOR_nothing; - rld[t_reload].secondary_p = 1; - - n_reloads++; - } - } - /* See if we can reuse an existing secondary reload. */ for (s_reload = 0; s_reload < n_reloads; s_reload++) if (rld[s_reload].secondary_p @@ -581,7 +486,58 @@ push_secondary_reload (int in_p, rtx x, int opnum, int optional, *picode = icode; return s_reload; } -#endif /* HAVE_SECONDARY_RELOADS */ + +/* If a secondary reload is needed, return its class. If both an intermediate + register and a scratch register is needed, we return the class of the + intermediate register. */ +enum reg_class +secondary_reload_class (bool in_p, enum reg_class class, + enum machine_mode mode, rtx x) +{ + enum insn_code icode; + secondary_reload_info sri; + + sri.icode = CODE_FOR_nothing; + sri.prev_sri = NULL; + class = targetm.secondary_reload (in_p, x, class, mode, &sri); + icode = sri.icode; + + /* If there are no secondary reloads at all, we return NO_REGS. + If an intermediate register is needed, we return its class. */ + if (icode == CODE_FOR_nothing || class != NO_REGS) + return class; + + /* No intermediate register is needed, but we have a special reload + pattern, which we assume for now needs a scratch register. */ + return scratch_reload_class (icode); +} + +/* ICODE is the insn_code of a reload pattern. Check that it has exactly + three operands, verify that operand 2 is an output operand, and return + its register class. + ??? We'd like to be able to handle any pattern with at least 2 operands, + for zero or more scratch registers, but that needs more infrastructure. */ +enum reg_class +scratch_reload_class (enum insn_code icode) +{ + const char *scratch_constraint; + char scratch_letter; + enum reg_class class; + + gcc_assert (insn_data[(int) icode].n_operands == 3); + scratch_constraint = insn_data[(int) icode].operand[2].constraint; + gcc_assert (*scratch_constraint == '='); + scratch_constraint++; + if (*scratch_constraint == '&') + scratch_constraint++; + scratch_letter = *scratch_constraint; + if (scratch_letter == 'r') + return GENERAL_REGS; + class = REG_CLASS_FROM_CONSTRAINT ((unsigned char) scratch_letter, + scratch_constraint); + gcc_assert (class != NO_REGS); + return class; +} #ifdef SECONDARY_MEMORY_NEEDED @@ -1058,13 +1014,10 @@ push_reload (rtx in, rtx out, rtx *inloc, rtx *outloc, != (int) hard_regno_nregs[REGNO (SUBREG_REG (in))] [GET_MODE (SUBREG_REG (in))])) || ! HARD_REGNO_MODE_OK (subreg_regno (in), inmode))) -#ifdef SECONDARY_INPUT_RELOAD_CLASS - || (SECONDARY_INPUT_RELOAD_CLASS (class, inmode, in) != NO_REGS - && (SECONDARY_INPUT_RELOAD_CLASS (class, - GET_MODE (SUBREG_REG (in)), - SUBREG_REG (in)) + || (secondary_reload_class (1, class, inmode, in) != NO_REGS + && (secondary_reload_class (1, class, GET_MODE (SUBREG_REG (in)), + SUBREG_REG (in)) == NO_REGS)) -#endif #ifdef CANNOT_CHANGE_MODE_CLASS || (REG_P (SUBREG_REG (in)) && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER @@ -1154,13 +1107,10 @@ push_reload (rtx in, rtx out, rtx *inloc, rtx *outloc, != (int) hard_regno_nregs[REGNO (SUBREG_REG (out))] [GET_MODE (SUBREG_REG (out))])) || ! HARD_REGNO_MODE_OK (subreg_regno (out), outmode))) -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - || (SECONDARY_OUTPUT_RELOAD_CLASS (class, outmode, out) != NO_REGS - && (SECONDARY_OUTPUT_RELOAD_CLASS (class, - GET_MODE (SUBREG_REG (out)), - SUBREG_REG (out)) + || (secondary_reload_class (0, class, outmode, out) != NO_REGS + && (secondary_reload_class (0, class, GET_MODE (SUBREG_REG (out)), + SUBREG_REG (out)) == NO_REGS)) -#endif #ifdef CANNOT_CHANGE_MODE_CLASS || (REG_P (SUBREG_REG (out)) && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER @@ -1310,19 +1260,14 @@ push_reload (rtx in, rtx out, rtx *inloc, rtx *outloc, and IN or CLASS and OUT. Get the icode and push any required reloads needed for each of them if so. */ -#ifdef SECONDARY_INPUT_RELOAD_CLASS if (in != 0) secondary_in_reload = push_secondary_reload (1, in, opnum, optional, class, inmode, type, - &secondary_in_icode); -#endif - -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + &secondary_in_icode, NULL); if (out != 0 && GET_CODE (out) != SCRATCH) secondary_out_reload = push_secondary_reload (0, out, opnum, optional, class, outmode, - type, &secondary_out_icode); -#endif + type, &secondary_out_icode, NULL); /* We found no existing reload suitable for re-use. So add an additional reload. */ diff --git a/gcc/reload.h b/gcc/reload.h index d2f038b79b8..2792e9a5fd9 100644 --- a/gcc/reload.h +++ b/gcc/reload.h @@ -30,19 +30,10 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA SECONDARY_RELOAD_CLASS (CLASS, MODE, X) #endif -/* If either macro is defined, show that we need secondary reloads. */ -#if defined(SECONDARY_INPUT_RELOAD_CLASS) || defined(SECONDARY_OUTPUT_RELOAD_CLASS) -#define HAVE_SECONDARY_RELOADS -#endif - /* If MEMORY_MOVE_COST isn't defined, give it a default here. */ #ifndef MEMORY_MOVE_COST -#ifdef HAVE_SECONDARY_RELOADS #define MEMORY_MOVE_COST(MODE,CLASS,IN) \ (4 + memory_move_secondary_cost ((MODE), (CLASS), (IN))) -#else -#define MEMORY_MOVE_COST(MODE,CLASS,IN) 4 -#endif #endif extern int memory_move_secondary_cost (enum machine_mode, enum reg_class, int); @@ -252,6 +243,13 @@ extern void compute_use_by_pseudos (HARD_REG_SET *, regset); /* Functions from reload.c: */ +extern enum reg_class secondary_reload_class (bool, enum reg_class, + enum machine_mode, rtx); + +#ifdef GCC_INSN_CODES_H +extern enum reg_class scratch_reload_class (enum insn_code); +#endif + /* Return a memory location that will be used to copy X in mode MODE. If we haven't already made a location for this mode in this insn, call find_reloads_address on the location being returned. */ diff --git a/gcc/reload1.c b/gcc/reload1.c index 4a7d22dcfb5..970f5ec39bc 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -43,6 +43,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "toplev.h" #include "except.h" #include "tree.h" +#include "target.h" /* This file contains the reload pass of the compiler, which is run after register allocation has been done. It checks that @@ -5540,11 +5541,9 @@ choose_reload_regs (struct insn_chain *chain) enough. */ || ((REGISTER_MOVE_COST (mode, last_class, class) < MEMORY_MOVE_COST (mode, class, 1)) -#ifdef SECONDARY_INPUT_RELOAD_CLASS - && (SECONDARY_INPUT_RELOAD_CLASS (class, mode, - last_reg) + && (secondary_reload_class (1, class, mode, + last_reg) == NO_REGS) -#endif #ifdef SECONDARY_MEMORY_NEEDED && ! SECONDARY_MEMORY_NEEDED (last_class, class, mode) @@ -6205,6 +6204,55 @@ static rtx other_output_reload_insns[MAX_RECOG_OPERANDS]; static rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER]; static HARD_REG_SET reg_reloaded_died; +/* Check if *RELOAD_REG is suitable as an intermediate or scratch register + of class NEW_CLASS with mode NEW_MODE. Or alternatively, if alt_reload_reg + is nonzero, if that is suitable. On success, change *RELOAD_REG to the + adjusted register, and return true. Otherwise, return false. */ +static bool +reload_adjust_reg_for_temp (rtx *reload_reg, rtx alt_reload_reg, + enum reg_class new_class, + enum machine_mode new_mode) + +{ + rtx reg; + + for (reg = *reload_reg; reg; reg = alt_reload_reg, alt_reload_reg = 0) + { + unsigned regno = REGNO (reg); + + if (!TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], regno)) + continue; + if (GET_MODE (reg) != new_mode) + { + if (!HARD_REGNO_MODE_OK (regno, new_mode)) + continue; + if (hard_regno_nregs[regno][new_mode] + > hard_regno_nregs[regno][GET_MODE (reg)]) + continue; + reg = reload_adjust_reg_for_mode (reg, new_mode); + } + *reload_reg = reg; + return true; + } + return false; +} + +/* Check if *RELOAD_REG is suitable as a scratch register for the reload + pattern with insn_code ICODE, or alternatively, if alt_reload_reg is + nonzero, if that is suitable. On success, change *RELOAD_REG to the + adjusted register, and return true. Otherwise, return false. */ +static bool +reload_adjust_reg_for_icode (rtx *reload_reg, rtx alt_reload_reg, + enum insn_code icode) + +{ + enum reg_class new_class = scratch_reload_class (icode); + enum machine_mode new_mode = insn_data[(int) icode].operand[2].mode; + + return reload_adjust_reg_for_temp (reload_reg, alt_reload_reg, + new_class, new_mode); +} + /* Generate insns to perform reload RL, which is for the insn in CHAIN and has the number J. OLD contains the value to be used as input. */ @@ -6256,7 +6304,6 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, if (mode == VOIDmode) mode = rl->inmode; -#ifdef SECONDARY_INPUT_RELOAD_CLASS /* If we need a secondary register for this operation, see if the value is already in a register in that class. Don't do this if the secondary register will be used as a scratch @@ -6269,7 +6316,6 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, = find_equiv_reg (old, insn, rld[rl->secondary_in_reload].class, -1, NULL, 0, mode); -#endif /* If reloading from memory, see if there is a register that already holds the same value. If so, reload from there. @@ -6306,11 +6352,8 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, && (REGISTER_MOVE_COST (mode, REGNO_REG_CLASS (regno), rl->class) >= MEMORY_MOVE_COST (mode, rl->class, 1))) -#ifdef SECONDARY_INPUT_RELOAD_CLASS - || (SECONDARY_INPUT_RELOAD_CLASS (rl->class, - mode, oldequiv) + || (secondary_reload_class (1, rl->class, mode, oldequiv) != NO_REGS) -#endif #ifdef SECONDARY_MEMORY_NEEDED || SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (regno), rl->class, @@ -6496,7 +6539,6 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, /* We can't do that, so output an insn to load RELOADREG. */ -#ifdef SECONDARY_INPUT_RELOAD_CLASS /* If we have a secondary reload, pick up the secondary register and icode, if any. If OLDEQUIV and OLD are different or if this is an in-out reload, recompute whether or not we @@ -6511,11 +6553,13 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, if (! special && rl->secondary_in_reload >= 0) { rtx second_reload_reg = 0; + rtx third_reload_reg = 0; int secondary_reload = rl->secondary_in_reload; rtx real_oldequiv = oldequiv; rtx real_old = old; rtx tmp; enum insn_code icode; + enum insn_code tertiary_icode = CODE_FOR_nothing; /* If OLDEQUIV is a pseudo with a MEM, get the real MEM and similarly for OLD. @@ -6563,53 +6607,89 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, } second_reload_reg = rld[secondary_reload].reg_rtx; + if (rld[secondary_reload].secondary_in_reload >= 0) + { + int tertiary_reload = rld[secondary_reload].secondary_in_reload; + + third_reload_reg = rld[tertiary_reload].reg_rtx; + tertiary_icode = rld[secondary_reload].secondary_in_icode; + /* We'd have to add more code for quartary reloads. */ + gcc_assert (rld[tertiary_reload].secondary_in_reload < 0); + } icode = rl->secondary_in_icode; if ((old != oldequiv && ! rtx_equal_p (old, oldequiv)) || (rl->in != 0 && rl->out != 0)) { - enum reg_class new_class - = SECONDARY_INPUT_RELOAD_CLASS (rl->class, - mode, real_oldequiv); + secondary_reload_info sri, sri2; + enum reg_class new_class, new_t_class; - if (new_class == NO_REGS) + sri.icode = CODE_FOR_nothing; + sri.prev_sri = NULL; + new_class = targetm.secondary_reload (1, real_oldequiv, rl->class, + mode, &sri); + + if (new_class == NO_REGS && sri.icode == CODE_FOR_nothing) second_reload_reg = 0; - else + else if (new_class == NO_REGS) { - enum insn_code new_icode; - enum machine_mode new_mode; - - if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], - REGNO (second_reload_reg))) - oldequiv = old, real_oldequiv = real_old; + if (reload_adjust_reg_for_icode (&second_reload_reg, + third_reload_reg, sri.icode)) + icode = sri.icode, third_reload_reg = 0; else + oldequiv = old, real_oldequiv = real_old; + } + else if (sri.icode != CODE_FOR_nothing) + /* We currently lack a way to express this in reloads. */ + gcc_unreachable (); + else + { + sri2.icode = CODE_FOR_nothing; + sri2.prev_sri = &sri; + new_t_class = targetm.secondary_reload (1, real_oldequiv, + new_class, mode, &sri); + if (new_t_class == NO_REGS && sri2.icode == CODE_FOR_nothing) { - new_icode = reload_in_optab[(int) mode]; - if (new_icode != CODE_FOR_nothing - && ((insn_data[(int) new_icode].operand[0].predicate - && ! ((*insn_data[(int) new_icode].operand[0].predicate) - (reloadreg, mode))) - || (insn_data[(int) new_icode].operand[1].predicate - && ! ((*insn_data[(int) new_icode].operand[1].predicate) - (real_oldequiv, mode))))) - new_icode = CODE_FOR_nothing; - - if (new_icode == CODE_FOR_nothing) - new_mode = mode; + if (reload_adjust_reg_for_temp (&second_reload_reg, + third_reload_reg, + new_class, mode)) + third_reload_reg = 0, tertiary_icode = sri2.icode; else - new_mode = insn_data[(int) new_icode].operand[2].mode; + oldequiv = old, real_oldequiv = real_old; + } + else if (new_t_class == NO_REGS && sri2.icode != CODE_FOR_nothing) + { + rtx intermediate = second_reload_reg; - if (GET_MODE (second_reload_reg) != new_mode) + if (reload_adjust_reg_for_temp (&intermediate, NULL, + new_class, mode) + && reload_adjust_reg_for_icode (&third_reload_reg, NULL, + sri2.icode)) { - if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg), - new_mode)) - oldequiv = old, real_oldequiv = real_old; - else - second_reload_reg - = reload_adjust_reg_for_mode (second_reload_reg, - new_mode); + second_reload_reg = intermediate; + tertiary_icode = sri2.icode; + } + else + oldequiv = old, real_oldequiv = real_old; + } + else if (new_t_class != NO_REGS && sri2.icode == CODE_FOR_nothing) + { + rtx intermediate = second_reload_reg; + + if (reload_adjust_reg_for_temp (&intermediate, NULL, + new_class, mode) + && reload_adjust_reg_for_temp (&third_reload_reg, NULL, + new_t_class, mode)) + { + second_reload_reg = intermediate; + tertiary_icode = sri2.icode; } + else + oldequiv = old, real_oldequiv = real_old; } + else + /* This could be handled more intelligently too. */ + oldequiv = old, real_oldequiv = real_old; } } @@ -6624,6 +6704,9 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, { if (icode != CODE_FOR_nothing) { + /* We'd have to add extra code to handle this case. */ + gcc_assert (!third_reload_reg); + emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv, second_reload_reg)); special = 1; @@ -6632,18 +6715,21 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, { /* See if we need a scratch register to load the intermediate register (a tertiary reload). */ - enum insn_code tertiary_icode - = rld[secondary_reload].secondary_in_icode; - if (tertiary_icode != CODE_FOR_nothing) { - rtx third_reload_reg - = rld[rld[secondary_reload].secondary_in_reload].reg_rtx; - emit_insn ((GEN_FCN (tertiary_icode) (second_reload_reg, real_oldequiv, third_reload_reg))); } + else if (third_reload_reg) + { + gen_reload (third_reload_reg, real_oldequiv, + rl->opnum, + rl->when_needed); + gen_reload (second_reload_reg, third_reload_reg, + rl->opnum, + rl->when_needed); + } else gen_reload (second_reload_reg, real_oldequiv, rl->opnum, @@ -6653,7 +6739,6 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl, } } } -#endif if (! special && ! rtx_equal_p (reloadreg, oldequiv)) { @@ -6729,8 +6814,6 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl, if (GET_MODE (reloadreg) != mode) reloadreg = reload_adjust_reg_for_mode (reloadreg, mode); -#ifdef SECONDARY_OUTPUT_RELOAD_CLASS - /* If we need two reload regs, set RELOADREG to the intermediate one, since it will be stored into OLD. We might need a secondary register only for an input reload, so check again here. */ @@ -6738,22 +6821,25 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl, if (rl->secondary_out_reload >= 0) { rtx real_old = old; + int secondary_reload = rl->secondary_out_reload; + int tertiary_reload = rld[secondary_reload].secondary_out_reload; if (REG_P (old) && REGNO (old) >= FIRST_PSEUDO_REGISTER && reg_equiv_mem[REGNO (old)] != 0) real_old = reg_equiv_mem[REGNO (old)]; - if ((SECONDARY_OUTPUT_RELOAD_CLASS (rl->class, - mode, real_old) - != NO_REGS)) + if (secondary_reload_class (0, rl->class, mode, real_old) != NO_REGS) { rtx second_reloadreg = reloadreg; - reloadreg = rld[rl->secondary_out_reload].reg_rtx; + reloadreg = rld[secondary_reload].reg_rtx; /* See if RELOADREG is to be used as a scratch register or as an intermediate register. */ if (rl->secondary_out_icode != CODE_FOR_nothing) { + /* We'd have to add extra code to handle this case. */ + gcc_assert (tertiary_reload < 0); + emit_insn ((GEN_FCN (rl->secondary_out_icode) (real_old, second_reloadreg, reloadreg))); special = 1; @@ -6763,17 +6849,19 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl, /* See if we need both a scratch and intermediate reload register. */ - int secondary_reload = rl->secondary_out_reload; enum insn_code tertiary_icode = rld[secondary_reload].secondary_out_icode; + /* We'd have to add more code for quartary reloads. */ + gcc_assert (tertiary_reload < 0 + || rld[tertiary_reload].secondary_out_reload < 0); + if (GET_MODE (reloadreg) != mode) reloadreg = reload_adjust_reg_for_mode (reloadreg, mode); if (tertiary_icode != CODE_FOR_nothing) { - rtx third_reloadreg - = rld[rld[secondary_reload].secondary_out_reload].reg_rtx; + rtx third_reloadreg = rld[tertiary_reload].reg_rtx; rtx tem; /* Copy primary reload reg to secondary reload reg. @@ -6799,15 +6887,24 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl, } else - /* Copy between the reload regs here and then to - OUT later. */ + { + /* Copy between the reload regs here and then to + OUT later. */ + + gen_reload (reloadreg, second_reloadreg, + rl->opnum, rl->when_needed); + if (tertiary_reload >= 0) + { + rtx third_reloadreg = rld[tertiary_reload].reg_rtx; - gen_reload (reloadreg, second_reloadreg, - rl->opnum, rl->when_needed); + gen_reload (third_reloadreg, reloadreg, + rl->opnum, rl->when_needed); + reloadreg = third_reloadreg; + } + } } } } -#endif /* Output the last reload insn. */ if (! special) diff --git a/gcc/target-def.h b/gcc/target-def.h index 7e8837451f3..90dc55c4f25 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -480,6 +480,10 @@ Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define TARGET_HANDLE_PRAGMA_EXTERN_PREFIX 0 #endif +#ifndef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD default_secondary_reload +#endif + /* C++ specific. */ #ifndef TARGET_CXX_GUARD_TYPE @@ -609,6 +613,7 @@ Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. TARGET_INVALID_CONVERSION, \ TARGET_INVALID_UNARY_OP, \ TARGET_INVALID_BINARY_OP, \ + TARGET_SECONDARY_RELOAD, \ TARGET_CXX, \ TARGET_UNWIND_TABLES_DEFAULT, \ TARGET_HAVE_NAMED_SECTIONS, \ diff --git a/gcc/target.h b/gcc/target.h index 0f775f1de15..8d679d32857 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -52,6 +52,21 @@ Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. struct stdarg_info; +/* The struct used by the secondary_reload target hook. */ +typedef struct secondary_reload_info +{ + /* icode is actually an enum insn_code, but we don't want to force every + file that includes target.h to include optabs.h . */ + int icode; + int extra_cost; /* Cost for using (a) scratch register(s) to be taken + into account by copy_cost. */ + /* The next two members are for the use of the backward + compatibility hook. */ + struct secondary_reload_info *prev_sri; + int t_icode; /* Actually an enum insn_code - see above. */ +} secondary_reload_info; + + struct gcc_target { /* Functions that output assembler for the target. */ @@ -631,6 +646,11 @@ struct gcc_target is not permitted on TYPE1 and TYPE2, NULL otherwise. */ const char *(*invalid_binary_op) (int op, tree type1, tree type2); + /* Return the class for a secondary reload, and fill in extra information. */ + enum reg_class (*secondary_reload) (bool, rtx, enum reg_class, + enum machine_mode, + struct secondary_reload_info *); + /* Functions specific to the C++ frontend. */ struct cxx { /* Return the integer type used for guard variables. */ diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 94469a76b0f..1d5a7fec42c 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -63,6 +63,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "target-def.h" #include "ggc.h" #include "hard-reg-set.h" +#include "reload.h" +#include "optabs.h" +#include "recog.h" void @@ -455,4 +458,87 @@ default_internal_arg_pointer (void) return virtual_incoming_args_rtx; } +enum reg_class +default_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x ATTRIBUTE_UNUSED, + enum reg_class reload_class ATTRIBUTE_UNUSED, + enum machine_mode reload_mode ATTRIBUTE_UNUSED, + secondary_reload_info *sri) +{ + enum reg_class class = NO_REGS; + + if (sri->prev_sri && sri->prev_sri->t_icode != CODE_FOR_nothing) + { + sri->icode = sri->prev_sri->t_icode; + return NO_REGS; + } +#ifdef SECONDARY_INPUT_RELOAD_CLASS + if (in_p) + class = SECONDARY_INPUT_RELOAD_CLASS (reload_class, reload_mode, x); +#endif +#ifdef SECONDARY_OUTPUT_RELOAD_CLASS + if (! in_p) + class = SECONDARY_OUTPUT_RELOAD_CLASS (reload_class, reload_mode, x); +#endif + if (class != NO_REGS) + { + enum insn_code icode = (in_p ? reload_in_optab[(int) reload_mode] + : reload_out_optab[(int) reload_mode]); + + if (icode != CODE_FOR_nothing + && insn_data[(int) icode].operand[in_p].predicate + && ! insn_data[(int) icode].operand[in_p].predicate (x, reload_mode)) + icode = CODE_FOR_nothing; + else if (icode != CODE_FOR_nothing) + { + const char *insn_constraint, *scratch_constraint; + char insn_letter, scratch_letter; + enum reg_class insn_class, scratch_class; + + gcc_assert (insn_data[(int) icode].n_operands == 3); + insn_constraint = insn_data[(int) icode].operand[!in_p].constraint; + if (!*insn_constraint) + insn_class = ALL_REGS; + else + { + if (in_p) + { + gcc_assert (*insn_constraint == '='); + insn_constraint++; + } + insn_letter = *insn_constraint; + insn_class + = (insn_letter == 'r' ? GENERAL_REGS + : REG_CLASS_FROM_CONSTRAINT ((unsigned char) insn_letter, + insn_constraint)); + gcc_assert (insn_class != NO_REGS); + } + + scratch_constraint = insn_data[(int) icode].operand[2].constraint; + /* The scratch register's constraint must start with "=&". */ + gcc_assert (scratch_constraint[0] == '=' + && scratch_constraint[1] == '&'); + scratch_constraint += 2; + scratch_letter = *scratch_constraint; + scratch_class + = (scratch_letter == 'r' ? GENERAL_REGS + : REG_CLASS_FROM_CONSTRAINT ((unsigned char) scratch_letter, + scratch_constraint)); + + if (reg_class_subset_p (reload_class, insn_class)) + { + gcc_assert (scratch_class == class); + class = NO_REGS; + } + else + class = insn_class; + + } + if (class == NO_REGS) + sri->icode = icode; + else + sri->t_icode = icode; + } + return class; +} + #include "gt-targhooks.h" diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 24e3b6d38be..8acccfdcb05 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -69,3 +69,6 @@ extern const char *hook_invalid_arg_for_unprototyped_fn extern bool hook_bool_rtx_commutative_p (rtx, int); extern rtx default_function_value (tree, tree, bool); extern rtx default_internal_arg_pointer (void); +extern enum reg_class default_secondary_reload (bool, rtx, enum reg_class, + enum machine_mode, + secondary_reload_info *); |