X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fconfig%2Fsh%2Fsh.c;h=66a7da47e1ef6ddc3871c32b89d070da552461ff;hb=f5f12868d3e558faad9ad5f465aafb431e587bb6;hp=6cad65136eba7e80bf2d5cc81fd5edac95ca79de;hpb=36a2dfdb85d3fc67b8d5a4852d0ed01bdec76c1a;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index 6cad65136eb..66a7da47e1e 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -1,23 +1,23 @@ -/* Output routines for GCC for Hitachi / SuperH SH. - Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002, 2003 - Free Software Foundation, Inc. +/* Output routines for GCC for Renesas / SuperH SH. + Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004 Free Software Foundation, Inc. Contributed by Steve Chamberlain (sac@cygnus.com). Improved by Jim Wilson (wilson@cygnus.com). -This file is part of GNU CC. +This file is part of GCC. -GNU CC is free software; you can redistribute it and/or modify +GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. -GNU CC is distributed in the hope that it will be useful, +GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to +along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -47,6 +47,10 @@ Boston, MA 02111-1307, USA. */ #include "langhooks.h" #include "basic-block.h" #include "ra.h" +#include "cfglayout.h" +#include "intl.h" +#include "sched-int.h" +#include "ggc.h" int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch; @@ -55,7 +59,7 @@ int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch; /* These are some macros to abstract register modes. */ #define CONST_OK_FOR_ADD(size) \ - (TARGET_SHMEDIA ? CONST_OK_FOR_P (size) : CONST_OK_FOR_I (size)) + (TARGET_SHMEDIA ? CONST_OK_FOR_I10 (size) : CONST_OK_FOR_I08 (size)) #define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi)) #define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3)) #define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3)) @@ -89,7 +93,7 @@ static int pragma_trapa; interrupted. */ int pragma_nosave_low_regs; -/* This is used for communication between SETUP_INCOMING_VARARGS and +/* This is used for communication between TARGET_SETUP_INCOMING_VARARGS and sh_expand_prologue. */ int current_function_anonymous_args; @@ -98,6 +102,21 @@ int current_function_anonymous_args; /* Which cpu are we scheduling for. */ enum processor_type sh_cpu; +/* Definitions used in ready queue reordering for first scheduling pass. */ + +/* Reg weights arrays for modes SFmode and SImode, indexed by insn LUID. */ +static short *regmode_weight[2]; + +/* Total SFmode and SImode weights of scheduled insns. */ +static int curr_regmode_pressure[2]; + +/* If true, skip cycles for Q -> R movement. */ +static int skip_cycles = 0; + +/* Cached value of can_issue_more. This is cached in sh_variable_issue hook + and returned from sh_reorder2. */ +static short cached_can_issue_more; + /* Saved operands from the last compare to use when we generate an scc or bcc insn. */ @@ -107,7 +126,7 @@ rtx sh_compare_op1; /* Provides the class number of the smallest class containing reg number. */ -int regno_reg_class[FIRST_PSEUDO_REGISTER] = +enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] = { R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, @@ -174,58 +193,93 @@ enum reg_class reg_class_from_letter[] = int assembler_dialect; -static void split_branches PARAMS ((rtx)); -static int branch_dest PARAMS ((rtx)); -static void force_into PARAMS ((rtx, rtx)); -static void print_slot PARAMS ((rtx)); -static rtx add_constant PARAMS ((rtx, enum machine_mode, rtx)); -static void dump_table PARAMS ((rtx)); -static int hi_const PARAMS ((rtx)); -static int broken_move PARAMS ((rtx)); -static int mova_p PARAMS ((rtx)); -static rtx find_barrier PARAMS ((int, rtx, rtx)); -static int noncall_uses_reg PARAMS ((rtx, rtx, rtx *)); -static rtx gen_block_redirect PARAMS ((rtx, int, int)); -static void output_stack_adjust PARAMS ((int, rtx, int, rtx (*) (rtx))); -static rtx frame_insn PARAMS ((rtx)); -static rtx push PARAMS ((int)); -static void pop PARAMS ((int)); -static void push_regs PARAMS ((HARD_REG_SET *, int)); -static int calc_live_regs PARAMS ((HARD_REG_SET *)); -static void mark_use PARAMS ((rtx, rtx *)); -static HOST_WIDE_INT rounded_frame_size PARAMS ((int)); -static rtx mark_constant_pool_use PARAMS ((rtx)); +static bool shmedia_space_reserved_for_target_registers; + +static void split_branches (rtx); +static int branch_dest (rtx); +static void force_into (rtx, rtx); +static void print_slot (rtx); +static rtx add_constant (rtx, enum machine_mode, rtx); +static void dump_table (rtx); +static int hi_const (rtx); +static int broken_move (rtx); +static int mova_p (rtx); +static rtx find_barrier (int, rtx, rtx); +static int noncall_uses_reg (rtx, rtx, rtx *); +static rtx gen_block_redirect (rtx, int, int); +static void sh_reorg (void); +static void output_stack_adjust (int, rtx, int, HARD_REG_SET *); +static rtx frame_insn (rtx); +static rtx push (int); +static void pop (int); +static void push_regs (HARD_REG_SET *, int); +static int calc_live_regs (HARD_REG_SET *); +static void mark_use (rtx, rtx *); +static HOST_WIDE_INT rounded_frame_size (int); +static rtx mark_constant_pool_use (rtx); const struct attribute_spec sh_attribute_table[]; -static tree sh_handle_interrupt_handler_attribute PARAMS ((tree *, tree, tree, int, bool *)); -static tree sh_handle_sp_switch_attribute PARAMS ((tree *, tree, tree, int, bool *)); -static tree sh_handle_trap_exit_attribute PARAMS ((tree *, tree, tree, int, bool *)); -static void sh_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT)); -static void sh_insert_attributes PARAMS ((tree, tree *)); -static int sh_adjust_cost PARAMS ((rtx, rtx, rtx, int)); -static int sh_use_dfa_interface PARAMS ((void)); -static int sh_issue_rate PARAMS ((void)); -static bool sh_function_ok_for_sibcall PARAMS ((tree, tree)); - -static bool sh_cannot_modify_jumps_p PARAMS ((void)); -static bool sh_ms_bitfield_layout_p PARAMS ((tree)); - -static void sh_encode_section_info PARAMS ((tree, int)); -static const char *sh_strip_name_encoding PARAMS ((const char *)); -static void sh_init_builtins PARAMS ((void)); -static void sh_media_init_builtins PARAMS ((void)); -static rtx sh_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int)); -static void sh_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, - HOST_WIDE_INT, tree)); -static int flow_dependent_p PARAMS ((rtx, rtx)); -static void flow_dependent_p_1 PARAMS ((rtx, rtx, void *)); -static int shiftcosts PARAMS ((rtx)); -static int andcosts PARAMS ((rtx)); -static int addsubcosts PARAMS ((rtx)); -static int multcosts PARAMS ((rtx)); -static bool unspec_caller_rtx_p PARAMS ((rtx)); -static bool sh_cannot_copy_insn_p PARAMS ((rtx)); -static bool sh_rtx_costs PARAMS ((rtx, int, int, int *)); -static int sh_address_cost PARAMS ((rtx)); +static tree sh_handle_interrupt_handler_attribute (tree *, tree, tree, int, bool *); +static tree sh_handle_sp_switch_attribute (tree *, tree, tree, int, bool *); +static tree sh_handle_trap_exit_attribute (tree *, tree, tree, int, bool *); +static tree sh_handle_renesas_attribute (tree *, tree, tree, int, bool *); +static void sh_output_function_epilogue (FILE *, HOST_WIDE_INT); +static void sh_insert_attributes (tree, tree *); +static int sh_adjust_cost (rtx, rtx, rtx, int); +static int sh_use_dfa_interface (void); +static int sh_issue_rate (void); +static int sh_dfa_new_cycle (FILE *, int, rtx, int, int, int *sort_p); +static short find_set_regmode_weight (rtx, enum machine_mode); +static short find_insn_regmode_weight (rtx, enum machine_mode); +static void find_regmode_weight (int, enum machine_mode); +static void sh_md_init_global (FILE *, int, int); +static void sh_md_finish_global (FILE *, int); +static int rank_for_reorder (const void *, const void *); +static void swap_reorder (rtx *, int); +static void ready_reorder (rtx *, int); +static short high_pressure (enum machine_mode); +static int sh_reorder (FILE *, int, rtx *, int *, int); +static int sh_reorder2 (FILE *, int, rtx *, int *, int); +static void sh_md_init (FILE *, int, int); +static int sh_variable_issue (FILE *, int, rtx, int); + +static bool sh_function_ok_for_sibcall (tree, tree); + +static bool sh_cannot_modify_jumps_p (void); +static int sh_target_reg_class (void); +static bool sh_optimize_target_register_callee_saved (bool); +static bool sh_ms_bitfield_layout_p (tree); + +static void sh_init_builtins (void); +static void sh_media_init_builtins (void); +static rtx sh_expand_builtin (tree, rtx, rtx, enum machine_mode, int); +static void sh_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); +static void sh_file_start (void); +static int flow_dependent_p (rtx, rtx); +static void flow_dependent_p_1 (rtx, rtx, void *); +static int shiftcosts (rtx); +static int andcosts (rtx); +static int addsubcosts (rtx); +static int multcosts (rtx); +static bool unspec_caller_rtx_p (rtx); +static bool sh_cannot_copy_insn_p (rtx); +static bool sh_rtx_costs (rtx, int, int, int *); +static int sh_address_cost (rtx); +static int shmedia_target_regs_stack_space (HARD_REG_SET *); +static int shmedia_reserve_space_for_target_registers_p (int, HARD_REG_SET *); +static int shmedia_target_regs_stack_adjust (HARD_REG_SET *); +static int scavenge_reg (HARD_REG_SET *s); +struct save_schedule_s; +static struct save_entry_s *sh5_schedule_saves (HARD_REG_SET *, + struct save_schedule_s *, int); + +static rtx sh_struct_value_rtx (tree, int); +static bool sh_return_in_memory (tree, tree); +static rtx sh_builtin_saveregs (void); +static void sh_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, tree, int *, int); +static bool sh_strict_argument_naming (CUMULATIVE_ARGS *); +static bool sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *); +static tree sh_build_builtin_va_list (void); + /* Initialize the GCC target structure. */ #undef TARGET_ATTRIBUTE_TABLE @@ -252,6 +306,11 @@ static int sh_address_cost PARAMS ((rtx)); #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true +#undef TARGET_ASM_FILE_START +#define TARGET_ASM_FILE_START sh_file_start +#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE +#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true + #undef TARGET_INSERT_ATTRIBUTES #define TARGET_INSERT_ATTRIBUTES sh_insert_attributes @@ -264,17 +323,73 @@ static int sh_address_cost PARAMS ((rtx)); #undef TARGET_SCHED_ISSUE_RATE #define TARGET_SCHED_ISSUE_RATE sh_issue_rate +/* The next 5 hooks have been implemented for reenabling sched1. With the + help of these macros we are limiting the movement of insns in sched1 to + reduce the register pressure. The overall idea is to keep count of SImode + and SFmode regs required by already scheduled insns. When these counts + cross some threshold values; give priority to insns that free registers. + The insn that frees registers is most likely to be the insn with lowest + LUID (original insn order); but such an insn might be there in the stalled + queue (Q) instead of the ready queue (R). To solve this, we skip cycles + upto a max of 8 cycles so that such insns may move from Q -> R. + + The description of the hooks are as below: + + TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic + scheduler; it is called inside the sched_init function just after + find_insn_reg_weights function call. It is used to calculate the SImode + and SFmode weights of insns of basic blocks; much similiar to what + find_insn_reg_weights does. + TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook. + + TARGET_SCHED_DFA_NEW_CYCLE: Skip cycles if high register pressure is + indicated by TARGET_SCHED_REORDER2; doing this may move insns from + (Q)->(R). + + TARGET_SCHED_REORDER: If the register pressure for SImode or SFmode is + high; reorder the ready queue so that the insn with lowest LUID will be + issued next. + + TARGET_SCHED_REORDER2: If the register pressure is high, indicate to + TARGET_SCHED_DFA_NEW_CYCLE to skip cycles. + + TARGET_SCHED_VARIABLE_ISSUE: Cache the value of can_issue_more so that it + can be returned from TARGET_SCHED_REORDER2. + + TARGET_SCHED_INIT: Reset the register pressure counting variables. */ + +#undef TARGET_SCHED_DFA_NEW_CYCLE +#define TARGET_SCHED_DFA_NEW_CYCLE sh_dfa_new_cycle + +#undef TARGET_SCHED_INIT_GLOBAL +#define TARGET_SCHED_INIT_GLOBAL sh_md_init_global + +#undef TARGET_SCHED_FINISH_GLOBAL +#define TARGET_SCHED_FINISH_GLOBAL sh_md_finish_global + +#undef TARGET_SCHED_VARIABLE_ISSUE +#define TARGET_SCHED_VARIABLE_ISSUE sh_variable_issue + +#undef TARGET_SCHED_REORDER +#define TARGET_SCHED_REORDER sh_reorder + +#undef TARGET_SCHED_REORDER2 +#define TARGET_SCHED_REORDER2 sh_reorder2 + +#undef TARGET_SCHED_INIT +#define TARGET_SCHED_INIT sh_md_init + #undef TARGET_CANNOT_MODIFY_JUMPS_P #define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p +#undef TARGET_BRANCH_TARGET_REGISTER_CLASS +#define TARGET_BRANCH_TARGET_REGISTER_CLASS sh_target_reg_class +#undef TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED +#define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED \ + sh_optimize_target_register_callee_saved #undef TARGET_MS_BITFIELD_LAYOUT_P #define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p -#undef TARGET_ENCODE_SECTION_INFO -#define TARGET_ENCODE_SECTION_INFO sh_encode_section_info -#undef TARGET_STRIP_NAME_ENCODING -#define TARGET_STRIP_NAME_ENCODING sh_strip_name_encoding - #undef TARGET_INIT_BUILTINS #define TARGET_INIT_BUILTINS sh_init_builtins #undef TARGET_EXPAND_BUILTIN @@ -290,19 +405,53 @@ static int sh_address_cost PARAMS ((rtx)); #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST sh_address_cost +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG sh_reorg + #ifdef HAVE_AS_TLS #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS true #endif +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES sh_promote_prototypes +#undef TARGET_PROMOTE_FUNCTION_ARGS +#define TARGET_PROMOTE_FUNCTION_ARGS sh_promote_prototypes +#undef TARGET_PROMOTE_FUNCTION_RETURN +#define TARGET_PROMOTE_FUNCTION_RETURN sh_promote_prototypes + +#undef TARGET_STRUCT_VALUE_RTX +#define TARGET_STRUCT_VALUE_RTX sh_struct_value_rtx +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY sh_return_in_memory + +#undef TARGET_EXPAND_BUILTIN_SAVEREGS +#define TARGET_EXPAND_BUILTIN_SAVEREGS sh_builtin_saveregs +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS sh_setup_incoming_varargs +#undef TARGET_STRICT_ARGUMENT_NAMING +#define TARGET_STRICT_ARGUMENT_NAMING sh_strict_argument_naming +#undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED +#define TARGET_PRETEND_OUTGOING_VARARGS_NAMED sh_pretend_outgoing_varargs_named + +#undef TARGET_BUILD_BUILTIN_VA_LIST +#define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list + +#undef TARGET_PCH_VALID_P +#define TARGET_PCH_VALID_P sh_pch_valid_p + +/* Return regmode weight for insn. */ +#define INSN_REGMODE_WEIGHT(INSN, MODE) regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)] + +/* Return current register pressure for regmode. */ +#define CURR_REGMODE_PRESSURE(MODE) curr_regmode_pressure[((MODE) == SImode) ? 0 : 1] + struct gcc_target targetm = TARGET_INITIALIZER; /* Print the operand address in x to the stream. */ void -print_operand_address (stream, x) - FILE *stream; - rtx x; +print_operand_address (FILE *stream, rtx x) { switch (GET_CODE (x)) { @@ -363,7 +512,7 @@ print_operand_address (stream, x) ',' print LOCAL_LABEL_PREFIX '@' print trap, rte or rts depending upon pragma interruptness '#' output a nop if there is nothing to put in the delay slot - ''' print likelyhood suffix (/u for unlikely). + ''' print likelihood suffix (/u for unlikely). 'O' print a constant without the # 'R' print the LSW of a dp value - changes if in little endian 'S' print the MSW of a dp value - changes if in little endian @@ -375,10 +524,7 @@ print_operand_address (stream, x) 'o' output an operator. */ void -print_operand (stream, x, code) - FILE *stream; - rtx x; - int code; +print_operand (FILE *stream, rtx x, int code) { switch (code) { @@ -587,8 +733,7 @@ print_operand (stream, x, code) /* Like force_operand, but guarantees that VALUE ends up in TARGET. */ static void -force_into (value, target) - rtx value, target; +force_into (rtx value, rtx target) { value = force_operand (value, target); if (! rtx_equal_p (value, target)) @@ -603,8 +748,7 @@ force_into (value, target) OPERANDS[3] is the alignment safe to use. */ int -expand_block_move (operands) - rtx *operands; +expand_block_move (rtx *operands) { int align = INTVAL (operands[3]); int constp = (GET_CODE (operands[2]) == CONST_INT); @@ -624,12 +768,12 @@ expand_block_move (operands) tree entry_name; rtx sym; rtx func_addr_rtx; - rtx r4 = gen_rtx (REG, SImode, 4); - rtx r5 = gen_rtx (REG, SImode, 5); + rtx r4 = gen_rtx_REG (SImode, 4); + rtx r5 = gen_rtx_REG (SImode, 5); entry_name = get_identifier ("__movstrSI12_i4"); - sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); + sym = function_symbol (IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); @@ -642,14 +786,14 @@ expand_block_move (operands) rtx sym; rtx func_addr_rtx; int dwords; - rtx r4 = gen_rtx (REG, SImode, 4); - rtx r5 = gen_rtx (REG, SImode, 5); - rtx r6 = gen_rtx (REG, SImode, 6); + rtx r4 = gen_rtx_REG (SImode, 4); + rtx r5 = gen_rtx_REG (SImode, 5); + rtx r6 = gen_rtx_REG (SImode, 6); entry_name = get_identifier (bytes & 4 ? "__movstr_i4_odd" : "__movstr_i4_even"); - sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); + sym = function_symbol (IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); @@ -673,7 +817,7 @@ expand_block_move (operands) sprintf (entry, "__movstrSI%d", bytes); entry_name = get_identifier (entry); - sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); + sym = function_symbol (IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); @@ -694,7 +838,7 @@ expand_block_move (operands) rtx r6 = gen_rtx_REG (SImode, 6); entry_name = get_identifier ("__movstr"); - sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); + sym = function_symbol (IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); @@ -719,9 +863,7 @@ expand_block_move (operands) operands must be in a register. */ int -prepare_move_operands (operands, mode) - rtx operands[]; - enum machine_mode mode; +prepare_move_operands (rtx operands[], enum machine_mode mode) { if ((mode == SImode || mode == DImode) && flag_pic @@ -765,11 +907,20 @@ prepare_move_operands (operands, mode) && ! sh_register_operand (operands[1], mode)) operands[1] = copy_to_mode_reg (mode, operands[1]); + if (GET_CODE (operands[0]) == MEM && ! memory_operand (operands[0], mode)) + { + /* This is like change_address_1 (operands[0], mode, 0, 1) , + except that we can't use that function because it is static. */ + rtx new = change_address (operands[0], mode, 0); + MEM_COPY_ATTRIBUTES (new, operands[0]); + operands[0] = new; + } + /* This case can happen while generating code to move the result of a library call to the target. Reject `st r0,@(rX,rY)' because reload will fail to find a spill register for rX, since r0 is already being used for the source. */ - else if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == 0 + else if (refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0) && GET_CODE (operands[0]) == MEM && GET_CODE (XEXP (operands[0], 0)) == PLUS && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG) @@ -792,13 +943,13 @@ prepare_move_operands (operands, mode) { case TLS_MODEL_GLOBAL_DYNAMIC: tga_ret = gen_rtx_REG (Pmode, R0_REG); - emit_insn (gen_tls_global_dynamic (tga_ret, op1)); + emit_call_insn (gen_tls_global_dynamic (tga_ret, op1)); op1 = tga_ret; break; case TLS_MODEL_LOCAL_DYNAMIC: tga_ret = gen_rtx_REG (Pmode, R0_REG); - emit_insn (gen_tls_local_dynamic (tga_ret, op1)); + emit_call_insn (gen_tls_local_dynamic (tga_ret, op1)); tmp = gen_reg_rtx (Pmode); emit_move_insn (tmp, tga_ret); @@ -849,8 +1000,7 @@ prepare_move_operands (operands, mode) /* Prepare the operands for an scc instruction; make sure that the compare has been done. */ rtx -prepare_scc_operands (code) - enum rtx_code code; +prepare_scc_operands (enum rtx_code code) { rtx t_reg = gen_rtx_REG (SImode, T_REG); enum rtx_code oldcode = code; @@ -898,15 +1048,15 @@ prepare_scc_operands (code) if (TARGET_SH4 && GET_MODE_CLASS (mode) == MODE_FLOAT) (mode == SFmode ? emit_sf_insn : emit_df_insn) - (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, - gen_rtx (SET, VOIDmode, t_reg, - gen_rtx (code, SImode, - sh_compare_op0, sh_compare_op1)), - gen_rtx (USE, VOIDmode, get_fpscr_rtx ())))); + (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + gen_rtx_SET (VOIDmode, t_reg, + gen_rtx_fmt_ee (code, SImode, + sh_compare_op0, sh_compare_op1)), + gen_rtx_USE (VOIDmode, get_fpscr_rtx ())))); else - emit_insn (gen_rtx (SET, VOIDmode, t_reg, - gen_rtx (code, SImode, sh_compare_op0, - sh_compare_op1))); + emit_insn (gen_rtx_SET (VOIDmode, t_reg, + gen_rtx_fmt_ee (code, SImode, + sh_compare_op0, sh_compare_op1))); return t_reg; } @@ -914,9 +1064,7 @@ prepare_scc_operands (code) /* Called from the md file, set up the operands of a compare instruction. */ void -from_compare (operands, code) - rtx *operands; - int code; +from_compare (rtx *operands, int code) { enum machine_mode mode = GET_MODE (sh_compare_op0); rtx insn; @@ -941,13 +1089,13 @@ from_compare (operands, code) else insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG), - gen_rtx (code, SImode, sh_compare_op0, - sh_compare_op1)); + gen_rtx_fmt_ee (code, SImode, + sh_compare_op0, sh_compare_op1)); if (TARGET_SH4 && GET_MODE_CLASS (mode) == MODE_FLOAT) { - insn = gen_rtx (PARALLEL, VOIDmode, + insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, insn, - gen_rtx (USE, VOIDmode, get_fpscr_rtx ()))); + gen_rtx_USE (VOIDmode, get_fpscr_rtx ()))); (mode == SFmode ? emit_sf_insn : emit_df_insn) (insn); } else @@ -962,10 +1110,8 @@ from_compare (operands, code) to take care when we see overlapping source and dest registers. */ const char * -output_movedouble (insn, operands, mode) - rtx insn ATTRIBUTE_UNUSED; - rtx operands[]; - enum machine_mode mode; +output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[], + enum machine_mode mode) { rtx dst = operands[0]; rtx src = operands[1]; @@ -1040,18 +1186,15 @@ output_movedouble (insn, operands, mode) into a sequence where putting the slot insn at the end wouldn't work. */ static void -print_slot (insn) - rtx insn; +print_slot (rtx insn) { - final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1); + final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1, NULL); INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1; } const char * -output_far_jump (insn, op) - rtx insn; - rtx op; +output_far_jump (rtx insn, rtx op) { struct { rtx lab, reg, op; } this; rtx braf_base_lab = NULL_RTX; @@ -1145,10 +1288,7 @@ static int lf = 100; /* Output code for ordinary branches. */ const char * -output_branch (logic, insn, operands) - int logic; - rtx insn; - rtx *operands; +output_branch (int logic, rtx insn, rtx *operands) { switch (get_attr_length (insn)) { @@ -1183,7 +1323,7 @@ output_branch (logic, insn, operands) output_asm_insn ("bra\t%l0", &op0); fprintf (asm_out_file, "\tnop\n"); - (*targetm.asm_out.internal_label)(asm_out_file, "LF", label); + (*targetm.asm_out.internal_label) (asm_out_file, "LF", label); return ""; } @@ -1233,11 +1373,8 @@ output_branch (logic, insn, operands) } const char * -output_branchy_insn (code, template, insn, operands) - enum rtx_code code; - const char *template; - rtx insn; - rtx *operands; +output_branchy_insn (enum rtx_code code, const char *template, + rtx insn, rtx *operands) { rtx next_insn = NEXT_INSN (insn); @@ -1277,39 +1414,49 @@ output_branchy_insn (code, template, insn, operands) } const char * -output_ieee_ccmpeq (insn, operands) - rtx insn, *operands; +output_ieee_ccmpeq (rtx insn, rtx *operands) { return output_branchy_insn (NE, "bt\t%l9\\;fcmp/eq\t%1,%0", insn, operands); } -/* Output to FILE the start of the assembler file. */ - -void -output_file_start (file) - FILE *file; -{ - output_file_directive (file, main_input_filename); +/* Output the start of the assembler file. */ - /* Switch to the data section so that the coffsem symbol - isn't in the text section. */ - data_section (); +static void +sh_file_start (void) +{ + default_file_start (); + + if (TARGET_ELF) + /* We need to show the text section with the proper + attributes as in TEXT_SECTION_ASM_OP, before dwarf2out + emits it without attributes in TEXT_SECTION_ASM_OP, else GAS + will complain. We can teach GAS specifically about the + default attributes for our choice of text section, but + then we would have to change GAS again if/when we change + the text section name. */ + fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); + else + /* Switch to the data section so that the coffsem symbol + isn't in the text section. */ + data_section (); if (TARGET_LITTLE_ENDIAN) - fprintf (file, "\t.little\n"); + fputs ("\t.little\n", asm_out_file); - if (TARGET_SHCOMPACT) - fprintf (file, "\t.mode\tSHcompact\n"); - else if (TARGET_SHMEDIA) - fprintf (file, "\t.mode\tSHmedia\n\t.abi\t%i\n", - TARGET_SHMEDIA64 ? 64 : 32); + if (!TARGET_ELF) + { + if (TARGET_SHCOMPACT) + fputs ("\t.mode\tSHcompact\n", asm_out_file); + else if (TARGET_SHMEDIA) + fprintf (asm_out_file, "\t.mode\tSHmedia\n\t.abi\t%i\n", + TARGET_SHMEDIA64 ? 64 : 32); + } } /* Check if PAT includes UNSPEC_CALLER unspec pattern. */ static bool -unspec_caller_rtx_p (pat) - rtx pat; +unspec_caller_rtx_p (rtx pat) { switch (GET_CODE (pat)) { @@ -1334,8 +1481,7 @@ unspec_caller_rtx_p (pat) that generates an unique label. */ static bool -sh_cannot_copy_insn_p (insn) - rtx insn; +sh_cannot_copy_insn_p (rtx insn) { rtx pat; @@ -1406,8 +1552,7 @@ static const short ext_shift_amounts[32][4] = { of arbitrary constant shift instructions. */ int -shift_insns_rtx (insn) - rtx insn; +shift_insns_rtx (rtx insn) { rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); int shift_count = INTVAL (XEXP (set_src, 1)); @@ -1421,15 +1566,14 @@ shift_insns_rtx (insn) case ASHIFT: return shift_insns[shift_count]; default: - abort(); + abort (); } } /* Return the cost of a shift. */ static inline int -shiftcosts (x) - rtx x; +shiftcosts (rtx x) { int value; @@ -1468,8 +1612,7 @@ shiftcosts (x) /* Return the cost of an AND operation. */ static inline int -andcosts (x) - rtx x; +andcosts (rtx x) { int i; @@ -1482,8 +1625,8 @@ andcosts (x) if (TARGET_SHMEDIA) { if ((GET_CODE (XEXP (x, 1)) == CONST_INT - && CONST_OK_FOR_J (INTVAL (XEXP (x, 1)))) - || EXTRA_CONSTRAINT_S (XEXP (x, 1))) + && CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)))) + || EXTRA_CONSTRAINT_C16 (XEXP (x, 1))) return 1; else return 2; @@ -1492,13 +1635,13 @@ andcosts (x) /* These constants are single cycle extu.[bw] instructions. */ if (i == 0xff || i == 0xffff) return 1; - /* Constants that can be used in an and immediate instruction is a single + /* Constants that can be used in an and immediate instruction in a single cycle, but this requires r0, so make it a little more expensive. */ - if (CONST_OK_FOR_L (i)) + if (CONST_OK_FOR_K08 (i)) return 2; /* Constants that can be loaded with a mov immediate and an and. This case is probably unnecessary. */ - if (CONST_OK_FOR_I (i)) + if (CONST_OK_FOR_I08 (i)) return 2; /* Any other constants requires a 2 cycle pc-relative load plus an and. This case is probably unnecessary. */ @@ -1508,8 +1651,7 @@ andcosts (x) /* Return the cost of an addition or a subtraction. */ static inline int -addsubcosts (x) - rtx x; +addsubcosts (rtx x) { /* Adding a register is a single cycle insn. */ if (GET_CODE (XEXP (x, 1)) == REG @@ -1530,16 +1672,16 @@ addsubcosts (x) return TARGET_SHMEDIA64 ? 5 : 3; case CONST_INT: - if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1)))) + if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)))) return 2; - else if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1)) >> 16)) + else if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)) >> 16)) return 3; - else if (CONST_OK_FOR_J ((INTVAL (XEXP (x, 1)) >> 16) >> 16)) + else if (CONST_OK_FOR_I16 ((INTVAL (XEXP (x, 1)) >> 16) >> 16)) return 4; /* Fall through. */ default: - return 5; + return 5; } /* Any other constant requires a 2 cycle pc-relative load plus an @@ -1549,8 +1691,7 @@ addsubcosts (x) /* Return the cost of a multiply. */ static inline int -multcosts (x) - rtx x ATTRIBUTE_UNUSED; +multcosts (rtx x ATTRIBUTE_UNUSED) { if (TARGET_SHMEDIA) return 3; @@ -1579,9 +1720,7 @@ multcosts (x) scanned. In either case, *TOTAL contains the cost result. */ static bool -sh_rtx_costs (x, code, outer_code, total) - rtx x; - int code, outer_code, *total; +sh_rtx_costs (rtx x, int code, int outer_code, int *total) { switch (code) { @@ -1594,22 +1733,22 @@ sh_rtx_costs (x, code, outer_code, total) *total = 0; else if ((outer_code == IOR || outer_code == XOR || outer_code == PLUS) - && CONST_OK_FOR_P (INTVAL (x))) + && CONST_OK_FOR_I10 (INTVAL (x))) *total = 0; - else if (CONST_OK_FOR_J (INTVAL (x))) + else if (CONST_OK_FOR_I16 (INTVAL (x))) *total = COSTS_N_INSNS (outer_code != SET); - else if (CONST_OK_FOR_J (INTVAL (x) >> 16)) + else if (CONST_OK_FOR_I16 (INTVAL (x) >> 16)) *total = COSTS_N_INSNS (2); - else if (CONST_OK_FOR_J ((INTVAL (x) >> 16) >> 16)) + else if (CONST_OK_FOR_I16 ((INTVAL (x) >> 16) >> 16)) *total = COSTS_N_INSNS (3); else *total = COSTS_N_INSNS (4); return true; } - if (CONST_OK_FOR_I (INTVAL (x))) + if (CONST_OK_FOR_I08 (INTVAL (x))) *total = 0; else if ((outer_code == AND || outer_code == IOR || outer_code == XOR) - && CONST_OK_FOR_L (INTVAL (x))) + && CONST_OK_FOR_K08 (INTVAL (x))) *total = 1; else *total = 8; @@ -1673,8 +1812,7 @@ sh_rtx_costs (x, code, outer_code, total) since it increases pressure on r0. */ static int -sh_address_cost (X) - rtx X; +sh_address_cost (rtx X) { return (GET_CODE (X) == PLUS && ! CONSTANT_P (XEXP (X, 1)) @@ -1684,10 +1822,7 @@ sh_address_cost (X) /* Code to expand a shift. */ void -gen_ashift (type, n, reg) - int type; - int n; - rtx reg; +gen_ashift (int type, int n, rtx reg) { /* Negative values here come from the shift_amounts array. */ if (n < 0) @@ -1719,10 +1854,7 @@ gen_ashift (type, n, reg) /* Same for HImode */ void -gen_ashift_hi (type, n, reg) - int type; - int n; - rtx reg; +gen_ashift_hi (int type, int n, rtx reg) { /* Negative values here come from the shift_amounts array. */ if (n < 0) @@ -1763,9 +1895,7 @@ gen_ashift_hi (type, n, reg) shift instructions. */ void -gen_shifty_op (code, operands) - int code; - rtx *operands; +gen_shifty_op (int code, rtx *operands) { int value = INTVAL (operands[2]); int max, i; @@ -1811,13 +1941,11 @@ gen_shifty_op (code, operands) matter. */ void -gen_shifty_hi_op (code, operands) - int code; - rtx *operands; +gen_shifty_hi_op (int code, rtx *operands) { int value = INTVAL (operands[2]); int max, i; - void (*gen_fun) PARAMS ((int, int, rtx)); + void (*gen_fun) (int, int, rtx); /* This operation is used by and_shl for SImode values with a few high bits known to be cleared. */ @@ -1847,8 +1975,7 @@ gen_shifty_hi_op (code, operands) /* ??? Rewrite to use super-optimizer sequences. */ int -expand_ashiftrt (operands) - rtx *operands; +expand_ashiftrt (rtx *operands) { rtx sym; rtx wrk; @@ -1911,7 +2038,7 @@ expand_ashiftrt (operands) emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]); sprintf (func, "__ashiftrt_r4_%d", value); func_name = get_identifier (func); - sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (func_name)); + sym = function_symbol (IDENTIFIER_POINTER (func_name)); emit_move_insn (wrk, sym); emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk)); emit_move_insn (operands[0], gen_rtx_REG (SImode, 4)); @@ -1919,8 +2046,7 @@ expand_ashiftrt (operands) } int -sh_dynamicalize_shift_p (count) - rtx count; +sh_dynamicalize_shift_p (rtx count) { return shift_insns[INTVAL (count)] > 1 + SH_DYNAMIC_SHIFT_COST; } @@ -1946,9 +2072,7 @@ sh_dynamicalize_shift_p (count) shift_amounts for the last shift value that is to be used before the sign extend. */ int -shl_and_kind (left_rtx, mask_rtx, attrp) - rtx left_rtx, mask_rtx; - int *attrp; +shl_and_kind (rtx left_rtx, rtx mask_rtx, int *attrp) { unsigned HOST_WIDE_INT mask, lsb, mask2, lsb2; int left = INTVAL (left_rtx), right; @@ -1964,7 +2088,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp) mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> left; else mask = (unsigned HOST_WIDE_INT) GET_MODE_MASK (SImode) >> left; - /* Can this be expressed as a right shift / left shift pair ? */ + /* Can this be expressed as a right shift / left shift pair? */ lsb = ((mask ^ (mask - 1)) >> 1) + 1; right = exact_log2 (lsb); mask2 = ~(mask + lsb - 1); @@ -1978,15 +2102,15 @@ shl_and_kind (left_rtx, mask_rtx, attrp) int late_right = exact_log2 (lsb2); best_cost = shift_insns[left + late_right] + shift_insns[late_right]; } - /* Try to use zero extend */ + /* Try to use zero extend. */ if (mask2 == ~(lsb2 - 1)) { int width, first; for (width = 8; width <= 16; width += 8) { - /* Can we zero-extend right away? */ - if (lsb2 == (unsigned HOST_WIDE_INT)1 << width) + /* Can we zero-extend right away? */ + if (lsb2 == (unsigned HOST_WIDE_INT) 1 << width) { cost = 1 + ext_shift_insns[right] + ext_shift_insns[left + right]; @@ -2018,7 +2142,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp) best_len = cost; if (attrp) attrp[2] = first; - } + } } } } @@ -2027,7 +2151,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp) { if (i > right) break; - if (! CONST_OK_FOR_L (mask >> i)) + if (! CONST_OK_FOR_K08 (mask >> i)) continue; cost = (i != 0) + 2 + ext_shift_insns[left + i]; if (cost < best_cost) @@ -2039,19 +2163,19 @@ shl_and_kind (left_rtx, mask_rtx, attrp) } } /* Try to use a scratch register to hold the AND operand. */ - can_ext = ((mask << left) & ((unsigned HOST_WIDE_INT)3 << 30)) == 0; + can_ext = ((mask << left) & ((unsigned HOST_WIDE_INT) 3 << 30)) == 0; for (i = 0; i <= 2; i++) { if (i > right) break; - cost = (i != 0) + (CONST_OK_FOR_I (mask >> i) ? 2 : 3) + cost = (i != 0) + (CONST_OK_FOR_I08 (mask >> i) ? 2 : 3) + (can_ext ? ext_shift_insns : shift_insns)[left + i]; if (cost < best_cost) { best = 4 - can_ext; best_cost = cost; best_right = i; - best_len = cost - 1 - ! CONST_OK_FOR_I (mask >> i); + best_len = cost - 1 - ! CONST_OK_FOR_I08 (mask >> i); } } @@ -2066,8 +2190,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp) /* This is used in length attributes of the unnamed instructions corresponding to shl_and_kind return values of 1 and 2. */ int -shl_and_length (insn) - rtx insn; +shl_and_length (rtx insn) { rtx set_src, left_rtx, mask_rtx; int attributes[3]; @@ -2082,8 +2205,7 @@ shl_and_length (insn) /* This is used in length attribute of the and_shl_scratch instruction. */ int -shl_and_scr_length (insn) - rtx insn; +shl_and_scr_length (rtx insn) { rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); int len = shift_insns[INTVAL (XEXP (set_src, 1))]; @@ -2100,14 +2222,13 @@ extern int rtx_equal_function_value_matters; method of generating them, i.e. returned zero. */ int -gen_shl_and (dest, left_rtx, mask_rtx, source) - rtx dest, left_rtx, mask_rtx, source; +gen_shl_and (rtx dest, rtx left_rtx, rtx mask_rtx, rtx source) { int attributes[3]; unsigned HOST_WIDE_INT mask; int kind = shl_and_kind (left_rtx, mask_rtx, attributes); int right, total_shift; - void (*shift_gen_fun) PARAMS ((int, rtx*)) = gen_shifty_hi_op; + void (*shift_gen_fun) (int, rtx *) = gen_shifty_hi_op; right = attributes[0]; total_shift = INTVAL (left_rtx) + right; @@ -2124,10 +2245,10 @@ gen_shl_and (dest, left_rtx, mask_rtx, source) if (first < 0) { emit_insn ((mask << right) <= 0xff - ? gen_zero_extendqisi2(dest, - gen_lowpart (QImode, source)) - : gen_zero_extendhisi2(dest, - gen_lowpart (HImode, source))); + ? gen_zero_extendqisi2 (dest, + gen_lowpart (QImode, source)) + : gen_zero_extendhisi2 (dest, + gen_lowpart (HImode, source))); source = dest; } if (source != dest) @@ -2147,8 +2268,8 @@ gen_shl_and (dest, left_rtx, mask_rtx, source) } if (first >= 0) emit_insn (mask <= 0xff - ? gen_zero_extendqisi2(dest, gen_lowpart (QImode, dest)) - : gen_zero_extendhisi2(dest, gen_lowpart (HImode, dest))); + ? gen_zero_extendqisi2 (dest, gen_lowpart (QImode, dest)) + : gen_zero_extendhisi2 (dest, gen_lowpart (HImode, dest))); if (total_shift > 0) { operands[2] = GEN_INT (total_shift); @@ -2162,8 +2283,8 @@ gen_shl_and (dest, left_rtx, mask_rtx, source) /* If the topmost bit that matters is set, set the topmost bits that don't matter. This way, we might be able to get a shorter signed constant. */ - if (mask & ((HOST_WIDE_INT)1 << (31 - total_shift))) - mask |= (HOST_WIDE_INT)~0 << (31 - total_shift); + if (mask & ((HOST_WIDE_INT) 1 << (31 - total_shift))) + mask |= (HOST_WIDE_INT) ~0 << (31 - total_shift); case 2: /* Don't expand fine-grained when combining, because that will make the pattern fail. */ @@ -2233,9 +2354,7 @@ gen_shl_and (dest, left_rtx, mask_rtx, source) If COSTP is nonzero, assign the calculated cost to *COSTP. */ int -shl_sext_kind (left_rtx, size_rtx, costp) - rtx left_rtx, size_rtx; - int *costp; +shl_sext_kind (rtx left_rtx, rtx size_rtx, int *costp) { int left, size, insize, ext; int cost = 0, best_cost; @@ -2329,8 +2448,7 @@ shl_sext_kind (left_rtx, size_rtx, costp) implementing this pattern. */ int -shl_sext_length (insn) - rtx insn; +shl_sext_length (rtx insn) { rtx set_src, left_rtx, size_rtx; int cost; @@ -2345,8 +2463,7 @@ shl_sext_length (insn) /* Generate rtl for this pattern */ int -gen_shl_sext (dest, left_rtx, size_rtx, source) - rtx dest, left_rtx, size_rtx, source; +gen_shl_sext (rtx dest, rtx left_rtx, rtx size_rtx, rtx source) { int kind; int left, size, insize, cost; @@ -2384,8 +2501,8 @@ gen_shl_sext (dest, left_rtx, size_rtx, source) gen_shifty_hi_op (ASHIFT, operands); } emit_insn (kind & 1 - ? gen_extendqisi2(dest, gen_lowpart (QImode, dest)) - : gen_extendhisi2(dest, gen_lowpart (HImode, dest))); + ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest)) + : gen_extendhisi2 (dest, gen_lowpart (HImode, dest))); if (kind <= 2) { if (shift2) @@ -2402,7 +2519,7 @@ gen_shl_sext (dest, left_rtx, size_rtx, source) { operands[2] = GEN_INT (shift2 + 1); gen_shifty_op (ASHIFT, operands); - operands[2] = GEN_INT (1); + operands[2] = const1_rtx; gen_shifty_op (ASHIFTRT, operands); break; } @@ -2456,7 +2573,7 @@ gen_shl_sext (dest, left_rtx, size_rtx, source) operands[2] = kind == 7 ? GEN_INT (left + 1) : left_rtx; gen_shifty_op (ASHIFT, operands); if (kind == 7) - emit_insn (gen_ashrsi3_k (dest, dest, GEN_INT (1))); + emit_insn (gen_ashrsi3_k (dest, dest, const1_rtx)); break; default: return -1; @@ -2467,8 +2584,7 @@ gen_shl_sext (dest, left_rtx, size_rtx, source) /* Prefix a symbol_ref name with "datalabel". */ rtx -gen_datalabel_ref (sym) - rtx sym; +gen_datalabel_ref (rtx sym) { if (GET_CODE (sym) == LABEL_REF) return gen_rtx_CONST (GET_MODE (sym), @@ -2479,8 +2595,6 @@ gen_datalabel_ref (sym) if (GET_CODE (sym) != SYMBOL_REF) abort (); - XSTR (sym, 0) = concat (SH_DATALABEL_ENCODING, XSTR (sym, 0), NULL); - return sym; } @@ -2573,10 +2687,7 @@ static int pool_window_last; /* Add a constant to the pool and return its label. */ static rtx -add_constant (x, mode, last_value) - rtx x; - enum machine_mode mode; - rtx last_value; +add_constant (rtx x, enum machine_mode mode, rtx last_value) { int i; rtx lab, new, ref, newref; @@ -2648,8 +2759,7 @@ add_constant (x, mode, last_value) /* Output the literal table. */ static void -dump_table (scan) - rtx scan; +dump_table (rtx scan) { int i; int need_align = 1; @@ -2713,7 +2823,7 @@ dump_table (scan) { lab = XEXP (ref, 0); emit_insn_before (gen_consttable_window_end (lab), - align_insn); + align_insn); } delete_insn (align_insn); align_insn = NULL_RTX; @@ -2820,8 +2930,7 @@ dump_table (scan) mov.w instead of a mov.l. */ static int -hi_const (src) - rtx src; +hi_const (rtx src) { return (GET_CODE (src) == CONST_INT && INTVAL (src) >= -32768 @@ -2831,12 +2940,11 @@ hi_const (src) /* Nonzero if the insn is a move instruction which needs to be fixed. */ /* ??? For a DImode/DFmode moves, we don't need to fix it if each half of the - CONST_DOUBLE input value is CONST_OK_FOR_I. For a SFmode move, we don't - need to fix it if the input value is CONST_OK_FOR_I. */ + CONST_DOUBLE input value is CONST_OK_FOR_I08. For a SFmode move, we don't + need to fix it if the input value is CONST_OK_FOR_I08. */ static int -broken_move (insn) - rtx insn; +broken_move (rtx insn) { if (GET_CODE (insn) == INSN) { @@ -2869,7 +2977,7 @@ broken_move (insn) && GET_CODE (SET_DEST (pat)) == REG && FP_REGISTER_P (REGNO (SET_DEST (pat)))) && (GET_CODE (SET_SRC (pat)) != CONST_INT - || ! CONST_OK_FOR_I (INTVAL (SET_SRC (pat))))) + || ! CONST_OK_FOR_I08 (INTVAL (SET_SRC (pat))))) return 1; } @@ -2877,8 +2985,7 @@ broken_move (insn) } static int -mova_p (insn) - rtx insn; +mova_p (rtx insn) { return (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET @@ -2893,9 +3000,7 @@ mova_p (insn) the range. */ static rtx -find_barrier (num_mova, mova, from) - int num_mova; - rtx mova, from; +find_barrier (int num_mova, rtx mova, rtx from) { int count_si = 0; int count_hi = 0; @@ -2987,7 +3092,7 @@ find_barrier (num_mova, mova, from) the limit is the same, but the alignment requirements are higher. We may waste up to 4 additional bytes for alignment, and the DF/DI constant may have - another SF/SI constant placed before it. */ + another SF/SI constant placed before it. */ if (TARGET_SHCOMPACT && ! found_di && (mode == DFmode || mode == DImode)) @@ -3137,8 +3242,7 @@ find_barrier (num_mova, mova, from) register is not used anywhere else in this instruction - except as the destination of a set, return this register; else, return 0. */ rtx -sfunc_uses_reg (insn) - rtx insn; +sfunc_uses_reg (rtx insn) { int i; rtx pattern, part, reg_part, reg; @@ -3176,10 +3280,7 @@ sfunc_uses_reg (insn) is set by INSN. */ static int -noncall_uses_reg (reg, insn, set) - rtx reg; - rtx insn; - rtx *set; +noncall_uses_reg (rtx reg, rtx insn, rtx *set) { rtx pattern, reg2; @@ -3264,8 +3365,7 @@ noncall_uses_reg (reg, insn, set) registers 0..15, respectively, are used as outputs, or are clobbered. IS_DEST should be set to 16 if X is the destination of a SET, else to 0. */ int -regs_used (x, is_dest) - rtx x; int is_dest; +regs_used (rtx x, int is_dest) { enum rtx_code code; const char *fmt; @@ -3341,9 +3441,7 @@ regs_used (x, is_dest) If a blocking instruction is made or recognized, return it. */ static rtx -gen_block_redirect (jump, addr, need_block) - rtx jump; - int addr, need_block; +gen_block_redirect (rtx jump, int addr, int need_block) { int dead = 0; rtx prev = prev_nonnote_insn (jump); @@ -3363,6 +3461,14 @@ gen_block_redirect (jump, addr, need_block) else if (recog_memoized (prev) == CODE_FOR_block_branch_redirect) need_block = 0; } + if (GET_CODE (PATTERN (jump)) == RETURN) + { + if (! need_block) + return prev; + /* Reorg even does nasty things with return insns that cause branches + to go out of range - see find_end_label and callers. */ + return emit_insn_before (gen_block_branch_redirect (const0_rtx) , jump); + } /* We can't use JUMP_LABEL here because it might be undefined when not optimizing. */ dest = XEXP (SET_SRC (PATTERN (jump)), 0); @@ -3407,7 +3513,7 @@ gen_block_redirect (jump, addr, need_block) if (INSN_DELETED_P (scan)) continue; code = GET_CODE (scan); - if (GET_RTX_CLASS (code) == 'i') + if (INSN_P (scan)) { used |= regs_used (PATTERN (scan), 0); if (code == CALL_INSN) @@ -3441,7 +3547,7 @@ gen_block_redirect (jump, addr, need_block) rtx next = next_active_insn (next_active_insn (dest)); if (next && GET_CODE (next) == JUMP_INSN && GET_CODE (PATTERN (next)) == SET - && recog_memoized (next) == CODE_FOR_jump) + && recog_memoized (next) == CODE_FOR_jump_compact) { dest = JUMP_LABEL (next); if (dest @@ -3471,7 +3577,7 @@ gen_block_redirect (jump, addr, need_block) NOTE_INSN_BLOCK_END notes between the indirect_jump_scratch and the jump. */ - INSN_SCOPE (insn) = INSN_SCOPE (jump); + INSN_LOCATOR (insn) = INSN_LOCATOR (jump); INSN_CODE (insn) = CODE_FOR_indirect_jump_scratch; return insn; } @@ -3502,11 +3608,10 @@ struct far_branch int address; }; -static void gen_far_branch PARAMS ((struct far_branch *)); +static void gen_far_branch (struct far_branch *); enum mdep_reorg_phase_e mdep_reorg_phase; static void -gen_far_branch (bp) - struct far_branch *bp; +gen_far_branch (struct far_branch *bp) { rtx insn = bp->insert_place; rtx jump; @@ -3522,7 +3627,7 @@ gen_far_branch (bp) jump = emit_jump_insn_after (gen_return (), insn); /* Emit a barrier so that reorg knows that any following instructions are not reachable via a fall-through path. - But don't do this when not optimizing, since we wouldn't supress the + But don't do this when not optimizing, since we wouldn't suppress the alignment for the barrier then, and could end up with out-of-range pc-relative loads. */ if (optimize) @@ -3531,19 +3636,23 @@ gen_far_branch (bp) JUMP_LABEL (jump) = bp->far_label; if (! invert_jump (insn, label, 1)) abort (); - (emit_insn_after - (gen_stuff_delay_slot - (GEN_INT (INSN_UID (XEXP (SET_SRC (PATTERN (jump)), 0))), - GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)), - insn)); + /* If we are branching around a jump (rather than a return), prevent + reorg from using an insn from the jump target as the delay slot insn - + when reorg did this, it pessimized code (we rather hide the delay slot) + and it could cause branches to go out of range. */ + if (bp->far_label) + (emit_insn_after + (gen_stuff_delay_slot + (GEN_INT (INSN_UID (XEXP (SET_SRC (PATTERN (jump)), 0))), + GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)), + insn)); /* Prevent reorg from undoing our splits. */ gen_block_redirect (jump, bp->address += 2, 2); } /* Fix up ADDR_DIFF_VECs. */ void -fixup_addr_diff_vecs (first) - rtx first; +fixup_addr_diff_vecs (rtx first) { rtx insn; @@ -3587,8 +3696,7 @@ fixup_addr_diff_vecs (first) /* BARRIER_OR_LABEL is either a BARRIER or a CODE_LABEL immediately following a barrier. Return the base 2 logarithm of the desired alignment. */ int -barrier_align (barrier_or_label) - rtx barrier_or_label; +barrier_align (rtx barrier_or_label) { rtx next = next_real_insn (barrier_or_label), pat, prev; int slot, credit, jump_to_next = 0; @@ -3613,7 +3721,7 @@ barrier_align (barrier_or_label) the table to the minimum for proper code alignment. */ return ((TARGET_SMALLCODE || ((unsigned) XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat)) - <= (unsigned)1 << (CACHE_LOG - 2))) + <= (unsigned) 1 << (CACHE_LOG - 2))) ? 1 << TARGET_SHMEDIA : align_jumps_log); } @@ -3686,7 +3794,8 @@ barrier_align (barrier_or_label) || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))), (INSN_P (x) && (INSN_CODE (x) == CODE_FOR_block_branch_redirect - || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch)))) + || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch + || INSN_CODE (x) == CODE_FOR_stuff_delay_slot)))) { rtx pat = PATTERN (prev); if (GET_CODE (pat) == PARALLEL) @@ -3707,8 +3816,7 @@ barrier_align (barrier_or_label) Applying loop alignment to small constant or switch tables is a waste of space, so we suppress this too. */ int -sh_loop_align (label) - rtx label; +sh_loop_align (rtx label) { rtx next = label; @@ -3725,20 +3833,19 @@ sh_loop_align (label) return align_loops_log; } -/* Exported to toplev.c. - - Do a final pass over the function, just before delayed branch +/* Do a final pass over the function, just before delayed branch scheduling. */ -void -machine_dependent_reorg (first) - rtx first; +static void +sh_reorg (void) { - rtx insn, mova = NULL_RTX; + rtx first, insn, mova = NULL_RTX; int num_mova; rtx r0_rtx = gen_rtx_REG (Pmode, 0); rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx); + first = get_insns (); + /* We must split call insns before introducing `mova's. If we're optimizing, they'll have already been split. Otherwise, make sure we don't split them too late. */ @@ -4077,8 +4184,10 @@ machine_dependent_reorg (first) if (GET_CODE (dst) == REG && FP_ANY_REGISTER_P (REGNO (dst))) { /* This must be an insn that clobbers r0. */ - rtx clobber = XVECEXP (PATTERN (scan), 0, - XVECLEN (PATTERN (scan), 0) - 1); + rtx *clobberp = &XVECEXP (PATTERN (scan), 0, + XVECLEN (PATTERN (scan), 0) + - 1); + rtx clobber = *clobberp; if (GET_CODE (clobber) != CLOBBER || ! rtx_equal_p (XEXP (clobber, 0), r0_rtx)) @@ -4114,7 +4223,7 @@ machine_dependent_reorg (first) } last_float_move = scan; last_float = src; - newsrc = gen_rtx (MEM, mode, + newsrc = gen_rtx_MEM (mode, (((TARGET_SH4 && ! TARGET_FMOVD) || REGNO (dst) == FPUL_REG) ? r0_inc_rtx @@ -4122,7 +4231,8 @@ machine_dependent_reorg (first) last_float_addr = &XEXP (newsrc, 0); /* Remove the clobber of r0. */ - XEXP (clobber, 0) = gen_rtx_SCRATCH (Pmode); + *clobberp = gen_rtx_CLOBBER (GET_MODE (clobber), + gen_rtx_SCRATCH (Pmode)); RTX_UNCHANGING_P (newsrc) = 1; } /* This is a mova needing a label. Create it. */ @@ -4183,9 +4293,7 @@ machine_dependent_reorg (first) } int -get_dest_uid (label, max_uid) - rtx label; - int max_uid; +get_dest_uid (rtx label, int max_uid) { rtx dest = next_real_insn (label); int dest_uid; @@ -4214,8 +4322,7 @@ get_dest_uid (label, max_uid) find branches with common targets more easily. */ static void -split_branches (first) - rtx first; +split_branches (rtx first) { rtx insn; struct far_branch **uid_branch, *far_branch_list = 0; @@ -4330,7 +4437,7 @@ split_branches (first) || ((beyond = next_active_insn (beyond)) && GET_CODE (beyond) == JUMP_INSN)) && GET_CODE (PATTERN (beyond)) == SET - && recog_memoized (beyond) == CODE_FOR_jump + && recog_memoized (beyond) == CODE_FOR_jump_compact && ((INSN_ADDRESSES (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0))) - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252) @@ -4344,7 +4451,7 @@ split_branches (first) if ((GET_CODE (next) == JUMP_INSN || GET_CODE (next = next_active_insn (next)) == JUMP_INSN) && GET_CODE (PATTERN (next)) == SET - && recog_memoized (next) == CODE_FOR_jump + && recog_memoized (next) == CODE_FOR_jump_compact && ((INSN_ADDRESSES (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0))) - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252) @@ -4439,10 +4546,8 @@ split_branches (first) does not bother to update them. */ void -final_prescan_insn (insn, opvec, noperands) - rtx insn; - rtx *opvec ATTRIBUTE_UNUSED; - int noperands ATTRIBUTE_UNUSED; +final_prescan_insn (rtx insn, rtx *opvec ATTRIBUTE_UNUSED, + int noperands ATTRIBUTE_UNUSED) { if (TARGET_DUMPISIZE) fprintf (asm_out_file, "\n! at %04x\n", INSN_ADDRESSES (INSN_UID (insn))); @@ -4478,7 +4583,7 @@ final_prescan_insn (insn, opvec, noperands) only be labels. */ const char * -output_jump_label_table () +output_jump_label_table (void) { int i; @@ -4523,17 +4628,16 @@ output_jump_label_table () static int extra_push; -/* Adjust the stack by SIZE bytes. REG holds the rtl of the register - to be adjusted, and TEMP, if nonnegative, holds the register number - of a general register that we may clobber. */ +/* Adjust the stack by SIZE bytes. REG holds the rtl of the register to be + adjusted. If epilogue_p is zero, this is for a prologue; otherwise, it's + for an epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET + of all the registers that are about to be restored, and hence dead. */ static void -output_stack_adjust (size, reg, temp, emit_fn) - int size; - rtx reg; - int temp; - rtx (*emit_fn) PARAMS ((rtx)); +output_stack_adjust (int size, rtx reg, int epilogue_p, + HARD_REG_SET *live_regs_mask) { + rtx (*emit_fn) (rtx) = epilogue_p ? &emit_insn : &frame_insn; if (size) { HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT; @@ -4556,10 +4660,43 @@ output_stack_adjust (size, reg, temp, emit_fn) { rtx const_reg; rtx insn; + int temp = epilogue_p ? 7 : (TARGET_SH5 ? 0 : 1); + int i; /* If TEMP is invalid, we could temporarily save a general register to MACL. However, there is currently no need to handle this case, so just abort when we see it. */ + if (current_function_interrupt + || ! call_used_regs[temp] || fixed_regs[temp]) + temp = -1; + if (temp < 0 && ! current_function_interrupt) + { + HARD_REG_SET temps; + COPY_HARD_REG_SET (temps, call_used_reg_set); + AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set); + if (epilogue_p) + { + for (i = 0; i < HARD_REGNO_NREGS (FIRST_RET_REG, DImode); i++) + CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i); + if (current_function_calls_eh_return) + { + CLEAR_HARD_REG_BIT (temps, EH_RETURN_STACKADJ_REGNO); + for (i = 0; i <= 3; i++) + CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i)); + } + } + else + { + for (i = FIRST_PARM_REG; + i < FIRST_PARM_REG + NPARM_REGS (SImode); i++) + CLEAR_HARD_REG_BIT (temps, i); + if (current_function_needs_context) + CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM); + } + temp = scavenge_reg (&temps); + } + if (temp < 0 && live_regs_mask) + temp = scavenge_reg (live_regs_mask); if (temp < 0) abort (); const_reg = gen_rtx_REG (GET_MODE (reg), temp); @@ -4577,7 +4714,7 @@ output_stack_adjust (size, reg, temp, emit_fn) emit_insn (GEN_MOV (const_reg, GEN_INT (size))); insn = emit_fn (GEN_ADD3 (reg, reg, const_reg)); } - if (emit_fn == frame_insn) + if (! epilogue_p) REG_NOTES (insn) = (gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, @@ -4589,8 +4726,7 @@ output_stack_adjust (size, reg, temp, emit_fn) } static rtx -frame_insn (x) - rtx x; +frame_insn (rtx x) { x = emit_insn (x); RTX_FRAME_RELATED_P (x) = 1; @@ -4600,8 +4736,7 @@ frame_insn (x) /* Output RTL to push register RN onto the stack. */ static rtx -push (rn) - int rn; +push (int rn) { rtx x; if (rn == FPUL_REG) @@ -4630,8 +4765,7 @@ push (rn) /* Output RTL to pop register RN from the stack. */ static void -pop (rn) - int rn; +pop (int rn) { rtx x; if (rn == FPUL_REG) @@ -4659,9 +4793,7 @@ pop (rn) /* Generate code to push the regs specified in the mask. */ static void -push_regs (mask, interrupt_handler) - HARD_REG_SET *mask; - int interrupt_handler; +push_regs (HARD_REG_SET *mask, int interrupt_handler) { int i; int skip_fpscr = 0; @@ -4680,7 +4812,7 @@ push_regs (mask, interrupt_handler) HARD_REG_SET unsaved; push (FPSCR_REG); - COMPL_HARD_REG_SET(unsaved, *mask); + COMPL_HARD_REG_SET (unsaved, *mask); fpscr_set_from_mem (NORMAL_MODE (FP_MODE), unsaved); skip_fpscr = 1; } @@ -4693,6 +4825,53 @@ push_regs (mask, interrupt_handler) push (PR_REG); } +/* Calculate how much extra space is needed to save all callee-saved + target registers. + LIVE_REGS_MASK is the register mask calculated by calc_live_regs. */ + +static int +shmedia_target_regs_stack_space (HARD_REG_SET *live_regs_mask) +{ + int reg; + int stack_space = 0; + int interrupt_handler = sh_cfun_interrupt_handler_p (); + + for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--) + if ((! call_used_regs[reg] || interrupt_handler) + && ! TEST_HARD_REG_BIT (*live_regs_mask, reg)) + /* Leave space to save this target register on the stack, + in case target register allocation wants to use it. */ + stack_space += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg)); + return stack_space; +} + +/* Decide whether we should reserve space for callee-save target registers, + in case target register allocation wants to use them. REGS_SAVED is + the space, in bytes, that is already required for register saves. + LIVE_REGS_MASK is the register mask calculated by calc_live_regs. */ + +static int +shmedia_reserve_space_for_target_registers_p (int regs_saved, + HARD_REG_SET *live_regs_mask) +{ + if (optimize_size) + return 0; + return shmedia_target_regs_stack_space (live_regs_mask) <= regs_saved; +} + +/* Decide how much space to reserve for callee-save target registers + in case target register allocation wants to use them. + LIVE_REGS_MASK is the register mask calculated by calc_live_regs. */ + +static int +shmedia_target_regs_stack_adjust (HARD_REG_SET *live_regs_mask) +{ + if (shmedia_space_reserved_for_target_registers) + return shmedia_target_regs_stack_space (live_regs_mask); + else + return 0; +} + /* Work out the registers which need to be saved, both as a mask and a count of saved words. Return the count. @@ -4701,18 +4880,16 @@ push_regs (mask, interrupt_handler) make sure that all the regs it clobbers are safe too. */ static int -calc_live_regs (live_regs_mask) - HARD_REG_SET *live_regs_mask; +calc_live_regs (HARD_REG_SET *live_regs_mask) { int reg; int count; int interrupt_handler; - int pr_live; + int pr_live, has_call; interrupt_handler = sh_cfun_interrupt_handler_p (); - for (count = 0; 32 * count < FIRST_PSEUDO_REGISTER; count++) - CLEAR_HARD_REG_SET (*live_regs_mask); + CLEAR_HARD_REG_SET (*live_regs_mask); if (TARGET_SH4 && TARGET_FMOVD && interrupt_handler && regs_ever_live[FPSCR_REG]) target_flags &= ~FPU_SINGLE_BIT; @@ -4731,7 +4908,9 @@ calc_live_regs (live_regs_mask) the initial value can become the PR_MEDIA_REG hard register, as seen for execute/20010122-1.c:test9. */ if (TARGET_SHMEDIA) - pr_live = regs_ever_live[PR_MEDIA_REG]; + /* ??? this function is called from initial_elimination_offset, hence we + can't use the result of sh_media_register_for_return here. */ + pr_live = sh_pr_n_sets (); else { rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG); @@ -4739,6 +4918,10 @@ calc_live_regs (live_regs_mask) ? (GET_CODE (pr_initial) != REG || REGNO (pr_initial) != (PR_REG)) : regs_ever_live[PR_REG]); + /* For Shcompact, if not optimizing, we end up with a memory reference + using the return address pointer for __builtin_return_address even + though there is no actual need to put the PR register on the stack. */ + pr_live |= regs_ever_live[RETURN_ADDRESS_POINTER_REGNUM]; } /* Force PR to be live if the prologue has to call the SHmedia argument decoder or register saver. */ @@ -4747,6 +4930,7 @@ calc_live_regs (live_regs_mask) & ~ CALL_COOKIE_RET_TRAMP (1)) || current_function_has_nonlocal_label)) pr_live = 1; + has_call = TARGET_SHMEDIA ? ! leaf_function_p () : pr_live; for (count = 0, reg = FIRST_PSEUDO_REGISTER - 1; reg >= 0; reg--) { if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG) @@ -4756,7 +4940,9 @@ calc_live_regs (live_regs_mask) (regs_ever_live[reg] || (call_used_regs[reg] && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG) - && pr_live)) + && has_call) + || (has_call && REGISTER_NATURAL_MODE (reg) == SImode + && (GENERAL_REGISTER_P (reg) || TARGET_REGISTER_P (reg)))) && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM && reg != RETURN_ADDRESS_POINTER_REGNUM && reg != T_REG && reg != GBR_REG @@ -4766,13 +4952,17 @@ calc_live_regs (live_regs_mask) (TARGET_SHCOMPACT && flag_pic && current_function_args_info.call_cookie - && reg == PIC_OFFSET_TABLE_REGNUM) + && reg == (int) PIC_OFFSET_TABLE_REGNUM) || (regs_ever_live[reg] && ! call_used_regs[reg]) || (current_function_calls_eh_return - && (reg == EH_RETURN_DATA_REGNO (0) - || reg == EH_RETURN_DATA_REGNO (1) - || reg == EH_RETURN_DATA_REGNO (2) - || reg == EH_RETURN_DATA_REGNO (3))))) + && (reg == (int) EH_RETURN_DATA_REGNO (0) + || reg == (int) EH_RETURN_DATA_REGNO (1) + || reg == (int) EH_RETURN_DATA_REGNO (2) + || reg == (int) EH_RETURN_DATA_REGNO (3))) + || ((reg == MACL_REG || reg == MACH_REG) + && regs_ever_live[reg] + && sh_cfun_attr_renesas_p ()) + )) { SET_HARD_REG_BIT (*live_regs_mask, reg); count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg)); @@ -4796,6 +4986,32 @@ calc_live_regs (live_regs_mask) } } } + /* If we have a target register optimization pass after prologue / epilogue + threading, we need to assume all target registers will be live even if + they aren't now. */ + if (flag_branch_target_load_optimize2 + && TARGET_SAVE_ALL_TARGET_REGS + && shmedia_space_reserved_for_target_registers) + for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--) + if ((! call_used_regs[reg] || interrupt_handler) + && ! TEST_HARD_REG_BIT (*live_regs_mask, reg)) + { + SET_HARD_REG_BIT (*live_regs_mask, reg); + count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg)); + } + /* If this is an interrupt handler, we don't have any call-clobbered + registers we can conveniently use for target register save/restore. + Make sure we save at least one general purpose register when we need + to save target registers. */ + if (interrupt_handler + && hard_regs_intersect_p (live_regs_mask, + ®_class_contents[TARGET_REGS]) + && ! hard_regs_intersect_p (live_regs_mask, + ®_class_contents[GENERAL_REGS])) + { + SET_HARD_REG_BIT (*live_regs_mask, R0_REG); + count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (R0_REG)); + } return count; } @@ -4806,8 +5022,7 @@ calc_live_regs (live_regs_mask) stack for register saves. Return the frame size, padded appropriately so that the stack stays properly aligned. */ static HOST_WIDE_INT -rounded_frame_size (pushed) - int pushed; +rounded_frame_size (int pushed) { HOST_WIDE_INT size = get_frame_size (); HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT; @@ -4819,13 +5034,16 @@ rounded_frame_size (pushed) unchanged along the whole function. We set it up as the return value in the prologue. */ int -sh_media_register_for_return () +sh_media_register_for_return (void) { int regno; int tr0_used; if (! current_function_is_leaf) return -1; + if (lookup_attribute ("interrupt_handler", + DECL_ATTRIBUTES (current_function_decl))) + return -1; tr0_used = flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]; @@ -4836,8 +5054,132 @@ sh_media_register_for_return () return -1; } +/* The maximum registers we need to save are: + - 62 general purpose registers (r15 is stack pointer, r63 is zero) + - 32 floating point registers (for each pair, we save none, + one single precision value, or a double precision value). + - 8 target registers + - add 1 entry for a delimiter. */ +#define MAX_SAVED_REGS (62+32+8) + +typedef struct save_entry_s +{ + unsigned char reg; + unsigned char mode; + short offset; +} save_entry; + +#define MAX_TEMPS 4 + +/* There will be a delimiter entry with VOIDmode both at the start and the + end of a filled in schedule. The end delimiter has the offset of the + save with the smallest (i.e. most negative) offset. */ +typedef struct save_schedule_s +{ + save_entry entries[MAX_SAVED_REGS + 2]; + int temps[MAX_TEMPS+1]; +} save_schedule; + +/* Fill in SCHEDULE according to LIVE_REGS_MASK. If RESTORE is nonzero, + use reverse order. Returns the last entry written to (not counting + the delimiter). OFFSET_BASE is a number to be added to all offset + entries. */ + +static save_entry * +sh5_schedule_saves (HARD_REG_SET *live_regs_mask, save_schedule *schedule, + int offset_base) +{ + int align, i; + save_entry *entry = schedule->entries; + int tmpx = 0; + int offset; + + if (! current_function_interrupt) + for (i = FIRST_GENERAL_REG; tmpx < MAX_TEMPS && i <= LAST_GENERAL_REG; i++) + if (call_used_regs[i] && ! fixed_regs[i] && i != PR_MEDIA_REG + && ! FUNCTION_ARG_REGNO_P (i) + && i != FIRST_RET_REG + && ! (current_function_needs_context && i == STATIC_CHAIN_REGNUM) + && ! (current_function_calls_eh_return + && (i == EH_RETURN_STACKADJ_REGNO + || ((unsigned) i <= EH_RETURN_DATA_REGNO (0) + && (unsigned) i >= EH_RETURN_DATA_REGNO (3))))) + schedule->temps[tmpx++] = i; + entry->reg = -1; + entry->mode = VOIDmode; + entry->offset = offset_base; + entry++; + /* We loop twice: first, we save 8-byte aligned registers in the + higher addresses, that are known to be aligned. Then, we + proceed to saving 32-bit registers that don't need 8-byte + alignment. + If this is an interrupt function, all registers that need saving + need to be saved in full. moreover, we need to postpone saving + target registers till we have saved some general purpose registers + we can then use as scratch registers. */ + offset = offset_base; + for (align = 1; align >= 0; align--) + { + for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) + if (TEST_HARD_REG_BIT (*live_regs_mask, i)) + { + enum machine_mode mode = REGISTER_NATURAL_MODE (i); + int reg = i; + + if (current_function_interrupt) + { + if (TARGET_REGISTER_P (i)) + continue; + if (GENERAL_REGISTER_P (i)) + mode = DImode; + } + if (mode == SFmode && (i % 2) == 1 + && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i) + && (TEST_HARD_REG_BIT (*live_regs_mask, (i ^ 1)))) + { + mode = DFmode; + i--; + reg--; + } + + /* If we're doing the aligned pass and this is not aligned, + or we're doing the unaligned pass and this is aligned, + skip it. */ + if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) == 0) + != align) + continue; + + if (current_function_interrupt + && GENERAL_REGISTER_P (i) + && tmpx < MAX_TEMPS) + schedule->temps[tmpx++] = i; + + offset -= GET_MODE_SIZE (mode); + entry->reg = i; + entry->mode = mode; + entry->offset = offset; + entry++; + } + if (align && current_function_interrupt) + for (i = LAST_TARGET_REG; i >= FIRST_TARGET_REG; i--) + if (TEST_HARD_REG_BIT (*live_regs_mask, i)) + { + offset -= GET_MODE_SIZE (DImode); + entry->reg = i; + entry->mode = DImode; + entry->offset = offset; + entry++; + } + } + entry->reg = -1; + entry->mode = VOIDmode; + entry->offset = offset; + schedule->temps[tmpx] = -1; + return entry - 1; +} + void -sh_expand_prologue () +sh_expand_prologue (void) { HARD_REG_SET live_regs_mask; int d, i; @@ -4850,7 +5192,7 @@ sh_expand_prologue () and partially on the stack, e.g. a large structure. */ output_stack_adjust (-current_function_pretend_args_size - current_function_args_info.stack_regs * 8, - stack_pointer_rtx, TARGET_SH5 ? 0 : 1, frame_insn); + stack_pointer_rtx, 0, NULL); extra_push = 0; @@ -4896,6 +5238,9 @@ sh_expand_prologue () rtx insn = emit_move_insn (gen_rtx_REG (DImode, tr), gen_rtx_REG (DImode, PR_MEDIA_REG)); + /* ??? We should suppress saving pr when we don't need it, but this + is tricky because of builtin_return_address. */ + /* If this function only exits with sibcalls, this copy will be flagged as dead. */ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, @@ -4908,7 +5253,8 @@ sh_expand_prologue () if (current_function_stdarg) { /* This is not used by the SH2E calling convention */ - if (TARGET_SH1 && ! TARGET_SH2E && ! TARGET_SH5 && ! TARGET_HITACHI) + if (TARGET_SH1 && ! TARGET_SH2E && ! TARGET_SH5 + && ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ())) { /* Push arg regs as if they'd been provided by caller in stack. */ for (i = 0; i < NPARM_REGS(SImode); i++) @@ -4939,155 +5285,191 @@ sh_expand_prologue () if (TARGET_SH5) { - int i; - int offset; - int align; - rtx r0 = gen_rtx_REG (Pmode, R0_REG); + int offset_base, offset; + rtx r0 = NULL_RTX; int offset_in_r0 = -1; int sp_in_r0 = 0; - - if (d % (STACK_BOUNDARY / BITS_PER_UNIT)) + int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask); + int total_size, save_size; + save_schedule schedule; + save_entry *entry; + int *tmp_pnt; + + if (call_used_regs[R0_REG] && ! fixed_regs[R0_REG] + && ! current_function_interrupt) + r0 = gen_rtx_REG (Pmode, R0_REG); + + /* D is the actual number of bytes that we need for saving registers, + however, in initial_elimination_offset we have committed to using + an additional TREGS_SPACE amount of bytes - in order to keep both + addresses to arguments supplied by the caller and local variables + valid, we must keep this gap. Place it between the incoming + arguments and the actually saved registers in a bid to optimize + locality of reference. */ + total_size = d + tregs_space; + total_size += rounded_frame_size (total_size); + save_size = total_size - rounded_frame_size (d); + if (save_size % (STACK_BOUNDARY / BITS_PER_UNIT)) d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT) - - d % (STACK_BOUNDARY / BITS_PER_UNIT)); - - offset = d + d_rounding; - output_stack_adjust (-offset, stack_pointer_rtx, 1, frame_insn); - - /* We loop twice: first, we save 8-byte aligned registers in the - higher addresses, that are known to be aligned. Then, we - proceed to saving 32-bit registers that don't need 8-byte - alignment. */ - /* Note that if you change this code in a way that affects where - the return register is saved, you have to update not only - sh_expand_epilogue, but also sh_set_return_address. */ - for (align = 1; align >= 0; align--) - for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) - if (TEST_HARD_REG_BIT (live_regs_mask, i)) - { - enum machine_mode mode = REGISTER_NATURAL_MODE (i); - int reg = i; - rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX; - - if (mode == SFmode && (i % 2) == 1 - && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i) - && (TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1)))) - { - mode = DFmode; - i--; - reg--; - } - - /* If we're doing the aligned pass and this is not aligned, - or we're doing the unaligned pass and this is aligned, - skip it. */ - if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) - == 0) != align) - continue; + - save_size % (STACK_BOUNDARY / BITS_PER_UNIT)); + + /* If adjusting the stack in a single step costs nothing extra, do so. + I.e. either if a single addi is enough, or we need a movi anyway, + and we don't exceed the maximum offset range (the test for the + latter is conservative for simplicity). */ + if (TARGET_SHMEDIA + && (CONST_OK_FOR_I10 (-total_size) + || (! CONST_OK_FOR_I10 (-(save_size + d_rounding)) + && total_size <= 2044))) + d_rounding = total_size - save_size; + + offset_base = d + d_rounding; + + output_stack_adjust (-(save_size + d_rounding), stack_pointer_rtx, + 0, NULL); + + sh5_schedule_saves (&live_regs_mask, &schedule, offset_base); + tmp_pnt = schedule.temps; + for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++) + { + enum machine_mode mode = entry->mode; + int reg = entry->reg; + rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX; - offset -= GET_MODE_SIZE (mode); + offset = entry->offset; - reg_rtx = gen_rtx_REG (mode, reg); + reg_rtx = gen_rtx_REG (mode, reg); - mem_rtx = gen_rtx_MEM (mode, - gen_rtx_PLUS (Pmode, - stack_pointer_rtx, - GEN_INT (offset))); + mem_rtx = gen_rtx_MEM (mode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + GEN_INT (offset))); - GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec); + GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec); - mem_rtx = NULL_RTX; + if (! r0) + abort (); + mem_rtx = NULL_RTX; - try_pre_dec: - do - if (HAVE_PRE_DECREMENT - && (offset_in_r0 - offset == GET_MODE_SIZE (mode) - || mem_rtx == NULL_RTX - || i == PR_REG || SPECIAL_REGISTER_P (i))) - { - pre_dec = gen_rtx_MEM (mode, - gen_rtx_PRE_DEC (Pmode, r0)); + try_pre_dec: + do + if (HAVE_PRE_DECREMENT + && (offset_in_r0 - offset == GET_MODE_SIZE (mode) + || mem_rtx == NULL_RTX + || reg == PR_REG || SPECIAL_REGISTER_P (reg))) + { + pre_dec = gen_rtx_MEM (mode, + gen_rtx_PRE_DEC (Pmode, r0)); - GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0), - pre_dec_ok); + GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0), + pre_dec_ok); - pre_dec = NULL_RTX; + pre_dec = NULL_RTX; - break; + break; - pre_dec_ok: - mem_rtx = NULL_RTX; - offset += GET_MODE_SIZE (mode); - } - while (0); + pre_dec_ok: + mem_rtx = NULL_RTX; + offset += GET_MODE_SIZE (mode); + } + while (0); - if (mem_rtx != NULL_RTX) - goto addr_ok; + if (mem_rtx != NULL_RTX) + goto addr_ok; - if (offset_in_r0 == -1) - { - emit_move_insn (r0, GEN_INT (offset)); - offset_in_r0 = offset; - } - else if (offset != offset_in_r0) + if (offset_in_r0 == -1) + { + emit_move_insn (r0, GEN_INT (offset)); + offset_in_r0 = offset; + } + else if (offset != offset_in_r0) + { + emit_move_insn (r0, + gen_rtx_PLUS + (Pmode, r0, + GEN_INT (offset - offset_in_r0))); + offset_in_r0 += offset - offset_in_r0; + } + + if (pre_dec != NULL_RTX) + { + if (! sp_in_r0) { emit_move_insn (r0, gen_rtx_PLUS - (Pmode, r0, - GEN_INT (offset - offset_in_r0))); - offset_in_r0 += offset - offset_in_r0; + (Pmode, r0, stack_pointer_rtx)); + sp_in_r0 = 1; } - - if (pre_dec != NULL_RTX) - { - if (! sp_in_r0) - { - emit_move_insn (r0, - gen_rtx_PLUS - (Pmode, r0, stack_pointer_rtx)); - sp_in_r0 = 1; - } - offset -= GET_MODE_SIZE (mode); - offset_in_r0 -= GET_MODE_SIZE (mode); + offset -= GET_MODE_SIZE (mode); + offset_in_r0 -= GET_MODE_SIZE (mode); - mem_rtx = pre_dec; - } - else if (sp_in_r0) - mem_rtx = gen_rtx_MEM (mode, r0); - else - mem_rtx = gen_rtx_MEM (mode, - gen_rtx_PLUS (Pmode, - stack_pointer_rtx, - r0)); - - /* We must not use an r0-based address for target-branch - registers or for special registers without pre-dec - memory addresses, since we store their values in r0 - first. */ - if (TARGET_REGISTER_P (i) - || ((i == PR_REG || SPECIAL_REGISTER_P (i)) - && mem_rtx != pre_dec)) - abort (); - - addr_ok: - if (TARGET_REGISTER_P (i) - || ((i == PR_REG || SPECIAL_REGISTER_P (i)) - && mem_rtx != pre_dec)) - { - rtx r0mode = gen_rtx_REG (GET_MODE (reg_rtx), R0_REG); + mem_rtx = pre_dec; + } + else if (sp_in_r0) + mem_rtx = gen_rtx_MEM (mode, r0); + else + mem_rtx = gen_rtx_MEM (mode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + r0)); + + /* We must not use an r0-based address for target-branch + registers or for special registers without pre-dec + memory addresses, since we store their values in r0 + first. */ + if (TARGET_REGISTER_P (reg) + || ((reg == PR_REG || SPECIAL_REGISTER_P (reg)) + && mem_rtx != pre_dec)) + abort (); - emit_move_insn (r0mode, reg_rtx); + addr_ok: + if (TARGET_REGISTER_P (reg) + || ((reg == PR_REG || SPECIAL_REGISTER_P (reg)) + && mem_rtx != pre_dec)) + { + rtx tmp_reg = gen_rtx_REG (GET_MODE (reg_rtx), *tmp_pnt); + + emit_move_insn (tmp_reg, reg_rtx); + if (REGNO (tmp_reg) == R0_REG) + { offset_in_r0 = -1; sp_in_r0 = 0; - - reg_rtx = r0mode; + if (refers_to_regno_p (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0)) + abort (); } - emit_move_insn (mem_rtx, reg_rtx); + if (*++tmp_pnt <= 0) + tmp_pnt = schedule.temps; + + reg_rtx = tmp_reg; } + { + rtx insn; + + /* Mark as interesting for dwarf cfi generator */ + insn = emit_move_insn (mem_rtx, reg_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + + if (TARGET_SHCOMPACT && (offset_in_r0 != -1)) + { + rtx reg_rtx = gen_rtx_REG (mode, reg); + rtx set, note_rtx; + rtx mem_rtx = gen_rtx_MEM (mode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + GEN_INT (offset))); + + set = gen_rtx_SET (VOIDmode, mem_rtx, reg_rtx); + note_rtx = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, set, + REG_NOTES (insn)); + REG_NOTES (insn) = note_rtx; + } + } + } - if (offset != d_rounding) + if (entry->offset != d_rounding) abort (); } else @@ -5115,10 +5497,9 @@ sh_expand_prologue () if (SHMEDIA_REGS_STACK_ADJUST ()) { emit_move_insn (gen_rtx_REG (Pmode, R0_REG), - gen_rtx_SYMBOL_REF (Pmode, - TARGET_FPU_ANY - ? "__GCC_push_shmedia_regs" - : "__GCC_push_shmedia_regs_nofpu")); + function_symbol (TARGET_FPU_ANY + ? "__GCC_push_shmedia_regs" + : "__GCC_push_shmedia_regs_nofpu")); /* This must NOT go through the PLT, otherwise mach and macl may be clobbered. */ emit_insn (gen_shmedia_save_restore_regs_compact @@ -5140,7 +5521,7 @@ sh_expand_prologue () target_flags = save_flags; output_stack_adjust (-rounded_frame_size (d) + d_rounding, - stack_pointer_rtx, TARGET_SH5 ? 0 : 1, frame_insn); + stack_pointer_rtx, 0, NULL); if (frame_pointer_needed) frame_insn (GEN_MOV (frame_pointer_rtx, stack_pointer_rtx)); @@ -5151,34 +5532,56 @@ sh_expand_prologue () /* This must NOT go through the PLT, otherwise mach and macl may be clobbered. */ emit_move_insn (gen_rtx_REG (Pmode, R0_REG), - gen_rtx_SYMBOL_REF (Pmode, - "__GCC_shcompact_incoming_args")); + function_symbol ("__GCC_shcompact_incoming_args")); emit_insn (gen_shcompact_incoming_args ()); } } void -sh_expand_epilogue () +sh_expand_epilogue (void) { HARD_REG_SET live_regs_mask; int d, i; int d_rounding = 0; int save_flags = target_flags; - int frame_size; + int frame_size, save_size; int fpscr_deferred = 0; d = calc_live_regs (&live_regs_mask); - if (TARGET_SH5 && d % (STACK_BOUNDARY / BITS_PER_UNIT)) - d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT) - - d % (STACK_BOUNDARY / BITS_PER_UNIT)); + save_size = d; + frame_size = rounded_frame_size (d); + + if (TARGET_SH5) + { + int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask); + int total_size; + if (d % (STACK_BOUNDARY / BITS_PER_UNIT)) + d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT) + - d % (STACK_BOUNDARY / BITS_PER_UNIT)); + + total_size = d + tregs_space; + total_size += rounded_frame_size (total_size); + save_size = total_size - frame_size; + + /* If adjusting the stack in a single step costs nothing extra, do so. + I.e. either if a single addi is enough, or we need a movi anyway, + and we don't exceed the maximum offset range (the test for the + latter is conservative for simplicity). */ + if (TARGET_SHMEDIA + && ! frame_pointer_needed + && (CONST_OK_FOR_I10 (total_size) + || (! CONST_OK_FOR_I10 (save_size + d_rounding) + && total_size <= 2044))) + d_rounding = frame_size; - frame_size = rounded_frame_size (d) - d_rounding; + frame_size -= d_rounding; + } if (frame_pointer_needed) { - output_stack_adjust (frame_size, frame_pointer_rtx, 7, emit_insn); + output_stack_adjust (frame_size, frame_pointer_rtx, 1, &live_regs_mask); /* We must avoid moving the stack pointer adjustment past code which reads from the local frame, else an interrupt could @@ -5194,16 +5597,15 @@ sh_expand_epilogue () occur after the SP adjustment and clobber data in the local frame. */ emit_insn (gen_blockage ()); - output_stack_adjust (frame_size, stack_pointer_rtx, 7, emit_insn); + output_stack_adjust (frame_size, stack_pointer_rtx, 1, &live_regs_mask); } if (SHMEDIA_REGS_STACK_ADJUST ()) { emit_move_insn (gen_rtx_REG (Pmode, R0_REG), - gen_rtx_SYMBOL_REF (Pmode, - TARGET_FPU_ANY - ? "__GCC_pop_shmedia_regs" - : "__GCC_pop_shmedia_regs_nofpu")); + function_symbol (TARGET_FPU_ANY + ? "__GCC_pop_shmedia_regs" + : "__GCC_pop_shmedia_regs_nofpu")); /* This must NOT go through the PLT, otherwise mach and macl may be clobbered. */ emit_insn (gen_shmedia_save_restore_regs_compact @@ -5216,173 +5618,159 @@ sh_expand_epilogue () emit_insn (gen_toggle_sz ()); if (TARGET_SH5) { - int offset = d_rounding; + int offset_base, offset; int offset_in_r0 = -1; int sp_in_r0 = 0; - int align; rtx r0 = gen_rtx_REG (Pmode, R0_REG); - int tmp_regno = R20_REG; - - /* We loop twice: first, we save 8-byte aligned registers in the - higher addresses, that are known to be aligned. Then, we - proceed to saving 32-bit registers that don't need 8-byte - alignment. */ - for (align = 0; align <= 1; align++) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (live_regs_mask, i)) - { - enum machine_mode mode = REGISTER_NATURAL_MODE (i); - int reg = i; - rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn; - - if (mode == SFmode && (i % 2) == 0 - && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i) - && (TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1)))) - { - mode = DFmode; - i++; - } + save_schedule schedule; + save_entry *entry; + int *tmp_pnt; + + entry = sh5_schedule_saves (&live_regs_mask, &schedule, d_rounding); + offset_base = -entry[1].offset + d_rounding; + tmp_pnt = schedule.temps; + for (; entry->mode != VOIDmode; entry--) + { + enum machine_mode mode = entry->mode; + int reg = entry->reg; + rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn; - /* If we're doing the aligned pass and this is not aligned, - or we're doing the unaligned pass and this is aligned, - skip it. */ - if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) - == 0) != align) - continue; + offset = offset_base + entry->offset; + reg_rtx = gen_rtx_REG (mode, reg); - reg_rtx = gen_rtx_REG (mode, reg); + mem_rtx = gen_rtx_MEM (mode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + GEN_INT (offset))); - mem_rtx = gen_rtx_MEM (mode, - gen_rtx_PLUS (Pmode, - stack_pointer_rtx, - GEN_INT (offset))); + GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc); - GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc); + mem_rtx = NULL_RTX; - mem_rtx = NULL_RTX; + try_post_inc: + do + if (HAVE_POST_INCREMENT + && (offset == offset_in_r0 + || (offset + GET_MODE_SIZE (mode) != d + d_rounding + && mem_rtx == NULL_RTX) + || reg == PR_REG || SPECIAL_REGISTER_P (reg))) + { + post_inc = gen_rtx_MEM (mode, + gen_rtx_POST_INC (Pmode, r0)); - try_post_inc: - do - if (HAVE_POST_INCREMENT - && (offset == offset_in_r0 - || (offset + GET_MODE_SIZE (mode) != d + d_rounding - && mem_rtx == NULL_RTX) - || i == PR_REG || SPECIAL_REGISTER_P (i))) - { - post_inc = gen_rtx_MEM (mode, - gen_rtx_POST_INC (Pmode, r0)); + GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0), + post_inc_ok); - GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0), - post_inc_ok); + post_inc = NULL_RTX; - post_inc = NULL_RTX; + break; + + post_inc_ok: + mem_rtx = NULL_RTX; + } + while (0); + + if (mem_rtx != NULL_RTX) + goto addr_ok; - break; - - post_inc_ok: - mem_rtx = NULL_RTX; - } - while (0); + if (offset_in_r0 == -1) + { + emit_move_insn (r0, GEN_INT (offset)); + offset_in_r0 = offset; + } + else if (offset != offset_in_r0) + { + emit_move_insn (r0, + gen_rtx_PLUS + (Pmode, r0, + GEN_INT (offset - offset_in_r0))); + offset_in_r0 += offset - offset_in_r0; + } - if (mem_rtx != NULL_RTX) - goto addr_ok; - - if (offset_in_r0 == -1) - { - emit_move_insn (r0, GEN_INT (offset)); - offset_in_r0 = offset; - } - else if (offset != offset_in_r0) + if (post_inc != NULL_RTX) + { + if (! sp_in_r0) { emit_move_insn (r0, gen_rtx_PLUS - (Pmode, r0, - GEN_INT (offset - offset_in_r0))); - offset_in_r0 += offset - offset_in_r0; + (Pmode, r0, stack_pointer_rtx)); + sp_in_r0 = 1; } - - if (post_inc != NULL_RTX) - { - if (! sp_in_r0) - { - emit_move_insn (r0, - gen_rtx_PLUS - (Pmode, r0, stack_pointer_rtx)); - sp_in_r0 = 1; - } - - mem_rtx = post_inc; + + mem_rtx = post_inc; - offset_in_r0 += GET_MODE_SIZE (mode); - } - else if (sp_in_r0) - mem_rtx = gen_rtx_MEM (mode, r0); - else - mem_rtx = gen_rtx_MEM (mode, - gen_rtx_PLUS (Pmode, - stack_pointer_rtx, - r0)); - - if ((i == PR_REG || SPECIAL_REGISTER_P (i)) - && mem_rtx != post_inc) - abort (); - - addr_ok: - if ((i == PR_REG || SPECIAL_REGISTER_P (i)) - && mem_rtx != post_inc) - { - insn = emit_move_insn (r0, mem_rtx); - mem_rtx = r0; - } - else if (TARGET_REGISTER_P (i)) - { - rtx tmp_reg = gen_rtx_REG (mode, tmp_regno); - - /* Give the scheduler a bit of freedom by using R20..R23 - in a round-robin fashion. Don't use R1 here because - we want to use it for EH_RETURN_STACKADJ_RTX. */ - insn = emit_move_insn (tmp_reg, mem_rtx); - mem_rtx = tmp_reg; - if (++tmp_regno > R23_REG) - tmp_regno = R20_REG; - } + offset_in_r0 += GET_MODE_SIZE (mode); + } + else if (sp_in_r0) + mem_rtx = gen_rtx_MEM (mode, r0); + else + mem_rtx = gen_rtx_MEM (mode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + r0)); - insn = emit_move_insn (reg_rtx, mem_rtx); + if ((reg == PR_REG || SPECIAL_REGISTER_P (reg)) + && mem_rtx != post_inc) + abort (); - offset += GET_MODE_SIZE (mode); + addr_ok: + if ((reg == PR_REG || SPECIAL_REGISTER_P (reg)) + && mem_rtx != post_inc) + { + insn = emit_move_insn (r0, mem_rtx); + mem_rtx = r0; + } + else if (TARGET_REGISTER_P (reg)) + { + rtx tmp_reg = gen_rtx_REG (mode, *tmp_pnt); + + /* Give the scheduler a bit of freedom by using up to + MAX_TEMPS registers in a round-robin fashion. */ + insn = emit_move_insn (tmp_reg, mem_rtx); + mem_rtx = tmp_reg; + if (*++tmp_pnt < 0) + tmp_pnt = schedule.temps; } - if (offset != d + d_rounding) - abort (); + insn = emit_move_insn (reg_rtx, mem_rtx); + if (reg == PR_MEDIA_REG && sh_media_register_for_return () >= 0) + /* This is dead, unless we return with a sibcall. */ + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, + const0_rtx, + REG_NOTES (insn)); + } - goto finish; + if (entry->offset + offset_base != d + d_rounding) + abort (); } - else - d = 0; - if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)) - pop (PR_REG); - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + else /* ! TARGET_SH5 */ { - int j = (FIRST_PSEUDO_REGISTER - 1) - i; + save_size = 0; + if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)) + pop (PR_REG); + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + int j = (FIRST_PSEUDO_REGISTER - 1) - i; + + if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD + && hard_regs_intersect_p (&live_regs_mask, + ®_class_contents[DF_REGS])) + fpscr_deferred = 1; + else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)) + pop (j); + if (j == FIRST_FP_REG && fpscr_deferred) + pop (FPSCR_REG); - if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD - && hard_regs_intersect_p (&live_regs_mask, - ®_class_contents[DF_REGS])) - fpscr_deferred = 1; - else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)) - pop (j); - if (j == FIRST_FP_REG && fpscr_deferred) - pop (FPSCR_REG); + } } - finish: if (target_flags != save_flags && ! current_function_interrupt) emit_insn (gen_toggle_sz ()); target_flags = save_flags; output_stack_adjust (extra_push + current_function_pretend_args_size - + d + d_rounding + + save_size + d_rounding + current_function_args_info.stack_regs * 8, - stack_pointer_rtx, 7, emit_insn); + stack_pointer_rtx, 1, NULL); if (current_function_calls_eh_return) emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx, @@ -5403,7 +5791,7 @@ sh_expand_epilogue () static int sh_need_epilogue_known = 0; int -sh_need_epilogue () +sh_need_epilogue (void) { if (! sh_need_epilogue_known) { @@ -5422,12 +5810,10 @@ sh_need_epilogue () TEMP is available as a scratch register, if needed. */ void -sh_set_return_address (ra, tmp) - rtx ra, tmp; +sh_set_return_address (rtx ra, rtx tmp) { HARD_REG_SET live_regs_mask; int d; - int d_rounding = 0; int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG; int pr_offset; @@ -5459,56 +5845,26 @@ sh_set_return_address (ra, tmp) if (TARGET_SH5) { - int i; int offset; - int align; + save_schedule schedule; + save_entry *entry; - if (d % (STACK_BOUNDARY / BITS_PER_UNIT)) - d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT) - - d % (STACK_BOUNDARY / BITS_PER_UNIT)); - - offset = 0; - - /* We loop twice: first, we save 8-byte aligned registers in the - higher addresses, that are known to be aligned. Then, we - proceed to saving 32-bit registers that don't need 8-byte - alignment. */ - for (align = 0; align <= 1; align++) - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (TEST_HARD_REG_BIT (live_regs_mask, i)) - { - enum machine_mode mode = REGISTER_NATURAL_MODE (i); - - if (mode == SFmode && (i % 2) == 0 - && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i) - && (TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1)))) - { - mode = DFmode; - i++; - } - - /* If we're doing the aligned pass and this is not aligned, - or we're doing the unaligned pass and this is aligned, - skip it. */ - if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) - == 0) != align) - continue; - - if (i == pr_reg) - goto found; - - offset += GET_MODE_SIZE (mode); - } + entry = sh5_schedule_saves (&live_regs_mask, &schedule, 0); + offset = entry[1].offset; + for (; entry->mode != VOIDmode; entry--) + if (entry->reg == pr_reg) + goto found; /* We can't find pr register. */ abort (); found: - pr_offset = (rounded_frame_size (d) - d_rounding + offset + offset = entry->offset - offset; + pr_offset = (rounded_frame_size (d) + offset + SHMEDIA_REGS_STACK_ADJUST ()); } else - pr_offset = rounded_frame_size (d) - d_rounding; + pr_offset = rounded_frame_size (d); emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset))); emit_insn (GEN_ADD3 (tmp, tmp, frame_pointer_rtx)); @@ -5520,17 +5876,16 @@ sh_set_return_address (ra, tmp) /* Clear variables at function end. */ static void -sh_output_function_epilogue (file, size) - FILE *file ATTRIBUTE_UNUSED; - HOST_WIDE_INT size ATTRIBUTE_UNUSED; +sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, + HOST_WIDE_INT size ATTRIBUTE_UNUSED) { trap_exit = pragma_interrupt = pragma_trapa = pragma_nosave_low_regs = 0; sh_need_epilogue_known = 0; sp_switch = NULL_RTX; } -rtx -sh_builtin_saveregs () +static rtx +sh_builtin_saveregs (void) { /* First unnamed integer register. */ int first_intreg = current_function_args_info.arg_count[(int) SH_ARG_INT]; @@ -5614,7 +5969,7 @@ sh_builtin_saveregs () move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg, adjust_address (regbuf, BLKmode, n_floatregs * UNITS_PER_WORD), - n_intregs, n_intregs * UNITS_PER_WORD); + n_intregs); if (TARGET_SHMEDIA) /* Return the address of the regbuf. */ @@ -5642,16 +5997,16 @@ sh_builtin_saveregs () mem = gen_rtx_MEM (DFmode, fpregs); set_mem_alias_set (mem, alias_set); emit_move_insn (mem, - gen_rtx (REG, DFmode, BASE_ARG_REG (DFmode) + regno)); + gen_rtx_REG (DFmode, BASE_ARG_REG (DFmode) + regno)); } regno = first_floatreg; if (regno & 1) { - emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (- UNITS_PER_WORD))); + emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD))); mem = gen_rtx_MEM (SFmode, fpregs); set_mem_alias_set (mem, alias_set); emit_move_insn (mem, - gen_rtx (REG, SFmode, BASE_ARG_REG (SFmode) + regno + gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno - (TARGET_LITTLE_ENDIAN != 0))); } } @@ -5660,7 +6015,7 @@ sh_builtin_saveregs () { rtx mem; - emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (- UNITS_PER_WORD))); + emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD))); mem = gen_rtx_MEM (SFmode, fpregs); set_mem_alias_set (mem, alias_set); emit_move_insn (mem, @@ -5673,16 +6028,17 @@ sh_builtin_saveregs () /* Define the `__builtin_va_list' type for the ABI. */ -tree -sh_build_va_list () +static tree +sh_build_builtin_va_list (void) { tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack; tree record; - if (TARGET_SH5 || (! TARGET_SH2E && ! TARGET_SH4) || TARGET_HITACHI) + if (TARGET_SH5 || (! TARGET_SH2E && ! TARGET_SH4) + || TARGET_HITACHI || sh_cfun_attr_renesas_p ()) return ptr_type_node; - record = make_node (RECORD_TYPE); + record = (*lang_hooks.types.make_type) (RECORD_TYPE); f_next_o = build_decl (FIELD_DECL, get_identifier ("__va_next_o"), ptr_type_node); @@ -5717,9 +6073,7 @@ sh_build_va_list () /* Implement `va_start' for varargs and stdarg. */ void -sh_va_start (valist, nextarg) - tree valist; - rtx nextarg; +sh_va_start (tree valist, rtx nextarg) { tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack; tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack; @@ -5733,7 +6087,8 @@ sh_va_start (valist, nextarg) return; } - if ((! TARGET_SH2E && ! TARGET_SH4) || TARGET_HITACHI) + if ((! TARGET_SH2E && ! TARGET_SH4) + || TARGET_HITACHI || sh_cfun_attr_renesas_p ()) { std_expand_builtin_va_start (valist, nextarg); return; @@ -5795,14 +6150,14 @@ sh_va_start (valist, nextarg) /* Implement `va_arg'. */ rtx -sh_va_arg (valist, type) - tree valist, type; +sh_va_arg (tree valist, tree type) { HOST_WIDE_INT size, rsize; tree tmp, pptr_type_node; rtx addr_rtx, r; - rtx result; + rtx result_ptr, result = NULL_RTX; int pass_by_ref = MUST_PASS_IN_STACK (TYPE_MODE (type), type); + rtx lab_over; size = int_size_in_bytes (type); rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; @@ -5811,12 +6166,13 @@ sh_va_arg (valist, type) if (pass_by_ref) type = build_pointer_type (type); - if (! TARGET_SH5 && (TARGET_SH2E || TARGET_SH4) && ! TARGET_HITACHI) + if (! TARGET_SH5 && (TARGET_SH2E || TARGET_SH4) + && ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ())) { tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack; tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack; int pass_as_float; - rtx lab_false, lab_over; + rtx lab_false; f_next_o = TYPE_FIELDS (va_list_type_node); f_next_o_limit = TREE_CHAIN (f_next_o); @@ -5834,6 +6190,16 @@ sh_va_arg (valist, type) next_stack = build (COMPONENT_REF, TREE_TYPE (f_next_stack), valist, f_next_stack); + /* Structures with a single member with a distinct mode are passed + like their member. This is relevant if the latter has a REAL_TYPE + or COMPLEX_TYPE type. */ + if (TREE_CODE (type) == RECORD_TYPE + && TYPE_FIELDS (type) + && TREE_CODE (TYPE_FIELDS (type)) == FIELD_DECL + && (TREE_CODE (TREE_TYPE (TYPE_FIELDS (type))) == REAL_TYPE + || TREE_CODE (TREE_TYPE (TYPE_FIELDS (type))) == COMPLEX_TYPE) + && TREE_CHAIN (TYPE_FIELDS (type)) == NULL_TREE) + type = TREE_TYPE (TYPE_FIELDS (type)); if (TARGET_SH4) { pass_as_float = ((TREE_CODE (type) == REAL_TYPE && size <= 8) @@ -5850,6 +6216,9 @@ sh_va_arg (valist, type) lab_false = gen_label_rtx (); lab_over = gen_label_rtx (); + tmp = make_tree (pptr_type_node, addr_rtx); + valist = build1 (INDIRECT_REF, ptr_type_node, tmp); + if (pass_as_float) { int first_floatreg @@ -5879,12 +6248,43 @@ sh_va_arg (valist, type) if (r != addr_rtx) emit_move_insn (addr_rtx, r); - emit_jump_insn (gen_jump (lab_over)); - emit_barrier (); - emit_label (lab_false); +#ifdef FUNCTION_ARG_SCmode_WART + if (TYPE_MODE (type) == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN) + { + rtx addr, real, imag, result_value, slot; + tree subtype = TREE_TYPE (type); - tmp = build1 (ADDR_EXPR, pptr_type_node, next_stack); - r = expand_expr (tmp, addr_rtx, Pmode, EXPAND_NORMAL); + addr = std_expand_builtin_va_arg (valist, subtype); +#ifdef POINTERS_EXTEND_UNSIGNED + if (GET_MODE (addr) != Pmode) + addr = convert_memory_address (Pmode, addr); +#endif + imag = gen_rtx_MEM (TYPE_MODE (type), addr); + set_mem_alias_set (imag, get_varargs_alias_set ()); + + addr = std_expand_builtin_va_arg (valist, subtype); +#ifdef POINTERS_EXTEND_UNSIGNED + if (GET_MODE (addr) != Pmode) + addr = convert_memory_address (Pmode, addr); +#endif + real = gen_rtx_MEM (TYPE_MODE (type), addr); + set_mem_alias_set (real, get_varargs_alias_set ()); + + result_value = gen_rtx_CONCAT (SCmode, real, imag); + /* ??? this interface is stupid - why require a pointer? */ + result = gen_reg_rtx (Pmode); + slot = assign_stack_temp (SCmode, 8, 0); + emit_move_insn (slot, result_value); + emit_move_insn (result, XEXP (slot, 0)); + } +#endif /* FUNCTION_ARG_SCmode_WART */ + + emit_jump_insn (gen_jump (lab_over)); + emit_barrier (); + emit_label (lab_false); + + tmp = build1 (ADDR_EXPR, pptr_type_node, next_stack); + r = expand_expr (tmp, addr_rtx, Pmode, EXPAND_NORMAL); if (r != addr_rtx) emit_move_insn (addr_rtx, r); } @@ -5921,16 +6321,22 @@ sh_va_arg (valist, type) emit_move_insn (addr_rtx, r); } - emit_label (lab_over); - - tmp = make_tree (pptr_type_node, addr_rtx); - valist = build1 (INDIRECT_REF, ptr_type_node, tmp); + if (! result) + emit_label (lab_over); } /* ??? In va-sh.h, there had been code to make values larger than size 8 indirect. This does not match the FUNCTION_ARG macros. */ - result = std_expand_builtin_va_arg (valist, type); + result_ptr = std_expand_builtin_va_arg (valist, type); + if (result) + { + emit_move_insn (result, result_ptr); + emit_label (lab_over); + } + else + result = result_ptr; + if (pass_by_ref) { #ifdef POINTERS_EXTEND_UNSIGNED @@ -5945,13 +6351,332 @@ sh_va_arg (valist, type) return result; } +bool +sh_promote_prototypes (tree type) +{ + if (TARGET_HITACHI) + return 0; + if (! type) + return 1; + return ! sh_attr_renesas_p (type); +} + +/* Define where to put the arguments to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + + On SH the first args are normally in registers + and the rest are pushed. Any arg that starts within the first + NPARM_REGS words is at least partially passed in a register unless + its data type forbids. */ + + +rtx +sh_function_arg (CUMULATIVE_ARGS *ca, enum machine_mode mode, + tree type, int named) +{ + if (! TARGET_SH5 && mode == VOIDmode) + return GEN_INT (ca->renesas_abi ? 1 : 0); + + if (! TARGET_SH5 + && PASS_IN_REG_P (*ca, mode, type) + && (named || ! (TARGET_HITACHI || ca->renesas_abi))) + { + int regno; + + if (mode == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN + && (! FUNCTION_ARG_SCmode_WART || (ROUND_REG (*ca, mode) & 1))) + { + rtx r1 = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (SFmode, + BASE_ARG_REG (mode) + + (ROUND_REG (*ca, mode) ^ 1)), + const0_rtx); + rtx r2 = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (SFmode, + BASE_ARG_REG (mode) + + ((ROUND_REG (*ca, mode) + 1) ^ 1)), + GEN_INT (4)); + return gen_rtx_PARALLEL(SCmode, gen_rtvec(2, r1, r2)); + } + + /* If the alignment of a DF value causes an SF register to be + skipped, we will use that skipped register for the next SF + value. */ + if ((TARGET_HITACHI || ca->renesas_abi) + && ca->free_single_fp_reg + && mode == SFmode) + return gen_rtx_REG (mode, ca->free_single_fp_reg); + + regno = (BASE_ARG_REG (mode) + ROUND_REG (*ca, mode)) + ^ (mode == SFmode && TARGET_SH4 + && TARGET_LITTLE_ENDIAN != 0 + && ! TARGET_HITACHI && ! ca->renesas_abi); + return gen_rtx_REG (mode, regno); + + } + + if (TARGET_SH5) + { + if (mode == VOIDmode && TARGET_SHCOMPACT) + return GEN_INT (ca->call_cookie); + + /* The following test assumes unnamed arguments are promoted to + DFmode. */ + if (mode == SFmode && ca->free_single_fp_reg) + return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode, ca->free_single_fp_reg); + + if ((GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT) + && (named || ! ca->prototype_p) + && ca->arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (SFmode)) + { + if (! ca->prototype_p && TARGET_SHMEDIA) + return SH5_PROTOTYPELESS_FLOAT_ARG (*ca, mode); + + return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode, + FIRST_FP_PARM_REG + + ca->arg_count[(int) SH_ARG_FLOAT]); + } + + if (ca->arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode) + && (! TARGET_SHCOMPACT + || (! SHCOMPACT_FORCE_ON_STACK (mode, type) + && ! SH5_WOULD_BE_PARTIAL_NREGS (*ca, mode, + type, named)))) + { + return gen_rtx_REG (mode, (FIRST_PARM_REG + + ca->arg_count[(int) SH_ARG_INT])); + } + + return 0; + } + + return 0; +} + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be + available.) */ + +void +sh_function_arg_advance (CUMULATIVE_ARGS *ca, enum machine_mode mode, + tree type, int named) +{ + if (ca->force_mem) + ca->force_mem = 0; + else if (TARGET_SH5) + { + tree type2 = (ca->byref && type + ? TREE_TYPE (type) + : type); + enum machine_mode mode2 = (ca->byref && type + ? TYPE_MODE (type2) + : mode); + int dwords = ((ca->byref + ? ca->byref + : mode2 == BLKmode + ? int_size_in_bytes (type2) + : GET_MODE_SIZE (mode2)) + 7) / 8; + int numregs = MIN (dwords, NPARM_REGS (SImode) + - ca->arg_count[(int) SH_ARG_INT]); + + if (numregs) + { + ca->arg_count[(int) SH_ARG_INT] += numregs; + if (TARGET_SHCOMPACT + && SHCOMPACT_FORCE_ON_STACK (mode2, type2)) + { + ca->call_cookie + |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT] + - numregs, 1); + /* N.B. We want this also for outgoing. */ + ca->stack_regs += numregs; + } + else if (ca->byref) + { + if (! ca->outgoing) + ca->stack_regs += numregs; + ca->byref_regs += numregs; + ca->byref = 0; + do + ca->call_cookie + |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT] + - numregs, 2); + while (--numregs); + ca->call_cookie + |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT] + - 1, 1); + } + else if (dwords > numregs) + { + int pushregs = numregs; + + if (TARGET_SHCOMPACT) + ca->stack_regs += numregs; + while (pushregs < NPARM_REGS (SImode) - 1 + && (CALL_COOKIE_INT_REG_GET + (ca->call_cookie, + NPARM_REGS (SImode) - pushregs) + == 1)) + { + ca->call_cookie + &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode) + - pushregs, 1); + pushregs++; + } + if (numregs == NPARM_REGS (SImode)) + ca->call_cookie + |= CALL_COOKIE_INT_REG (0, 1) + | CALL_COOKIE_STACKSEQ (numregs - 1); + else + ca->call_cookie + |= CALL_COOKIE_STACKSEQ (numregs); + } + } + if (GET_SH_ARG_CLASS (mode2) == SH_ARG_FLOAT + && (named || ! ca->prototype_p)) + { + if (mode2 == SFmode && ca->free_single_fp_reg) + ca->free_single_fp_reg = 0; + else if (ca->arg_count[(int) SH_ARG_FLOAT] + < NPARM_REGS (SFmode)) + { + int numfpregs + = MIN ((GET_MODE_SIZE (mode2) + 7) / 8 * 2, + NPARM_REGS (SFmode) + - ca->arg_count[(int) SH_ARG_FLOAT]); + + ca->arg_count[(int) SH_ARG_FLOAT] += numfpregs; + + if (TARGET_SHCOMPACT && ! ca->prototype_p) + { + if (ca->outgoing && numregs > 0) + do + { + ca->call_cookie + |= (CALL_COOKIE_INT_REG + (ca->arg_count[(int) SH_ARG_INT] + - numregs + ((numfpregs - 2) / 2), + 4 + (ca->arg_count[(int) SH_ARG_FLOAT] + - numfpregs) / 2)); + } + while (numfpregs -= 2); + } + else if (mode2 == SFmode && (named) + && (ca->arg_count[(int) SH_ARG_FLOAT] + < NPARM_REGS (SFmode))) + ca->free_single_fp_reg + = FIRST_FP_PARM_REG - numfpregs + + ca->arg_count[(int) SH_ARG_FLOAT] + 1; + } + } + return; + } + + if ((TARGET_HITACHI || ca->renesas_abi) && TARGET_FPU_DOUBLE) + { + /* Note that we've used the skipped register. */ + if (mode == SFmode && ca->free_single_fp_reg) + { + ca->free_single_fp_reg = 0; + return; + } + /* When we have a DF after an SF, there's an SF register that get + skipped in order to align the DF value. We note this skipped + register, because the next SF value will use it, and not the + SF that follows the DF. */ + if (mode == DFmode + && ROUND_REG (*ca, DFmode) != ROUND_REG (*ca, SFmode)) + { + ca->free_single_fp_reg = (ROUND_REG (*ca, SFmode) + + BASE_ARG_REG (mode)); + } + } + + if (! (TARGET_SH4 || ca->renesas_abi) + || PASS_IN_REG_P (*ca, mode, type)) + (ca->arg_count[(int) GET_SH_ARG_CLASS (mode)] + = (ROUND_REG (*ca, mode) + + (mode == BLKmode + ? ROUND_ADVANCE (int_size_in_bytes (type)) + : ROUND_ADVANCE (GET_MODE_SIZE (mode))))); +} + +/* The Renesas calling convention doesn't quite fit into this scheme since + the address is passed like an invisible argument, but one that is always + passed in memory. */ +static rtx +sh_struct_value_rtx (tree fndecl, int incoming ATTRIBUTE_UNUSED) +{ + if (TARGET_HITACHI || sh_attr_renesas_p (fndecl)) + return 0; + return gen_rtx_REG (Pmode, 2); +} + +/* Worker function for TARGET_RETURN_IN_MEMORY. */ + +static bool +sh_return_in_memory (tree type, tree fndecl) +{ + if (TARGET_SH5) + { + if (TYPE_MODE (type) == BLKmode) + return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)) > 8; + else + return GET_MODE_SIZE (TYPE_MODE (type)) > 8; + } + else + { + return (TYPE_MODE (type) == BLKmode + || ((TARGET_HITACHI || sh_attr_renesas_p (fndecl)) + && TREE_CODE (type) == RECORD_TYPE)); + } +} + +/* We actually emit the code in sh_expand_prologue. We used to use + a static variable to flag that we need to emit this code, but that + doesn't when inlining, when functions are deferred and then emitted + later. Fortunately, we already have two flags that are part of struct + function that tell if a function uses varargs or stdarg. */ +static void +sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + int *pretend_arg_size ATTRIBUTE_UNUSED, + int second_time ATTRIBUTE_UNUSED) +{ + if (! current_function_stdarg) + abort (); +} + +static bool +sh_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED) +{ + return TARGET_SH5; +} + +static bool +sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *ca) +{ + return ! (TARGET_HITACHI || ca->renesas_abi) && ! TARGET_SH5; +} + + /* Define the offset between two registers, one to be eliminated, and the other its replacement, at the start of a routine. */ int -initial_elimination_offset (from, to) - int from; - int to; +initial_elimination_offset (int from, int to) { int regs_saved; int regs_saved_rounding = 0; @@ -5959,10 +6684,18 @@ initial_elimination_offset (from, to) int total_auto_space; int save_flags = target_flags; int copy_flags; - HARD_REG_SET live_regs_mask; + + shmedia_space_reserved_for_target_registers = false; regs_saved = calc_live_regs (&live_regs_mask); regs_saved += SHMEDIA_REGS_STACK_ADJUST (); + + if (shmedia_reserve_space_for_target_registers_p (regs_saved, &live_regs_mask)) + { + shmedia_space_reserved_for_target_registers = true; + regs_saved += shmedia_target_regs_stack_adjust (&live_regs_mask); + } + if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT)) regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT) - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT)); @@ -5990,9 +6723,10 @@ initial_elimination_offset (from, to) { if (TARGET_SH5) { - int i, n = total_saved_regs_space; - int align; + int n = total_saved_regs_space; int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG; + save_schedule schedule; + save_entry *entry; n += total_auto_space; @@ -6002,40 +6736,13 @@ initial_elimination_offset (from, to) target_flags = copy_flags; - /* We loop twice: first, check 8-byte aligned registers, - that are stored in the higher addresses, that are known - to be aligned. Then, check 32-bit registers that don't - need 8-byte alignment. */ - for (align = 1; align >= 0; align--) - for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--) - if (TEST_HARD_REG_BIT (live_regs_mask, i)) - { - enum machine_mode mode = REGISTER_NATURAL_MODE (i); - - if (mode == SFmode && (i % 2) == 1 - && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i) - && TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1))) - { - mode = DFmode; - i--; - } - - /* If we're doing the aligned pass and this is not aligned, - or we're doing the unaligned pass and this is aligned, - skip it. */ - if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) - == 0) != align) - continue; - - n -= GET_MODE_SIZE (mode); - - if (i == pr_reg) - { - target_flags = save_flags; - return n; - } - } - + sh5_schedule_saves (&live_regs_mask, &schedule, n); + for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++) + if (entry->reg == pr_reg) + { + target_flags = save_flags; + return entry->offset; + } abort (); } else @@ -6045,26 +6752,23 @@ initial_elimination_offset (from, to) abort (); } -/* Handle machine specific pragmas to be semi-compatible with Hitachi +/* Handle machine specific pragmas to be semi-compatible with Renesas compiler. */ void -sh_pr_interrupt (pfile) - struct cpp_reader *pfile ATTRIBUTE_UNUSED; +sh_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED) { pragma_interrupt = 1; } void -sh_pr_trapa (pfile) - struct cpp_reader *pfile ATTRIBUTE_UNUSED; +sh_pr_trapa (struct cpp_reader *pfile ATTRIBUTE_UNUSED) { pragma_interrupt = pragma_trapa = 1; } void -sh_pr_nosave_low_regs (pfile) - struct cpp_reader *pfile ATTRIBUTE_UNUSED; +sh_pr_nosave_low_regs (struct cpp_reader *pfile ATTRIBUTE_UNUSED) { pragma_nosave_low_regs = 1; } @@ -6072,9 +6776,7 @@ sh_pr_nosave_low_regs (pfile) /* Generate 'handle_interrupt' attribute for decls */ static void -sh_insert_attributes (node, attributes) - tree node; - tree * attributes; +sh_insert_attributes (tree node, tree *attributes) { if (! pragma_interrupt || TREE_CODE (node) != FUNCTION_DECL) @@ -6098,7 +6800,12 @@ sh_insert_attributes (node, attributes) to run on. trap_exit -- use a trapa to exit an interrupt function instead of - an rte instruction. */ + an rte instruction. + + renesas -- use Renesas calling/layout conventions (functions and + structures). + +*/ const struct attribute_spec sh_attribute_table[] = { @@ -6106,18 +6813,17 @@ const struct attribute_spec sh_attribute_table[] = { "interrupt_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute }, { "sp_switch", 1, 1, true, false, false, sh_handle_sp_switch_attribute }, { "trap_exit", 1, 1, true, false, false, sh_handle_trap_exit_attribute }, + { "renesas", 0, 0, false, true, false, sh_handle_renesas_attribute }, { NULL, 0, 0, false, false, false, NULL } }; /* Handle an "interrupt_handler" attribute; arguments as in struct attribute_spec.handler. */ static tree -sh_handle_interrupt_handler_attribute (node, name, args, flags, no_add_attrs) - tree *node; - tree name; - tree args ATTRIBUTE_UNUSED; - int flags ATTRIBUTE_UNUSED; - bool *no_add_attrs; +sh_handle_interrupt_handler_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { @@ -6137,12 +6843,8 @@ sh_handle_interrupt_handler_attribute (node, name, args, flags, no_add_attrs) /* Handle an "sp_switch" attribute; arguments as in struct attribute_spec.handler. */ static tree -sh_handle_sp_switch_attribute (node, name, args, flags, no_add_attrs) - tree *node; - tree name; - tree args; - int flags ATTRIBUTE_UNUSED; - bool *no_add_attrs; +sh_handle_sp_switch_attribute (tree *node, tree name, tree args, + int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { @@ -6176,12 +6878,8 @@ sh_handle_sp_switch_attribute (node, name, args, flags, no_add_attrs) /* Handle an "trap_exit" attribute; arguments as in struct attribute_spec.handler. */ static tree -sh_handle_trap_exit_attribute (node, name, args, flags, no_add_attrs) - tree *node; - tree name; - tree args; - int flags ATTRIBUTE_UNUSED; - bool *no_add_attrs; +sh_handle_trap_exit_attribute (tree *node, tree name, tree args, + int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { @@ -6211,13 +6909,129 @@ sh_handle_trap_exit_attribute (node, name, args, flags, no_add_attrs) return NULL_TREE; } +static tree +sh_handle_renesas_attribute (tree *node ATTRIBUTE_UNUSED, + tree name ATTRIBUTE_UNUSED, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs ATTRIBUTE_UNUSED) +{ + return NULL_TREE; +} + +/* True if __attribute__((renesas)) or -mrenesas. */ int -sh_cfun_interrupt_handler_p () +sh_attr_renesas_p (tree td) +{ + if (TARGET_HITACHI) + return 1; + if (td == 0) + return 0; + if (DECL_P (td)) + td = TREE_TYPE (td); + return (lookup_attribute ("renesas", TYPE_ATTRIBUTES (td)) + != NULL_TREE); +} + +/* True if __attribute__((renesas)) or -mrenesas, for the current + function. */ +int +sh_cfun_attr_renesas_p (void) +{ + return sh_attr_renesas_p (current_function_decl); +} + +int +sh_cfun_interrupt_handler_p (void) { return (lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE); } + +/* ??? target_switches in toplev.c is static, hence we have to duplicate it. */ +static const struct +{ + const char *const name; + const int value; + const char *const description; +} +sh_target_switches[] = TARGET_SWITCHES; +#define target_switches sh_target_switches + +/* Like default_pch_valid_p, but take flag_mask into account. */ +const char * +sh_pch_valid_p (const void *data_p, size_t len) +{ + const char *data = (const char *)data_p; + const char *flag_that_differs = NULL; + size_t i; + int old_flags; + int flag_mask + = (SH1_BIT | SH2_BIT | SH3_BIT | SH_E_BIT | HARD_SH4_BIT | FPU_SINGLE_BIT + | SH4_BIT | HITACHI_BIT | LITTLE_ENDIAN_BIT); + + /* -fpic and -fpie also usually make a PCH invalid. */ + if (data[0] != flag_pic) + return _("created and used with different settings of -fpic"); + if (data[1] != flag_pie) + return _("created and used with different settings of -fpie"); + data += 2; + + /* Check target_flags. */ + memcpy (&old_flags, data, sizeof (target_flags)); + if (((old_flags ^ target_flags) & flag_mask) != 0) + { + for (i = 0; i < ARRAY_SIZE (target_switches); i++) + { + int bits; + + bits = target_switches[i].value; + if (bits < 0) + bits = -bits; + bits &= flag_mask; + if ((target_flags & bits) != (old_flags & bits)) + { + flag_that_differs = target_switches[i].name; + goto make_message; + } + } + abort (); + } + data += sizeof (target_flags); + len -= sizeof (target_flags); + + /* Check string options. */ +#ifdef TARGET_OPTIONS + for (i = 0; i < ARRAY_SIZE (target_options); i++) + { + const char *str = *target_options[i].variable; + size_t l; + if (! str) + str = ""; + l = strlen (str) + 1; + if (len < l || memcmp (data, str, l) != 0) + { + flag_that_differs = target_options[i].prefix; + goto make_message; + } + data += l; + len -= l; + } +#endif + + return NULL; + + make_message: + { + char *r; + asprintf (&r, _("created and used with differing settings of `-m%s'"), + flag_that_differs); + if (r == NULL) + return _("out of memory"); + return r; + } +} /* Predicates used by the templates. */ @@ -6225,9 +7039,7 @@ sh_cfun_interrupt_handler_p () Used only in general_movsrc_operand. */ int -system_reg_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +system_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { switch (REGNO (op)) { @@ -6244,9 +7056,7 @@ system_reg_operand (op, mode) invalid as are subregs of system registers. */ int -general_movsrc_operand (op, mode) - rtx op; - enum machine_mode mode; +general_movsrc_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) == MEM) { @@ -6280,9 +7090,7 @@ general_movsrc_operand (op, mode) Same as general_operand, but no preinc allowed. */ int -general_movdst_operand (op, mode) - rtx op; - enum machine_mode mode; +general_movdst_operand (rtx op, enum machine_mode mode) { /* Only pre dec allowed. */ if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC) @@ -6294,9 +7102,7 @@ general_movdst_operand (op, mode) /* Returns 1 if OP is a normal arithmetic register. */ int -arith_reg_operand (op, mode) - rtx op; - enum machine_mode mode; +arith_reg_operand (rtx op, enum machine_mode mode) { if (register_operand (op, mode)) { @@ -6321,9 +7127,7 @@ arith_reg_operand (op, mode) because this would lead to missing sign extensions when truncating from DImode to SImode. */ int -arith_reg_dest (op, mode) - rtx op; - enum machine_mode mode; +arith_reg_dest (rtx op, enum machine_mode mode) { if (mode == DImode && GET_CODE (op) == SUBREG && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) < 8) @@ -6332,9 +7136,7 @@ arith_reg_dest (op, mode) } int -int_gpr_dest (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +int_gpr_dest (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { enum machine_mode op_mode = GET_MODE (op); @@ -6347,9 +7149,7 @@ int_gpr_dest (op, mode) } int -fp_arith_reg_operand (op, mode) - rtx op; - enum machine_mode mode; +fp_arith_reg_operand (rtx op, enum machine_mode mode) { if (register_operand (op, mode)) { @@ -6371,9 +7171,7 @@ fp_arith_reg_operand (op, mode) /* Returns 1 if OP is a valid source operand for an arithmetic insn. */ int -arith_operand (op, mode) - rtx op; - enum machine_mode mode; +arith_operand (rtx op, enum machine_mode mode) { if (arith_reg_operand (op, mode)) return 1; @@ -6381,17 +7179,17 @@ arith_operand (op, mode) if (TARGET_SHMEDIA) { /* FIXME: We should be checking whether the CONST_INT fits in a - CONST_OK_FOR_J here, but this causes reload_cse to crash when + CONST_OK_FOR_I16 here, but this causes reload_cse to crash when attempting to transform a sequence of two 64-bit sets of the same register from literal constants into a set and an add, when the difference is too wide for an add. */ if (GET_CODE (op) == CONST_INT - || EXTRA_CONSTRAINT_S (op)) + || EXTRA_CONSTRAINT_C16 (op)) return 1; else return 0; } - else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I (INTVAL (op))) + else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I08 (INTVAL (op))) return 1; return 0; @@ -6400,14 +7198,12 @@ arith_operand (op, mode) /* Returns 1 if OP is a valid source operand for a compare insn. */ int -arith_reg_or_0_operand (op, mode) - rtx op; - enum machine_mode mode; +arith_reg_or_0_operand (rtx op, enum machine_mode mode) { if (arith_reg_operand (op, mode)) return 1; - if (EXTRA_CONSTRAINT_U (op)) + if (EXTRA_CONSTRAINT_Z (op)) return 1; return 0; @@ -6417,41 +7213,35 @@ arith_reg_or_0_operand (op, mode) that takes either a register or a 6-bit immediate. */ int -shmedia_6bit_operand (op, mode) - rtx op; - enum machine_mode mode; +shmedia_6bit_operand (rtx op, enum machine_mode mode) { return (arith_reg_operand (op, mode) - || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_O (INTVAL (op)))); + || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I06 (INTVAL (op)))); } /* Returns 1 if OP is a valid source operand for a logical operation. */ int -logical_operand (op, mode) - rtx op; - enum machine_mode mode; +logical_operand (rtx op, enum machine_mode mode) { if (arith_reg_operand (op, mode)) return 1; if (TARGET_SHMEDIA) { - if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_P (INTVAL (op))) + if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I10 (INTVAL (op))) return 1; else return 0; } - else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_L (INTVAL (op))) + else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_K08 (INTVAL (op))) return 1; return 0; } int -and_operand (op, mode) - rtx op; - enum machine_mode mode; +and_operand (rtx op, enum machine_mode mode) { if (logical_operand (op, mode)) return 1; @@ -6460,9 +7250,8 @@ and_operand (op, mode) if (TARGET_SHMEDIA && mode == DImode && GET_CODE (op) == CONST_INT - && (INTVAL (op) == (unsigned) 0xffffffff - || INTVAL (op) == (HOST_WIDE_INT) -1 << 32)) - return 1; + && CONST_OK_FOR_J16 (INTVAL (op))) + return 1; return 0; } @@ -6470,8 +7259,7 @@ and_operand (op, mode) /* Nonzero if OP is a floating point value with value 0.0. */ int -fp_zero_operand (op) - rtx op; +fp_zero_operand (rtx op) { REAL_VALUE_TYPE r; @@ -6485,8 +7273,7 @@ fp_zero_operand (op) /* Nonzero if OP is a floating point value with value 1.0. */ int -fp_one_operand (op) - rtx op; +fp_one_operand (rtx op) { REAL_VALUE_TYPE r; @@ -6505,33 +7292,27 @@ fp_one_operand (op) choosing an fldi alternative during reload and thus failing to allocate a scratch register for the constant loading. */ int -fldi_ok () +fldi_ok (void) { return ! TARGET_SH4 || TARGET_FMOVD || reload_completed; } int -tertiary_reload_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +tertiary_reload_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { enum rtx_code code = GET_CODE (op); return code == MEM || (TARGET_SH4 && code == CONST_DOUBLE); } int -fpscr_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +fpscr_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == REG && REGNO (op) == FPSCR_REG && GET_MODE (op) == PSImode); } int -fpul_operand (op, mode) - rtx op; - enum machine_mode mode; +fpul_operand (rtx op, enum machine_mode mode) { if (TARGET_SHMEDIA) return fp_arith_reg_operand (op, mode); @@ -6542,47 +7323,22 @@ fpul_operand (op, mode) } int -symbol_ref_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +symbol_ref_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return (GET_CODE (op) == SYMBOL_REF); } /* Return the TLS type for TLS symbols, 0 for otherwise. */ int -tls_symbolic_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { - const char *str; - if (GET_CODE (op) != SYMBOL_REF) return 0; - - str = XSTR (op, 0); - STRIP_DATALABEL_ENCODING(str, str); - if (! TLS_SYMNAME_P (str)) - return 0; - - switch (str[1]) - { - case 'G': - return TLS_MODEL_GLOBAL_DYNAMIC; - case 'L': - return TLS_MODEL_LOCAL_DYNAMIC; - case 'i': - return TLS_MODEL_INITIAL_EXEC; - case 'l': - return TLS_MODEL_LOCAL_EXEC; - } - return 0; + return SYMBOL_REF_TLS_MODEL (op); } int -commutative_float_operator (op, mode) - rtx op; - enum machine_mode mode; +commutative_float_operator (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode) return 0; @@ -6598,9 +7354,7 @@ commutative_float_operator (op, mode) } int -noncommutative_float_operator (op, mode) - rtx op; - enum machine_mode mode; +noncommutative_float_operator (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode) return 0; @@ -6616,9 +7370,7 @@ noncommutative_float_operator (op, mode) } int -unary_float_operator (op, mode) - rtx op; - enum machine_mode mode; +unary_float_operator (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode) return 0; @@ -6635,9 +7387,7 @@ unary_float_operator (op, mode) } int -binary_float_operator (op, mode) - rtx op; - enum machine_mode mode; +binary_float_operator (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode) return 0; @@ -6655,9 +7405,7 @@ binary_float_operator (op, mode) } int -binary_logical_operator (op, mode) - rtx op; - enum machine_mode mode; +binary_logical_operator (rtx op, enum machine_mode mode) { if (GET_MODE (op) != mode) return 0; @@ -6674,17 +7422,14 @@ binary_logical_operator (op, mode) } int -equality_comparison_operator (op, mode) - rtx op; - enum machine_mode mode; +equality_comparison_operator (rtx op, enum machine_mode mode) { return ((mode == VOIDmode || GET_MODE (op) == mode) && (GET_CODE (op) == EQ || GET_CODE (op) == NE)); } -int greater_comparison_operator (op, mode) - rtx op; - enum machine_mode mode; +int +greater_comparison_operator (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) == mode) return 0; @@ -6700,9 +7445,8 @@ int greater_comparison_operator (op, mode) } } -int less_comparison_operator (op, mode) - rtx op; - enum machine_mode mode; +int +less_comparison_operator (rtx op, enum machine_mode mode) { if (mode != VOIDmode && GET_MODE (op) == mode) return 0; @@ -6720,9 +7464,7 @@ int less_comparison_operator (op, mode) /* Accept pseudos and branch target registers. */ int -target_reg_operand (op, mode) - rtx op; - enum machine_mode mode; +target_reg_operand (rtx op, enum machine_mode mode) { if (mode != DImode || GET_MODE (op) != DImode) @@ -6747,37 +7489,31 @@ target_reg_operand (op, mode) /* Same as target_reg_operand, except that label_refs and symbol_refs are accepted before reload. */ int -target_operand (op, mode) - rtx op; - enum machine_mode mode; +target_operand (rtx op, enum machine_mode mode) { if (mode != DImode) return 0; if ((GET_MODE (op) == DImode || GET_MODE (op) == VOIDmode) - && EXTRA_CONSTRAINT_T (op)) + && EXTRA_CONSTRAINT_Csy (op)) return ! reload_completed; return target_reg_operand (op, mode); } int -mextr_bit_offset (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +mextr_bit_offset (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { HOST_WIDE_INT i; if (GET_CODE (op) != CONST_INT) return 0; i = INTVAL (op); - return i >= 1*8 && i <= 7*8 && (i & 7) == 0; + return i >= 1 * 8 && i <= 7 * 8 && (i & 7) == 0; } int -extend_reg_operand (op, mode) - rtx op; - enum machine_mode mode; +extend_reg_operand (rtx op, enum machine_mode mode) { return (GET_CODE (op) == TRUNCATE ? arith_operand @@ -6785,9 +7521,7 @@ extend_reg_operand (op, mode) } int -trunc_hi_operand (op, mode) - rtx op; - enum machine_mode mode; +trunc_hi_operand (rtx op, enum machine_mode mode) { enum machine_mode op_mode = GET_MODE (op); @@ -6798,9 +7532,7 @@ trunc_hi_operand (op, mode) } int -extend_reg_or_0_operand (op, mode) - rtx op; - enum machine_mode mode; +extend_reg_or_0_operand (rtx op, enum machine_mode mode) { return (GET_CODE (op) == TRUNCATE ? arith_operand @@ -6808,9 +7540,7 @@ extend_reg_or_0_operand (op, mode) } int -general_extend_operand (op, mode) - rtx op; - enum machine_mode mode; +general_extend_operand (rtx op, enum machine_mode mode) { return (GET_CODE (op) == TRUNCATE ? arith_operand @@ -6818,9 +7548,7 @@ general_extend_operand (op, mode) } int -inqhi_operand (op, mode) - rtx op; - enum machine_mode mode; +inqhi_operand (rtx op, enum machine_mode mode) { if (GET_CODE (op) != TRUNCATE || mode != GET_MODE (op)) return 0; @@ -6831,9 +7559,7 @@ inqhi_operand (op, mode) } int -sh_rep_vec (v, mode) - rtx v; - enum machine_mode mode; +sh_rep_vec (rtx v, enum machine_mode mode) { int i; rtx x, y; @@ -6846,7 +7572,7 @@ sh_rep_vec (v, mode) if (GET_MODE_UNIT_SIZE (mode) == 1) { y = XVECEXP (v, 0, i); - for (i -= 2 ; i >= 0; i -= 2) + for (i -= 2; i >= 0; i -= 2) if (! rtx_equal_p (XVECEXP (v, 0, i + 1), x) || ! rtx_equal_p (XVECEXP (v, 0, i), y)) return 0; @@ -6861,9 +7587,7 @@ sh_rep_vec (v, mode) /* Determine if V is a constant vector matching MODE with only one element that is not a sign extension. Two byte-sized elements count as one. */ int -sh_1el_vec (v, mode) - rtx v; - enum machine_mode mode; +sh_1el_vec (rtx v, enum machine_mode mode) { int unit_size; int i, last, least, sign_ix; @@ -6894,9 +7618,7 @@ sh_1el_vec (v, mode) } int -sh_const_vec (v, mode) - rtx v; - enum machine_mode mode; +sh_const_vec (rtx v, enum machine_mode mode) { int i; @@ -6913,8 +7635,7 @@ sh_const_vec (v, mode) /* Return the destination address of a branch. */ static int -branch_dest (branch) - rtx branch; +branch_dest (rtx branch) { rtx dest = SET_SRC (PATTERN (branch)); int dest_uid; @@ -6930,9 +7651,7 @@ branch_dest (branch) We assume REG is a reload reg, and therefore does not live past labels. It may live past calls or jumps though. */ int -reg_unused_after (reg, insn) - rtx reg; - rtx insn; +reg_unused_after (rtx reg, rtx insn) { enum rtx_code code; rtx set; @@ -6947,6 +7666,10 @@ reg_unused_after (reg, insn) while ((insn = NEXT_INSN (insn))) { + rtx set; + if (!INSN_P (insn)) + continue; + code = GET_CODE (insn); #if 0 @@ -7003,17 +7726,14 @@ reg_unused_after (reg, insn) else if (code == JUMP_INSN) return 0; } - else if (GET_RTX_CLASS (code) == 'i') - { - rtx set = single_set (insn); - if (set && reg_overlap_mentioned_p (reg, SET_SRC (set))) - return 0; - if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) - return GET_CODE (SET_DEST (set)) != MEM; - if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn))) - return 0; - } + set = single_set (insn); + if (set && reg_overlap_mentioned_p (reg, SET_SRC (set))) + return 0; + if (set && reg_overlap_mentioned_p (reg, SET_DEST (set))) + return GET_CODE (SET_DEST (set)) != MEM; + if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn))) + return 0; if (code == CALL_INSN && call_used_regs[REGNO (reg)]) return 1; @@ -7025,11 +7745,11 @@ reg_unused_after (reg, insn) static GTY(()) rtx fpscr_rtx; rtx -get_fpscr_rtx () +get_fpscr_rtx (void) { if (! fpscr_rtx) { - fpscr_rtx = gen_rtx (REG, PSImode, FPSCR_REG); + fpscr_rtx = gen_rtx_REG (PSImode, FPSCR_REG); REG_USERVAR_P (fpscr_rtx) = 1; mark_user_reg (fpscr_rtx); } @@ -7039,51 +7759,41 @@ get_fpscr_rtx () } void -emit_sf_insn (pat) - rtx pat; +emit_sf_insn (rtx pat) { emit_insn (pat); } void -emit_df_insn (pat) - rtx pat; +emit_df_insn (rtx pat) { emit_insn (pat); } void -expand_sf_unop (fun, operands) - rtx (*fun) PARAMS ((rtx, rtx, rtx)); - rtx *operands; +expand_sf_unop (rtx (*fun) (rtx, rtx, rtx), rtx *operands) { emit_sf_insn ((*fun) (operands[0], operands[1], get_fpscr_rtx ())); } void -expand_sf_binop (fun, operands) - rtx (*fun) PARAMS ((rtx, rtx, rtx, rtx)); - rtx *operands; +expand_sf_binop (rtx (*fun) (rtx, rtx, rtx, rtx), rtx *operands) { emit_sf_insn ((*fun) (operands[0], operands[1], operands[2], get_fpscr_rtx ())); } void -expand_df_unop (fun, operands) - rtx (*fun) PARAMS ((rtx, rtx, rtx)); - rtx *operands; +expand_df_unop (rtx (*fun) (rtx, rtx, rtx), rtx *operands) { emit_df_insn ((*fun) (operands[0], operands[1], get_fpscr_rtx ())); } void -expand_df_binop (fun, operands) - rtx (*fun) PARAMS ((rtx, rtx, rtx, rtx)); - rtx *operands; +expand_df_binop (rtx (*fun) (rtx, rtx, rtx, rtx), rtx *operands) { emit_df_insn ((*fun) (operands[0], operands[1], operands[2], - get_fpscr_rtx ())); + get_fpscr_rtx ())); } /* ??? gcc does flow analysis strictly after common subexpression @@ -7114,8 +7824,7 @@ f(double a) same basic block. */ static void -mark_use (x, reg_set_block) - rtx x, *reg_set_block; +mark_use (rtx x, rtx *reg_set_block) { enum rtx_code code; @@ -7167,7 +7876,7 @@ mark_use (x, reg_set_block) } } -static rtx get_free_reg PARAMS ((HARD_REG_SET)); +static rtx get_free_reg (HARD_REG_SET); /* This function returns a register to use to load the address to load the fpscr from. Currently it always returns r1 or r7, but when we are @@ -7184,8 +7893,7 @@ __complex__ long long f (double d) { if (d == 0) return 2; else return 3; } the middle. */ static rtx -get_free_reg (regs_live) - HARD_REG_SET regs_live; +get_free_reg (HARD_REG_SET regs_live) { if (! TEST_HARD_REG_BIT (regs_live, 1)) return gen_rtx_REG (Pmode, 1); @@ -7201,9 +7909,7 @@ get_free_reg (regs_live) /* This function will set the fpscr from memory. MODE is the mode we are setting it to. */ void -fpscr_set_from_mem (mode, regs_live) - int mode; - HARD_REG_SET regs_live; +fpscr_set_from_mem (int mode, HARD_REG_SET regs_live) { enum attr_fp_mode fp_mode = mode; rtx addr_reg = get_free_reg (regs_live); @@ -7220,8 +7926,7 @@ fpscr_set_from_mem (mode, regs_live) #endif int -sh_insn_length_adjustment (insn) - rtx insn; +sh_insn_length_adjustment (rtx insn) { /* Instructions with unfilled delay slots take up an extra two bytes for the nop in the delay slot. */ @@ -7304,8 +8009,7 @@ sh_insn_length_adjustment (insn) /* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol isn't protected by a PIC unspec. */ int -nonpic_symbol_mentioned_p (x) - rtx x; +nonpic_symbol_mentioned_p (rtx x) { register const char *fmt; register int i; @@ -7327,7 +8031,7 @@ nonpic_symbol_mentioned_p (x) || XINT (x, 1) == UNSPEC_GOTTPOFF || XINT (x, 1) == UNSPEC_DTPOFF || XINT (x, 1) == UNSPEC_PLT)) - return 0; + return 0; fmt = GET_RTX_FORMAT (GET_CODE (x)); for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) @@ -7350,19 +8054,14 @@ nonpic_symbol_mentioned_p (x) /* Convert a non-PIC address in `orig' to a PIC address using @GOT or @GOTOFF in `reg'. */ rtx -legitimize_pic_address (orig, mode, reg) - rtx orig; - enum machine_mode mode ATTRIBUTE_UNUSED; - rtx reg; +legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED, + rtx reg) { if (tls_symbolic_operand (orig, Pmode)) return orig; if (GET_CODE (orig) == LABEL_REF - || (GET_CODE (orig) == SYMBOL_REF - && (CONSTANT_POOL_ADDRESS_P (orig) - /* SYMBOL_REF_FLAG is set on static symbols. */ - || SYMBOL_REF_FLAG (orig)))) + || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig))) { if (reg == 0) reg = gen_reg_rtx (Pmode); @@ -7384,8 +8083,7 @@ legitimize_pic_address (orig, mode, reg) /* Mark the use of a constant in the literal table. If the constant has multiple labels, make it unique. */ static rtx -mark_constant_pool_use (x) - rtx x; +mark_constant_pool_use (rtx x) { rtx insn, lab, pattern; @@ -7451,9 +8149,7 @@ mark_constant_pool_use (x) of an unconditional jump BRANCH2. We only want to do this if the resulting branch will have a short displacement. */ int -sh_can_redirect_branch (branch1, branch2) - rtx branch1; - rtx branch2; +sh_can_redirect_branch (rtx branch1, rtx branch2) { if (flag_expensive_optimizations && simplejump_p (branch2)) { @@ -7485,19 +8181,17 @@ sh_can_redirect_branch (branch1, branch2) /* Return nonzero if register old_reg can be renamed to register new_reg. */ int -sh_hard_regno_rename_ok (old_reg, new_reg) - unsigned int old_reg ATTRIBUTE_UNUSED; - unsigned int new_reg; +sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, + unsigned int new_reg) { - -/* Interrupt functions can only use registers that have already been - saved by the prologue, even if they would normally be - call-clobbered. */ + /* Interrupt functions can only use registers that have already been + saved by the prologue, even if they would normally be + call-clobbered. */ if (sh_cfun_interrupt_handler_p () && !regs_ever_live[new_reg]) - return 0; + return 0; - return 1; + return 1; } /* Function to update the integer COST @@ -7508,18 +8202,14 @@ sh_hard_regno_rename_ok (old_reg, new_reg) the same cost as a data-dependence. The return value should be the new value for COST. */ static int -sh_adjust_cost (insn, link, dep_insn, cost) - rtx insn; - rtx link ATTRIBUTE_UNUSED; - rtx dep_insn; - int cost; +sh_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx dep_insn, int cost) { rtx reg, use_pat; if (TARGET_SHMEDIA) { /* On SHmedia, if the dependence is an anti-dependence or - output-dependence, there is no cost. */ + output-dependence, there is no cost. */ if (REG_NOTE_KIND (link) != 0) cost = 0; @@ -7577,7 +8267,7 @@ sh_adjust_cost (insn, link, dep_insn, cost) && get_attr_type (insn) == TYPE_DYN_SHIFT && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES && reg_overlap_mentioned_p (SET_DEST (PATTERN (dep_insn)), - XEXP (SET_SRC (single_set(insn)), + XEXP (SET_SRC (single_set (insn)), 1))) cost++; /* When an LS group instruction with a latency of less than @@ -7618,8 +8308,7 @@ sh_adjust_cost (insn, link, dep_insn, cost) /* Check if INSN is flow-dependent on DEP_INSN. Can also be used to check if DEP_INSN is anti-flow dependent on INSN. */ static int -flow_dependent_p (insn, dep_insn) - rtx insn, dep_insn; +flow_dependent_p (rtx insn, rtx dep_insn) { rtx tmp = PATTERN (insn); @@ -7629,10 +8318,7 @@ flow_dependent_p (insn, dep_insn) /* A helper function for flow_dependent_p called through note_stores. */ static void -flow_dependent_p_1 (x, pat, data) - rtx x; - rtx pat ATTRIBUTE_UNUSED; - void *data; +flow_dependent_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data) { rtx * pinsn = (rtx *) data; @@ -7645,7 +8331,7 @@ flow_dependent_p_1 (x, pat, data) do not look like function calls to leaf_function_p. Hence we must do this extra check. */ int -sh_pr_n_sets () +sh_pr_n_sets (void) { return REG_N_SETS (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG); } @@ -7653,7 +8339,7 @@ sh_pr_n_sets () /* This Function returns nonzero if the DFA based scheduler interface is to be used. At present this is supported for the SH4 only. */ static int -sh_use_dfa_interface() +sh_use_dfa_interface (void) { if (TARGET_HARD_SH4) return 1; @@ -7664,7 +8350,7 @@ sh_use_dfa_interface() /* This function returns "2" to indicate dual issue for the SH4 processor. To be used by the DFA pipeline description. */ static int -sh_issue_rate() +sh_issue_rate (void) { if (TARGET_SUPERSCALAR) return 2; @@ -7672,114 +8358,331 @@ sh_issue_rate() return 1; } -/* SHmedia requires registers for branches, so we can't generate new - branches past reload. */ -static bool -sh_cannot_modify_jumps_p () +/* Functions for ready queue reordering for sched1. */ + +/* Get weight for mode for a set x. */ +static short +find_set_regmode_weight (rtx x, enum machine_mode mode) { - return (TARGET_SHMEDIA && (reload_in_progress || reload_completed)); + if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode)) + return 1; + if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode)) + { + if (GET_CODE (SET_DEST (x)) == REG) + { + if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x))) + return 1; + else + return 0; + } + return 1; + } + return 0; } -static bool -sh_ms_bitfield_layout_p (record_type) - tree record_type ATTRIBUTE_UNUSED; +/* Get regmode weight for insn. */ +static short +find_insn_regmode_weight (rtx insn, enum machine_mode mode) { - return TARGET_SH5; + short reg_weight = 0; + rtx x; + + /* Increment weight for each register born here. */ + x = PATTERN (insn); + reg_weight += find_set_regmode_weight (x, mode); + if (GET_CODE (x) == PARALLEL) + { + int j; + for (j = XVECLEN (x, 0) - 1; j >= 0; j--) + { + x = XVECEXP (PATTERN (insn), 0, j); + reg_weight += find_set_regmode_weight (x, mode); + } + } + /* Decrement weight for each register that dies here. */ + for (x = REG_NOTES (insn); x; x = XEXP (x, 1)) + { + if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED) + { + rtx note = XEXP (x, 0); + if (GET_CODE (note) == REG && GET_MODE (note) == mode) + reg_weight--; + } + } + return reg_weight; } -/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we - may access it using GOTOFF instead of GOT. */ +/* Calculate regmode weights for all insns of a basic block. */ +static void +find_regmode_weight (int b, enum machine_mode mode) +{ + rtx insn, next_tail, head, tail; + + get_block_head_tail (b, &head, &tail); + next_tail = NEXT_INSN (tail); + + for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) + { + /* Handle register life information. */ + if (!INSN_P (insn)) + continue; + if (mode == SFmode) + INSN_REGMODE_WEIGHT (insn, mode) = + find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DFmode); + else if (mode == SImode) + INSN_REGMODE_WEIGHT (insn, mode) = + find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DImode); + } +} + +/* Comparison function for ready queue sorting. */ +static int +rank_for_reorder (const void *x, const void *y) +{ + rtx tmp = *(const rtx *) y; + rtx tmp2 = *(const rtx *) x; + + /* The insn in a schedule group should be issued the first. */ + if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2)) + return SCHED_GROUP_P (tmp2) ? 1 : -1; + + /* If insns are equally good, sort by INSN_LUID (original insn order), This + minimizes instruction movement, thus minimizing sched's effect on + register pressure. */ + return INSN_LUID (tmp) - INSN_LUID (tmp2); +} + +/* Resort the array A in which only element at index N may be out of order. */ +static void +swap_reorder (rtx *a, int n) +{ + rtx insn = a[n - 1]; + int i = n - 2; + + while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0) + { + a[i + 1] = a[i]; + i -= 1; + } + a[i + 1] = insn; +} + +#define SCHED_REORDER(READY, N_READY) \ + do \ + { \ + if ((N_READY) == 2) \ + swap_reorder (READY, N_READY); \ + else if ((N_READY) > 2) \ + qsort (READY, N_READY, sizeof (rtx), rank_for_reorder); \ + } \ + while (0) + +/* Sort the ready list READY by ascending priority, using the SCHED_REORDER + macro. */ +static void +ready_reorder (rtx *ready, int nready) +{ + SCHED_REORDER (ready, nready); +} + +/* Calculate regmode weights for all insns of all basic block. */ +static void +sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED, + int verbose ATTRIBUTE_UNUSED, + int old_max_uid) +{ + basic_block b; + + regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short)); + regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short)); + + FOR_EACH_BB_REVERSE (b) + { + find_regmode_weight (b->index, SImode); + find_regmode_weight (b->index, SFmode); + } + + CURR_REGMODE_PRESSURE (SImode) = 0; + CURR_REGMODE_PRESSURE (SFmode) = 0; + +} + +/* Cleanup. */ static void -sh_encode_section_info (decl, first) - tree decl; - int first; +sh_md_finish_global (FILE *dump ATTRIBUTE_UNUSED, + int verbose ATTRIBUTE_UNUSED) { - rtx rtl, symbol; + if (regmode_weight[0]) + { + free (regmode_weight[0]); + regmode_weight[0] = NULL; + } + if (regmode_weight[1]) + { + free (regmode_weight[1]); + regmode_weight[1] = NULL; + } +} - if (DECL_P (decl)) - rtl = DECL_RTL (decl); +/* Cache the can_issue_more so that we can return it from reorder2. Also, + keep count of register pressures on SImode and SFmode. */ +static int +sh_variable_issue (FILE *dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + rtx insn, + int can_issue_more) +{ + if (GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER) + cached_can_issue_more = can_issue_more - 1; else - rtl = TREE_CST_RTL (decl); - if (GET_CODE (rtl) != MEM) - return; - symbol = XEXP (rtl, 0); - if (GET_CODE (symbol) != SYMBOL_REF) - return; + cached_can_issue_more = can_issue_more; - if (flag_pic) - SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl); + if (reload_completed) + return cached_can_issue_more; + + CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode); + CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode); + + return cached_can_issue_more; +} - if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl)) +static void +sh_md_init (FILE *dump ATTRIBUTE_UNUSED, + int verbose ATTRIBUTE_UNUSED, + int veclen ATTRIBUTE_UNUSED) +{ + CURR_REGMODE_PRESSURE (SImode) = 0; + CURR_REGMODE_PRESSURE (SFmode) = 0; +} + +/* Some magic numbers. */ +/* Pressure on register r0 can lead to spill failures. so avoid sched1 for + functions that already have high pressure on r0. */ +#define R0_MAX_LIFE_REGIONS 2 +#define R0_MAX_LIVE_LENGTH 12 +/* Register Pressure thresholds for SImode and SFmode registers. */ +#define SIMODE_MAX_WEIGHT 5 +#define SFMODE_MAX_WEIGHT 10 + +/* Return true if the pressure is high for MODE. */ +static short +high_pressure (enum machine_mode mode) +{ + /* Pressure on register r0 can lead to spill failures. so avoid sched1 for + functions that already have high pressure on r0. */ + if ((REG_N_SETS (0) - REG_N_DEATHS (0)) >= R0_MAX_LIFE_REGIONS + && REG_LIVE_LENGTH (0) >= R0_MAX_LIVE_LENGTH) + return 1; + + if (mode == SFmode) + return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT); + else + return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT); +} + +/* Reorder ready queue if register pressure is high. */ +static int +sh_reorder (FILE *dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + rtx *ready, + int *n_readyp, + int clock_var ATTRIBUTE_UNUSED) +{ + if (reload_completed) + return sh_issue_rate (); + + if (high_pressure (SFmode) || high_pressure (SImode)) { - const char *symbol_str, *orig_str; - bool is_local; - enum tls_model kind; - char encoding; - char *newstr; - size_t len, dlen; + ready_reorder (ready, *n_readyp); + } - orig_str = XSTR (symbol, 0); - is_local = (*targetm.binds_local_p) (decl); + return sh_issue_rate (); +} - if (! flag_pic) - { - if (is_local) - kind = TLS_MODEL_LOCAL_EXEC; - else - kind = TLS_MODEL_INITIAL_EXEC; - } - else if (is_local) - kind = TLS_MODEL_LOCAL_DYNAMIC; - else - kind = TLS_MODEL_GLOBAL_DYNAMIC; - if (kind < flag_tls_default) - kind = flag_tls_default; +/* Skip cycles if the current register pressure is high. */ +static int +sh_reorder2 (FILE *dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + rtx *ready ATTRIBUTE_UNUSED, + int *n_readyp ATTRIBUTE_UNUSED, + int clock_var ATTRIBUTE_UNUSED) +{ + if (reload_completed) + return cached_can_issue_more; + + if (high_pressure(SFmode) || high_pressure (SImode)) + skip_cycles = 1; + + return cached_can_issue_more; +} - STRIP_DATALABEL_ENCODING (symbol_str, orig_str); - dlen = symbol_str - orig_str; +/* Skip cycles without sorting the ready queue. This will move insn from + Q->R. If this is the last cycle we are skipping; allow sorting of ready + queue by sh_reorder. */ - encoding = " GLil"[kind]; - if (TLS_SYMNAME_P (symbol_str)) +/* Generally, skipping these many cycles are sufficient for all insns to move + from Q -> R. */ +#define MAX_SKIPS 8 + +static int +sh_dfa_new_cycle (FILE *sched_dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + rtx insn ATTRIBUTE_UNUSED, + int last_clock_var, + int clock_var, + int *sort_p) +{ + if (reload_completed) + return 0; + + if (skip_cycles) + { + if ((clock_var - last_clock_var) < MAX_SKIPS) { - if (encoding == symbol_str[1]) - return; - /* Handle the changes from initial-exec to local-exec and - from global-dynamic to local-dynamic. */ - if ((encoding == 'l' && symbol_str[1] == 'i') - || (encoding == 'L' && symbol_str[1] == 'G')) - symbol_str += 2; - else - abort (); + *sort_p = 0; + return 1; + } + /* If this is the last cycle we are skipping, allow reordering of R. */ + if ((clock_var - last_clock_var) == MAX_SKIPS) + { + *sort_p = 1; + return 1; } + } - len = strlen (symbol_str); - newstr = alloca (dlen + len + 3); - if (dlen) - memcpy (newstr, orig_str, dlen); - newstr[dlen + 0] = SH_TLS_ENCODING[0]; - newstr[dlen + 1] = encoding; - memcpy (newstr + dlen + 2, symbol_str, len + 1); + skip_cycles = 0; - XSTR (symbol, 0) = ggc_alloc_string (newstr, dlen + len + 2); - } + return 0; +} - if (TARGET_SH5 && first && TREE_CODE (decl) != FUNCTION_DECL) - XEXP (rtl, 0) = gen_datalabel_ref (symbol); +/* SHmedia requires registers for branches, so we can't generate new + branches past reload. */ +static bool +sh_cannot_modify_jumps_p (void) +{ + return (TARGET_SHMEDIA && (reload_in_progress || reload_completed)); } -/* Undo the effects of the above. */ +static int +sh_target_reg_class (void) +{ + return TARGET_SHMEDIA ? TARGET_REGS : NO_REGS; +} -static const char * -sh_strip_name_encoding (str) - const char *str; +static bool +sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen) { - STRIP_DATALABEL_ENCODING (str, str); - STRIP_TLS_ENCODING (str, str); - str += *str == '*'; - return str; + return (shmedia_space_reserved_for_target_registers + && (! after_prologue_epilogue_gen || TARGET_SAVE_ALL_TARGET_REGS)); } +static bool +sh_ms_bitfield_layout_p (tree record_type ATTRIBUTE_UNUSED) +{ + return (TARGET_SH5 || TARGET_HITACHI || sh_attr_renesas_p (record_type)); +} /* On the SH1..SH4, the trampoline looks like @@ -7798,8 +8701,7 @@ sh_strip_name_encoding (str) CXT is an RTX for the static chain value for the function. */ void -sh_initialize_trampoline (tramp, fnaddr, cxt) - rtx tramp, fnaddr, cxt; +sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt) { if (TARGET_SHMEDIA64) { @@ -7903,14 +8805,14 @@ sh_initialize_trampoline (tramp, fnaddr, cxt) movishori)); emit_insn (gen_rotrdi3_mextr (quad0, quad0, GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56))); - emit_insn (gen_ashldi3_media (quad0, quad0, GEN_INT (2))); + emit_insn (gen_ashldi3_media (quad0, quad0, const2_rtx)); emit_move_insn (gen_rtx_MEM (DImode, tramp), quad0); emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0), gen_rtx_SUBREG (V2HImode, cxt, 0), movishori)); emit_insn (gen_rotrdi3_mextr (cxtload, cxtload, GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56))); - emit_insn (gen_ashldi3_media (cxtload, cxtload, GEN_INT (2))); + emit_insn (gen_ashldi3_media (cxtload, cxtload, const2_rtx)); if (TARGET_LITTLE_ENDIAN) { emit_insn (gen_mshflo_l_di (quad1, ptabs, cxtload)); @@ -7944,7 +8846,7 @@ sh_initialize_trampoline (tramp, fnaddr, cxt) if (TARGET_HARVARD) { if (TARGET_USERMODE) - emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__ic_invalidate"), + emit_library_call (function_symbol ("__ic_invalidate"), 0, VOIDmode, 1, tramp, SImode); else emit_insn (gen_ic_invalidate_line (tramp)); @@ -7956,9 +8858,7 @@ sh_initialize_trampoline (tramp, fnaddr, cxt) own stack frame, so it must not pass pointers or references to these arguments to other functions by means of sibling calls. */ static bool -sh_function_ok_for_sibcall (decl, exp) - tree decl; - tree exp ATTRIBUTE_UNUSED; +sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) { return (decl && (! TARGET_SHCOMPACT @@ -8026,10 +8926,10 @@ static const char signature_args[][4] = #define SH_BLTIN_PV 20 { 0, 8 }, }; -/* mcmv: operands considered unsigned. */ +/* mcmv: operands considered unsigned. */ /* mmulsum_wq, msad_ubq: result considered unsigned long long. */ -/* mperm: control value considered unsigned int. */ -/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int. */ +/* mperm: control value considered unsigned int. */ +/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int. */ /* mshards_q: returns signed short. */ /* nsb: takes long long arg, returns unsigned char. */ static const struct builtin_description bdesc[] = @@ -8126,13 +9026,13 @@ static const struct builtin_description bdesc[] = }; static void -sh_media_init_builtins () +sh_media_init_builtins (void) { tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES]; const struct builtin_description *d; memset (shared, 0, sizeof shared); - for (d = bdesc; d - bdesc < (int) (sizeof bdesc / sizeof bdesc[0]); d++) + for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++) { tree type, arg_type; int signature = d->signature; @@ -8180,7 +9080,7 @@ sh_media_init_builtins () } static void -sh_init_builtins () +sh_init_builtins (void) { if (TARGET_SHMEDIA) sh_media_init_builtins (); @@ -8193,12 +9093,8 @@ sh_init_builtins () IGNORE is nonzero if the value is to be ignored. */ static rtx -sh_expand_builtin (exp, target, subtarget, mode, ignore) - tree exp; - rtx target; - rtx subtarget ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; - int ignore; +sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, int ignore) { tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); tree arglist = TREE_OPERAND (exp, 1); @@ -8271,13 +9167,11 @@ sh_expand_builtin (exp, target, subtarget, mode, ignore) } void -sh_expand_unop_v2sf (code, op0, op1) - enum rtx_code code; - rtx op0, op1; +sh_expand_unop_v2sf (enum rtx_code code, rtx op0, rtx op1) { rtx sel0 = const0_rtx; rtx sel1 = const1_rtx; - rtx (*fn) PARAMS ((rtx, rtx, rtx, rtx, rtx)) = gen_unary_sf_op; + rtx (*fn) (rtx, rtx, rtx, rtx, rtx) = gen_unary_sf_op; rtx op = gen_rtx_fmt_e (code, SFmode, op1); emit_insn ((*fn) (op0, op1, op, sel0, sel0)); @@ -8285,13 +9179,11 @@ sh_expand_unop_v2sf (code, op0, op1) } void -sh_expand_binop_v2sf (code, op0, op1, op2) - enum rtx_code code; - rtx op0, op1, op2; +sh_expand_binop_v2sf (enum rtx_code code, rtx op0, rtx op1, rtx op2) { rtx sel0 = const0_rtx; rtx sel1 = const1_rtx; - rtx (*fn) PARAMS ((rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)) + rtx (*fn) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) = gen_binary_sf_op; rtx op = gen_rtx_fmt_ee (code, SFmode, op1, op2); @@ -8302,22 +9194,21 @@ sh_expand_binop_v2sf (code, op0, op1, op2) /* Return the class of registers for which a mode change from FROM to TO is invalid. */ bool -sh_cannot_change_mode_class (from, to, class) - enum machine_mode from, to; - enum reg_class class; +sh_cannot_change_mode_class (enum machine_mode from, enum machine_mode to, + enum reg_class class) { if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to)) { - if (TARGET_LITTLE_ENDIAN) - { - if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8) - return reg_classes_intersect_p (DF_REGS, class); - } - else - { - if (GET_MODE_SIZE (from) < 8) - return reg_classes_intersect_p (DF_HI_REGS, class); - } + if (TARGET_LITTLE_ENDIAN) + { + if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8) + return reg_classes_intersect_p (DF_REGS, class); + } + else + { + if (GET_MODE_SIZE (from) < 8) + return reg_classes_intersect_p (DF_HI_REGS, class); + } } return 0; } @@ -8327,9 +9218,7 @@ sh_cannot_change_mode_class (from, to, class) that label is used. */ void -sh_mark_label (address, nuses) - rtx address; - int nuses; +sh_mark_label (rtx address, int nuses) { if (GOTOFF_P (address)) { @@ -8352,24 +9241,30 @@ sh_mark_label (address, nuses) register information here is not used for SFmode. */ int -sh_register_move_cost (mode, srcclass, dstclass) - enum machine_mode mode; - enum reg_class srcclass, dstclass; +sh_register_move_cost (enum machine_mode mode, + enum reg_class srcclass, enum reg_class dstclass) { if (dstclass == T_REGS || dstclass == PR_REGS) return 10; + if (dstclass == MAC_REGS && srcclass == MAC_REGS) + return 4; + if (mode == SImode && ! TARGET_SHMEDIA && TARGET_FMOVD && REGCLASS_HAS_FP_REG (srcclass) && REGCLASS_HAS_FP_REG (dstclass)) return 4; + if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS) + || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass))) + return 9; + if ((REGCLASS_HAS_FP_REG (dstclass) && REGCLASS_HAS_GENERAL_REG (srcclass)) || (REGCLASS_HAS_GENERAL_REG (dstclass) && REGCLASS_HAS_FP_REG (srcclass))) - return ((TARGET_SHMEDIA ? 4 : TARGET_FMOVD ? 8 : 12) - * ((GET_MODE_SIZE (mode) + 7) / 8U)); + return ((TARGET_SHMEDIA ? 4 : TARGET_FMOVD ? 8 : 12) + * ((GET_MODE_SIZE (mode) + 7) / 8U)); if ((dstclass == FPUL_REGS && REGCLASS_HAS_GENERAL_REG (srcclass)) @@ -8403,20 +9298,26 @@ sh_register_move_cost (mode, srcclass, dstclass) /* Like register_operand, but take into account that SHMEDIA can use the constant zero like a general register. */ int -sh_register_operand (op, mode) - rtx op; - enum machine_mode mode; +sh_register_operand (rtx op, enum machine_mode mode) { if (op == CONST0_RTX (mode) && TARGET_SHMEDIA) return 1; return register_operand (op, mode); } -static rtx emit_load_ptr PARAMS ((rtx, rtx)); +int +cmpsi_operand (rtx op, enum machine_mode mode) +{ + if (GET_CODE (op) == REG && REGNO (op) == T_REG + && GET_MODE (op) == SImode) + return 1; + return arith_operand (op, mode); +} + +static rtx emit_load_ptr (rtx, rtx); static rtx -emit_load_ptr (reg, addr) - rtx reg, addr; +emit_load_ptr (rtx reg, rtx addr) { rtx mem = gen_rtx_MEM (ptr_mode, addr); @@ -8426,38 +9327,35 @@ emit_load_ptr (reg, addr) } void -sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) - FILE *file; - tree thunk_fndecl ATTRIBUTE_UNUSED; - HOST_WIDE_INT delta; - HOST_WIDE_INT vcall_offset; - tree function; +sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, + tree function) { CUMULATIVE_ARGS cum; int structure_value_byref = 0; rtx this, this_value, sibcall, insns, funexp; tree funtype = TREE_TYPE (function); - int simple_add - = (TARGET_SHMEDIA ? CONST_OK_FOR_J (delta) : CONST_OK_FOR_I (delta)); + int simple_add = CONST_OK_FOR_ADD (delta); int did_load = 0; rtx scratch0, scratch1, scratch2; reload_completed = 1; + epilogue_completed = 1; no_new_pseudos = 1; current_function_uses_only_leaf_regs = 1; - emit_note (NULL, NOTE_INSN_PROLOGUE_END); + emit_note (NOTE_INSN_PROLOGUE_END); /* Find the "this" pointer. We have such a wide range of ABIs for the SH that it's best to do this completely machine independently. "this" is passed as first argument, unless a structure return pointer comes first, in which case "this" comes second. */ - INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0); + INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0, 1); #ifndef PCC_STATIC_STRUCT_RETURN - if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)))) + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) structure_value_byref = 1; #endif /* not PCC_STATIC_STRUCT_RETURN */ - if (structure_value_byref && struct_value_rtx == 0) + if (structure_value_byref && sh_struct_value_rtx (function, 0) == 0) { tree ptype = build_pointer_type (TREE_TYPE (funtype)); @@ -8521,9 +9419,7 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) emit_move_insn (scratch1, GEN_INT (vcall_offset)); offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1); } - else if (TARGET_SHMEDIA - ? CONST_OK_FOR_J (vcall_offset) - : CONST_OK_FOR_I (vcall_offset)) + else if (CONST_OK_FOR_ADD (vcall_offset)) { emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset))); offset_addr = scratch0; @@ -8538,7 +9434,7 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) abort (); /* FIXME */ emit_load_ptr (scratch0, offset_addr); - if (Pmode != ptr_mode) + if (Pmode != ptr_mode) scratch0 = gen_rtx_TRUNCATE (ptr_mode, scratch0); emit_insn (gen_add2_insn (this, scratch0)); } @@ -8557,26 +9453,27 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this); emit_barrier (); - /* Run just enough of rest_of_compilation to do scheduling and get + /* Run just enough of rest_of_compilation to do scheduling and get the insns emitted. Note that use_thunk calls assemble_start_function and assemble_end_function. */ + + insn_locators_initialize (); insns = get_insns (); if (optimize > 0 && flag_schedule_insns_after_reload) { - - find_basic_blocks (insns, max_reg_num (), rtl_dump_file); - life_analysis (insns, rtl_dump_file, PROP_FINAL); + find_basic_blocks (insns, max_reg_num (), dump_file); + life_analysis (insns, dump_file, PROP_FINAL); split_all_insns (1); - schedule_insns (rtl_dump_file); + schedule_insns (dump_file); } - MACHINE_DEPENDENT_REORG (insns); + sh_reorg (); if (optimize > 0 && flag_delayed_branch) - dbr_schedule (insns, rtl_dump_file); + dbr_schedule (insns, dump_file); shorten_branches (insns); final_start_function (insns, file, 1); final (insns, file, 1, 0); @@ -8592,7 +9489,135 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) } reload_completed = 0; + epilogue_completed = 0; no_new_pseudos = 0; } +rtx +function_symbol (const char *name) +{ + rtx sym = gen_rtx_SYMBOL_REF (Pmode, name); + SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION; + return sym; +} + +/* Find the number of a general purpose register in S. */ +static int +scavenge_reg (HARD_REG_SET *s) +{ + int r; + for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++) + if (TEST_HARD_REG_BIT (*s, r)) + return r; + return -1; +} + +rtx +sh_get_pr_initial_val (void) +{ + rtx val; + + /* ??? Unfortunately, get_hard_reg_initial_val doesn't always work for the + PR register on SHcompact, because it might be clobbered by the prologue. + We check first if that is known to be the case. */ + if (TARGET_SHCOMPACT + && ((current_function_args_info.call_cookie + & ~ CALL_COOKIE_RET_TRAMP (1)) + || current_function_has_nonlocal_label)) + return gen_rtx_MEM (SImode, return_address_pointer_rtx); + + /* If we haven't finished rtl generation, there might be a nonlocal label + that we haven't seen yet. + ??? get_hard_reg_initial_val fails if it is called while no_new_pseudos + is set, unless it has been called before for the same register. And even + then, we end in trouble if we didn't use the register in the same + basic block before. So call get_hard_reg_initial_val now and wrap it + in an unspec if we might need to replace it. */ + /* ??? We also must do this for TARGET_SH1 in general, because otherwise + combine can put the pseudo returned by get_hard_reg_initial_val into + instructions that need a general purpose registers, which will fail to + be recognized when the pseudo becomes allocated to PR. */ + val + = get_hard_reg_initial_val (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG); + if (TARGET_SH1) + return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA); + return val; +} + +int +sh_expand_t_scc (enum rtx_code code, rtx target) +{ + rtx result = target; + HOST_WIDE_INT val; + + if (GET_CODE (sh_compare_op0) != REG || REGNO (sh_compare_op0) != T_REG + || GET_CODE (sh_compare_op1) != CONST_INT) + return 0; + if (GET_CODE (result) != REG) + result = gen_reg_rtx (SImode); + val = INTVAL (sh_compare_op1); + if ((code == EQ && val == 1) || (code == NE && val == 0)) + emit_insn (gen_movt (result)); + else if ((code == EQ && val == 0) || (code == NE && val == 1)) + { + emit_insn (gen_rtx_CLOBBER (VOIDmode, result)); + emit_insn (gen_subc (result, result, result)); + emit_insn (gen_addsi3 (result, result, const1_rtx)); + } + else if (code == EQ || code == NE) + emit_insn (gen_move_insn (result, GEN_INT (code == NE))); + else + return 0; + if (result != target) + emit_move_insn (target, result); + return 1; +} + +/* INSN is an sfunc; return the rtx that describes the address used. */ +static rtx +extract_sfunc_addr (rtx insn) +{ + rtx pattern, part = NULL_RTX; + int len, i; + + pattern = PATTERN (insn); + len = XVECLEN (pattern, 0); + for (i = 0; i < len; i++) + { + part = XVECEXP (pattern, 0, i); + if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == Pmode + && GENERAL_REGISTER_P (true_regnum (XEXP (part, 0)))) + return XEXP (part, 0); + } + if (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE) + return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1); + abort (); +} + +/* Verify that the register in use_sfunc_addr still agrees with the address + used in the sfunc. This prevents fill_slots_from_thread from changing + use_sfunc_addr. + INSN is the use_sfunc_addr instruction, and REG is the register it + guards. */ +int +check_use_sfunc_addr (rtx insn, rtx reg) +{ + /* Search for the sfunc. It should really come right after INSN. */ + while ((insn = NEXT_INSN (insn))) + { + if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN) + break; + if (! INSN_P (insn)) + continue; + + if (GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); + if (GET_CODE (PATTERN (insn)) != PARALLEL + || get_attr_type (insn) != TYPE_SFUNC) + continue; + return rtx_equal_p (extract_sfunc_addr (insn), reg); + } + abort (); +} + #include "gt-sh.h"