OpenCores
URL https://opencores.org/ocsvn/openrisc_2011-10-31/openrisc_2011-10-31/trunk

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [tags/] [gnu-src/] [gcc-4.5.1/] [gcc-4.5.1-or32-1.0rc1/] [gcc/] [config/] [sh/] [sh.c] - Diff between revs 282 and 338

Only display areas with differences | Details | Blame | View Log

Rev 282 Rev 338
/* Output routines for GCC for Renesas / SuperH SH.
/* Output routines for GCC for Renesas / SuperH SH.
   Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
   Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
   Contributed by Steve Chamberlain (sac@cygnus.com).
   Contributed by Steve Chamberlain (sac@cygnus.com).
   Improved by Jim Wilson (wilson@cygnus.com).
   Improved by Jim Wilson (wilson@cygnus.com).
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC 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
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
the Free Software Foundation; either version 3, or (at your option)
any later version.
any later version.
 
 
GCC 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
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
GNU General Public License for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
<http://www.gnu.org/licenses/>.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tm.h"
#include "tm.h"
#include "insn-config.h"
#include "insn-config.h"
#include "rtl.h"
#include "rtl.h"
#include "tree.h"
#include "tree.h"
#include "flags.h"
#include "flags.h"
#include "expr.h"
#include "expr.h"
#include "optabs.h"
#include "optabs.h"
#include "function.h"
#include "function.h"
#include "regs.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "output.h"
#include "output.h"
#include "insn-attr.h"
#include "insn-attr.h"
#include "toplev.h"
#include "toplev.h"
#include "recog.h"
#include "recog.h"
#include "integrate.h"
#include "integrate.h"
#include "dwarf2.h"
#include "dwarf2.h"
#include "tm_p.h"
#include "tm_p.h"
#include "target.h"
#include "target.h"
#include "target-def.h"
#include "target-def.h"
#include "real.h"
#include "real.h"
#include "langhooks.h"
#include "langhooks.h"
#include "basic-block.h"
#include "basic-block.h"
#include "df.h"
#include "df.h"
#include "cfglayout.h"
#include "cfglayout.h"
#include "intl.h"
#include "intl.h"
#include "sched-int.h"
#include "sched-int.h"
#include "params.h"
#include "params.h"
#include "ggc.h"
#include "ggc.h"
#include "gimple.h"
#include "gimple.h"
#include "cfgloop.h"
#include "cfgloop.h"
#include "alloc-pool.h"
#include "alloc-pool.h"
#include "tm-constrs.h"
#include "tm-constrs.h"
 
 
 
 
int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
 
 
#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
 
 
/* These are some macros to abstract register modes.  */
/* These are some macros to abstract register modes.  */
#define CONST_OK_FOR_ADD(size) \
#define CONST_OK_FOR_ADD(size) \
  (TARGET_SHMEDIA ? CONST_OK_FOR_I10 (size) : CONST_OK_FOR_I08 (size))
  (TARGET_SHMEDIA ? CONST_OK_FOR_I10 (size) : CONST_OK_FOR_I08 (size))
#define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))
#define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))
#define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))
#define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))
#define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))
#define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))
 
 
/* Used to simplify the logic below.  Find the attributes wherever
/* Used to simplify the logic below.  Find the attributes wherever
   they may be.  */
   they may be.  */
#define SH_ATTRIBUTES(decl) \
#define SH_ATTRIBUTES(decl) \
  (TYPE_P (decl)) ? TYPE_ATTRIBUTES (decl) \
  (TYPE_P (decl)) ? TYPE_ATTRIBUTES (decl) \
                  : DECL_ATTRIBUTES (decl) \
                  : DECL_ATTRIBUTES (decl) \
                  ? (DECL_ATTRIBUTES (decl)) \
                  ? (DECL_ATTRIBUTES (decl)) \
                  : TYPE_ATTRIBUTES (TREE_TYPE (decl))
                  : TYPE_ATTRIBUTES (TREE_TYPE (decl))
 
 
/* Set to 1 by expand_prologue() when the function is an interrupt handler.  */
/* Set to 1 by expand_prologue() when the function is an interrupt handler.  */
int current_function_interrupt;
int current_function_interrupt;
 
 
tree sh_deferred_function_attributes;
tree sh_deferred_function_attributes;
tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
 
 
/* Global variables for machine-dependent things.  */
/* Global variables for machine-dependent things.  */
 
 
/* Which cpu are we scheduling for.  */
/* Which cpu are we scheduling for.  */
enum processor_type sh_cpu;
enum processor_type sh_cpu;
 
 
/* Definitions used in ready queue reordering for first scheduling pass.  */
/* Definitions used in ready queue reordering for first scheduling pass.  */
 
 
/* Reg weights arrays for modes SFmode and SImode, indexed by insn LUID.  */
/* Reg weights arrays for modes SFmode and SImode, indexed by insn LUID.  */
static short *regmode_weight[2];
static short *regmode_weight[2];
 
 
/* Total SFmode and SImode weights of scheduled insns.  */
/* Total SFmode and SImode weights of scheduled insns.  */
static int curr_regmode_pressure[2];
static int curr_regmode_pressure[2];
 
 
/* Number of r0 life regions.  */
/* Number of r0 life regions.  */
static int r0_life_regions;
static int r0_life_regions;
 
 
/* If true, skip cycles for Q -> R movement.  */
/* If true, skip cycles for Q -> R movement.  */
static int skip_cycles = 0;
static int skip_cycles = 0;
 
 
/* Cached value of can_issue_more. This is cached in sh_variable_issue hook
/* Cached value of can_issue_more. This is cached in sh_variable_issue hook
   and returned from sh_reorder2.  */
   and returned from sh_reorder2.  */
static short cached_can_issue_more;
static short cached_can_issue_more;
 
 
/* Unique number for UNSPEC_BBR pattern.  */
/* Unique number for UNSPEC_BBR pattern.  */
static unsigned int unspec_bbr_uid = 1;
static unsigned int unspec_bbr_uid = 1;
 
 
/* Provides the class number of the smallest class containing
/* Provides the class number of the smallest class containing
   reg number.  */
   reg number.  */
 
 
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
{
{
  R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
  FP0_REGS,FP_REGS, FP_REGS, FP_REGS,
  FP0_REGS,FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
  TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
  TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
  TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
  TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
  DF_REGS, DF_REGS, DF_REGS, DF_REGS,
  DF_REGS, DF_REGS, DF_REGS, DF_REGS,
  DF_REGS, DF_REGS, DF_REGS, DF_REGS,
  DF_REGS, DF_REGS, DF_REGS, DF_REGS,
  NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
  NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
  MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
  MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
  GENERAL_REGS, GENERAL_REGS,
  GENERAL_REGS, GENERAL_REGS,
};
};
 
 
char sh_register_names[FIRST_PSEUDO_REGISTER] \
char sh_register_names[FIRST_PSEUDO_REGISTER] \
  [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;
  [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;
 
 
char sh_additional_register_names[ADDREGNAMES_SIZE] \
char sh_additional_register_names[ADDREGNAMES_SIZE] \
  [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1]
  [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1]
  = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;
  = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;
 
 
int assembler_dialect;
int assembler_dialect;
 
 
static bool shmedia_space_reserved_for_target_registers;
static bool shmedia_space_reserved_for_target_registers;
 
 
static bool sh_handle_option (size_t, const char *, int);
static bool sh_handle_option (size_t, const char *, int);
static void split_branches (rtx);
static void split_branches (rtx);
static int branch_dest (rtx);
static int branch_dest (rtx);
static void force_into (rtx, rtx);
static void force_into (rtx, rtx);
static void print_slot (rtx);
static void print_slot (rtx);
static rtx add_constant (rtx, enum machine_mode, rtx);
static rtx add_constant (rtx, enum machine_mode, rtx);
static void dump_table (rtx, rtx);
static void dump_table (rtx, rtx);
static int hi_const (rtx);
static int hi_const (rtx);
static int broken_move (rtx);
static int broken_move (rtx);
static int mova_p (rtx);
static int mova_p (rtx);
static rtx find_barrier (int, rtx, rtx);
static rtx find_barrier (int, rtx, rtx);
static int noncall_uses_reg (rtx, rtx, rtx *);
static int noncall_uses_reg (rtx, rtx, rtx *);
static rtx gen_block_redirect (rtx, int, int);
static rtx gen_block_redirect (rtx, int, int);
static void sh_reorg (void);
static void sh_reorg (void);
static void output_stack_adjust (int, rtx, int, HARD_REG_SET *, bool);
static void output_stack_adjust (int, rtx, int, HARD_REG_SET *, bool);
static rtx frame_insn (rtx);
static rtx frame_insn (rtx);
static rtx push (int);
static rtx push (int);
static void pop (int);
static void pop (int);
static void push_regs (HARD_REG_SET *, int);
static void push_regs (HARD_REG_SET *, int);
static int calc_live_regs (HARD_REG_SET *);
static int calc_live_regs (HARD_REG_SET *);
static HOST_WIDE_INT rounded_frame_size (int);
static HOST_WIDE_INT rounded_frame_size (int);
static rtx mark_constant_pool_use (rtx);
static rtx mark_constant_pool_use (rtx);
static tree sh_handle_interrupt_handler_attribute (tree *, tree, tree, int, bool *);
static tree sh_handle_interrupt_handler_attribute (tree *, tree, tree, int, bool *);
static tree sh_handle_resbank_handler_attribute (tree *, tree,
static tree sh_handle_resbank_handler_attribute (tree *, tree,
                                                 tree, int, bool *);
                                                 tree, int, bool *);
static tree sh2a_handle_function_vector_handler_attribute (tree *, tree,
static tree sh2a_handle_function_vector_handler_attribute (tree *, tree,
                                                           tree, int, bool *);
                                                           tree, int, bool *);
static tree sh_handle_sp_switch_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_trap_exit_attribute (tree *, tree, tree, int, bool *);
static tree sh_handle_renesas_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_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void sh_insert_attributes (tree, tree *);
static void sh_insert_attributes (tree, tree *);
static const char *sh_check_pch_target_flags (int);
static const char *sh_check_pch_target_flags (int);
static int sh_adjust_cost (rtx, rtx, rtx, int);
static int sh_adjust_cost (rtx, rtx, rtx, int);
static int sh_issue_rate (void);
static int sh_issue_rate (void);
static int sh_dfa_new_cycle (FILE *, int, rtx, int, int, int *sort_p);
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_set_regmode_weight (rtx, enum machine_mode);
static short find_insn_regmode_weight (rtx, enum machine_mode);
static short find_insn_regmode_weight (rtx, enum machine_mode);
static void find_regmode_weight (basic_block, enum machine_mode);
static void find_regmode_weight (basic_block, enum machine_mode);
static int find_r0_life_regions (basic_block);
static int find_r0_life_regions (basic_block);
static void  sh_md_init_global (FILE *, int, int);
static void  sh_md_init_global (FILE *, int, int);
static void  sh_md_finish_global (FILE *, int);
static void  sh_md_finish_global (FILE *, int);
static int rank_for_reorder (const void *, const void *);
static int rank_for_reorder (const void *, const void *);
static void swap_reorder (rtx *, int);
static void swap_reorder (rtx *, int);
static void ready_reorder (rtx *, int);
static void ready_reorder (rtx *, int);
static short high_pressure (enum machine_mode);
static short high_pressure (enum machine_mode);
static int sh_reorder (FILE *, int, rtx *, int *, int);
static int sh_reorder (FILE *, int, rtx *, int *, int);
static int sh_reorder2 (FILE *, int, rtx *, int *, int);
static int sh_reorder2 (FILE *, int, rtx *, int *, int);
static void sh_md_init (FILE *, int, int);
static void sh_md_init (FILE *, int, int);
static int sh_variable_issue (FILE *, int, rtx, int);
static int sh_variable_issue (FILE *, int, rtx, int);
 
 
static bool sh_function_ok_for_sibcall (tree, tree);
static bool sh_function_ok_for_sibcall (tree, tree);
 
 
static bool sh_cannot_modify_jumps_p (void);
static bool sh_cannot_modify_jumps_p (void);
static enum reg_class sh_target_reg_class (void);
static enum reg_class sh_target_reg_class (void);
static bool sh_optimize_target_register_callee_saved (bool);
static bool sh_optimize_target_register_callee_saved (bool);
static bool sh_ms_bitfield_layout_p (const_tree);
static bool sh_ms_bitfield_layout_p (const_tree);
 
 
static void sh_init_builtins (void);
static void sh_init_builtins (void);
static tree sh_builtin_decl (unsigned, bool);
static tree sh_builtin_decl (unsigned, bool);
static void sh_media_init_builtins (void);
static void sh_media_init_builtins (void);
static tree sh_media_builtin_decl (unsigned, bool);
static tree sh_media_builtin_decl (unsigned, bool);
static rtx sh_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
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_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
static void sh_file_start (void);
static void sh_file_start (void);
static int flow_dependent_p (rtx, rtx);
static int flow_dependent_p (rtx, rtx);
static void flow_dependent_p_1 (rtx, const_rtx, void *);
static void flow_dependent_p_1 (rtx, const_rtx, void *);
static int shiftcosts (rtx);
static int shiftcosts (rtx);
static int andcosts (rtx);
static int andcosts (rtx);
static int addsubcosts (rtx);
static int addsubcosts (rtx);
static int multcosts (rtx);
static int multcosts (rtx);
static bool unspec_caller_rtx_p (rtx);
static bool unspec_caller_rtx_p (rtx);
static bool sh_cannot_copy_insn_p (rtx);
static bool sh_cannot_copy_insn_p (rtx);
static bool sh_rtx_costs (rtx, int, int, int *, bool);
static bool sh_rtx_costs (rtx, int, int, int *, bool);
static int sh_address_cost (rtx, bool);
static int sh_address_cost (rtx, bool);
static int sh_pr_n_sets (void);
static int sh_pr_n_sets (void);
static rtx sh_allocate_initial_value (rtx);
static rtx sh_allocate_initial_value (rtx);
static bool sh_legitimate_address_p (enum machine_mode, rtx, bool);
static bool sh_legitimate_address_p (enum machine_mode, rtx, bool);
static rtx sh_legitimize_address (rtx, rtx, enum machine_mode);
static rtx sh_legitimize_address (rtx, rtx, enum machine_mode);
static int shmedia_target_regs_stack_space (HARD_REG_SET *);
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_reserve_space_for_target_registers_p (int, HARD_REG_SET *);
static int shmedia_target_regs_stack_adjust (HARD_REG_SET *);
static int shmedia_target_regs_stack_adjust (HARD_REG_SET *);
static int scavenge_reg (HARD_REG_SET *s);
static int scavenge_reg (HARD_REG_SET *s);
struct save_schedule_s;
struct save_schedule_s;
static struct save_entry_s *sh5_schedule_saves (HARD_REG_SET *,
static struct save_entry_s *sh5_schedule_saves (HARD_REG_SET *,
                                                struct save_schedule_s *, int);
                                                struct save_schedule_s *, int);
 
 
static rtx sh_struct_value_rtx (tree, int);
static rtx sh_struct_value_rtx (tree, int);
static rtx sh_function_value (const_tree, const_tree, bool);
static rtx sh_function_value (const_tree, const_tree, bool);
static rtx sh_libcall_value (enum machine_mode, const_rtx);
static rtx sh_libcall_value (enum machine_mode, const_rtx);
static bool sh_return_in_memory (const_tree, const_tree);
static bool sh_return_in_memory (const_tree, const_tree);
static rtx sh_builtin_saveregs (void);
static rtx sh_builtin_saveregs (void);
static void sh_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, tree, int *, int);
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_strict_argument_naming (CUMULATIVE_ARGS *);
static bool sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *);
static bool sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *);
static tree sh_build_builtin_va_list (void);
static tree sh_build_builtin_va_list (void);
static void sh_va_start (tree, rtx);
static void sh_va_start (tree, rtx);
static tree sh_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
static tree sh_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
static bool sh_promote_prototypes (const_tree);
static bool sh_promote_prototypes (const_tree);
static enum machine_mode sh_promote_function_mode (const_tree type,
static enum machine_mode sh_promote_function_mode (const_tree type,
                                                   enum machine_mode,
                                                   enum machine_mode,
                                                   int *punsignedp,
                                                   int *punsignedp,
                                                   const_tree funtype,
                                                   const_tree funtype,
                                                   int for_return);
                                                   int for_return);
static bool sh_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
static bool sh_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
                                  const_tree, bool);
                                  const_tree, bool);
static bool sh_callee_copies (CUMULATIVE_ARGS *, enum machine_mode,
static bool sh_callee_copies (CUMULATIVE_ARGS *, enum machine_mode,
                              const_tree, bool);
                              const_tree, bool);
static int sh_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
static int sh_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
                                 tree, bool);
                                 tree, bool);
static bool sh_scalar_mode_supported_p (enum machine_mode);
static bool sh_scalar_mode_supported_p (enum machine_mode);
static int sh_dwarf_calling_convention (const_tree);
static int sh_dwarf_calling_convention (const_tree);
static void sh_encode_section_info (tree, rtx, int);
static void sh_encode_section_info (tree, rtx, int);
static int sh2a_function_vector_p (tree);
static int sh2a_function_vector_p (tree);
static void sh_trampoline_init (rtx, tree, rtx);
static void sh_trampoline_init (rtx, tree, rtx);
static rtx sh_trampoline_adjust_address (rtx);
static rtx sh_trampoline_adjust_address (rtx);


static const struct attribute_spec sh_attribute_table[] =
static const struct attribute_spec sh_attribute_table[] =
{
{
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
  { "interrupt_handler", 0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
  { "interrupt_handler", 0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
  { "sp_switch",         1, 1, true,  false, false, sh_handle_sp_switch_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 },
  { "trap_exit",         1, 1, true,  false, false, sh_handle_trap_exit_attribute },
  { "renesas",           0, 0, false, true, false, sh_handle_renesas_attribute },
  { "renesas",           0, 0, false, true, false, sh_handle_renesas_attribute },
  { "trapa_handler",     0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
  { "trapa_handler",     0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
  { "nosave_low_regs",   0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
  { "nosave_low_regs",   0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
  { "resbank",           0, 0, true,  false, false, sh_handle_resbank_handler_attribute },
  { "resbank",           0, 0, true,  false, false, sh_handle_resbank_handler_attribute },
  { "function_vector",   1, 1, true,  false, false, sh2a_handle_function_vector_handler_attribute },
  { "function_vector",   1, 1, true,  false, false, sh2a_handle_function_vector_handler_attribute },
#ifdef SYMBIAN
#ifdef SYMBIAN
  /* Symbian support adds three new attributes:
  /* Symbian support adds three new attributes:
     dllexport - for exporting a function/variable that will live in a dll
     dllexport - for exporting a function/variable that will live in a dll
     dllimport - for importing a function/variable from a dll
     dllimport - for importing a function/variable from a dll
 
 
     Microsoft allows multiple declspecs in one __declspec, separating
     Microsoft allows multiple declspecs in one __declspec, separating
     them with spaces.  We do NOT support this.  Instead, use __declspec
     them with spaces.  We do NOT support this.  Instead, use __declspec
     multiple times.  */
     multiple times.  */
  { "dllimport",         0, 0, true,  false, false, sh_symbian_handle_dll_attribute },
  { "dllimport",         0, 0, true,  false, false, sh_symbian_handle_dll_attribute },
  { "dllexport",         0, 0, true,  false, false, sh_symbian_handle_dll_attribute },
  { "dllexport",         0, 0, true,  false, false, sh_symbian_handle_dll_attribute },
#endif
#endif
  { NULL,                0, 0, false, false, false, NULL }
  { NULL,                0, 0, false, false, false, NULL }
};
};


/* Initialize the GCC target structure.  */
/* Initialize the GCC target structure.  */
#undef TARGET_ATTRIBUTE_TABLE
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE sh_attribute_table
#define TARGET_ATTRIBUTE_TABLE sh_attribute_table
 
 
/* The next two are used for debug info when compiling with -gdwarf.  */
/* The next two are used for debug info when compiling with -gdwarf.  */
#undef TARGET_ASM_UNALIGNED_HI_OP
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP "\t.uaword\t"
#define TARGET_ASM_UNALIGNED_HI_OP "\t.uaword\t"
#undef TARGET_ASM_UNALIGNED_SI_OP
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
 
 
/* These are NULLed out on non-SH5 in OVERRIDE_OPTIONS.  */
/* These are NULLed out on non-SH5 in OVERRIDE_OPTIONS.  */
#undef TARGET_ASM_UNALIGNED_DI_OP
#undef TARGET_ASM_UNALIGNED_DI_OP
#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"
#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"
#undef TARGET_ASM_ALIGNED_DI_OP
#undef TARGET_ASM_ALIGNED_DI_OP
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
 
 
#undef TARGET_ASM_FUNCTION_EPILOGUE
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue
#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue
 
 
#undef TARGET_ASM_OUTPUT_MI_THUNK
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK sh_output_mi_thunk
#define TARGET_ASM_OUTPUT_MI_THUNK sh_output_mi_thunk
 
 
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
 
#undef TARGET_ASM_FILE_START
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START sh_file_start
#define TARGET_ASM_FILE_START sh_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
 
#undef TARGET_DEFAULT_TARGET_FLAGS
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
#undef TARGET_HANDLE_OPTION
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION sh_handle_option
#define TARGET_HANDLE_OPTION sh_handle_option
 
 
#undef TARGET_INSERT_ATTRIBUTES
#undef TARGET_INSERT_ATTRIBUTES
#define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
#define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
 
 
#undef TARGET_SCHED_ADJUST_COST
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST sh_adjust_cost
#define TARGET_SCHED_ADJUST_COST sh_adjust_cost
 
 
#undef TARGET_SCHED_ISSUE_RATE
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE sh_issue_rate
#define TARGET_SCHED_ISSUE_RATE sh_issue_rate
 
 
/* The next 5 hooks have been implemented for reenabling sched1.  With the
/* 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
   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
   reduce the register pressure.  The overall idea is to keep count of SImode
   and SFmode regs required by already scheduled insns. When these counts
   and SFmode regs required by already scheduled insns. When these counts
   cross some threshold values; give priority to insns that free registers.
   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
   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
   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
   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.
   upto a max of 8 cycles so that such insns may move from Q -> R.
 
 
   The description of the hooks are as below:
   The description of the hooks are as below:
 
 
   TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic
   TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic
   scheduler; it is called inside the sched_init function just after
   scheduler; it is called inside the sched_init function just after
   find_insn_reg_weights function call. It is used to calculate the SImode
   find_insn_reg_weights function call. It is used to calculate the SImode
   and SFmode weights of insns of basic blocks; much similar to what
   and SFmode weights of insns of basic blocks; much similar to what
   find_insn_reg_weights does.
   find_insn_reg_weights does.
   TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook.
   TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook.
 
 
   TARGET_SCHED_DFA_NEW_CYCLE: Skip cycles if high register pressure is
   TARGET_SCHED_DFA_NEW_CYCLE: Skip cycles if high register pressure is
   indicated by TARGET_SCHED_REORDER2; doing this may move insns from
   indicated by TARGET_SCHED_REORDER2; doing this may move insns from
   (Q)->(R).
   (Q)->(R).
 
 
   TARGET_SCHED_REORDER: If the register pressure for SImode or SFmode is
   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
   high; reorder the ready queue so that the insn with lowest LUID will be
   issued next.
   issued next.
 
 
   TARGET_SCHED_REORDER2: If the register pressure is high, indicate to
   TARGET_SCHED_REORDER2: If the register pressure is high, indicate to
   TARGET_SCHED_DFA_NEW_CYCLE to skip cycles.
   TARGET_SCHED_DFA_NEW_CYCLE to skip cycles.
 
 
   TARGET_SCHED_VARIABLE_ISSUE: Cache the value of can_issue_more so that it
   TARGET_SCHED_VARIABLE_ISSUE: Cache the value of can_issue_more so that it
   can be returned from TARGET_SCHED_REORDER2.
   can be returned from TARGET_SCHED_REORDER2.
 
 
   TARGET_SCHED_INIT: Reset the register pressure counting variables.  */
   TARGET_SCHED_INIT: Reset the register pressure counting variables.  */
 
 
#undef TARGET_SCHED_DFA_NEW_CYCLE
#undef TARGET_SCHED_DFA_NEW_CYCLE
#define TARGET_SCHED_DFA_NEW_CYCLE sh_dfa_new_cycle
#define TARGET_SCHED_DFA_NEW_CYCLE sh_dfa_new_cycle
 
 
#undef TARGET_SCHED_INIT_GLOBAL
#undef TARGET_SCHED_INIT_GLOBAL
#define TARGET_SCHED_INIT_GLOBAL sh_md_init_global
#define TARGET_SCHED_INIT_GLOBAL sh_md_init_global
 
 
#undef TARGET_SCHED_FINISH_GLOBAL
#undef TARGET_SCHED_FINISH_GLOBAL
#define TARGET_SCHED_FINISH_GLOBAL sh_md_finish_global
#define TARGET_SCHED_FINISH_GLOBAL sh_md_finish_global
 
 
#undef TARGET_SCHED_VARIABLE_ISSUE
#undef TARGET_SCHED_VARIABLE_ISSUE
#define TARGET_SCHED_VARIABLE_ISSUE sh_variable_issue
#define TARGET_SCHED_VARIABLE_ISSUE sh_variable_issue
 
 
#undef TARGET_SCHED_REORDER
#undef TARGET_SCHED_REORDER
#define TARGET_SCHED_REORDER sh_reorder
#define TARGET_SCHED_REORDER sh_reorder
 
 
#undef TARGET_SCHED_REORDER2
#undef TARGET_SCHED_REORDER2
#define TARGET_SCHED_REORDER2 sh_reorder2
#define TARGET_SCHED_REORDER2 sh_reorder2
 
 
#undef TARGET_SCHED_INIT
#undef TARGET_SCHED_INIT
#define TARGET_SCHED_INIT sh_md_init
#define TARGET_SCHED_INIT sh_md_init
 
 
#undef TARGET_LEGITIMIZE_ADDRESS
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS sh_legitimize_address
#define TARGET_LEGITIMIZE_ADDRESS sh_legitimize_address
 
 
#undef TARGET_CANNOT_MODIFY_JUMPS_P
#undef TARGET_CANNOT_MODIFY_JUMPS_P
#define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p
#define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p
#undef TARGET_BRANCH_TARGET_REGISTER_CLASS
#undef TARGET_BRANCH_TARGET_REGISTER_CLASS
#define TARGET_BRANCH_TARGET_REGISTER_CLASS sh_target_reg_class
#define TARGET_BRANCH_TARGET_REGISTER_CLASS sh_target_reg_class
#undef TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED
#undef TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED
#define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED \
#define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED \
 sh_optimize_target_register_callee_saved
 sh_optimize_target_register_callee_saved
 
 
#undef TARGET_MS_BITFIELD_LAYOUT_P
#undef TARGET_MS_BITFIELD_LAYOUT_P
#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
 
 
#undef TARGET_INIT_BUILTINS
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS sh_init_builtins
#define TARGET_INIT_BUILTINS sh_init_builtins
#undef TARGET_BUILTIN_DECL
#undef TARGET_BUILTIN_DECL
#define TARGET_BUILTIN_DECL sh_builtin_decl
#define TARGET_BUILTIN_DECL sh_builtin_decl
#undef TARGET_EXPAND_BUILTIN
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN sh_expand_builtin
#define TARGET_EXPAND_BUILTIN sh_expand_builtin
 
 
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL sh_function_ok_for_sibcall
#define TARGET_FUNCTION_OK_FOR_SIBCALL sh_function_ok_for_sibcall
 
 
#undef TARGET_CANNOT_COPY_INSN_P
#undef TARGET_CANNOT_COPY_INSN_P
#define TARGET_CANNOT_COPY_INSN_P sh_cannot_copy_insn_p
#define TARGET_CANNOT_COPY_INSN_P sh_cannot_copy_insn_p
#undef TARGET_RTX_COSTS
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS sh_rtx_costs
#define TARGET_RTX_COSTS sh_rtx_costs
#undef TARGET_ADDRESS_COST
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST sh_address_cost
#define TARGET_ADDRESS_COST sh_address_cost
#undef TARGET_ALLOCATE_INITIAL_VALUE
#undef TARGET_ALLOCATE_INITIAL_VALUE
#define TARGET_ALLOCATE_INITIAL_VALUE sh_allocate_initial_value
#define TARGET_ALLOCATE_INITIAL_VALUE sh_allocate_initial_value
 
 
#undef TARGET_MACHINE_DEPENDENT_REORG
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG sh_reorg
#define TARGET_MACHINE_DEPENDENT_REORG sh_reorg
 
 
#undef TARGET_DWARF_REGISTER_SPAN
#undef TARGET_DWARF_REGISTER_SPAN
#define TARGET_DWARF_REGISTER_SPAN sh_dwarf_register_span
#define TARGET_DWARF_REGISTER_SPAN sh_dwarf_register_span
 
 
#ifdef HAVE_AS_TLS
#ifdef HAVE_AS_TLS
#undef TARGET_HAVE_TLS
#undef TARGET_HAVE_TLS
#define TARGET_HAVE_TLS true
#define TARGET_HAVE_TLS true
#endif
#endif
 
 
#undef TARGET_PROMOTE_PROTOTYPES
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES sh_promote_prototypes
#define TARGET_PROMOTE_PROTOTYPES sh_promote_prototypes
#undef TARGET_PROMOTE_FUNCTION_MODE
#undef TARGET_PROMOTE_FUNCTION_MODE
#define TARGET_PROMOTE_FUNCTION_MODE sh_promote_function_mode
#define TARGET_PROMOTE_FUNCTION_MODE sh_promote_function_mode
 
 
#undef TARGET_FUNCTION_VALUE
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE sh_function_value
#define TARGET_FUNCTION_VALUE sh_function_value
#undef TARGET_LIBCALL_VALUE
#undef TARGET_LIBCALL_VALUE
#define TARGET_LIBCALL_VALUE sh_libcall_value
#define TARGET_LIBCALL_VALUE sh_libcall_value
#undef TARGET_STRUCT_VALUE_RTX
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX sh_struct_value_rtx
#define TARGET_STRUCT_VALUE_RTX sh_struct_value_rtx
#undef TARGET_RETURN_IN_MEMORY
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY sh_return_in_memory
#define TARGET_RETURN_IN_MEMORY sh_return_in_memory
 
 
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS sh_builtin_saveregs
#define TARGET_EXPAND_BUILTIN_SAVEREGS sh_builtin_saveregs
#undef TARGET_SETUP_INCOMING_VARARGS
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS sh_setup_incoming_varargs
#define TARGET_SETUP_INCOMING_VARARGS sh_setup_incoming_varargs
#undef TARGET_STRICT_ARGUMENT_NAMING
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING sh_strict_argument_naming
#define TARGET_STRICT_ARGUMENT_NAMING sh_strict_argument_naming
#undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED
#undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED
#define TARGET_PRETEND_OUTGOING_VARARGS_NAMED sh_pretend_outgoing_varargs_named
#define TARGET_PRETEND_OUTGOING_VARARGS_NAMED sh_pretend_outgoing_varargs_named
#undef TARGET_MUST_PASS_IN_STACK
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#undef TARGET_PASS_BY_REFERENCE
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE sh_pass_by_reference
#define TARGET_PASS_BY_REFERENCE sh_pass_by_reference
#undef TARGET_CALLEE_COPIES
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES sh_callee_copies
#define TARGET_CALLEE_COPIES sh_callee_copies
#undef TARGET_ARG_PARTIAL_BYTES
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES sh_arg_partial_bytes
#define TARGET_ARG_PARTIAL_BYTES sh_arg_partial_bytes
 
 
#undef TARGET_BUILD_BUILTIN_VA_LIST
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list
#define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list
#undef TARGET_EXPAND_BUILTIN_VA_START
#undef TARGET_EXPAND_BUILTIN_VA_START
#define TARGET_EXPAND_BUILTIN_VA_START sh_va_start
#define TARGET_EXPAND_BUILTIN_VA_START sh_va_start
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR sh_gimplify_va_arg_expr
#define TARGET_GIMPLIFY_VA_ARG_EXPR sh_gimplify_va_arg_expr
 
 
#undef TARGET_SCALAR_MODE_SUPPORTED_P
#undef TARGET_SCALAR_MODE_SUPPORTED_P
#define TARGET_SCALAR_MODE_SUPPORTED_P sh_scalar_mode_supported_p
#define TARGET_SCALAR_MODE_SUPPORTED_P sh_scalar_mode_supported_p
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P sh_vector_mode_supported_p
#define TARGET_VECTOR_MODE_SUPPORTED_P sh_vector_mode_supported_p
 
 
#undef TARGET_CHECK_PCH_TARGET_FLAGS
#undef TARGET_CHECK_PCH_TARGET_FLAGS
#define TARGET_CHECK_PCH_TARGET_FLAGS sh_check_pch_target_flags
#define TARGET_CHECK_PCH_TARGET_FLAGS sh_check_pch_target_flags
 
 
#undef TARGET_DWARF_CALLING_CONVENTION
#undef TARGET_DWARF_CALLING_CONVENTION
#define TARGET_DWARF_CALLING_CONVENTION sh_dwarf_calling_convention
#define TARGET_DWARF_CALLING_CONVENTION sh_dwarf_calling_convention
 
 
/* Return regmode weight for insn.  */
/* Return regmode weight for insn.  */
#define INSN_REGMODE_WEIGHT(INSN, MODE)  regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)]
#define INSN_REGMODE_WEIGHT(INSN, MODE)  regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)]
 
 
/* Return current register pressure for regmode.  */
/* Return current register pressure for regmode.  */
#define CURR_REGMODE_PRESSURE(MODE)     curr_regmode_pressure[((MODE) == SImode) ? 0 : 1]
#define CURR_REGMODE_PRESSURE(MODE)     curr_regmode_pressure[((MODE) == SImode) ? 0 : 1]
 
 
#undef  TARGET_ENCODE_SECTION_INFO
#undef  TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO      sh_encode_section_info
#define TARGET_ENCODE_SECTION_INFO      sh_encode_section_info
 
 
#ifdef SYMBIAN
#ifdef SYMBIAN
 
 
#undef  TARGET_ENCODE_SECTION_INFO
#undef  TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO      sh_symbian_encode_section_info
#define TARGET_ENCODE_SECTION_INFO      sh_symbian_encode_section_info
#undef  TARGET_STRIP_NAME_ENCODING
#undef  TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING      sh_symbian_strip_name_encoding
#define TARGET_STRIP_NAME_ENCODING      sh_symbian_strip_name_encoding
#undef  TARGET_CXX_IMPORT_EXPORT_CLASS
#undef  TARGET_CXX_IMPORT_EXPORT_CLASS
#define TARGET_CXX_IMPORT_EXPORT_CLASS  sh_symbian_import_export_class
#define TARGET_CXX_IMPORT_EXPORT_CLASS  sh_symbian_import_export_class
 
 
#endif /* SYMBIAN */
#endif /* SYMBIAN */
 
 
#undef TARGET_SECONDARY_RELOAD
#undef TARGET_SECONDARY_RELOAD
#define TARGET_SECONDARY_RELOAD sh_secondary_reload
#define TARGET_SECONDARY_RELOAD sh_secondary_reload
 
 
#undef TARGET_LEGITIMATE_ADDRESS_P
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P     sh_legitimate_address_p
#define TARGET_LEGITIMATE_ADDRESS_P     sh_legitimate_address_p
 
 
#undef TARGET_TRAMPOLINE_INIT
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT          sh_trampoline_init
#define TARGET_TRAMPOLINE_INIT          sh_trampoline_init
#undef TARGET_TRAMPOLINE_ADJUST_ADDRESS
#undef TARGET_TRAMPOLINE_ADJUST_ADDRESS
#define TARGET_TRAMPOLINE_ADJUST_ADDRESS sh_trampoline_adjust_address
#define TARGET_TRAMPOLINE_ADJUST_ADDRESS sh_trampoline_adjust_address
 
 
/* Machine-specific symbol_ref flags.  */
/* Machine-specific symbol_ref flags.  */
#define SYMBOL_FLAG_FUNCVEC_FUNCTION    (SYMBOL_FLAG_MACH_DEP << 0)
#define SYMBOL_FLAG_FUNCVEC_FUNCTION    (SYMBOL_FLAG_MACH_DEP << 0)
 
 
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;


/* Implement TARGET_HANDLE_OPTION.  */
/* Implement TARGET_HANDLE_OPTION.  */
 
 
static bool
static bool
sh_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
sh_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
                  int value ATTRIBUTE_UNUSED)
                  int value ATTRIBUTE_UNUSED)
{
{
  switch (code)
  switch (code)
    {
    {
    case OPT_m1:
    case OPT_m1:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH1;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH1;
      return true;
      return true;
 
 
    case OPT_m2:
    case OPT_m2:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2;
      return true;
      return true;
 
 
    case OPT_m2a:
    case OPT_m2a:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A;
      return true;
      return true;
 
 
    case OPT_m2a_nofpu:
    case OPT_m2a_nofpu:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_NOFPU;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_NOFPU;
      return true;
      return true;
 
 
    case OPT_m2a_single:
    case OPT_m2a_single:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE;
      return true;
      return true;
 
 
    case OPT_m2a_single_only:
    case OPT_m2a_single_only:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE_ONLY;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE_ONLY;
      return true;
      return true;
 
 
    case OPT_m2e:
    case OPT_m2e:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2E;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2E;
      return true;
      return true;
 
 
    case OPT_m3:
    case OPT_m3:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3;
      return true;
      return true;
 
 
    case OPT_m3e:
    case OPT_m3e:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3E;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3E;
      return true;
      return true;
 
 
    case OPT_m4:
    case OPT_m4:
    case OPT_m4_100:
    case OPT_m4_100:
    case OPT_m4_200:
    case OPT_m4_200:
    case OPT_m4_300:
    case OPT_m4_300:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4;
      return true;
      return true;
 
 
    case OPT_m4_nofpu:
    case OPT_m4_nofpu:
    case OPT_m4_100_nofpu:
    case OPT_m4_100_nofpu:
    case OPT_m4_200_nofpu:
    case OPT_m4_200_nofpu:
    case OPT_m4_300_nofpu:
    case OPT_m4_300_nofpu:
    case OPT_m4_340:
    case OPT_m4_340:
    case OPT_m4_400:
    case OPT_m4_400:
    case OPT_m4_500:
    case OPT_m4_500:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_NOFPU;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_NOFPU;
      return true;
      return true;
 
 
    case OPT_m4_single:
    case OPT_m4_single:
    case OPT_m4_100_single:
    case OPT_m4_100_single:
    case OPT_m4_200_single:
    case OPT_m4_200_single:
    case OPT_m4_300_single:
    case OPT_m4_300_single:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE;
      return true;
      return true;
 
 
    case OPT_m4_single_only:
    case OPT_m4_single_only:
    case OPT_m4_100_single_only:
    case OPT_m4_100_single_only:
    case OPT_m4_200_single_only:
    case OPT_m4_200_single_only:
    case OPT_m4_300_single_only:
    case OPT_m4_300_single_only:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE_ONLY;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE_ONLY;
      return true;
      return true;
 
 
    case OPT_m4a:
    case OPT_m4a:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A;
      return true;
      return true;
 
 
    case OPT_m4a_nofpu:
    case OPT_m4a_nofpu:
    case OPT_m4al:
    case OPT_m4al:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_NOFPU;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_NOFPU;
      return true;
      return true;
 
 
    case OPT_m4a_single:
    case OPT_m4a_single:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE;
      return true;
      return true;
 
 
    case OPT_m4a_single_only:
    case OPT_m4a_single_only:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE_ONLY;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE_ONLY;
      return true;
      return true;
 
 
    case OPT_m5_32media:
    case OPT_m5_32media:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA;
      return true;
      return true;
 
 
    case OPT_m5_32media_nofpu:
    case OPT_m5_32media_nofpu:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA_NOFPU;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA_NOFPU;
      return true;
      return true;
 
 
    case OPT_m5_64media:
    case OPT_m5_64media:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA;
      return true;
      return true;
 
 
    case OPT_m5_64media_nofpu:
    case OPT_m5_64media_nofpu:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA_NOFPU;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA_NOFPU;
      return true;
      return true;
 
 
    case OPT_m5_compact:
    case OPT_m5_compact:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT;
      return true;
      return true;
 
 
    case OPT_m5_compact_nofpu:
    case OPT_m5_compact_nofpu:
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT_NOFPU;
      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT_NOFPU;
      return true;
      return true;
 
 
    default:
    default:
      return true;
      return true;
    }
    }
}
}


/* Set default optimization options.  */
/* Set default optimization options.  */
void
void
sh_optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
sh_optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
{
{
  if (level)
  if (level)
    {
    {
      flag_omit_frame_pointer = 2;
      flag_omit_frame_pointer = 2;
      if (!size)
      if (!size)
        sh_div_str = "inv:minlat";
        sh_div_str = "inv:minlat";
    }
    }
  if (size)
  if (size)
    {
    {
      target_flags |= MASK_SMALLCODE;
      target_flags |= MASK_SMALLCODE;
      sh_div_str = SH_DIV_STR_FOR_SIZE ;
      sh_div_str = SH_DIV_STR_FOR_SIZE ;
    }
    }
  else
  else
    TARGET_CBRANCHDI4 = 1;
    TARGET_CBRANCHDI4 = 1;
  /* We can't meaningfully test TARGET_SHMEDIA here, because -m options
  /* We can't meaningfully test TARGET_SHMEDIA here, because -m options
     haven't been parsed yet, hence we'd read only the default.
     haven't been parsed yet, hence we'd read only the default.
     sh_target_reg_class will return NO_REGS if this is not SHMEDIA, so
     sh_target_reg_class will return NO_REGS if this is not SHMEDIA, so
     it's OK to always set flag_branch_target_load_optimize.  */
     it's OK to always set flag_branch_target_load_optimize.  */
  if (level > 1)
  if (level > 1)
    {
    {
      flag_branch_target_load_optimize = 1;
      flag_branch_target_load_optimize = 1;
      if (!size)
      if (!size)
        target_flags |= MASK_SAVE_ALL_TARGET_REGS;
        target_flags |= MASK_SAVE_ALL_TARGET_REGS;
    }
    }
  /* Likewise, we can't meaningfully test TARGET_SH2E / TARGET_IEEE
  /* Likewise, we can't meaningfully test TARGET_SH2E / TARGET_IEEE
     here, so leave it to OVERRIDE_OPTIONS to set
     here, so leave it to OVERRIDE_OPTIONS to set
    flag_finite_math_only.  We set it to 2 here so we know if the user
    flag_finite_math_only.  We set it to 2 here so we know if the user
    explicitly requested this to be on or off.  */
    explicitly requested this to be on or off.  */
  flag_finite_math_only = 2;
  flag_finite_math_only = 2;
  /* If flag_schedule_insns is 1, we set it to 2 here so we know if
  /* If flag_schedule_insns is 1, we set it to 2 here so we know if
     the user explicitly requested this to be on or off.  */
     the user explicitly requested this to be on or off.  */
  if (flag_schedule_insns > 0)
  if (flag_schedule_insns > 0)
    flag_schedule_insns = 2;
    flag_schedule_insns = 2;
 
 
  set_param_value ("simultaneous-prefetches", 2);
  set_param_value ("simultaneous-prefetches", 2);
}
}
 
 
/* Implement OVERRIDE_OPTIONS macro.  Validate and override various
/* Implement OVERRIDE_OPTIONS macro.  Validate and override various
   options, and do some machine dependent initialization.  */
   options, and do some machine dependent initialization.  */
void
void
sh_override_options (void)
sh_override_options (void)
{
{
  int regno;
  int regno;
 
 
  SUBTARGET_OVERRIDE_OPTIONS;
  SUBTARGET_OVERRIDE_OPTIONS;
  if (flag_finite_math_only == 2)
  if (flag_finite_math_only == 2)
    flag_finite_math_only
    flag_finite_math_only
      = !flag_signaling_nans && TARGET_SH2E && ! TARGET_IEEE;
      = !flag_signaling_nans && TARGET_SH2E && ! TARGET_IEEE;
  if (TARGET_SH2E && !flag_finite_math_only)
  if (TARGET_SH2E && !flag_finite_math_only)
    target_flags |= MASK_IEEE;
    target_flags |= MASK_IEEE;
  sh_cpu = PROCESSOR_SH1;
  sh_cpu = PROCESSOR_SH1;
  assembler_dialect = 0;
  assembler_dialect = 0;
  if (TARGET_SH2)
  if (TARGET_SH2)
    sh_cpu = PROCESSOR_SH2;
    sh_cpu = PROCESSOR_SH2;
  if (TARGET_SH2E)
  if (TARGET_SH2E)
    sh_cpu = PROCESSOR_SH2E;
    sh_cpu = PROCESSOR_SH2E;
  if (TARGET_SH2A)
  if (TARGET_SH2A)
    sh_cpu = PROCESSOR_SH2A;
    sh_cpu = PROCESSOR_SH2A;
  if (TARGET_SH3)
  if (TARGET_SH3)
    sh_cpu = PROCESSOR_SH3;
    sh_cpu = PROCESSOR_SH3;
  if (TARGET_SH3E)
  if (TARGET_SH3E)
    sh_cpu = PROCESSOR_SH3E;
    sh_cpu = PROCESSOR_SH3E;
  if (TARGET_SH4)
  if (TARGET_SH4)
    {
    {
      assembler_dialect = 1;
      assembler_dialect = 1;
      sh_cpu = PROCESSOR_SH4;
      sh_cpu = PROCESSOR_SH4;
    }
    }
  if (TARGET_SH4A_ARCH)
  if (TARGET_SH4A_ARCH)
    {
    {
      assembler_dialect = 1;
      assembler_dialect = 1;
      sh_cpu = PROCESSOR_SH4A;
      sh_cpu = PROCESSOR_SH4A;
    }
    }
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      sh_cpu = PROCESSOR_SH5;
      sh_cpu = PROCESSOR_SH5;
      target_flags |= MASK_ALIGN_DOUBLE;
      target_flags |= MASK_ALIGN_DOUBLE;
      if (TARGET_SHMEDIA_FPU)
      if (TARGET_SHMEDIA_FPU)
        target_flags |= MASK_FMOVD;
        target_flags |= MASK_FMOVD;
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        {
        {
          /* There are no delay slots on SHmedia.  */
          /* There are no delay slots on SHmedia.  */
          flag_delayed_branch = 0;
          flag_delayed_branch = 0;
          /* Relaxation isn't yet supported for SHmedia */
          /* Relaxation isn't yet supported for SHmedia */
          target_flags &= ~MASK_RELAX;
          target_flags &= ~MASK_RELAX;
          /* After reload, if conversion does little good but can cause
          /* After reload, if conversion does little good but can cause
             ICEs:
             ICEs:
             - find_if_block doesn't do anything for SH because we don't
             - find_if_block doesn't do anything for SH because we don't
               have conditional execution patterns.  (We use conditional
               have conditional execution patterns.  (We use conditional
               move patterns, which are handled differently, and only
               move patterns, which are handled differently, and only
               before reload).
               before reload).
             - find_cond_trap doesn't do anything for the SH because we
             - find_cond_trap doesn't do anything for the SH because we
               don't have conditional traps.
               don't have conditional traps.
             - find_if_case_1 uses redirect_edge_and_branch_force in
             - find_if_case_1 uses redirect_edge_and_branch_force in
               the only path that does an optimization, and this causes
               the only path that does an optimization, and this causes
               an ICE when branch targets are in registers.
               an ICE when branch targets are in registers.
             - find_if_case_2 doesn't do anything for the SHmedia after
             - find_if_case_2 doesn't do anything for the SHmedia after
               reload except when it can redirect a tablejump - and
               reload except when it can redirect a tablejump - and
               that's rather rare.  */
               that's rather rare.  */
          flag_if_conversion2 = 0;
          flag_if_conversion2 = 0;
          if (! strcmp (sh_div_str, "call"))
          if (! strcmp (sh_div_str, "call"))
            sh_div_strategy = SH_DIV_CALL;
            sh_div_strategy = SH_DIV_CALL;
          else if (! strcmp (sh_div_str, "call2"))
          else if (! strcmp (sh_div_str, "call2"))
            sh_div_strategy = SH_DIV_CALL2;
            sh_div_strategy = SH_DIV_CALL2;
          if (! strcmp (sh_div_str, "fp") && TARGET_FPU_ANY)
          if (! strcmp (sh_div_str, "fp") && TARGET_FPU_ANY)
            sh_div_strategy = SH_DIV_FP;
            sh_div_strategy = SH_DIV_FP;
          else if (! strcmp (sh_div_str, "inv"))
          else if (! strcmp (sh_div_str, "inv"))
            sh_div_strategy = SH_DIV_INV;
            sh_div_strategy = SH_DIV_INV;
          else if (! strcmp (sh_div_str, "inv:minlat"))
          else if (! strcmp (sh_div_str, "inv:minlat"))
            sh_div_strategy = SH_DIV_INV_MINLAT;
            sh_div_strategy = SH_DIV_INV_MINLAT;
          else if (! strcmp (sh_div_str, "inv20u"))
          else if (! strcmp (sh_div_str, "inv20u"))
            sh_div_strategy = SH_DIV_INV20U;
            sh_div_strategy = SH_DIV_INV20U;
          else if (! strcmp (sh_div_str, "inv20l"))
          else if (! strcmp (sh_div_str, "inv20l"))
            sh_div_strategy = SH_DIV_INV20L;
            sh_div_strategy = SH_DIV_INV20L;
          else if (! strcmp (sh_div_str, "inv:call2"))
          else if (! strcmp (sh_div_str, "inv:call2"))
            sh_div_strategy = SH_DIV_INV_CALL2;
            sh_div_strategy = SH_DIV_INV_CALL2;
          else if (! strcmp (sh_div_str, "inv:call"))
          else if (! strcmp (sh_div_str, "inv:call"))
            sh_div_strategy = SH_DIV_INV_CALL;
            sh_div_strategy = SH_DIV_INV_CALL;
          else if (! strcmp (sh_div_str, "inv:fp"))
          else if (! strcmp (sh_div_str, "inv:fp"))
            {
            {
              if (TARGET_FPU_ANY)
              if (TARGET_FPU_ANY)
                sh_div_strategy = SH_DIV_INV_FP;
                sh_div_strategy = SH_DIV_INV_FP;
              else
              else
                sh_div_strategy = SH_DIV_INV;
                sh_div_strategy = SH_DIV_INV;
            }
            }
          TARGET_CBRANCHDI4 = 0;
          TARGET_CBRANCHDI4 = 0;
          /* Assembler CFI isn't yet fully supported for SHmedia.  */
          /* Assembler CFI isn't yet fully supported for SHmedia.  */
          flag_dwarf2_cfi_asm = 0;
          flag_dwarf2_cfi_asm = 0;
        }
        }
    }
    }
  else
  else
    {
    {
       /* Only the sh64-elf assembler fully supports .quad properly.  */
       /* Only the sh64-elf assembler fully supports .quad properly.  */
       targetm.asm_out.aligned_op.di = NULL;
       targetm.asm_out.aligned_op.di = NULL;
       targetm.asm_out.unaligned_op.di = NULL;
       targetm.asm_out.unaligned_op.di = NULL;
    }
    }
  if (TARGET_SH1)
  if (TARGET_SH1)
    {
    {
      if (! strcmp (sh_div_str, "call-div1"))
      if (! strcmp (sh_div_str, "call-div1"))
        sh_div_strategy = SH_DIV_CALL_DIV1;
        sh_div_strategy = SH_DIV_CALL_DIV1;
      else if (! strcmp (sh_div_str, "call-fp")
      else if (! strcmp (sh_div_str, "call-fp")
               && (TARGET_FPU_DOUBLE
               && (TARGET_FPU_DOUBLE
                   || (TARGET_HARD_SH4 && TARGET_SH2E)
                   || (TARGET_HARD_SH4 && TARGET_SH2E)
                   || (TARGET_SHCOMPACT && TARGET_FPU_ANY)))
                   || (TARGET_SHCOMPACT && TARGET_FPU_ANY)))
        sh_div_strategy = SH_DIV_CALL_FP;
        sh_div_strategy = SH_DIV_CALL_FP;
      else if (! strcmp (sh_div_str, "call-table") && TARGET_SH2)
      else if (! strcmp (sh_div_str, "call-table") && TARGET_SH2)
        sh_div_strategy = SH_DIV_CALL_TABLE;
        sh_div_strategy = SH_DIV_CALL_TABLE;
      else
      else
        /* Pick one that makes most sense for the target in general.
        /* Pick one that makes most sense for the target in general.
           It is not much good to use different functions depending
           It is not much good to use different functions depending
           on -Os, since then we'll end up with two different functions
           on -Os, since then we'll end up with two different functions
           when some of the code is compiled for size, and some for
           when some of the code is compiled for size, and some for
           speed.  */
           speed.  */
 
 
        /* SH4 tends to emphasize speed.  */
        /* SH4 tends to emphasize speed.  */
        if (TARGET_HARD_SH4)
        if (TARGET_HARD_SH4)
          sh_div_strategy = SH_DIV_CALL_TABLE;
          sh_div_strategy = SH_DIV_CALL_TABLE;
        /* These have their own way of doing things.  */
        /* These have their own way of doing things.  */
        else if (TARGET_SH2A)
        else if (TARGET_SH2A)
          sh_div_strategy = SH_DIV_INTRINSIC;
          sh_div_strategy = SH_DIV_INTRINSIC;
        /* ??? Should we use the integer SHmedia function instead?  */
        /* ??? Should we use the integer SHmedia function instead?  */
        else if (TARGET_SHCOMPACT && TARGET_FPU_ANY)
        else if (TARGET_SHCOMPACT && TARGET_FPU_ANY)
          sh_div_strategy = SH_DIV_CALL_FP;
          sh_div_strategy = SH_DIV_CALL_FP;
        /* SH1 .. SH3 cores often go into small-footprint systems, so
        /* SH1 .. SH3 cores often go into small-footprint systems, so
           default to the smallest implementation available.  */
           default to the smallest implementation available.  */
        else if (TARGET_SH2)    /* ??? EXPERIMENTAL */
        else if (TARGET_SH2)    /* ??? EXPERIMENTAL */
          sh_div_strategy = SH_DIV_CALL_TABLE;
          sh_div_strategy = SH_DIV_CALL_TABLE;
        else
        else
          sh_div_strategy = SH_DIV_CALL_DIV1;
          sh_div_strategy = SH_DIV_CALL_DIV1;
    }
    }
  if (!TARGET_SH1)
  if (!TARGET_SH1)
    TARGET_PRETEND_CMOVE = 0;
    TARGET_PRETEND_CMOVE = 0;
  if (sh_divsi3_libfunc[0])
  if (sh_divsi3_libfunc[0])
    ; /* User supplied - leave it alone.  */
    ; /* User supplied - leave it alone.  */
  else if (TARGET_DIVIDE_CALL_FP)
  else if (TARGET_DIVIDE_CALL_FP)
    sh_divsi3_libfunc = "__sdivsi3_i4";
    sh_divsi3_libfunc = "__sdivsi3_i4";
  else if (TARGET_DIVIDE_CALL_TABLE)
  else if (TARGET_DIVIDE_CALL_TABLE)
    sh_divsi3_libfunc = "__sdivsi3_i4i";
    sh_divsi3_libfunc = "__sdivsi3_i4i";
  else if (TARGET_SH5)
  else if (TARGET_SH5)
    sh_divsi3_libfunc = "__sdivsi3_1";
    sh_divsi3_libfunc = "__sdivsi3_1";
  else
  else
    sh_divsi3_libfunc = "__sdivsi3";
    sh_divsi3_libfunc = "__sdivsi3";
  if (sh_branch_cost == -1)
  if (sh_branch_cost == -1)
    sh_branch_cost
    sh_branch_cost
      = TARGET_SH5 ? 1 : ! TARGET_SH2 || TARGET_HARD_SH4 ? 2 : 1;
      = TARGET_SH5 ? 1 : ! TARGET_SH2 || TARGET_HARD_SH4 ? 2 : 1;
 
 
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
    if (! VALID_REGISTER_P (regno))
    if (! VALID_REGISTER_P (regno))
      sh_register_names[regno][0] = '\0';
      sh_register_names[regno][0] = '\0';
 
 
  for (regno = 0; regno < ADDREGNAMES_SIZE; regno++)
  for (regno = 0; regno < ADDREGNAMES_SIZE; regno++)
    if (! VALID_REGISTER_P (ADDREGNAMES_REGNO (regno)))
    if (! VALID_REGISTER_P (ADDREGNAMES_REGNO (regno)))
      sh_additional_register_names[regno][0] = '\0';
      sh_additional_register_names[regno][0] = '\0';
 
 
  if (flag_omit_frame_pointer == 2)
  if (flag_omit_frame_pointer == 2)
   {
   {
     /* The debugging information is sufficient,
     /* The debugging information is sufficient,
        but gdb doesn't implement this yet */
        but gdb doesn't implement this yet */
     if (0)
     if (0)
      flag_omit_frame_pointer
      flag_omit_frame_pointer
        = (PREFERRED_DEBUGGING_TYPE == DWARF2_DEBUG);
        = (PREFERRED_DEBUGGING_TYPE == DWARF2_DEBUG);
     else
     else
      flag_omit_frame_pointer = 0;
      flag_omit_frame_pointer = 0;
   }
   }
 
 
  if ((flag_pic && ! TARGET_PREFERGOT)
  if ((flag_pic && ! TARGET_PREFERGOT)
      || (TARGET_SHMEDIA && !TARGET_PT_FIXED))
      || (TARGET_SHMEDIA && !TARGET_PT_FIXED))
    flag_no_function_cse = 1;
    flag_no_function_cse = 1;
 
 
  if (SMALL_REGISTER_CLASSES)
  if (SMALL_REGISTER_CLASSES)
    {
    {
      /* Never run scheduling before reload, since that can
      /* Never run scheduling before reload, since that can
         break global alloc, and generates slower code anyway due
         break global alloc, and generates slower code anyway due
         to the pressure on R0.  */
         to the pressure on R0.  */
      /* Enable sched1 for SH4 if the user explicitly requests.
      /* Enable sched1 for SH4 if the user explicitly requests.
         When sched1 is enabled, the ready queue will be reordered by
         When sched1 is enabled, the ready queue will be reordered by
         the target hooks if pressure is high.  We can not do this for
         the target hooks if pressure is high.  We can not do this for
         PIC, SH3 and lower as they give spill failures for R0.  */
         PIC, SH3 and lower as they give spill failures for R0.  */
      if (!TARGET_HARD_SH4 || flag_pic)
      if (!TARGET_HARD_SH4 || flag_pic)
        flag_schedule_insns = 0;
        flag_schedule_insns = 0;
      /* ??? Current exception handling places basic block boundaries
      /* ??? Current exception handling places basic block boundaries
         after call_insns.  It causes the high pressure on R0 and gives
         after call_insns.  It causes the high pressure on R0 and gives
         spill failures for R0 in reload.  See PR 22553 and the thread
         spill failures for R0 in reload.  See PR 22553 and the thread
         on gcc-patches
         on gcc-patches
         <http://gcc.gnu.org/ml/gcc-patches/2005-10/msg00816.html>.  */
         <http://gcc.gnu.org/ml/gcc-patches/2005-10/msg00816.html>.  */
      else if (flag_exceptions)
      else if (flag_exceptions)
        {
        {
          if (flag_schedule_insns == 1)
          if (flag_schedule_insns == 1)
            warning (0, "ignoring -fschedule-insns because of exception handling bug");
            warning (0, "ignoring -fschedule-insns because of exception handling bug");
          flag_schedule_insns = 0;
          flag_schedule_insns = 0;
        }
        }
      else if (flag_schedule_insns == 2)
      else if (flag_schedule_insns == 2)
        flag_schedule_insns = 0;
        flag_schedule_insns = 0;
    }
    }
 
 
  /* Unwinding with -freorder-blocks-and-partition does not work on this
  /* Unwinding with -freorder-blocks-and-partition does not work on this
     architecture, because it requires far jumps to label crossing between
     architecture, because it requires far jumps to label crossing between
     hot/cold sections which are rejected on this architecture.  */
     hot/cold sections which are rejected on this architecture.  */
  if (flag_reorder_blocks_and_partition)
  if (flag_reorder_blocks_and_partition)
    {
    {
      if (flag_exceptions)
      if (flag_exceptions)
        {
        {
          inform (input_location,
          inform (input_location,
                  "-freorder-blocks-and-partition does not work with "
                  "-freorder-blocks-and-partition does not work with "
                  "exceptions on this architecture");
                  "exceptions on this architecture");
          flag_reorder_blocks_and_partition = 0;
          flag_reorder_blocks_and_partition = 0;
          flag_reorder_blocks = 1;
          flag_reorder_blocks = 1;
        }
        }
      else if (flag_unwind_tables)
      else if (flag_unwind_tables)
        {
        {
          inform (input_location,
          inform (input_location,
                  "-freorder-blocks-and-partition does not support unwind "
                  "-freorder-blocks-and-partition does not support unwind "
                  "info on this architecture");
                  "info on this architecture");
          flag_reorder_blocks_and_partition = 0;
          flag_reorder_blocks_and_partition = 0;
          flag_reorder_blocks = 1;
          flag_reorder_blocks = 1;
        }
        }
    }
    }
 
 
  if (align_loops == 0)
  if (align_loops == 0)
    align_loops =  1 << (TARGET_SH5 ? 3 : 2);
    align_loops =  1 << (TARGET_SH5 ? 3 : 2);
  if (align_jumps == 0)
  if (align_jumps == 0)
    align_jumps = 1 << CACHE_LOG;
    align_jumps = 1 << CACHE_LOG;
  else if (align_jumps < (TARGET_SHMEDIA ? 4 : 2))
  else if (align_jumps < (TARGET_SHMEDIA ? 4 : 2))
    align_jumps = TARGET_SHMEDIA ? 4 : 2;
    align_jumps = TARGET_SHMEDIA ? 4 : 2;
 
 
  /* Allocation boundary (in *bytes*) for the code of a function.
  /* Allocation boundary (in *bytes*) for the code of a function.
     SH1: 32 bit alignment is faster, because instructions are always
     SH1: 32 bit alignment is faster, because instructions are always
     fetched as a pair from a longword boundary.
     fetched as a pair from a longword boundary.
     SH2 .. SH5 : align to cache line start.  */
     SH2 .. SH5 : align to cache line start.  */
  if (align_functions == 0)
  if (align_functions == 0)
    align_functions
    align_functions
      = TARGET_SMALLCODE ? FUNCTION_BOUNDARY/8 : (1 << CACHE_LOG);
      = TARGET_SMALLCODE ? FUNCTION_BOUNDARY/8 : (1 << CACHE_LOG);
  /* The linker relaxation code breaks when a function contains
  /* The linker relaxation code breaks when a function contains
     alignments that are larger than that at the start of a
     alignments that are larger than that at the start of a
     compilation unit.  */
     compilation unit.  */
  if (TARGET_RELAX)
  if (TARGET_RELAX)
    {
    {
      int min_align
      int min_align
        = align_loops > align_jumps ? align_loops : align_jumps;
        = align_loops > align_jumps ? align_loops : align_jumps;
 
 
      /* Also take possible .long constants / mova tables int account.  */
      /* Also take possible .long constants / mova tables int account.  */
      if (min_align < 4)
      if (min_align < 4)
        min_align = 4;
        min_align = 4;
      if (align_functions < min_align)
      if (align_functions < min_align)
        align_functions = min_align;
        align_functions = min_align;
    }
    }
 
 
  if (sh_fixed_range_str)
  if (sh_fixed_range_str)
    sh_fix_range (sh_fixed_range_str);
    sh_fix_range (sh_fixed_range_str);
}
}


/* Print the operand address in x to the stream.  */
/* Print the operand address in x to the stream.  */
 
 
void
void
print_operand_address (FILE *stream, rtx x)
print_operand_address (FILE *stream, rtx x)
{
{
  switch (GET_CODE (x))
  switch (GET_CODE (x))
    {
    {
    case REG:
    case REG:
    case SUBREG:
    case SUBREG:
      fprintf (stream, "@%s", reg_names[true_regnum (x)]);
      fprintf (stream, "@%s", reg_names[true_regnum (x)]);
      break;
      break;
 
 
    case PLUS:
    case PLUS:
      {
      {
        rtx base = XEXP (x, 0);
        rtx base = XEXP (x, 0);
        rtx index = XEXP (x, 1);
        rtx index = XEXP (x, 1);
 
 
        switch (GET_CODE (index))
        switch (GET_CODE (index))
          {
          {
          case CONST_INT:
          case CONST_INT:
            fprintf (stream, "@(%d,%s)", (int) INTVAL (index),
            fprintf (stream, "@(%d,%s)", (int) INTVAL (index),
                     reg_names[true_regnum (base)]);
                     reg_names[true_regnum (base)]);
            break;
            break;
 
 
          case REG:
          case REG:
          case SUBREG:
          case SUBREG:
            {
            {
              int base_num = true_regnum (base);
              int base_num = true_regnum (base);
              int index_num = true_regnum (index);
              int index_num = true_regnum (index);
 
 
              fprintf (stream, "@(r0,%s)",
              fprintf (stream, "@(r0,%s)",
                       reg_names[MAX (base_num, index_num)]);
                       reg_names[MAX (base_num, index_num)]);
              break;
              break;
            }
            }
 
 
          default:
          default:
            gcc_unreachable ();
            gcc_unreachable ();
          }
          }
      }
      }
      break;
      break;
 
 
    case PRE_DEC:
    case PRE_DEC:
      fprintf (stream, "@-%s", reg_names[true_regnum (XEXP (x, 0))]);
      fprintf (stream, "@-%s", reg_names[true_regnum (XEXP (x, 0))]);
      break;
      break;
 
 
    case POST_INC:
    case POST_INC:
      fprintf (stream, "@%s+", reg_names[true_regnum (XEXP (x, 0))]);
      fprintf (stream, "@%s+", reg_names[true_regnum (XEXP (x, 0))]);
      break;
      break;
 
 
    default:
    default:
      x = mark_constant_pool_use (x);
      x = mark_constant_pool_use (x);
      output_addr_const (stream, x);
      output_addr_const (stream, x);
      break;
      break;
    }
    }
}
}
 
 
/* Print operand x (an rtx) in assembler syntax to file stream
/* Print operand x (an rtx) in assembler syntax to file stream
   according to modifier code.
   according to modifier code.
 
 
   '.'  print a .s if insn needs delay slot
   '.'  print a .s if insn needs delay slot
   ','  print LOCAL_LABEL_PREFIX
   ','  print LOCAL_LABEL_PREFIX
   '@'  print trap, rte or rts depending upon pragma interruptness
   '@'  print trap, rte or rts depending upon pragma interruptness
   '#'  output a nop if there is nothing to put in the delay slot
   '#'  output a nop if there is nothing to put in the delay slot
   '''  print likelihood suffix (/u for unlikely).
   '''  print likelihood suffix (/u for unlikely).
   '>'  print branch target if -fverbose-asm
   '>'  print branch target if -fverbose-asm
   'O'  print a constant without the #
   'O'  print a constant without the #
   'R'  print the LSW of a dp value - changes if in little endian
   '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
   'S'  print the MSW of a dp value - changes if in little endian
   'T'  print the next word of a dp value - same as 'R' in big endian mode.
   'T'  print the next word of a dp value - same as 'R' in big endian mode.
   'M'  SHMEDIA: print an `x' if `m' will print `base,index'.
   'M'  SHMEDIA: print an `x' if `m' will print `base,index'.
        otherwise: print .b / .w / .l / .s / .d suffix if operand is a MEM.
        otherwise: print .b / .w / .l / .s / .d suffix if operand is a MEM.
   'N'  print 'r63' if the operand is (const_int 0).
   'N'  print 'r63' if the operand is (const_int 0).
   'd'  print a V2SF reg as dN instead of fpN.
   'd'  print a V2SF reg as dN instead of fpN.
   'm'  print a pair `base,offset' or `base,index', for LD and ST.
   'm'  print a pair `base,offset' or `base,index', for LD and ST.
   'U'  Likewise for {LD,ST}{HI,LO}.
   'U'  Likewise for {LD,ST}{HI,LO}.
   'V'  print the position of a single bit set.
   'V'  print the position of a single bit set.
   'W'  print the position of a single bit cleared.
   'W'  print the position of a single bit cleared.
   't'  print a memory address which is a register.
   't'  print a memory address which is a register.
   'u'  prints the lowest 16 bits of CONST_INT, as an unsigned value.
   'u'  prints the lowest 16 bits of CONST_INT, as an unsigned value.
   'o'  output an operator.  */
   'o'  output an operator.  */
 
 
void
void
print_operand (FILE *stream, rtx x, int code)
print_operand (FILE *stream, rtx x, int code)
{
{
  int regno;
  int regno;
  enum machine_mode mode;
  enum machine_mode mode;
 
 
  switch (code)
  switch (code)
    {
    {
      tree trapa_attr;
      tree trapa_attr;
 
 
    case '.':
    case '.':
      if (final_sequence
      if (final_sequence
          && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
          && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
          && get_attr_length (XVECEXP (final_sequence, 0, 1)))
          && get_attr_length (XVECEXP (final_sequence, 0, 1)))
        fprintf (stream, ASSEMBLER_DIALECT ? "/s" : ".s");
        fprintf (stream, ASSEMBLER_DIALECT ? "/s" : ".s");
      break;
      break;
    case ',':
    case ',':
      fprintf (stream, "%s", LOCAL_LABEL_PREFIX);
      fprintf (stream, "%s", LOCAL_LABEL_PREFIX);
      break;
      break;
    case '@':
    case '@':
      trapa_attr = lookup_attribute ("trap_exit",
      trapa_attr = lookup_attribute ("trap_exit",
                                      DECL_ATTRIBUTES (current_function_decl));
                                      DECL_ATTRIBUTES (current_function_decl));
      if (trapa_attr)
      if (trapa_attr)
        fprintf (stream, "trapa #%ld",
        fprintf (stream, "trapa #%ld",
                 (long) TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (trapa_attr))));
                 (long) TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (trapa_attr))));
      else if (sh_cfun_interrupt_handler_p ())
      else if (sh_cfun_interrupt_handler_p ())
        {
        {
          if (sh_cfun_resbank_handler_p ())
          if (sh_cfun_resbank_handler_p ())
            fprintf (stream, "resbank\n");
            fprintf (stream, "resbank\n");
          fprintf (stream, "rte");
          fprintf (stream, "rte");
        }
        }
      else
      else
        fprintf (stream, "rts");
        fprintf (stream, "rts");
      break;
      break;
    case '#':
    case '#':
      /* Output a nop if there's nothing in the delay slot.  */
      /* Output a nop if there's nothing in the delay slot.  */
      if (dbr_sequence_length () == 0)
      if (dbr_sequence_length () == 0)
        fprintf (stream, "\n\tnop");
        fprintf (stream, "\n\tnop");
      break;
      break;
    case '\'':
    case '\'':
      {
      {
        rtx note = find_reg_note (current_output_insn, REG_BR_PROB, 0);
        rtx note = find_reg_note (current_output_insn, REG_BR_PROB, 0);
 
 
        if (note && INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
        if (note && INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
          fputs ("/u", stream);
          fputs ("/u", stream);
        break;
        break;
      }
      }
    case '>':
    case '>':
      if (flag_verbose_asm && JUMP_LABEL (current_output_insn))
      if (flag_verbose_asm && JUMP_LABEL (current_output_insn))
        {
        {
          fputs ("\t! target: ", stream);
          fputs ("\t! target: ", stream);
          output_addr_const (stream, JUMP_LABEL (current_output_insn));
          output_addr_const (stream, JUMP_LABEL (current_output_insn));
        }
        }
      break;
      break;
    case 'O':
    case 'O':
      x = mark_constant_pool_use (x);
      x = mark_constant_pool_use (x);
      output_addr_const (stream, x);
      output_addr_const (stream, x);
      break;
      break;
    /* N.B.: %R / %S / %T adjust memory addresses by four.
    /* N.B.: %R / %S / %T adjust memory addresses by four.
       For SHMEDIA, that means they can be used to access the first and
       For SHMEDIA, that means they can be used to access the first and
       second 32 bit part of a 64 bit (or larger) value that
       second 32 bit part of a 64 bit (or larger) value that
       might be held in floating point registers or memory.
       might be held in floating point registers or memory.
       While they can be used to access 64 bit parts of a larger value
       While they can be used to access 64 bit parts of a larger value
       held in general purpose registers, that won't work with memory -
       held in general purpose registers, that won't work with memory -
       neither for fp registers, since the frxx names are used.  */
       neither for fp registers, since the frxx names are used.  */
    case 'R':
    case 'R':
      if (REG_P (x) || GET_CODE (x) == SUBREG)
      if (REG_P (x) || GET_CODE (x) == SUBREG)
        {
        {
          regno = true_regnum (x);
          regno = true_regnum (x);
          regno += FP_REGISTER_P (regno) ? 1 : LSW;
          regno += FP_REGISTER_P (regno) ? 1 : LSW;
          fputs (reg_names[regno], (stream));
          fputs (reg_names[regno], (stream));
        }
        }
      else if (MEM_P (x))
      else if (MEM_P (x))
        {
        {
          x = adjust_address (x, SImode, 4 * LSW);
          x = adjust_address (x, SImode, 4 * LSW);
          print_operand_address (stream, XEXP (x, 0));
          print_operand_address (stream, XEXP (x, 0));
        }
        }
      else
      else
        {
        {
          rtx sub = NULL_RTX;
          rtx sub = NULL_RTX;
 
 
          mode = GET_MODE (x);
          mode = GET_MODE (x);
          if (mode == VOIDmode)
          if (mode == VOIDmode)
            mode = DImode;
            mode = DImode;
          if (GET_MODE_SIZE (mode) >= 8)
          if (GET_MODE_SIZE (mode) >= 8)
            sub = simplify_subreg (SImode, x, mode, 4 * LSW);
            sub = simplify_subreg (SImode, x, mode, 4 * LSW);
          if (sub)
          if (sub)
            print_operand (stream, sub, 0);
            print_operand (stream, sub, 0);
          else
          else
            output_operand_lossage ("invalid operand to %%R");
            output_operand_lossage ("invalid operand to %%R");
        }
        }
      break;
      break;
    case 'S':
    case 'S':
      if (REG_P (x) || GET_CODE (x) == SUBREG)
      if (REG_P (x) || GET_CODE (x) == SUBREG)
        {
        {
          regno = true_regnum (x);
          regno = true_regnum (x);
          regno += FP_REGISTER_P (regno) ? 0 : MSW;
          regno += FP_REGISTER_P (regno) ? 0 : MSW;
          fputs (reg_names[regno], (stream));
          fputs (reg_names[regno], (stream));
        }
        }
      else if (MEM_P (x))
      else if (MEM_P (x))
        {
        {
          x = adjust_address (x, SImode, 4 * MSW);
          x = adjust_address (x, SImode, 4 * MSW);
          print_operand_address (stream, XEXP (x, 0));
          print_operand_address (stream, XEXP (x, 0));
        }
        }
      else
      else
        {
        {
          rtx sub = NULL_RTX;
          rtx sub = NULL_RTX;
 
 
          mode = GET_MODE (x);
          mode = GET_MODE (x);
          if (mode == VOIDmode)
          if (mode == VOIDmode)
            mode = DImode;
            mode = DImode;
          if (GET_MODE_SIZE (mode) >= 8)
          if (GET_MODE_SIZE (mode) >= 8)
            sub = simplify_subreg (SImode, x, mode, 4 * MSW);
            sub = simplify_subreg (SImode, x, mode, 4 * MSW);
          if (sub)
          if (sub)
            print_operand (stream, sub, 0);
            print_operand (stream, sub, 0);
          else
          else
            output_operand_lossage ("invalid operand to %%S");
            output_operand_lossage ("invalid operand to %%S");
        }
        }
      break;
      break;
    case 'T':
    case 'T':
      /* Next word of a double.  */
      /* Next word of a double.  */
      switch (GET_CODE (x))
      switch (GET_CODE (x))
        {
        {
        case REG:
        case REG:
          fputs (reg_names[REGNO (x) + 1], (stream));
          fputs (reg_names[REGNO (x) + 1], (stream));
          break;
          break;
        case MEM:
        case MEM:
          if (GET_CODE (XEXP (x, 0)) != PRE_DEC
          if (GET_CODE (XEXP (x, 0)) != PRE_DEC
              && GET_CODE (XEXP (x, 0)) != POST_INC)
              && GET_CODE (XEXP (x, 0)) != POST_INC)
            x = adjust_address (x, SImode, 4);
            x = adjust_address (x, SImode, 4);
          print_operand_address (stream, XEXP (x, 0));
          print_operand_address (stream, XEXP (x, 0));
          break;
          break;
        default:
        default:
          break;
          break;
        }
        }
      break;
      break;
 
 
    case 't':
    case 't':
      gcc_assert (MEM_P (x));
      gcc_assert (MEM_P (x));
      x = XEXP (x, 0);
      x = XEXP (x, 0);
      switch (GET_CODE (x))
      switch (GET_CODE (x))
        {
        {
        case REG:
        case REG:
        case SUBREG:
        case SUBREG:
          print_operand (stream, x, 0);
          print_operand (stream, x, 0);
          break;
          break;
        default:
        default:
          break;
          break;
        }
        }
      break;
      break;
 
 
    case 'o':
    case 'o':
      switch (GET_CODE (x))
      switch (GET_CODE (x))
        {
        {
        case PLUS:  fputs ("add", stream); break;
        case PLUS:  fputs ("add", stream); break;
        case MINUS: fputs ("sub", stream); break;
        case MINUS: fputs ("sub", stream); break;
        case MULT:  fputs ("mul", stream); break;
        case MULT:  fputs ("mul", stream); break;
        case DIV:   fputs ("div", stream); break;
        case DIV:   fputs ("div", stream); break;
        case EQ:    fputs ("eq",  stream); break;
        case EQ:    fputs ("eq",  stream); break;
        case NE:    fputs ("ne",  stream); break;
        case NE:    fputs ("ne",  stream); break;
        case GT:  case LT:  fputs ("gt",  stream); break;
        case GT:  case LT:  fputs ("gt",  stream); break;
        case GE:  case LE:  fputs ("ge",  stream); break;
        case GE:  case LE:  fputs ("ge",  stream); break;
        case GTU: case LTU: fputs ("gtu", stream); break;
        case GTU: case LTU: fputs ("gtu", stream); break;
        case GEU: case LEU: fputs ("geu", stream); break;
        case GEU: case LEU: fputs ("geu", stream); break;
        default:
        default:
          break;
          break;
        }
        }
      break;
      break;
    case 'M':
    case 'M':
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        {
        {
          if (MEM_P (x)
          if (MEM_P (x)
              && GET_CODE (XEXP (x, 0)) == PLUS
              && GET_CODE (XEXP (x, 0)) == PLUS
              && (REG_P (XEXP (XEXP (x, 0), 1))
              && (REG_P (XEXP (XEXP (x, 0), 1))
                  || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG))
                  || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG))
            fputc ('x', stream);
            fputc ('x', stream);
        }
        }
      else
      else
        {
        {
          if (MEM_P (x))
          if (MEM_P (x))
            {
            {
              switch (GET_MODE (x))
              switch (GET_MODE (x))
                {
                {
                case QImode: fputs (".b", stream); break;
                case QImode: fputs (".b", stream); break;
                case HImode: fputs (".w", stream); break;
                case HImode: fputs (".w", stream); break;
                case SImode: fputs (".l", stream); break;
                case SImode: fputs (".l", stream); break;
                case SFmode: fputs (".s", stream); break;
                case SFmode: fputs (".s", stream); break;
                case DFmode: fputs (".d", stream); break;
                case DFmode: fputs (".d", stream); break;
                default: gcc_unreachable ();
                default: gcc_unreachable ();
                }
                }
            }
            }
        }
        }
      break;
      break;
 
 
    case 'm':
    case 'm':
      gcc_assert (MEM_P (x));
      gcc_assert (MEM_P (x));
      x = XEXP (x, 0);
      x = XEXP (x, 0);
      /* Fall through.  */
      /* Fall through.  */
    case 'U':
    case 'U':
      switch (GET_CODE (x))
      switch (GET_CODE (x))
        {
        {
        case REG:
        case REG:
        case SUBREG:
        case SUBREG:
          print_operand (stream, x, 0);
          print_operand (stream, x, 0);
          fputs (", 0", stream);
          fputs (", 0", stream);
          break;
          break;
 
 
        case PLUS:
        case PLUS:
          print_operand (stream, XEXP (x, 0), 0);
          print_operand (stream, XEXP (x, 0), 0);
          fputs (", ", stream);
          fputs (", ", stream);
          print_operand (stream, XEXP (x, 1), 0);
          print_operand (stream, XEXP (x, 1), 0);
          break;
          break;
 
 
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
      break;
      break;
 
 
    case 'V':
    case 'V':
      {
      {
        int num = exact_log2 (INTVAL (x));
        int num = exact_log2 (INTVAL (x));
        gcc_assert (num >= 0);
        gcc_assert (num >= 0);
        fprintf (stream, "#%d", num);
        fprintf (stream, "#%d", num);
      }
      }
      break;
      break;
 
 
    case 'W':
    case 'W':
      {
      {
        int num = exact_log2 (~INTVAL (x));
        int num = exact_log2 (~INTVAL (x));
        gcc_assert (num >= 0);
        gcc_assert (num >= 0);
        fprintf (stream, "#%d", num);
        fprintf (stream, "#%d", num);
      }
      }
      break;
      break;
 
 
    case 'd':
    case 'd':
      gcc_assert (REG_P (x) && GET_MODE (x) == V2SFmode);
      gcc_assert (REG_P (x) && GET_MODE (x) == V2SFmode);
 
 
      fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
      fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
      break;
      break;
 
 
    case 'N':
    case 'N':
      if (x == CONST0_RTX (GET_MODE (x)))
      if (x == CONST0_RTX (GET_MODE (x)))
        {
        {
          fprintf ((stream), "r63");
          fprintf ((stream), "r63");
          break;
          break;
        }
        }
      goto default_output;
      goto default_output;
    case 'u':
    case 'u':
      if (CONST_INT_P (x))
      if (CONST_INT_P (x))
        {
        {
          fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1));
          fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1));
          break;
          break;
        }
        }
      /* Fall through.  */
      /* Fall through.  */
 
 
    default_output:
    default_output:
    default:
    default:
      regno = 0;
      regno = 0;
      mode = GET_MODE (x);
      mode = GET_MODE (x);
 
 
      switch (GET_CODE (x))
      switch (GET_CODE (x))
        {
        {
        case TRUNCATE:
        case TRUNCATE:
          {
          {
            rtx inner = XEXP (x, 0);
            rtx inner = XEXP (x, 0);
            int offset = 0;
            int offset = 0;
            enum machine_mode inner_mode;
            enum machine_mode inner_mode;
 
 
            /* We might see SUBREGs with vector mode registers inside.  */
            /* We might see SUBREGs with vector mode registers inside.  */
            if (GET_CODE (inner) == SUBREG
            if (GET_CODE (inner) == SUBREG
                && (GET_MODE_SIZE (GET_MODE (inner))
                && (GET_MODE_SIZE (GET_MODE (inner))
                    == GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
                    == GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
                && subreg_lowpart_p (inner))
                && subreg_lowpart_p (inner))
              inner = SUBREG_REG (inner);
              inner = SUBREG_REG (inner);
            if (CONST_INT_P (inner))
            if (CONST_INT_P (inner))
              {
              {
                x = GEN_INT (trunc_int_for_mode (INTVAL (inner), GET_MODE (x)));
                x = GEN_INT (trunc_int_for_mode (INTVAL (inner), GET_MODE (x)));
                goto default_output;
                goto default_output;
              }
              }
            inner_mode = GET_MODE (inner);
            inner_mode = GET_MODE (inner);
            if (GET_CODE (inner) == SUBREG
            if (GET_CODE (inner) == SUBREG
                && (GET_MODE_SIZE (GET_MODE (inner))
                && (GET_MODE_SIZE (GET_MODE (inner))
                    < GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
                    < GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
                && REG_P (SUBREG_REG (inner)))
                && REG_P (SUBREG_REG (inner)))
              {
              {
                offset = subreg_regno_offset (REGNO (SUBREG_REG (inner)),
                offset = subreg_regno_offset (REGNO (SUBREG_REG (inner)),
                                              GET_MODE (SUBREG_REG (inner)),
                                              GET_MODE (SUBREG_REG (inner)),
                                              SUBREG_BYTE (inner),
                                              SUBREG_BYTE (inner),
                                              GET_MODE (inner));
                                              GET_MODE (inner));
                inner = SUBREG_REG (inner);
                inner = SUBREG_REG (inner);
              }
              }
            if (!REG_P (inner) || GET_MODE_SIZE (inner_mode) > 8)
            if (!REG_P (inner) || GET_MODE_SIZE (inner_mode) > 8)
              abort ();
              abort ();
            /* Floating point register pairs are always big endian;
            /* Floating point register pairs are always big endian;
               general purpose registers are 64 bit wide.  */
               general purpose registers are 64 bit wide.  */
            regno = REGNO (inner);
            regno = REGNO (inner);
            regno = (HARD_REGNO_NREGS (regno, inner_mode)
            regno = (HARD_REGNO_NREGS (regno, inner_mode)
                     - HARD_REGNO_NREGS (regno, mode))
                     - HARD_REGNO_NREGS (regno, mode))
                     + offset;
                     + offset;
            x = inner;
            x = inner;
            goto reg;
            goto reg;
          }
          }
        case SIGN_EXTEND:
        case SIGN_EXTEND:
          x = XEXP (x, 0);
          x = XEXP (x, 0);
          goto reg;
          goto reg;
          /* FIXME: We need this on SHmedia32 because reload generates
          /* FIXME: We need this on SHmedia32 because reload generates
             some sign-extended HI or QI loads into DImode registers
             some sign-extended HI or QI loads into DImode registers
             but, because Pmode is SImode, the address ends up with a
             but, because Pmode is SImode, the address ends up with a
             subreg:SI of the DImode register.  Maybe reload should be
             subreg:SI of the DImode register.  Maybe reload should be
             fixed so as to apply alter_subreg to such loads?  */
             fixed so as to apply alter_subreg to such loads?  */
        case IF_THEN_ELSE:
        case IF_THEN_ELSE:
          gcc_assert (trapping_target_operand (x, VOIDmode));
          gcc_assert (trapping_target_operand (x, VOIDmode));
          x = XEXP (XEXP (x, 2), 0);
          x = XEXP (XEXP (x, 2), 0);
          goto default_output;
          goto default_output;
        case SUBREG:
        case SUBREG:
          gcc_assert (SUBREG_BYTE (x) == 0
          gcc_assert (SUBREG_BYTE (x) == 0
                      && REG_P (SUBREG_REG (x)));
                      && REG_P (SUBREG_REG (x)));
 
 
          x = SUBREG_REG (x);
          x = SUBREG_REG (x);
          /* Fall through.  */
          /* Fall through.  */
 
 
        reg:
        reg:
        case REG:
        case REG:
          regno += REGNO (x);
          regno += REGNO (x);
          if (FP_REGISTER_P (regno)
          if (FP_REGISTER_P (regno)
              && mode == V16SFmode)
              && mode == V16SFmode)
            fprintf ((stream), "mtrx%s", reg_names[regno] + 2);
            fprintf ((stream), "mtrx%s", reg_names[regno] + 2);
          else if (FP_REGISTER_P (REGNO (x))
          else if (FP_REGISTER_P (REGNO (x))
                   && mode == V4SFmode)
                   && mode == V4SFmode)
            fprintf ((stream), "fv%s", reg_names[regno] + 2);
            fprintf ((stream), "fv%s", reg_names[regno] + 2);
          else if (REG_P (x)
          else if (REG_P (x)
                   && mode == V2SFmode)
                   && mode == V2SFmode)
            fprintf ((stream), "fp%s", reg_names[regno] + 2);
            fprintf ((stream), "fp%s", reg_names[regno] + 2);
          else if (FP_REGISTER_P (REGNO (x))
          else if (FP_REGISTER_P (REGNO (x))
                   && GET_MODE_SIZE (mode) > 4)
                   && GET_MODE_SIZE (mode) > 4)
            fprintf ((stream), "d%s", reg_names[regno] + 1);
            fprintf ((stream), "d%s", reg_names[regno] + 1);
          else
          else
            fputs (reg_names[regno], (stream));
            fputs (reg_names[regno], (stream));
          break;
          break;
 
 
        case MEM:
        case MEM:
          output_address (XEXP (x, 0));
          output_address (XEXP (x, 0));
          break;
          break;
 
 
        default:
        default:
          if (TARGET_SH1)
          if (TARGET_SH1)
            fputc ('#', stream);
            fputc ('#', stream);
          output_addr_const (stream, x);
          output_addr_const (stream, x);
          break;
          break;
        }
        }
      break;
      break;
    }
    }
}
}


 
 
/* Encode symbol attributes of a SYMBOL_REF into its
/* Encode symbol attributes of a SYMBOL_REF into its
   SYMBOL_REF_FLAGS.  */
   SYMBOL_REF_FLAGS.  */
static void
static void
sh_encode_section_info (tree decl, rtx rtl, int first)
sh_encode_section_info (tree decl, rtx rtl, int first)
{
{
  default_encode_section_info (decl, rtl, first);
  default_encode_section_info (decl, rtl, first);
 
 
  if (TREE_CODE (decl) == FUNCTION_DECL
  if (TREE_CODE (decl) == FUNCTION_DECL
      && sh2a_function_vector_p (decl) && TARGET_SH2A)
      && sh2a_function_vector_p (decl) && TARGET_SH2A)
    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_FUNCVEC_FUNCTION;
    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_FUNCVEC_FUNCTION;
}
}
 
 
/* Like force_operand, but guarantees that VALUE ends up in TARGET.  */
/* Like force_operand, but guarantees that VALUE ends up in TARGET.  */
static void
static void
force_into (rtx value, rtx target)
force_into (rtx value, rtx target)
{
{
  value = force_operand (value, target);
  value = force_operand (value, target);
  if (! rtx_equal_p (value, target))
  if (! rtx_equal_p (value, target))
    emit_insn (gen_move_insn (target, value));
    emit_insn (gen_move_insn (target, value));
}
}
 
 
/* Emit code to perform a block move.  Choose the best method.
/* Emit code to perform a block move.  Choose the best method.
 
 
   OPERANDS[0] is the destination.
   OPERANDS[0] is the destination.
   OPERANDS[1] is the source.
   OPERANDS[1] is the source.
   OPERANDS[2] is the size.
   OPERANDS[2] is the size.
   OPERANDS[3] is the alignment safe to use.  */
   OPERANDS[3] is the alignment safe to use.  */
 
 
int
int
expand_block_move (rtx *operands)
expand_block_move (rtx *operands)
{
{
  int align = INTVAL (operands[3]);
  int align = INTVAL (operands[3]);
  int constp = (CONST_INT_P (operands[2]));
  int constp = (CONST_INT_P (operands[2]));
  int bytes = (constp ? INTVAL (operands[2]) : 0);
  int bytes = (constp ? INTVAL (operands[2]) : 0);
 
 
  if (! constp)
  if (! constp)
    return 0;
    return 0;
 
 
  /* If we could use mov.l to move words and dest is word-aligned, we
  /* If we could use mov.l to move words and dest is word-aligned, we
     can use movua.l for loads and still generate a relatively short
     can use movua.l for loads and still generate a relatively short
     and efficient sequence.  */
     and efficient sequence.  */
  if (TARGET_SH4A_ARCH && align < 4
  if (TARGET_SH4A_ARCH && align < 4
      && MEM_ALIGN (operands[0]) >= 32
      && MEM_ALIGN (operands[0]) >= 32
      && can_move_by_pieces (bytes, 32))
      && can_move_by_pieces (bytes, 32))
    {
    {
      rtx dest = copy_rtx (operands[0]);
      rtx dest = copy_rtx (operands[0]);
      rtx src = copy_rtx (operands[1]);
      rtx src = copy_rtx (operands[1]);
      /* We could use different pseudos for each copied word, but
      /* We could use different pseudos for each copied word, but
         since movua can only load into r0, it's kind of
         since movua can only load into r0, it's kind of
         pointless.  */
         pointless.  */
      rtx temp = gen_reg_rtx (SImode);
      rtx temp = gen_reg_rtx (SImode);
      rtx src_addr = copy_addr_to_reg (XEXP (src, 0));
      rtx src_addr = copy_addr_to_reg (XEXP (src, 0));
      int copied = 0;
      int copied = 0;
 
 
      while (copied + 4 <= bytes)
      while (copied + 4 <= bytes)
        {
        {
          rtx to = adjust_address (dest, SImode, copied);
          rtx to = adjust_address (dest, SImode, copied);
          rtx from = adjust_automodify_address (src, BLKmode,
          rtx from = adjust_automodify_address (src, BLKmode,
                                                src_addr, copied);
                                                src_addr, copied);
 
 
          set_mem_size (from, GEN_INT (4));
          set_mem_size (from, GEN_INT (4));
          emit_insn (gen_movua (temp, from));
          emit_insn (gen_movua (temp, from));
          emit_move_insn (src_addr, plus_constant (src_addr, 4));
          emit_move_insn (src_addr, plus_constant (src_addr, 4));
          emit_move_insn (to, temp);
          emit_move_insn (to, temp);
          copied += 4;
          copied += 4;
        }
        }
 
 
      if (copied < bytes)
      if (copied < bytes)
        move_by_pieces (adjust_address (dest, BLKmode, copied),
        move_by_pieces (adjust_address (dest, BLKmode, copied),
                        adjust_automodify_address (src, BLKmode,
                        adjust_automodify_address (src, BLKmode,
                                                   src_addr, copied),
                                                   src_addr, copied),
                        bytes - copied, align, 0);
                        bytes - copied, align, 0);
 
 
      return 1;
      return 1;
    }
    }
 
 
  /* If it isn't a constant number of bytes, or if it doesn't have 4 byte
  /* If it isn't a constant number of bytes, or if it doesn't have 4 byte
     alignment, or if it isn't a multiple of 4 bytes, then fail.  */
     alignment, or if it isn't a multiple of 4 bytes, then fail.  */
  if (align < 4 || (bytes % 4 != 0))
  if (align < 4 || (bytes % 4 != 0))
    return 0;
    return 0;
 
 
  if (TARGET_HARD_SH4)
  if (TARGET_HARD_SH4)
    {
    {
      if (bytes < 12)
      if (bytes < 12)
        return 0;
        return 0;
      else if (bytes == 12)
      else if (bytes == 12)
        {
        {
          rtx func_addr_rtx = gen_reg_rtx (Pmode);
          rtx func_addr_rtx = gen_reg_rtx (Pmode);
          rtx r4 = gen_rtx_REG (SImode, 4);
          rtx r4 = gen_rtx_REG (SImode, 4);
          rtx r5 = gen_rtx_REG (SImode, 5);
          rtx r5 = gen_rtx_REG (SImode, 5);
 
 
          function_symbol (func_addr_rtx, "__movmemSI12_i4", SFUNC_STATIC);
          function_symbol (func_addr_rtx, "__movmemSI12_i4", SFUNC_STATIC);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[1], 0), r5);
          force_into (XEXP (operands[1], 0), r5);
          emit_insn (gen_block_move_real_i4 (func_addr_rtx));
          emit_insn (gen_block_move_real_i4 (func_addr_rtx));
          return 1;
          return 1;
        }
        }
      else if (! TARGET_SMALLCODE)
      else if (! TARGET_SMALLCODE)
        {
        {
          const char *entry_name;
          const char *entry_name;
          rtx func_addr_rtx = gen_reg_rtx (Pmode);
          rtx func_addr_rtx = gen_reg_rtx (Pmode);
          int dwords;
          int dwords;
          rtx r4 = gen_rtx_REG (SImode, 4);
          rtx r4 = gen_rtx_REG (SImode, 4);
          rtx r5 = gen_rtx_REG (SImode, 5);
          rtx r5 = gen_rtx_REG (SImode, 5);
          rtx r6 = gen_rtx_REG (SImode, 6);
          rtx r6 = gen_rtx_REG (SImode, 6);
 
 
          entry_name = (bytes & 4 ? "__movmem_i4_odd" : "__movmem_i4_even");
          entry_name = (bytes & 4 ? "__movmem_i4_odd" : "__movmem_i4_even");
          function_symbol (func_addr_rtx, entry_name, SFUNC_STATIC);
          function_symbol (func_addr_rtx, entry_name, SFUNC_STATIC);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[1], 0), r5);
          force_into (XEXP (operands[1], 0), r5);
 
 
          dwords = bytes >> 3;
          dwords = bytes >> 3;
          emit_insn (gen_move_insn (r6, GEN_INT (dwords - 1)));
          emit_insn (gen_move_insn (r6, GEN_INT (dwords - 1)));
          emit_insn (gen_block_lump_real_i4 (func_addr_rtx));
          emit_insn (gen_block_lump_real_i4 (func_addr_rtx));
          return 1;
          return 1;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
  if (bytes < 64)
  if (bytes < 64)
    {
    {
      char entry[30];
      char entry[30];
      rtx func_addr_rtx = gen_reg_rtx (Pmode);
      rtx func_addr_rtx = gen_reg_rtx (Pmode);
      rtx r4 = gen_rtx_REG (SImode, 4);
      rtx r4 = gen_rtx_REG (SImode, 4);
      rtx r5 = gen_rtx_REG (SImode, 5);
      rtx r5 = gen_rtx_REG (SImode, 5);
 
 
      sprintf (entry, "__movmemSI%d", bytes);
      sprintf (entry, "__movmemSI%d", bytes);
      function_symbol (func_addr_rtx, entry, SFUNC_STATIC);
      function_symbol (func_addr_rtx, entry, SFUNC_STATIC);
      force_into (XEXP (operands[0], 0), r4);
      force_into (XEXP (operands[0], 0), r4);
      force_into (XEXP (operands[1], 0), r5);
      force_into (XEXP (operands[1], 0), r5);
      emit_insn (gen_block_move_real (func_addr_rtx));
      emit_insn (gen_block_move_real (func_addr_rtx));
      return 1;
      return 1;
    }
    }
 
 
  /* This is the same number of bytes as a memcpy call, but to a different
  /* This is the same number of bytes as a memcpy call, but to a different
     less common function name, so this will occasionally use more space.  */
     less common function name, so this will occasionally use more space.  */
  if (! TARGET_SMALLCODE)
  if (! TARGET_SMALLCODE)
    {
    {
      rtx func_addr_rtx = gen_reg_rtx (Pmode);
      rtx func_addr_rtx = gen_reg_rtx (Pmode);
      int final_switch, while_loop;
      int final_switch, while_loop;
      rtx r4 = gen_rtx_REG (SImode, 4);
      rtx r4 = gen_rtx_REG (SImode, 4);
      rtx r5 = gen_rtx_REG (SImode, 5);
      rtx r5 = gen_rtx_REG (SImode, 5);
      rtx r6 = gen_rtx_REG (SImode, 6);
      rtx r6 = gen_rtx_REG (SImode, 6);
 
 
      function_symbol (func_addr_rtx, "__movmem", SFUNC_STATIC);
      function_symbol (func_addr_rtx, "__movmem", SFUNC_STATIC);
      force_into (XEXP (operands[0], 0), r4);
      force_into (XEXP (operands[0], 0), r4);
      force_into (XEXP (operands[1], 0), r5);
      force_into (XEXP (operands[1], 0), r5);
 
 
      /* r6 controls the size of the move.  16 is decremented from it
      /* r6 controls the size of the move.  16 is decremented from it
         for each 64 bytes moved.  Then the negative bit left over is used
         for each 64 bytes moved.  Then the negative bit left over is used
         as an index into a list of move instructions.  e.g., a 72 byte move
         as an index into a list of move instructions.  e.g., a 72 byte move
         would be set up with size(r6) = 14, for one iteration through the
         would be set up with size(r6) = 14, for one iteration through the
         big while loop, and a switch of -2 for the last part.  */
         big while loop, and a switch of -2 for the last part.  */
 
 
      final_switch = 16 - ((bytes / 4) % 16);
      final_switch = 16 - ((bytes / 4) % 16);
      while_loop = ((bytes / 4) / 16 - 1) * 16;
      while_loop = ((bytes / 4) / 16 - 1) * 16;
      emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch)));
      emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch)));
      emit_insn (gen_block_lump_real (func_addr_rtx));
      emit_insn (gen_block_lump_real (func_addr_rtx));
      return 1;
      return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Prepare operands for a move define_expand; specifically, one of the
/* Prepare operands for a move define_expand; specifically, one of the
   operands must be in a register.  */
   operands must be in a register.  */
 
 
int
int
prepare_move_operands (rtx operands[], enum machine_mode mode)
prepare_move_operands (rtx operands[], enum machine_mode mode)
{
{
  if ((mode == SImode || mode == DImode)
  if ((mode == SImode || mode == DImode)
      && flag_pic
      && flag_pic
      && ! ((mode == Pmode || mode == ptr_mode)
      && ! ((mode == Pmode || mode == ptr_mode)
            && tls_symbolic_operand (operands[1], Pmode) != TLS_MODEL_NONE))
            && tls_symbolic_operand (operands[1], Pmode) != TLS_MODEL_NONE))
    {
    {
      rtx temp;
      rtx temp;
      if (SYMBOLIC_CONST_P (operands[1]))
      if (SYMBOLIC_CONST_P (operands[1]))
        {
        {
          if (MEM_P (operands[0]))
          if (MEM_P (operands[0]))
            operands[1] = force_reg (Pmode, operands[1]);
            operands[1] = force_reg (Pmode, operands[1]);
          else if (TARGET_SHMEDIA
          else if (TARGET_SHMEDIA
                   && GET_CODE (operands[1]) == LABEL_REF
                   && GET_CODE (operands[1]) == LABEL_REF
                   && target_reg_operand (operands[0], mode))
                   && target_reg_operand (operands[0], mode))
            /* It's ok.  */;
            /* It's ok.  */;
          else
          else
            {
            {
              temp = (!can_create_pseudo_p ()
              temp = (!can_create_pseudo_p ()
                      ? operands[0]
                      ? operands[0]
                      : gen_reg_rtx (Pmode));
                      : gen_reg_rtx (Pmode));
              operands[1] = legitimize_pic_address (operands[1], mode, temp);
              operands[1] = legitimize_pic_address (operands[1], mode, temp);
            }
            }
        }
        }
      else if (GET_CODE (operands[1]) == CONST
      else if (GET_CODE (operands[1]) == CONST
               && GET_CODE (XEXP (operands[1], 0)) == PLUS
               && GET_CODE (XEXP (operands[1], 0)) == PLUS
               && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
               && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
        {
        {
          temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
          temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
          temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
          temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
                                         mode, temp);
                                         mode, temp);
          operands[1] = expand_binop (mode, add_optab, temp,
          operands[1] = expand_binop (mode, add_optab, temp,
                                      XEXP (XEXP (operands[1], 0), 1),
                                      XEXP (XEXP (operands[1], 0), 1),
                                      (!can_create_pseudo_p ()
                                      (!can_create_pseudo_p ()
                                       ? temp
                                       ? temp
                                       : gen_reg_rtx (Pmode)),
                                       : gen_reg_rtx (Pmode)),
                                      0, OPTAB_LIB_WIDEN);
                                      0, OPTAB_LIB_WIDEN);
        }
        }
    }
    }
 
 
  if (! reload_in_progress && ! reload_completed)
  if (! reload_in_progress && ! reload_completed)
    {
    {
      /* Copy the source to a register if both operands aren't registers.  */
      /* Copy the source to a register if both operands aren't registers.  */
      if (! register_operand (operands[0], mode)
      if (! register_operand (operands[0], mode)
          && ! sh_register_operand (operands[1], mode))
          && ! sh_register_operand (operands[1], mode))
        operands[1] = copy_to_mode_reg (mode, operands[1]);
        operands[1] = copy_to_mode_reg (mode, operands[1]);
 
 
      if (MEM_P (operands[0]) && ! memory_operand (operands[0], mode))
      if (MEM_P (operands[0]) && ! memory_operand (operands[0], mode))
        {
        {
          /* This is like change_address_1 (operands[0], mode, 0, 1) ,
          /* This is like change_address_1 (operands[0], mode, 0, 1) ,
             except that we can't use that function because it is static.  */
             except that we can't use that function because it is static.  */
          rtx new_rtx = change_address (operands[0], mode, 0);
          rtx new_rtx = change_address (operands[0], mode, 0);
          MEM_COPY_ATTRIBUTES (new_rtx, operands[0]);
          MEM_COPY_ATTRIBUTES (new_rtx, operands[0]);
          operands[0] = new_rtx;
          operands[0] = new_rtx;
        }
        }
 
 
      /* This case can happen while generating code to move the result
      /* This case can happen while generating code to move the result
         of a library call to the target.  Reject `st r0,@(rX,rY)' because
         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
         reload will fail to find a spill register for rX, since r0 is already
         being used for the source.  */
         being used for the source.  */
      else if (TARGET_SH1
      else if (TARGET_SH1
               && refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0)
               && refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0)
               && MEM_P (operands[0])
               && MEM_P (operands[0])
               && GET_CODE (XEXP (operands[0], 0)) == PLUS
               && GET_CODE (XEXP (operands[0], 0)) == PLUS
               && REG_P (XEXP (XEXP (operands[0], 0), 1)))
               && REG_P (XEXP (XEXP (operands[0], 0), 1)))
        operands[1] = copy_to_mode_reg (mode, operands[1]);
        operands[1] = copy_to_mode_reg (mode, operands[1]);
    }
    }
 
 
  if (mode == Pmode || mode == ptr_mode)
  if (mode == Pmode || mode == ptr_mode)
    {
    {
      rtx op0, op1, opc;
      rtx op0, op1, opc;
      enum tls_model tls_kind;
      enum tls_model tls_kind;
 
 
      op0 = operands[0];
      op0 = operands[0];
      op1 = operands[1];
      op1 = operands[1];
      if (GET_CODE (op1) == CONST
      if (GET_CODE (op1) == CONST
          && GET_CODE (XEXP (op1, 0)) == PLUS
          && GET_CODE (XEXP (op1, 0)) == PLUS
          && (tls_symbolic_operand (XEXP (XEXP (op1, 0), 0), Pmode)
          && (tls_symbolic_operand (XEXP (XEXP (op1, 0), 0), Pmode)
              != TLS_MODEL_NONE))
              != TLS_MODEL_NONE))
        {
        {
          opc = XEXP (XEXP (op1, 0), 1);
          opc = XEXP (XEXP (op1, 0), 1);
          op1 = XEXP (XEXP (op1, 0), 0);
          op1 = XEXP (XEXP (op1, 0), 0);
        }
        }
      else
      else
        opc = NULL_RTX;
        opc = NULL_RTX;
 
 
      if ((tls_kind = tls_symbolic_operand (op1, Pmode)) != TLS_MODEL_NONE)
      if ((tls_kind = tls_symbolic_operand (op1, Pmode)) != TLS_MODEL_NONE)
        {
        {
          rtx tga_op1, tga_ret, tmp, tmp2;
          rtx tga_op1, tga_ret, tmp, tmp2;
 
 
          switch (tls_kind)
          switch (tls_kind)
            {
            {
            case TLS_MODEL_GLOBAL_DYNAMIC:
            case TLS_MODEL_GLOBAL_DYNAMIC:
              tga_ret = gen_rtx_REG (Pmode, R0_REG);
              tga_ret = gen_rtx_REG (Pmode, R0_REG);
              emit_call_insn (gen_tls_global_dynamic (tga_ret, op1));
              emit_call_insn (gen_tls_global_dynamic (tga_ret, op1));
              op1 = tga_ret;
              op1 = tga_ret;
              break;
              break;
 
 
            case TLS_MODEL_LOCAL_DYNAMIC:
            case TLS_MODEL_LOCAL_DYNAMIC:
              tga_ret = gen_rtx_REG (Pmode, R0_REG);
              tga_ret = gen_rtx_REG (Pmode, R0_REG);
              emit_call_insn (gen_tls_local_dynamic (tga_ret, op1));
              emit_call_insn (gen_tls_local_dynamic (tga_ret, op1));
 
 
              tmp = gen_reg_rtx (Pmode);
              tmp = gen_reg_rtx (Pmode);
              emit_move_insn (tmp, tga_ret);
              emit_move_insn (tmp, tga_ret);
 
 
              if (register_operand (op0, Pmode))
              if (register_operand (op0, Pmode))
                tmp2 = op0;
                tmp2 = op0;
              else
              else
                tmp2 = gen_reg_rtx (Pmode);
                tmp2 = gen_reg_rtx (Pmode);
 
 
              emit_insn (gen_symDTPOFF2reg (tmp2, op1, tmp));
              emit_insn (gen_symDTPOFF2reg (tmp2, op1, tmp));
              op1 = tmp2;
              op1 = tmp2;
              break;
              break;
 
 
            case TLS_MODEL_INITIAL_EXEC:
            case TLS_MODEL_INITIAL_EXEC:
              if (! flag_pic)
              if (! flag_pic)
                {
                {
                  /* Don't schedule insns for getting GOT address when
                  /* Don't schedule insns for getting GOT address when
                     the first scheduling is enabled, to avoid spill
                     the first scheduling is enabled, to avoid spill
                     failures for R0.  */
                     failures for R0.  */
                  if (flag_schedule_insns)
                  if (flag_schedule_insns)
                    emit_insn (gen_blockage ());
                    emit_insn (gen_blockage ());
                  emit_insn (gen_GOTaddr2picreg ());
                  emit_insn (gen_GOTaddr2picreg ());
                  emit_use (gen_rtx_REG (SImode, PIC_REG));
                  emit_use (gen_rtx_REG (SImode, PIC_REG));
                  if (flag_schedule_insns)
                  if (flag_schedule_insns)
                    emit_insn (gen_blockage ());
                    emit_insn (gen_blockage ());
                }
                }
              tga_op1 = !can_create_pseudo_p () ? op0 : gen_reg_rtx (Pmode);
              tga_op1 = !can_create_pseudo_p () ? op0 : gen_reg_rtx (Pmode);
              tmp = gen_sym2GOTTPOFF (op1);
              tmp = gen_sym2GOTTPOFF (op1);
              emit_insn (gen_tls_initial_exec (tga_op1, tmp));
              emit_insn (gen_tls_initial_exec (tga_op1, tmp));
              op1 = tga_op1;
              op1 = tga_op1;
              break;
              break;
 
 
            case TLS_MODEL_LOCAL_EXEC:
            case TLS_MODEL_LOCAL_EXEC:
              tmp2 = gen_reg_rtx (Pmode);
              tmp2 = gen_reg_rtx (Pmode);
              emit_insn (gen_load_gbr (tmp2));
              emit_insn (gen_load_gbr (tmp2));
              tmp = gen_reg_rtx (Pmode);
              tmp = gen_reg_rtx (Pmode);
              emit_insn (gen_symTPOFF2reg (tmp, op1));
              emit_insn (gen_symTPOFF2reg (tmp, op1));
 
 
              if (register_operand (op0, Pmode))
              if (register_operand (op0, Pmode))
                op1 = op0;
                op1 = op0;
              else
              else
                op1 = gen_reg_rtx (Pmode);
                op1 = gen_reg_rtx (Pmode);
 
 
              emit_insn (gen_addsi3 (op1, tmp, tmp2));
              emit_insn (gen_addsi3 (op1, tmp, tmp2));
              break;
              break;
 
 
            default:
            default:
              gcc_unreachable ();
              gcc_unreachable ();
            }
            }
          if (opc)
          if (opc)
            emit_insn (gen_addsi3 (op1, op1, force_reg (SImode, opc)));
            emit_insn (gen_addsi3 (op1, op1, force_reg (SImode, opc)));
          operands[1] = op1;
          operands[1] = op1;
        }
        }
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
enum rtx_code
enum rtx_code
prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
                          enum rtx_code comparison)
                          enum rtx_code comparison)
{
{
  rtx op1;
  rtx op1;
  rtx scratch = NULL_RTX;
  rtx scratch = NULL_RTX;
 
 
  if (comparison == LAST_AND_UNUSED_RTX_CODE)
  if (comparison == LAST_AND_UNUSED_RTX_CODE)
    comparison = GET_CODE (operands[0]);
    comparison = GET_CODE (operands[0]);
  else
  else
    scratch = operands[4];
    scratch = operands[4];
  if (CONST_INT_P (operands[1])
  if (CONST_INT_P (operands[1])
      && !CONST_INT_P (operands[2]))
      && !CONST_INT_P (operands[2]))
    {
    {
      rtx tmp = operands[1];
      rtx tmp = operands[1];
 
 
      operands[1] = operands[2];
      operands[1] = operands[2];
      operands[2] = tmp;
      operands[2] = tmp;
      comparison = swap_condition (comparison);
      comparison = swap_condition (comparison);
    }
    }
  if (CONST_INT_P (operands[2]))
  if (CONST_INT_P (operands[2]))
    {
    {
      HOST_WIDE_INT val = INTVAL (operands[2]);
      HOST_WIDE_INT val = INTVAL (operands[2]);
      if ((val == -1 || val == -0x81)
      if ((val == -1 || val == -0x81)
          && (comparison == GT || comparison == LE))
          && (comparison == GT || comparison == LE))
        {
        {
          comparison = (comparison == GT) ? GE : LT;
          comparison = (comparison == GT) ? GE : LT;
          operands[2] = gen_int_mode (val + 1, mode);
          operands[2] = gen_int_mode (val + 1, mode);
        }
        }
      else if ((val == 1 || val == 0x80)
      else if ((val == 1 || val == 0x80)
               && (comparison == GE || comparison == LT))
               && (comparison == GE || comparison == LT))
        {
        {
          comparison = (comparison == GE) ? GT : LE;
          comparison = (comparison == GE) ? GT : LE;
          operands[2] = gen_int_mode (val - 1, mode);
          operands[2] = gen_int_mode (val - 1, mode);
        }
        }
      else if (val == 1 && (comparison == GEU || comparison == LTU))
      else if (val == 1 && (comparison == GEU || comparison == LTU))
        {
        {
          comparison = (comparison == GEU) ? NE : EQ;
          comparison = (comparison == GEU) ? NE : EQ;
          operands[2] = CONST0_RTX (mode);
          operands[2] = CONST0_RTX (mode);
        }
        }
      else if (val == 0x80 && (comparison == GEU || comparison == LTU))
      else if (val == 0x80 && (comparison == GEU || comparison == LTU))
        {
        {
          comparison = (comparison == GEU) ? GTU : LEU;
          comparison = (comparison == GEU) ? GTU : LEU;
          operands[2] = gen_int_mode (val - 1, mode);
          operands[2] = gen_int_mode (val - 1, mode);
        }
        }
      else if (val == 0 && (comparison == GTU || comparison == LEU))
      else if (val == 0 && (comparison == GTU || comparison == LEU))
        comparison = (comparison == GTU) ? NE : EQ;
        comparison = (comparison == GTU) ? NE : EQ;
      else if (mode == SImode
      else if (mode == SImode
               && ((val == 0x7fffffff
               && ((val == 0x7fffffff
                    && (comparison == GTU || comparison == LEU))
                    && (comparison == GTU || comparison == LEU))
                   || ((unsigned HOST_WIDE_INT) val
                   || ((unsigned HOST_WIDE_INT) val
                        == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
                        == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
                       && (comparison == GEU || comparison == LTU))))
                       && (comparison == GEU || comparison == LTU))))
        {
        {
          comparison = (comparison == GTU || comparison == GEU) ? LT : GE;
          comparison = (comparison == GTU || comparison == GEU) ? LT : GE;
          operands[2] = CONST0_RTX (mode);
          operands[2] = CONST0_RTX (mode);
        }
        }
    }
    }
  op1 = operands[1];
  op1 = operands[1];
  if (can_create_pseudo_p ())
  if (can_create_pseudo_p ())
    operands[1] = force_reg (mode, op1);
    operands[1] = force_reg (mode, op1);
  /* When we are handling DImode comparisons, we want to keep constants so
  /* When we are handling DImode comparisons, we want to keep constants so
     that we can optimize the component comparisons; however, memory loads
     that we can optimize the component comparisons; however, memory loads
     are better issued as a whole so that they can be scheduled well.
     are better issued as a whole so that they can be scheduled well.
     SImode equality comparisons allow I08 constants, but only when they
     SImode equality comparisons allow I08 constants, but only when they
     compare r0.  Hence, if operands[1] has to be loaded from somewhere else
     compare r0.  Hence, if operands[1] has to be loaded from somewhere else
     into a register, that register might as well be r0, and we allow the
     into a register, that register might as well be r0, and we allow the
     constant.  If it is already in a register, this is likely to be
     constant.  If it is already in a register, this is likely to be
     allocated to a different hard register, thus we load the constant into
     allocated to a different hard register, thus we load the constant into
     a register unless it is zero.  */
     a register unless it is zero.  */
  if (!REG_P (operands[2])
  if (!REG_P (operands[2])
      && (!CONST_INT_P (operands[2])
      && (!CONST_INT_P (operands[2])
          || (mode == SImode && operands[2] != CONST0_RTX (SImode)
          || (mode == SImode && operands[2] != CONST0_RTX (SImode)
              && ((comparison != EQ && comparison != NE)
              && ((comparison != EQ && comparison != NE)
                  || (REG_P (op1) && REGNO (op1) != R0_REG)
                  || (REG_P (op1) && REGNO (op1) != R0_REG)
                  || !satisfies_constraint_I08 (operands[2])))))
                  || !satisfies_constraint_I08 (operands[2])))))
    {
    {
      if (scratch && GET_MODE (scratch) == mode)
      if (scratch && GET_MODE (scratch) == mode)
        {
        {
          emit_move_insn (scratch, operands[2]);
          emit_move_insn (scratch, operands[2]);
          operands[2] = scratch;
          operands[2] = scratch;
        }
        }
      else if (can_create_pseudo_p ())
      else if (can_create_pseudo_p ())
        operands[2] = force_reg (mode, operands[2]);
        operands[2] = force_reg (mode, operands[2]);
    }
    }
  return comparison;
  return comparison;
}
}
 
 
void
void
expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
{
{
  rtx (*branch_expander) (rtx) = gen_branch_true;
  rtx (*branch_expander) (rtx) = gen_branch_true;
  rtx jump;
  rtx jump;
 
 
  comparison = prepare_cbranch_operands (operands, SImode, comparison);
  comparison = prepare_cbranch_operands (operands, SImode, comparison);
  switch (comparison)
  switch (comparison)
    {
    {
    case NE: case LT: case LE: case LTU: case LEU:
    case NE: case LT: case LE: case LTU: case LEU:
      comparison = reverse_condition (comparison);
      comparison = reverse_condition (comparison);
      branch_expander = gen_branch_false;
      branch_expander = gen_branch_false;
    default: ;
    default: ;
    }
    }
  emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG),
  emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG),
                          gen_rtx_fmt_ee (comparison, SImode,
                          gen_rtx_fmt_ee (comparison, SImode,
                                          operands[1], operands[2])));
                                          operands[1], operands[2])));
  jump = emit_jump_insn (branch_expander (operands[3]));
  jump = emit_jump_insn (branch_expander (operands[3]));
  if (probability >= 0)
  if (probability >= 0)
    add_reg_note (jump, REG_BR_PROB, GEN_INT (probability));
    add_reg_note (jump, REG_BR_PROB, GEN_INT (probability));
 
 
}
}
 
 
/* ??? How should we distribute probabilities when more than one branch
/* ??? How should we distribute probabilities when more than one branch
   is generated.  So far we only have soem ad-hoc observations:
   is generated.  So far we only have soem ad-hoc observations:
   - If the operands are random, they are likely to differ in both parts.
   - If the operands are random, they are likely to differ in both parts.
   - If comparing items in a hash chain, the operands are random or equal;
   - If comparing items in a hash chain, the operands are random or equal;
     operation should be EQ or NE.
     operation should be EQ or NE.
   - If items are searched in an ordered tree from the root, we can expect
   - If items are searched in an ordered tree from the root, we can expect
     the highpart to be unequal about half of the time; operation should be
     the highpart to be unequal about half of the time; operation should be
     an inequality comparison, operands non-constant, and overall probability
     an inequality comparison, operands non-constant, and overall probability
     about 50%.  Likewise for quicksort.
     about 50%.  Likewise for quicksort.
   - Range checks will be often made against constants.  Even if we assume for
   - Range checks will be often made against constants.  Even if we assume for
     simplicity an even distribution of the non-constant operand over a
     simplicity an even distribution of the non-constant operand over a
     sub-range here, the same probability could be generated with differently
     sub-range here, the same probability could be generated with differently
     wide sub-ranges - as long as the ratio of the part of the subrange that
     wide sub-ranges - as long as the ratio of the part of the subrange that
     is before the threshold to the part that comes after the threshold stays
     is before the threshold to the part that comes after the threshold stays
     the same.  Thus, we can't really tell anything here;
     the same.  Thus, we can't really tell anything here;
     assuming random distribution is at least simple.
     assuming random distribution is at least simple.
 */
 */
 
 
bool
bool
expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
{
{
  enum rtx_code msw_taken, msw_skip, lsw_taken;
  enum rtx_code msw_taken, msw_skip, lsw_taken;
  rtx skip_label = NULL_RTX;
  rtx skip_label = NULL_RTX;
  rtx op1h, op1l, op2h, op2l;
  rtx op1h, op1l, op2h, op2l;
  int num_branches;
  int num_branches;
  int prob, rev_prob;
  int prob, rev_prob;
  int msw_taken_prob = -1, msw_skip_prob = -1, lsw_taken_prob = -1;
  int msw_taken_prob = -1, msw_skip_prob = -1, lsw_taken_prob = -1;
  rtx scratch = operands[4];
  rtx scratch = operands[4];
 
 
  comparison = prepare_cbranch_operands (operands, DImode, comparison);
  comparison = prepare_cbranch_operands (operands, DImode, comparison);
  op1h = gen_highpart_mode (SImode, DImode, operands[1]);
  op1h = gen_highpart_mode (SImode, DImode, operands[1]);
  op2h = gen_highpart_mode (SImode, DImode, operands[2]);
  op2h = gen_highpart_mode (SImode, DImode, operands[2]);
  op1l = gen_lowpart (SImode, operands[1]);
  op1l = gen_lowpart (SImode, operands[1]);
  op2l = gen_lowpart (SImode, operands[2]);
  op2l = gen_lowpart (SImode, operands[2]);
  msw_taken = msw_skip = lsw_taken = LAST_AND_UNUSED_RTX_CODE;
  msw_taken = msw_skip = lsw_taken = LAST_AND_UNUSED_RTX_CODE;
  prob = split_branch_probability;
  prob = split_branch_probability;
  rev_prob = REG_BR_PROB_BASE - prob;
  rev_prob = REG_BR_PROB_BASE - prob;
  switch (comparison)
  switch (comparison)
    {
    {
    /* ??? Should we use the cmpeqdi_t pattern for equality comparisons?
    /* ??? Should we use the cmpeqdi_t pattern for equality comparisons?
       That costs 1 cycle more when the first branch can be predicted taken,
       That costs 1 cycle more when the first branch can be predicted taken,
       but saves us mispredicts because only one branch needs prediction.
       but saves us mispredicts because only one branch needs prediction.
       It also enables generating the cmpeqdi_t-1 pattern.  */
       It also enables generating the cmpeqdi_t-1 pattern.  */
    case EQ:
    case EQ:
      if (TARGET_CMPEQDI_T)
      if (TARGET_CMPEQDI_T)
        {
        {
          emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
          emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
          emit_jump_insn (gen_branch_true (operands[3]));
          emit_jump_insn (gen_branch_true (operands[3]));
          return true;
          return true;
        }
        }
      msw_skip = NE;
      msw_skip = NE;
      lsw_taken = EQ;
      lsw_taken = EQ;
      if (prob >= 0)
      if (prob >= 0)
        {
        {
          /* If we had more precision, we'd use rev_prob - (rev_prob >> 32) .
          /* If we had more precision, we'd use rev_prob - (rev_prob >> 32) .
           */
           */
          msw_skip_prob = rev_prob;
          msw_skip_prob = rev_prob;
          if (REG_BR_PROB_BASE <= 65535)
          if (REG_BR_PROB_BASE <= 65535)
            lsw_taken_prob = prob ? REG_BR_PROB_BASE : 0;
            lsw_taken_prob = prob ? REG_BR_PROB_BASE : 0;
          else
          else
            {
            {
              gcc_assert (HOST_BITS_PER_WIDEST_INT >= 64);
              gcc_assert (HOST_BITS_PER_WIDEST_INT >= 64);
              lsw_taken_prob
              lsw_taken_prob
                = (prob
                = (prob
                   ? (REG_BR_PROB_BASE
                   ? (REG_BR_PROB_BASE
                      - ((HOST_WIDEST_INT) REG_BR_PROB_BASE * rev_prob
                      - ((HOST_WIDEST_INT) REG_BR_PROB_BASE * rev_prob
                         / ((HOST_WIDEST_INT) prob << 32)))
                         / ((HOST_WIDEST_INT) prob << 32)))
                   : 0);
                   : 0);
            }
            }
        }
        }
      break;
      break;
    case NE:
    case NE:
      if (TARGET_CMPEQDI_T)
      if (TARGET_CMPEQDI_T)
        {
        {
          emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
          emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
          emit_jump_insn (gen_branch_false (operands[3]));
          emit_jump_insn (gen_branch_false (operands[3]));
          return true;
          return true;
        }
        }
      msw_taken = NE;
      msw_taken = NE;
      msw_taken_prob = prob;
      msw_taken_prob = prob;
      lsw_taken = NE;
      lsw_taken = NE;
      lsw_taken_prob = 0;
      lsw_taken_prob = 0;
      break;
      break;
    case GTU: case GT:
    case GTU: case GT:
      msw_taken = comparison;
      msw_taken = comparison;
      if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
      if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
        break;
        break;
      if (comparison != GTU || op2h != CONST0_RTX (SImode))
      if (comparison != GTU || op2h != CONST0_RTX (SImode))
        msw_skip = swap_condition (msw_taken);
        msw_skip = swap_condition (msw_taken);
      lsw_taken = GTU;
      lsw_taken = GTU;
      break;
      break;
    case GEU: case GE:
    case GEU: case GE:
      if (op2l == CONST0_RTX (SImode))
      if (op2l == CONST0_RTX (SImode))
        msw_taken = comparison;
        msw_taken = comparison;
      else
      else
        {
        {
          msw_taken = comparison == GE ? GT : GTU;
          msw_taken = comparison == GE ? GT : GTU;
          msw_skip = swap_condition (msw_taken);
          msw_skip = swap_condition (msw_taken);
          lsw_taken = GEU;
          lsw_taken = GEU;
        }
        }
      break;
      break;
    case LTU: case LT:
    case LTU: case LT:
      msw_taken = comparison;
      msw_taken = comparison;
      if (op2l == CONST0_RTX (SImode))
      if (op2l == CONST0_RTX (SImode))
        break;
        break;
      msw_skip = swap_condition (msw_taken);
      msw_skip = swap_condition (msw_taken);
      lsw_taken = LTU;
      lsw_taken = LTU;
      break;
      break;
    case LEU: case LE:
    case LEU: case LE:
      if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
      if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
        msw_taken = comparison;
        msw_taken = comparison;
      else
      else
        {
        {
          lsw_taken = LEU;
          lsw_taken = LEU;
          if (comparison == LE)
          if (comparison == LE)
            msw_taken = LT;
            msw_taken = LT;
          else if (op2h != CONST0_RTX (SImode))
          else if (op2h != CONST0_RTX (SImode))
            msw_taken = LTU;
            msw_taken = LTU;
          else
          else
            break;
            break;
          msw_skip = swap_condition (msw_taken);
          msw_skip = swap_condition (msw_taken);
        }
        }
      break;
      break;
    default: return false;
    default: return false;
    }
    }
  num_branches = ((msw_taken != LAST_AND_UNUSED_RTX_CODE)
  num_branches = ((msw_taken != LAST_AND_UNUSED_RTX_CODE)
                  + (msw_skip != LAST_AND_UNUSED_RTX_CODE)
                  + (msw_skip != LAST_AND_UNUSED_RTX_CODE)
                  + (lsw_taken != LAST_AND_UNUSED_RTX_CODE));
                  + (lsw_taken != LAST_AND_UNUSED_RTX_CODE));
  if (comparison != EQ && comparison != NE && num_branches > 1)
  if (comparison != EQ && comparison != NE && num_branches > 1)
    {
    {
      if (!CONSTANT_P (operands[2])
      if (!CONSTANT_P (operands[2])
          && prob >= (int) (REG_BR_PROB_BASE * 3 / 8U)
          && prob >= (int) (REG_BR_PROB_BASE * 3 / 8U)
          && prob <= (int) (REG_BR_PROB_BASE * 5 / 8U))
          && prob <= (int) (REG_BR_PROB_BASE * 5 / 8U))
        {
        {
          msw_taken_prob = prob / 2U;
          msw_taken_prob = prob / 2U;
          msw_skip_prob
          msw_skip_prob
            = REG_BR_PROB_BASE * rev_prob / (REG_BR_PROB_BASE + rev_prob);
            = REG_BR_PROB_BASE * rev_prob / (REG_BR_PROB_BASE + rev_prob);
          lsw_taken_prob = prob;
          lsw_taken_prob = prob;
        }
        }
      else
      else
        {
        {
          msw_taken_prob = prob;
          msw_taken_prob = prob;
          msw_skip_prob = REG_BR_PROB_BASE;
          msw_skip_prob = REG_BR_PROB_BASE;
          /* ??? If we have a constant op2h, should we use that when
          /* ??? If we have a constant op2h, should we use that when
             calculating lsw_taken_prob?  */
             calculating lsw_taken_prob?  */
          lsw_taken_prob = prob;
          lsw_taken_prob = prob;
        }
        }
    }
    }
  operands[1] = op1h;
  operands[1] = op1h;
  operands[2] = op2h;
  operands[2] = op2h;
  operands[4] = NULL_RTX;
  operands[4] = NULL_RTX;
  if (reload_completed
  if (reload_completed
      && ! arith_reg_or_0_operand (op2h, SImode)
      && ! arith_reg_or_0_operand (op2h, SImode)
      && (true_regnum (op1h) || (comparison != EQ && comparison != NE))
      && (true_regnum (op1h) || (comparison != EQ && comparison != NE))
      && (msw_taken != LAST_AND_UNUSED_RTX_CODE
      && (msw_taken != LAST_AND_UNUSED_RTX_CODE
          || msw_skip != LAST_AND_UNUSED_RTX_CODE))
          || msw_skip != LAST_AND_UNUSED_RTX_CODE))
    {
    {
      emit_move_insn (scratch, operands[2]);
      emit_move_insn (scratch, operands[2]);
      operands[2] = scratch;
      operands[2] = scratch;
    }
    }
  if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
  if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
    expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
    expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
  if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
  if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
    {
    {
      rtx taken_label = operands[3];
      rtx taken_label = operands[3];
 
 
      /* Operands were possibly modified, but msw_skip doesn't expect this.
      /* Operands were possibly modified, but msw_skip doesn't expect this.
         Always use the original ones.  */
         Always use the original ones.  */
      if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
      if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
        {
        {
          operands[1] = op1h;
          operands[1] = op1h;
          operands[2] = op2h;
          operands[2] = op2h;
        }
        }
 
 
      operands[3] = skip_label = gen_label_rtx ();
      operands[3] = skip_label = gen_label_rtx ();
      expand_cbranchsi4 (operands, msw_skip, msw_skip_prob);
      expand_cbranchsi4 (operands, msw_skip, msw_skip_prob);
      operands[3] = taken_label;
      operands[3] = taken_label;
    }
    }
  operands[1] = op1l;
  operands[1] = op1l;
  operands[2] = op2l;
  operands[2] = op2l;
  if (lsw_taken != LAST_AND_UNUSED_RTX_CODE)
  if (lsw_taken != LAST_AND_UNUSED_RTX_CODE)
    {
    {
      if (reload_completed
      if (reload_completed
          && ! arith_reg_or_0_operand (op2l, SImode)
          && ! arith_reg_or_0_operand (op2l, SImode)
          && (true_regnum (op1l) || (lsw_taken != EQ && lsw_taken != NE)))
          && (true_regnum (op1l) || (lsw_taken != EQ && lsw_taken != NE)))
        {
        {
          emit_move_insn (scratch, operands[2]);
          emit_move_insn (scratch, operands[2]);
          operands[2] = scratch;
          operands[2] = scratch;
        }
        }
      expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
      expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
    }
    }
  if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
  if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
    emit_label (skip_label);
    emit_label (skip_label);
  return true;
  return true;
}
}
 
 
/* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4.  */
/* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4.  */
 
 
static void
static void
sh_emit_set_t_insn (rtx insn, enum machine_mode mode)
sh_emit_set_t_insn (rtx insn, enum machine_mode mode)
{
{
  if ((TARGET_SH4 || TARGET_SH2A) && GET_MODE_CLASS (mode) == MODE_FLOAT)
  if ((TARGET_SH4 || TARGET_SH2A) && GET_MODE_CLASS (mode) == MODE_FLOAT)
    {
    {
      insn = gen_rtx_PARALLEL (VOIDmode,
      insn = gen_rtx_PARALLEL (VOIDmode,
                       gen_rtvec (2, insn,
                       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);
      (mode == SFmode ? emit_sf_insn : emit_df_insn) (insn);
    }
    }
  else
  else
    emit_insn (insn);
    emit_insn (insn);
}
}
 
 
/* Prepare the operands for an scc instruction; make sure that the
/* Prepare the operands for an scc instruction; make sure that the
   compare has been done and the result is in T_REG.  */
   compare has been done and the result is in T_REG.  */
void
void
sh_emit_scc_to_t (enum rtx_code code, rtx op0, rtx op1)
sh_emit_scc_to_t (enum rtx_code code, rtx op0, rtx op1)
{
{
  rtx t_reg = gen_rtx_REG (SImode, T_REG);
  rtx t_reg = gen_rtx_REG (SImode, T_REG);
  enum rtx_code oldcode = code;
  enum rtx_code oldcode = code;
  enum machine_mode mode;
  enum machine_mode mode;
 
 
  /* First need a compare insn.  */
  /* First need a compare insn.  */
  switch (code)
  switch (code)
    {
    {
    case NE:
    case NE:
      /* It isn't possible to handle this case.  */
      /* It isn't possible to handle this case.  */
      gcc_unreachable ();
      gcc_unreachable ();
    case LT:
    case LT:
      code = GT;
      code = GT;
      break;
      break;
    case LE:
    case LE:
      code = GE;
      code = GE;
      break;
      break;
    case LTU:
    case LTU:
      code = GTU;
      code = GTU;
      break;
      break;
    case LEU:
    case LEU:
      code = GEU;
      code = GEU;
      break;
      break;
    default:
    default:
      break;
      break;
    }
    }
  if (code != oldcode)
  if (code != oldcode)
    {
    {
      rtx tmp = op0;
      rtx tmp = op0;
      op0 = op1;
      op0 = op1;
      op1 = tmp;
      op1 = tmp;
    }
    }
 
 
  mode = GET_MODE (op0);
  mode = GET_MODE (op0);
  if (mode == VOIDmode)
  if (mode == VOIDmode)
    mode = GET_MODE (op1);
    mode = GET_MODE (op1);
 
 
  op0 = force_reg (mode, op0);
  op0 = force_reg (mode, op0);
  if ((code != EQ && code != NE
  if ((code != EQ && code != NE
       && (op1 != const0_rtx
       && (op1 != const0_rtx
           || code == GTU  || code == GEU || code == LTU || code == LEU))
           || code == GTU  || code == GEU || code == LTU || code == LEU))
      || (mode == DImode && op1 != const0_rtx)
      || (mode == DImode && op1 != const0_rtx)
      || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
      || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
    op1 = force_reg (mode, op1);
    op1 = force_reg (mode, op1);
 
 
  sh_emit_set_t_insn (gen_rtx_SET (VOIDmode, t_reg,
  sh_emit_set_t_insn (gen_rtx_SET (VOIDmode, t_reg,
                                   gen_rtx_fmt_ee (code, SImode, op0, op1)),
                                   gen_rtx_fmt_ee (code, SImode, op0, op1)),
                      mode);
                      mode);
}
}
 
 
rtx
rtx
sh_emit_cheap_store_flag (enum machine_mode mode, enum rtx_code code,
sh_emit_cheap_store_flag (enum machine_mode mode, enum rtx_code code,
                          rtx op0, rtx op1)
                          rtx op0, rtx op1)
{
{
  rtx target = gen_reg_rtx (SImode);
  rtx target = gen_reg_rtx (SImode);
  rtx tmp;
  rtx tmp;
 
 
  gcc_assert (TARGET_SHMEDIA);
  gcc_assert (TARGET_SHMEDIA);
  switch (code)
  switch (code)
    {
    {
    case EQ:
    case EQ:
    case GT:
    case GT:
    case LT:
    case LT:
    case UNORDERED:
    case UNORDERED:
    case GTU:
    case GTU:
    case LTU:
    case LTU:
      tmp = gen_rtx_fmt_ee (code, SImode, op0, op1);
      tmp = gen_rtx_fmt_ee (code, SImode, op0, op1);
      emit_insn (gen_cstore4_media (target, tmp, op0, op1));
      emit_insn (gen_cstore4_media (target, tmp, op0, op1));
      code = NE;
      code = NE;
      break;
      break;
 
 
    case NE:
    case NE:
    case GE:
    case GE:
    case LE:
    case LE:
    case ORDERED:
    case ORDERED:
    case GEU:
    case GEU:
    case LEU:
    case LEU:
      tmp = gen_rtx_fmt_ee (reverse_condition (code), mode, op0, op1);
      tmp = gen_rtx_fmt_ee (reverse_condition (code), mode, op0, op1);
      emit_insn (gen_cstore4_media (target, tmp, op0, op1));
      emit_insn (gen_cstore4_media (target, tmp, op0, op1));
      code = EQ;
      code = EQ;
      break;
      break;
 
 
    case UNEQ:
    case UNEQ:
    case UNGE:
    case UNGE:
    case UNGT:
    case UNGT:
    case UNLE:
    case UNLE:
    case UNLT:
    case UNLT:
    case LTGT:
    case LTGT:
      return NULL_RTX;
      return NULL_RTX;
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  if (mode == DImode)
  if (mode == DImode)
    {
    {
      rtx t2 = gen_reg_rtx (DImode);
      rtx t2 = gen_reg_rtx (DImode);
      emit_insn (gen_extendsidi2 (t2, target));
      emit_insn (gen_extendsidi2 (t2, target));
      target = t2;
      target = t2;
    }
    }
 
 
  return gen_rtx_fmt_ee (code, VOIDmode, target, const0_rtx);
  return gen_rtx_fmt_ee (code, VOIDmode, target, const0_rtx);
}
}
 
 
/* Called from the md file, set up the operands of a compare instruction.  */
/* Called from the md file, set up the operands of a compare instruction.  */
 
 
void
void
sh_emit_compare_and_branch (rtx *operands, enum machine_mode mode)
sh_emit_compare_and_branch (rtx *operands, enum machine_mode mode)
{
{
  enum rtx_code code = GET_CODE (operands[0]);
  enum rtx_code code = GET_CODE (operands[0]);
  enum rtx_code branch_code;
  enum rtx_code branch_code;
  rtx op0 = operands[1];
  rtx op0 = operands[1];
  rtx op1 = operands[2];
  rtx op1 = operands[2];
  rtx insn, tem;
  rtx insn, tem;
  bool need_ccmpeq = false;
  bool need_ccmpeq = false;
 
 
  if (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)
  if (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)
    {
    {
      op0 = force_reg (mode, op0);
      op0 = force_reg (mode, op0);
      op1 = force_reg (mode, op1);
      op1 = force_reg (mode, op1);
    }
    }
  else
  else
    {
    {
      if (code != EQ || mode == DImode)
      if (code != EQ || mode == DImode)
        {
        {
          /* Force args into regs, since we can't use constants here.  */
          /* Force args into regs, since we can't use constants here.  */
          op0 = force_reg (mode, op0);
          op0 = force_reg (mode, op0);
          if (op1 != const0_rtx || code == GTU  || code == GEU)
          if (op1 != const0_rtx || code == GTU  || code == GEU)
            op1 = force_reg (mode, op1);
            op1 = force_reg (mode, op1);
        }
        }
    }
    }
 
 
  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
    {
    {
      if (code == LT
      if (code == LT
          || (code == LE && TARGET_IEEE && TARGET_SH2E)
          || (code == LE && TARGET_IEEE && TARGET_SH2E)
          || (code == GE && !(TARGET_IEEE && TARGET_SH2E)))
          || (code == GE && !(TARGET_IEEE && TARGET_SH2E)))
        {
        {
          tem = op0, op0 = op1, op1 = tem;
          tem = op0, op0 = op1, op1 = tem;
          code = swap_condition (code);
          code = swap_condition (code);
        }
        }
 
 
      /* GE becomes fcmp/gt+fcmp/eq, for SH2E and TARGET_IEEE only.  */
      /* GE becomes fcmp/gt+fcmp/eq, for SH2E and TARGET_IEEE only.  */
      if (code == GE)
      if (code == GE)
        {
        {
          gcc_assert (TARGET_IEEE && TARGET_SH2E);
          gcc_assert (TARGET_IEEE && TARGET_SH2E);
          need_ccmpeq = true;
          need_ccmpeq = true;
          code = GT;
          code = GT;
        }
        }
 
 
      /* Now we can have EQ, NE, GT, LE.  NE and LE are then transformed
      /* Now we can have EQ, NE, GT, LE.  NE and LE are then transformed
         to EQ/GT respectively.  */
         to EQ/GT respectively.  */
      gcc_assert (code == EQ || code == GT || code == NE || code == LE);
      gcc_assert (code == EQ || code == GT || code == NE || code == LE);
    }
    }
 
 
  switch (code)
  switch (code)
    {
    {
    case EQ:
    case EQ:
    case GT:
    case GT:
    case GE:
    case GE:
    case GTU:
    case GTU:
    case GEU:
    case GEU:
      branch_code = code;
      branch_code = code;
      break;
      break;
    case NE:
    case NE:
    case LT:
    case LT:
    case LE:
    case LE:
    case LTU:
    case LTU:
    case LEU:
    case LEU:
      branch_code = reverse_condition (code);
      branch_code = reverse_condition (code);
      break;
      break;
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  insn = gen_rtx_SET (VOIDmode,
  insn = gen_rtx_SET (VOIDmode,
                      gen_rtx_REG (SImode, T_REG),
                      gen_rtx_REG (SImode, T_REG),
                      gen_rtx_fmt_ee (branch_code, SImode, op0, op1));
                      gen_rtx_fmt_ee (branch_code, SImode, op0, op1));
 
 
  sh_emit_set_t_insn (insn, mode);
  sh_emit_set_t_insn (insn, mode);
  if (need_ccmpeq)
  if (need_ccmpeq)
    sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode);
    sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode);
 
 
  if (branch_code == code)
  if (branch_code == code)
    emit_jump_insn (gen_branch_true (operands[3]));
    emit_jump_insn (gen_branch_true (operands[3]));
  else
  else
    emit_jump_insn (gen_branch_false (operands[3]));
    emit_jump_insn (gen_branch_false (operands[3]));
}
}
 
 
void
void
sh_emit_compare_and_set (rtx *operands, enum machine_mode mode)
sh_emit_compare_and_set (rtx *operands, enum machine_mode mode)
{
{
  enum rtx_code code = GET_CODE (operands[1]);
  enum rtx_code code = GET_CODE (operands[1]);
  rtx op0 = operands[2];
  rtx op0 = operands[2];
  rtx op1 = operands[3];
  rtx op1 = operands[3];
  rtx lab = NULL_RTX;
  rtx lab = NULL_RTX;
  bool invert = false;
  bool invert = false;
  rtx tem;
  rtx tem;
 
 
  op0 = force_reg (mode, op0);
  op0 = force_reg (mode, op0);
  if ((code != EQ && code != NE
  if ((code != EQ && code != NE
       && (op1 != const0_rtx
       && (op1 != const0_rtx
           || code == GTU  || code == GEU || code == LTU || code == LEU))
           || code == GTU  || code == GEU || code == LTU || code == LEU))
      || (mode == DImode && op1 != const0_rtx)
      || (mode == DImode && op1 != const0_rtx)
      || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
      || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
    op1 = force_reg (mode, op1);
    op1 = force_reg (mode, op1);
 
 
  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
    {
    {
      if (code == LT || code == LE)
      if (code == LT || code == LE)
        {
        {
          code = swap_condition (code);
          code = swap_condition (code);
          tem = op0, op0 = op1, op1 = tem;
          tem = op0, op0 = op1, op1 = tem;
        }
        }
      if (code == GE)
      if (code == GE)
        {
        {
          if (TARGET_IEEE)
          if (TARGET_IEEE)
            {
            {
              lab = gen_label_rtx ();
              lab = gen_label_rtx ();
              sh_emit_scc_to_t (EQ, op0, op1);
              sh_emit_scc_to_t (EQ, op0, op1);
              emit_jump_insn (gen_branch_true (lab));
              emit_jump_insn (gen_branch_true (lab));
              code = GT;
              code = GT;
           }
           }
          else
          else
            {
            {
              code = LT;
              code = LT;
              invert = true;
              invert = true;
            }
            }
        }
        }
    }
    }
 
 
  if (code == NE)
  if (code == NE)
    {
    {
      code = EQ;
      code = EQ;
      invert = true;
      invert = true;
    }
    }
 
 
  sh_emit_scc_to_t (code, op0, op1);
  sh_emit_scc_to_t (code, op0, op1);
  if (lab)
  if (lab)
    emit_label (lab);
    emit_label (lab);
  if (invert)
  if (invert)
    emit_insn (gen_movnegt (operands[0]));
    emit_insn (gen_movnegt (operands[0]));
  else
  else
    emit_move_insn (operands[0], gen_rtx_REG (SImode, T_REG));
    emit_move_insn (operands[0], gen_rtx_REG (SImode, T_REG));
}
}


/* Functions to output assembly code.  */
/* Functions to output assembly code.  */
 
 
/* Return a sequence of instructions to perform DI or DF move.
/* Return a sequence of instructions to perform DI or DF move.
 
 
   Since the SH cannot move a DI or DF in one instruction, we have
   Since the SH cannot move a DI or DF in one instruction, we have
   to take care when we see overlapping source and dest registers.  */
   to take care when we see overlapping source and dest registers.  */
 
 
const char *
const char *
output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
                   enum machine_mode mode)
                   enum machine_mode mode)
{
{
  rtx dst = operands[0];
  rtx dst = operands[0];
  rtx src = operands[1];
  rtx src = operands[1];
 
 
  if (MEM_P (dst)
  if (MEM_P (dst)
      && GET_CODE (XEXP (dst, 0)) == PRE_DEC)
      && GET_CODE (XEXP (dst, 0)) == PRE_DEC)
    return "mov.l       %T1,%0\n\tmov.l %1,%0";
    return "mov.l       %T1,%0\n\tmov.l %1,%0";
 
 
  if (register_operand (dst, mode)
  if (register_operand (dst, mode)
      && register_operand (src, mode))
      && register_operand (src, mode))
    {
    {
      if (REGNO (src) == MACH_REG)
      if (REGNO (src) == MACH_REG)
        return "sts     mach,%S0\n\tsts macl,%R0";
        return "sts     mach,%S0\n\tsts macl,%R0";
 
 
      /* When mov.d r1,r2 do r2->r3 then r1->r2;
      /* When mov.d r1,r2 do r2->r3 then r1->r2;
         when mov.d r1,r0 do r1->r0 then r2->r1.  */
         when mov.d r1,r0 do r1->r0 then r2->r1.  */
 
 
      if (REGNO (src) + 1 == REGNO (dst))
      if (REGNO (src) + 1 == REGNO (dst))
        return "mov     %T1,%T0\n\tmov  %1,%0";
        return "mov     %T1,%T0\n\tmov  %1,%0";
      else
      else
        return "mov     %1,%0\n\tmov    %T1,%T0";
        return "mov     %1,%0\n\tmov    %T1,%T0";
    }
    }
  else if (CONST_INT_P (src))
  else if (CONST_INT_P (src))
    {
    {
      if (INTVAL (src) < 0)
      if (INTVAL (src) < 0)
        output_asm_insn ("mov   #-1,%S0", operands);
        output_asm_insn ("mov   #-1,%S0", operands);
      else
      else
        output_asm_insn ("mov   #0,%S0", operands);
        output_asm_insn ("mov   #0,%S0", operands);
 
 
      return "mov       %1,%R0";
      return "mov       %1,%R0";
    }
    }
  else if (MEM_P (src))
  else if (MEM_P (src))
    {
    {
      int ptrreg = -1;
      int ptrreg = -1;
      int dreg = REGNO (dst);
      int dreg = REGNO (dst);
      rtx inside = XEXP (src, 0);
      rtx inside = XEXP (src, 0);
 
 
      switch (GET_CODE (inside))
      switch (GET_CODE (inside))
        {
        {
        case REG:
        case REG:
          ptrreg = REGNO (inside);
          ptrreg = REGNO (inside);
          break;
          break;
 
 
        case SUBREG:
        case SUBREG:
          ptrreg = subreg_regno (inside);
          ptrreg = subreg_regno (inside);
          break;
          break;
 
 
        case PLUS:
        case PLUS:
          ptrreg = REGNO (XEXP (inside, 0));
          ptrreg = REGNO (XEXP (inside, 0));
          /* ??? A r0+REG address shouldn't be possible here, because it isn't
          /* ??? A r0+REG address shouldn't be possible here, because it isn't
             an offsettable address.  Unfortunately, offsettable addresses use
             an offsettable address.  Unfortunately, offsettable addresses use
             QImode to check the offset, and a QImode offsettable address
             QImode to check the offset, and a QImode offsettable address
             requires r0 for the other operand, which is not currently
             requires r0 for the other operand, which is not currently
             supported, so we can't use the 'o' constraint.
             supported, so we can't use the 'o' constraint.
             Thus we must check for and handle r0+REG addresses here.
             Thus we must check for and handle r0+REG addresses here.
             We punt for now, since this is likely very rare.  */
             We punt for now, since this is likely very rare.  */
          gcc_assert (!REG_P (XEXP (inside, 1)));
          gcc_assert (!REG_P (XEXP (inside, 1)));
          break;
          break;
 
 
        case LABEL_REF:
        case LABEL_REF:
          return "mov.l %1,%0\n\tmov.l  %1+4,%T0";
          return "mov.l %1,%0\n\tmov.l  %1+4,%T0";
        case POST_INC:
        case POST_INC:
          return "mov.l %1,%0\n\tmov.l  %1,%T0";
          return "mov.l %1,%0\n\tmov.l  %1,%T0";
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
      /* Work out the safe way to copy.  Copy into the second half first.  */
      /* Work out the safe way to copy.  Copy into the second half first.  */
      if (dreg == ptrreg)
      if (dreg == ptrreg)
        return "mov.l   %T1,%T0\n\tmov.l        %1,%0";
        return "mov.l   %T1,%T0\n\tmov.l        %1,%0";
    }
    }
 
 
  return "mov.l %1,%0\n\tmov.l  %T1,%T0";
  return "mov.l %1,%0\n\tmov.l  %T1,%T0";
}
}
 
 
/* Print an instruction which would have gone into a delay slot after
/* Print an instruction which would have gone into a delay slot after
   another instruction, but couldn't because the other instruction expanded
   another instruction, but couldn't because the other instruction expanded
   into a sequence where putting the slot insn at the end wouldn't work.  */
   into a sequence where putting the slot insn at the end wouldn't work.  */
 
 
static void
static void
print_slot (rtx insn)
print_slot (rtx insn)
{
{
  final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 1, NULL);
  final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 1, NULL);
 
 
  INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
  INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
}
}
 
 
const char *
const char *
output_far_jump (rtx insn, rtx op)
output_far_jump (rtx insn, rtx op)
{
{
  struct { rtx lab, reg, op; } this_jmp;
  struct { rtx lab, reg, op; } this_jmp;
  rtx braf_base_lab = NULL_RTX;
  rtx braf_base_lab = NULL_RTX;
  const char *jump;
  const char *jump;
  int far;
  int far;
  int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
  int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
  rtx prev;
  rtx prev;
 
 
  this_jmp.lab = gen_label_rtx ();
  this_jmp.lab = gen_label_rtx ();
 
 
  if (TARGET_SH2
  if (TARGET_SH2
      && offset >= -32764
      && offset >= -32764
      && offset - get_attr_length (insn) <= 32766)
      && offset - get_attr_length (insn) <= 32766)
    {
    {
      far = 0;
      far = 0;
      jump = "mov.w     %O0,%1; braf    %1";
      jump = "mov.w     %O0,%1; braf    %1";
    }
    }
  else
  else
    {
    {
      far = 1;
      far = 1;
      if (flag_pic)
      if (flag_pic)
        {
        {
          if (TARGET_SH2)
          if (TARGET_SH2)
            jump = "mov.l       %O0,%1; braf    %1";
            jump = "mov.l       %O0,%1; braf    %1";
          else
          else
            jump = "mov.l       r0,@-r15; mova  %O0,r0; mov.l   @r0,%1; add     r0,%1; mov.l    @r15+,r0; jmp   @%1";
            jump = "mov.l       r0,@-r15; mova  %O0,r0; mov.l   @r0,%1; add     r0,%1; mov.l    @r15+,r0; jmp   @%1";
        }
        }
      else
      else
        jump = "mov.l   %O0,%1; jmp     @%1";
        jump = "mov.l   %O0,%1; jmp     @%1";
    }
    }
  /* If we have a scratch register available, use it.  */
  /* If we have a scratch register available, use it.  */
  if (NONJUMP_INSN_P ((prev = prev_nonnote_insn (insn)))
  if (NONJUMP_INSN_P ((prev = prev_nonnote_insn (insn)))
      && INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
      && INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
    {
    {
      this_jmp.reg = SET_DEST (XVECEXP (PATTERN (prev), 0, 0));
      this_jmp.reg = SET_DEST (XVECEXP (PATTERN (prev), 0, 0));
      if (REGNO (this_jmp.reg) == R0_REG && flag_pic && ! TARGET_SH2)
      if (REGNO (this_jmp.reg) == R0_REG && flag_pic && ! TARGET_SH2)
        jump = "mov.l   r1,@-r15; mova  %O0,r0; mov.l   @r0,r1; add     r1,r0; mov.l    @r15+,r1; jmp   @%1";
        jump = "mov.l   r1,@-r15; mova  %O0,r0; mov.l   @r0,r1; add     r1,r0; mov.l    @r15+,r1; jmp   @%1";
      output_asm_insn (jump, &this_jmp.lab);
      output_asm_insn (jump, &this_jmp.lab);
      if (dbr_sequence_length ())
      if (dbr_sequence_length ())
        print_slot (final_sequence);
        print_slot (final_sequence);
      else
      else
        output_asm_insn ("nop", 0);
        output_asm_insn ("nop", 0);
    }
    }
  else
  else
    {
    {
      /* Output the delay slot insn first if any.  */
      /* Output the delay slot insn first if any.  */
      if (dbr_sequence_length ())
      if (dbr_sequence_length ())
        print_slot (final_sequence);
        print_slot (final_sequence);
 
 
      this_jmp.reg = gen_rtx_REG (SImode, 13);
      this_jmp.reg = gen_rtx_REG (SImode, 13);
      /* We must keep the stack aligned to 8-byte boundaries on SH5.
      /* We must keep the stack aligned to 8-byte boundaries on SH5.
         Fortunately, MACL is fixed and call-clobbered, and we never
         Fortunately, MACL is fixed and call-clobbered, and we never
         need its value across jumps, so save r13 in it instead of in
         need its value across jumps, so save r13 in it instead of in
         the stack.  */
         the stack.  */
      if (TARGET_SH5)
      if (TARGET_SH5)
        output_asm_insn ("lds   r13, macl", 0);
        output_asm_insn ("lds   r13, macl", 0);
      else
      else
        output_asm_insn ("mov.l r13,@-r15", 0);
        output_asm_insn ("mov.l r13,@-r15", 0);
      output_asm_insn (jump, &this_jmp.lab);
      output_asm_insn (jump, &this_jmp.lab);
      if (TARGET_SH5)
      if (TARGET_SH5)
        output_asm_insn ("sts   macl, r13", 0);
        output_asm_insn ("sts   macl, r13", 0);
      else
      else
        output_asm_insn ("mov.l @r15+,r13", 0);
        output_asm_insn ("mov.l @r15+,r13", 0);
    }
    }
  if (far && flag_pic && TARGET_SH2)
  if (far && flag_pic && TARGET_SH2)
    {
    {
      braf_base_lab = gen_label_rtx ();
      braf_base_lab = gen_label_rtx ();
      (*targetm.asm_out.internal_label) (asm_out_file, "L",
      (*targetm.asm_out.internal_label) (asm_out_file, "L",
                                 CODE_LABEL_NUMBER (braf_base_lab));
                                 CODE_LABEL_NUMBER (braf_base_lab));
    }
    }
  if (far)
  if (far)
    output_asm_insn (".align    2", 0);
    output_asm_insn (".align    2", 0);
  (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (this_jmp.lab));
  (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (this_jmp.lab));
  this_jmp.op = op;
  this_jmp.op = op;
  if (far && flag_pic)
  if (far && flag_pic)
    {
    {
      if (TARGET_SH2)
      if (TARGET_SH2)
        this_jmp.lab = braf_base_lab;
        this_jmp.lab = braf_base_lab;
      output_asm_insn (".long   %O2-%O0", &this_jmp.lab);
      output_asm_insn (".long   %O2-%O0", &this_jmp.lab);
    }
    }
  else
  else
    output_asm_insn (far ? ".long       %O2" : ".word %O2-%O0", &this_jmp.lab);
    output_asm_insn (far ? ".long       %O2" : ".word %O2-%O0", &this_jmp.lab);
  return "";
  return "";
}
}
 
 
/* Local label counter, used for constants in the pool and inside
/* Local label counter, used for constants in the pool and inside
   pattern branches.  */
   pattern branches.  */
 
 
static int lf = 100;
static int lf = 100;
 
 
/* Output code for ordinary branches.  */
/* Output code for ordinary branches.  */
 
 
const char *
const char *
output_branch (int logic, rtx insn, rtx *operands)
output_branch (int logic, rtx insn, rtx *operands)
{
{
  switch (get_attr_length (insn))
  switch (get_attr_length (insn))
    {
    {
    case 6:
    case 6:
      /* This can happen if filling the delay slot has caused a forward
      /* This can happen if filling the delay slot has caused a forward
         branch to exceed its range (we could reverse it, but only
         branch to exceed its range (we could reverse it, but only
         when we know we won't overextend other branches; this should
         when we know we won't overextend other branches; this should
         best be handled by relaxation).
         best be handled by relaxation).
         It can also happen when other condbranches hoist delay slot insn
         It can also happen when other condbranches hoist delay slot insn
         from their destination, thus leading to code size increase.
         from their destination, thus leading to code size increase.
         But the branch will still be in the range -4092..+4098 bytes.  */
         But the branch will still be in the range -4092..+4098 bytes.  */
 
 
      if (! TARGET_RELAX)
      if (! TARGET_RELAX)
        {
        {
          int label = lf++;
          int label = lf++;
          /* The call to print_slot will clobber the operands.  */
          /* The call to print_slot will clobber the operands.  */
          rtx op0 = operands[0];
          rtx op0 = operands[0];
 
 
          /* If the instruction in the delay slot is annulled (true), then
          /* If the instruction in the delay slot is annulled (true), then
             there is no delay slot where we can put it now.  The only safe
             there is no delay slot where we can put it now.  The only safe
             place for it is after the label.  final will do that by default.  */
             place for it is after the label.  final will do that by default.  */
 
 
          if (final_sequence
          if (final_sequence
              && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
              && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
              && get_attr_length (XVECEXP (final_sequence, 0, 1)))
              && get_attr_length (XVECEXP (final_sequence, 0, 1)))
            {
            {
              asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n", logic ? "f" : "t",
              asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n", logic ? "f" : "t",
                           ASSEMBLER_DIALECT ? "/" : ".", label);
                           ASSEMBLER_DIALECT ? "/" : ".", label);
              print_slot (final_sequence);
              print_slot (final_sequence);
            }
            }
          else
          else
            asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t", label);
            asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t", label);
 
 
          output_asm_insn ("bra\t%l0", &op0);
          output_asm_insn ("bra\t%l0", &op0);
          fprintf (asm_out_file, "\tnop\n");
          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 "";
          return "";
        }
        }
      /* When relaxing, handle this like a short branch.  The linker
      /* When relaxing, handle this like a short branch.  The linker
         will fix it up if it still doesn't fit after relaxation.  */
         will fix it up if it still doesn't fit after relaxation.  */
    case 2:
    case 2:
      return logic ? "bt%.\t%l0" : "bf%.\t%l0";
      return logic ? "bt%.\t%l0" : "bf%.\t%l0";
 
 
      /* These are for SH2e, in which we have to account for the
      /* These are for SH2e, in which we have to account for the
         extra nop because of the hardware bug in annulled branches.  */
         extra nop because of the hardware bug in annulled branches.  */
    case 8:
    case 8:
      if (! TARGET_RELAX)
      if (! TARGET_RELAX)
        {
        {
          int label = lf++;
          int label = lf++;
 
 
          gcc_assert (!final_sequence
          gcc_assert (!final_sequence
                      || !(INSN_ANNULLED_BRANCH_P
                      || !(INSN_ANNULLED_BRANCH_P
                           (XVECEXP (final_sequence, 0, 0))));
                           (XVECEXP (final_sequence, 0, 0))));
          asm_fprintf (asm_out_file, "b%s%ss\t%LLF%d\n",
          asm_fprintf (asm_out_file, "b%s%ss\t%LLF%d\n",
                       logic ? "f" : "t",
                       logic ? "f" : "t",
                       ASSEMBLER_DIALECT ? "/" : ".", label);
                       ASSEMBLER_DIALECT ? "/" : ".", label);
          fprintf (asm_out_file, "\tnop\n");
          fprintf (asm_out_file, "\tnop\n");
          output_asm_insn ("bra\t%l0", operands);
          output_asm_insn ("bra\t%l0", operands);
          fprintf (asm_out_file, "\tnop\n");
          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 "";
          return "";
        }
        }
      /* When relaxing, fall through.  */
      /* When relaxing, fall through.  */
    case 4:
    case 4:
      {
      {
        char buffer[10];
        char buffer[10];
 
 
        sprintf (buffer, "b%s%ss\t%%l0",
        sprintf (buffer, "b%s%ss\t%%l0",
                 logic ? "t" : "f",
                 logic ? "t" : "f",
                 ASSEMBLER_DIALECT ? "/" : ".");
                 ASSEMBLER_DIALECT ? "/" : ".");
        output_asm_insn (buffer, &operands[0]);
        output_asm_insn (buffer, &operands[0]);
        return "nop";
        return "nop";
      }
      }
 
 
    default:
    default:
      /* There should be no longer branches now - that would
      /* There should be no longer branches now - that would
         indicate that something has destroyed the branches set
         indicate that something has destroyed the branches set
         up in machine_dependent_reorg.  */
         up in machine_dependent_reorg.  */
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
/* Output a code sequence for INSN using TEMPL with OPERANDS; but before,
/* Output a code sequence for INSN using TEMPL with OPERANDS; but before,
   fill in operands 9 as a label to the successor insn.
   fill in operands 9 as a label to the successor insn.
   We try to use jump threading where possible.
   We try to use jump threading where possible.
   IF CODE matches the comparison in the IF_THEN_ELSE of a following jump,
   IF CODE matches the comparison in the IF_THEN_ELSE of a following jump,
   we assume the jump is taken.  I.e. EQ means follow jmp and bf, NE means
   we assume the jump is taken.  I.e. EQ means follow jmp and bf, NE means
   follow jmp and bt, if the address is in range.  */
   follow jmp and bt, if the address is in range.  */
const char *
const char *
output_branchy_insn (enum rtx_code code, const char *templ,
output_branchy_insn (enum rtx_code code, const char *templ,
                     rtx insn, rtx *operands)
                     rtx insn, rtx *operands)
{
{
  rtx next_insn = NEXT_INSN (insn);
  rtx next_insn = NEXT_INSN (insn);
 
 
  if (next_insn && JUMP_P (next_insn) && condjump_p (next_insn))
  if (next_insn && JUMP_P (next_insn) && condjump_p (next_insn))
    {
    {
      rtx src = SET_SRC (PATTERN (next_insn));
      rtx src = SET_SRC (PATTERN (next_insn));
      if (GET_CODE (src) == IF_THEN_ELSE && GET_CODE (XEXP (src, 0)) != code)
      if (GET_CODE (src) == IF_THEN_ELSE && GET_CODE (XEXP (src, 0)) != code)
        {
        {
          /* Following branch not taken */
          /* Following branch not taken */
          operands[9] = gen_label_rtx ();
          operands[9] = gen_label_rtx ();
          emit_label_after (operands[9], next_insn);
          emit_label_after (operands[9], next_insn);
          INSN_ADDRESSES_NEW (operands[9],
          INSN_ADDRESSES_NEW (operands[9],
                              INSN_ADDRESSES (INSN_UID (next_insn))
                              INSN_ADDRESSES (INSN_UID (next_insn))
                              + get_attr_length (next_insn));
                              + get_attr_length (next_insn));
          return templ;
          return templ;
        }
        }
      else
      else
        {
        {
          int offset = (branch_dest (next_insn)
          int offset = (branch_dest (next_insn)
                        - INSN_ADDRESSES (INSN_UID (next_insn)) + 4);
                        - INSN_ADDRESSES (INSN_UID (next_insn)) + 4);
          if (offset >= -252 && offset <= 258)
          if (offset >= -252 && offset <= 258)
            {
            {
              if (GET_CODE (src) == IF_THEN_ELSE)
              if (GET_CODE (src) == IF_THEN_ELSE)
                /* branch_true */
                /* branch_true */
                src = XEXP (src, 1);
                src = XEXP (src, 1);
              operands[9] = src;
              operands[9] = src;
              return templ;
              return templ;
            }
            }
        }
        }
    }
    }
  operands[9] = gen_label_rtx ();
  operands[9] = gen_label_rtx ();
  emit_label_after (operands[9], insn);
  emit_label_after (operands[9], insn);
  INSN_ADDRESSES_NEW (operands[9],
  INSN_ADDRESSES_NEW (operands[9],
                      INSN_ADDRESSES (INSN_UID (insn))
                      INSN_ADDRESSES (INSN_UID (insn))
                      + get_attr_length (insn));
                      + get_attr_length (insn));
  return templ;
  return templ;
}
}
 
 
const char *
const char *
output_ieee_ccmpeq (rtx insn, rtx *operands)
output_ieee_ccmpeq (rtx insn, rtx *operands)
{
{
  return output_branchy_insn (NE, "bt\t%l9\n\tfcmp/eq\t%1,%0",
  return output_branchy_insn (NE, "bt\t%l9\n\tfcmp/eq\t%1,%0",
                              insn, operands);
                              insn, operands);
}
}


/* Output the start of the assembler file.  */
/* Output the start of the assembler file.  */
 
 
static void
static void
sh_file_start (void)
sh_file_start (void)
{
{
  default_file_start ();
  default_file_start ();
 
 
#ifdef SYMBIAN
#ifdef SYMBIAN
  /* Declare the .directive section before it is used.  */
  /* Declare the .directive section before it is used.  */
  fputs ("\t.section .directive, \"SM\", @progbits, 1\n", asm_out_file);
  fputs ("\t.section .directive, \"SM\", @progbits, 1\n", asm_out_file);
  fputs ("\t.asciz \"#<SYMEDIT>#\\n\"\n", asm_out_file);
  fputs ("\t.asciz \"#<SYMEDIT>#\\n\"\n", asm_out_file);
#endif
#endif
 
 
  if (TARGET_ELF)
  if (TARGET_ELF)
    /* We need to show the text section with the proper
    /* We need to show the text section with the proper
       attributes as in TEXT_SECTION_ASM_OP, before dwarf2out
       attributes as in TEXT_SECTION_ASM_OP, before dwarf2out
       emits it without attributes in TEXT_SECTION_ASM_OP, else GAS
       emits it without attributes in TEXT_SECTION_ASM_OP, else GAS
       will complain.  We can teach GAS specifically about the
       will complain.  We can teach GAS specifically about the
       default attributes for our choice of text section, but
       default attributes for our choice of text section, but
       then we would have to change GAS again if/when we change
       then we would have to change GAS again if/when we change
       the text section name.  */
       the text section name.  */
    fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
    fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
  else
  else
    /* Switch to the data section so that the coffsem symbol
    /* Switch to the data section so that the coffsem symbol
       isn't in the text section.  */
       isn't in the text section.  */
    switch_to_section (data_section);
    switch_to_section (data_section);
 
 
  if (TARGET_LITTLE_ENDIAN)
  if (TARGET_LITTLE_ENDIAN)
    fputs ("\t.little\n", asm_out_file);
    fputs ("\t.little\n", asm_out_file);
 
 
  if (!TARGET_ELF)
  if (!TARGET_ELF)
    {
    {
      if (TARGET_SHCOMPACT)
      if (TARGET_SHCOMPACT)
        fputs ("\t.mode\tSHcompact\n", asm_out_file);
        fputs ("\t.mode\tSHcompact\n", asm_out_file);
      else if (TARGET_SHMEDIA)
      else if (TARGET_SHMEDIA)
        fprintf (asm_out_file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
        fprintf (asm_out_file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
                 TARGET_SHMEDIA64 ? 64 : 32);
                 TARGET_SHMEDIA64 ? 64 : 32);
    }
    }
}
}


/* Check if PAT includes UNSPEC_CALLER unspec pattern.  */
/* Check if PAT includes UNSPEC_CALLER unspec pattern.  */
 
 
static bool
static bool
unspec_caller_rtx_p (rtx pat)
unspec_caller_rtx_p (rtx pat)
{
{
  rtx base, offset;
  rtx base, offset;
  int i;
  int i;
 
 
  split_const (pat, &base, &offset);
  split_const (pat, &base, &offset);
  if (GET_CODE (base) == UNSPEC)
  if (GET_CODE (base) == UNSPEC)
    {
    {
      if (XINT (base, 1) == UNSPEC_CALLER)
      if (XINT (base, 1) == UNSPEC_CALLER)
        return true;
        return true;
      for (i = 0; i < XVECLEN (base, 0); i++)
      for (i = 0; i < XVECLEN (base, 0); i++)
        if (unspec_caller_rtx_p (XVECEXP (base, 0, i)))
        if (unspec_caller_rtx_p (XVECEXP (base, 0, i)))
          return true;
          return true;
    }
    }
  return false;
  return false;
}
}
 
 
/* Indicate that INSN cannot be duplicated.  This is true for insn
/* Indicate that INSN cannot be duplicated.  This is true for insn
   that generates a unique label.  */
   that generates a unique label.  */
 
 
static bool
static bool
sh_cannot_copy_insn_p (rtx insn)
sh_cannot_copy_insn_p (rtx insn)
{
{
  rtx pat;
  rtx pat;
 
 
  if (!reload_completed || !flag_pic)
  if (!reload_completed || !flag_pic)
    return false;
    return false;
 
 
  if (!NONJUMP_INSN_P (insn))
  if (!NONJUMP_INSN_P (insn))
    return false;
    return false;
  if (asm_noperands (insn) >= 0)
  if (asm_noperands (insn) >= 0)
    return false;
    return false;
 
 
  pat = PATTERN (insn);
  pat = PATTERN (insn);
  if (GET_CODE (pat) != SET)
  if (GET_CODE (pat) != SET)
    return false;
    return false;
  pat = SET_SRC (pat);
  pat = SET_SRC (pat);
 
 
  if (unspec_caller_rtx_p (pat))
  if (unspec_caller_rtx_p (pat))
    return true;
    return true;
 
 
  return false;
  return false;
}
}


/* Actual number of instructions used to make a shift by N.  */
/* Actual number of instructions used to make a shift by N.  */
static const char ashiftrt_insns[] =
static const char ashiftrt_insns[] =
  { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};
  { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};
 
 
/* Left shift and logical right shift are the same.  */
/* Left shift and logical right shift are the same.  */
static const char shift_insns[]    =
static const char shift_insns[]    =
  { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,3,3,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
  { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,3,3,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
 
 
/* Individual shift amounts needed to get the above length sequences.
/* Individual shift amounts needed to get the above length sequences.
   One bit right shifts clobber the T bit, so when possible, put one bit
   One bit right shifts clobber the T bit, so when possible, put one bit
   shifts in the middle of the sequence, so the ends are eligible for
   shifts in the middle of the sequence, so the ends are eligible for
   branch delay slots.  */
   branch delay slots.  */
static const short shift_amounts[32][5] = {
static const short shift_amounts[32][5] = {
  {0}, {1}, {2}, {2, 1},
  {0}, {1}, {2}, {2, 1},
  {2, 2}, {2, 1, 2}, {2, 2, 2}, {2, 2, 1, 2},
  {2, 2}, {2, 1, 2}, {2, 2, 2}, {2, 2, 1, 2},
  {8}, {8, 1}, {8, 2}, {8, 1, 2},
  {8}, {8, 1}, {8, 2}, {8, 1, 2},
  {8, 2, 2}, {8, 2, 1, 2}, {8, -2, 8}, {8, -1, 8},
  {8, 2, 2}, {8, 2, 1, 2}, {8, -2, 8}, {8, -1, 8},
  {16}, {16, 1}, {16, 2}, {16, 1, 2},
  {16}, {16, 1}, {16, 2}, {16, 1, 2},
  {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
  {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
  {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
  {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
  {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
  {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
 
 
/* Likewise, but for shift amounts < 16, up to three highmost bits
/* Likewise, but for shift amounts < 16, up to three highmost bits
   might be clobbered.  This is typically used when combined with some
   might be clobbered.  This is typically used when combined with some
   kind of sign or zero extension.  */
   kind of sign or zero extension.  */
 
 
static const char ext_shift_insns[]    =
static const char ext_shift_insns[]    =
  { 0,1,1,2,2,3,2,2,1,2,2,3,3,3,2,2,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
  { 0,1,1,2,2,3,2,2,1,2,2,3,3,3,2,2,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
 
 
static const short ext_shift_amounts[32][4] = {
static const short ext_shift_amounts[32][4] = {
  {0}, {1}, {2}, {2, 1},
  {0}, {1}, {2}, {2, 1},
  {2, 2}, {2, 1, 2}, {8, -2}, {8, -1},
  {2, 2}, {2, 1, 2}, {8, -2}, {8, -1},
  {8}, {8, 1}, {8, 2}, {8, 1, 2},
  {8}, {8, 1}, {8, 2}, {8, 1, 2},
  {8, 2, 2}, {16, -2, -1}, {16, -2}, {16, -1},
  {8, 2, 2}, {16, -2, -1}, {16, -2}, {16, -1},
  {16}, {16, 1}, {16, 2}, {16, 1, 2},
  {16}, {16, 1}, {16, 2}, {16, 1, 2},
  {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
  {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
  {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
  {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
  {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
  {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
 
 
/* Assuming we have a value that has been sign-extended by at least one bit,
/* Assuming we have a value that has been sign-extended by at least one bit,
   can we use the ext_shift_amounts with the last shift turned to an arithmetic shift
   can we use the ext_shift_amounts with the last shift turned to an arithmetic shift
   to shift it by N without data loss, and quicker than by other means?  */
   to shift it by N without data loss, and quicker than by other means?  */
#define EXT_SHIFT_SIGNED(n) (((n) | 8) == 15)
#define EXT_SHIFT_SIGNED(n) (((n) | 8) == 15)
 
 
/* This is used in length attributes in sh.md to help compute the length
/* This is used in length attributes in sh.md to help compute the length
   of arbitrary constant shift instructions.  */
   of arbitrary constant shift instructions.  */
 
 
int
int
shift_insns_rtx (rtx insn)
shift_insns_rtx (rtx insn)
{
{
  rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  int shift_count = INTVAL (XEXP (set_src, 1)) & 31;
  int shift_count = INTVAL (XEXP (set_src, 1)) & 31;
  enum rtx_code shift_code = GET_CODE (set_src);
  enum rtx_code shift_code = GET_CODE (set_src);
 
 
  switch (shift_code)
  switch (shift_code)
    {
    {
    case ASHIFTRT:
    case ASHIFTRT:
      return ashiftrt_insns[shift_count];
      return ashiftrt_insns[shift_count];
    case LSHIFTRT:
    case LSHIFTRT:
    case ASHIFT:
    case ASHIFT:
      return shift_insns[shift_count];
      return shift_insns[shift_count];
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
/* Return the cost of a shift.  */
/* Return the cost of a shift.  */
 
 
static inline int
static inline int
shiftcosts (rtx x)
shiftcosts (rtx x)
{
{
  int value;
  int value;
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    return 1;
    return 1;
 
 
  if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
  if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
    {
    {
      if (GET_MODE (x) == DImode
      if (GET_MODE (x) == DImode
          && CONST_INT_P (XEXP (x, 1))
          && CONST_INT_P (XEXP (x, 1))
          && INTVAL (XEXP (x, 1)) == 1)
          && INTVAL (XEXP (x, 1)) == 1)
        return 2;
        return 2;
 
 
      /* Everything else is invalid, because there is no pattern for it.  */
      /* Everything else is invalid, because there is no pattern for it.  */
      return MAX_COST;
      return MAX_COST;
    }
    }
  /* If shift by a non constant, then this will be expensive.  */
  /* If shift by a non constant, then this will be expensive.  */
  if (!CONST_INT_P (XEXP (x, 1)))
  if (!CONST_INT_P (XEXP (x, 1)))
    return SH_DYNAMIC_SHIFT_COST;
    return SH_DYNAMIC_SHIFT_COST;
 
 
  /* Otherwise, return the true cost in instructions.  Cope with out of range
  /* Otherwise, return the true cost in instructions.  Cope with out of range
     shift counts more or less arbitrarily.  */
     shift counts more or less arbitrarily.  */
  value = INTVAL (XEXP (x, 1)) & 31;
  value = INTVAL (XEXP (x, 1)) & 31;
 
 
  if (GET_CODE (x) == ASHIFTRT)
  if (GET_CODE (x) == ASHIFTRT)
    {
    {
      int cost = ashiftrt_insns[value];
      int cost = ashiftrt_insns[value];
      /* If SH3, then we put the constant in a reg and use shad.  */
      /* If SH3, then we put the constant in a reg and use shad.  */
      if (cost > 1 + SH_DYNAMIC_SHIFT_COST)
      if (cost > 1 + SH_DYNAMIC_SHIFT_COST)
        cost = 1 + SH_DYNAMIC_SHIFT_COST;
        cost = 1 + SH_DYNAMIC_SHIFT_COST;
      return cost;
      return cost;
    }
    }
  else
  else
    return shift_insns[value];
    return shift_insns[value];
}
}
 
 
/* Return the cost of an AND operation.  */
/* Return the cost of an AND operation.  */
 
 
static inline int
static inline int
andcosts (rtx x)
andcosts (rtx x)
{
{
  int i;
  int i;
 
 
  /* Anding with a register is a single cycle and instruction.  */
  /* Anding with a register is a single cycle and instruction.  */
  if (!CONST_INT_P (XEXP (x, 1)))
  if (!CONST_INT_P (XEXP (x, 1)))
    return 1;
    return 1;
 
 
  i = INTVAL (XEXP (x, 1));
  i = INTVAL (XEXP (x, 1));
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    {
    {
      if (satisfies_constraint_I10 (XEXP (x, 1))
      if (satisfies_constraint_I10 (XEXP (x, 1))
          || satisfies_constraint_J16 (XEXP (x, 1)))
          || satisfies_constraint_J16 (XEXP (x, 1)))
        return 1;
        return 1;
      else
      else
        return 1 + rtx_cost (XEXP (x, 1), AND, !optimize_size);
        return 1 + rtx_cost (XEXP (x, 1), AND, !optimize_size);
    }
    }
 
 
  /* These constants are single cycle extu.[bw] instructions.  */
  /* These constants are single cycle extu.[bw] instructions.  */
  if (i == 0xff || i == 0xffff)
  if (i == 0xff || i == 0xffff)
    return 1;
    return 1;
  /* Constants that can be used in an and immediate instruction in 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.  */
     cycle, but this requires r0, so make it a little more expensive.  */
  if (CONST_OK_FOR_K08 (i))
  if (CONST_OK_FOR_K08 (i))
    return 2;
    return 2;
  /* Constants that can be loaded with a mov immediate and an and.
  /* Constants that can be loaded with a mov immediate and an and.
     This case is probably unnecessary.  */
     This case is probably unnecessary.  */
  if (CONST_OK_FOR_I08 (i))
  if (CONST_OK_FOR_I08 (i))
    return 2;
    return 2;
  /* Any other constants requires a 2 cycle pc-relative load plus an and.
  /* Any other constants requires a 2 cycle pc-relative load plus an and.
     This case is probably unnecessary.  */
     This case is probably unnecessary.  */
  return 3;
  return 3;
}
}
 
 
/* Return the cost of an addition or a subtraction.  */
/* Return the cost of an addition or a subtraction.  */
 
 
static inline int
static inline int
addsubcosts (rtx x)
addsubcosts (rtx x)
{
{
  /* Adding a register is a single cycle insn.  */
  /* Adding a register is a single cycle insn.  */
  if (REG_P (XEXP (x, 1))
  if (REG_P (XEXP (x, 1))
      || GET_CODE (XEXP (x, 1)) == SUBREG)
      || GET_CODE (XEXP (x, 1)) == SUBREG)
    return 1;
    return 1;
 
 
  /* Likewise for small constants.  */
  /* Likewise for small constants.  */
  if (CONST_INT_P (XEXP (x, 1))
  if (CONST_INT_P (XEXP (x, 1))
      && CONST_OK_FOR_ADD (INTVAL (XEXP (x, 1))))
      && CONST_OK_FOR_ADD (INTVAL (XEXP (x, 1))))
    return 1;
    return 1;
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    switch (GET_CODE (XEXP (x, 1)))
    switch (GET_CODE (XEXP (x, 1)))
      {
      {
      case CONST:
      case CONST:
      case LABEL_REF:
      case LABEL_REF:
      case SYMBOL_REF:
      case SYMBOL_REF:
        return TARGET_SHMEDIA64 ? 5 : 3;
        return TARGET_SHMEDIA64 ? 5 : 3;
 
 
      case CONST_INT:
      case CONST_INT:
        if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1))))
        if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1))))
          return 2;
          return 2;
        else if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)) >> 16))
        else if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)) >> 16))
          return 3;
          return 3;
        else if (CONST_OK_FOR_I16 ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
        else if (CONST_OK_FOR_I16 ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
          return 4;
          return 4;
 
 
        /* Fall through.  */
        /* Fall through.  */
      default:
      default:
        return 5;
        return 5;
      }
      }
 
 
  /* Any other constant requires a 2 cycle pc-relative load plus an
  /* Any other constant requires a 2 cycle pc-relative load plus an
     addition.  */
     addition.  */
  return 3;
  return 3;
}
}
 
 
/* Return the cost of a multiply.  */
/* Return the cost of a multiply.  */
static inline int
static inline int
multcosts (rtx x ATTRIBUTE_UNUSED)
multcosts (rtx x ATTRIBUTE_UNUSED)
{
{
  if (sh_multcost >= 0)
  if (sh_multcost >= 0)
    return sh_multcost;
    return sh_multcost;
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    /* ??? We have a mul insn, but it has a latency of three, and doesn't
    /* ??? We have a mul insn, but it has a latency of three, and doesn't
       accept constants.  Ideally, we would use a cost of one or two and
       accept constants.  Ideally, we would use a cost of one or two and
       add the cost of the operand, but disregard the latter when inside loops
       add the cost of the operand, but disregard the latter when inside loops
       and loop invariant code motion is still to follow.
       and loop invariant code motion is still to follow.
       Using a multiply first and splitting it later if it's a loss
       Using a multiply first and splitting it later if it's a loss
       doesn't work because of different sign / zero extension semantics
       doesn't work because of different sign / zero extension semantics
       of multiplies vs. shifts.  */
       of multiplies vs. shifts.  */
    return TARGET_SMALLCODE ? 2 : 3;
    return TARGET_SMALLCODE ? 2 : 3;
 
 
  if (TARGET_SH2)
  if (TARGET_SH2)
    {
    {
      /* We have a mul insn, so we can never take more than the mul and the
      /* We have a mul insn, so we can never take more than the mul and the
         read of the mac reg, but count more because of the latency and extra
         read of the mac reg, but count more because of the latency and extra
         reg usage.  */
         reg usage.  */
      if (TARGET_SMALLCODE)
      if (TARGET_SMALLCODE)
        return 2;
        return 2;
      return 3;
      return 3;
    }
    }
 
 
  /* If we're aiming at small code, then just count the number of
  /* If we're aiming at small code, then just count the number of
     insns in a multiply call sequence.  */
     insns in a multiply call sequence.  */
  if (TARGET_SMALLCODE)
  if (TARGET_SMALLCODE)
    return 5;
    return 5;
 
 
  /* Otherwise count all the insns in the routine we'd be calling too.  */
  /* Otherwise count all the insns in the routine we'd be calling too.  */
  return 20;
  return 20;
}
}
 
 
/* Compute a (partial) cost for rtx X.  Return true if the complete
/* Compute a (partial) cost for rtx X.  Return true if the complete
   cost has been computed, and false if subexpressions should be
   cost has been computed, and false if subexpressions should be
   scanned.  In either case, *TOTAL contains the cost result.  */
   scanned.  In either case, *TOTAL contains the cost result.  */
 
 
static bool
static bool
sh_rtx_costs (rtx x, int code, int outer_code, int *total,
sh_rtx_costs (rtx x, int code, int outer_code, int *total,
              bool speed ATTRIBUTE_UNUSED)
              bool speed ATTRIBUTE_UNUSED)
{
{
  switch (code)
  switch (code)
    {
    {
    case CONST_INT:
    case CONST_INT:
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        {
        {
          if (INTVAL (x) == 0)
          if (INTVAL (x) == 0)
            *total = 0;
            *total = 0;
          else if (outer_code == AND && and_operand ((x), DImode))
          else if (outer_code == AND && and_operand ((x), DImode))
            *total = 0;
            *total = 0;
          else if ((outer_code == IOR || outer_code == XOR
          else if ((outer_code == IOR || outer_code == XOR
                    || outer_code == PLUS)
                    || outer_code == PLUS)
                   && CONST_OK_FOR_I10 (INTVAL (x)))
                   && CONST_OK_FOR_I10 (INTVAL (x)))
            *total = 0;
            *total = 0;
          else if (CONST_OK_FOR_I16 (INTVAL (x)))
          else if (CONST_OK_FOR_I16 (INTVAL (x)))
            *total = COSTS_N_INSNS (outer_code != SET);
            *total = COSTS_N_INSNS (outer_code != SET);
          else if (CONST_OK_FOR_I16 (INTVAL (x) >> 16))
          else if (CONST_OK_FOR_I16 (INTVAL (x) >> 16))
            *total = COSTS_N_INSNS ((outer_code != SET) + 1);
            *total = COSTS_N_INSNS ((outer_code != SET) + 1);
          else if (CONST_OK_FOR_I16 ((INTVAL (x) >> 16) >> 16))
          else if (CONST_OK_FOR_I16 ((INTVAL (x) >> 16) >> 16))
            *total = COSTS_N_INSNS ((outer_code != SET) + 2);
            *total = COSTS_N_INSNS ((outer_code != SET) + 2);
          else
          else
            *total = COSTS_N_INSNS ((outer_code != SET) + 3);
            *total = COSTS_N_INSNS ((outer_code != SET) + 3);
          return true;
          return true;
        }
        }
      if (CONST_OK_FOR_I08 (INTVAL (x)))
      if (CONST_OK_FOR_I08 (INTVAL (x)))
        *total = 0;
        *total = 0;
      else if ((outer_code == AND || outer_code == IOR || outer_code == XOR)
      else if ((outer_code == AND || outer_code == IOR || outer_code == XOR)
               && CONST_OK_FOR_K08 (INTVAL (x)))
               && CONST_OK_FOR_K08 (INTVAL (x)))
        *total = 1;
        *total = 1;
      /* prepare_cmp_insn will force costly constants int registers before
      /* prepare_cmp_insn will force costly constants int registers before
         the cbranch[sd]i4 patterns can see them, so preserve potentially
         the cbranch[sd]i4 patterns can see them, so preserve potentially
         interesting ones not covered by I08 above.  */
         interesting ones not covered by I08 above.  */
      else if (outer_code == COMPARE
      else if (outer_code == COMPARE
               && ((unsigned HOST_WIDE_INT) INTVAL (x)
               && ((unsigned HOST_WIDE_INT) INTVAL (x)
                    == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
                    == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
                    || INTVAL (x) == 0x7fffffff
                    || INTVAL (x) == 0x7fffffff
                   || INTVAL (x) == 0x80 || INTVAL (x) == -0x81))
                   || INTVAL (x) == 0x80 || INTVAL (x) == -0x81))
        *total = 1;
        *total = 1;
      else
      else
        *total = 8;
        *total = 8;
      return true;
      return true;
 
 
    case CONST:
    case CONST:
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
      if (TARGET_SHMEDIA64)
      if (TARGET_SHMEDIA64)
        *total = COSTS_N_INSNS (4);
        *total = COSTS_N_INSNS (4);
      else if (TARGET_SHMEDIA32)
      else if (TARGET_SHMEDIA32)
        *total = COSTS_N_INSNS (2);
        *total = COSTS_N_INSNS (2);
      else
      else
        *total = 5;
        *total = 5;
      return true;
      return true;
 
 
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        *total = COSTS_N_INSNS (4);
        *total = COSTS_N_INSNS (4);
      /* prepare_cmp_insn will force costly constants int registers before
      /* prepare_cmp_insn will force costly constants int registers before
         the cbranchdi4 pattern can see them, so preserve potentially
         the cbranchdi4 pattern can see them, so preserve potentially
         interesting ones.  */
         interesting ones.  */
      else if (outer_code == COMPARE && GET_MODE (x) == DImode)
      else if (outer_code == COMPARE && GET_MODE (x) == DImode)
        *total = 1;
        *total = 1;
      else
      else
        *total = 10;
        *total = 10;
      return true;
      return true;
    case CONST_VECTOR:
    case CONST_VECTOR:
      if (x == CONST0_RTX (GET_MODE (x)))
      if (x == CONST0_RTX (GET_MODE (x)))
        *total = 0;
        *total = 0;
      else if (sh_1el_vec (x, VOIDmode))
      else if (sh_1el_vec (x, VOIDmode))
        *total = outer_code != SET;
        *total = outer_code != SET;
      if (sh_rep_vec (x, VOIDmode))
      if (sh_rep_vec (x, VOIDmode))
        *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
        *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
                  + (outer_code != SET));
                  + (outer_code != SET));
      *total = COSTS_N_INSNS (3) + (outer_code != SET);
      *total = COSTS_N_INSNS (3) + (outer_code != SET);
      return true;
      return true;
 
 
    case PLUS:
    case PLUS:
    case MINUS:
    case MINUS:
      *total = COSTS_N_INSNS (addsubcosts (x));
      *total = COSTS_N_INSNS (addsubcosts (x));
      return true;
      return true;
 
 
    case AND:
    case AND:
      *total = COSTS_N_INSNS (andcosts (x));
      *total = COSTS_N_INSNS (andcosts (x));
      return true;
      return true;
 
 
    case MULT:
    case MULT:
      *total = COSTS_N_INSNS (multcosts (x));
      *total = COSTS_N_INSNS (multcosts (x));
      return true;
      return true;
 
 
    case ASHIFT:
    case ASHIFT:
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
      *total = COSTS_N_INSNS (shiftcosts (x));
      *total = COSTS_N_INSNS (shiftcosts (x));
      return true;
      return true;
 
 
    case DIV:
    case DIV:
    case UDIV:
    case UDIV:
    case MOD:
    case MOD:
    case UMOD:
    case UMOD:
      *total = COSTS_N_INSNS (20);
      *total = COSTS_N_INSNS (20);
      return true;
      return true;
 
 
    case PARALLEL:
    case PARALLEL:
      if (sh_1el_vec (x, VOIDmode))
      if (sh_1el_vec (x, VOIDmode))
        *total = outer_code != SET;
        *total = outer_code != SET;
      if (sh_rep_vec (x, VOIDmode))
      if (sh_rep_vec (x, VOIDmode))
        *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
        *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
                  + (outer_code != SET));
                  + (outer_code != SET));
      *total = COSTS_N_INSNS (3) + (outer_code != SET);
      *total = COSTS_N_INSNS (3) + (outer_code != SET);
      return true;
      return true;
 
 
    case FLOAT:
    case FLOAT:
    case FIX:
    case FIX:
      *total = 100;
      *total = 100;
      return true;
      return true;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
}
}
 
 
/* Compute the cost of an address.  For the SH, all valid addresses are
/* Compute the cost of an address.  For the SH, all valid addresses are
   the same cost.  Use a slightly higher cost for reg + reg addressing,
   the same cost.  Use a slightly higher cost for reg + reg addressing,
   since it increases pressure on r0.  */
   since it increases pressure on r0.  */
 
 
static int
static int
sh_address_cost (rtx X,
sh_address_cost (rtx X,
                 bool speed ATTRIBUTE_UNUSED)
                 bool speed ATTRIBUTE_UNUSED)
{
{
  return (GET_CODE (X) == PLUS
  return (GET_CODE (X) == PLUS
          && ! CONSTANT_P (XEXP (X, 1))
          && ! CONSTANT_P (XEXP (X, 1))
          && ! TARGET_SHMEDIA ? 1 : 0);
          && ! TARGET_SHMEDIA ? 1 : 0);
}
}
 
 
/* Code to expand a shift.  */
/* Code to expand a shift.  */
 
 
void
void
gen_ashift (int type, int n, rtx reg)
gen_ashift (int type, int n, rtx reg)
{
{
  /* Negative values here come from the shift_amounts array.  */
  /* Negative values here come from the shift_amounts array.  */
  if (n < 0)
  if (n < 0)
    {
    {
      if (type == ASHIFT)
      if (type == ASHIFT)
        type = LSHIFTRT;
        type = LSHIFTRT;
      else
      else
        type = ASHIFT;
        type = ASHIFT;
      n = -n;
      n = -n;
    }
    }
 
 
  switch (type)
  switch (type)
    {
    {
    case ASHIFTRT:
    case ASHIFTRT:
      emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n)));
      emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n)));
      break;
      break;
    case LSHIFTRT:
    case LSHIFTRT:
      if (n == 1)
      if (n == 1)
        emit_insn (gen_lshrsi3_m (reg, reg, GEN_INT (n)));
        emit_insn (gen_lshrsi3_m (reg, reg, GEN_INT (n)));
      else
      else
        emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n)));
        emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n)));
      break;
      break;
    case ASHIFT:
    case ASHIFT:
      emit_insn (gen_ashlsi3_std (reg, reg, GEN_INT (n)));
      emit_insn (gen_ashlsi3_std (reg, reg, GEN_INT (n)));
      break;
      break;
    }
    }
}
}
 
 
/* Same for HImode */
/* Same for HImode */
 
 
void
void
gen_ashift_hi (int type, int n, rtx reg)
gen_ashift_hi (int type, int n, rtx reg)
{
{
  /* Negative values here come from the shift_amounts array.  */
  /* Negative values here come from the shift_amounts array.  */
  if (n < 0)
  if (n < 0)
    {
    {
      if (type == ASHIFT)
      if (type == ASHIFT)
        type = LSHIFTRT;
        type = LSHIFTRT;
      else
      else
        type = ASHIFT;
        type = ASHIFT;
      n = -n;
      n = -n;
    }
    }
 
 
  switch (type)
  switch (type)
    {
    {
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
      /* We don't have HImode right shift operations because using the
      /* We don't have HImode right shift operations because using the
         ordinary 32 bit shift instructions for that doesn't generate proper
         ordinary 32 bit shift instructions for that doesn't generate proper
         zero/sign extension.
         zero/sign extension.
         gen_ashift_hi is only called in contexts where we know that the
         gen_ashift_hi is only called in contexts where we know that the
         sign extension works out correctly.  */
         sign extension works out correctly.  */
      {
      {
        int offset = 0;
        int offset = 0;
        if (GET_CODE (reg) == SUBREG)
        if (GET_CODE (reg) == SUBREG)
          {
          {
            offset = SUBREG_BYTE (reg);
            offset = SUBREG_BYTE (reg);
            reg = SUBREG_REG (reg);
            reg = SUBREG_REG (reg);
          }
          }
        gen_ashift (type, n, gen_rtx_SUBREG (SImode, reg, offset));
        gen_ashift (type, n, gen_rtx_SUBREG (SImode, reg, offset));
        break;
        break;
      }
      }
    case ASHIFT:
    case ASHIFT:
      emit_insn (gen_ashlhi3_k (reg, reg, GEN_INT (n)));
      emit_insn (gen_ashlhi3_k (reg, reg, GEN_INT (n)));
      break;
      break;
    }
    }
}
}
 
 
/* Output RTL to split a constant shift into its component SH constant
/* Output RTL to split a constant shift into its component SH constant
   shift instructions.  */
   shift instructions.  */
 
 
void
void
gen_shifty_op (int code, rtx *operands)
gen_shifty_op (int code, rtx *operands)
{
{
  int value = INTVAL (operands[2]);
  int value = INTVAL (operands[2]);
  int max, i;
  int max, i;
 
 
  /* Truncate the shift count in case it is out of bounds.  */
  /* Truncate the shift count in case it is out of bounds.  */
  value = value & 31;
  value = value & 31;
 
 
  if (value == 31)
  if (value == 31)
    {
    {
      if (code == LSHIFTRT)
      if (code == LSHIFTRT)
        {
        {
          emit_insn (gen_rotlsi3_1 (operands[0], operands[0]));
          emit_insn (gen_rotlsi3_1 (operands[0], operands[0]));
          emit_insn (gen_movt (operands[0]));
          emit_insn (gen_movt (operands[0]));
          return;
          return;
        }
        }
      else if (code == ASHIFT)
      else if (code == ASHIFT)
        {
        {
          /* There is a two instruction sequence for 31 bit left shifts,
          /* There is a two instruction sequence for 31 bit left shifts,
             but it requires r0.  */
             but it requires r0.  */
          if (REG_P (operands[0]) && REGNO (operands[0]) == 0)
          if (REG_P (operands[0]) && REGNO (operands[0]) == 0)
            {
            {
              emit_insn (gen_andsi3 (operands[0], operands[0], const1_rtx));
              emit_insn (gen_andsi3 (operands[0], operands[0], const1_rtx));
              emit_insn (gen_rotlsi3_31 (operands[0], operands[0]));
              emit_insn (gen_rotlsi3_31 (operands[0], operands[0]));
              return;
              return;
            }
            }
        }
        }
    }
    }
  else if (value == 0)
  else if (value == 0)
    {
    {
      /* This can happen even when optimizing, if there were subregs before
      /* This can happen even when optimizing, if there were subregs before
         reload.  Don't output a nop here, as this is never optimized away;
         reload.  Don't output a nop here, as this is never optimized away;
         use a no-op move instead.  */
         use a no-op move instead.  */
      emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[0]));
      emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[0]));
      return;
      return;
    }
    }
 
 
  max = shift_insns[value];
  max = shift_insns[value];
  for (i = 0; i < max; i++)
  for (i = 0; i < max; i++)
    gen_ashift (code, shift_amounts[value][i], operands[0]);
    gen_ashift (code, shift_amounts[value][i], operands[0]);
}
}
 
 
/* Same as above, but optimized for values where the topmost bits don't
/* Same as above, but optimized for values where the topmost bits don't
   matter.  */
   matter.  */
 
 
void
void
gen_shifty_hi_op (int code, rtx *operands)
gen_shifty_hi_op (int code, rtx *operands)
{
{
  int value = INTVAL (operands[2]);
  int value = INTVAL (operands[2]);
  int max, i;
  int max, i;
  void (*gen_fun) (int, int, rtx);
  void (*gen_fun) (int, int, rtx);
 
 
  /* This operation is used by and_shl for SImode values with a few
  /* This operation is used by and_shl for SImode values with a few
     high bits known to be cleared.  */
     high bits known to be cleared.  */
  value &= 31;
  value &= 31;
  if (value == 0)
  if (value == 0)
    {
    {
      emit_insn (gen_nop ());
      emit_insn (gen_nop ());
      return;
      return;
    }
    }
 
 
  gen_fun = GET_MODE (operands[0]) == HImode ? gen_ashift_hi : gen_ashift;
  gen_fun = GET_MODE (operands[0]) == HImode ? gen_ashift_hi : gen_ashift;
  if (code == ASHIFT)
  if (code == ASHIFT)
    {
    {
      max = ext_shift_insns[value];
      max = ext_shift_insns[value];
      for (i = 0; i < max; i++)
      for (i = 0; i < max; i++)
        gen_fun (code, ext_shift_amounts[value][i], operands[0]);
        gen_fun (code, ext_shift_amounts[value][i], operands[0]);
    }
    }
  else
  else
    /* When shifting right, emit the shifts in reverse order, so that
    /* When shifting right, emit the shifts in reverse order, so that
       solitary negative values come first.  */
       solitary negative values come first.  */
    for (i = ext_shift_insns[value] - 1; i >= 0; i--)
    for (i = ext_shift_insns[value] - 1; i >= 0; i--)
      gen_fun (code, ext_shift_amounts[value][i], operands[0]);
      gen_fun (code, ext_shift_amounts[value][i], operands[0]);
}
}
 
 
/* Output RTL for an arithmetic right shift.  */
/* Output RTL for an arithmetic right shift.  */
 
 
/* ??? Rewrite to use super-optimizer sequences.  */
/* ??? Rewrite to use super-optimizer sequences.  */
 
 
int
int
expand_ashiftrt (rtx *operands)
expand_ashiftrt (rtx *operands)
{
{
  rtx wrk;
  rtx wrk;
  char func[18];
  char func[18];
  int value;
  int value;
 
 
  if (TARGET_SH3)
  if (TARGET_SH3)
    {
    {
      if (!CONST_INT_P (operands[2]))
      if (!CONST_INT_P (operands[2]))
        {
        {
          rtx count = copy_to_mode_reg (SImode, operands[2]);
          rtx count = copy_to_mode_reg (SImode, operands[2]);
          emit_insn (gen_negsi2 (count, count));
          emit_insn (gen_negsi2 (count, count));
          emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
          emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
          return 1;
          return 1;
        }
        }
      else if (ashiftrt_insns[INTVAL (operands[2]) & 31]
      else if (ashiftrt_insns[INTVAL (operands[2]) & 31]
               > 1 + SH_DYNAMIC_SHIFT_COST)
               > 1 + SH_DYNAMIC_SHIFT_COST)
        {
        {
          rtx count
          rtx count
            = force_reg (SImode, GEN_INT (- (INTVAL (operands[2]) & 31)));
            = force_reg (SImode, GEN_INT (- (INTVAL (operands[2]) & 31)));
          emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
          emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
          return 1;
          return 1;
        }
        }
    }
    }
  if (!CONST_INT_P (operands[2]))
  if (!CONST_INT_P (operands[2]))
    return 0;
    return 0;
 
 
  value = INTVAL (operands[2]) & 31;
  value = INTVAL (operands[2]) & 31;
 
 
  if (value == 31)
  if (value == 31)
    {
    {
      /* If we are called from abs expansion, arrange things so that we
      /* If we are called from abs expansion, arrange things so that we
         we can use a single MT instruction that doesn't clobber the source,
         we can use a single MT instruction that doesn't clobber the source,
         if LICM can hoist out the load of the constant zero.  */
         if LICM can hoist out the load of the constant zero.  */
      if (currently_expanding_to_rtl)
      if (currently_expanding_to_rtl)
        {
        {
          emit_insn (gen_cmpgtsi_t (force_reg (SImode, CONST0_RTX (SImode)),
          emit_insn (gen_cmpgtsi_t (force_reg (SImode, CONST0_RTX (SImode)),
                                    operands[1]));
                                    operands[1]));
          emit_insn (gen_mov_neg_si_t (operands[0]));
          emit_insn (gen_mov_neg_si_t (operands[0]));
          return 1;
          return 1;
        }
        }
      emit_insn (gen_ashrsi2_31 (operands[0], operands[1]));
      emit_insn (gen_ashrsi2_31 (operands[0], operands[1]));
      return 1;
      return 1;
    }
    }
  else if (value >= 16 && value <= 19)
  else if (value >= 16 && value <= 19)
    {
    {
      wrk = gen_reg_rtx (SImode);
      wrk = gen_reg_rtx (SImode);
      emit_insn (gen_ashrsi2_16 (wrk, operands[1]));
      emit_insn (gen_ashrsi2_16 (wrk, operands[1]));
      value -= 16;
      value -= 16;
      while (value--)
      while (value--)
        gen_ashift (ASHIFTRT, 1, wrk);
        gen_ashift (ASHIFTRT, 1, wrk);
      emit_move_insn (operands[0], wrk);
      emit_move_insn (operands[0], wrk);
      return 1;
      return 1;
    }
    }
  /* Expand a short sequence inline, longer call a magic routine.  */
  /* Expand a short sequence inline, longer call a magic routine.  */
  else if (value <= 5)
  else if (value <= 5)
    {
    {
      wrk = gen_reg_rtx (SImode);
      wrk = gen_reg_rtx (SImode);
      emit_move_insn (wrk, operands[1]);
      emit_move_insn (wrk, operands[1]);
      while (value--)
      while (value--)
        gen_ashift (ASHIFTRT, 1, wrk);
        gen_ashift (ASHIFTRT, 1, wrk);
      emit_move_insn (operands[0], wrk);
      emit_move_insn (operands[0], wrk);
      return 1;
      return 1;
    }
    }
 
 
  wrk = gen_reg_rtx (Pmode);
  wrk = gen_reg_rtx (Pmode);
 
 
  /* Load the value into an arg reg and call a helper.  */
  /* Load the value into an arg reg and call a helper.  */
  emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
  emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
  sprintf (func, "__ashiftrt_r4_%d", value);
  sprintf (func, "__ashiftrt_r4_%d", value);
  function_symbol (wrk, func, SFUNC_STATIC);
  function_symbol (wrk, func, SFUNC_STATIC);
  emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk));
  emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk));
  emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
  emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
  return 1;
  return 1;
}
}
 
 
int
int
sh_dynamicalize_shift_p (rtx count)
sh_dynamicalize_shift_p (rtx count)
{
{
  return shift_insns[INTVAL (count) & 31] > 1 + SH_DYNAMIC_SHIFT_COST;
  return shift_insns[INTVAL (count) & 31] > 1 + SH_DYNAMIC_SHIFT_COST;
}
}
 
 
/* Try to find a good way to implement the combiner pattern
/* Try to find a good way to implement the combiner pattern
  [(set (match_operand:SI 0 "register_operand" "r")
  [(set (match_operand:SI 0 "register_operand" "r")
        (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
        (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
                           (match_operand:SI 2 "const_int_operand" "n"))
                           (match_operand:SI 2 "const_int_operand" "n"))
                (match_operand:SI 3 "const_int_operand" "n"))) .
                (match_operand:SI 3 "const_int_operand" "n"))) .
  LEFT_RTX is operand 2 in the above pattern, and MASK_RTX is operand 3.
  LEFT_RTX is operand 2 in the above pattern, and MASK_RTX is operand 3.
  return 0 for simple right / left or left/right shift combination.
  return 0 for simple right / left or left/right shift combination.
  return 1 for a combination of shifts with zero_extend.
  return 1 for a combination of shifts with zero_extend.
  return 2 for a combination of shifts with an AND that needs r0.
  return 2 for a combination of shifts with an AND that needs r0.
  return 3 for a combination of shifts with an AND that needs an extra
  return 3 for a combination of shifts with an AND that needs an extra
    scratch register, when the three highmost bits of the AND mask are clear.
    scratch register, when the three highmost bits of the AND mask are clear.
  return 4 for a combination of shifts with an AND that needs an extra
  return 4 for a combination of shifts with an AND that needs an extra
    scratch register, when any of the three highmost bits of the AND mask
    scratch register, when any of the three highmost bits of the AND mask
    is set.
    is set.
  If ATTRP is set, store an initial right shift width in ATTRP[0],
  If ATTRP is set, store an initial right shift width in ATTRP[0],
  and the instruction length in ATTRP[1] .  These values are not valid
  and the instruction length in ATTRP[1] .  These values are not valid
  when returning 0.
  when returning 0.
  When ATTRP is set and returning 1, ATTRP[2] gets set to the index into
  When ATTRP is set and returning 1, ATTRP[2] gets set to the index into
  shift_amounts for the last shift value that is to be used before the
  shift_amounts for the last shift value that is to be used before the
  sign extend.  */
  sign extend.  */
int
int
shl_and_kind (rtx left_rtx, rtx mask_rtx, int *attrp)
shl_and_kind (rtx left_rtx, rtx mask_rtx, int *attrp)
{
{
  unsigned HOST_WIDE_INT mask, lsb, mask2, lsb2;
  unsigned HOST_WIDE_INT mask, lsb, mask2, lsb2;
  int left = INTVAL (left_rtx), right;
  int left = INTVAL (left_rtx), right;
  int best = 0;
  int best = 0;
  int cost, best_cost = 10000;
  int cost, best_cost = 10000;
  int best_right = 0, best_len = 0;
  int best_right = 0, best_len = 0;
  int i;
  int i;
  int can_ext;
  int can_ext;
 
 
  if (left < 0 || left > 31)
  if (left < 0 || left > 31)
    return 0;
    return 0;
  if (CONST_INT_P (mask_rtx))
  if (CONST_INT_P (mask_rtx))
    mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> left;
    mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> left;
  else
  else
    mask = (unsigned HOST_WIDE_INT) GET_MODE_MASK (SImode) >> left;
    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;
  lsb = ((mask ^ (mask - 1)) >> 1) + 1;
  right = exact_log2 (lsb);
  right = exact_log2 (lsb);
  mask2 = ~(mask + lsb - 1);
  mask2 = ~(mask + lsb - 1);
  lsb2 = ((mask2 ^ (mask2 - 1)) >> 1) + 1;
  lsb2 = ((mask2 ^ (mask2 - 1)) >> 1) + 1;
  /* mask has no zeroes but trailing zeroes <==> ! mask2 */
  /* mask has no zeroes but trailing zeroes <==> ! mask2 */
  if (! mask2)
  if (! mask2)
    best_cost = shift_insns[right] + shift_insns[right + left];
    best_cost = shift_insns[right] + shift_insns[right + left];
  /* mask has no trailing zeroes <==> ! right */
  /* mask has no trailing zeroes <==> ! right */
  else if (! right && mask2 == ~(lsb2 - 1))
  else if (! right && mask2 == ~(lsb2 - 1))
    {
    {
      int late_right = exact_log2 (lsb2);
      int late_right = exact_log2 (lsb2);
      best_cost = shift_insns[left + late_right] + shift_insns[late_right];
      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))
  if (mask2 == ~(lsb2 - 1))
    {
    {
      int width, first;
      int width, first;
 
 
      for (width = 8; width <= 16; width += 8)
      for (width = 8; width <= 16; width += 8)
        {
        {
          /* Can we zero-extend right away?  */
          /* Can we zero-extend right away?  */
          if (lsb2 == (unsigned HOST_WIDE_INT) 1 << width)
          if (lsb2 == (unsigned HOST_WIDE_INT) 1 << width)
            {
            {
              cost
              cost
                = 1 + ext_shift_insns[right] + ext_shift_insns[left + right];
                = 1 + ext_shift_insns[right] + ext_shift_insns[left + right];
              if (cost < best_cost)
              if (cost < best_cost)
                {
                {
                  best = 1;
                  best = 1;
                  best_cost = cost;
                  best_cost = cost;
                  best_right = right;
                  best_right = right;
                  best_len = cost;
                  best_len = cost;
                  if (attrp)
                  if (attrp)
                    attrp[2] = -1;
                    attrp[2] = -1;
                }
                }
              continue;
              continue;
            }
            }
          /* ??? Could try to put zero extend into initial right shift,
          /* ??? Could try to put zero extend into initial right shift,
             or even shift a bit left before the right shift.  */
             or even shift a bit left before the right shift.  */
          /* Determine value of first part of left shift, to get to the
          /* Determine value of first part of left shift, to get to the
             zero extend cut-off point.  */
             zero extend cut-off point.  */
          first = width - exact_log2 (lsb2) + right;
          first = width - exact_log2 (lsb2) + right;
          if (first >= 0 && right + left - first >= 0)
          if (first >= 0 && right + left - first >= 0)
            {
            {
              cost = ext_shift_insns[right] + ext_shift_insns[first] + 1
              cost = ext_shift_insns[right] + ext_shift_insns[first] + 1
                + ext_shift_insns[right + left - first];
                + ext_shift_insns[right + left - first];
              if (cost < best_cost)
              if (cost < best_cost)
                {
                {
                  best = 1;
                  best = 1;
                  best_cost = cost;
                  best_cost = cost;
                  best_right = right;
                  best_right = right;
                  best_len = cost;
                  best_len = cost;
                  if (attrp)
                  if (attrp)
                    attrp[2] = first;
                    attrp[2] = first;
                }
                }
            }
            }
        }
        }
    }
    }
  /* Try to use r0 AND pattern */
  /* Try to use r0 AND pattern */
  for (i = 0; i <= 2; i++)
  for (i = 0; i <= 2; i++)
    {
    {
      if (i > right)
      if (i > right)
        break;
        break;
      if (! CONST_OK_FOR_K08 (mask >> i))
      if (! CONST_OK_FOR_K08 (mask >> i))
        continue;
        continue;
      cost = (i != 0) + 2 + ext_shift_insns[left + i];
      cost = (i != 0) + 2 + ext_shift_insns[left + i];
      if (cost < best_cost)
      if (cost < best_cost)
        {
        {
          best = 2;
          best = 2;
          best_cost = cost;
          best_cost = cost;
          best_right = i;
          best_right = i;
          best_len = cost - 1;
          best_len = cost - 1;
        }
        }
    }
    }
  /* Try to use a scratch register to hold the AND operand.  */
  /* 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++)
  for (i = 0; i <= 2; i++)
    {
    {
      if (i > right)
      if (i > right)
        break;
        break;
      cost = (i != 0) + (CONST_OK_FOR_I08 (mask >> i) ? 2 : 3)
      cost = (i != 0) + (CONST_OK_FOR_I08 (mask >> i) ? 2 : 3)
        + (can_ext ? ext_shift_insns : shift_insns)[left + i];
        + (can_ext ? ext_shift_insns : shift_insns)[left + i];
      if (cost < best_cost)
      if (cost < best_cost)
        {
        {
          best = 4 - can_ext;
          best = 4 - can_ext;
          best_cost = cost;
          best_cost = cost;
          best_right = i;
          best_right = i;
          best_len = cost - 1 - ! CONST_OK_FOR_I08 (mask >> i);
          best_len = cost - 1 - ! CONST_OK_FOR_I08 (mask >> i);
        }
        }
    }
    }
 
 
  if (attrp)
  if (attrp)
    {
    {
      attrp[0] = best_right;
      attrp[0] = best_right;
      attrp[1] = best_len;
      attrp[1] = best_len;
    }
    }
  return best;
  return best;
}
}
 
 
/* This is used in length attributes of the unnamed instructions
/* This is used in length attributes of the unnamed instructions
   corresponding to shl_and_kind return values of 1 and 2.  */
   corresponding to shl_and_kind return values of 1 and 2.  */
int
int
shl_and_length (rtx insn)
shl_and_length (rtx insn)
{
{
  rtx set_src, left_rtx, mask_rtx;
  rtx set_src, left_rtx, mask_rtx;
  int attributes[3];
  int attributes[3];
 
 
  set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  left_rtx = XEXP (XEXP (set_src, 0), 1);
  left_rtx = XEXP (XEXP (set_src, 0), 1);
  mask_rtx = XEXP (set_src, 1);
  mask_rtx = XEXP (set_src, 1);
  shl_and_kind (left_rtx, mask_rtx, attributes);
  shl_and_kind (left_rtx, mask_rtx, attributes);
  return attributes[1];
  return attributes[1];
}
}
 
 
/* This is used in length attribute of the and_shl_scratch instruction.  */
/* This is used in length attribute of the and_shl_scratch instruction.  */
 
 
int
int
shl_and_scr_length (rtx insn)
shl_and_scr_length (rtx insn)
{
{
  rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  int len = shift_insns[INTVAL (XEXP (set_src, 1)) & 31];
  int len = shift_insns[INTVAL (XEXP (set_src, 1)) & 31];
  rtx op = XEXP (set_src, 0);
  rtx op = XEXP (set_src, 0);
  len += shift_insns[INTVAL (XEXP (op, 1)) & 31] + 1;
  len += shift_insns[INTVAL (XEXP (op, 1)) & 31] + 1;
  op = XEXP (XEXP (op, 0), 0);
  op = XEXP (XEXP (op, 0), 0);
  return len + shift_insns[INTVAL (XEXP (op, 1)) & 31];
  return len + shift_insns[INTVAL (XEXP (op, 1)) & 31];
}
}
 
 
/* Generate rtl for instructions for which shl_and_kind advised a particular
/* Generate rtl for instructions for which shl_and_kind advised a particular
   method of generating them, i.e. returned zero.  */
   method of generating them, i.e. returned zero.  */
 
 
int
int
gen_shl_and (rtx dest, rtx left_rtx, rtx mask_rtx, rtx source)
gen_shl_and (rtx dest, rtx left_rtx, rtx mask_rtx, rtx source)
{
{
  int attributes[3];
  int attributes[3];
  unsigned HOST_WIDE_INT mask;
  unsigned HOST_WIDE_INT mask;
  int kind = shl_and_kind (left_rtx, mask_rtx, attributes);
  int kind = shl_and_kind (left_rtx, mask_rtx, attributes);
  int right, total_shift;
  int right, total_shift;
  void (*shift_gen_fun) (int, rtx *) = gen_shifty_hi_op;
  void (*shift_gen_fun) (int, rtx *) = gen_shifty_hi_op;
 
 
  right = attributes[0];
  right = attributes[0];
  total_shift = INTVAL (left_rtx) + right;
  total_shift = INTVAL (left_rtx) + right;
  mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> total_shift;
  mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> total_shift;
  switch (kind)
  switch (kind)
    {
    {
    default:
    default:
      return -1;
      return -1;
    case 1:
    case 1:
      {
      {
        int first = attributes[2];
        int first = attributes[2];
        rtx operands[3];
        rtx operands[3];
 
 
        if (first < 0)
        if (first < 0)
          {
          {
            emit_insn ((mask << right) <= 0xff
            emit_insn ((mask << right) <= 0xff
                       ? gen_zero_extendqisi2 (dest,
                       ? gen_zero_extendqisi2 (dest,
                                               gen_lowpart (QImode, source))
                                               gen_lowpart (QImode, source))
                       : gen_zero_extendhisi2 (dest,
                       : gen_zero_extendhisi2 (dest,
                                               gen_lowpart (HImode, source)));
                                               gen_lowpart (HImode, source)));
            source = dest;
            source = dest;
          }
          }
        if (source != dest)
        if (source != dest)
          emit_insn (gen_movsi (dest, source));
          emit_insn (gen_movsi (dest, source));
        operands[0] = dest;
        operands[0] = dest;
        if (right)
        if (right)
          {
          {
            operands[2] = GEN_INT (right);
            operands[2] = GEN_INT (right);
            gen_shifty_hi_op (LSHIFTRT, operands);
            gen_shifty_hi_op (LSHIFTRT, operands);
          }
          }
        if (first > 0)
        if (first > 0)
          {
          {
            operands[2] = GEN_INT (first);
            operands[2] = GEN_INT (first);
            gen_shifty_hi_op (ASHIFT, operands);
            gen_shifty_hi_op (ASHIFT, operands);
            total_shift -= first;
            total_shift -= first;
            mask <<= first;
            mask <<= first;
          }
          }
        if (first >= 0)
        if (first >= 0)
          emit_insn (mask <= 0xff
          emit_insn (mask <= 0xff
                     ? gen_zero_extendqisi2 (dest, gen_lowpart (QImode, dest))
                     ? gen_zero_extendqisi2 (dest, gen_lowpart (QImode, dest))
                     : gen_zero_extendhisi2 (dest, gen_lowpart (HImode, dest)));
                     : gen_zero_extendhisi2 (dest, gen_lowpart (HImode, dest)));
        if (total_shift > 0)
        if (total_shift > 0)
          {
          {
            operands[2] = GEN_INT (total_shift);
            operands[2] = GEN_INT (total_shift);
            gen_shifty_hi_op (ASHIFT, operands);
            gen_shifty_hi_op (ASHIFT, operands);
          }
          }
        break;
        break;
      }
      }
    case 4:
    case 4:
      shift_gen_fun = gen_shifty_op;
      shift_gen_fun = gen_shifty_op;
    case 3:
    case 3:
      /* If the topmost bit that matters is set, set the topmost bits
      /* 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
         that don't matter.  This way, we might be able to get a shorter
         signed constant.  */
         signed constant.  */
      if (mask & ((HOST_WIDE_INT) 1 << (31 - total_shift)))
      if (mask & ((HOST_WIDE_INT) 1 << (31 - total_shift)))
        mask |= (HOST_WIDE_INT) ~0 << (31 - total_shift);
        mask |= (HOST_WIDE_INT) ~0 << (31 - total_shift);
    case 2:
    case 2:
      /* Don't expand fine-grained when combining, because that will
      /* Don't expand fine-grained when combining, because that will
         make the pattern fail.  */
         make the pattern fail.  */
      if (currently_expanding_to_rtl
      if (currently_expanding_to_rtl
          || reload_in_progress || reload_completed)
          || reload_in_progress || reload_completed)
        {
        {
          rtx operands[3];
          rtx operands[3];
 
 
          /* Cases 3 and 4 should be handled by this split
          /* Cases 3 and 4 should be handled by this split
             only while combining  */
             only while combining  */
          gcc_assert (kind <= 2);
          gcc_assert (kind <= 2);
          if (right)
          if (right)
            {
            {
              emit_insn (gen_lshrsi3 (dest, source, GEN_INT (right)));
              emit_insn (gen_lshrsi3 (dest, source, GEN_INT (right)));
              source = dest;
              source = dest;
            }
            }
          emit_insn (gen_andsi3 (dest, source, GEN_INT (mask)));
          emit_insn (gen_andsi3 (dest, source, GEN_INT (mask)));
          if (total_shift)
          if (total_shift)
            {
            {
              operands[0] = dest;
              operands[0] = dest;
              operands[1] = dest;
              operands[1] = dest;
              operands[2] = GEN_INT (total_shift);
              operands[2] = GEN_INT (total_shift);
              shift_gen_fun (ASHIFT, operands);
              shift_gen_fun (ASHIFT, operands);
            }
            }
          break;
          break;
        }
        }
      else
      else
        {
        {
          int neg = 0;
          int neg = 0;
          if (kind != 4 && total_shift < 16)
          if (kind != 4 && total_shift < 16)
            {
            {
              neg = -ext_shift_amounts[total_shift][1];
              neg = -ext_shift_amounts[total_shift][1];
              if (neg > 0)
              if (neg > 0)
                neg -= ext_shift_amounts[total_shift][2];
                neg -= ext_shift_amounts[total_shift][2];
              else
              else
                neg = 0;
                neg = 0;
            }
            }
          emit_insn (gen_and_shl_scratch (dest, source,
          emit_insn (gen_and_shl_scratch (dest, source,
                                          GEN_INT (right),
                                          GEN_INT (right),
                                          GEN_INT (mask),
                                          GEN_INT (mask),
                                          GEN_INT (total_shift + neg),
                                          GEN_INT (total_shift + neg),
                                          GEN_INT (neg)));
                                          GEN_INT (neg)));
          emit_insn (gen_movsi (dest, dest));
          emit_insn (gen_movsi (dest, dest));
          break;
          break;
        }
        }
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Try to find a good way to implement the combiner pattern
/* Try to find a good way to implement the combiner pattern
  [(set (match_operand:SI 0 "register_operand" "=r")
  [(set (match_operand:SI 0 "register_operand" "=r")
        (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
        (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
                                    (match_operand:SI 2 "const_int_operand" "n")
                                    (match_operand:SI 2 "const_int_operand" "n")
                         (match_operand:SI 3 "const_int_operand" "n")
                         (match_operand:SI 3 "const_int_operand" "n")
                         (const_int 0)))
                         (const_int 0)))
   (clobber (reg:SI T_REG))]
   (clobber (reg:SI T_REG))]
  LEFT_RTX is operand 2 in the above pattern, and SIZE_RTX is operand 3.
  LEFT_RTX is operand 2 in the above pattern, and SIZE_RTX is operand 3.
  return 0 for simple left / right shift combination.
  return 0 for simple left / right shift combination.
  return 1 for left shift / 8 bit sign extend / left shift.
  return 1 for left shift / 8 bit sign extend / left shift.
  return 2 for left shift / 16 bit sign extend / left shift.
  return 2 for left shift / 16 bit sign extend / left shift.
  return 3 for left shift / 8 bit sign extend / shift / sign extend.
  return 3 for left shift / 8 bit sign extend / shift / sign extend.
  return 4 for left shift / 16 bit sign extend / shift / sign extend.
  return 4 for left shift / 16 bit sign extend / shift / sign extend.
  return 5 for left shift / 16 bit sign extend / right shift
  return 5 for left shift / 16 bit sign extend / right shift
  return 6 for < 8 bit sign extend / left shift.
  return 6 for < 8 bit sign extend / left shift.
  return 7 for < 8 bit sign extend / left shift / single right shift.
  return 7 for < 8 bit sign extend / left shift / single right shift.
  If COSTP is nonzero, assign the calculated cost to *COSTP.  */
  If COSTP is nonzero, assign the calculated cost to *COSTP.  */
 
 
int
int
shl_sext_kind (rtx left_rtx, rtx size_rtx, int *costp)
shl_sext_kind (rtx left_rtx, rtx size_rtx, int *costp)
{
{
  int left, size, insize, ext;
  int left, size, insize, ext;
  int cost = 0, best_cost;
  int cost = 0, best_cost;
  int kind;
  int kind;
 
 
  left = INTVAL (left_rtx);
  left = INTVAL (left_rtx);
  size = INTVAL (size_rtx);
  size = INTVAL (size_rtx);
  insize = size - left;
  insize = size - left;
  gcc_assert (insize > 0);
  gcc_assert (insize > 0);
  /* Default to left / right shift.  */
  /* Default to left / right shift.  */
  kind = 0;
  kind = 0;
  best_cost = shift_insns[32 - insize] + ashiftrt_insns[32 - size];
  best_cost = shift_insns[32 - insize] + ashiftrt_insns[32 - size];
  if (size <= 16)
  if (size <= 16)
    {
    {
      /* 16 bit shift / sign extend / 16 bit shift */
      /* 16 bit shift / sign extend / 16 bit shift */
      cost = shift_insns[16 - insize] + 1 + ashiftrt_insns[16 - size];
      cost = shift_insns[16 - insize] + 1 + ashiftrt_insns[16 - size];
      /* If ashiftrt_insns[16 - size] is 8, this choice will be overridden
      /* If ashiftrt_insns[16 - size] is 8, this choice will be overridden
         below, by alternative 3 or something even better.  */
         below, by alternative 3 or something even better.  */
      if (cost < best_cost)
      if (cost < best_cost)
        {
        {
          kind = 5;
          kind = 5;
          best_cost = cost;
          best_cost = cost;
        }
        }
    }
    }
  /* Try a plain sign extend between two shifts.  */
  /* Try a plain sign extend between two shifts.  */
  for (ext = 16; ext >= insize; ext -= 8)
  for (ext = 16; ext >= insize; ext -= 8)
    {
    {
      if (ext <= size)
      if (ext <= size)
        {
        {
          cost = ext_shift_insns[ext - insize] + 1 + shift_insns[size - ext];
          cost = ext_shift_insns[ext - insize] + 1 + shift_insns[size - ext];
          if (cost < best_cost)
          if (cost < best_cost)
            {
            {
              kind = ext / (unsigned) 8;
              kind = ext / (unsigned) 8;
              best_cost = cost;
              best_cost = cost;
            }
            }
        }
        }
      /* Check if we can do a sloppy shift with a final signed shift
      /* Check if we can do a sloppy shift with a final signed shift
         restoring the sign.  */
         restoring the sign.  */
      if (EXT_SHIFT_SIGNED (size - ext))
      if (EXT_SHIFT_SIGNED (size - ext))
        cost = ext_shift_insns[ext - insize] + ext_shift_insns[size - ext] + 1;
        cost = ext_shift_insns[ext - insize] + ext_shift_insns[size - ext] + 1;
      /* If not, maybe it's still cheaper to do the second shift sloppy,
      /* If not, maybe it's still cheaper to do the second shift sloppy,
         and do a final sign extend?  */
         and do a final sign extend?  */
      else if (size <= 16)
      else if (size <= 16)
        cost = ext_shift_insns[ext - insize] + 1
        cost = ext_shift_insns[ext - insize] + 1
          + ext_shift_insns[size > ext ? size - ext : ext - size] + 1;
          + ext_shift_insns[size > ext ? size - ext : ext - size] + 1;
      else
      else
        continue;
        continue;
      if (cost < best_cost)
      if (cost < best_cost)
        {
        {
          kind = ext / (unsigned) 8 + 2;
          kind = ext / (unsigned) 8 + 2;
          best_cost = cost;
          best_cost = cost;
        }
        }
    }
    }
  /* Check if we can sign extend in r0 */
  /* Check if we can sign extend in r0 */
  if (insize < 8)
  if (insize < 8)
    {
    {
      cost = 3 + shift_insns[left];
      cost = 3 + shift_insns[left];
      if (cost < best_cost)
      if (cost < best_cost)
        {
        {
          kind = 6;
          kind = 6;
          best_cost = cost;
          best_cost = cost;
        }
        }
      /* Try the same with a final signed shift.  */
      /* Try the same with a final signed shift.  */
      if (left < 31)
      if (left < 31)
        {
        {
          cost = 3 + ext_shift_insns[left + 1] + 1;
          cost = 3 + ext_shift_insns[left + 1] + 1;
          if (cost < best_cost)
          if (cost < best_cost)
            {
            {
              kind = 7;
              kind = 7;
              best_cost = cost;
              best_cost = cost;
            }
            }
        }
        }
    }
    }
  if (TARGET_SH3)
  if (TARGET_SH3)
    {
    {
      /* Try to use a dynamic shift.  */
      /* Try to use a dynamic shift.  */
      cost = shift_insns[32 - insize] + 1 + SH_DYNAMIC_SHIFT_COST;
      cost = shift_insns[32 - insize] + 1 + SH_DYNAMIC_SHIFT_COST;
      if (cost < best_cost)
      if (cost < best_cost)
        {
        {
          kind = 0;
          kind = 0;
          best_cost = cost;
          best_cost = cost;
        }
        }
    }
    }
  if (costp)
  if (costp)
    *costp = cost;
    *costp = cost;
  return kind;
  return kind;
}
}
 
 
/* Function to be used in the length attribute of the instructions
/* Function to be used in the length attribute of the instructions
   implementing this pattern.  */
   implementing this pattern.  */
 
 
int
int
shl_sext_length (rtx insn)
shl_sext_length (rtx insn)
{
{
  rtx set_src, left_rtx, size_rtx;
  rtx set_src, left_rtx, size_rtx;
  int cost;
  int cost;
 
 
  set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
  left_rtx = XEXP (XEXP (set_src, 0), 1);
  left_rtx = XEXP (XEXP (set_src, 0), 1);
  size_rtx = XEXP (set_src, 1);
  size_rtx = XEXP (set_src, 1);
  shl_sext_kind (left_rtx, size_rtx, &cost);
  shl_sext_kind (left_rtx, size_rtx, &cost);
  return cost;
  return cost;
}
}
 
 
/* Generate rtl for this pattern */
/* Generate rtl for this pattern */
 
 
int
int
gen_shl_sext (rtx dest, rtx left_rtx, rtx size_rtx, rtx source)
gen_shl_sext (rtx dest, rtx left_rtx, rtx size_rtx, rtx source)
{
{
  int kind;
  int kind;
  int left, size, insize, cost;
  int left, size, insize, cost;
  rtx operands[3];
  rtx operands[3];
 
 
  kind = shl_sext_kind (left_rtx, size_rtx, &cost);
  kind = shl_sext_kind (left_rtx, size_rtx, &cost);
  left = INTVAL (left_rtx);
  left = INTVAL (left_rtx);
  size = INTVAL (size_rtx);
  size = INTVAL (size_rtx);
  insize = size - left;
  insize = size - left;
  switch (kind)
  switch (kind)
    {
    {
    case 1:
    case 1:
    case 2:
    case 2:
    case 3:
    case 3:
    case 4:
    case 4:
      {
      {
        int ext = kind & 1 ? 8 : 16;
        int ext = kind & 1 ? 8 : 16;
        int shift2 = size - ext;
        int shift2 = size - ext;
 
 
        /* Don't expand fine-grained when combining, because that will
        /* Don't expand fine-grained when combining, because that will
           make the pattern fail.  */
           make the pattern fail.  */
        if (! currently_expanding_to_rtl
        if (! currently_expanding_to_rtl
            && ! reload_in_progress && ! reload_completed)
            && ! reload_in_progress && ! reload_completed)
          {
          {
            emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
            emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
            emit_insn (gen_movsi (dest, source));
            emit_insn (gen_movsi (dest, source));
            break;
            break;
          }
          }
        if (dest != source)
        if (dest != source)
          emit_insn (gen_movsi (dest, source));
          emit_insn (gen_movsi (dest, source));
        operands[0] = dest;
        operands[0] = dest;
        if (ext - insize)
        if (ext - insize)
          {
          {
            operands[2] = GEN_INT (ext - insize);
            operands[2] = GEN_INT (ext - insize);
            gen_shifty_hi_op (ASHIFT, operands);
            gen_shifty_hi_op (ASHIFT, operands);
          }
          }
        emit_insn (kind & 1
        emit_insn (kind & 1
                   ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
                   ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
                   : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
                   : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
        if (kind <= 2)
        if (kind <= 2)
          {
          {
            if (shift2)
            if (shift2)
              {
              {
                operands[2] = GEN_INT (shift2);
                operands[2] = GEN_INT (shift2);
                gen_shifty_op (ASHIFT, operands);
                gen_shifty_op (ASHIFT, operands);
              }
              }
          }
          }
        else
        else
          {
          {
            if (shift2 > 0)
            if (shift2 > 0)
              {
              {
                if (EXT_SHIFT_SIGNED (shift2))
                if (EXT_SHIFT_SIGNED (shift2))
                  {
                  {
                    operands[2] = GEN_INT (shift2 + 1);
                    operands[2] = GEN_INT (shift2 + 1);
                    gen_shifty_op (ASHIFT, operands);
                    gen_shifty_op (ASHIFT, operands);
                    operands[2] = const1_rtx;
                    operands[2] = const1_rtx;
                    gen_shifty_op (ASHIFTRT, operands);
                    gen_shifty_op (ASHIFTRT, operands);
                    break;
                    break;
                  }
                  }
                operands[2] = GEN_INT (shift2);
                operands[2] = GEN_INT (shift2);
                gen_shifty_hi_op (ASHIFT, operands);
                gen_shifty_hi_op (ASHIFT, operands);
              }
              }
            else if (shift2)
            else if (shift2)
              {
              {
                operands[2] = GEN_INT (-shift2);
                operands[2] = GEN_INT (-shift2);
                gen_shifty_hi_op (LSHIFTRT, operands);
                gen_shifty_hi_op (LSHIFTRT, operands);
              }
              }
            emit_insn (size <= 8
            emit_insn (size <= 8
                       ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
                       ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
                       : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
                       : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
          }
          }
        break;
        break;
      }
      }
    case 5:
    case 5:
      {
      {
        int i = 16 - size;
        int i = 16 - size;
        if (! currently_expanding_to_rtl
        if (! currently_expanding_to_rtl
            && ! reload_in_progress && ! reload_completed)
            && ! reload_in_progress && ! reload_completed)
          emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
          emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
        else
        else
          {
          {
            operands[0] = dest;
            operands[0] = dest;
            operands[2] = GEN_INT (16 - insize);
            operands[2] = GEN_INT (16 - insize);
            gen_shifty_hi_op (ASHIFT, operands);
            gen_shifty_hi_op (ASHIFT, operands);
            emit_insn (gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
            emit_insn (gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
          }
          }
        /* Don't use gen_ashrsi3 because it generates new pseudos.  */
        /* Don't use gen_ashrsi3 because it generates new pseudos.  */
        while (--i >= 0)
        while (--i >= 0)
          gen_ashift (ASHIFTRT, 1, dest);
          gen_ashift (ASHIFTRT, 1, dest);
        break;
        break;
      }
      }
    case 6:
    case 6:
    case 7:
    case 7:
      /* Don't expand fine-grained when combining, because that will
      /* Don't expand fine-grained when combining, because that will
         make the pattern fail.  */
         make the pattern fail.  */
      if (! currently_expanding_to_rtl
      if (! currently_expanding_to_rtl
          && ! reload_in_progress && ! reload_completed)
          && ! reload_in_progress && ! reload_completed)
        {
        {
          emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
          emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
          emit_insn (gen_movsi (dest, source));
          emit_insn (gen_movsi (dest, source));
          break;
          break;
        }
        }
      emit_insn (gen_andsi3 (dest, source, GEN_INT ((1 << insize) - 1)));
      emit_insn (gen_andsi3 (dest, source, GEN_INT ((1 << insize) - 1)));
      emit_insn (gen_xorsi3 (dest, dest, GEN_INT (1 << (insize - 1))));
      emit_insn (gen_xorsi3 (dest, dest, GEN_INT (1 << (insize - 1))));
      emit_insn (gen_addsi3 (dest, dest, GEN_INT (-1 << (insize - 1))));
      emit_insn (gen_addsi3 (dest, dest, GEN_INT (-1 << (insize - 1))));
      operands[0] = dest;
      operands[0] = dest;
      operands[2] = kind == 7 ? GEN_INT (left + 1) : left_rtx;
      operands[2] = kind == 7 ? GEN_INT (left + 1) : left_rtx;
      gen_shifty_op (ASHIFT, operands);
      gen_shifty_op (ASHIFT, operands);
      if (kind == 7)
      if (kind == 7)
        emit_insn (gen_ashrsi3_k (dest, dest, const1_rtx));
        emit_insn (gen_ashrsi3_k (dest, dest, const1_rtx));
      break;
      break;
    default:
    default:
      return -1;
      return -1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Prefix a symbol_ref name with "datalabel".  */
/* Prefix a symbol_ref name with "datalabel".  */
 
 
rtx
rtx
gen_datalabel_ref (rtx sym)
gen_datalabel_ref (rtx sym)
{
{
  const char *str;
  const char *str;
 
 
  if (GET_CODE (sym) == LABEL_REF)
  if (GET_CODE (sym) == LABEL_REF)
    return gen_rtx_CONST (GET_MODE (sym),
    return gen_rtx_CONST (GET_MODE (sym),
                          gen_rtx_UNSPEC (GET_MODE (sym),
                          gen_rtx_UNSPEC (GET_MODE (sym),
                                          gen_rtvec (1, sym),
                                          gen_rtvec (1, sym),
                                          UNSPEC_DATALABEL));
                                          UNSPEC_DATALABEL));
 
 
  gcc_assert (GET_CODE (sym) == SYMBOL_REF);
  gcc_assert (GET_CODE (sym) == SYMBOL_REF);
 
 
  str = XSTR (sym, 0);
  str = XSTR (sym, 0);
  /* Share all SYMBOL_REF strings with the same value - that is important
  /* Share all SYMBOL_REF strings with the same value - that is important
     for cse.  */
     for cse.  */
  str = IDENTIFIER_POINTER (get_identifier (str));
  str = IDENTIFIER_POINTER (get_identifier (str));
  XSTR (sym, 0) = str;
  XSTR (sym, 0) = str;
 
 
  return sym;
  return sym;
}
}
 
 


static alloc_pool label_ref_list_pool;
static alloc_pool label_ref_list_pool;
 
 
typedef struct label_ref_list_d
typedef struct label_ref_list_d
{
{
  rtx label;
  rtx label;
  struct label_ref_list_d *next;
  struct label_ref_list_d *next;
} *label_ref_list_t;
} *label_ref_list_t;
 
 
/* The SH cannot load a large constant into a register, constants have to
/* The SH cannot load a large constant into a register, constants have to
   come from a pc relative load.  The reference of a pc relative load
   come from a pc relative load.  The reference of a pc relative load
   instruction must be less than 1k in front of the instruction.  This
   instruction must be less than 1k in front of the instruction.  This
   means that we often have to dump a constant inside a function, and
   means that we often have to dump a constant inside a function, and
   generate code to branch around it.
   generate code to branch around it.
 
 
   It is important to minimize this, since the branches will slow things
   It is important to minimize this, since the branches will slow things
   down and make things bigger.
   down and make things bigger.
 
 
   Worst case code looks like:
   Worst case code looks like:
 
 
   mov.l L1,rn
   mov.l L1,rn
   bra   L2
   bra   L2
   nop
   nop
   align
   align
   L1:   .long value
   L1:   .long value
   L2:
   L2:
   ..
   ..
 
 
   mov.l L3,rn
   mov.l L3,rn
   bra   L4
   bra   L4
   nop
   nop
   align
   align
   L3:   .long value
   L3:   .long value
   L4:
   L4:
   ..
   ..
 
 
   We fix this by performing a scan before scheduling, which notices which
   We fix this by performing a scan before scheduling, which notices which
   instructions need to have their operands fetched from the constant table
   instructions need to have their operands fetched from the constant table
   and builds the table.
   and builds the table.
 
 
   The algorithm is:
   The algorithm is:
 
 
   scan, find an instruction which needs a pcrel move.  Look forward, find the
   scan, find an instruction which needs a pcrel move.  Look forward, find the
   last barrier which is within MAX_COUNT bytes of the requirement.
   last barrier which is within MAX_COUNT bytes of the requirement.
   If there isn't one, make one.  Process all the instructions between
   If there isn't one, make one.  Process all the instructions between
   the find and the barrier.
   the find and the barrier.
 
 
   In the above example, we can tell that L3 is within 1k of L1, so
   In the above example, we can tell that L3 is within 1k of L1, so
   the first move can be shrunk from the 3 insn+constant sequence into
   the first move can be shrunk from the 3 insn+constant sequence into
   just 1 insn, and the constant moved to L3 to make:
   just 1 insn, and the constant moved to L3 to make:
 
 
   mov.l        L1,rn
   mov.l        L1,rn
   ..
   ..
   mov.l        L3,rn
   mov.l        L3,rn
   bra          L4
   bra          L4
   nop
   nop
   align
   align
   L3:.long value
   L3:.long value
   L4:.long value
   L4:.long value
 
 
   Then the second move becomes the target for the shortening process.  */
   Then the second move becomes the target for the shortening process.  */
 
 
typedef struct
typedef struct
{
{
  rtx value;                    /* Value in table.  */
  rtx value;                    /* Value in table.  */
  rtx label;                    /* Label of value.  */
  rtx label;                    /* Label of value.  */
  label_ref_list_t wend;        /* End of window.  */
  label_ref_list_t wend;        /* End of window.  */
  enum machine_mode mode;       /* Mode of value.  */
  enum machine_mode mode;       /* Mode of value.  */
 
 
  /* True if this constant is accessed as part of a post-increment
  /* True if this constant is accessed as part of a post-increment
     sequence.  Note that HImode constants are never accessed in this way.  */
     sequence.  Note that HImode constants are never accessed in this way.  */
  bool part_of_sequence_p;
  bool part_of_sequence_p;
} pool_node;
} pool_node;
 
 
/* The maximum number of constants that can fit into one pool, since
/* The maximum number of constants that can fit into one pool, since
   constants in the range 0..510 are at least 2 bytes long, and in the
   constants in the range 0..510 are at least 2 bytes long, and in the
   range from there to 1018 at least 4 bytes.  */
   range from there to 1018 at least 4 bytes.  */
 
 
#define MAX_POOL_SIZE 372
#define MAX_POOL_SIZE 372
static pool_node pool_vector[MAX_POOL_SIZE];
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
static int pool_size;
static rtx pool_window_label;
static rtx pool_window_label;
static int pool_window_last;
static int pool_window_last;
 
 
static int max_labelno_before_reorg;
static int max_labelno_before_reorg;
 
 
/* ??? If we need a constant in HImode which is the truncated value of a
/* ??? If we need a constant in HImode which is the truncated value of a
   constant we need in SImode, we could combine the two entries thus saving
   constant we need in SImode, we could combine the two entries thus saving
   two bytes.  Is this common enough to be worth the effort of implementing
   two bytes.  Is this common enough to be worth the effort of implementing
   it?  */
   it?  */
 
 
/* ??? This stuff should be done at the same time that we shorten branches.
/* ??? This stuff should be done at the same time that we shorten branches.
   As it is now, we must assume that all branches are the maximum size, and
   As it is now, we must assume that all branches are the maximum size, and
   this causes us to almost always output constant pools sooner than
   this causes us to almost always output constant pools sooner than
   necessary.  */
   necessary.  */
 
 
/* Add a constant to the pool and return its label.  */
/* Add a constant to the pool and return its label.  */
 
 
static rtx
static rtx
add_constant (rtx x, enum machine_mode mode, rtx last_value)
add_constant (rtx x, enum machine_mode mode, rtx last_value)
{
{
  int i;
  int i;
  rtx lab, new_rtx;
  rtx lab, new_rtx;
  label_ref_list_t ref, newref;
  label_ref_list_t ref, newref;
 
 
  /* First see if we've already got it.  */
  /* First see if we've already got it.  */
  for (i = 0; i < pool_size; i++)
  for (i = 0; i < pool_size; i++)
    {
    {
      if (x->code == pool_vector[i].value->code
      if (x->code == pool_vector[i].value->code
          && mode == pool_vector[i].mode)
          && mode == pool_vector[i].mode)
        {
        {
          if (x->code == CODE_LABEL)
          if (x->code == CODE_LABEL)
            {
            {
              if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
              if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
                continue;
                continue;
            }
            }
          if (rtx_equal_p (x, pool_vector[i].value))
          if (rtx_equal_p (x, pool_vector[i].value))
            {
            {
              lab = new_rtx = 0;
              lab = new_rtx = 0;
              if (! last_value
              if (! last_value
                  || ! i
                  || ! i
                  || ! rtx_equal_p (last_value, pool_vector[i-1].value))
                  || ! rtx_equal_p (last_value, pool_vector[i-1].value))
                {
                {
                  new_rtx = gen_label_rtx ();
                  new_rtx = gen_label_rtx ();
                  LABEL_REFS (new_rtx) = pool_vector[i].label;
                  LABEL_REFS (new_rtx) = pool_vector[i].label;
                  pool_vector[i].label = lab = new_rtx;
                  pool_vector[i].label = lab = new_rtx;
                }
                }
              if (lab && pool_window_label)
              if (lab && pool_window_label)
                {
                {
                  newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
                  newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
                  newref->label = pool_window_label;
                  newref->label = pool_window_label;
                  ref = pool_vector[pool_window_last].wend;
                  ref = pool_vector[pool_window_last].wend;
                  newref->next = ref;
                  newref->next = ref;
                  pool_vector[pool_window_last].wend = newref;
                  pool_vector[pool_window_last].wend = newref;
                }
                }
              if (new_rtx)
              if (new_rtx)
                pool_window_label = new_rtx;
                pool_window_label = new_rtx;
              pool_window_last = i;
              pool_window_last = i;
              return lab;
              return lab;
            }
            }
        }
        }
    }
    }
 
 
  /* Need a new one.  */
  /* Need a new one.  */
  pool_vector[pool_size].value = x;
  pool_vector[pool_size].value = x;
  if (last_value && rtx_equal_p (last_value, pool_vector[pool_size - 1].value))
  if (last_value && rtx_equal_p (last_value, pool_vector[pool_size - 1].value))
    {
    {
      lab = 0;
      lab = 0;
      pool_vector[pool_size - 1].part_of_sequence_p = true;
      pool_vector[pool_size - 1].part_of_sequence_p = true;
    }
    }
  else
  else
    lab = gen_label_rtx ();
    lab = gen_label_rtx ();
  pool_vector[pool_size].mode = mode;
  pool_vector[pool_size].mode = mode;
  pool_vector[pool_size].label = lab;
  pool_vector[pool_size].label = lab;
  pool_vector[pool_size].wend = NULL;
  pool_vector[pool_size].wend = NULL;
  pool_vector[pool_size].part_of_sequence_p = (lab == 0);
  pool_vector[pool_size].part_of_sequence_p = (lab == 0);
  if (lab && pool_window_label)
  if (lab && pool_window_label)
    {
    {
      newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
      newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
      newref->label = pool_window_label;
      newref->label = pool_window_label;
      ref = pool_vector[pool_window_last].wend;
      ref = pool_vector[pool_window_last].wend;
      newref->next = ref;
      newref->next = ref;
      pool_vector[pool_window_last].wend = newref;
      pool_vector[pool_window_last].wend = newref;
    }
    }
  if (lab)
  if (lab)
    pool_window_label = lab;
    pool_window_label = lab;
  pool_window_last = pool_size;
  pool_window_last = pool_size;
  pool_size++;
  pool_size++;
  return lab;
  return lab;
}
}
 
 
/* Output the literal table.  START, if nonzero, is the first instruction
/* Output the literal table.  START, if nonzero, is the first instruction
   this table is needed for, and also indicates that there is at least one
   this table is needed for, and also indicates that there is at least one
   casesi_worker_2 instruction; We have to emit the operand3 labels from
   casesi_worker_2 instruction; We have to emit the operand3 labels from
   these insns at a 4-byte  aligned position.  BARRIER is the barrier
   these insns at a 4-byte  aligned position.  BARRIER is the barrier
   after which we are to place the table.  */
   after which we are to place the table.  */
 
 
static void
static void
dump_table (rtx start, rtx barrier)
dump_table (rtx start, rtx barrier)
{
{
  rtx scan = barrier;
  rtx scan = barrier;
  int i;
  int i;
  int need_align = 1;
  int need_align = 1;
  rtx lab;
  rtx lab;
  label_ref_list_t ref;
  label_ref_list_t ref;
  int have_df = 0;
  int have_df = 0;
 
 
  /* Do two passes, first time dump out the HI sized constants.  */
  /* Do two passes, first time dump out the HI sized constants.  */
 
 
  for (i = 0; i < pool_size; i++)
  for (i = 0; i < pool_size; i++)
    {
    {
      pool_node *p = &pool_vector[i];
      pool_node *p = &pool_vector[i];
 
 
      if (p->mode == HImode)
      if (p->mode == HImode)
        {
        {
          if (need_align)
          if (need_align)
            {
            {
              scan = emit_insn_after (gen_align_2 (), scan);
              scan = emit_insn_after (gen_align_2 (), scan);
              need_align = 0;
              need_align = 0;
            }
            }
          for (lab = p->label; lab; lab = LABEL_REFS (lab))
          for (lab = p->label; lab; lab = LABEL_REFS (lab))
            scan = emit_label_after (lab, scan);
            scan = emit_label_after (lab, scan);
          scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
          scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
                                  scan);
                                  scan);
          for (ref = p->wend; ref; ref = ref->next)
          for (ref = p->wend; ref; ref = ref->next)
            {
            {
              lab = ref->label;
              lab = ref->label;
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
            }
            }
        }
        }
      else if (p->mode == DFmode)
      else if (p->mode == DFmode)
        have_df = 1;
        have_df = 1;
    }
    }
 
 
  need_align = 1;
  need_align = 1;
 
 
  if (start)
  if (start)
    {
    {
      scan = emit_insn_after (gen_align_4 (), scan);
      scan = emit_insn_after (gen_align_4 (), scan);
      need_align = 0;
      need_align = 0;
      for (; start != barrier; start = NEXT_INSN (start))
      for (; start != barrier; start = NEXT_INSN (start))
        if (NONJUMP_INSN_P (start)
        if (NONJUMP_INSN_P (start)
            && recog_memoized (start) == CODE_FOR_casesi_worker_2)
            && recog_memoized (start) == CODE_FOR_casesi_worker_2)
          {
          {
            rtx src = SET_SRC (XVECEXP (PATTERN (start), 0, 0));
            rtx src = SET_SRC (XVECEXP (PATTERN (start), 0, 0));
            rtx lab = XEXP (XVECEXP (src, 0, 3), 0);
            rtx lab = XEXP (XVECEXP (src, 0, 3), 0);
 
 
            scan = emit_label_after (lab, scan);
            scan = emit_label_after (lab, scan);
          }
          }
    }
    }
  if (TARGET_FMOVD && TARGET_ALIGN_DOUBLE && have_df)
  if (TARGET_FMOVD && TARGET_ALIGN_DOUBLE && have_df)
    {
    {
      rtx align_insn = NULL_RTX;
      rtx align_insn = NULL_RTX;
 
 
      scan = emit_label_after (gen_label_rtx (), scan);
      scan = emit_label_after (gen_label_rtx (), scan);
      scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
      scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
      need_align = 0;
      need_align = 0;
 
 
      for (i = 0; i < pool_size; i++)
      for (i = 0; i < pool_size; i++)
        {
        {
          pool_node *p = &pool_vector[i];
          pool_node *p = &pool_vector[i];
 
 
          switch (p->mode)
          switch (p->mode)
            {
            {
            case HImode:
            case HImode:
              break;
              break;
            case SImode:
            case SImode:
            case SFmode:
            case SFmode:
              if (align_insn && !p->part_of_sequence_p)
              if (align_insn && !p->part_of_sequence_p)
                {
                {
                  for (lab = p->label; lab; lab = LABEL_REFS (lab))
                  for (lab = p->label; lab; lab = LABEL_REFS (lab))
                    emit_label_before (lab, align_insn);
                    emit_label_before (lab, align_insn);
                  emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
                  emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
                                    align_insn);
                                    align_insn);
                  for (ref = p->wend; ref; ref = ref->next)
                  for (ref = p->wend; ref; ref = ref->next)
                    {
                    {
                      lab = ref->label;
                      lab = ref->label;
                      emit_insn_before (gen_consttable_window_end (lab),
                      emit_insn_before (gen_consttable_window_end (lab),
                                        align_insn);
                                        align_insn);
                    }
                    }
                  delete_insn (align_insn);
                  delete_insn (align_insn);
                  align_insn = NULL_RTX;
                  align_insn = NULL_RTX;
                  continue;
                  continue;
                }
                }
              else
              else
                {
                {
                  for (lab = p->label; lab; lab = LABEL_REFS (lab))
                  for (lab = p->label; lab; lab = LABEL_REFS (lab))
                    scan = emit_label_after (lab, scan);
                    scan = emit_label_after (lab, scan);
                  scan = emit_insn_after (gen_consttable_4 (p->value,
                  scan = emit_insn_after (gen_consttable_4 (p->value,
                                                            const0_rtx), scan);
                                                            const0_rtx), scan);
                  need_align = ! need_align;
                  need_align = ! need_align;
                }
                }
              break;
              break;
            case DFmode:
            case DFmode:
              if (need_align)
              if (need_align)
                {
                {
                  scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
                  scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
                  align_insn = scan;
                  align_insn = scan;
                  need_align = 0;
                  need_align = 0;
                }
                }
            case DImode:
            case DImode:
              for (lab = p->label; lab; lab = LABEL_REFS (lab))
              for (lab = p->label; lab; lab = LABEL_REFS (lab))
                scan = emit_label_after (lab, scan);
                scan = emit_label_after (lab, scan);
              scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
              scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
                                      scan);
                                      scan);
              break;
              break;
            default:
            default:
              gcc_unreachable ();
              gcc_unreachable ();
            }
            }
 
 
          if (p->mode != HImode)
          if (p->mode != HImode)
            {
            {
              for (ref = p->wend; ref; ref = ref->next)
              for (ref = p->wend; ref; ref = ref->next)
                {
                {
                  lab = ref->label;
                  lab = ref->label;
                  scan = emit_insn_after (gen_consttable_window_end (lab),
                  scan = emit_insn_after (gen_consttable_window_end (lab),
                                          scan);
                                          scan);
                }
                }
            }
            }
        }
        }
 
 
      pool_size = 0;
      pool_size = 0;
    }
    }
 
 
  for (i = 0; i < pool_size; i++)
  for (i = 0; i < pool_size; i++)
    {
    {
      pool_node *p = &pool_vector[i];
      pool_node *p = &pool_vector[i];
 
 
      switch (p->mode)
      switch (p->mode)
        {
        {
        case HImode:
        case HImode:
          break;
          break;
        case SImode:
        case SImode:
        case SFmode:
        case SFmode:
          if (need_align)
          if (need_align)
            {
            {
              need_align = 0;
              need_align = 0;
              scan = emit_label_after (gen_label_rtx (), scan);
              scan = emit_label_after (gen_label_rtx (), scan);
              scan = emit_insn_after (gen_align_4 (), scan);
              scan = emit_insn_after (gen_align_4 (), scan);
            }
            }
          for (lab = p->label; lab; lab = LABEL_REFS (lab))
          for (lab = p->label; lab; lab = LABEL_REFS (lab))
            scan = emit_label_after (lab, scan);
            scan = emit_label_after (lab, scan);
          scan = emit_insn_after (gen_consttable_4 (p->value, const0_rtx),
          scan = emit_insn_after (gen_consttable_4 (p->value, const0_rtx),
                                  scan);
                                  scan);
          break;
          break;
        case DFmode:
        case DFmode:
        case DImode:
        case DImode:
          if (need_align)
          if (need_align)
            {
            {
              need_align = 0;
              need_align = 0;
              scan = emit_label_after (gen_label_rtx (), scan);
              scan = emit_label_after (gen_label_rtx (), scan);
              scan = emit_insn_after (gen_align_4 (), scan);
              scan = emit_insn_after (gen_align_4 (), scan);
            }
            }
          for (lab = p->label; lab; lab = LABEL_REFS (lab))
          for (lab = p->label; lab; lab = LABEL_REFS (lab))
            scan = emit_label_after (lab, scan);
            scan = emit_label_after (lab, scan);
          scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
          scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
                                  scan);
                                  scan);
          break;
          break;
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
      if (p->mode != HImode)
      if (p->mode != HImode)
        {
        {
          for (ref = p->wend; ref; ref = ref->next)
          for (ref = p->wend; ref; ref = ref->next)
            {
            {
              lab = ref->label;
              lab = ref->label;
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
            }
            }
        }
        }
    }
    }
 
 
  scan = emit_insn_after (gen_consttable_end (), scan);
  scan = emit_insn_after (gen_consttable_end (), scan);
  scan = emit_barrier_after (scan);
  scan = emit_barrier_after (scan);
  pool_size = 0;
  pool_size = 0;
  pool_window_label = NULL_RTX;
  pool_window_label = NULL_RTX;
  pool_window_last = 0;
  pool_window_last = 0;
}
}
 
 
/* Return nonzero if constant would be an ok source for a
/* Return nonzero if constant would be an ok source for a
   mov.w instead of a mov.l.  */
   mov.w instead of a mov.l.  */
 
 
static int
static int
hi_const (rtx src)
hi_const (rtx src)
{
{
  return (CONST_INT_P (src)
  return (CONST_INT_P (src)
          && INTVAL (src) >= -32768
          && INTVAL (src) >= -32768
          && INTVAL (src) <= 32767);
          && INTVAL (src) <= 32767);
}
}
 
 
#define MOVA_LABELREF(mova) XVECEXP (SET_SRC (PATTERN (mova)), 0, 0)
#define MOVA_LABELREF(mova) XVECEXP (SET_SRC (PATTERN (mova)), 0, 0)
 
 
/* Nonzero if the insn is a move instruction which needs to be fixed.  */
/* 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
/* ??? 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_I08.  For a SFmode move, we don't
   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.  */
   need to fix it if the input value is CONST_OK_FOR_I08.  */
 
 
static int
static int
broken_move (rtx insn)
broken_move (rtx insn)
{
{
  if (NONJUMP_INSN_P (insn))
  if (NONJUMP_INSN_P (insn))
    {
    {
      rtx pat = PATTERN (insn);
      rtx pat = PATTERN (insn);
      if (GET_CODE (pat) == PARALLEL)
      if (GET_CODE (pat) == PARALLEL)
        pat = XVECEXP (pat, 0, 0);
        pat = XVECEXP (pat, 0, 0);
      if (GET_CODE (pat) == SET
      if (GET_CODE (pat) == SET
          /* We can load any 8-bit value if we don't care what the high
          /* We can load any 8-bit value if we don't care what the high
             order bits end up as.  */
             order bits end up as.  */
          && GET_MODE (SET_DEST (pat)) != QImode
          && GET_MODE (SET_DEST (pat)) != QImode
          && (CONSTANT_P (SET_SRC (pat))
          && (CONSTANT_P (SET_SRC (pat))
              /* Match mova_const.  */
              /* Match mova_const.  */
              || (GET_CODE (SET_SRC (pat)) == UNSPEC
              || (GET_CODE (SET_SRC (pat)) == UNSPEC
                  && XINT (SET_SRC (pat), 1) == UNSPEC_MOVA
                  && XINT (SET_SRC (pat), 1) == UNSPEC_MOVA
                  && GET_CODE (XVECEXP (SET_SRC (pat), 0, 0)) == CONST))
                  && GET_CODE (XVECEXP (SET_SRC (pat), 0, 0)) == CONST))
          && ! (TARGET_SH2E
          && ! (TARGET_SH2E
                && GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
                && GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
                && (fp_zero_operand (SET_SRC (pat))
                && (fp_zero_operand (SET_SRC (pat))
                    || fp_one_operand (SET_SRC (pat)))
                    || fp_one_operand (SET_SRC (pat)))
                /* In general we don't know the current setting of fpscr, so disable fldi.
                /* In general we don't know the current setting of fpscr, so disable fldi.
                   There is an exception if this was a register-register move
                   There is an exception if this was a register-register move
                   before reload - and hence it was ascertained that we have
                   before reload - and hence it was ascertained that we have
                   single precision setting - and in a post-reload optimization
                   single precision setting - and in a post-reload optimization
                   we changed this to do a constant load.  In that case
                   we changed this to do a constant load.  In that case
                   we don't have an r0 clobber, hence we must use fldi.  */
                   we don't have an r0 clobber, hence we must use fldi.  */
                && (TARGET_FMOVD
                && (TARGET_FMOVD
                    || (GET_CODE (XEXP (XVECEXP (PATTERN (insn), 0, 2), 0))
                    || (GET_CODE (XEXP (XVECEXP (PATTERN (insn), 0, 2), 0))
                        == SCRATCH))
                        == SCRATCH))
                && REG_P (SET_DEST (pat))
                && REG_P (SET_DEST (pat))
                && FP_REGISTER_P (REGNO (SET_DEST (pat))))
                && FP_REGISTER_P (REGNO (SET_DEST (pat))))
          && ! (TARGET_SH2A
          && ! (TARGET_SH2A
                && GET_MODE (SET_DEST (pat)) == SImode
                && GET_MODE (SET_DEST (pat)) == SImode
                && (satisfies_constraint_I20 (SET_SRC (pat))
                && (satisfies_constraint_I20 (SET_SRC (pat))
                   || satisfies_constraint_I28 (SET_SRC (pat))))
                   || satisfies_constraint_I28 (SET_SRC (pat))))
          && ! satisfies_constraint_I08 (SET_SRC (pat)))
          && ! satisfies_constraint_I08 (SET_SRC (pat)))
        return 1;
        return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
static int
static int
mova_p (rtx insn)
mova_p (rtx insn)
{
{
  return (NONJUMP_INSN_P (insn)
  return (NONJUMP_INSN_P (insn)
          && GET_CODE (PATTERN (insn)) == SET
          && GET_CODE (PATTERN (insn)) == SET
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
          && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_MOVA
          && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_MOVA
          /* Don't match mova_const.  */
          /* Don't match mova_const.  */
          && GET_CODE (MOVA_LABELREF (insn)) == LABEL_REF);
          && GET_CODE (MOVA_LABELREF (insn)) == LABEL_REF);
}
}
 
 
/* Fix up a mova from a switch that went out of range.  */
/* Fix up a mova from a switch that went out of range.  */
static void
static void
fixup_mova (rtx mova)
fixup_mova (rtx mova)
{
{
  PUT_MODE (XEXP (MOVA_LABELREF (mova), 0), QImode);
  PUT_MODE (XEXP (MOVA_LABELREF (mova), 0), QImode);
  if (! flag_pic)
  if (! flag_pic)
    {
    {
      SET_SRC (PATTERN (mova)) = MOVA_LABELREF (mova);
      SET_SRC (PATTERN (mova)) = MOVA_LABELREF (mova);
      INSN_CODE (mova) = -1;
      INSN_CODE (mova) = -1;
    }
    }
  else
  else
    {
    {
      rtx worker = mova;
      rtx worker = mova;
      rtx lab = gen_label_rtx ();
      rtx lab = gen_label_rtx ();
      rtx wpat, wpat0, wpat1, wsrc, target, base, diff;
      rtx wpat, wpat0, wpat1, wsrc, target, base, diff;
 
 
      do
      do
        {
        {
          worker = NEXT_INSN (worker);
          worker = NEXT_INSN (worker);
          gcc_assert (worker
          gcc_assert (worker
                      && !LABEL_P (worker)
                      && !LABEL_P (worker)
                      && !JUMP_P (worker));
                      && !JUMP_P (worker));
        } while (NOTE_P (worker)
        } while (NOTE_P (worker)
                 || recog_memoized (worker) != CODE_FOR_casesi_worker_1);
                 || recog_memoized (worker) != CODE_FOR_casesi_worker_1);
      wpat = PATTERN (worker);
      wpat = PATTERN (worker);
      wpat0 = XVECEXP (wpat, 0, 0);
      wpat0 = XVECEXP (wpat, 0, 0);
      wpat1 = XVECEXP (wpat, 0, 1);
      wpat1 = XVECEXP (wpat, 0, 1);
      wsrc = SET_SRC (wpat0);
      wsrc = SET_SRC (wpat0);
      PATTERN (worker) = (gen_casesi_worker_2
      PATTERN (worker) = (gen_casesi_worker_2
                          (SET_DEST (wpat0), XVECEXP (wsrc, 0, 1),
                          (SET_DEST (wpat0), XVECEXP (wsrc, 0, 1),
                           XEXP (XVECEXP (wsrc, 0, 2), 0), lab,
                           XEXP (XVECEXP (wsrc, 0, 2), 0), lab,
                           XEXP (wpat1, 0)));
                           XEXP (wpat1, 0)));
      INSN_CODE (worker) = -1;
      INSN_CODE (worker) = -1;
      target = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
      target = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
      base = gen_rtx_LABEL_REF (Pmode, lab);
      base = gen_rtx_LABEL_REF (Pmode, lab);
      diff = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, target, base), UNSPEC_SYMOFF);
      diff = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, target, base), UNSPEC_SYMOFF);
      SET_SRC (PATTERN (mova)) = gen_rtx_CONST (Pmode, diff);
      SET_SRC (PATTERN (mova)) = gen_rtx_CONST (Pmode, diff);
      INSN_CODE (mova) = -1;
      INSN_CODE (mova) = -1;
    }
    }
}
}
 
 
/* NEW_MOVA is a mova we've just encountered while scanning forward.  Update
/* NEW_MOVA is a mova we've just encountered while scanning forward.  Update
   *num_mova, and check if the new mova is not nested within the first one.
   *num_mova, and check if the new mova is not nested within the first one.
   return 0 if *first_mova was replaced, 1 if new_mova was replaced,
   return 0 if *first_mova was replaced, 1 if new_mova was replaced,
   2 if new_mova has been assigned to *first_mova, -1 otherwise..  */
   2 if new_mova has been assigned to *first_mova, -1 otherwise..  */
static int
static int
untangle_mova (int *num_mova, rtx *first_mova, rtx new_mova)
untangle_mova (int *num_mova, rtx *first_mova, rtx new_mova)
{
{
  int n_addr = 0; /* Initialization to shut up spurious warning.  */
  int n_addr = 0; /* Initialization to shut up spurious warning.  */
  int f_target, n_target = 0; /* Likewise.  */
  int f_target, n_target = 0; /* Likewise.  */
 
 
  if (optimize)
  if (optimize)
    {
    {
      /* If NEW_MOVA has no address yet, it will be handled later.  */
      /* If NEW_MOVA has no address yet, it will be handled later.  */
      if (INSN_ADDRESSES_SIZE() <= (unsigned) INSN_UID (new_mova))
      if (INSN_ADDRESSES_SIZE() <= (unsigned) INSN_UID (new_mova))
        return -1;
        return -1;
 
 
      n_addr = INSN_ADDRESSES (INSN_UID (new_mova));
      n_addr = INSN_ADDRESSES (INSN_UID (new_mova));
      n_target = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (new_mova), 0)));
      n_target = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (new_mova), 0)));
      if (n_addr > n_target || n_addr + 1022 < n_target)
      if (n_addr > n_target || n_addr + 1022 < n_target)
        {
        {
          /* Change the mova into a load.
          /* Change the mova into a load.
             broken_move will then return true for it.  */
             broken_move will then return true for it.  */
          fixup_mova (new_mova);
          fixup_mova (new_mova);
          return 1;
          return 1;
        }
        }
    }
    }
  if (!(*num_mova)++)
  if (!(*num_mova)++)
    {
    {
      *first_mova = new_mova;
      *first_mova = new_mova;
      return 2;
      return 2;
    }
    }
  if (!optimize
  if (!optimize
      || ((f_target
      || ((f_target
           = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (*first_mova), 0))))
           = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (*first_mova), 0))))
          >= n_target))
          >= n_target))
    return -1;
    return -1;
 
 
  (*num_mova)--;
  (*num_mova)--;
  if (f_target - INSN_ADDRESSES (INSN_UID (*first_mova))
  if (f_target - INSN_ADDRESSES (INSN_UID (*first_mova))
      > n_target - n_addr)
      > n_target - n_addr)
    {
    {
      fixup_mova (*first_mova);
      fixup_mova (*first_mova);
      return 0;
      return 0;
    }
    }
  else
  else
    {
    {
      fixup_mova (new_mova);
      fixup_mova (new_mova);
      return 1;
      return 1;
    }
    }
}
}
 
 
/* Find the last barrier from insn FROM which is close enough to hold the
/* Find the last barrier from insn FROM which is close enough to hold the
   constant pool.  If we can't find one, then create one near the end of
   constant pool.  If we can't find one, then create one near the end of
   the range.  */
   the range.  */
 
 
static rtx
static rtx
find_barrier (int num_mova, rtx mova, rtx from)
find_barrier (int num_mova, rtx mova, rtx from)
{
{
  int count_si = 0;
  int count_si = 0;
  int count_hi = 0;
  int count_hi = 0;
  int found_hi = 0;
  int found_hi = 0;
  int found_si = 0;
  int found_si = 0;
  int found_di = 0;
  int found_di = 0;
  int hi_align = 2;
  int hi_align = 2;
  int si_align = 2;
  int si_align = 2;
  int leading_mova = num_mova;
  int leading_mova = num_mova;
  rtx barrier_before_mova = 0, found_barrier = 0, good_barrier = 0;
  rtx barrier_before_mova = 0, found_barrier = 0, good_barrier = 0;
  int si_limit;
  int si_limit;
  int hi_limit;
  int hi_limit;
  rtx orig = from;
  rtx orig = from;
  rtx last_got = NULL_RTX;
  rtx last_got = NULL_RTX;
  rtx last_symoff = NULL_RTX;
  rtx last_symoff = NULL_RTX;
 
 
  /* For HImode: range is 510, add 4 because pc counts from address of
  /* For HImode: range is 510, add 4 because pc counts from address of
     second instruction after this one, subtract 2 for the jump instruction
     second instruction after this one, subtract 2 for the jump instruction
     that we may need to emit before the table, subtract 2 for the instruction
     that we may need to emit before the table, subtract 2 for the instruction
     that fills the jump delay slot (in very rare cases, reorg will take an
     that fills the jump delay slot (in very rare cases, reorg will take an
     instruction from after the constant pool or will leave the delay slot
     instruction from after the constant pool or will leave the delay slot
     empty).  This gives 510.
     empty).  This gives 510.
     For SImode: range is 1020, add 4 because pc counts from address of
     For SImode: range is 1020, add 4 because pc counts from address of
     second instruction after this one, subtract 2 in case pc is 2 byte
     second instruction after this one, subtract 2 in case pc is 2 byte
     aligned, subtract 2 for the jump instruction that we may need to emit
     aligned, subtract 2 for the jump instruction that we may need to emit
     before the table, subtract 2 for the instruction that fills the jump
     before the table, subtract 2 for the instruction that fills the jump
     delay slot.  This gives 1018.  */
     delay slot.  This gives 1018.  */
 
 
  /* The branch will always be shortened now that the reference address for
  /* The branch will always be shortened now that the reference address for
     forward branches is the successor address, thus we need no longer make
     forward branches is the successor address, thus we need no longer make
     adjustments to the [sh]i_limit for -O0.  */
     adjustments to the [sh]i_limit for -O0.  */
 
 
  si_limit = 1018;
  si_limit = 1018;
  hi_limit = 510;
  hi_limit = 510;
 
 
  while (from && count_si < si_limit && count_hi < hi_limit)
  while (from && count_si < si_limit && count_hi < hi_limit)
    {
    {
      int inc = get_attr_length (from);
      int inc = get_attr_length (from);
      int new_align = 1;
      int new_align = 1;
 
 
      /* If this is a label that existed at the time of the compute_alignments
      /* If this is a label that existed at the time of the compute_alignments
         call, determine the alignment.  N.B.  When find_barrier recurses for
         call, determine the alignment.  N.B.  When find_barrier recurses for
         an out-of-reach mova, we might see labels at the start of previously
         an out-of-reach mova, we might see labels at the start of previously
         inserted constant tables.  */
         inserted constant tables.  */
      if (LABEL_P (from)
      if (LABEL_P (from)
          && CODE_LABEL_NUMBER (from) <= max_labelno_before_reorg)
          && CODE_LABEL_NUMBER (from) <= max_labelno_before_reorg)
        {
        {
          if (optimize)
          if (optimize)
            new_align = 1 << label_to_alignment (from);
            new_align = 1 << label_to_alignment (from);
          else if (BARRIER_P (prev_nonnote_insn (from)))
          else if (BARRIER_P (prev_nonnote_insn (from)))
            new_align = 1 << barrier_align (from);
            new_align = 1 << barrier_align (from);
          else
          else
            new_align = 1;
            new_align = 1;
          inc = 0;
          inc = 0;
        }
        }
      /* In case we are scanning a constant table because of recursion, check
      /* In case we are scanning a constant table because of recursion, check
         for explicit alignments.  If the table is long, we might be forced
         for explicit alignments.  If the table is long, we might be forced
         to emit the new table in front of it; the length of the alignment
         to emit the new table in front of it; the length of the alignment
         might be the last straw.  */
         might be the last straw.  */
      else if (NONJUMP_INSN_P (from)
      else if (NONJUMP_INSN_P (from)
               && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
               && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
               && XINT (PATTERN (from), 1) == UNSPECV_ALIGN)
               && XINT (PATTERN (from), 1) == UNSPECV_ALIGN)
        new_align = INTVAL (XVECEXP (PATTERN (from), 0, 0));
        new_align = INTVAL (XVECEXP (PATTERN (from), 0, 0));
      /* When we find the end of a constant table, paste the new constant
      /* When we find the end of a constant table, paste the new constant
         at the end.  That is better than putting it in front because
         at the end.  That is better than putting it in front because
         this way, we don't need extra alignment for adding a 4-byte-aligned
         this way, we don't need extra alignment for adding a 4-byte-aligned
         mov(a) label to a 2/4 or 8/4 byte aligned table.  */
         mov(a) label to a 2/4 or 8/4 byte aligned table.  */
      else if (NONJUMP_INSN_P (from)
      else if (NONJUMP_INSN_P (from)
               && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
               && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
               && XINT (PATTERN (from), 1) == UNSPECV_CONST_END)
               && XINT (PATTERN (from), 1) == UNSPECV_CONST_END)
        return from;
        return from;
 
 
      if (BARRIER_P (from))
      if (BARRIER_P (from))
        {
        {
          rtx next;
          rtx next;
 
 
          found_barrier = from;
          found_barrier = from;
 
 
          /* If we are at the end of the function, or in front of an alignment
          /* If we are at the end of the function, or in front of an alignment
             instruction, we need not insert an extra alignment.  We prefer
             instruction, we need not insert an extra alignment.  We prefer
             this kind of barrier.  */
             this kind of barrier.  */
          if (barrier_align (from) > 2)
          if (barrier_align (from) > 2)
            good_barrier = from;
            good_barrier = from;
 
 
          /* If we are at the end of a hot/cold block, dump the constants
          /* If we are at the end of a hot/cold block, dump the constants
             here.  */
             here.  */
          next = NEXT_INSN (from);
          next = NEXT_INSN (from);
          if (next
          if (next
              && NOTE_P (next)
              && NOTE_P (next)
              && NOTE_KIND (next) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
              && NOTE_KIND (next) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
            break;
            break;
        }
        }
 
 
      if (broken_move (from))
      if (broken_move (from))
        {
        {
          rtx pat, src, dst;
          rtx pat, src, dst;
          enum machine_mode mode;
          enum machine_mode mode;
 
 
          pat = PATTERN (from);
          pat = PATTERN (from);
          if (GET_CODE (pat) == PARALLEL)
          if (GET_CODE (pat) == PARALLEL)
            pat = XVECEXP (pat, 0, 0);
            pat = XVECEXP (pat, 0, 0);
          src = SET_SRC (pat);
          src = SET_SRC (pat);
          dst = SET_DEST (pat);
          dst = SET_DEST (pat);
          mode = GET_MODE (dst);
          mode = GET_MODE (dst);
 
 
          /* GOT pcrelat setting comes in pair of
          /* GOT pcrelat setting comes in pair of
             mova       .L8,r0
             mova       .L8,r0
             mov.l      .L8,r12
             mov.l      .L8,r12
             instructions.  (plus add r0,r12).
             instructions.  (plus add r0,r12).
             Remember if we see one without the other.  */
             Remember if we see one without the other.  */
          if (GET_CODE (src) == UNSPEC && PIC_ADDR_P (XVECEXP (src, 0, 0)))
          if (GET_CODE (src) == UNSPEC && PIC_ADDR_P (XVECEXP (src, 0, 0)))
            last_got = last_got ? NULL_RTX : from;
            last_got = last_got ? NULL_RTX : from;
          else if (PIC_ADDR_P (src))
          else if (PIC_ADDR_P (src))
            last_got = last_got ? NULL_RTX : from;
            last_got = last_got ? NULL_RTX : from;
 
 
          /* We must explicitly check the mode, because sometimes the
          /* We must explicitly check the mode, because sometimes the
             front end will generate code to load unsigned constants into
             front end will generate code to load unsigned constants into
             HImode targets without properly sign extending them.  */
             HImode targets without properly sign extending them.  */
          if (mode == HImode
          if (mode == HImode
              || (mode == SImode && hi_const (src) && REGNO (dst) != FPUL_REG))
              || (mode == SImode && hi_const (src) && REGNO (dst) != FPUL_REG))
            {
            {
              found_hi += 2;
              found_hi += 2;
              /* We put the short constants before the long constants, so
              /* We put the short constants before the long constants, so
                 we must count the length of short constants in the range
                 we must count the length of short constants in the range
                 for the long constants.  */
                 for the long constants.  */
              /* ??? This isn't optimal, but is easy to do.  */
              /* ??? This isn't optimal, but is easy to do.  */
              si_limit -= 2;
              si_limit -= 2;
            }
            }
          else
          else
            {
            {
              /* We dump DF/DI constants before SF/SI ones, because
              /* We dump DF/DI constants before SF/SI ones, because
                 the limit is the same, but the alignment requirements
                 the limit is the same, but the alignment requirements
                 are higher.  We may waste up to 4 additional bytes
                 are higher.  We may waste up to 4 additional bytes
                 for alignment, and the DF/DI constant may have
                 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
              if (TARGET_SHCOMPACT
                  && ! found_di
                  && ! found_di
                  && (mode == DFmode || mode == DImode))
                  && (mode == DFmode || mode == DImode))
                {
                {
                  found_di = 1;
                  found_di = 1;
                  si_limit -= 8;
                  si_limit -= 8;
                }
                }
              while (si_align > 2 && found_si + si_align - 2 > count_si)
              while (si_align > 2 && found_si + si_align - 2 > count_si)
                si_align >>= 1;
                si_align >>= 1;
              if (found_si > count_si)
              if (found_si > count_si)
                count_si = found_si;
                count_si = found_si;
              found_si += GET_MODE_SIZE (mode);
              found_si += GET_MODE_SIZE (mode);
              if (num_mova)
              if (num_mova)
                si_limit -= GET_MODE_SIZE (mode);
                si_limit -= GET_MODE_SIZE (mode);
            }
            }
        }
        }
 
 
      if (mova_p (from))
      if (mova_p (from))
        {
        {
          switch (untangle_mova (&num_mova, &mova, from))
          switch (untangle_mova (&num_mova, &mova, from))
            {
            {
              case 1:
              case 1:
                if (flag_pic)
                if (flag_pic)
                  {
                  {
                    rtx src = SET_SRC (PATTERN (from));
                    rtx src = SET_SRC (PATTERN (from));
                    if (GET_CODE (src) == CONST
                    if (GET_CODE (src) == CONST
                        && GET_CODE (XEXP (src, 0)) == UNSPEC
                        && GET_CODE (XEXP (src, 0)) == UNSPEC
                        && XINT (XEXP (src, 0), 1) == UNSPEC_SYMOFF)
                        && XINT (XEXP (src, 0), 1) == UNSPEC_SYMOFF)
                      last_symoff = from;
                      last_symoff = from;
                  }
                  }
                break;
                break;
              case 0:    return find_barrier (0, 0, mova);
              case 0:    return find_barrier (0, 0, mova);
              case 2:
              case 2:
                {
                {
                  leading_mova = 0;
                  leading_mova = 0;
                  barrier_before_mova
                  barrier_before_mova
                    = good_barrier ? good_barrier : found_barrier;
                    = good_barrier ? good_barrier : found_barrier;
                }
                }
              default:  break;
              default:  break;
            }
            }
          if (found_si > count_si)
          if (found_si > count_si)
            count_si = found_si;
            count_si = found_si;
        }
        }
      else if (JUMP_TABLE_DATA_P (from))
      else if (JUMP_TABLE_DATA_P (from))
        {
        {
          if ((num_mova > 1 && GET_MODE (prev_nonnote_insn (from)) == VOIDmode)
          if ((num_mova > 1 && GET_MODE (prev_nonnote_insn (from)) == VOIDmode)
              || (num_mova
              || (num_mova
                  && (prev_nonnote_insn (from)
                  && (prev_nonnote_insn (from)
                      == XEXP (MOVA_LABELREF (mova), 0))))
                      == XEXP (MOVA_LABELREF (mova), 0))))
            num_mova--;
            num_mova--;
          if (barrier_align (next_real_insn (from)) == align_jumps_log)
          if (barrier_align (next_real_insn (from)) == align_jumps_log)
            {
            {
              /* We have just passed the barrier in front of the
              /* We have just passed the barrier in front of the
                 ADDR_DIFF_VEC, which is stored in found_barrier.  Since
                 ADDR_DIFF_VEC, which is stored in found_barrier.  Since
                 the ADDR_DIFF_VEC is accessed as data, just like our pool
                 the ADDR_DIFF_VEC is accessed as data, just like our pool
                 constants, this is a good opportunity to accommodate what
                 constants, this is a good opportunity to accommodate what
                 we have gathered so far.
                 we have gathered so far.
                 If we waited any longer, we could end up at a barrier in
                 If we waited any longer, we could end up at a barrier in
                 front of code, which gives worse cache usage for separated
                 front of code, which gives worse cache usage for separated
                 instruction / data caches.  */
                 instruction / data caches.  */
              good_barrier = found_barrier;
              good_barrier = found_barrier;
              break;
              break;
            }
            }
          else
          else
            {
            {
              rtx body = PATTERN (from);
              rtx body = PATTERN (from);
              inc = XVECLEN (body, 1) * GET_MODE_SIZE (GET_MODE (body));
              inc = XVECLEN (body, 1) * GET_MODE_SIZE (GET_MODE (body));
            }
            }
        }
        }
      /* For the SH1, we generate alignments even after jumps-around-jumps.  */
      /* For the SH1, we generate alignments even after jumps-around-jumps.  */
      else if (JUMP_P (from)
      else if (JUMP_P (from)
               && ! TARGET_SH2
               && ! TARGET_SH2
               && ! TARGET_SMALLCODE)
               && ! TARGET_SMALLCODE)
        new_align = 4;
        new_align = 4;
 
 
      /* There is a possibility that a bf is transformed into a bf/s by the
      /* There is a possibility that a bf is transformed into a bf/s by the
         delay slot scheduler.  */
         delay slot scheduler.  */
      if (JUMP_P (from) && !JUMP_TABLE_DATA_P (from)
      if (JUMP_P (from) && !JUMP_TABLE_DATA_P (from)
          && get_attr_type (from) == TYPE_CBRANCH
          && get_attr_type (from) == TYPE_CBRANCH
          && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (from)))) != SEQUENCE)
          && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (from)))) != SEQUENCE)
        inc += 2;
        inc += 2;
 
 
      if (found_si)
      if (found_si)
        {
        {
          count_si += inc;
          count_si += inc;
          if (new_align > si_align)
          if (new_align > si_align)
            {
            {
              si_limit -= (count_si - 1) & (new_align - si_align);
              si_limit -= (count_si - 1) & (new_align - si_align);
              si_align = new_align;
              si_align = new_align;
            }
            }
          count_si = (count_si + new_align - 1) & -new_align;
          count_si = (count_si + new_align - 1) & -new_align;
        }
        }
      if (found_hi)
      if (found_hi)
        {
        {
          count_hi += inc;
          count_hi += inc;
          if (new_align > hi_align)
          if (new_align > hi_align)
            {
            {
              hi_limit -= (count_hi - 1) & (new_align - hi_align);
              hi_limit -= (count_hi - 1) & (new_align - hi_align);
              hi_align = new_align;
              hi_align = new_align;
            }
            }
          count_hi = (count_hi + new_align - 1) & -new_align;
          count_hi = (count_hi + new_align - 1) & -new_align;
        }
        }
      from = NEXT_INSN (from);
      from = NEXT_INSN (from);
    }
    }
 
 
  if (num_mova)
  if (num_mova)
    {
    {
      if (leading_mova)
      if (leading_mova)
        {
        {
          /* Try as we might, the leading mova is out of range.  Change
          /* Try as we might, the leading mova is out of range.  Change
             it into a load (which will become a pcload) and retry.  */
             it into a load (which will become a pcload) and retry.  */
          fixup_mova (mova);
          fixup_mova (mova);
          return find_barrier (0, 0, mova);
          return find_barrier (0, 0, mova);
        }
        }
      else
      else
        {
        {
          /* Insert the constant pool table before the mova instruction,
          /* Insert the constant pool table before the mova instruction,
             to prevent the mova label reference from going out of range.  */
             to prevent the mova label reference from going out of range.  */
          from = mova;
          from = mova;
          good_barrier = found_barrier = barrier_before_mova;
          good_barrier = found_barrier = barrier_before_mova;
        }
        }
    }
    }
 
 
  if (found_barrier)
  if (found_barrier)
    {
    {
      if (good_barrier && next_real_insn (found_barrier))
      if (good_barrier && next_real_insn (found_barrier))
        found_barrier = good_barrier;
        found_barrier = good_barrier;
    }
    }
  else
  else
    {
    {
      /* We didn't find a barrier in time to dump our stuff,
      /* We didn't find a barrier in time to dump our stuff,
         so we'll make one.  */
         so we'll make one.  */
      rtx label = gen_label_rtx ();
      rtx label = gen_label_rtx ();
 
 
      /* Don't emit a constant table in the middle of insns for
      /* Don't emit a constant table in the middle of insns for
         casesi_worker_2.  This is a bit overkill but is enough
         casesi_worker_2.  This is a bit overkill but is enough
         because casesi_worker_2 wouldn't appear so frequently.  */
         because casesi_worker_2 wouldn't appear so frequently.  */
      if (last_symoff)
      if (last_symoff)
        from = last_symoff;
        from = last_symoff;
 
 
      /* If we exceeded the range, then we must back up over the last
      /* If we exceeded the range, then we must back up over the last
         instruction we looked at.  Otherwise, we just need to undo the
         instruction we looked at.  Otherwise, we just need to undo the
         NEXT_INSN at the end of the loop.  */
         NEXT_INSN at the end of the loop.  */
      if (PREV_INSN (from) != orig
      if (PREV_INSN (from) != orig
          && (count_hi > hi_limit || count_si > si_limit))
          && (count_hi > hi_limit || count_si > si_limit))
        from = PREV_INSN (PREV_INSN (from));
        from = PREV_INSN (PREV_INSN (from));
      else
      else
        from = PREV_INSN (from);
        from = PREV_INSN (from);
 
 
      /* Don't emit a constant table int the middle of global pointer setting,
      /* Don't emit a constant table int the middle of global pointer setting,
         since that that would move the addressing base GOT into another table.
         since that that would move the addressing base GOT into another table.
         We need the first mov instruction before the _GLOBAL_OFFSET_TABLE_
         We need the first mov instruction before the _GLOBAL_OFFSET_TABLE_
         in the pool anyway, so just move up the whole constant pool.  */
         in the pool anyway, so just move up the whole constant pool.  */
      if (last_got)
      if (last_got)
        from = PREV_INSN (last_got);
        from = PREV_INSN (last_got);
 
 
      /* Don't insert the constant pool table at the position which
      /* Don't insert the constant pool table at the position which
         may be the landing pad.  */
         may be the landing pad.  */
      if (flag_exceptions
      if (flag_exceptions
          && CALL_P (from)
          && CALL_P (from)
          && find_reg_note (from, REG_EH_REGION, NULL_RTX))
          && find_reg_note (from, REG_EH_REGION, NULL_RTX))
        from = PREV_INSN (from);
        from = PREV_INSN (from);
 
 
      /* Walk back to be just before any jump or label.
      /* Walk back to be just before any jump or label.
         Putting it before a label reduces the number of times the branch
         Putting it before a label reduces the number of times the branch
         around the constant pool table will be hit.  Putting it before
         around the constant pool table will be hit.  Putting it before
         a jump makes it more likely that the bra delay slot will be
         a jump makes it more likely that the bra delay slot will be
         filled.  */
         filled.  */
      while (NOTE_P (from) || JUMP_P (from)
      while (NOTE_P (from) || JUMP_P (from)
             || LABEL_P (from))
             || LABEL_P (from))
        from = PREV_INSN (from);
        from = PREV_INSN (from);
 
 
      from = emit_jump_insn_after (gen_jump (label), from);
      from = emit_jump_insn_after (gen_jump (label), from);
      JUMP_LABEL (from) = label;
      JUMP_LABEL (from) = label;
      LABEL_NUSES (label) = 1;
      LABEL_NUSES (label) = 1;
      found_barrier = emit_barrier_after (from);
      found_barrier = emit_barrier_after (from);
      emit_label_after (label, found_barrier);
      emit_label_after (label, found_barrier);
    }
    }
 
 
  return found_barrier;
  return found_barrier;
}
}
 
 
/* If the instruction INSN is implemented by a special function, and we can
/* If the instruction INSN is implemented by a special function, and we can
   positively find the register that is used to call the sfunc, and this
   positively find the register that is used to call the sfunc, and this
   register is not used anywhere else in this instruction - except as the
   register is not used anywhere else in this instruction - except as the
   destination of a set, return this register; else, return 0.  */
   destination of a set, return this register; else, return 0.  */
rtx
rtx
sfunc_uses_reg (rtx insn)
sfunc_uses_reg (rtx insn)
{
{
  int i;
  int i;
  rtx pattern, part, reg_part, reg;
  rtx pattern, part, reg_part, reg;
 
 
  if (!NONJUMP_INSN_P (insn))
  if (!NONJUMP_INSN_P (insn))
    return 0;
    return 0;
  pattern = PATTERN (insn);
  pattern = PATTERN (insn);
  if (GET_CODE (pattern) != PARALLEL || get_attr_type (insn) != TYPE_SFUNC)
  if (GET_CODE (pattern) != PARALLEL || get_attr_type (insn) != TYPE_SFUNC)
    return 0;
    return 0;
 
 
  for (reg_part = 0, i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
  for (reg_part = 0, i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
    {
    {
      part = XVECEXP (pattern, 0, i);
      part = XVECEXP (pattern, 0, i);
      if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == SImode)
      if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == SImode)
        reg_part = part;
        reg_part = part;
    }
    }
  if (! reg_part)
  if (! reg_part)
    return 0;
    return 0;
  reg = XEXP (reg_part, 0);
  reg = XEXP (reg_part, 0);
  for (i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
  for (i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
    {
    {
      part = XVECEXP (pattern, 0, i);
      part = XVECEXP (pattern, 0, i);
      if (part == reg_part || GET_CODE (part) == CLOBBER)
      if (part == reg_part || GET_CODE (part) == CLOBBER)
        continue;
        continue;
      if (reg_mentioned_p (reg, ((GET_CODE (part) == SET
      if (reg_mentioned_p (reg, ((GET_CODE (part) == SET
                                  && REG_P (SET_DEST (part)))
                                  && REG_P (SET_DEST (part)))
                                 ? SET_SRC (part) : part)))
                                 ? SET_SRC (part) : part)))
        return 0;
        return 0;
    }
    }
  return reg;
  return reg;
}
}
 
 
/* See if the only way in which INSN uses REG is by calling it, or by
/* See if the only way in which INSN uses REG is by calling it, or by
   setting it while calling it.  Set *SET to a SET rtx if the register
   setting it while calling it.  Set *SET to a SET rtx if the register
   is set by INSN.  */
   is set by INSN.  */
 
 
static int
static int
noncall_uses_reg (rtx reg, rtx insn, rtx *set)
noncall_uses_reg (rtx reg, rtx insn, rtx *set)
{
{
  rtx pattern, reg2;
  rtx pattern, reg2;
 
 
  *set = NULL_RTX;
  *set = NULL_RTX;
 
 
  reg2 = sfunc_uses_reg (insn);
  reg2 = sfunc_uses_reg (insn);
  if (reg2 && REGNO (reg2) == REGNO (reg))
  if (reg2 && REGNO (reg2) == REGNO (reg))
    {
    {
      pattern = single_set (insn);
      pattern = single_set (insn);
      if (pattern
      if (pattern
          && REG_P (SET_DEST (pattern))
          && REG_P (SET_DEST (pattern))
          && REGNO (reg) == REGNO (SET_DEST (pattern)))
          && REGNO (reg) == REGNO (SET_DEST (pattern)))
        *set = pattern;
        *set = pattern;
      return 0;
      return 0;
    }
    }
  if (!CALL_P (insn))
  if (!CALL_P (insn))
    {
    {
      /* We don't use rtx_equal_p because we don't care if the mode is
      /* We don't use rtx_equal_p because we don't care if the mode is
         different.  */
         different.  */
      pattern = single_set (insn);
      pattern = single_set (insn);
      if (pattern
      if (pattern
          && REG_P (SET_DEST (pattern))
          && REG_P (SET_DEST (pattern))
          && REGNO (reg) == REGNO (SET_DEST (pattern)))
          && REGNO (reg) == REGNO (SET_DEST (pattern)))
        {
        {
          rtx par, part;
          rtx par, part;
          int i;
          int i;
 
 
          *set = pattern;
          *set = pattern;
          par = PATTERN (insn);
          par = PATTERN (insn);
          if (GET_CODE (par) == PARALLEL)
          if (GET_CODE (par) == PARALLEL)
            for (i = XVECLEN (par, 0) - 1; i >= 0; i--)
            for (i = XVECLEN (par, 0) - 1; i >= 0; i--)
              {
              {
                part = XVECEXP (par, 0, i);
                part = XVECEXP (par, 0, i);
                if (GET_CODE (part) != SET && reg_mentioned_p (reg, part))
                if (GET_CODE (part) != SET && reg_mentioned_p (reg, part))
                  return 1;
                  return 1;
              }
              }
          return reg_mentioned_p (reg, SET_SRC (pattern));
          return reg_mentioned_p (reg, SET_SRC (pattern));
        }
        }
 
 
      return 1;
      return 1;
    }
    }
 
 
  pattern = PATTERN (insn);
  pattern = PATTERN (insn);
 
 
  if (GET_CODE (pattern) == PARALLEL)
  if (GET_CODE (pattern) == PARALLEL)
    {
    {
      int i;
      int i;
 
 
      for (i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
      for (i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
        if (reg_mentioned_p (reg, XVECEXP (pattern, 0, i)))
        if (reg_mentioned_p (reg, XVECEXP (pattern, 0, i)))
          return 1;
          return 1;
      pattern = XVECEXP (pattern, 0, 0);
      pattern = XVECEXP (pattern, 0, 0);
    }
    }
 
 
  if (GET_CODE (pattern) == SET)
  if (GET_CODE (pattern) == SET)
    {
    {
      if (reg_mentioned_p (reg, SET_DEST (pattern)))
      if (reg_mentioned_p (reg, SET_DEST (pattern)))
        {
        {
          /* We don't use rtx_equal_p, because we don't care if the
          /* We don't use rtx_equal_p, because we don't care if the
             mode is different.  */
             mode is different.  */
          if (!REG_P (SET_DEST (pattern))
          if (!REG_P (SET_DEST (pattern))
              || REGNO (reg) != REGNO (SET_DEST (pattern)))
              || REGNO (reg) != REGNO (SET_DEST (pattern)))
            return 1;
            return 1;
 
 
          *set = pattern;
          *set = pattern;
        }
        }
 
 
      pattern = SET_SRC (pattern);
      pattern = SET_SRC (pattern);
    }
    }
 
 
  if (GET_CODE (pattern) != CALL
  if (GET_CODE (pattern) != CALL
      || !MEM_P (XEXP (pattern, 0))
      || !MEM_P (XEXP (pattern, 0))
      || ! rtx_equal_p (reg, XEXP (XEXP (pattern, 0), 0)))
      || ! rtx_equal_p (reg, XEXP (XEXP (pattern, 0), 0)))
    return 1;
    return 1;
 
 
  return 0;
  return 0;
}
}
 
 
/* Given a X, a pattern of an insn or a part of it, return a mask of used
/* Given a X, a pattern of an insn or a part of it, return a mask of used
   general registers.  Bits 0..15 mean that the respective registers
   general registers.  Bits 0..15 mean that the respective registers
   are used as inputs in the instruction.  Bits 16..31 mean that the
   are used as inputs in the instruction.  Bits 16..31 mean that the
   registers 0..15, respectively, are used as outputs, or are clobbered.
   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.  */
   IS_DEST should be set to 16 if X is the destination of a SET, else to 0.  */
int
int
regs_used (rtx x, int is_dest)
regs_used (rtx x, int is_dest)
{
{
  enum rtx_code code;
  enum rtx_code code;
  const char *fmt;
  const char *fmt;
  int i, used = 0;
  int i, used = 0;
 
 
  if (! x)
  if (! x)
    return used;
    return used;
  code = GET_CODE (x);
  code = GET_CODE (x);
  switch (code)
  switch (code)
    {
    {
    case REG:
    case REG:
      if (REGNO (x) < 16)
      if (REGNO (x) < 16)
        return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
        return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
                << (REGNO (x) + is_dest));
                << (REGNO (x) + is_dest));
      return 0;
      return 0;
    case SUBREG:
    case SUBREG:
      {
      {
        rtx y = SUBREG_REG (x);
        rtx y = SUBREG_REG (x);
 
 
        if (!REG_P (y))
        if (!REG_P (y))
          break;
          break;
        if (REGNO (y) < 16)
        if (REGNO (y) < 16)
          return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
          return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
                  << (REGNO (y) +
                  << (REGNO (y) +
                      subreg_regno_offset (REGNO (y),
                      subreg_regno_offset (REGNO (y),
                                           GET_MODE (y),
                                           GET_MODE (y),
                                           SUBREG_BYTE (x),
                                           SUBREG_BYTE (x),
                                           GET_MODE (x)) + is_dest));
                                           GET_MODE (x)) + is_dest));
        return 0;
        return 0;
      }
      }
    case SET:
    case SET:
      return regs_used (SET_SRC (x), 0) | regs_used (SET_DEST (x), 16);
      return regs_used (SET_SRC (x), 0) | regs_used (SET_DEST (x), 16);
    case RETURN:
    case RETURN:
      /* If there was a return value, it must have been indicated with USE.  */
      /* If there was a return value, it must have been indicated with USE.  */
      return 0x00ffff00;
      return 0x00ffff00;
    case CLOBBER:
    case CLOBBER:
      is_dest = 1;
      is_dest = 1;
      break;
      break;
    case MEM:
    case MEM:
      is_dest = 0;
      is_dest = 0;
      break;
      break;
    case CALL:
    case CALL:
      used |= 0x00ff00f0;
      used |= 0x00ff00f0;
      break;
      break;
    default:
    default:
      break;
      break;
    }
    }
 
 
  fmt = GET_RTX_FORMAT (code);
  fmt = GET_RTX_FORMAT (code);
 
 
  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
    {
    {
      if (fmt[i] == 'E')
      if (fmt[i] == 'E')
        {
        {
          register int j;
          register int j;
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
            used |= regs_used (XVECEXP (x, i, j), is_dest);
            used |= regs_used (XVECEXP (x, i, j), is_dest);
        }
        }
      else if (fmt[i] == 'e')
      else if (fmt[i] == 'e')
        used |= regs_used (XEXP (x, i), is_dest);
        used |= regs_used (XEXP (x, i), is_dest);
    }
    }
  return used;
  return used;
}
}
 
 
/* Create an instruction that prevents redirection of a conditional branch
/* Create an instruction that prevents redirection of a conditional branch
   to the destination of the JUMP with address ADDR.
   to the destination of the JUMP with address ADDR.
   If the branch needs to be implemented as an indirect jump, try to find
   If the branch needs to be implemented as an indirect jump, try to find
   a scratch register for it.
   a scratch register for it.
   If NEED_BLOCK is 0, don't do anything unless we need a scratch register.
   If NEED_BLOCK is 0, don't do anything unless we need a scratch register.
   If any preceding insn that doesn't fit into a delay slot is good enough,
   If any preceding insn that doesn't fit into a delay slot is good enough,
   pass 1.  Pass 2 if a definite blocking insn is needed.
   pass 1.  Pass 2 if a definite blocking insn is needed.
   -1 is used internally to avoid deep recursion.
   -1 is used internally to avoid deep recursion.
   If a blocking instruction is made or recognized, return it.  */
   If a blocking instruction is made or recognized, return it.  */
 
 
static rtx
static rtx
gen_block_redirect (rtx jump, int addr, int need_block)
gen_block_redirect (rtx jump, int addr, int need_block)
{
{
  int dead = 0;
  int dead = 0;
  rtx prev = prev_nonnote_insn (jump);
  rtx prev = prev_nonnote_insn (jump);
  rtx dest;
  rtx dest;
 
 
  /* First, check if we already have an instruction that satisfies our need.  */
  /* First, check if we already have an instruction that satisfies our need.  */
  if (prev && NONJUMP_INSN_P (prev) && ! INSN_DELETED_P (prev))
  if (prev && NONJUMP_INSN_P (prev) && ! INSN_DELETED_P (prev))
    {
    {
      if (INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
      if (INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
        return prev;
        return prev;
      if (GET_CODE (PATTERN (prev)) == USE
      if (GET_CODE (PATTERN (prev)) == USE
          || GET_CODE (PATTERN (prev)) == CLOBBER
          || GET_CODE (PATTERN (prev)) == CLOBBER
          || get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
          || get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
        prev = jump;
        prev = jump;
      else if ((need_block &= ~1) < 0)
      else if ((need_block &= ~1) < 0)
        return prev;
        return prev;
      else if (recog_memoized (prev) == CODE_FOR_block_branch_redirect)
      else if (recog_memoized (prev) == CODE_FOR_block_branch_redirect)
        need_block = 0;
        need_block = 0;
    }
    }
  if (GET_CODE (PATTERN (jump)) == RETURN)
  if (GET_CODE (PATTERN (jump)) == RETURN)
    {
    {
      if (! need_block)
      if (! need_block)
        return prev;
        return prev;
      /* Reorg even does nasty things with return insns that cause branches
      /* Reorg even does nasty things with return insns that cause branches
         to go out of range - see find_end_label and callers.  */
         to go out of range - see find_end_label and callers.  */
      return emit_insn_before (gen_block_branch_redirect (const0_rtx) , jump);
      return emit_insn_before (gen_block_branch_redirect (const0_rtx) , jump);
    }
    }
  /* We can't use JUMP_LABEL here because it might be undefined
  /* We can't use JUMP_LABEL here because it might be undefined
     when not optimizing.  */
     when not optimizing.  */
  dest = XEXP (SET_SRC (PATTERN (jump)), 0);
  dest = XEXP (SET_SRC (PATTERN (jump)), 0);
  /* If the branch is out of range, try to find a scratch register for it.  */
  /* If the branch is out of range, try to find a scratch register for it.  */
  if (optimize
  if (optimize
      && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
      && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
          > 4092 + 4098))
          > 4092 + 4098))
    {
    {
      rtx scan;
      rtx scan;
      /* Don't look for the stack pointer as a scratch register,
      /* Don't look for the stack pointer as a scratch register,
         it would cause trouble if an interrupt occurred.  */
         it would cause trouble if an interrupt occurred.  */
      unsigned attempt = 0x7fff, used;
      unsigned attempt = 0x7fff, used;
      int jump_left = flag_expensive_optimizations + 1;
      int jump_left = flag_expensive_optimizations + 1;
 
 
      /* It is likely that the most recent eligible instruction is wanted for
      /* It is likely that the most recent eligible instruction is wanted for
         the delay slot.  Therefore, find out which registers it uses, and
         the delay slot.  Therefore, find out which registers it uses, and
         try to avoid using them.  */
         try to avoid using them.  */
 
 
      for (scan = jump; (scan = PREV_INSN (scan)); )
      for (scan = jump; (scan = PREV_INSN (scan)); )
        {
        {
          enum rtx_code code;
          enum rtx_code code;
 
 
          if (INSN_DELETED_P (scan))
          if (INSN_DELETED_P (scan))
            continue;
            continue;
          code = GET_CODE (scan);
          code = GET_CODE (scan);
          if (code == CODE_LABEL || code == JUMP_INSN)
          if (code == CODE_LABEL || code == JUMP_INSN)
            break;
            break;
          if (code == INSN
          if (code == INSN
              && GET_CODE (PATTERN (scan)) != USE
              && GET_CODE (PATTERN (scan)) != USE
              && GET_CODE (PATTERN (scan)) != CLOBBER
              && GET_CODE (PATTERN (scan)) != CLOBBER
              && get_attr_in_delay_slot (scan) == IN_DELAY_SLOT_YES)
              && get_attr_in_delay_slot (scan) == IN_DELAY_SLOT_YES)
            {
            {
              attempt &= ~regs_used (PATTERN (scan), 0);
              attempt &= ~regs_used (PATTERN (scan), 0);
              break;
              break;
            }
            }
        }
        }
      for (used = dead = 0, scan = JUMP_LABEL (jump);
      for (used = dead = 0, scan = JUMP_LABEL (jump);
           (scan = NEXT_INSN (scan)); )
           (scan = NEXT_INSN (scan)); )
        {
        {
          enum rtx_code code;
          enum rtx_code code;
 
 
          if (INSN_DELETED_P (scan))
          if (INSN_DELETED_P (scan))
            continue;
            continue;
          code = GET_CODE (scan);
          code = GET_CODE (scan);
          if (INSN_P (scan))
          if (INSN_P (scan))
            {
            {
              used |= regs_used (PATTERN (scan), 0);
              used |= regs_used (PATTERN (scan), 0);
              if (code == CALL_INSN)
              if (code == CALL_INSN)
                used |= regs_used (CALL_INSN_FUNCTION_USAGE (scan), 0);
                used |= regs_used (CALL_INSN_FUNCTION_USAGE (scan), 0);
              dead |= (used >> 16) & ~used;
              dead |= (used >> 16) & ~used;
              if (dead & attempt)
              if (dead & attempt)
                {
                {
                  dead &= attempt;
                  dead &= attempt;
                  break;
                  break;
                }
                }
              if (code == JUMP_INSN)
              if (code == JUMP_INSN)
                {
                {
                  if (jump_left-- && simplejump_p (scan))
                  if (jump_left-- && simplejump_p (scan))
                    scan = JUMP_LABEL (scan);
                    scan = JUMP_LABEL (scan);
                  else
                  else
                    break;
                    break;
                }
                }
            }
            }
        }
        }
      /* Mask out the stack pointer again, in case it was
      /* Mask out the stack pointer again, in case it was
         the only 'free' register we have found.  */
         the only 'free' register we have found.  */
      dead &= 0x7fff;
      dead &= 0x7fff;
    }
    }
  /* If the immediate destination is still in range, check for possible
  /* If the immediate destination is still in range, check for possible
     threading with a jump beyond the delay slot insn.
     threading with a jump beyond the delay slot insn.
     Don't check if we are called recursively; the jump has been or will be
     Don't check if we are called recursively; the jump has been or will be
     checked in a different invocation then.  */
     checked in a different invocation then.  */
 
 
  else if (optimize && need_block >= 0)
  else if (optimize && need_block >= 0)
    {
    {
      rtx next = next_active_insn (next_active_insn (dest));
      rtx next = next_active_insn (next_active_insn (dest));
      if (next && JUMP_P (next)
      if (next && JUMP_P (next)
          && GET_CODE (PATTERN (next)) == SET
          && GET_CODE (PATTERN (next)) == SET
          && recog_memoized (next) == CODE_FOR_jump_compact)
          && recog_memoized (next) == CODE_FOR_jump_compact)
        {
        {
          dest = JUMP_LABEL (next);
          dest = JUMP_LABEL (next);
          if (dest
          if (dest
              && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
              && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
                  > 4092 + 4098))
                  > 4092 + 4098))
            gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), -1);
            gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), -1);
        }
        }
    }
    }
 
 
  if (dead)
  if (dead)
    {
    {
      rtx reg = gen_rtx_REG (SImode, exact_log2 (dead & -dead));
      rtx reg = gen_rtx_REG (SImode, exact_log2 (dead & -dead));
 
 
      /* It would be nice if we could convert the jump into an indirect
      /* It would be nice if we could convert the jump into an indirect
         jump / far branch right now, and thus exposing all constituent
         jump / far branch right now, and thus exposing all constituent
         instructions to further optimization.  However, reorg uses
         instructions to further optimization.  However, reorg uses
         simplejump_p to determine if there is an unconditional jump where
         simplejump_p to determine if there is an unconditional jump where
         it should try to schedule instructions from the target of the
         it should try to schedule instructions from the target of the
         branch; simplejump_p fails for indirect jumps even if they have
         branch; simplejump_p fails for indirect jumps even if they have
         a JUMP_LABEL.  */
         a JUMP_LABEL.  */
      rtx insn = emit_insn_before (gen_indirect_jump_scratch
      rtx insn = emit_insn_before (gen_indirect_jump_scratch
                                   (reg, GEN_INT (unspec_bbr_uid++)),
                                   (reg, GEN_INT (unspec_bbr_uid++)),
                                   jump);
                                   jump);
      /* ??? We would like this to have the scope of the jump, but that
      /* ??? We would like this to have the scope of the jump, but that
         scope will change when a delay slot insn of an inner scope is added.
         scope will change when a delay slot insn of an inner scope is added.
         Hence, after delay slot scheduling, we'll have to expect
         Hence, after delay slot scheduling, we'll have to expect
         NOTE_INSN_BLOCK_END notes between the indirect_jump_scratch and
         NOTE_INSN_BLOCK_END notes between the indirect_jump_scratch and
         the jump.  */
         the jump.  */
 
 
      INSN_LOCATOR (insn) = INSN_LOCATOR (jump);
      INSN_LOCATOR (insn) = INSN_LOCATOR (jump);
      INSN_CODE (insn) = CODE_FOR_indirect_jump_scratch;
      INSN_CODE (insn) = CODE_FOR_indirect_jump_scratch;
      return insn;
      return insn;
    }
    }
  else if (need_block)
  else if (need_block)
    /* We can't use JUMP_LABEL here because it might be undefined
    /* We can't use JUMP_LABEL here because it might be undefined
       when not optimizing.  */
       when not optimizing.  */
    return emit_insn_before (gen_block_branch_redirect
    return emit_insn_before (gen_block_branch_redirect
                             (GEN_INT (unspec_bbr_uid++)),
                             (GEN_INT (unspec_bbr_uid++)),
                             jump);
                             jump);
  return prev;
  return prev;
}
}
 
 
#define CONDJUMP_MIN -252
#define CONDJUMP_MIN -252
#define CONDJUMP_MAX 262
#define CONDJUMP_MAX 262
struct far_branch
struct far_branch
{
{
  /* A label (to be placed) in front of the jump
  /* A label (to be placed) in front of the jump
     that jumps to our ultimate destination.  */
     that jumps to our ultimate destination.  */
  rtx near_label;
  rtx near_label;
  /* Where we are going to insert it if we cannot move the jump any farther,
  /* Where we are going to insert it if we cannot move the jump any farther,
     or the jump itself if we have picked up an existing jump.  */
     or the jump itself if we have picked up an existing jump.  */
  rtx insert_place;
  rtx insert_place;
  /* The ultimate destination.  */
  /* The ultimate destination.  */
  rtx far_label;
  rtx far_label;
  struct far_branch *prev;
  struct far_branch *prev;
  /* If the branch has already been created, its address;
  /* If the branch has already been created, its address;
     else the address of its first prospective user.  */
     else the address of its first prospective user.  */
  int address;
  int address;
};
};
 
 
static void gen_far_branch (struct far_branch *);
static void gen_far_branch (struct far_branch *);
enum mdep_reorg_phase_e mdep_reorg_phase;
enum mdep_reorg_phase_e mdep_reorg_phase;
static void
static void
gen_far_branch (struct far_branch *bp)
gen_far_branch (struct far_branch *bp)
{
{
  rtx insn = bp->insert_place;
  rtx insn = bp->insert_place;
  rtx jump;
  rtx jump;
  rtx label = gen_label_rtx ();
  rtx label = gen_label_rtx ();
  int ok;
  int ok;
 
 
  emit_label_after (label, insn);
  emit_label_after (label, insn);
  if (bp->far_label)
  if (bp->far_label)
    {
    {
      jump = emit_jump_insn_after (gen_jump (bp->far_label), insn);
      jump = emit_jump_insn_after (gen_jump (bp->far_label), insn);
      LABEL_NUSES (bp->far_label)++;
      LABEL_NUSES (bp->far_label)++;
    }
    }
  else
  else
    jump = emit_jump_insn_after (gen_return (), insn);
    jump = emit_jump_insn_after (gen_return (), insn);
  /* Emit a barrier so that reorg knows that any following instructions
  /* Emit a barrier so that reorg knows that any following instructions
     are not reachable via a fall-through path.
     are not reachable via a fall-through path.
     But don't do this when not optimizing, since we wouldn't suppress 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
     alignment for the barrier then, and could end up with out-of-range
     pc-relative loads.  */
     pc-relative loads.  */
  if (optimize)
  if (optimize)
    emit_barrier_after (jump);
    emit_barrier_after (jump);
  emit_label_after (bp->near_label, insn);
  emit_label_after (bp->near_label, insn);
  JUMP_LABEL (jump) = bp->far_label;
  JUMP_LABEL (jump) = bp->far_label;
  ok = invert_jump (insn, label, 1);
  ok = invert_jump (insn, label, 1);
  gcc_assert (ok);
  gcc_assert (ok);
 
 
  /* If we are branching around a jump (rather than a return), prevent
  /* 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 -
     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)
     when reorg did this, it pessimized code (we rather hide the delay slot)
     and it could cause branches to go out of range.  */
     and it could cause branches to go out of range.  */
  if (bp->far_label)
  if (bp->far_label)
    (emit_insn_after
    (emit_insn_after
     (gen_stuff_delay_slot
     (gen_stuff_delay_slot
      (GEN_INT (unspec_bbr_uid++),
      (GEN_INT (unspec_bbr_uid++),
       GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)),
       GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)),
      insn));
      insn));
  /* Prevent reorg from undoing our splits.  */
  /* Prevent reorg from undoing our splits.  */
  gen_block_redirect (jump, bp->address += 2, 2);
  gen_block_redirect (jump, bp->address += 2, 2);
}
}
 
 
/* Fix up ADDR_DIFF_VECs.  */
/* Fix up ADDR_DIFF_VECs.  */
void
void
fixup_addr_diff_vecs (rtx first)
fixup_addr_diff_vecs (rtx first)
{
{
  rtx insn;
  rtx insn;
 
 
  for (insn = first; insn; insn = NEXT_INSN (insn))
  for (insn = first; insn; insn = NEXT_INSN (insn))
    {
    {
      rtx vec_lab, pat, prev, prevpat, x, braf_label;
      rtx vec_lab, pat, prev, prevpat, x, braf_label;
 
 
      if (!JUMP_P (insn)
      if (!JUMP_P (insn)
          || GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)
          || GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)
        continue;
        continue;
      pat = PATTERN (insn);
      pat = PATTERN (insn);
      vec_lab = XEXP (XEXP (pat, 0), 0);
      vec_lab = XEXP (XEXP (pat, 0), 0);
 
 
      /* Search the matching casesi_jump_2.  */
      /* Search the matching casesi_jump_2.  */
      for (prev = vec_lab; ; prev = PREV_INSN (prev))
      for (prev = vec_lab; ; prev = PREV_INSN (prev))
        {
        {
          if (!JUMP_P (prev))
          if (!JUMP_P (prev))
            continue;
            continue;
          prevpat = PATTERN (prev);
          prevpat = PATTERN (prev);
          if (GET_CODE (prevpat) != PARALLEL || XVECLEN (prevpat, 0) != 2)
          if (GET_CODE (prevpat) != PARALLEL || XVECLEN (prevpat, 0) != 2)
            continue;
            continue;
          x = XVECEXP (prevpat, 0, 1);
          x = XVECEXP (prevpat, 0, 1);
          if (GET_CODE (x) != USE)
          if (GET_CODE (x) != USE)
            continue;
            continue;
          x = XEXP (x, 0);
          x = XEXP (x, 0);
          if (GET_CODE (x) == LABEL_REF && XEXP (x, 0) == vec_lab)
          if (GET_CODE (x) == LABEL_REF && XEXP (x, 0) == vec_lab)
            break;
            break;
        }
        }
      /* FIXME: This is a bug in the optimizer, but it seems harmless
      /* FIXME: This is a bug in the optimizer, but it seems harmless
         to just avoid panicing.  */
         to just avoid panicing.  */
      if (!prev)
      if (!prev)
        continue;
        continue;
 
 
      /* Emit the reference label of the braf where it belongs, right after
      /* Emit the reference label of the braf where it belongs, right after
         the casesi_jump_2 (i.e. braf).  */
         the casesi_jump_2 (i.e. braf).  */
      braf_label = XEXP (XEXP (SET_SRC (XVECEXP (prevpat, 0, 0)), 1), 0);
      braf_label = XEXP (XEXP (SET_SRC (XVECEXP (prevpat, 0, 0)), 1), 0);
      emit_label_after (braf_label, prev);
      emit_label_after (braf_label, prev);
 
 
      /* Fix up the ADDR_DIF_VEC to be relative
      /* Fix up the ADDR_DIF_VEC to be relative
         to the reference address of the braf.  */
         to the reference address of the braf.  */
      XEXP (XEXP (pat, 0), 0) = braf_label;
      XEXP (XEXP (pat, 0), 0) = braf_label;
    }
    }
}
}
 
 
/* BARRIER_OR_LABEL is either a BARRIER or a CODE_LABEL immediately following
/* BARRIER_OR_LABEL is either a BARRIER or a CODE_LABEL immediately following
   a barrier.  Return the base 2 logarithm of the desired alignment.  */
   a barrier.  Return the base 2 logarithm of the desired alignment.  */
int
int
barrier_align (rtx barrier_or_label)
barrier_align (rtx barrier_or_label)
{
{
  rtx next = next_real_insn (barrier_or_label), pat, prev;
  rtx next = next_real_insn (barrier_or_label), pat, prev;
  int slot, credit, jump_to_next = 0;
  int slot, credit, jump_to_next = 0;
 
 
  if (! next)
  if (! next)
    return 0;
    return 0;
 
 
  pat = PATTERN (next);
  pat = PATTERN (next);
 
 
  if (GET_CODE (pat) == ADDR_DIFF_VEC)
  if (GET_CODE (pat) == ADDR_DIFF_VEC)
    return 2;
    return 2;
 
 
  if (GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_ALIGN)
  if (GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_ALIGN)
    /* This is a barrier in front of a constant table.  */
    /* This is a barrier in front of a constant table.  */
    return 0;
    return 0;
 
 
  prev = prev_real_insn (barrier_or_label);
  prev = prev_real_insn (barrier_or_label);
  if (GET_CODE (PATTERN (prev)) == ADDR_DIFF_VEC)
  if (GET_CODE (PATTERN (prev)) == ADDR_DIFF_VEC)
    {
    {
      pat = PATTERN (prev);
      pat = PATTERN (prev);
      /* If this is a very small table, we want to keep the alignment after
      /* If this is a very small table, we want to keep the alignment after
         the table to the minimum for proper code alignment.  */
         the table to the minimum for proper code alignment.  */
      return ((TARGET_SMALLCODE
      return ((TARGET_SMALLCODE
               || ((unsigned) XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
               || ((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);
              ? 1 << TARGET_SHMEDIA : align_jumps_log);
    }
    }
 
 
  if (TARGET_SMALLCODE)
  if (TARGET_SMALLCODE)
    return 0;
    return 0;
 
 
  if (! TARGET_SH2 || ! optimize)
  if (! TARGET_SH2 || ! optimize)
    return align_jumps_log;
    return align_jumps_log;
 
 
  /* When fixing up pcloads, a constant table might be inserted just before
  /* When fixing up pcloads, a constant table might be inserted just before
     the basic block that ends with the barrier.  Thus, we can't trust the
     the basic block that ends with the barrier.  Thus, we can't trust the
     instruction lengths before that.  */
     instruction lengths before that.  */
  if (mdep_reorg_phase > SH_FIXUP_PCLOAD)
  if (mdep_reorg_phase > SH_FIXUP_PCLOAD)
    {
    {
      /* Check if there is an immediately preceding branch to the insn beyond
      /* Check if there is an immediately preceding branch to the insn beyond
         the barrier.  We must weight the cost of discarding useful information
         the barrier.  We must weight the cost of discarding useful information
         from the current cache line when executing this branch and there is
         from the current cache line when executing this branch and there is
         an alignment, against that of fetching unneeded insn in front of the
         an alignment, against that of fetching unneeded insn in front of the
         branch target when there is no alignment.  */
         branch target when there is no alignment.  */
 
 
      /* There are two delay_slot cases to consider.  One is the simple case
      /* There are two delay_slot cases to consider.  One is the simple case
         where the preceding branch is to the insn beyond the barrier (simple
         where the preceding branch is to the insn beyond the barrier (simple
         delay slot filling), and the other is where the preceding branch has
         delay slot filling), and the other is where the preceding branch has
         a delay slot that is a duplicate of the insn after the barrier
         a delay slot that is a duplicate of the insn after the barrier
         (fill_eager_delay_slots) and the branch is to the insn after the insn
         (fill_eager_delay_slots) and the branch is to the insn after the insn
         after the barrier.  */
         after the barrier.  */
 
 
      /* PREV is presumed to be the JUMP_INSN for the barrier under
      /* PREV is presumed to be the JUMP_INSN for the barrier under
         investigation.  Skip to the insn before it.  */
         investigation.  Skip to the insn before it.  */
      prev = prev_real_insn (prev);
      prev = prev_real_insn (prev);
 
 
      for (slot = 2, credit = (1 << (CACHE_LOG - 2)) + 2;
      for (slot = 2, credit = (1 << (CACHE_LOG - 2)) + 2;
           credit >= 0 && prev && NONJUMP_INSN_P (prev);
           credit >= 0 && prev && NONJUMP_INSN_P (prev);
           prev = prev_real_insn (prev))
           prev = prev_real_insn (prev))
        {
        {
          jump_to_next = 0;
          jump_to_next = 0;
          if (GET_CODE (PATTERN (prev)) == USE
          if (GET_CODE (PATTERN (prev)) == USE
              || GET_CODE (PATTERN (prev)) == CLOBBER)
              || GET_CODE (PATTERN (prev)) == CLOBBER)
            continue;
            continue;
          if (GET_CODE (PATTERN (prev)) == SEQUENCE)
          if (GET_CODE (PATTERN (prev)) == SEQUENCE)
            {
            {
              prev = XVECEXP (PATTERN (prev), 0, 1);
              prev = XVECEXP (PATTERN (prev), 0, 1);
              if (INSN_UID (prev) == INSN_UID (next))
              if (INSN_UID (prev) == INSN_UID (next))
                {
                {
                  /* Delay slot was filled with insn at jump target.  */
                  /* Delay slot was filled with insn at jump target.  */
                  jump_to_next = 1;
                  jump_to_next = 1;
                  continue;
                  continue;
                }
                }
            }
            }
 
 
          if (slot &&
          if (slot &&
              get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
              get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
            slot = 0;
            slot = 0;
          credit -= get_attr_length (prev);
          credit -= get_attr_length (prev);
        }
        }
      if (prev
      if (prev
          && JUMP_P (prev)
          && JUMP_P (prev)
          && JUMP_LABEL (prev))
          && JUMP_LABEL (prev))
        {
        {
          rtx x;
          rtx x;
          if (jump_to_next
          if (jump_to_next
              || next_real_insn (JUMP_LABEL (prev)) == next
              || next_real_insn (JUMP_LABEL (prev)) == next
              /* If relax_delay_slots() decides NEXT was redundant
              /* If relax_delay_slots() decides NEXT was redundant
                 with some previous instruction, it will have
                 with some previous instruction, it will have
                 redirected PREV's jump to the following insn.  */
                 redirected PREV's jump to the following insn.  */
              || JUMP_LABEL (prev) == next_nonnote_insn (next)
              || JUMP_LABEL (prev) == next_nonnote_insn (next)
              /* There is no upper bound on redundant instructions
              /* There is no upper bound on redundant instructions
                 that might have been skipped, but we must not put an
                 that might have been skipped, but we must not put an
                 alignment where none had been before.  */
                 alignment where none had been before.  */
              || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))),
              || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))),
                  (INSN_P (x)
                  (INSN_P (x)
                   && (INSN_CODE (x) == CODE_FOR_block_branch_redirect
                   && (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))))
                       || INSN_CODE (x) == CODE_FOR_stuff_delay_slot))))
            {
            {
              rtx pat = PATTERN (prev);
              rtx pat = PATTERN (prev);
              if (GET_CODE (pat) == PARALLEL)
              if (GET_CODE (pat) == PARALLEL)
                pat = XVECEXP (pat, 0, 0);
                pat = XVECEXP (pat, 0, 0);
              if (credit - slot >= (GET_CODE (SET_SRC (pat)) == PC ? 2 : 0))
              if (credit - slot >= (GET_CODE (SET_SRC (pat)) == PC ? 2 : 0))
                return 0;
                return 0;
            }
            }
        }
        }
    }
    }
 
 
  return align_jumps_log;
  return align_jumps_log;
}
}
 
 
/* If we are inside a phony loop, almost any kind of label can turn up as the
/* If we are inside a phony loop, almost any kind of label can turn up as the
   first one in the loop.  Aligning a braf label causes incorrect switch
   first one in the loop.  Aligning a braf label causes incorrect switch
   destination addresses; we can detect braf labels because they are
   destination addresses; we can detect braf labels because they are
   followed by a BARRIER.
   followed by a BARRIER.
   Applying loop alignment to small constant or switch tables is a waste
   Applying loop alignment to small constant or switch tables is a waste
   of space, so we suppress this too.  */
   of space, so we suppress this too.  */
int
int
sh_loop_align (rtx label)
sh_loop_align (rtx label)
{
{
  rtx next = label;
  rtx next = label;
 
 
  do
  do
    next = next_nonnote_insn (next);
    next = next_nonnote_insn (next);
  while (next && LABEL_P (next));
  while (next && LABEL_P (next));
 
 
  if (! next
  if (! next
      || ! INSN_P (next)
      || ! INSN_P (next)
      || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC
      || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC
      || recog_memoized (next) == CODE_FOR_consttable_2)
      || recog_memoized (next) == CODE_FOR_consttable_2)
    return 0;
    return 0;
 
 
  return align_loops_log;
  return align_loops_log;
}
}
 
 
/* Do a final pass over the function, just before delayed branch
/* Do a final pass over the function, just before delayed branch
   scheduling.  */
   scheduling.  */
 
 
static void
static void
sh_reorg (void)
sh_reorg (void)
{
{
  rtx first, insn, mova = NULL_RTX;
  rtx first, insn, mova = NULL_RTX;
  int num_mova;
  int num_mova;
  rtx r0_rtx = gen_rtx_REG (Pmode, 0);
  rtx r0_rtx = gen_rtx_REG (Pmode, 0);
  rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx);
  rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx);
 
 
  first = get_insns ();
  first = get_insns ();
  max_labelno_before_reorg = max_label_num ();
  max_labelno_before_reorg = max_label_num ();
 
 
  /* We must split call insns before introducing `mova's.  If we're
  /* We must split call insns before introducing `mova's.  If we're
     optimizing, they'll have already been split.  Otherwise, make
     optimizing, they'll have already been split.  Otherwise, make
     sure we don't split them too late.  */
     sure we don't split them too late.  */
  if (! optimize)
  if (! optimize)
    split_all_insns_noflow ();
    split_all_insns_noflow ();
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    return;
    return;
 
 
  /* If relaxing, generate pseudo-ops to associate function calls with
  /* If relaxing, generate pseudo-ops to associate function calls with
     the symbols they call.  It does no harm to not generate these
     the symbols they call.  It does no harm to not generate these
     pseudo-ops.  However, when we can generate them, it enables to
     pseudo-ops.  However, when we can generate them, it enables to
     linker to potentially relax the jsr to a bsr, and eliminate the
     linker to potentially relax the jsr to a bsr, and eliminate the
     register load and, possibly, the constant pool entry.  */
     register load and, possibly, the constant pool entry.  */
 
 
  mdep_reorg_phase = SH_INSERT_USES_LABELS;
  mdep_reorg_phase = SH_INSERT_USES_LABELS;
  if (TARGET_RELAX)
  if (TARGET_RELAX)
    {
    {
      /* Remove all REG_LABEL_OPERAND notes.  We want to use them for our
      /* Remove all REG_LABEL_OPERAND notes.  We want to use them for our
         own purposes.  This works because none of the remaining passes
         own purposes.  This works because none of the remaining passes
         need to look at them.
         need to look at them.
 
 
         ??? But it may break in the future.  We should use a machine
         ??? But it may break in the future.  We should use a machine
         dependent REG_NOTE, or some other approach entirely.  */
         dependent REG_NOTE, or some other approach entirely.  */
      for (insn = first; insn; insn = NEXT_INSN (insn))
      for (insn = first; insn; insn = NEXT_INSN (insn))
        {
        {
          if (INSN_P (insn))
          if (INSN_P (insn))
            {
            {
              rtx note;
              rtx note;
 
 
              while ((note = find_reg_note (insn, REG_LABEL_OPERAND,
              while ((note = find_reg_note (insn, REG_LABEL_OPERAND,
                                            NULL_RTX)) != 0)
                                            NULL_RTX)) != 0)
                remove_note (insn, note);
                remove_note (insn, note);
            }
            }
        }
        }
 
 
      for (insn = first; insn; insn = NEXT_INSN (insn))
      for (insn = first; insn; insn = NEXT_INSN (insn))
        {
        {
          rtx pattern, reg, link, set, scan, dies, label;
          rtx pattern, reg, link, set, scan, dies, label;
          int rescan = 0, foundinsn = 0;
          int rescan = 0, foundinsn = 0;
 
 
          if (CALL_P (insn))
          if (CALL_P (insn))
            {
            {
              pattern = PATTERN (insn);
              pattern = PATTERN (insn);
 
 
              if (GET_CODE (pattern) == PARALLEL)
              if (GET_CODE (pattern) == PARALLEL)
                pattern = XVECEXP (pattern, 0, 0);
                pattern = XVECEXP (pattern, 0, 0);
              if (GET_CODE (pattern) == SET)
              if (GET_CODE (pattern) == SET)
                pattern = SET_SRC (pattern);
                pattern = SET_SRC (pattern);
 
 
              if (GET_CODE (pattern) != CALL
              if (GET_CODE (pattern) != CALL
                  || !MEM_P (XEXP (pattern, 0)))
                  || !MEM_P (XEXP (pattern, 0)))
                continue;
                continue;
 
 
              reg = XEXP (XEXP (pattern, 0), 0);
              reg = XEXP (XEXP (pattern, 0), 0);
            }
            }
          else
          else
            {
            {
              reg = sfunc_uses_reg (insn);
              reg = sfunc_uses_reg (insn);
              if (! reg)
              if (! reg)
                continue;
                continue;
            }
            }
 
 
          if (!REG_P (reg))
          if (!REG_P (reg))
            continue;
            continue;
 
 
          /* Try scanning backward to find where the register is set.  */
          /* Try scanning backward to find where the register is set.  */
          link = NULL;
          link = NULL;
          for (scan = PREV_INSN (insn);
          for (scan = PREV_INSN (insn);
               scan && !LABEL_P (scan);
               scan && !LABEL_P (scan);
               scan = PREV_INSN (scan))
               scan = PREV_INSN (scan))
            {
            {
              if (! INSN_P (scan))
              if (! INSN_P (scan))
                continue;
                continue;
 
 
              if (! reg_mentioned_p (reg, scan))
              if (! reg_mentioned_p (reg, scan))
                continue;
                continue;
 
 
              if (noncall_uses_reg (reg, scan, &set))
              if (noncall_uses_reg (reg, scan, &set))
                break;
                break;
 
 
              if (set)
              if (set)
                {
                {
                  link = scan;
                  link = scan;
                  break;
                  break;
                }
                }
            }
            }
 
 
          if (! link)
          if (! link)
            continue;
            continue;
 
 
          /* The register is set at LINK.  */
          /* The register is set at LINK.  */
 
 
          /* We can only optimize the function call if the register is
          /* We can only optimize the function call if the register is
             being set to a symbol.  In theory, we could sometimes
             being set to a symbol.  In theory, we could sometimes
             optimize calls to a constant location, but the assembler
             optimize calls to a constant location, but the assembler
             and linker do not support that at present.  */
             and linker do not support that at present.  */
          if (GET_CODE (SET_SRC (set)) != SYMBOL_REF
          if (GET_CODE (SET_SRC (set)) != SYMBOL_REF
              && GET_CODE (SET_SRC (set)) != LABEL_REF)
              && GET_CODE (SET_SRC (set)) != LABEL_REF)
            continue;
            continue;
 
 
          /* Scan forward from LINK to the place where REG dies, and
          /* Scan forward from LINK to the place where REG dies, and
             make sure that the only insns which use REG are
             make sure that the only insns which use REG are
             themselves function calls.  */
             themselves function calls.  */
 
 
          /* ??? This doesn't work for call targets that were allocated
          /* ??? This doesn't work for call targets that were allocated
             by reload, since there may not be a REG_DEAD note for the
             by reload, since there may not be a REG_DEAD note for the
             register.  */
             register.  */
 
 
          dies = NULL_RTX;
          dies = NULL_RTX;
          for (scan = NEXT_INSN (link); scan; scan = NEXT_INSN (scan))
          for (scan = NEXT_INSN (link); scan; scan = NEXT_INSN (scan))
            {
            {
              rtx scanset;
              rtx scanset;
 
 
              /* Don't try to trace forward past a CODE_LABEL if we haven't
              /* Don't try to trace forward past a CODE_LABEL if we haven't
                 seen INSN yet.  Ordinarily, we will only find the setting insn
                 seen INSN yet.  Ordinarily, we will only find the setting insn
                 if it is in the same basic block.  However,
                 if it is in the same basic block.  However,
                 cross-jumping can insert code labels in between the load and
                 cross-jumping can insert code labels in between the load and
                 the call, and can result in situations where a single call
                 the call, and can result in situations where a single call
                 insn may have two targets depending on where we came from.  */
                 insn may have two targets depending on where we came from.  */
 
 
              if (LABEL_P (scan) && ! foundinsn)
              if (LABEL_P (scan) && ! foundinsn)
                break;
                break;
 
 
              if (! INSN_P (scan))
              if (! INSN_P (scan))
                continue;
                continue;
 
 
              /* Don't try to trace forward past a JUMP.  To optimize
              /* Don't try to trace forward past a JUMP.  To optimize
                 safely, we would have to check that all the
                 safely, we would have to check that all the
                 instructions at the jump destination did not use REG.  */
                 instructions at the jump destination did not use REG.  */
 
 
              if (JUMP_P (scan))
              if (JUMP_P (scan))
                break;
                break;
 
 
              if (! reg_mentioned_p (reg, scan))
              if (! reg_mentioned_p (reg, scan))
                continue;
                continue;
 
 
              if (noncall_uses_reg (reg, scan, &scanset))
              if (noncall_uses_reg (reg, scan, &scanset))
                break;
                break;
 
 
              if (scan == insn)
              if (scan == insn)
                foundinsn = 1;
                foundinsn = 1;
 
 
              if (scan != insn
              if (scan != insn
                  && (CALL_P (scan) || sfunc_uses_reg (scan)))
                  && (CALL_P (scan) || sfunc_uses_reg (scan)))
                {
                {
                  /* There is a function call to this register other
                  /* There is a function call to this register other
                     than the one we are checking.  If we optimize
                     than the one we are checking.  If we optimize
                     this call, we need to rescan again below.  */
                     this call, we need to rescan again below.  */
                  rescan = 1;
                  rescan = 1;
                }
                }
 
 
              /* ??? We shouldn't have to worry about SCANSET here.
              /* ??? We shouldn't have to worry about SCANSET here.
                 We should just be able to check for a REG_DEAD note
                 We should just be able to check for a REG_DEAD note
                 on a function call.  However, the REG_DEAD notes are
                 on a function call.  However, the REG_DEAD notes are
                 apparently not dependable around libcalls; c-torture
                 apparently not dependable around libcalls; c-torture
                 execute/920501-2 is a test case.  If SCANSET is set,
                 execute/920501-2 is a test case.  If SCANSET is set,
                 then this insn sets the register, so it must have
                 then this insn sets the register, so it must have
                 died earlier.  Unfortunately, this will only handle
                 died earlier.  Unfortunately, this will only handle
                 the cases in which the register is, in fact, set in a
                 the cases in which the register is, in fact, set in a
                 later insn.  */
                 later insn.  */
 
 
              /* ??? We shouldn't have to use FOUNDINSN here.
              /* ??? We shouldn't have to use FOUNDINSN here.
                 This dates back to when we used LOG_LINKS to find
                 This dates back to when we used LOG_LINKS to find
                 the most recent insn which sets the register.  */
                 the most recent insn which sets the register.  */
 
 
              if (foundinsn
              if (foundinsn
                  && (scanset
                  && (scanset
                      || find_reg_note (scan, REG_DEAD, reg)))
                      || find_reg_note (scan, REG_DEAD, reg)))
                {
                {
                  dies = scan;
                  dies = scan;
                  break;
                  break;
                }
                }
            }
            }
 
 
          if (! dies)
          if (! dies)
            {
            {
              /* Either there was a branch, or some insn used REG
              /* Either there was a branch, or some insn used REG
                 other than as a function call address.  */
                 other than as a function call address.  */
              continue;
              continue;
            }
            }
 
 
          /* Create a code label, and put it in a REG_LABEL_OPERAND note
          /* Create a code label, and put it in a REG_LABEL_OPERAND note
             on the insn which sets the register, and on each call insn
             on the insn which sets the register, and on each call insn
             which uses the register.  In final_prescan_insn we look for
             which uses the register.  In final_prescan_insn we look for
             the REG_LABEL_OPERAND notes, and output the appropriate label
             the REG_LABEL_OPERAND notes, and output the appropriate label
             or pseudo-op.  */
             or pseudo-op.  */
 
 
          label = gen_label_rtx ();
          label = gen_label_rtx ();
          add_reg_note (link, REG_LABEL_OPERAND, label);
          add_reg_note (link, REG_LABEL_OPERAND, label);
          add_reg_note (insn, REG_LABEL_OPERAND, label);
          add_reg_note (insn, REG_LABEL_OPERAND, label);
          if (rescan)
          if (rescan)
            {
            {
              scan = link;
              scan = link;
              do
              do
                {
                {
                  rtx reg2;
                  rtx reg2;
 
 
                  scan = NEXT_INSN (scan);
                  scan = NEXT_INSN (scan);
                  if (scan != insn
                  if (scan != insn
                      && ((CALL_P (scan)
                      && ((CALL_P (scan)
                           && reg_mentioned_p (reg, scan))
                           && reg_mentioned_p (reg, scan))
                          || ((reg2 = sfunc_uses_reg (scan))
                          || ((reg2 = sfunc_uses_reg (scan))
                              && REGNO (reg2) == REGNO (reg))))
                              && REGNO (reg2) == REGNO (reg))))
                    add_reg_note (scan, REG_LABEL_OPERAND, label);
                    add_reg_note (scan, REG_LABEL_OPERAND, label);
                }
                }
              while (scan != dies);
              while (scan != dies);
            }
            }
        }
        }
    }
    }
 
 
  if (TARGET_SH2)
  if (TARGET_SH2)
    fixup_addr_diff_vecs (first);
    fixup_addr_diff_vecs (first);
 
 
  if (optimize)
  if (optimize)
    {
    {
      mdep_reorg_phase = SH_SHORTEN_BRANCHES0;
      mdep_reorg_phase = SH_SHORTEN_BRANCHES0;
      shorten_branches (first);
      shorten_branches (first);
    }
    }
 
 
  /* Scan the function looking for move instructions which have to be
  /* Scan the function looking for move instructions which have to be
     changed to pc-relative loads and insert the literal tables.  */
     changed to pc-relative loads and insert the literal tables.  */
  label_ref_list_pool = create_alloc_pool ("label references list",
  label_ref_list_pool = create_alloc_pool ("label references list",
                                           sizeof (struct label_ref_list_d),
                                           sizeof (struct label_ref_list_d),
                                           30);
                                           30);
  mdep_reorg_phase = SH_FIXUP_PCLOAD;
  mdep_reorg_phase = SH_FIXUP_PCLOAD;
  for (insn = first, num_mova = 0; insn; insn = NEXT_INSN (insn))
  for (insn = first, num_mova = 0; insn; insn = NEXT_INSN (insn))
    {
    {
      if (mova_p (insn))
      if (mova_p (insn))
        {
        {
          /* ??? basic block reordering can move a switch table dispatch
          /* ??? basic block reordering can move a switch table dispatch
             below the switch table.  Check if that has happened.
             below the switch table.  Check if that has happened.
             We only have the addresses available when optimizing; but then,
             We only have the addresses available when optimizing; but then,
             this check shouldn't be needed when not optimizing.  */
             this check shouldn't be needed when not optimizing.  */
          if (!untangle_mova (&num_mova, &mova, insn))
          if (!untangle_mova (&num_mova, &mova, insn))
            {
            {
              insn = mova;
              insn = mova;
              num_mova = 0;
              num_mova = 0;
            }
            }
        }
        }
      else if (JUMP_P (insn)
      else if (JUMP_P (insn)
               && GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
               && GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
               && num_mova
               && num_mova
               /* ??? loop invariant motion can also move a mova out of a
               /* ??? loop invariant motion can also move a mova out of a
                  loop.  Since loop does this code motion anyway, maybe we
                  loop.  Since loop does this code motion anyway, maybe we
                  should wrap UNSPEC_MOVA into a CONST, so that reload can
                  should wrap UNSPEC_MOVA into a CONST, so that reload can
                  move it back.  */
                  move it back.  */
               && ((num_mova > 1
               && ((num_mova > 1
                    && GET_MODE (prev_nonnote_insn (insn)) == VOIDmode)
                    && GET_MODE (prev_nonnote_insn (insn)) == VOIDmode)
                   || (prev_nonnote_insn (insn)
                   || (prev_nonnote_insn (insn)
                       == XEXP (MOVA_LABELREF (mova), 0))))
                       == XEXP (MOVA_LABELREF (mova), 0))))
        {
        {
          rtx scan;
          rtx scan;
          int total;
          int total;
 
 
          num_mova--;
          num_mova--;
 
 
          /* Some code might have been inserted between the mova and
          /* Some code might have been inserted between the mova and
             its ADDR_DIFF_VEC.  Check if the mova is still in range.  */
             its ADDR_DIFF_VEC.  Check if the mova is still in range.  */
          for (scan = mova, total = 0; scan != insn; scan = NEXT_INSN (scan))
          for (scan = mova, total = 0; scan != insn; scan = NEXT_INSN (scan))
            total += get_attr_length (scan);
            total += get_attr_length (scan);
 
 
          /* range of mova is 1020, add 4 because pc counts from address of
          /* range of mova is 1020, add 4 because pc counts from address of
             second instruction after this one, subtract 2 in case pc is 2
             second instruction after this one, subtract 2 in case pc is 2
             byte aligned.  Possible alignment needed for the ADDR_DIFF_VEC
             byte aligned.  Possible alignment needed for the ADDR_DIFF_VEC
             cancels out with alignment effects of the mova itself.  */
             cancels out with alignment effects of the mova itself.  */
          if (total > 1022)
          if (total > 1022)
            {
            {
              /* Change the mova into a load, and restart scanning
              /* Change the mova into a load, and restart scanning
                 there.  broken_move will then return true for mova.  */
                 there.  broken_move will then return true for mova.  */
              fixup_mova (mova);
              fixup_mova (mova);
              insn = mova;
              insn = mova;
            }
            }
        }
        }
      if (broken_move (insn)
      if (broken_move (insn)
          || (NONJUMP_INSN_P (insn)
          || (NONJUMP_INSN_P (insn)
              && recog_memoized (insn) == CODE_FOR_casesi_worker_2))
              && recog_memoized (insn) == CODE_FOR_casesi_worker_2))
        {
        {
          rtx scan;
          rtx scan;
          /* Scan ahead looking for a barrier to stick the constant table
          /* Scan ahead looking for a barrier to stick the constant table
             behind.  */
             behind.  */
          rtx barrier = find_barrier (num_mova, mova, insn);
          rtx barrier = find_barrier (num_mova, mova, insn);
          rtx last_float_move = NULL_RTX, last_float = 0, *last_float_addr = NULL;
          rtx last_float_move = NULL_RTX, last_float = 0, *last_float_addr = NULL;
          int need_aligned_label = 0;
          int need_aligned_label = 0;
 
 
          if (num_mova && ! mova_p (mova))
          if (num_mova && ! mova_p (mova))
            {
            {
              /* find_barrier had to change the first mova into a
              /* find_barrier had to change the first mova into a
                 pcload; thus, we have to start with this new pcload.  */
                 pcload; thus, we have to start with this new pcload.  */
              insn = mova;
              insn = mova;
              num_mova = 0;
              num_mova = 0;
            }
            }
          /* Now find all the moves between the points and modify them.  */
          /* Now find all the moves between the points and modify them.  */
          for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
          for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
            {
            {
              if (LABEL_P (scan))
              if (LABEL_P (scan))
                last_float = 0;
                last_float = 0;
              if (NONJUMP_INSN_P (scan)
              if (NONJUMP_INSN_P (scan)
                  && recog_memoized (scan) == CODE_FOR_casesi_worker_2)
                  && recog_memoized (scan) == CODE_FOR_casesi_worker_2)
                need_aligned_label = 1;
                need_aligned_label = 1;
              if (broken_move (scan))
              if (broken_move (scan))
                {
                {
                  rtx *patp = &PATTERN (scan), pat = *patp;
                  rtx *patp = &PATTERN (scan), pat = *patp;
                  rtx src, dst;
                  rtx src, dst;
                  rtx lab;
                  rtx lab;
                  rtx newsrc;
                  rtx newsrc;
                  enum machine_mode mode;
                  enum machine_mode mode;
 
 
                  if (GET_CODE (pat) == PARALLEL)
                  if (GET_CODE (pat) == PARALLEL)
                    patp = &XVECEXP (pat, 0, 0), pat = *patp;
                    patp = &XVECEXP (pat, 0, 0), pat = *patp;
                  src = SET_SRC (pat);
                  src = SET_SRC (pat);
                  dst = SET_DEST (pat);
                  dst = SET_DEST (pat);
                  mode = GET_MODE (dst);
                  mode = GET_MODE (dst);
 
 
                  if (mode == SImode && hi_const (src)
                  if (mode == SImode && hi_const (src)
                      && REGNO (dst) != FPUL_REG)
                      && REGNO (dst) != FPUL_REG)
                    {
                    {
                      int offset = 0;
                      int offset = 0;
 
 
                      mode = HImode;
                      mode = HImode;
                      while (GET_CODE (dst) == SUBREG)
                      while (GET_CODE (dst) == SUBREG)
                        {
                        {
                          offset += subreg_regno_offset (REGNO (SUBREG_REG (dst)),
                          offset += subreg_regno_offset (REGNO (SUBREG_REG (dst)),
                                                         GET_MODE (SUBREG_REG (dst)),
                                                         GET_MODE (SUBREG_REG (dst)),
                                                         SUBREG_BYTE (dst),
                                                         SUBREG_BYTE (dst),
                                                         GET_MODE (dst));
                                                         GET_MODE (dst));
                          dst = SUBREG_REG (dst);
                          dst = SUBREG_REG (dst);
                        }
                        }
                      dst = gen_rtx_REG (HImode, REGNO (dst) + offset);
                      dst = gen_rtx_REG (HImode, REGNO (dst) + offset);
                    }
                    }
                  if (REG_P (dst) && FP_ANY_REGISTER_P (REGNO (dst)))
                  if (REG_P (dst) && FP_ANY_REGISTER_P (REGNO (dst)))
                    {
                    {
                      /* This must be an insn that clobbers r0.  */
                      /* This must be an insn that clobbers r0.  */
                      rtx *clobberp = &XVECEXP (PATTERN (scan), 0,
                      rtx *clobberp = &XVECEXP (PATTERN (scan), 0,
                                                XVECLEN (PATTERN (scan), 0)
                                                XVECLEN (PATTERN (scan), 0)
                                                - 1);
                                                - 1);
                      rtx clobber = *clobberp;
                      rtx clobber = *clobberp;
 
 
                      gcc_assert (GET_CODE (clobber) == CLOBBER
                      gcc_assert (GET_CODE (clobber) == CLOBBER
                                  && rtx_equal_p (XEXP (clobber, 0), r0_rtx));
                                  && rtx_equal_p (XEXP (clobber, 0), r0_rtx));
 
 
                      if (last_float
                      if (last_float
                          && reg_set_between_p (r0_rtx, last_float_move, scan))
                          && reg_set_between_p (r0_rtx, last_float_move, scan))
                        last_float = 0;
                        last_float = 0;
                      if (last_float
                      if (last_float
                          && TARGET_SHCOMPACT
                          && TARGET_SHCOMPACT
                          && GET_MODE_SIZE (mode) != 4
                          && GET_MODE_SIZE (mode) != 4
                          && GET_MODE_SIZE (GET_MODE (last_float)) == 4)
                          && GET_MODE_SIZE (GET_MODE (last_float)) == 4)
                        last_float = 0;
                        last_float = 0;
                      lab = add_constant (src, mode, last_float);
                      lab = add_constant (src, mode, last_float);
                      if (lab)
                      if (lab)
                        emit_insn_before (gen_mova (lab), scan);
                        emit_insn_before (gen_mova (lab), scan);
                      else
                      else
                        {
                        {
                          /* There will be a REG_UNUSED note for r0 on
                          /* There will be a REG_UNUSED note for r0 on
                             LAST_FLOAT_MOVE; we have to change it to REG_INC,
                             LAST_FLOAT_MOVE; we have to change it to REG_INC,
                             lest reorg:mark_target_live_regs will not
                             lest reorg:mark_target_live_regs will not
                             consider r0 to be used, and we end up with delay
                             consider r0 to be used, and we end up with delay
                             slot insn in front of SCAN that clobbers r0.  */
                             slot insn in front of SCAN that clobbers r0.  */
                          rtx note
                          rtx note
                            = find_regno_note (last_float_move, REG_UNUSED, 0);
                            = find_regno_note (last_float_move, REG_UNUSED, 0);
 
 
                          /* If we are not optimizing, then there may not be
                          /* If we are not optimizing, then there may not be
                             a note.  */
                             a note.  */
                          if (note)
                          if (note)
                            PUT_REG_NOTE_KIND (note, REG_INC);
                            PUT_REG_NOTE_KIND (note, REG_INC);
 
 
                          *last_float_addr = r0_inc_rtx;
                          *last_float_addr = r0_inc_rtx;
                        }
                        }
                      last_float_move = scan;
                      last_float_move = scan;
                      last_float = src;
                      last_float = src;
                      newsrc = gen_const_mem (mode,
                      newsrc = gen_const_mem (mode,
                                        (((TARGET_SH4 && ! TARGET_FMOVD)
                                        (((TARGET_SH4 && ! TARGET_FMOVD)
                                          || REGNO (dst) == FPUL_REG)
                                          || REGNO (dst) == FPUL_REG)
                                         ? r0_inc_rtx
                                         ? r0_inc_rtx
                                         : r0_rtx));
                                         : r0_rtx));
                      last_float_addr = &XEXP (newsrc, 0);
                      last_float_addr = &XEXP (newsrc, 0);
 
 
                      /* Remove the clobber of r0.  */
                      /* Remove the clobber of r0.  */
                      *clobberp = gen_rtx_CLOBBER (GET_MODE (clobber),
                      *clobberp = gen_rtx_CLOBBER (GET_MODE (clobber),
                                                   gen_rtx_SCRATCH (Pmode));
                                                   gen_rtx_SCRATCH (Pmode));
                    }
                    }
                  /* This is a mova needing a label.  Create it.  */
                  /* This is a mova needing a label.  Create it.  */
                  else if (GET_CODE (src) == UNSPEC
                  else if (GET_CODE (src) == UNSPEC
                           && XINT (src, 1) == UNSPEC_MOVA
                           && XINT (src, 1) == UNSPEC_MOVA
                           && GET_CODE (XVECEXP (src, 0, 0)) == CONST)
                           && GET_CODE (XVECEXP (src, 0, 0)) == CONST)
                    {
                    {
                      lab = add_constant (XVECEXP (src, 0, 0), mode, 0);
                      lab = add_constant (XVECEXP (src, 0, 0), mode, 0);
                      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
                      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
                      newsrc = gen_rtx_UNSPEC (SImode,
                      newsrc = gen_rtx_UNSPEC (SImode,
                                               gen_rtvec (1, newsrc),
                                               gen_rtvec (1, newsrc),
                                               UNSPEC_MOVA);
                                               UNSPEC_MOVA);
                    }
                    }
                  else
                  else
                    {
                    {
                      lab = add_constant (src, mode, 0);
                      lab = add_constant (src, mode, 0);
                      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
                      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
                      newsrc = gen_const_mem (mode, newsrc);
                      newsrc = gen_const_mem (mode, newsrc);
                    }
                    }
                  *patp = gen_rtx_SET (VOIDmode, dst, newsrc);
                  *patp = gen_rtx_SET (VOIDmode, dst, newsrc);
                  INSN_CODE (scan) = -1;
                  INSN_CODE (scan) = -1;
                }
                }
            }
            }
          dump_table (need_aligned_label ? insn : 0, barrier);
          dump_table (need_aligned_label ? insn : 0, barrier);
          insn = barrier;
          insn = barrier;
        }
        }
    }
    }
  free_alloc_pool (label_ref_list_pool);
  free_alloc_pool (label_ref_list_pool);
  for (insn = first; insn; insn = NEXT_INSN (insn))
  for (insn = first; insn; insn = NEXT_INSN (insn))
    PUT_MODE (insn, VOIDmode);
    PUT_MODE (insn, VOIDmode);
 
 
  mdep_reorg_phase = SH_SHORTEN_BRANCHES1;
  mdep_reorg_phase = SH_SHORTEN_BRANCHES1;
  INSN_ADDRESSES_FREE ();
  INSN_ADDRESSES_FREE ();
  split_branches (first);
  split_branches (first);
 
 
  /* The INSN_REFERENCES_ARE_DELAYED in sh.h is problematic because it
  /* The INSN_REFERENCES_ARE_DELAYED in sh.h is problematic because it
     also has an effect on the register that holds the address of the sfunc.
     also has an effect on the register that holds the address of the sfunc.
     Insert an extra dummy insn in front of each sfunc that pretends to
     Insert an extra dummy insn in front of each sfunc that pretends to
     use this register.  */
     use this register.  */
  if (flag_delayed_branch)
  if (flag_delayed_branch)
    {
    {
      for (insn = first; insn; insn = NEXT_INSN (insn))
      for (insn = first; insn; insn = NEXT_INSN (insn))
        {
        {
          rtx reg = sfunc_uses_reg (insn);
          rtx reg = sfunc_uses_reg (insn);
 
 
          if (! reg)
          if (! reg)
            continue;
            continue;
          emit_insn_before (gen_use_sfunc_addr (reg), insn);
          emit_insn_before (gen_use_sfunc_addr (reg), insn);
        }
        }
    }
    }
#if 0
#if 0
  /* fpscr is not actually a user variable, but we pretend it is for the
  /* fpscr is not actually a user variable, but we pretend it is for the
     sake of the previous optimization passes, since we want it handled like
     sake of the previous optimization passes, since we want it handled like
     one.  However, we don't have any debugging information for it, so turn
     one.  However, we don't have any debugging information for it, so turn
     it into a non-user variable now.  */
     it into a non-user variable now.  */
  if (TARGET_SH4)
  if (TARGET_SH4)
    REG_USERVAR_P (get_fpscr_rtx ()) = 0;
    REG_USERVAR_P (get_fpscr_rtx ()) = 0;
#endif
#endif
  mdep_reorg_phase = SH_AFTER_MDEP_REORG;
  mdep_reorg_phase = SH_AFTER_MDEP_REORG;
}
}
 
 
int
int
get_dest_uid (rtx label, int max_uid)
get_dest_uid (rtx label, int max_uid)
{
{
  rtx dest = next_real_insn (label);
  rtx dest = next_real_insn (label);
  int dest_uid;
  int dest_uid;
  if (! dest)
  if (! dest)
    /* This can happen for an undefined label.  */
    /* This can happen for an undefined label.  */
    return 0;
    return 0;
  dest_uid = INSN_UID (dest);
  dest_uid = INSN_UID (dest);
  /* If this is a newly created branch redirection blocking instruction,
  /* If this is a newly created branch redirection blocking instruction,
     we cannot index the branch_uid or insn_addresses arrays with its
     we cannot index the branch_uid or insn_addresses arrays with its
     uid.  But then, we won't need to, because the actual destination is
     uid.  But then, we won't need to, because the actual destination is
     the following branch.  */
     the following branch.  */
  while (dest_uid >= max_uid)
  while (dest_uid >= max_uid)
    {
    {
      dest = NEXT_INSN (dest);
      dest = NEXT_INSN (dest);
      dest_uid = INSN_UID (dest);
      dest_uid = INSN_UID (dest);
    }
    }
  if (JUMP_P (dest) && GET_CODE (PATTERN (dest)) == RETURN)
  if (JUMP_P (dest) && GET_CODE (PATTERN (dest)) == RETURN)
    return 0;
    return 0;
  return dest_uid;
  return dest_uid;
}
}
 
 
/* Split condbranches that are out of range.  Also add clobbers for
/* Split condbranches that are out of range.  Also add clobbers for
   scratch registers that are needed in far jumps.
   scratch registers that are needed in far jumps.
   We do this before delay slot scheduling, so that it can take our
   We do this before delay slot scheduling, so that it can take our
   newly created instructions into account.  It also allows us to
   newly created instructions into account.  It also allows us to
   find branches with common targets more easily.  */
   find branches with common targets more easily.  */
 
 
static void
static void
split_branches (rtx first)
split_branches (rtx first)
{
{
  rtx insn;
  rtx insn;
  struct far_branch **uid_branch, *far_branch_list = 0;
  struct far_branch **uid_branch, *far_branch_list = 0;
  int max_uid = get_max_uid ();
  int max_uid = get_max_uid ();
  int ok;
  int ok;
 
 
  /* Find out which branches are out of range.  */
  /* Find out which branches are out of range.  */
  shorten_branches (first);
  shorten_branches (first);
 
 
  uid_branch = (struct far_branch **) alloca (max_uid * sizeof *uid_branch);
  uid_branch = (struct far_branch **) alloca (max_uid * sizeof *uid_branch);
  memset ((char *) uid_branch, 0, max_uid * sizeof *uid_branch);
  memset ((char *) uid_branch, 0, max_uid * sizeof *uid_branch);
 
 
  for (insn = first; insn; insn = NEXT_INSN (insn))
  for (insn = first; insn; insn = NEXT_INSN (insn))
    if (! INSN_P (insn))
    if (! INSN_P (insn))
      continue;
      continue;
    else if (INSN_DELETED_P (insn))
    else if (INSN_DELETED_P (insn))
      {
      {
        /* Shorten_branches would split this instruction again,
        /* Shorten_branches would split this instruction again,
           so transform it into a note.  */
           so transform it into a note.  */
        SET_INSN_DELETED (insn);
        SET_INSN_DELETED (insn);
      }
      }
    else if (JUMP_P (insn)
    else if (JUMP_P (insn)
             /* Don't mess with ADDR_DIFF_VEC */
             /* Don't mess with ADDR_DIFF_VEC */
             && (GET_CODE (PATTERN (insn)) == SET
             && (GET_CODE (PATTERN (insn)) == SET
                 || GET_CODE (PATTERN (insn)) == RETURN))
                 || GET_CODE (PATTERN (insn)) == RETURN))
      {
      {
        enum attr_type type = get_attr_type (insn);
        enum attr_type type = get_attr_type (insn);
        if (type == TYPE_CBRANCH)
        if (type == TYPE_CBRANCH)
          {
          {
            rtx next, beyond;
            rtx next, beyond;
 
 
            if (get_attr_length (insn) > 4)
            if (get_attr_length (insn) > 4)
              {
              {
                rtx src = SET_SRC (PATTERN (insn));
                rtx src = SET_SRC (PATTERN (insn));
                rtx olabel = XEXP (XEXP (src, 1), 0);
                rtx olabel = XEXP (XEXP (src, 1), 0);
                int addr = INSN_ADDRESSES (INSN_UID (insn));
                int addr = INSN_ADDRESSES (INSN_UID (insn));
                rtx label = 0;
                rtx label = 0;
                int dest_uid = get_dest_uid (olabel, max_uid);
                int dest_uid = get_dest_uid (olabel, max_uid);
                struct far_branch *bp = uid_branch[dest_uid];
                struct far_branch *bp = uid_branch[dest_uid];
 
 
                /* redirect_jump needs a valid JUMP_LABEL, and it might delete
                /* redirect_jump needs a valid JUMP_LABEL, and it might delete
                   the label if the LABEL_NUSES count drops to zero.  There is
                   the label if the LABEL_NUSES count drops to zero.  There is
                   always a jump_optimize pass that sets these values, but it
                   always a jump_optimize pass that sets these values, but it
                   proceeds to delete unreferenced code, and then if not
                   proceeds to delete unreferenced code, and then if not
                   optimizing, to un-delete the deleted instructions, thus
                   optimizing, to un-delete the deleted instructions, thus
                   leaving labels with too low uses counts.  */
                   leaving labels with too low uses counts.  */
                if (! optimize)
                if (! optimize)
                  {
                  {
                    JUMP_LABEL (insn) = olabel;
                    JUMP_LABEL (insn) = olabel;
                    LABEL_NUSES (olabel)++;
                    LABEL_NUSES (olabel)++;
                  }
                  }
                if (! bp)
                if (! bp)
                  {
                  {
                    bp = (struct far_branch *) alloca (sizeof *bp);
                    bp = (struct far_branch *) alloca (sizeof *bp);
                    uid_branch[dest_uid] = bp;
                    uid_branch[dest_uid] = bp;
                    bp->prev = far_branch_list;
                    bp->prev = far_branch_list;
                    far_branch_list = bp;
                    far_branch_list = bp;
                    bp->far_label
                    bp->far_label
                      = XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0);
                      = XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0);
                    LABEL_NUSES (bp->far_label)++;
                    LABEL_NUSES (bp->far_label)++;
                  }
                  }
                else
                else
                  {
                  {
                    label = bp->near_label;
                    label = bp->near_label;
                    if (! label && bp->address - addr >= CONDJUMP_MIN)
                    if (! label && bp->address - addr >= CONDJUMP_MIN)
                      {
                      {
                        rtx block = bp->insert_place;
                        rtx block = bp->insert_place;
 
 
                        if (GET_CODE (PATTERN (block)) == RETURN)
                        if (GET_CODE (PATTERN (block)) == RETURN)
                          block = PREV_INSN (block);
                          block = PREV_INSN (block);
                        else
                        else
                          block = gen_block_redirect (block,
                          block = gen_block_redirect (block,
                                                      bp->address, 2);
                                                      bp->address, 2);
                        label = emit_label_after (gen_label_rtx (),
                        label = emit_label_after (gen_label_rtx (),
                                                  PREV_INSN (block));
                                                  PREV_INSN (block));
                        bp->near_label = label;
                        bp->near_label = label;
                      }
                      }
                    else if (label && ! NEXT_INSN (label))
                    else if (label && ! NEXT_INSN (label))
                      {
                      {
                        if (addr + 2 - bp->address <= CONDJUMP_MAX)
                        if (addr + 2 - bp->address <= CONDJUMP_MAX)
                          bp->insert_place = insn;
                          bp->insert_place = insn;
                        else
                        else
                          gen_far_branch (bp);
                          gen_far_branch (bp);
                      }
                      }
                  }
                  }
                if (! label
                if (! label
                    || (NEXT_INSN (label) && bp->address - addr < CONDJUMP_MIN))
                    || (NEXT_INSN (label) && bp->address - addr < CONDJUMP_MIN))
                  {
                  {
                    bp->near_label = label = gen_label_rtx ();
                    bp->near_label = label = gen_label_rtx ();
                    bp->insert_place = insn;
                    bp->insert_place = insn;
                    bp->address = addr;
                    bp->address = addr;
                  }
                  }
                ok = redirect_jump (insn, label, 0);
                ok = redirect_jump (insn, label, 0);
                gcc_assert (ok);
                gcc_assert (ok);
              }
              }
            else
            else
              {
              {
                /* get_attr_length (insn) == 2 */
                /* get_attr_length (insn) == 2 */
                /* Check if we have a pattern where reorg wants to redirect
                /* Check if we have a pattern where reorg wants to redirect
                   the branch to a label from an unconditional branch that
                   the branch to a label from an unconditional branch that
                   is too far away.  */
                   is too far away.  */
                /* We can't use JUMP_LABEL here because it might be undefined
                /* We can't use JUMP_LABEL here because it might be undefined
                   when not optimizing.  */
                   when not optimizing.  */
                /* A syntax error might cause beyond to be NULL_RTX.  */
                /* A syntax error might cause beyond to be NULL_RTX.  */
                beyond
                beyond
                  = next_active_insn (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1),
                  = next_active_insn (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1),
                                            0));
                                            0));
 
 
                if (beyond
                if (beyond
                    && (JUMP_P (beyond)
                    && (JUMP_P (beyond)
                        || ((beyond = next_active_insn (beyond))
                        || ((beyond = next_active_insn (beyond))
                            && JUMP_P (beyond)))
                            && JUMP_P (beyond)))
                    && GET_CODE (PATTERN (beyond)) == SET
                    && GET_CODE (PATTERN (beyond)) == SET
                    && recog_memoized (beyond) == CODE_FOR_jump_compact
                    && recog_memoized (beyond) == CODE_FOR_jump_compact
                    && ((INSN_ADDRESSES
                    && ((INSN_ADDRESSES
                         (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0)))
                         (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0)))
                         - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
                         - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
                        > 252 + 258 + 2))
                        > 252 + 258 + 2))
                  gen_block_redirect (beyond,
                  gen_block_redirect (beyond,
                                      INSN_ADDRESSES (INSN_UID (beyond)), 1);
                                      INSN_ADDRESSES (INSN_UID (beyond)), 1);
              }
              }
 
 
            next = next_active_insn (insn);
            next = next_active_insn (insn);
 
 
            if (next
            if (next
                && (JUMP_P (next)
                && (JUMP_P (next)
                    || ((next = next_active_insn (next))
                    || ((next = next_active_insn (next))
                        && JUMP_P (next)))
                        && JUMP_P (next)))
                && GET_CODE (PATTERN (next)) == SET
                && GET_CODE (PATTERN (next)) == SET
                && recog_memoized (next) == CODE_FOR_jump_compact
                && recog_memoized (next) == CODE_FOR_jump_compact
                && ((INSN_ADDRESSES
                && ((INSN_ADDRESSES
                     (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0)))
                     (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0)))
                     - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
                     - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
                    > 252 + 258 + 2))
                    > 252 + 258 + 2))
              gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), 1);
              gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), 1);
          }
          }
        else if (type == TYPE_JUMP || type == TYPE_RETURN)
        else if (type == TYPE_JUMP || type == TYPE_RETURN)
          {
          {
            int addr = INSN_ADDRESSES (INSN_UID (insn));
            int addr = INSN_ADDRESSES (INSN_UID (insn));
            rtx far_label = 0;
            rtx far_label = 0;
            int dest_uid = 0;
            int dest_uid = 0;
            struct far_branch *bp;
            struct far_branch *bp;
 
 
            if (type == TYPE_JUMP)
            if (type == TYPE_JUMP)
              {
              {
                far_label = XEXP (SET_SRC (PATTERN (insn)), 0);
                far_label = XEXP (SET_SRC (PATTERN (insn)), 0);
                dest_uid = get_dest_uid (far_label, max_uid);
                dest_uid = get_dest_uid (far_label, max_uid);
                if (! dest_uid)
                if (! dest_uid)
                  {
                  {
                    /* Parse errors can lead to labels outside
                    /* Parse errors can lead to labels outside
                      the insn stream.  */
                      the insn stream.  */
                    if (! NEXT_INSN (far_label))
                    if (! NEXT_INSN (far_label))
                      continue;
                      continue;
 
 
                    if (! optimize)
                    if (! optimize)
                      {
                      {
                        JUMP_LABEL (insn) = far_label;
                        JUMP_LABEL (insn) = far_label;
                        LABEL_NUSES (far_label)++;
                        LABEL_NUSES (far_label)++;
                      }
                      }
                    redirect_jump (insn, NULL_RTX, 1);
                    redirect_jump (insn, NULL_RTX, 1);
                    far_label = 0;
                    far_label = 0;
                  }
                  }
              }
              }
            bp = uid_branch[dest_uid];
            bp = uid_branch[dest_uid];
            if (! bp)
            if (! bp)
              {
              {
                bp = (struct far_branch *) alloca (sizeof *bp);
                bp = (struct far_branch *) alloca (sizeof *bp);
                uid_branch[dest_uid] = bp;
                uid_branch[dest_uid] = bp;
                bp->prev = far_branch_list;
                bp->prev = far_branch_list;
                far_branch_list = bp;
                far_branch_list = bp;
                bp->near_label = 0;
                bp->near_label = 0;
                bp->far_label = far_label;
                bp->far_label = far_label;
                if (far_label)
                if (far_label)
                  LABEL_NUSES (far_label)++;
                  LABEL_NUSES (far_label)++;
              }
              }
            else if (bp->near_label && ! NEXT_INSN (bp->near_label))
            else if (bp->near_label && ! NEXT_INSN (bp->near_label))
              if (addr - bp->address <= CONDJUMP_MAX)
              if (addr - bp->address <= CONDJUMP_MAX)
                emit_label_after (bp->near_label, PREV_INSN (insn));
                emit_label_after (bp->near_label, PREV_INSN (insn));
              else
              else
                {
                {
                  gen_far_branch (bp);
                  gen_far_branch (bp);
                  bp->near_label = 0;
                  bp->near_label = 0;
                }
                }
            else
            else
              bp->near_label = 0;
              bp->near_label = 0;
            bp->address = addr;
            bp->address = addr;
            bp->insert_place = insn;
            bp->insert_place = insn;
            if (! far_label)
            if (! far_label)
              emit_insn_before (gen_block_branch_redirect (const0_rtx), insn);
              emit_insn_before (gen_block_branch_redirect (const0_rtx), insn);
            else
            else
              gen_block_redirect (insn, addr, bp->near_label ? 2 : 0);
              gen_block_redirect (insn, addr, bp->near_label ? 2 : 0);
          }
          }
      }
      }
  /* Generate all pending far branches,
  /* Generate all pending far branches,
     and free our references to the far labels.  */
     and free our references to the far labels.  */
  while (far_branch_list)
  while (far_branch_list)
    {
    {
      if (far_branch_list->near_label
      if (far_branch_list->near_label
          && ! NEXT_INSN (far_branch_list->near_label))
          && ! NEXT_INSN (far_branch_list->near_label))
        gen_far_branch (far_branch_list);
        gen_far_branch (far_branch_list);
      if (optimize
      if (optimize
          && far_branch_list->far_label
          && far_branch_list->far_label
          && ! --LABEL_NUSES (far_branch_list->far_label))
          && ! --LABEL_NUSES (far_branch_list->far_label))
        delete_insn (far_branch_list->far_label);
        delete_insn (far_branch_list->far_label);
      far_branch_list = far_branch_list->prev;
      far_branch_list = far_branch_list->prev;
    }
    }
 
 
  /* Instruction length information is no longer valid due to the new
  /* Instruction length information is no longer valid due to the new
     instructions that have been generated.  */
     instructions that have been generated.  */
  init_insn_lengths ();
  init_insn_lengths ();
}
}
 
 
/* Dump out instruction addresses, which is useful for debugging the
/* Dump out instruction addresses, which is useful for debugging the
   constant pool table stuff.
   constant pool table stuff.
 
 
   If relaxing, output the label and pseudo-ops used to link together
   If relaxing, output the label and pseudo-ops used to link together
   calls and the instruction which set the registers.  */
   calls and the instruction which set the registers.  */
 
 
/* ??? The addresses printed by this routine for insns are nonsense for
/* ??? The addresses printed by this routine for insns are nonsense for
   insns which are inside of a sequence where none of the inner insns have
   insns which are inside of a sequence where none of the inner insns have
   variable length.  This is because the second pass of shorten_branches
   variable length.  This is because the second pass of shorten_branches
   does not bother to update them.  */
   does not bother to update them.  */
 
 
void
void
final_prescan_insn (rtx insn, rtx *opvec ATTRIBUTE_UNUSED,
final_prescan_insn (rtx insn, rtx *opvec ATTRIBUTE_UNUSED,
                    int noperands ATTRIBUTE_UNUSED)
                    int noperands ATTRIBUTE_UNUSED)
{
{
  if (TARGET_DUMPISIZE)
  if (TARGET_DUMPISIZE)
    fprintf (asm_out_file, "\n! at %04x\n", INSN_ADDRESSES (INSN_UID (insn)));
    fprintf (asm_out_file, "\n! at %04x\n", INSN_ADDRESSES (INSN_UID (insn)));
 
 
  if (TARGET_RELAX)
  if (TARGET_RELAX)
    {
    {
      rtx note;
      rtx note;
 
 
      note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX);
      note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX);
      if (note)
      if (note)
        {
        {
          rtx pattern;
          rtx pattern;
 
 
          pattern = PATTERN (insn);
          pattern = PATTERN (insn);
          if (GET_CODE (pattern) == PARALLEL)
          if (GET_CODE (pattern) == PARALLEL)
            pattern = XVECEXP (pattern, 0, 0);
            pattern = XVECEXP (pattern, 0, 0);
          switch (GET_CODE (pattern))
          switch (GET_CODE (pattern))
            {
            {
            case SET:
            case SET:
              if (GET_CODE (SET_SRC (pattern)) != CALL
              if (GET_CODE (SET_SRC (pattern)) != CALL
                  && get_attr_type (insn) != TYPE_SFUNC)
                  && get_attr_type (insn) != TYPE_SFUNC)
                {
                {
                  targetm.asm_out.internal_label
                  targetm.asm_out.internal_label
                    (asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (note, 0)));
                    (asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (note, 0)));
                  break;
                  break;
                }
                }
              /* else FALLTHROUGH */
              /* else FALLTHROUGH */
            case CALL:
            case CALL:
              asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
              asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
                           CODE_LABEL_NUMBER (XEXP (note, 0)));
                           CODE_LABEL_NUMBER (XEXP (note, 0)));
              break;
              break;
 
 
            default:
            default:
              gcc_unreachable ();
              gcc_unreachable ();
            }
            }
        }
        }
    }
    }
}
}
 
 
/* Dump out any constants accumulated in the final pass.  These will
/* Dump out any constants accumulated in the final pass.  These will
   only be labels.  */
   only be labels.  */
 
 
const char *
const char *
output_jump_label_table (void)
output_jump_label_table (void)
{
{
  int i;
  int i;
 
 
  if (pool_size)
  if (pool_size)
    {
    {
      fprintf (asm_out_file, "\t.align 2\n");
      fprintf (asm_out_file, "\t.align 2\n");
      for (i = 0; i < pool_size; i++)
      for (i = 0; i < pool_size; i++)
        {
        {
          pool_node *p = &pool_vector[i];
          pool_node *p = &pool_vector[i];
 
 
          (*targetm.asm_out.internal_label) (asm_out_file, "L",
          (*targetm.asm_out.internal_label) (asm_out_file, "L",
                                     CODE_LABEL_NUMBER (p->label));
                                     CODE_LABEL_NUMBER (p->label));
          output_asm_insn (".long       %O0", &p->value);
          output_asm_insn (".long       %O0", &p->value);
        }
        }
      pool_size = 0;
      pool_size = 0;
    }
    }
 
 
  return "";
  return "";
}
}


/* A full frame looks like:
/* A full frame looks like:
 
 
   arg-5
   arg-5
   arg-4
   arg-4
   [ if current_function_anonymous_args
   [ if current_function_anonymous_args
   arg-3
   arg-3
   arg-2
   arg-2
   arg-1
   arg-1
   arg-0 ]
   arg-0 ]
   saved-fp
   saved-fp
   saved-r10
   saved-r10
   saved-r11
   saved-r11
   saved-r12
   saved-r12
   saved-pr
   saved-pr
   local-n
   local-n
   ..
   ..
   local-1
   local-1
   local-0        <- fp points here.  */
   local-0        <- fp points here.  */
 
 
/* Number of bytes pushed for anonymous args, used to pass information
/* Number of bytes pushed for anonymous args, used to pass information
   between expand_prologue and expand_epilogue.  */
   between expand_prologue and expand_epilogue.  */
 
 
/* Adjust the stack by SIZE bytes.  REG holds the rtl of the register to be
/* 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
   adjusted.  If epilogue_p is zero, this is for a prologue; otherwise, it's
   for an epilogue and a negative value means that it's for a sibcall
   for an epilogue and a negative value means that it's for a sibcall
   epilogue.  If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET of
   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.  */
   all the registers that are about to be restored, and hence dead.  */
 
 
static void
static void
output_stack_adjust (int size, rtx reg, int epilogue_p,
output_stack_adjust (int size, rtx reg, int epilogue_p,
                     HARD_REG_SET *live_regs_mask, bool frame_p)
                     HARD_REG_SET *live_regs_mask, bool frame_p)
{
{
  rtx (*emit_fn) (rtx) = frame_p ? &frame_insn : &emit_insn;
  rtx (*emit_fn) (rtx) = frame_p ? &frame_insn : &emit_insn;
  if (size)
  if (size)
    {
    {
      HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
      HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
 
 
/* This test is bogus, as output_stack_adjust is used to re-align the
/* This test is bogus, as output_stack_adjust is used to re-align the
   stack.  */
   stack.  */
#if 0
#if 0
      gcc_assert (!(size % align));
      gcc_assert (!(size % align));
#endif
#endif
 
 
      if (CONST_OK_FOR_ADD (size))
      if (CONST_OK_FOR_ADD (size))
        emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size)));
        emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size)));
      /* Try to do it with two partial adjustments; however, we must make
      /* Try to do it with two partial adjustments; however, we must make
         sure that the stack is properly aligned at all times, in case
         sure that the stack is properly aligned at all times, in case
         an interrupt occurs between the two partial adjustments.  */
         an interrupt occurs between the two partial adjustments.  */
      else if (CONST_OK_FOR_ADD (size / 2 & -align)
      else if (CONST_OK_FOR_ADD (size / 2 & -align)
               && CONST_OK_FOR_ADD (size - (size / 2 & -align)))
               && CONST_OK_FOR_ADD (size - (size / 2 & -align)))
        {
        {
          emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size / 2 & -align)));
          emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size / 2 & -align)));
          emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size - (size / 2 & -align))));
          emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size - (size / 2 & -align))));
        }
        }
      else
      else
        {
        {
          rtx const_reg;
          rtx const_reg;
          rtx insn;
          rtx insn;
          int temp = epilogue_p ? 7 : (TARGET_SH5 ? 0 : 1);
          int temp = epilogue_p ? 7 : (TARGET_SH5 ? 0 : 1);
          int i;
          int i;
 
 
          /* If TEMP is invalid, we could temporarily save a general
          /* If TEMP is invalid, we could temporarily save a general
             register to MACL.  However, there is currently no need
             register to MACL.  However, there is currently no need
             to handle this case, so just die when we see it.  */
             to handle this case, so just die when we see it.  */
          if (epilogue_p < 0
          if (epilogue_p < 0
              || current_function_interrupt
              || current_function_interrupt
              || ! call_really_used_regs[temp] || fixed_regs[temp])
              || ! call_really_used_regs[temp] || fixed_regs[temp])
            temp = -1;
            temp = -1;
          if (temp < 0 && ! current_function_interrupt
          if (temp < 0 && ! current_function_interrupt
              && (TARGET_SHMEDIA || epilogue_p >= 0))
              && (TARGET_SHMEDIA || epilogue_p >= 0))
            {
            {
              HARD_REG_SET temps;
              HARD_REG_SET temps;
              COPY_HARD_REG_SET (temps, call_used_reg_set);
              COPY_HARD_REG_SET (temps, call_used_reg_set);
              AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set);
              AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set);
              if (epilogue_p > 0)
              if (epilogue_p > 0)
                {
                {
                  int nreg = 0;
                  int nreg = 0;
                  if (crtl->return_rtx)
                  if (crtl->return_rtx)
                    {
                    {
                      enum machine_mode mode;
                      enum machine_mode mode;
                      mode = GET_MODE (crtl->return_rtx);
                      mode = GET_MODE (crtl->return_rtx);
                      if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
                      if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
                        nreg = HARD_REGNO_NREGS (FIRST_RET_REG, mode);
                        nreg = HARD_REGNO_NREGS (FIRST_RET_REG, mode);
                    }
                    }
                  for (i = 0; i < nreg; i++)
                  for (i = 0; i < nreg; i++)
                    CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
                    CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
                  if (crtl->calls_eh_return)
                  if (crtl->calls_eh_return)
                    {
                    {
                      CLEAR_HARD_REG_BIT (temps, EH_RETURN_STACKADJ_REGNO);
                      CLEAR_HARD_REG_BIT (temps, EH_RETURN_STACKADJ_REGNO);
                      for (i = 0; i <= 3; i++)
                      for (i = 0; i <= 3; i++)
                        CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
                        CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
                    }
                    }
                }
                }
              if (TARGET_SHMEDIA && epilogue_p < 0)
              if (TARGET_SHMEDIA && epilogue_p < 0)
                for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
                for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
                  CLEAR_HARD_REG_BIT (temps, i);
                  CLEAR_HARD_REG_BIT (temps, i);
              if (epilogue_p <= 0)
              if (epilogue_p <= 0)
                {
                {
                  for (i = FIRST_PARM_REG;
                  for (i = FIRST_PARM_REG;
                       i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
                       i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
                    CLEAR_HARD_REG_BIT (temps, i);
                    CLEAR_HARD_REG_BIT (temps, i);
                  if (cfun->static_chain_decl != NULL)
                  if (cfun->static_chain_decl != NULL)
                    CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM);
                    CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM);
                }
                }
              temp = scavenge_reg (&temps);
              temp = scavenge_reg (&temps);
            }
            }
          if (temp < 0 && live_regs_mask)
          if (temp < 0 && live_regs_mask)
            {
            {
              HARD_REG_SET temps;
              HARD_REG_SET temps;
 
 
              COPY_HARD_REG_SET (temps, *live_regs_mask);
              COPY_HARD_REG_SET (temps, *live_regs_mask);
              CLEAR_HARD_REG_BIT (temps, REGNO (reg));
              CLEAR_HARD_REG_BIT (temps, REGNO (reg));
              temp = scavenge_reg (&temps);
              temp = scavenge_reg (&temps);
            }
            }
          if (temp < 0)
          if (temp < 0)
            {
            {
              rtx adj_reg, tmp_reg, mem;
              rtx adj_reg, tmp_reg, mem;
 
 
              /* If we reached here, the most likely case is the (sibcall)
              /* If we reached here, the most likely case is the (sibcall)
                 epilogue for non SHmedia.  Put a special push/pop sequence
                 epilogue for non SHmedia.  Put a special push/pop sequence
                 for such case as the last resort.  This looks lengthy but
                 for such case as the last resort.  This looks lengthy but
                 would not be problem because it seems to be very
                 would not be problem because it seems to be very
                 rare.  */
                 rare.  */
 
 
              gcc_assert (!TARGET_SHMEDIA && epilogue_p);
              gcc_assert (!TARGET_SHMEDIA && epilogue_p);
 
 
 
 
               /* ??? There is still the slight possibility that r4 or
               /* ??? There is still the slight possibility that r4 or
                  r5 have been reserved as fixed registers or assigned
                  r5 have been reserved as fixed registers or assigned
                  as global registers, and they change during an
                  as global registers, and they change during an
                  interrupt.  There are possible ways to handle this:
                  interrupt.  There are possible ways to handle this:
 
 
                  - If we are adjusting the frame pointer (r14), we can do
                  - If we are adjusting the frame pointer (r14), we can do
                    with a single temp register and an ordinary push / pop
                    with a single temp register and an ordinary push / pop
                    on the stack.
                    on the stack.
                  - Grab any call-used or call-saved registers (i.e. not
                  - Grab any call-used or call-saved registers (i.e. not
                    fixed or globals) for the temps we need.  We might
                    fixed or globals) for the temps we need.  We might
                    also grab r14 if we are adjusting the stack pointer.
                    also grab r14 if we are adjusting the stack pointer.
                    If we can't find enough available registers, issue
                    If we can't find enough available registers, issue
                    a diagnostic and die - the user must have reserved
                    a diagnostic and die - the user must have reserved
                    way too many registers.
                    way too many registers.
                 But since all this is rather unlikely to happen and
                 But since all this is rather unlikely to happen and
                 would require extra testing, we just die if r4 / r5
                 would require extra testing, we just die if r4 / r5
                 are not available.  */
                 are not available.  */
              gcc_assert (!fixed_regs[4] && !fixed_regs[5]
              gcc_assert (!fixed_regs[4] && !fixed_regs[5]
                          && !global_regs[4] && !global_regs[5]);
                          && !global_regs[4] && !global_regs[5]);
 
 
              adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
              adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
              tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
              tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
              emit_move_insn (gen_tmp_stack_mem (Pmode, reg), adj_reg);
              emit_move_insn (gen_tmp_stack_mem (Pmode, reg), adj_reg);
              emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
              emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
              emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
              emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
              emit_move_insn (mem, tmp_reg);
              emit_move_insn (mem, tmp_reg);
              emit_move_insn (tmp_reg, gen_tmp_stack_mem (Pmode, reg));
              emit_move_insn (tmp_reg, gen_tmp_stack_mem (Pmode, reg));
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
              emit_move_insn (mem, tmp_reg);
              emit_move_insn (mem, tmp_reg);
              emit_move_insn (reg, adj_reg);
              emit_move_insn (reg, adj_reg);
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
              emit_move_insn (adj_reg, mem);
              emit_move_insn (adj_reg, mem);
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
              mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
              emit_move_insn (tmp_reg, mem);
              emit_move_insn (tmp_reg, mem);
              /* Tell flow the insns that pop r4/r5 aren't dead.  */
              /* Tell flow the insns that pop r4/r5 aren't dead.  */
              emit_use (tmp_reg);
              emit_use (tmp_reg);
              emit_use (adj_reg);
              emit_use (adj_reg);
              return;
              return;
            }
            }
          const_reg = gen_rtx_REG (GET_MODE (reg), temp);
          const_reg = gen_rtx_REG (GET_MODE (reg), temp);
 
 
          /* If SIZE is negative, subtract the positive value.
          /* If SIZE is negative, subtract the positive value.
             This sometimes allows a constant pool entry to be shared
             This sometimes allows a constant pool entry to be shared
             between prologue and epilogue code.  */
             between prologue and epilogue code.  */
          if (size < 0)
          if (size < 0)
            {
            {
              emit_insn (GEN_MOV (const_reg, GEN_INT (-size)));
              emit_insn (GEN_MOV (const_reg, GEN_INT (-size)));
              insn = emit_fn (GEN_SUB3 (reg, reg, const_reg));
              insn = emit_fn (GEN_SUB3 (reg, reg, const_reg));
            }
            }
          else
          else
            {
            {
              emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
              emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
              insn = emit_fn (GEN_ADD3 (reg, reg, const_reg));
              insn = emit_fn (GEN_ADD3 (reg, reg, const_reg));
            }
            }
          if (! epilogue_p)
          if (! epilogue_p)
            add_reg_note (insn, REG_FRAME_RELATED_EXPR,
            add_reg_note (insn, REG_FRAME_RELATED_EXPR,
                          gen_rtx_SET (VOIDmode, reg,
                          gen_rtx_SET (VOIDmode, reg,
                                       gen_rtx_PLUS (SImode, reg,
                                       gen_rtx_PLUS (SImode, reg,
                                                     GEN_INT (size))));
                                                     GEN_INT (size))));
        }
        }
    }
    }
}
}
 
 
static rtx
static rtx
frame_insn (rtx x)
frame_insn (rtx x)
{
{
  x = emit_insn (x);
  x = emit_insn (x);
  RTX_FRAME_RELATED_P (x) = 1;
  RTX_FRAME_RELATED_P (x) = 1;
  return x;
  return x;
}
}
 
 
/* Output RTL to push register RN onto the stack.  */
/* Output RTL to push register RN onto the stack.  */
 
 
static rtx
static rtx
push (int rn)
push (int rn)
{
{
  rtx x;
  rtx x;
  if (rn == FPUL_REG)
  if (rn == FPUL_REG)
    x = gen_push_fpul ();
    x = gen_push_fpul ();
  else if (rn == FPSCR_REG)
  else if (rn == FPSCR_REG)
    x = gen_push_fpscr ();
    x = gen_push_fpscr ();
  else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && ! TARGET_FPU_SINGLE
  else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && ! TARGET_FPU_SINGLE
           && FP_OR_XD_REGISTER_P (rn))
           && FP_OR_XD_REGISTER_P (rn))
    {
    {
      if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
      if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
        return NULL_RTX;
        return NULL_RTX;
      x = gen_push_4 (gen_rtx_REG (DFmode, rn));
      x = gen_push_4 (gen_rtx_REG (DFmode, rn));
    }
    }
  else if (TARGET_SH2E && FP_REGISTER_P (rn))
  else if (TARGET_SH2E && FP_REGISTER_P (rn))
    x = gen_push_e (gen_rtx_REG (SFmode, rn));
    x = gen_push_e (gen_rtx_REG (SFmode, rn));
  else
  else
    x = gen_push (gen_rtx_REG (SImode, rn));
    x = gen_push (gen_rtx_REG (SImode, rn));
 
 
  x = frame_insn (x);
  x = frame_insn (x);
  add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
  add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
  return x;
  return x;
}
}
 
 
/* Output RTL to pop register RN from the stack.  */
/* Output RTL to pop register RN from the stack.  */
 
 
static void
static void
pop (int rn)
pop (int rn)
{
{
  rtx x;
  rtx x;
  if (rn == FPUL_REG)
  if (rn == FPUL_REG)
    x = gen_pop_fpul ();
    x = gen_pop_fpul ();
  else if (rn == FPSCR_REG)
  else if (rn == FPSCR_REG)
    x = gen_pop_fpscr ();
    x = gen_pop_fpscr ();
  else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && ! TARGET_FPU_SINGLE
  else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && ! TARGET_FPU_SINGLE
           && FP_OR_XD_REGISTER_P (rn))
           && FP_OR_XD_REGISTER_P (rn))
    {
    {
      if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
      if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
        return;
        return;
      x = gen_pop_4 (gen_rtx_REG (DFmode, rn));
      x = gen_pop_4 (gen_rtx_REG (DFmode, rn));
    }
    }
  else if (TARGET_SH2E && FP_REGISTER_P (rn))
  else if (TARGET_SH2E && FP_REGISTER_P (rn))
    x = gen_pop_e (gen_rtx_REG (SFmode, rn));
    x = gen_pop_e (gen_rtx_REG (SFmode, rn));
  else
  else
    x = gen_pop (gen_rtx_REG (SImode, rn));
    x = gen_pop (gen_rtx_REG (SImode, rn));
 
 
  x = emit_insn (x);
  x = emit_insn (x);
  add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
  add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
}
}
 
 
/* Generate code to push the regs specified in the mask.  */
/* Generate code to push the regs specified in the mask.  */
 
 
static void
static void
push_regs (HARD_REG_SET *mask, int interrupt_handler)
push_regs (HARD_REG_SET *mask, int interrupt_handler)
{
{
  int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
  int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
  int skip_fpscr = 0;
  int skip_fpscr = 0;
 
 
  /* Push PR last; this gives better latencies after the prologue, and
  /* Push PR last; this gives better latencies after the prologue, and
     candidates for the return delay slot when there are no general
     candidates for the return delay slot when there are no general
     registers pushed.  */
     registers pushed.  */
  for (; i < FIRST_PSEUDO_REGISTER; i++)
  for (; i < FIRST_PSEUDO_REGISTER; i++)
    {
    {
      /* If this is an interrupt handler, and the SZ bit varies,
      /* If this is an interrupt handler, and the SZ bit varies,
         and we have to push any floating point register, we need
         and we have to push any floating point register, we need
         to switch to the correct precision first.  */
         to switch to the correct precision first.  */
      if (i == FIRST_FP_REG && interrupt_handler && TARGET_FMOVD
      if (i == FIRST_FP_REG && interrupt_handler && TARGET_FMOVD
          && hard_reg_set_intersect_p (*mask, reg_class_contents[DF_REGS]))
          && hard_reg_set_intersect_p (*mask, reg_class_contents[DF_REGS]))
        {
        {
          HARD_REG_SET unsaved;
          HARD_REG_SET unsaved;
 
 
          push (FPSCR_REG);
          push (FPSCR_REG);
          COMPL_HARD_REG_SET (unsaved, *mask);
          COMPL_HARD_REG_SET (unsaved, *mask);
          fpscr_set_from_mem (NORMAL_MODE (FP_MODE), unsaved);
          fpscr_set_from_mem (NORMAL_MODE (FP_MODE), unsaved);
          skip_fpscr = 1;
          skip_fpscr = 1;
        }
        }
      if (i != PR_REG
      if (i != PR_REG
          && (i != FPSCR_REG || ! skip_fpscr)
          && (i != FPSCR_REG || ! skip_fpscr)
          && TEST_HARD_REG_BIT (*mask, i))
          && TEST_HARD_REG_BIT (*mask, i))
           {
           {
        /* If the ISR has RESBANK attribute assigned, don't push any of
        /* If the ISR has RESBANK attribute assigned, don't push any of
           the following registers - R0-R14, MACH, MACL and GBR.  */
           the following registers - R0-R14, MACH, MACL and GBR.  */
      if (! (sh_cfun_resbank_handler_p ()
      if (! (sh_cfun_resbank_handler_p ()
             && ((i >= FIRST_GENERAL_REG && i < LAST_GENERAL_REG)
             && ((i >= FIRST_GENERAL_REG && i < LAST_GENERAL_REG)
                 || i == MACH_REG
                 || i == MACH_REG
                 || i == MACL_REG
                 || i == MACL_REG
                 || i == GBR_REG)))
                 || i == GBR_REG)))
          push (i);
          push (i);
        }
        }
    }
    }
 
 
  /* Push banked registers last to improve delay slot opportunities.  */
  /* Push banked registers last to improve delay slot opportunities.  */
  if (interrupt_handler)
  if (interrupt_handler)
    for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
    for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
      if (TEST_HARD_REG_BIT (*mask, i))
      if (TEST_HARD_REG_BIT (*mask, i))
        push (i);
        push (i);
 
 
  /* Don't push PR register for an ISR with RESBANK attribute assigned.  */
  /* Don't push PR register for an ISR with RESBANK attribute assigned.  */
  if (TEST_HARD_REG_BIT (*mask, PR_REG) && !sh_cfun_resbank_handler_p ())
  if (TEST_HARD_REG_BIT (*mask, PR_REG) && !sh_cfun_resbank_handler_p ())
    push (PR_REG);
    push (PR_REG);
}
}
 
 
/* Calculate how much extra space is needed to save all callee-saved
/* Calculate how much extra space is needed to save all callee-saved
   target registers.
   target registers.
   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
 
 
static int
static int
shmedia_target_regs_stack_space (HARD_REG_SET *live_regs_mask)
shmedia_target_regs_stack_space (HARD_REG_SET *live_regs_mask)
{
{
  int reg;
  int reg;
  int stack_space = 0;
  int stack_space = 0;
  int interrupt_handler = sh_cfun_interrupt_handler_p ();
  int interrupt_handler = sh_cfun_interrupt_handler_p ();
 
 
  for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
  for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
    if ((! call_really_used_regs[reg] || interrupt_handler)
    if ((! call_really_used_regs[reg] || interrupt_handler)
        && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
        && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
      /* Leave space to save this target register on the stack,
      /* Leave space to save this target register on the stack,
         in case target register allocation wants to use it.  */
         in case target register allocation wants to use it.  */
      stack_space += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
      stack_space += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
  return stack_space;
  return stack_space;
}
}
 
 
/* Decide whether we should reserve space for callee-save target registers,
/* Decide whether we should reserve space for callee-save target registers,
   in case target register allocation wants to use them.  REGS_SAVED is
   in case target register allocation wants to use them.  REGS_SAVED is
   the space, in bytes, that is already required for register saves.
   the space, in bytes, that is already required for register saves.
   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
 
 
static int
static int
shmedia_reserve_space_for_target_registers_p (int regs_saved,
shmedia_reserve_space_for_target_registers_p (int regs_saved,
                                              HARD_REG_SET *live_regs_mask)
                                              HARD_REG_SET *live_regs_mask)
{
{
  if (optimize_size)
  if (optimize_size)
    return 0;
    return 0;
  return shmedia_target_regs_stack_space (live_regs_mask) <= regs_saved;
  return shmedia_target_regs_stack_space (live_regs_mask) <= regs_saved;
}
}
 
 
/* Decide how much space to reserve for callee-save target registers
/* Decide how much space to reserve for callee-save target registers
   in case target register allocation wants to use them.
   in case target register allocation wants to use them.
   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
 
 
static int
static int
shmedia_target_regs_stack_adjust (HARD_REG_SET *live_regs_mask)
shmedia_target_regs_stack_adjust (HARD_REG_SET *live_regs_mask)
{
{
  if (shmedia_space_reserved_for_target_registers)
  if (shmedia_space_reserved_for_target_registers)
    return shmedia_target_regs_stack_space (live_regs_mask);
    return shmedia_target_regs_stack_space (live_regs_mask);
  else
  else
    return 0;
    return 0;
}
}
 
 
/* Work out the registers which need to be saved, both as a mask and a
/* Work out the registers which need to be saved, both as a mask and a
   count of saved words.  Return the count.
   count of saved words.  Return the count.
 
 
   If doing a pragma interrupt function, then push all regs used by the
   If doing a pragma interrupt function, then push all regs used by the
   function, and if we call another function (we can tell by looking at PR),
   function, and if we call another function (we can tell by looking at PR),
   make sure that all the regs it clobbers are safe too.  */
   make sure that all the regs it clobbers are safe too.  */
 
 
static int
static int
calc_live_regs (HARD_REG_SET *live_regs_mask)
calc_live_regs (HARD_REG_SET *live_regs_mask)
{
{
  unsigned int reg;
  unsigned int reg;
  int count;
  int count;
  tree attrs;
  tree attrs;
  bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler;
  bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler;
  bool nosave_low_regs;
  bool nosave_low_regs;
  int pr_live, has_call;
  int pr_live, has_call;
 
 
  attrs = DECL_ATTRIBUTES (current_function_decl);
  attrs = DECL_ATTRIBUTES (current_function_decl);
  interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p ();
  interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p ();
  trapa_handler = lookup_attribute ("trapa_handler", attrs) != NULL_TREE;
  trapa_handler = lookup_attribute ("trapa_handler", attrs) != NULL_TREE;
  interrupt_handler = interrupt_or_trapa_handler && ! trapa_handler;
  interrupt_handler = interrupt_or_trapa_handler && ! trapa_handler;
  nosave_low_regs = lookup_attribute ("nosave_low_regs", attrs) != NULL_TREE;
  nosave_low_regs = lookup_attribute ("nosave_low_regs", attrs) != NULL_TREE;
 
 
  CLEAR_HARD_REG_SET (*live_regs_mask);
  CLEAR_HARD_REG_SET (*live_regs_mask);
  if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && interrupt_handler
  if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && interrupt_handler
      && df_regs_ever_live_p (FPSCR_REG))
      && df_regs_ever_live_p (FPSCR_REG))
    target_flags &= ~MASK_FPU_SINGLE;
    target_flags &= ~MASK_FPU_SINGLE;
  /* If we can save a lot of saves by switching to double mode, do that.  */
  /* If we can save a lot of saves by switching to double mode, do that.  */
  else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && TARGET_FPU_SINGLE)
  else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && TARGET_FPU_SINGLE)
    for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
    for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
      if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1)
      if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1)
          && (! call_really_used_regs[reg]
          && (! call_really_used_regs[reg]
              || interrupt_handler)
              || interrupt_handler)
          && ++count > 2)
          && ++count > 2)
        {
        {
          target_flags &= ~MASK_FPU_SINGLE;
          target_flags &= ~MASK_FPU_SINGLE;
          break;
          break;
        }
        }
  /* PR_MEDIA_REG is a general purpose register, thus global_alloc already
  /* PR_MEDIA_REG is a general purpose register, thus global_alloc already
     knows how to use it.  That means the pseudo originally allocated for
     knows how to use it.  That means the pseudo originally allocated for
     the initial value can become the PR_MEDIA_REG hard register, as seen for
     the initial value can become the PR_MEDIA_REG hard register, as seen for
     execute/20010122-1.c:test9.  */
     execute/20010122-1.c:test9.  */
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    /* ??? this function is called from initial_elimination_offset, hence we
    /* ??? this function is called from initial_elimination_offset, hence we
       can't use the result of sh_media_register_for_return here.  */
       can't use the result of sh_media_register_for_return here.  */
    pr_live = sh_pr_n_sets ();
    pr_live = sh_pr_n_sets ();
  else
  else
    {
    {
      rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
      rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
      pr_live = (pr_initial
      pr_live = (pr_initial
                 ? (!REG_P (pr_initial)
                 ? (!REG_P (pr_initial)
                    || REGNO (pr_initial) != (PR_REG))
                    || REGNO (pr_initial) != (PR_REG))
                 : df_regs_ever_live_p (PR_REG));
                 : df_regs_ever_live_p (PR_REG));
      /* For Shcompact, if not optimizing, we end up with a memory reference
      /* For Shcompact, if not optimizing, we end up with a memory reference
         using the return address pointer for __builtin_return_address even
         using the return address pointer for __builtin_return_address even
         though there is no actual need to put the PR register on the stack.  */
         though there is no actual need to put the PR register on the stack.  */
      pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
      pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
    }
    }
  /* Force PR to be live if the prologue has to call the SHmedia
  /* Force PR to be live if the prologue has to call the SHmedia
     argument decoder or register saver.  */
     argument decoder or register saver.  */
  if (TARGET_SHCOMPACT
  if (TARGET_SHCOMPACT
      && ((crtl->args.info.call_cookie
      && ((crtl->args.info.call_cookie
           & ~ CALL_COOKIE_RET_TRAMP (1))
           & ~ CALL_COOKIE_RET_TRAMP (1))
          || crtl->saves_all_registers))
          || crtl->saves_all_registers))
    pr_live = 1;
    pr_live = 1;
  has_call = TARGET_SHMEDIA ? ! leaf_function_p () : pr_live;
  has_call = TARGET_SHMEDIA ? ! leaf_function_p () : pr_live;
  for (count = 0, reg = FIRST_PSEUDO_REGISTER; reg-- != 0; )
  for (count = 0, reg = FIRST_PSEUDO_REGISTER; reg-- != 0; )
    {
    {
      if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
      if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
          ? pr_live
          ? pr_live
          : interrupt_handler
          : interrupt_handler
          ? (/* Need to save all the regs ever live.  */
          ? (/* Need to save all the regs ever live.  */
             (df_regs_ever_live_p (reg)
             (df_regs_ever_live_p (reg)
              || (call_really_used_regs[reg]
              || (call_really_used_regs[reg]
                  && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG
                  && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG
                      || reg == PIC_OFFSET_TABLE_REGNUM)
                      || reg == PIC_OFFSET_TABLE_REGNUM)
                  && has_call)
                  && has_call)
              || (TARGET_SHMEDIA && has_call
              || (TARGET_SHMEDIA && has_call
                  && REGISTER_NATURAL_MODE (reg) == SImode
                  && REGISTER_NATURAL_MODE (reg) == SImode
                  && (GENERAL_REGISTER_P (reg) || TARGET_REGISTER_P (reg))))
                  && (GENERAL_REGISTER_P (reg) || TARGET_REGISTER_P (reg))))
             && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
             && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
             && reg != RETURN_ADDRESS_POINTER_REGNUM
             && reg != RETURN_ADDRESS_POINTER_REGNUM
             && reg != T_REG && reg != GBR_REG
             && reg != T_REG && reg != GBR_REG
             /* Push fpscr only on targets which have FPU */
             /* Push fpscr only on targets which have FPU */
             && (reg != FPSCR_REG || TARGET_FPU_ANY))
             && (reg != FPSCR_REG || TARGET_FPU_ANY))
          : (/* Only push those regs which are used and need to be saved.  */
          : (/* Only push those regs which are used and need to be saved.  */
             (TARGET_SHCOMPACT
             (TARGET_SHCOMPACT
              && flag_pic
              && flag_pic
              && crtl->args.info.call_cookie
              && crtl->args.info.call_cookie
              && reg == PIC_OFFSET_TABLE_REGNUM)
              && reg == PIC_OFFSET_TABLE_REGNUM)
             || (df_regs_ever_live_p (reg)
             || (df_regs_ever_live_p (reg)
                 && ((!call_really_used_regs[reg]
                 && ((!call_really_used_regs[reg]
                      && !(reg != PIC_OFFSET_TABLE_REGNUM
                      && !(reg != PIC_OFFSET_TABLE_REGNUM
                           && fixed_regs[reg] && call_used_regs[reg]))
                           && fixed_regs[reg] && call_used_regs[reg]))
                     || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY)))
                     || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY)))
             || (crtl->calls_eh_return
             || (crtl->calls_eh_return
                 && (reg == EH_RETURN_DATA_REGNO (0)
                 && (reg == EH_RETURN_DATA_REGNO (0)
                     || reg == EH_RETURN_DATA_REGNO (1)
                     || reg == EH_RETURN_DATA_REGNO (1)
                     || reg == EH_RETURN_DATA_REGNO (2)
                     || reg == EH_RETURN_DATA_REGNO (2)
                     || reg == EH_RETURN_DATA_REGNO (3)))
                     || reg == EH_RETURN_DATA_REGNO (3)))
             || ((reg == MACL_REG || reg == MACH_REG)
             || ((reg == MACL_REG || reg == MACH_REG)
                 && df_regs_ever_live_p (reg)
                 && df_regs_ever_live_p (reg)
                 && sh_cfun_attr_renesas_p ())
                 && sh_cfun_attr_renesas_p ())
             ))
             ))
        {
        {
          SET_HARD_REG_BIT (*live_regs_mask, reg);
          SET_HARD_REG_BIT (*live_regs_mask, reg);
          count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
          count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
 
 
          if ((TARGET_SH4 || TARGET_SH2A_DOUBLE || TARGET_SH5) && TARGET_FMOVD
          if ((TARGET_SH4 || TARGET_SH2A_DOUBLE || TARGET_SH5) && TARGET_FMOVD
              && GET_MODE_CLASS (REGISTER_NATURAL_MODE (reg)) == MODE_FLOAT)
              && GET_MODE_CLASS (REGISTER_NATURAL_MODE (reg)) == MODE_FLOAT)
            {
            {
              if (FP_REGISTER_P (reg))
              if (FP_REGISTER_P (reg))
                {
                {
                  if (! TARGET_FPU_SINGLE && ! df_regs_ever_live_p (reg ^ 1))
                  if (! TARGET_FPU_SINGLE && ! df_regs_ever_live_p (reg ^ 1))
                    {
                    {
                      SET_HARD_REG_BIT (*live_regs_mask, (reg ^ 1));
                      SET_HARD_REG_BIT (*live_regs_mask, (reg ^ 1));
                      count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
                      count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
                    }
                    }
                }
                }
              else if (XD_REGISTER_P (reg))
              else if (XD_REGISTER_P (reg))
                {
                {
                  /* Must switch to double mode to access these registers.  */
                  /* Must switch to double mode to access these registers.  */
                  target_flags &= ~MASK_FPU_SINGLE;
                  target_flags &= ~MASK_FPU_SINGLE;
                }
                }
            }
            }
        }
        }
      if (nosave_low_regs && reg == R8_REG)
      if (nosave_low_regs && reg == R8_REG)
        break;
        break;
    }
    }
  /* If we have a target register optimization pass after prologue / epilogue
  /* If we have a target register optimization pass after prologue / epilogue
     threading, we need to assume all target registers will be live even if
     threading, we need to assume all target registers will be live even if
     they aren't now.  */
     they aren't now.  */
  if (flag_branch_target_load_optimize2
  if (flag_branch_target_load_optimize2
      && TARGET_SAVE_ALL_TARGET_REGS
      && TARGET_SAVE_ALL_TARGET_REGS
      && shmedia_space_reserved_for_target_registers)
      && shmedia_space_reserved_for_target_registers)
    for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
    for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
      if ((! call_really_used_regs[reg] || interrupt_handler)
      if ((! call_really_used_regs[reg] || interrupt_handler)
          && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
          && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
        {
        {
          SET_HARD_REG_BIT (*live_regs_mask, reg);
          SET_HARD_REG_BIT (*live_regs_mask, reg);
          count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
          count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
        }
        }
  /* If this is an interrupt handler, we don't have any call-clobbered
  /* If this is an interrupt handler, we don't have any call-clobbered
     registers we can conveniently use for target register save/restore.
     registers we can conveniently use for target register save/restore.
     Make sure we save at least one general purpose register when we need
     Make sure we save at least one general purpose register when we need
     to save target registers.  */
     to save target registers.  */
  if (interrupt_handler
  if (interrupt_handler
      && hard_reg_set_intersect_p (*live_regs_mask,
      && hard_reg_set_intersect_p (*live_regs_mask,
                                   reg_class_contents[TARGET_REGS])
                                   reg_class_contents[TARGET_REGS])
      && ! hard_reg_set_intersect_p (*live_regs_mask,
      && ! hard_reg_set_intersect_p (*live_regs_mask,
                                     reg_class_contents[GENERAL_REGS]))
                                     reg_class_contents[GENERAL_REGS]))
    {
    {
      SET_HARD_REG_BIT (*live_regs_mask, R0_REG);
      SET_HARD_REG_BIT (*live_regs_mask, R0_REG);
      count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (R0_REG));
      count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (R0_REG));
    }
    }
 
 
  return count;
  return count;
}
}
 
 
/* Code to generate prologue and epilogue sequences */
/* Code to generate prologue and epilogue sequences */
 
 
/* PUSHED is the number of bytes that are being pushed on the
/* PUSHED is the number of bytes that are being pushed on the
   stack for register saves.  Return the frame size, padded
   stack for register saves.  Return the frame size, padded
   appropriately so that the stack stays properly aligned.  */
   appropriately so that the stack stays properly aligned.  */
static HOST_WIDE_INT
static HOST_WIDE_INT
rounded_frame_size (int pushed)
rounded_frame_size (int pushed)
{
{
  HOST_WIDE_INT size = get_frame_size ();
  HOST_WIDE_INT size = get_frame_size ();
  HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
  HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
 
 
  return ((size + pushed + align - 1) & -align) - pushed;
  return ((size + pushed + align - 1) & -align) - pushed;
}
}
 
 
/* Choose a call-clobbered target-branch register that remains
/* Choose a call-clobbered target-branch register that remains
   unchanged along the whole function.  We set it up as the return
   unchanged along the whole function.  We set it up as the return
   value in the prologue.  */
   value in the prologue.  */
int
int
sh_media_register_for_return (void)
sh_media_register_for_return (void)
{
{
  int regno;
  int regno;
  int tr0_used;
  int tr0_used;
 
 
  if (! current_function_is_leaf)
  if (! current_function_is_leaf)
    return -1;
    return -1;
  if (lookup_attribute ("interrupt_handler",
  if (lookup_attribute ("interrupt_handler",
                        DECL_ATTRIBUTES (current_function_decl)))
                        DECL_ATTRIBUTES (current_function_decl)))
    return -1;
    return -1;
  if (sh_cfun_interrupt_handler_p ())
  if (sh_cfun_interrupt_handler_p ())
    return -1;
    return -1;
 
 
  tr0_used = flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
  tr0_used = flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
 
 
  for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
  for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
    if (call_really_used_regs[regno] && ! df_regs_ever_live_p (regno))
    if (call_really_used_regs[regno] && ! df_regs_ever_live_p (regno))
      return regno;
      return regno;
 
 
  return -1;
  return -1;
}
}
 
 
/* The maximum registers we need to save are:
/* The maximum registers we need to save are:
   - 62 general purpose registers (r15 is stack pointer, r63 is zero)
   - 62 general purpose registers (r15 is stack pointer, r63 is zero)
   - 32 floating point registers (for each pair, we save none,
   - 32 floating point registers (for each pair, we save none,
         one single precision value, or a double precision value).
         one single precision value, or a double precision value).
   -  8 target registers
   -  8 target registers
   -  add 1 entry for a delimiter.  */
   -  add 1 entry for a delimiter.  */
#define MAX_SAVED_REGS (62+32+8)
#define MAX_SAVED_REGS (62+32+8)
 
 
typedef struct save_entry_s
typedef struct save_entry_s
{
{
  unsigned char reg;
  unsigned char reg;
  unsigned char mode;
  unsigned char mode;
  short offset;
  short offset;
} save_entry;
} save_entry;
 
 
#define MAX_TEMPS 4
#define MAX_TEMPS 4
 
 
/* There will be a delimiter entry with VOIDmode both at the start and the
/* 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
   end of a filled in schedule.  The end delimiter has the offset of the
   save with the smallest (i.e. most negative) offset.  */
   save with the smallest (i.e. most negative) offset.  */
typedef struct save_schedule_s
typedef struct save_schedule_s
{
{
  save_entry entries[MAX_SAVED_REGS + 2];
  save_entry entries[MAX_SAVED_REGS + 2];
  int temps[MAX_TEMPS+1];
  int temps[MAX_TEMPS+1];
} save_schedule;
} save_schedule;
 
 
/* Fill in SCHEDULE according to LIVE_REGS_MASK.  If RESTORE is nonzero,
/* Fill in SCHEDULE according to LIVE_REGS_MASK.  If RESTORE is nonzero,
   use reverse order.  Returns the last entry written to (not counting
   use reverse order.  Returns the last entry written to (not counting
   the delimiter).  OFFSET_BASE is a number to be added to all offset
   the delimiter).  OFFSET_BASE is a number to be added to all offset
   entries.  */
   entries.  */
 
 
static save_entry *
static save_entry *
sh5_schedule_saves (HARD_REG_SET *live_regs_mask, save_schedule *schedule,
sh5_schedule_saves (HARD_REG_SET *live_regs_mask, save_schedule *schedule,
                    int offset_base)
                    int offset_base)
{
{
  int align, i;
  int align, i;
  save_entry *entry = schedule->entries;
  save_entry *entry = schedule->entries;
  int tmpx = 0;
  int tmpx = 0;
  int offset;
  int offset;
 
 
  if (! current_function_interrupt)
  if (! current_function_interrupt)
    for (i = FIRST_GENERAL_REG; tmpx < MAX_TEMPS && i <= LAST_GENERAL_REG; i++)
    for (i = FIRST_GENERAL_REG; tmpx < MAX_TEMPS && i <= LAST_GENERAL_REG; i++)
      if (call_really_used_regs[i] && ! fixed_regs[i] && i != PR_MEDIA_REG
      if (call_really_used_regs[i] && ! fixed_regs[i] && i != PR_MEDIA_REG
          && ! FUNCTION_ARG_REGNO_P (i)
          && ! FUNCTION_ARG_REGNO_P (i)
          && i != FIRST_RET_REG
          && i != FIRST_RET_REG
          && ! (cfun->static_chain_decl != NULL && i == STATIC_CHAIN_REGNUM)
          && ! (cfun->static_chain_decl != NULL && i == STATIC_CHAIN_REGNUM)
          && ! (crtl->calls_eh_return
          && ! (crtl->calls_eh_return
                && (i == EH_RETURN_STACKADJ_REGNO
                && (i == EH_RETURN_STACKADJ_REGNO
                    || ((unsigned) i >= EH_RETURN_DATA_REGNO (0)
                    || ((unsigned) i >= EH_RETURN_DATA_REGNO (0)
                        && (unsigned) i <= EH_RETURN_DATA_REGNO (3)))))
                        && (unsigned) i <= EH_RETURN_DATA_REGNO (3)))))
        schedule->temps[tmpx++] = i;
        schedule->temps[tmpx++] = i;
  entry->reg = -1;
  entry->reg = -1;
  entry->mode = VOIDmode;
  entry->mode = VOIDmode;
  entry->offset = offset_base;
  entry->offset = offset_base;
  entry++;
  entry++;
  /* We loop twice: first, we save 8-byte aligned registers in the
  /* We loop twice: first, we save 8-byte aligned registers in the
     higher addresses, that are known to be aligned.  Then, we
     higher addresses, that are known to be aligned.  Then, we
     proceed to saving 32-bit registers that don't need 8-byte
     proceed to saving 32-bit registers that don't need 8-byte
     alignment.
     alignment.
     If this is an interrupt function, all registers that need saving
     If this is an interrupt function, all registers that need saving
     need to be saved in full.  moreover, we need to postpone saving
     need to be saved in full.  moreover, we need to postpone saving
     target registers till we have saved some general purpose registers
     target registers till we have saved some general purpose registers
     we can then use as scratch registers.  */
     we can then use as scratch registers.  */
  offset = offset_base;
  offset = offset_base;
  for (align = 1; align >= 0; align--)
  for (align = 1; align >= 0; align--)
    {
    {
      for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
      for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
        if (TEST_HARD_REG_BIT (*live_regs_mask, i))
        if (TEST_HARD_REG_BIT (*live_regs_mask, i))
          {
          {
            enum machine_mode mode = REGISTER_NATURAL_MODE (i);
            enum machine_mode mode = REGISTER_NATURAL_MODE (i);
            int reg = i;
            int reg = i;
 
 
            if (current_function_interrupt)
            if (current_function_interrupt)
              {
              {
                if (TARGET_REGISTER_P (i))
                if (TARGET_REGISTER_P (i))
                  continue;
                  continue;
                if (GENERAL_REGISTER_P (i))
                if (GENERAL_REGISTER_P (i))
                  mode = DImode;
                  mode = DImode;
              }
              }
            if (mode == SFmode && (i % 2) == 1
            if (mode == SFmode && (i % 2) == 1
                && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
                && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
                && (TEST_HARD_REG_BIT (*live_regs_mask, (i ^ 1))))
                && (TEST_HARD_REG_BIT (*live_regs_mask, (i ^ 1))))
              {
              {
                mode = DFmode;
                mode = DFmode;
                i--;
                i--;
                reg--;
                reg--;
              }
              }
 
 
            /* If we're doing the aligned pass and this is not aligned,
            /* If we're doing the aligned pass and this is not aligned,
               or we're doing the unaligned pass and this is aligned,
               or we're doing the unaligned pass and this is aligned,
               skip it.  */
               skip it.  */
            if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) == 0)
            if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) == 0)
                != align)
                != align)
              continue;
              continue;
 
 
            if (current_function_interrupt
            if (current_function_interrupt
                && GENERAL_REGISTER_P (i)
                && GENERAL_REGISTER_P (i)
                && tmpx < MAX_TEMPS)
                && tmpx < MAX_TEMPS)
              schedule->temps[tmpx++] = i;
              schedule->temps[tmpx++] = i;
 
 
            offset -= GET_MODE_SIZE (mode);
            offset -= GET_MODE_SIZE (mode);
            entry->reg = i;
            entry->reg = i;
            entry->mode = mode;
            entry->mode = mode;
            entry->offset = offset;
            entry->offset = offset;
            entry++;
            entry++;
          }
          }
      if (align && current_function_interrupt)
      if (align && current_function_interrupt)
        for (i = LAST_TARGET_REG; i >= FIRST_TARGET_REG; i--)
        for (i = LAST_TARGET_REG; i >= FIRST_TARGET_REG; i--)
          if (TEST_HARD_REG_BIT (*live_regs_mask, i))
          if (TEST_HARD_REG_BIT (*live_regs_mask, i))
            {
            {
              offset -= GET_MODE_SIZE (DImode);
              offset -= GET_MODE_SIZE (DImode);
              entry->reg = i;
              entry->reg = i;
              entry->mode = DImode;
              entry->mode = DImode;
              entry->offset = offset;
              entry->offset = offset;
              entry++;
              entry++;
            }
            }
    }
    }
  entry->reg = -1;
  entry->reg = -1;
  entry->mode = VOIDmode;
  entry->mode = VOIDmode;
  entry->offset = offset;
  entry->offset = offset;
  schedule->temps[tmpx] = -1;
  schedule->temps[tmpx] = -1;
  return entry - 1;
  return entry - 1;
}
}
 
 
void
void
sh_expand_prologue (void)
sh_expand_prologue (void)
{
{
  HARD_REG_SET live_regs_mask;
  HARD_REG_SET live_regs_mask;
  int d, i;
  int d, i;
  int d_rounding = 0;
  int d_rounding = 0;
  int save_flags = target_flags;
  int save_flags = target_flags;
  int pretend_args;
  int pretend_args;
  tree sp_switch_attr
  tree sp_switch_attr
    = lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl));
    = lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl));
 
 
  current_function_interrupt = sh_cfun_interrupt_handler_p ();
  current_function_interrupt = sh_cfun_interrupt_handler_p ();
 
 
  /* We have pretend args if we had an object sent partially in registers
  /* We have pretend args if we had an object sent partially in registers
     and partially on the stack, e.g. a large structure.  */
     and partially on the stack, e.g. a large structure.  */
  pretend_args = crtl->args.pretend_args_size;
  pretend_args = crtl->args.pretend_args_size;
  if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl)
  if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl)
      && (NPARM_REGS(SImode)
      && (NPARM_REGS(SImode)
          > crtl->args.info.arg_count[(int) SH_ARG_INT]))
          > crtl->args.info.arg_count[(int) SH_ARG_INT]))
    pretend_args = 0;
    pretend_args = 0;
  /* Dwarf2 module doesn't expect frame related insns here.  */
  /* Dwarf2 module doesn't expect frame related insns here.  */
  output_stack_adjust (-pretend_args
  output_stack_adjust (-pretend_args
                       - crtl->args.info.stack_regs * 8,
                       - crtl->args.info.stack_regs * 8,
                       stack_pointer_rtx, 0, NULL, false);
                       stack_pointer_rtx, 0, NULL, false);
 
 
  if (TARGET_SHCOMPACT && flag_pic && crtl->args.info.call_cookie)
  if (TARGET_SHCOMPACT && flag_pic && crtl->args.info.call_cookie)
    /* We're going to use the PIC register to load the address of the
    /* We're going to use the PIC register to load the address of the
       incoming-argument decoder and/or of the return trampoline from
       incoming-argument decoder and/or of the return trampoline from
       the GOT, so make sure the PIC register is preserved and
       the GOT, so make sure the PIC register is preserved and
       initialized.  */
       initialized.  */
    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
 
  if (TARGET_SHCOMPACT
  if (TARGET_SHCOMPACT
      && (crtl->args.info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
      && (crtl->args.info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
    {
    {
      int reg;
      int reg;
 
 
      /* First, make all registers with incoming arguments that will
      /* First, make all registers with incoming arguments that will
         be pushed onto the stack live, so that register renaming
         be pushed onto the stack live, so that register renaming
         doesn't overwrite them.  */
         doesn't overwrite them.  */
      for (reg = 0; reg < NPARM_REGS (SImode); reg++)
      for (reg = 0; reg < NPARM_REGS (SImode); reg++)
        if (CALL_COOKIE_STACKSEQ_GET (crtl->args.info.call_cookie)
        if (CALL_COOKIE_STACKSEQ_GET (crtl->args.info.call_cookie)
            >= NPARM_REGS (SImode) - reg)
            >= NPARM_REGS (SImode) - reg)
          for (; reg < NPARM_REGS (SImode); reg++)
          for (; reg < NPARM_REGS (SImode); reg++)
            emit_insn (gen_shcompact_preserve_incoming_args
            emit_insn (gen_shcompact_preserve_incoming_args
                       (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
                       (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
        else if (CALL_COOKIE_INT_REG_GET
        else if (CALL_COOKIE_INT_REG_GET
                 (crtl->args.info.call_cookie, reg) == 1)
                 (crtl->args.info.call_cookie, reg) == 1)
          emit_insn (gen_shcompact_preserve_incoming_args
          emit_insn (gen_shcompact_preserve_incoming_args
                     (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
                     (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
 
 
      emit_move_insn (gen_rtx_REG (Pmode, MACL_REG),
      emit_move_insn (gen_rtx_REG (Pmode, MACL_REG),
                      stack_pointer_rtx);
                      stack_pointer_rtx);
      emit_move_insn (gen_rtx_REG (SImode, R0_REG),
      emit_move_insn (gen_rtx_REG (SImode, R0_REG),
                      GEN_INT (crtl->args.info.call_cookie));
                      GEN_INT (crtl->args.info.call_cookie));
      emit_move_insn (gen_rtx_REG (SImode, MACH_REG),
      emit_move_insn (gen_rtx_REG (SImode, MACH_REG),
                      gen_rtx_REG (SImode, R0_REG));
                      gen_rtx_REG (SImode, R0_REG));
    }
    }
  else if (TARGET_SHMEDIA)
  else if (TARGET_SHMEDIA)
    {
    {
      int tr = sh_media_register_for_return ();
      int tr = sh_media_register_for_return ();
 
 
      if (tr >= 0)
      if (tr >= 0)
        emit_move_insn (gen_rtx_REG (DImode, tr),
        emit_move_insn (gen_rtx_REG (DImode, tr),
                        gen_rtx_REG (DImode, PR_MEDIA_REG));
                        gen_rtx_REG (DImode, PR_MEDIA_REG));
    }
    }
 
 
  /* Emit the code for SETUP_VARARGS.  */
  /* Emit the code for SETUP_VARARGS.  */
  if (cfun->stdarg)
  if (cfun->stdarg)
    {
    {
      if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
      if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
        {
        {
          /* Push arg regs as if they'd been provided by caller in stack.  */
          /* Push arg regs as if they'd been provided by caller in stack.  */
          for (i = 0; i < NPARM_REGS(SImode); i++)
          for (i = 0; i < NPARM_REGS(SImode); i++)
            {
            {
              int rn = NPARM_REGS(SImode) + FIRST_PARM_REG - i - 1;
              int rn = NPARM_REGS(SImode) + FIRST_PARM_REG - i - 1;
              rtx insn;
              rtx insn;
 
 
              if (i >= (NPARM_REGS(SImode)
              if (i >= (NPARM_REGS(SImode)
                        - crtl->args.info.arg_count[(int) SH_ARG_INT]
                        - crtl->args.info.arg_count[(int) SH_ARG_INT]
                        ))
                        ))
                break;
                break;
              insn = push (rn);
              insn = push (rn);
            }
            }
        }
        }
    }
    }
 
 
  /* If we're supposed to switch stacks at function entry, do so now.  */
  /* If we're supposed to switch stacks at function entry, do so now.  */
  if (sp_switch_attr)
  if (sp_switch_attr)
    {
    {
      rtx lab, newsrc;
      rtx lab, newsrc;
      /* The argument specifies a variable holding the address of the
      /* The argument specifies a variable holding the address of the
         stack the interrupt function should switch to/from at entry/exit.  */
         stack the interrupt function should switch to/from at entry/exit.  */
      tree arg = TREE_VALUE ( TREE_VALUE (sp_switch_attr));
      tree arg = TREE_VALUE ( TREE_VALUE (sp_switch_attr));
      const char *s
      const char *s
        = ggc_strdup (TREE_STRING_POINTER (arg));
        = ggc_strdup (TREE_STRING_POINTER (arg));
      rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
      rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
 
 
      lab = add_constant (sp_switch, SImode, 0);
      lab = add_constant (sp_switch, SImode, 0);
      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
      newsrc = gen_const_mem (SImode, newsrc);
      newsrc = gen_const_mem (SImode, newsrc);
 
 
      emit_insn (gen_sp_switch_1 (newsrc));
      emit_insn (gen_sp_switch_1 (newsrc));
    }
    }
 
 
  d = calc_live_regs (&live_regs_mask);
  d = calc_live_regs (&live_regs_mask);
  /* ??? Maybe we could save some switching if we can move a mode switch
  /* ??? Maybe we could save some switching if we can move a mode switch
     that already happens to be at the function start into the prologue.  */
     that already happens to be at the function start into the prologue.  */
  if (target_flags != save_flags && ! current_function_interrupt)
  if (target_flags != save_flags && ! current_function_interrupt)
    emit_insn (gen_toggle_sz ());
    emit_insn (gen_toggle_sz ());
 
 
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      int offset_base, offset;
      int offset_base, offset;
      rtx r0 = NULL_RTX;
      rtx r0 = NULL_RTX;
      int offset_in_r0 = -1;
      int offset_in_r0 = -1;
      int sp_in_r0 = 0;
      int sp_in_r0 = 0;
      int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
      int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
      int total_size, save_size;
      int total_size, save_size;
      save_schedule schedule;
      save_schedule schedule;
      save_entry *entry;
      save_entry *entry;
      int *tmp_pnt;
      int *tmp_pnt;
 
 
      if (call_really_used_regs[R0_REG] && ! fixed_regs[R0_REG]
      if (call_really_used_regs[R0_REG] && ! fixed_regs[R0_REG]
          && ! current_function_interrupt)
          && ! current_function_interrupt)
        r0 = gen_rtx_REG (Pmode, R0_REG);
        r0 = gen_rtx_REG (Pmode, R0_REG);
 
 
      /* D is the actual number of bytes that we need for saving registers,
      /* D is the actual number of bytes that we need for saving registers,
         however, in initial_elimination_offset we have committed to using
         however, in initial_elimination_offset we have committed to using
         an additional TREGS_SPACE amount of bytes - in order to keep both
         an additional TREGS_SPACE amount of bytes - in order to keep both
         addresses to arguments supplied by the caller and local variables
         addresses to arguments supplied by the caller and local variables
         valid, we must keep this gap.  Place it between the incoming
         valid, we must keep this gap.  Place it between the incoming
         arguments and the actually saved registers in a bid to optimize
         arguments and the actually saved registers in a bid to optimize
         locality of reference.  */
         locality of reference.  */
      total_size = d + tregs_space;
      total_size = d + tregs_space;
      total_size += rounded_frame_size (total_size);
      total_size += rounded_frame_size (total_size);
      save_size = total_size - rounded_frame_size (d);
      save_size = total_size - rounded_frame_size (d);
      if (save_size % (STACK_BOUNDARY / BITS_PER_UNIT))
      if (save_size % (STACK_BOUNDARY / BITS_PER_UNIT))
        d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
        d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
                        - save_size % (STACK_BOUNDARY / BITS_PER_UNIT));
                        - save_size % (STACK_BOUNDARY / BITS_PER_UNIT));
 
 
      /* If adjusting the stack in a single step costs nothing extra, do so.
      /* 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,
         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
         and we don't exceed the maximum offset range (the test for the
         latter is conservative for simplicity).  */
         latter is conservative for simplicity).  */
      if (TARGET_SHMEDIA
      if (TARGET_SHMEDIA
          && (CONST_OK_FOR_I10 (-total_size)
          && (CONST_OK_FOR_I10 (-total_size)
              || (! CONST_OK_FOR_I10 (-(save_size + d_rounding))
              || (! CONST_OK_FOR_I10 (-(save_size + d_rounding))
                  && total_size <= 2044)))
                  && total_size <= 2044)))
        d_rounding = total_size - save_size;
        d_rounding = total_size - save_size;
 
 
      offset_base = d + d_rounding;
      offset_base = d + d_rounding;
 
 
      output_stack_adjust (-(save_size + d_rounding), stack_pointer_rtx,
      output_stack_adjust (-(save_size + d_rounding), stack_pointer_rtx,
                           0, NULL, true);
                           0, NULL, true);
 
 
      sh5_schedule_saves (&live_regs_mask, &schedule, offset_base);
      sh5_schedule_saves (&live_regs_mask, &schedule, offset_base);
      tmp_pnt = schedule.temps;
      tmp_pnt = schedule.temps;
      for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
      for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
        {
        {
          enum machine_mode mode = (enum machine_mode) entry->mode;
          enum machine_mode mode = (enum machine_mode) entry->mode;
          unsigned int reg = entry->reg;
          unsigned int reg = entry->reg;
          rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
          rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
          rtx orig_reg_rtx;
          rtx orig_reg_rtx;
 
 
          offset = entry->offset;
          offset = entry->offset;
 
 
          reg_rtx = gen_rtx_REG (mode, reg);
          reg_rtx = gen_rtx_REG (mode, reg);
 
 
          mem_rtx = gen_frame_mem (mode,
          mem_rtx = gen_frame_mem (mode,
                                   gen_rtx_PLUS (Pmode,
                                   gen_rtx_PLUS (Pmode,
                                                 stack_pointer_rtx,
                                                 stack_pointer_rtx,
                                                 GEN_INT (offset)));
                                                 GEN_INT (offset)));
 
 
          if (!memory_address_p (mode, XEXP (mem_rtx, 0)))
          if (!memory_address_p (mode, XEXP (mem_rtx, 0)))
            {
            {
              gcc_assert (r0);
              gcc_assert (r0);
              mem_rtx = NULL_RTX;
              mem_rtx = NULL_RTX;
            }
            }
 
 
          if (HAVE_PRE_DECREMENT
          if (HAVE_PRE_DECREMENT
              && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
              && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
                  || mem_rtx == NULL_RTX
                  || mem_rtx == NULL_RTX
                  || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
                  || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
            {
            {
              pre_dec = gen_frame_mem (mode, gen_rtx_PRE_DEC (Pmode, r0));
              pre_dec = gen_frame_mem (mode, gen_rtx_PRE_DEC (Pmode, r0));
 
 
              if (!memory_address_p (mode, XEXP (pre_dec, 0)))
              if (!memory_address_p (mode, XEXP (pre_dec, 0)))
                pre_dec = NULL_RTX;
                pre_dec = NULL_RTX;
              else
              else
                {
                {
                  mem_rtx = NULL_RTX;
                  mem_rtx = NULL_RTX;
                  offset += GET_MODE_SIZE (mode);
                  offset += GET_MODE_SIZE (mode);
                }
                }
            }
            }
 
 
          if (mem_rtx != NULL_RTX)
          if (mem_rtx != NULL_RTX)
            goto addr_ok;
            goto addr_ok;
 
 
          if (offset_in_r0 == -1)
          if (offset_in_r0 == -1)
            {
            {
              emit_move_insn (r0, GEN_INT (offset));
              emit_move_insn (r0, GEN_INT (offset));
              offset_in_r0 = offset;
              offset_in_r0 = offset;
            }
            }
          else if (offset != offset_in_r0)
          else if (offset != offset_in_r0)
            {
            {
              emit_move_insn (r0,
              emit_move_insn (r0,
                              gen_rtx_PLUS
                              gen_rtx_PLUS
                              (Pmode, r0,
                              (Pmode, r0,
                               GEN_INT (offset - offset_in_r0)));
                               GEN_INT (offset - offset_in_r0)));
              offset_in_r0 += offset - offset_in_r0;
              offset_in_r0 += offset - offset_in_r0;
            }
            }
 
 
          if (pre_dec != NULL_RTX)
          if (pre_dec != NULL_RTX)
            {
            {
              if (! sp_in_r0)
              if (! sp_in_r0)
                {
                {
                  emit_move_insn (r0,
                  emit_move_insn (r0,
                                  gen_rtx_PLUS
                                  gen_rtx_PLUS
                                  (Pmode, r0, stack_pointer_rtx));
                                  (Pmode, r0, stack_pointer_rtx));
                  sp_in_r0 = 1;
                  sp_in_r0 = 1;
                }
                }
 
 
              offset -= GET_MODE_SIZE (mode);
              offset -= GET_MODE_SIZE (mode);
              offset_in_r0 -= GET_MODE_SIZE (mode);
              offset_in_r0 -= GET_MODE_SIZE (mode);
 
 
              mem_rtx = pre_dec;
              mem_rtx = pre_dec;
            }
            }
          else if (sp_in_r0)
          else if (sp_in_r0)
            mem_rtx = gen_frame_mem (mode, r0);
            mem_rtx = gen_frame_mem (mode, r0);
          else
          else
            mem_rtx = gen_frame_mem (mode,
            mem_rtx = gen_frame_mem (mode,
                                     gen_rtx_PLUS (Pmode,
                                     gen_rtx_PLUS (Pmode,
                                                   stack_pointer_rtx,
                                                   stack_pointer_rtx,
                                                   r0));
                                                   r0));
 
 
          /* We must not use an r0-based address for target-branch
          /* We must not use an r0-based address for target-branch
             registers or for special registers without pre-dec
             registers or for special registers without pre-dec
             memory addresses, since we store their values in r0
             memory addresses, since we store their values in r0
             first.  */
             first.  */
          gcc_assert (!TARGET_REGISTER_P (reg)
          gcc_assert (!TARGET_REGISTER_P (reg)
                      && ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
                      && ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
                          || mem_rtx == pre_dec));
                          || mem_rtx == pre_dec));
 
 
        addr_ok:
        addr_ok:
          orig_reg_rtx = reg_rtx;
          orig_reg_rtx = reg_rtx;
          if (TARGET_REGISTER_P (reg)
          if (TARGET_REGISTER_P (reg)
              || ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
              || ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
                  && mem_rtx != pre_dec))
                  && mem_rtx != pre_dec))
            {
            {
              rtx tmp_reg = gen_rtx_REG (GET_MODE (reg_rtx), *tmp_pnt);
              rtx tmp_reg = gen_rtx_REG (GET_MODE (reg_rtx), *tmp_pnt);
 
 
              emit_move_insn (tmp_reg, reg_rtx);
              emit_move_insn (tmp_reg, reg_rtx);
 
 
              if (REGNO (tmp_reg) == R0_REG)
              if (REGNO (tmp_reg) == R0_REG)
                {
                {
                  offset_in_r0 = -1;
                  offset_in_r0 = -1;
                  sp_in_r0 = 0;
                  sp_in_r0 = 0;
                  gcc_assert (!refers_to_regno_p
                  gcc_assert (!refers_to_regno_p
                              (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0));
                              (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0));
                }
                }
 
 
              if (*++tmp_pnt <= 0)
              if (*++tmp_pnt <= 0)
                tmp_pnt = schedule.temps;
                tmp_pnt = schedule.temps;
 
 
              reg_rtx = tmp_reg;
              reg_rtx = tmp_reg;
            }
            }
          {
          {
            rtx insn;
            rtx insn;
 
 
            /* Mark as interesting for dwarf cfi generator */
            /* Mark as interesting for dwarf cfi generator */
            insn = emit_move_insn (mem_rtx, reg_rtx);
            insn = emit_move_insn (mem_rtx, reg_rtx);
            RTX_FRAME_RELATED_P (insn) = 1;
            RTX_FRAME_RELATED_P (insn) = 1;
            /* If we use an intermediate register for the save, we can't
            /* If we use an intermediate register for the save, we can't
               describe this exactly in cfi as a copy of the to-be-saved
               describe this exactly in cfi as a copy of the to-be-saved
               register into the temporary register and then the temporary
               register into the temporary register and then the temporary
               register on the stack, because the temporary register can
               register on the stack, because the temporary register can
               have a different natural size than the to-be-saved register.
               have a different natural size than the to-be-saved register.
               Thus, we gloss over the intermediate copy and pretend we do
               Thus, we gloss over the intermediate copy and pretend we do
               a direct save from the to-be-saved register.  */
               a direct save from the to-be-saved register.  */
            if (REGNO (reg_rtx) != reg)
            if (REGNO (reg_rtx) != reg)
              {
              {
                rtx set;
                rtx set;
 
 
                set = gen_rtx_SET (VOIDmode, mem_rtx, orig_reg_rtx);
                set = gen_rtx_SET (VOIDmode, mem_rtx, orig_reg_rtx);
                add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
                add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
              }
              }
 
 
            if (TARGET_SHCOMPACT && (offset_in_r0 != -1))
            if (TARGET_SHCOMPACT && (offset_in_r0 != -1))
              {
              {
                rtx reg_rtx = gen_rtx_REG (mode, reg);
                rtx reg_rtx = gen_rtx_REG (mode, reg);
                rtx set;
                rtx set;
                rtx mem_rtx = gen_frame_mem (mode,
                rtx mem_rtx = gen_frame_mem (mode,
                                             gen_rtx_PLUS (Pmode,
                                             gen_rtx_PLUS (Pmode,
                                                           stack_pointer_rtx,
                                                           stack_pointer_rtx,
                                                           GEN_INT (offset)));
                                                           GEN_INT (offset)));
 
 
                set = gen_rtx_SET (VOIDmode, mem_rtx, reg_rtx);
                set = gen_rtx_SET (VOIDmode, mem_rtx, reg_rtx);
                add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
                add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
              }
              }
          }
          }
        }
        }
 
 
      gcc_assert (entry->offset == d_rounding);
      gcc_assert (entry->offset == d_rounding);
    }
    }
  else
  else
    push_regs (&live_regs_mask, current_function_interrupt);
    push_regs (&live_regs_mask, current_function_interrupt);
 
 
  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
    emit_insn (gen_GOTaddr2picreg ());
    emit_insn (gen_GOTaddr2picreg ());
 
 
  if (SHMEDIA_REGS_STACK_ADJUST ())
  if (SHMEDIA_REGS_STACK_ADJUST ())
    {
    {
      /* This must NOT go through the PLT, otherwise mach and macl
      /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
         may be clobbered.  */
      function_symbol (gen_rtx_REG (Pmode, R0_REG),
      function_symbol (gen_rtx_REG (Pmode, R0_REG),
                       (TARGET_FPU_ANY
                       (TARGET_FPU_ANY
                        ? "__GCC_push_shmedia_regs"
                        ? "__GCC_push_shmedia_regs"
                        : "__GCC_push_shmedia_regs_nofpu"), SFUNC_GOT);
                        : "__GCC_push_shmedia_regs_nofpu"), SFUNC_GOT);
      emit_insn (gen_shmedia_save_restore_regs_compact
      emit_insn (gen_shmedia_save_restore_regs_compact
                 (GEN_INT (-SHMEDIA_REGS_STACK_ADJUST ())));
                 (GEN_INT (-SHMEDIA_REGS_STACK_ADJUST ())));
    }
    }
 
 
  if (target_flags != save_flags && ! current_function_interrupt)
  if (target_flags != save_flags && ! current_function_interrupt)
    emit_insn (gen_toggle_sz ());
    emit_insn (gen_toggle_sz ());
 
 
  target_flags = save_flags;
  target_flags = save_flags;
 
 
  output_stack_adjust (-rounded_frame_size (d) + d_rounding,
  output_stack_adjust (-rounded_frame_size (d) + d_rounding,
                       stack_pointer_rtx, 0, NULL, true);
                       stack_pointer_rtx, 0, NULL, true);
 
 
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
    frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
 
 
  if (TARGET_SHCOMPACT
  if (TARGET_SHCOMPACT
      && (crtl->args.info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
      && (crtl->args.info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
    {
    {
      /* This must NOT go through the PLT, otherwise mach and macl
      /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
         may be clobbered.  */
      function_symbol (gen_rtx_REG (Pmode, R0_REG),
      function_symbol (gen_rtx_REG (Pmode, R0_REG),
                      "__GCC_shcompact_incoming_args", SFUNC_GOT);
                      "__GCC_shcompact_incoming_args", SFUNC_GOT);
      emit_insn (gen_shcompact_incoming_args ());
      emit_insn (gen_shcompact_incoming_args ());
    }
    }
}
}
 
 
void
void
sh_expand_epilogue (bool sibcall_p)
sh_expand_epilogue (bool sibcall_p)
{
{
  HARD_REG_SET live_regs_mask;
  HARD_REG_SET live_regs_mask;
  int d, i;
  int d, i;
  int d_rounding = 0;
  int d_rounding = 0;
 
 
  int save_flags = target_flags;
  int save_flags = target_flags;
  int frame_size, save_size;
  int frame_size, save_size;
  int fpscr_deferred = 0;
  int fpscr_deferred = 0;
  int e = sibcall_p ? -1 : 1;
  int e = sibcall_p ? -1 : 1;
 
 
  d = calc_live_regs (&live_regs_mask);
  d = calc_live_regs (&live_regs_mask);
 
 
  save_size = d;
  save_size = d;
  frame_size = rounded_frame_size (d);
  frame_size = rounded_frame_size (d);
 
 
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
      int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
      int total_size;
      int total_size;
      if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
      if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
      d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
      d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
                    - d % (STACK_BOUNDARY / BITS_PER_UNIT));
                    - d % (STACK_BOUNDARY / BITS_PER_UNIT));
 
 
      total_size = d + tregs_space;
      total_size = d + tregs_space;
      total_size += rounded_frame_size (total_size);
      total_size += rounded_frame_size (total_size);
      save_size = total_size - frame_size;
      save_size = total_size - frame_size;
 
 
      /* If adjusting the stack in a single step costs nothing extra, do so.
      /* 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,
         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
         and we don't exceed the maximum offset range (the test for the
         latter is conservative for simplicity).  */
         latter is conservative for simplicity).  */
      if (TARGET_SHMEDIA
      if (TARGET_SHMEDIA
          && ! frame_pointer_needed
          && ! frame_pointer_needed
          && (CONST_OK_FOR_I10 (total_size)
          && (CONST_OK_FOR_I10 (total_size)
              || (! CONST_OK_FOR_I10 (save_size + d_rounding)
              || (! CONST_OK_FOR_I10 (save_size + d_rounding)
                  && total_size <= 2044)))
                  && total_size <= 2044)))
        d_rounding = frame_size;
        d_rounding = frame_size;
 
 
      frame_size -= d_rounding;
      frame_size -= d_rounding;
    }
    }
 
 
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    {
    {
      /* We must avoid scheduling the epilogue with previous basic blocks.
      /* We must avoid scheduling the epilogue with previous basic blocks.
         See PR/18032 and PR/40313.  */
         See PR/18032 and PR/40313.  */
      emit_insn (gen_blockage ());
      emit_insn (gen_blockage ());
      output_stack_adjust (frame_size, hard_frame_pointer_rtx, e,
      output_stack_adjust (frame_size, hard_frame_pointer_rtx, e,
                           &live_regs_mask, false);
                           &live_regs_mask, false);
 
 
      /* We must avoid moving the stack pointer adjustment past code
      /* We must avoid moving the stack pointer adjustment past code
         which reads from the local frame, else an interrupt could
         which reads from the local frame, else an interrupt could
         occur after the SP adjustment and clobber data in the local
         occur after the SP adjustment and clobber data in the local
         frame.  */
         frame.  */
      emit_insn (gen_blockage ());
      emit_insn (gen_blockage ());
      emit_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
      emit_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
    }
    }
  else if (frame_size)
  else if (frame_size)
    {
    {
      /* We must avoid moving the stack pointer adjustment past code
      /* We must avoid moving the stack pointer adjustment past code
         which reads from the local frame, else an interrupt could
         which reads from the local frame, else an interrupt could
         occur after the SP adjustment and clobber data in the local
         occur after the SP adjustment and clobber data in the local
         frame.  */
         frame.  */
      emit_insn (gen_blockage ());
      emit_insn (gen_blockage ());
      output_stack_adjust (frame_size, stack_pointer_rtx, e,
      output_stack_adjust (frame_size, stack_pointer_rtx, e,
                           &live_regs_mask, false);
                           &live_regs_mask, false);
    }
    }
 
 
  if (SHMEDIA_REGS_STACK_ADJUST ())
  if (SHMEDIA_REGS_STACK_ADJUST ())
    {
    {
      function_symbol (gen_rtx_REG (Pmode, R0_REG),
      function_symbol (gen_rtx_REG (Pmode, R0_REG),
                       (TARGET_FPU_ANY
                       (TARGET_FPU_ANY
                        ? "__GCC_pop_shmedia_regs"
                        ? "__GCC_pop_shmedia_regs"
                        : "__GCC_pop_shmedia_regs_nofpu"), SFUNC_GOT);
                        : "__GCC_pop_shmedia_regs_nofpu"), SFUNC_GOT);
      /* This must NOT go through the PLT, otherwise mach and macl
      /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
         may be clobbered.  */
      emit_insn (gen_shmedia_save_restore_regs_compact
      emit_insn (gen_shmedia_save_restore_regs_compact
                 (GEN_INT (SHMEDIA_REGS_STACK_ADJUST ())));
                 (GEN_INT (SHMEDIA_REGS_STACK_ADJUST ())));
    }
    }
 
 
  /* Pop all the registers.  */
  /* Pop all the registers.  */
 
 
  if (target_flags != save_flags && ! current_function_interrupt)
  if (target_flags != save_flags && ! current_function_interrupt)
    emit_insn (gen_toggle_sz ());
    emit_insn (gen_toggle_sz ());
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      int offset_base, offset;
      int offset_base, offset;
      int offset_in_r0 = -1;
      int offset_in_r0 = -1;
      int sp_in_r0 = 0;
      int sp_in_r0 = 0;
      rtx r0 = gen_rtx_REG (Pmode, R0_REG);
      rtx r0 = gen_rtx_REG (Pmode, R0_REG);
      save_schedule schedule;
      save_schedule schedule;
      save_entry *entry;
      save_entry *entry;
      int *tmp_pnt;
      int *tmp_pnt;
 
 
      entry = sh5_schedule_saves (&live_regs_mask, &schedule, d_rounding);
      entry = sh5_schedule_saves (&live_regs_mask, &schedule, d_rounding);
      offset_base = -entry[1].offset + d_rounding;
      offset_base = -entry[1].offset + d_rounding;
      tmp_pnt = schedule.temps;
      tmp_pnt = schedule.temps;
      for (; entry->mode != VOIDmode; entry--)
      for (; entry->mode != VOIDmode; entry--)
        {
        {
          enum machine_mode mode = (enum machine_mode) entry->mode;
          enum machine_mode mode = (enum machine_mode) entry->mode;
          int reg = entry->reg;
          int reg = entry->reg;
          rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn;
          rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn;
 
 
          offset = offset_base + entry->offset;
          offset = offset_base + entry->offset;
          reg_rtx = gen_rtx_REG (mode, reg);
          reg_rtx = gen_rtx_REG (mode, reg);
 
 
          mem_rtx = gen_frame_mem (mode,
          mem_rtx = gen_frame_mem (mode,
                                   gen_rtx_PLUS (Pmode,
                                   gen_rtx_PLUS (Pmode,
                                                 stack_pointer_rtx,
                                                 stack_pointer_rtx,
                                                 GEN_INT (offset)));
                                                 GEN_INT (offset)));
 
 
          if (!memory_address_p (mode, XEXP (mem_rtx, 0)))
          if (!memory_address_p (mode, XEXP (mem_rtx, 0)))
            mem_rtx = NULL_RTX;
            mem_rtx = NULL_RTX;
 
 
          if (HAVE_POST_INCREMENT
          if (HAVE_POST_INCREMENT
              && (offset == offset_in_r0
              && (offset == offset_in_r0
                  || (offset + GET_MODE_SIZE (mode) != d + d_rounding
                  || (offset + GET_MODE_SIZE (mode) != d + d_rounding
                      && mem_rtx == NULL_RTX)
                      && mem_rtx == NULL_RTX)
                  || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
                  || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
            {
            {
              post_inc = gen_frame_mem (mode, gen_rtx_POST_INC (Pmode, r0));
              post_inc = gen_frame_mem (mode, gen_rtx_POST_INC (Pmode, r0));
 
 
              if (!memory_address_p (mode, XEXP (post_inc, 0)))
              if (!memory_address_p (mode, XEXP (post_inc, 0)))
                post_inc = NULL_RTX;
                post_inc = NULL_RTX;
              else
              else
                mem_rtx = NULL_RTX;
                mem_rtx = NULL_RTX;
            }
            }
 
 
          if (mem_rtx != NULL_RTX)
          if (mem_rtx != NULL_RTX)
            goto addr_ok;
            goto addr_ok;
 
 
          if (offset_in_r0 == -1)
          if (offset_in_r0 == -1)
            {
            {
              emit_move_insn (r0, GEN_INT (offset));
              emit_move_insn (r0, GEN_INT (offset));
              offset_in_r0 = offset;
              offset_in_r0 = offset;
            }
            }
          else if (offset != offset_in_r0)
          else if (offset != offset_in_r0)
            {
            {
              emit_move_insn (r0,
              emit_move_insn (r0,
                              gen_rtx_PLUS
                              gen_rtx_PLUS
                              (Pmode, r0,
                              (Pmode, r0,
                               GEN_INT (offset - offset_in_r0)));
                               GEN_INT (offset - offset_in_r0)));
              offset_in_r0 += offset - offset_in_r0;
              offset_in_r0 += offset - offset_in_r0;
            }
            }
 
 
          if (post_inc != NULL_RTX)
          if (post_inc != NULL_RTX)
            {
            {
              if (! sp_in_r0)
              if (! sp_in_r0)
                {
                {
                  emit_move_insn (r0,
                  emit_move_insn (r0,
                                  gen_rtx_PLUS
                                  gen_rtx_PLUS
                                  (Pmode, r0, stack_pointer_rtx));
                                  (Pmode, r0, stack_pointer_rtx));
                  sp_in_r0 = 1;
                  sp_in_r0 = 1;
                }
                }
 
 
              mem_rtx = post_inc;
              mem_rtx = post_inc;
 
 
              offset_in_r0 += GET_MODE_SIZE (mode);
              offset_in_r0 += GET_MODE_SIZE (mode);
            }
            }
          else if (sp_in_r0)
          else if (sp_in_r0)
            mem_rtx = gen_frame_mem (mode, r0);
            mem_rtx = gen_frame_mem (mode, r0);
          else
          else
            mem_rtx = gen_frame_mem (mode,
            mem_rtx = gen_frame_mem (mode,
                                     gen_rtx_PLUS (Pmode,
                                     gen_rtx_PLUS (Pmode,
                                                   stack_pointer_rtx,
                                                   stack_pointer_rtx,
                                                   r0));
                                                   r0));
 
 
          gcc_assert ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
          gcc_assert ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
                      || mem_rtx == post_inc);
                      || mem_rtx == post_inc);
 
 
        addr_ok:
        addr_ok:
          if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
          if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
              && mem_rtx != post_inc)
              && mem_rtx != post_inc)
            {
            {
              insn = emit_move_insn (r0, mem_rtx);
              insn = emit_move_insn (r0, mem_rtx);
              mem_rtx = r0;
              mem_rtx = r0;
            }
            }
          else if (TARGET_REGISTER_P (reg))
          else if (TARGET_REGISTER_P (reg))
            {
            {
              rtx tmp_reg = gen_rtx_REG (mode, *tmp_pnt);
              rtx tmp_reg = gen_rtx_REG (mode, *tmp_pnt);
 
 
              /* Give the scheduler a bit of freedom by using up to
              /* Give the scheduler a bit of freedom by using up to
                 MAX_TEMPS registers in a round-robin fashion.  */
                 MAX_TEMPS registers in a round-robin fashion.  */
              insn = emit_move_insn (tmp_reg, mem_rtx);
              insn = emit_move_insn (tmp_reg, mem_rtx);
              mem_rtx = tmp_reg;
              mem_rtx = tmp_reg;
              if (*++tmp_pnt < 0)
              if (*++tmp_pnt < 0)
                tmp_pnt = schedule.temps;
                tmp_pnt = schedule.temps;
            }
            }
 
 
          insn = emit_move_insn (reg_rtx, mem_rtx);
          insn = emit_move_insn (reg_rtx, mem_rtx);
        }
        }
 
 
      gcc_assert (entry->offset + offset_base == d + d_rounding);
      gcc_assert (entry->offset + offset_base == d + d_rounding);
    }
    }
  else /* ! TARGET_SH5 */
  else /* ! TARGET_SH5 */
    {
    {
      int last_reg;
      int last_reg;
 
 
      save_size = 0;
      save_size = 0;
        /* For an ISR with RESBANK attribute assigned, don't pop PR
        /* For an ISR with RESBANK attribute assigned, don't pop PR
           register.  */
           register.  */
      if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)
      if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)
          && !sh_cfun_resbank_handler_p ())
          && !sh_cfun_resbank_handler_p ())
        {
        {
          if (!frame_pointer_needed)
          if (!frame_pointer_needed)
            emit_insn (gen_blockage ());
            emit_insn (gen_blockage ());
          pop (PR_REG);
          pop (PR_REG);
        }
        }
 
 
      /* Banked registers are popped first to avoid being scheduled in the
      /* Banked registers are popped first to avoid being scheduled in the
         delay slot. RTE switches banks before the ds instruction.  */
         delay slot. RTE switches banks before the ds instruction.  */
      if (current_function_interrupt)
      if (current_function_interrupt)
        {
        {
          for (i = LAST_BANKED_REG; i >= FIRST_BANKED_REG; i--)
          for (i = LAST_BANKED_REG; i >= FIRST_BANKED_REG; i--)
            if (TEST_HARD_REG_BIT (live_regs_mask, i))
            if (TEST_HARD_REG_BIT (live_regs_mask, i))
              pop (i);
              pop (i);
 
 
          last_reg = FIRST_PSEUDO_REGISTER - LAST_BANKED_REG - 1;
          last_reg = FIRST_PSEUDO_REGISTER - LAST_BANKED_REG - 1;
        }
        }
      else
      else
        last_reg = FIRST_PSEUDO_REGISTER;
        last_reg = FIRST_PSEUDO_REGISTER;
 
 
      for (i = 0; i < last_reg; i++)
      for (i = 0; i < last_reg; i++)
        {
        {
          int j = (FIRST_PSEUDO_REGISTER - 1) - i;
          int j = (FIRST_PSEUDO_REGISTER - 1) - i;
 
 
          if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
          if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
              && hard_reg_set_intersect_p (live_regs_mask,
              && hard_reg_set_intersect_p (live_regs_mask,
                                          reg_class_contents[DF_REGS]))
                                          reg_class_contents[DF_REGS]))
            fpscr_deferred = 1;
            fpscr_deferred = 1;
          /* For an ISR with RESBANK attribute assigned, don't pop
          /* For an ISR with RESBANK attribute assigned, don't pop
             following registers, R0-R14, MACH, MACL and GBR.  */
             following registers, R0-R14, MACH, MACL and GBR.  */
          else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)
          else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)
                   && ! (sh_cfun_resbank_handler_p ()
                   && ! (sh_cfun_resbank_handler_p ()
                         && ((j >= FIRST_GENERAL_REG
                         && ((j >= FIRST_GENERAL_REG
                              && j < LAST_GENERAL_REG)
                              && j < LAST_GENERAL_REG)
                              || j == MACH_REG
                              || j == MACH_REG
                              || j == MACL_REG
                              || j == MACL_REG
                              || j == GBR_REG)))
                              || j == GBR_REG)))
            pop (j);
            pop (j);
 
 
          if (j == FIRST_FP_REG && fpscr_deferred)
          if (j == FIRST_FP_REG && fpscr_deferred)
            pop (FPSCR_REG);
            pop (FPSCR_REG);
        }
        }
    }
    }
  if (target_flags != save_flags && ! current_function_interrupt)
  if (target_flags != save_flags && ! current_function_interrupt)
    emit_insn (gen_toggle_sz ());
    emit_insn (gen_toggle_sz ());
  target_flags = save_flags;
  target_flags = save_flags;
 
 
  output_stack_adjust (crtl->args.pretend_args_size
  output_stack_adjust (crtl->args.pretend_args_size
                       + save_size + d_rounding
                       + save_size + d_rounding
                       + crtl->args.info.stack_regs * 8,
                       + crtl->args.info.stack_regs * 8,
                       stack_pointer_rtx, e, NULL, false);
                       stack_pointer_rtx, e, NULL, false);
 
 
  if (crtl->calls_eh_return)
  if (crtl->calls_eh_return)
    emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
    emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
                         EH_RETURN_STACKADJ_RTX));
                         EH_RETURN_STACKADJ_RTX));
 
 
  /* Switch back to the normal stack if necessary.  */
  /* Switch back to the normal stack if necessary.  */
  if (lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl)))
  if (lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl)))
    emit_insn (gen_sp_switch_2 ());
    emit_insn (gen_sp_switch_2 ());
 
 
  /* Tell flow the insn that pops PR isn't dead.  */
  /* Tell flow the insn that pops PR isn't dead.  */
  /* PR_REG will never be live in SHmedia mode, and we don't need to
  /* PR_REG will never be live in SHmedia mode, and we don't need to
     USE PR_MEDIA_REG, since it will be explicitly copied to TR0_REG
     USE PR_MEDIA_REG, since it will be explicitly copied to TR0_REG
     by the return pattern.  */
     by the return pattern.  */
  if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
  if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
    emit_use (gen_rtx_REG (SImode, PR_REG));
    emit_use (gen_rtx_REG (SImode, PR_REG));
}
}
 
 
static int sh_need_epilogue_known = 0;
static int sh_need_epilogue_known = 0;
 
 
int
int
sh_need_epilogue (void)
sh_need_epilogue (void)
{
{
  if (! sh_need_epilogue_known)
  if (! sh_need_epilogue_known)
    {
    {
      rtx epilogue;
      rtx epilogue;
 
 
      start_sequence ();
      start_sequence ();
      sh_expand_epilogue (0);
      sh_expand_epilogue (0);
      epilogue = get_insns ();
      epilogue = get_insns ();
      end_sequence ();
      end_sequence ();
      sh_need_epilogue_known = (epilogue == NULL ? -1 : 1);
      sh_need_epilogue_known = (epilogue == NULL ? -1 : 1);
    }
    }
  return sh_need_epilogue_known > 0;
  return sh_need_epilogue_known > 0;
}
}
 
 
/* Emit code to change the current function's return address to RA.
/* Emit code to change the current function's return address to RA.
   TEMP is available as a scratch register, if needed.  */
   TEMP is available as a scratch register, if needed.  */
 
 
void
void
sh_set_return_address (rtx ra, rtx tmp)
sh_set_return_address (rtx ra, rtx tmp)
{
{
  HARD_REG_SET live_regs_mask;
  HARD_REG_SET live_regs_mask;
  int d;
  int d;
  int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
  int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
  int pr_offset;
  int pr_offset;
 
 
  d = calc_live_regs (&live_regs_mask);
  d = calc_live_regs (&live_regs_mask);
 
 
  /* If pr_reg isn't life, we can set it (or the register given in
  /* If pr_reg isn't life, we can set it (or the register given in
     sh_media_register_for_return) directly.  */
     sh_media_register_for_return) directly.  */
  if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
  if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
    {
    {
      rtx rr;
      rtx rr;
 
 
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        {
        {
          int rr_regno = sh_media_register_for_return ();
          int rr_regno = sh_media_register_for_return ();
 
 
          if (rr_regno < 0)
          if (rr_regno < 0)
            rr_regno = pr_reg;
            rr_regno = pr_reg;
 
 
          rr = gen_rtx_REG (DImode, rr_regno);
          rr = gen_rtx_REG (DImode, rr_regno);
        }
        }
      else
      else
        rr = gen_rtx_REG (SImode, pr_reg);
        rr = gen_rtx_REG (SImode, pr_reg);
 
 
      emit_insn (GEN_MOV (rr, ra));
      emit_insn (GEN_MOV (rr, ra));
      /* Tell flow the register for return isn't dead.  */
      /* Tell flow the register for return isn't dead.  */
      emit_use (rr);
      emit_use (rr);
      return;
      return;
    }
    }
 
 
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      int offset;
      int offset;
      save_schedule schedule;
      save_schedule schedule;
      save_entry *entry;
      save_entry *entry;
 
 
      entry = sh5_schedule_saves (&live_regs_mask, &schedule, 0);
      entry = sh5_schedule_saves (&live_regs_mask, &schedule, 0);
      offset = entry[1].offset;
      offset = entry[1].offset;
      for (; entry->mode != VOIDmode; entry--)
      for (; entry->mode != VOIDmode; entry--)
        if (entry->reg == pr_reg)
        if (entry->reg == pr_reg)
          goto found;
          goto found;
 
 
      /* We can't find pr register.  */
      /* We can't find pr register.  */
      gcc_unreachable ();
      gcc_unreachable ();
 
 
    found:
    found:
      offset = entry->offset - offset;
      offset = entry->offset - offset;
      pr_offset = (rounded_frame_size (d) + offset
      pr_offset = (rounded_frame_size (d) + offset
                   + SHMEDIA_REGS_STACK_ADJUST ());
                   + SHMEDIA_REGS_STACK_ADJUST ());
    }
    }
  else
  else
    pr_offset = rounded_frame_size (d);
    pr_offset = rounded_frame_size (d);
 
 
  emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset)));
  emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset)));
  emit_insn (GEN_ADD3 (tmp, tmp, hard_frame_pointer_rtx));
  emit_insn (GEN_ADD3 (tmp, tmp, hard_frame_pointer_rtx));
 
 
  tmp = gen_frame_mem (Pmode, tmp);
  tmp = gen_frame_mem (Pmode, tmp);
  emit_insn (GEN_MOV (tmp, ra));
  emit_insn (GEN_MOV (tmp, ra));
  /* Tell this store isn't dead.  */
  /* Tell this store isn't dead.  */
  emit_use (tmp);
  emit_use (tmp);
}
}
 
 
/* Clear variables at function end.  */
/* Clear variables at function end.  */
 
 
static void
static void
sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT size ATTRIBUTE_UNUSED)
                             HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
{
  sh_need_epilogue_known = 0;
  sh_need_epilogue_known = 0;
}
}
 
 
static rtx
static rtx
sh_builtin_saveregs (void)
sh_builtin_saveregs (void)
{
{
  /* First unnamed integer register.  */
  /* First unnamed integer register.  */
  int first_intreg = crtl->args.info.arg_count[(int) SH_ARG_INT];
  int first_intreg = crtl->args.info.arg_count[(int) SH_ARG_INT];
  /* Number of integer registers we need to save.  */
  /* Number of integer registers we need to save.  */
  int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg);
  int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg);
  /* First unnamed SFmode float reg */
  /* First unnamed SFmode float reg */
  int first_floatreg = crtl->args.info.arg_count[(int) SH_ARG_FLOAT];
  int first_floatreg = crtl->args.info.arg_count[(int) SH_ARG_FLOAT];
  /* Number of SFmode float regs to save.  */
  /* Number of SFmode float regs to save.  */
  int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
  int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
  rtx regbuf, fpregs;
  rtx regbuf, fpregs;
  int bufsize, regno;
  int bufsize, regno;
  alias_set_type alias_set;
  alias_set_type alias_set;
 
 
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      if (n_intregs)
      if (n_intregs)
        {
        {
          int pushregs = n_intregs;
          int pushregs = n_intregs;
 
 
          while (pushregs < NPARM_REGS (SImode) - 1
          while (pushregs < NPARM_REGS (SImode) - 1
                 && (CALL_COOKIE_INT_REG_GET
                 && (CALL_COOKIE_INT_REG_GET
                        (crtl->args.info.call_cookie,
                        (crtl->args.info.call_cookie,
                         NPARM_REGS (SImode) - pushregs)
                         NPARM_REGS (SImode) - pushregs)
                     == 1))
                     == 1))
            {
            {
              crtl->args.info.call_cookie
              crtl->args.info.call_cookie
                &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
                &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
                                          - pushregs, 1);
                                          - pushregs, 1);
              pushregs++;
              pushregs++;
            }
            }
 
 
          if (pushregs == NPARM_REGS (SImode))
          if (pushregs == NPARM_REGS (SImode))
            crtl->args.info.call_cookie
            crtl->args.info.call_cookie
              |= (CALL_COOKIE_INT_REG (0, 1)
              |= (CALL_COOKIE_INT_REG (0, 1)
                  | CALL_COOKIE_STACKSEQ (pushregs - 1));
                  | CALL_COOKIE_STACKSEQ (pushregs - 1));
          else
          else
            crtl->args.info.call_cookie
            crtl->args.info.call_cookie
              |= CALL_COOKIE_STACKSEQ (pushregs);
              |= CALL_COOKIE_STACKSEQ (pushregs);
 
 
          crtl->args.pretend_args_size += 8 * n_intregs;
          crtl->args.pretend_args_size += 8 * n_intregs;
        }
        }
      if (TARGET_SHCOMPACT)
      if (TARGET_SHCOMPACT)
        return const0_rtx;
        return const0_rtx;
    }
    }
 
 
  if (! TARGET_SH2E && ! TARGET_SH4 && ! TARGET_SH5)
  if (! TARGET_SH2E && ! TARGET_SH4 && ! TARGET_SH5)
    {
    {
      error ("__builtin_saveregs not supported by this subtarget");
      error ("__builtin_saveregs not supported by this subtarget");
      return const0_rtx;
      return const0_rtx;
    }
    }
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    n_floatregs = 0;
    n_floatregs = 0;
 
 
  /* Allocate block of memory for the regs.  */
  /* Allocate block of memory for the regs.  */
  /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
  /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
     Or can assign_stack_local accept a 0 SIZE argument?  */
     Or can assign_stack_local accept a 0 SIZE argument?  */
  bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
  bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    regbuf = gen_frame_mem (BLKmode, gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
    regbuf = gen_frame_mem (BLKmode, gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
  else if (n_floatregs & 1)
  else if (n_floatregs & 1)
    {
    {
      rtx addr;
      rtx addr;
 
 
      regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
      regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
      addr = copy_to_mode_reg (Pmode, XEXP (regbuf, 0));
      addr = copy_to_mode_reg (Pmode, XEXP (regbuf, 0));
      emit_insn (gen_iorsi3 (addr, addr, GEN_INT (UNITS_PER_WORD)));
      emit_insn (gen_iorsi3 (addr, addr, GEN_INT (UNITS_PER_WORD)));
      regbuf = change_address (regbuf, BLKmode, addr);
      regbuf = change_address (regbuf, BLKmode, addr);
    }
    }
  else if (STACK_BOUNDARY < 64 && TARGET_FPU_DOUBLE && n_floatregs)
  else if (STACK_BOUNDARY < 64 && TARGET_FPU_DOUBLE && n_floatregs)
    {
    {
      rtx addr, mask;
      rtx addr, mask;
 
 
      regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
      regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
      addr = copy_to_mode_reg (Pmode, plus_constant (XEXP (regbuf, 0), 4));
      addr = copy_to_mode_reg (Pmode, plus_constant (XEXP (regbuf, 0), 4));
      mask = copy_to_mode_reg (Pmode, GEN_INT (-8));
      mask = copy_to_mode_reg (Pmode, GEN_INT (-8));
      emit_insn (gen_andsi3 (addr, addr, mask));
      emit_insn (gen_andsi3 (addr, addr, mask));
      regbuf = change_address (regbuf, BLKmode, addr);
      regbuf = change_address (regbuf, BLKmode, addr);
    }
    }
  else
  else
    regbuf = assign_stack_local (BLKmode, bufsize, TARGET_FPU_DOUBLE ? 64 : 0);
    regbuf = assign_stack_local (BLKmode, bufsize, TARGET_FPU_DOUBLE ? 64 : 0);
  alias_set = get_varargs_alias_set ();
  alias_set = get_varargs_alias_set ();
  set_mem_alias_set (regbuf, alias_set);
  set_mem_alias_set (regbuf, alias_set);
 
 
  /* Save int args.
  /* Save int args.
     This is optimized to only save the regs that are necessary.  Explicitly
     This is optimized to only save the regs that are necessary.  Explicitly
     named args need not be saved.  */
     named args need not be saved.  */
  if (n_intregs > 0)
  if (n_intregs > 0)
    move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg,
    move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg,
                         adjust_address (regbuf, BLKmode,
                         adjust_address (regbuf, BLKmode,
                                         n_floatregs * UNITS_PER_WORD),
                                         n_floatregs * UNITS_PER_WORD),
                         n_intregs);
                         n_intregs);
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    /* Return the address of the regbuf.  */
    /* Return the address of the regbuf.  */
    return XEXP (regbuf, 0);
    return XEXP (regbuf, 0);
 
 
  /* Save float args.
  /* Save float args.
     This is optimized to only save the regs that are necessary.  Explicitly
     This is optimized to only save the regs that are necessary.  Explicitly
     named args need not be saved.
     named args need not be saved.
     We explicitly build a pointer to the buffer because it halves the insn
     We explicitly build a pointer to the buffer because it halves the insn
     count when not optimizing (otherwise the pointer is built for each reg
     count when not optimizing (otherwise the pointer is built for each reg
     saved).
     saved).
     We emit the moves in reverse order so that we can use predecrement.  */
     We emit the moves in reverse order so that we can use predecrement.  */
 
 
  fpregs = copy_to_mode_reg (Pmode,
  fpregs = copy_to_mode_reg (Pmode,
                             plus_constant (XEXP (regbuf, 0),
                             plus_constant (XEXP (regbuf, 0),
                                            n_floatregs * UNITS_PER_WORD));
                                            n_floatregs * UNITS_PER_WORD));
  if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
  if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
    {
    {
      rtx mem;
      rtx mem;
      for (regno = NPARM_REGS (DFmode) - 2; regno >= first_floatreg; regno -= 2)
      for (regno = NPARM_REGS (DFmode) - 2; regno >= first_floatreg; regno -= 2)
        {
        {
          emit_insn (gen_addsi3 (fpregs, fpregs,
          emit_insn (gen_addsi3 (fpregs, fpregs,
                                 GEN_INT (-2 * UNITS_PER_WORD)));
                                 GEN_INT (-2 * UNITS_PER_WORD)));
          mem = change_address (regbuf, DFmode, fpregs);
          mem = change_address (regbuf, DFmode, fpregs);
          emit_move_insn (mem,
          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;
      regno = first_floatreg;
      if (regno & 1)
      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 = change_address (regbuf, SFmode, fpregs);
          mem = change_address (regbuf, SFmode, fpregs);
          emit_move_insn (mem,
          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)));
                                                - (TARGET_LITTLE_ENDIAN != 0)));
        }
        }
    }
    }
  else
  else
    for (regno = NPARM_REGS (SFmode) - 1; regno >= first_floatreg; regno--)
    for (regno = NPARM_REGS (SFmode) - 1; regno >= first_floatreg; regno--)
      {
      {
        rtx mem;
        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 = change_address (regbuf, SFmode, fpregs);
        mem = change_address (regbuf, SFmode, fpregs);
        emit_move_insn (mem,
        emit_move_insn (mem,
                        gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno));
                        gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno));
      }
      }
 
 
  /* Return the address of the regbuf.  */
  /* Return the address of the regbuf.  */
  return XEXP (regbuf, 0);
  return XEXP (regbuf, 0);
}
}
 
 
/* Define the `__builtin_va_list' type for the ABI.  */
/* Define the `__builtin_va_list' type for the ABI.  */
 
 
static tree
static tree
sh_build_builtin_va_list (void)
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 f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
  tree record;
  tree record;
 
 
  if (TARGET_SH5 || (! TARGET_SH2E && ! TARGET_SH4)
  if (TARGET_SH5 || (! TARGET_SH2E && ! TARGET_SH4)
      || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
      || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
    return ptr_type_node;
    return ptr_type_node;
 
 
  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
 
 
  f_next_o = build_decl (BUILTINS_LOCATION,
  f_next_o = build_decl (BUILTINS_LOCATION,
                         FIELD_DECL, get_identifier ("__va_next_o"),
                         FIELD_DECL, get_identifier ("__va_next_o"),
                         ptr_type_node);
                         ptr_type_node);
  f_next_o_limit = build_decl (BUILTINS_LOCATION,
  f_next_o_limit = build_decl (BUILTINS_LOCATION,
                               FIELD_DECL,
                               FIELD_DECL,
                               get_identifier ("__va_next_o_limit"),
                               get_identifier ("__va_next_o_limit"),
                               ptr_type_node);
                               ptr_type_node);
  f_next_fp = build_decl (BUILTINS_LOCATION,
  f_next_fp = build_decl (BUILTINS_LOCATION,
                          FIELD_DECL, get_identifier ("__va_next_fp"),
                          FIELD_DECL, get_identifier ("__va_next_fp"),
                          ptr_type_node);
                          ptr_type_node);
  f_next_fp_limit = build_decl (BUILTINS_LOCATION,
  f_next_fp_limit = build_decl (BUILTINS_LOCATION,
                                FIELD_DECL,
                                FIELD_DECL,
                                get_identifier ("__va_next_fp_limit"),
                                get_identifier ("__va_next_fp_limit"),
                                ptr_type_node);
                                ptr_type_node);
  f_next_stack = build_decl (BUILTINS_LOCATION,
  f_next_stack = build_decl (BUILTINS_LOCATION,
                             FIELD_DECL, get_identifier ("__va_next_stack"),
                             FIELD_DECL, get_identifier ("__va_next_stack"),
                             ptr_type_node);
                             ptr_type_node);
 
 
  DECL_FIELD_CONTEXT (f_next_o) = record;
  DECL_FIELD_CONTEXT (f_next_o) = record;
  DECL_FIELD_CONTEXT (f_next_o_limit) = record;
  DECL_FIELD_CONTEXT (f_next_o_limit) = record;
  DECL_FIELD_CONTEXT (f_next_fp) = record;
  DECL_FIELD_CONTEXT (f_next_fp) = record;
  DECL_FIELD_CONTEXT (f_next_fp_limit) = record;
  DECL_FIELD_CONTEXT (f_next_fp_limit) = record;
  DECL_FIELD_CONTEXT (f_next_stack) = record;
  DECL_FIELD_CONTEXT (f_next_stack) = record;
 
 
  TYPE_FIELDS (record) = f_next_o;
  TYPE_FIELDS (record) = f_next_o;
  TREE_CHAIN (f_next_o) = f_next_o_limit;
  TREE_CHAIN (f_next_o) = f_next_o_limit;
  TREE_CHAIN (f_next_o_limit) = f_next_fp;
  TREE_CHAIN (f_next_o_limit) = f_next_fp;
  TREE_CHAIN (f_next_fp) = f_next_fp_limit;
  TREE_CHAIN (f_next_fp) = f_next_fp_limit;
  TREE_CHAIN (f_next_fp_limit) = f_next_stack;
  TREE_CHAIN (f_next_fp_limit) = f_next_stack;
 
 
  layout_type (record);
  layout_type (record);
 
 
  return record;
  return record;
}
}
 
 
/* Implement `va_start' for varargs and stdarg.  */
/* Implement `va_start' for varargs and stdarg.  */
 
 
static void
static void
sh_va_start (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 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;
  tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
  tree t, u;
  tree t, u;
  int nfp, nint;
  int nfp, nint;
 
 
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      expand_builtin_saveregs ();
      expand_builtin_saveregs ();
      std_expand_builtin_va_start (valist, nextarg);
      std_expand_builtin_va_start (valist, nextarg);
      return;
      return;
    }
    }
 
 
  if ((! TARGET_SH2E && ! TARGET_SH4)
  if ((! TARGET_SH2E && ! TARGET_SH4)
      || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
      || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
    {
    {
      std_expand_builtin_va_start (valist, nextarg);
      std_expand_builtin_va_start (valist, nextarg);
      return;
      return;
    }
    }
 
 
  f_next_o = TYPE_FIELDS (va_list_type_node);
  f_next_o = TYPE_FIELDS (va_list_type_node);
  f_next_o_limit = TREE_CHAIN (f_next_o);
  f_next_o_limit = TREE_CHAIN (f_next_o);
  f_next_fp = TREE_CHAIN (f_next_o_limit);
  f_next_fp = TREE_CHAIN (f_next_o_limit);
  f_next_fp_limit = TREE_CHAIN (f_next_fp);
  f_next_fp_limit = TREE_CHAIN (f_next_fp);
  f_next_stack = TREE_CHAIN (f_next_fp_limit);
  f_next_stack = TREE_CHAIN (f_next_fp_limit);
 
 
  next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
  next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
                   NULL_TREE);
                   NULL_TREE);
  next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
  next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
                         valist, f_next_o_limit, NULL_TREE);
                         valist, f_next_o_limit, NULL_TREE);
  next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
  next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
                    NULL_TREE);
                    NULL_TREE);
  next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
  next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
                          valist, f_next_fp_limit, NULL_TREE);
                          valist, f_next_fp_limit, NULL_TREE);
  next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
  next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
                       valist, f_next_stack, NULL_TREE);
                       valist, f_next_stack, NULL_TREE);
 
 
  /* Call __builtin_saveregs.  */
  /* Call __builtin_saveregs.  */
  u = make_tree (sizetype, expand_builtin_saveregs ());
  u = make_tree (sizetype, expand_builtin_saveregs ());
  u = fold_convert (ptr_type_node, u);
  u = fold_convert (ptr_type_node, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_fp, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_fp, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
 
  nfp = crtl->args.info.arg_count[SH_ARG_FLOAT];
  nfp = crtl->args.info.arg_count[SH_ARG_FLOAT];
  if (nfp < 8)
  if (nfp < 8)
    nfp = 8 - nfp;
    nfp = 8 - nfp;
  else
  else
    nfp = 0;
    nfp = 0;
  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
                   size_int (UNITS_PER_WORD * nfp));
                   size_int (UNITS_PER_WORD * nfp));
  t = build2 (MODIFY_EXPR, ptr_type_node, next_fp_limit, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_fp_limit, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
 
  t = build2 (MODIFY_EXPR, ptr_type_node, next_o, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_o, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
 
  nint = crtl->args.info.arg_count[SH_ARG_INT];
  nint = crtl->args.info.arg_count[SH_ARG_INT];
  if (nint < 4)
  if (nint < 4)
    nint = 4 - nint;
    nint = 4 - nint;
  else
  else
    nint = 0;
    nint = 0;
  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
                   size_int (UNITS_PER_WORD * nint));
                   size_int (UNITS_PER_WORD * nint));
  t = build2 (MODIFY_EXPR, ptr_type_node, next_o_limit, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_o_limit, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
 
  u = make_tree (ptr_type_node, nextarg);
  u = make_tree (ptr_type_node, nextarg);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_stack, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_stack, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
}
 
 
/* TYPE is a RECORD_TYPE.  If there is only a single nonzero-sized
/* TYPE is a RECORD_TYPE.  If there is only a single nonzero-sized
   member, return it.  */
   member, return it.  */
static tree
static tree
find_sole_member (tree type)
find_sole_member (tree type)
{
{
  tree field, member = NULL_TREE;
  tree field, member = NULL_TREE;
 
 
  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
    {
    {
      if (TREE_CODE (field) != FIELD_DECL)
      if (TREE_CODE (field) != FIELD_DECL)
        continue;
        continue;
      if (!DECL_SIZE (field))
      if (!DECL_SIZE (field))
        return NULL_TREE;
        return NULL_TREE;
      if (integer_zerop (DECL_SIZE (field)))
      if (integer_zerop (DECL_SIZE (field)))
        continue;
        continue;
      if (member)
      if (member)
        return NULL_TREE;
        return NULL_TREE;
      member = field;
      member = field;
    }
    }
  return member;
  return member;
}
}
/* Implement `va_arg'.  */
/* Implement `va_arg'.  */
 
 
static tree
static tree
sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
                         gimple_seq *post_p ATTRIBUTE_UNUSED)
                         gimple_seq *post_p ATTRIBUTE_UNUSED)
{
{
  HOST_WIDE_INT size, rsize;
  HOST_WIDE_INT size, rsize;
  tree tmp, pptr_type_node;
  tree tmp, pptr_type_node;
  tree addr, lab_over = NULL, result = NULL;
  tree addr, lab_over = NULL, result = NULL;
  int pass_by_ref = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
  int pass_by_ref = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
  tree eff_type;
  tree eff_type;
 
 
  if (pass_by_ref)
  if (pass_by_ref)
    type = build_pointer_type (type);
    type = build_pointer_type (type);
 
 
  size = int_size_in_bytes (type);
  size = int_size_in_bytes (type);
  rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
  rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
  pptr_type_node = build_pointer_type (ptr_type_node);
  pptr_type_node = build_pointer_type (ptr_type_node);
 
 
  if (! TARGET_SH5 && (TARGET_SH2E || TARGET_SH4)
  if (! TARGET_SH5 && (TARGET_SH2E || TARGET_SH4)
      && ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ()))
      && ! (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 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;
      tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
      int pass_as_float;
      int pass_as_float;
      tree lab_false;
      tree lab_false;
      tree member;
      tree member;
 
 
      f_next_o = TYPE_FIELDS (va_list_type_node);
      f_next_o = TYPE_FIELDS (va_list_type_node);
      f_next_o_limit = TREE_CHAIN (f_next_o);
      f_next_o_limit = TREE_CHAIN (f_next_o);
      f_next_fp = TREE_CHAIN (f_next_o_limit);
      f_next_fp = TREE_CHAIN (f_next_o_limit);
      f_next_fp_limit = TREE_CHAIN (f_next_fp);
      f_next_fp_limit = TREE_CHAIN (f_next_fp);
      f_next_stack = TREE_CHAIN (f_next_fp_limit);
      f_next_stack = TREE_CHAIN (f_next_fp_limit);
 
 
      next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
      next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
                       NULL_TREE);
                       NULL_TREE);
      next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
      next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
                             valist, f_next_o_limit, NULL_TREE);
                             valist, f_next_o_limit, NULL_TREE);
      next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp),
      next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp),
                        valist, f_next_fp, NULL_TREE);
                        valist, f_next_fp, NULL_TREE);
      next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
      next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
                              valist, f_next_fp_limit, NULL_TREE);
                              valist, f_next_fp_limit, NULL_TREE);
      next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
      next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
                           valist, f_next_stack, NULL_TREE);
                           valist, f_next_stack, NULL_TREE);
 
 
      /* Structures with a single member with a distinct mode are passed
      /* Structures with a single member with a distinct mode are passed
         like their member.  This is relevant if the latter has a REAL_TYPE
         like their member.  This is relevant if the latter has a REAL_TYPE
         or COMPLEX_TYPE type.  */
         or COMPLEX_TYPE type.  */
      eff_type = type;
      eff_type = type;
      while (TREE_CODE (eff_type) == RECORD_TYPE
      while (TREE_CODE (eff_type) == RECORD_TYPE
             && (member = find_sole_member (eff_type))
             && (member = find_sole_member (eff_type))
             && (TREE_CODE (TREE_TYPE (member)) == REAL_TYPE
             && (TREE_CODE (TREE_TYPE (member)) == REAL_TYPE
                 || TREE_CODE (TREE_TYPE (member)) == COMPLEX_TYPE
                 || TREE_CODE (TREE_TYPE (member)) == COMPLEX_TYPE
                 || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE))
                 || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE))
        {
        {
          tree field_type = TREE_TYPE (member);
          tree field_type = TREE_TYPE (member);
 
 
          if (TYPE_MODE (eff_type) == TYPE_MODE (field_type))
          if (TYPE_MODE (eff_type) == TYPE_MODE (field_type))
            eff_type = field_type;
            eff_type = field_type;
          else
          else
            {
            {
              gcc_assert ((TYPE_ALIGN (eff_type)
              gcc_assert ((TYPE_ALIGN (eff_type)
                           < GET_MODE_ALIGNMENT (TYPE_MODE (field_type)))
                           < GET_MODE_ALIGNMENT (TYPE_MODE (field_type)))
                          || (TYPE_ALIGN (eff_type)
                          || (TYPE_ALIGN (eff_type)
                              > GET_MODE_BITSIZE (TYPE_MODE (field_type))));
                              > GET_MODE_BITSIZE (TYPE_MODE (field_type))));
              break;
              break;
            }
            }
        }
        }
 
 
      if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
      if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
        {
        {
          pass_as_float = ((TREE_CODE (eff_type) == REAL_TYPE && size <= 8)
          pass_as_float = ((TREE_CODE (eff_type) == REAL_TYPE && size <= 8)
                           || (TREE_CODE (eff_type) == COMPLEX_TYPE
                           || (TREE_CODE (eff_type) == COMPLEX_TYPE
                               && TREE_CODE (TREE_TYPE (eff_type)) == REAL_TYPE
                               && TREE_CODE (TREE_TYPE (eff_type)) == REAL_TYPE
                               && size <= 16));
                               && size <= 16));
        }
        }
      else
      else
        {
        {
          pass_as_float = (TREE_CODE (eff_type) == REAL_TYPE && size == 4);
          pass_as_float = (TREE_CODE (eff_type) == REAL_TYPE && size == 4);
        }
        }
 
 
      addr = create_tmp_var (pptr_type_node, NULL);
      addr = create_tmp_var (pptr_type_node, NULL);
      lab_false = create_artificial_label (UNKNOWN_LOCATION);
      lab_false = create_artificial_label (UNKNOWN_LOCATION);
      lab_over = create_artificial_label (UNKNOWN_LOCATION);
      lab_over = create_artificial_label (UNKNOWN_LOCATION);
 
 
      valist = build1 (INDIRECT_REF, ptr_type_node, addr);
      valist = build1 (INDIRECT_REF, ptr_type_node, addr);
 
 
      if (pass_as_float)
      if (pass_as_float)
        {
        {
          tree next_fp_tmp = create_tmp_var (TREE_TYPE (f_next_fp), NULL);
          tree next_fp_tmp = create_tmp_var (TREE_TYPE (f_next_fp), NULL);
          tree cmp;
          tree cmp;
          bool is_double = size == 8 && TREE_CODE (eff_type) == REAL_TYPE;
          bool is_double = size == 8 && TREE_CODE (eff_type) == REAL_TYPE;
 
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_fp));
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_fp));
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
 
 
          gimplify_assign (unshare_expr (next_fp_tmp), valist, pre_p);
          gimplify_assign (unshare_expr (next_fp_tmp), valist, pre_p);
          tmp = next_fp_limit;
          tmp = next_fp_limit;
          if (size > 4 && !is_double)
          if (size > 4 && !is_double)
            tmp = build2 (POINTER_PLUS_EXPR, TREE_TYPE (tmp),
            tmp = build2 (POINTER_PLUS_EXPR, TREE_TYPE (tmp),
                          unshare_expr (tmp), size_int (4 - size));
                          unshare_expr (tmp), size_int (4 - size));
          tmp = build2 (GE_EXPR, boolean_type_node,
          tmp = build2 (GE_EXPR, boolean_type_node,
                        unshare_expr (next_fp_tmp), unshare_expr (tmp));
                        unshare_expr (next_fp_tmp), unshare_expr (tmp));
          cmp = build3 (COND_EXPR, void_type_node, tmp,
          cmp = build3 (COND_EXPR, void_type_node, tmp,
                        build1 (GOTO_EXPR, void_type_node,
                        build1 (GOTO_EXPR, void_type_node,
                                unshare_expr (lab_false)), NULL_TREE);
                                unshare_expr (lab_false)), NULL_TREE);
          if (!is_double)
          if (!is_double)
            gimplify_and_add (cmp, pre_p);
            gimplify_and_add (cmp, pre_p);
 
 
          if (TYPE_ALIGN (eff_type) > BITS_PER_WORD
          if (TYPE_ALIGN (eff_type) > BITS_PER_WORD
              || (is_double || size == 16))
              || (is_double || size == 16))
            {
            {
              tmp = fold_convert (sizetype, next_fp_tmp);
              tmp = fold_convert (sizetype, next_fp_tmp);
              tmp = build2 (BIT_AND_EXPR, sizetype, tmp,
              tmp = build2 (BIT_AND_EXPR, sizetype, tmp,
                            size_int (UNITS_PER_WORD));
                            size_int (UNITS_PER_WORD));
              tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
              tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
                            unshare_expr (next_fp_tmp), tmp);
                            unshare_expr (next_fp_tmp), tmp);
              gimplify_assign (unshare_expr (next_fp_tmp), tmp, pre_p);
              gimplify_assign (unshare_expr (next_fp_tmp), tmp, pre_p);
            }
            }
          if (is_double)
          if (is_double)
            gimplify_and_add (cmp, pre_p);
            gimplify_and_add (cmp, pre_p);
 
 
#ifdef FUNCTION_ARG_SCmode_WART
#ifdef FUNCTION_ARG_SCmode_WART
          if (TYPE_MODE (eff_type) == SCmode
          if (TYPE_MODE (eff_type) == SCmode
              && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
              && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
            {
            {
              tree subtype = TREE_TYPE (eff_type);
              tree subtype = TREE_TYPE (eff_type);
              tree real, imag;
              tree real, imag;
 
 
              imag
              imag
                = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
                = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
              imag = get_initialized_tmp_var (imag, pre_p, NULL);
              imag = get_initialized_tmp_var (imag, pre_p, NULL);
 
 
              real
              real
                = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
                = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
              real = get_initialized_tmp_var (real, pre_p, NULL);
              real = get_initialized_tmp_var (real, pre_p, NULL);
 
 
              result = build2 (COMPLEX_EXPR, eff_type, real, imag);
              result = build2 (COMPLEX_EXPR, eff_type, real, imag);
              if (type != eff_type)
              if (type != eff_type)
                result = build1 (VIEW_CONVERT_EXPR, type, result);
                result = build1 (VIEW_CONVERT_EXPR, type, result);
              result = get_initialized_tmp_var (result, pre_p, NULL);
              result = get_initialized_tmp_var (result, pre_p, NULL);
            }
            }
#endif /* FUNCTION_ARG_SCmode_WART */
#endif /* FUNCTION_ARG_SCmode_WART */
 
 
          tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
          tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
          gimplify_and_add (tmp, pre_p);
          gimplify_and_add (tmp, pre_p);
 
 
          tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
          tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
          gimplify_and_add (tmp, pre_p);
          gimplify_and_add (tmp, pre_p);
 
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
          gimplify_assign (unshare_expr (next_fp_tmp),
          gimplify_assign (unshare_expr (next_fp_tmp),
                           unshare_expr (valist), pre_p);
                           unshare_expr (valist), pre_p);
 
 
          gimplify_assign (unshare_expr (valist),
          gimplify_assign (unshare_expr (valist),
                           unshare_expr (next_fp_tmp), post_p);
                           unshare_expr (next_fp_tmp), post_p);
          valist = next_fp_tmp;
          valist = next_fp_tmp;
        }
        }
      else
      else
        {
        {
          tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
          tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
                        unshare_expr (next_o), size_int (rsize));
                        unshare_expr (next_o), size_int (rsize));
          tmp = build2 (GT_EXPR, boolean_type_node, tmp,
          tmp = build2 (GT_EXPR, boolean_type_node, tmp,
                        unshare_expr (next_o_limit));
                        unshare_expr (next_o_limit));
          tmp = build3 (COND_EXPR, void_type_node, tmp,
          tmp = build3 (COND_EXPR, void_type_node, tmp,
                        build1 (GOTO_EXPR, void_type_node,
                        build1 (GOTO_EXPR, void_type_node,
                                unshare_expr (lab_false)),
                                unshare_expr (lab_false)),
                        NULL_TREE);
                        NULL_TREE);
          gimplify_and_add (tmp, pre_p);
          gimplify_and_add (tmp, pre_p);
 
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_o));
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_o));
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
 
 
          tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
          tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
          gimplify_and_add (tmp, pre_p);
          gimplify_and_add (tmp, pre_p);
 
 
          tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
          tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
          gimplify_and_add (tmp, pre_p);
          gimplify_and_add (tmp, pre_p);
 
 
          if (size > 4 && ! (TARGET_SH4 || TARGET_SH2A))
          if (size > 4 && ! (TARGET_SH4 || TARGET_SH2A))
            gimplify_assign (unshare_expr (next_o),
            gimplify_assign (unshare_expr (next_o),
                             unshare_expr (next_o_limit), pre_p);
                             unshare_expr (next_o_limit), pre_p);
 
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
          tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
          gimplify_assign (unshare_expr (addr), tmp, pre_p);
        }
        }
 
 
      if (!result)
      if (!result)
        {
        {
          tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
          tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
          gimplify_and_add (tmp, pre_p);
          gimplify_and_add (tmp, pre_p);
        }
        }
    }
    }
 
 
  /* ??? In va-sh.h, there had been code to make values larger than
  /* ??? In va-sh.h, there had been code to make values larger than
     size 8 indirect.  This does not match the FUNCTION_ARG macros.  */
     size 8 indirect.  This does not match the FUNCTION_ARG macros.  */
 
 
  tmp = std_gimplify_va_arg_expr (valist, type, pre_p, NULL);
  tmp = std_gimplify_va_arg_expr (valist, type, pre_p, NULL);
  if (result)
  if (result)
    {
    {
      gimplify_assign (result, tmp, pre_p);
      gimplify_assign (result, tmp, pre_p);
      result = build1 (NOP_EXPR, TREE_TYPE (result), result);
      result = build1 (NOP_EXPR, TREE_TYPE (result), result);
      tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
      tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
      gimplify_and_add (tmp, pre_p);
      gimplify_and_add (tmp, pre_p);
    }
    }
  else
  else
    result = tmp;
    result = tmp;
 
 
  if (pass_by_ref)
  if (pass_by_ref)
    result = build_va_arg_indirect_ref (result);
    result = build_va_arg_indirect_ref (result);
 
 
  return result;
  return result;
}
}
 
 
/* 64 bit floating points memory transfers are paired single precision loads
/* 64 bit floating points memory transfers are paired single precision loads
   or store. So DWARF information needs fixing in little endian (unless
   or store. So DWARF information needs fixing in little endian (unless
   PR=SZ=1 in FPSCR).  */
   PR=SZ=1 in FPSCR).  */
rtx
rtx
sh_dwarf_register_span (rtx reg)
sh_dwarf_register_span (rtx reg)
{
{
  unsigned regno = REGNO (reg);
  unsigned regno = REGNO (reg);
 
 
  if (WORDS_BIG_ENDIAN || GET_MODE (reg) != DFmode)
  if (WORDS_BIG_ENDIAN || GET_MODE (reg) != DFmode)
    return NULL_RTX;
    return NULL_RTX;
 
 
  return
  return
    gen_rtx_PARALLEL (VOIDmode,
    gen_rtx_PARALLEL (VOIDmode,
                      gen_rtvec (2,
                      gen_rtvec (2,
                                 gen_rtx_REG (SFmode,
                                 gen_rtx_REG (SFmode,
                                              DBX_REGISTER_NUMBER (regno+1)),
                                              DBX_REGISTER_NUMBER (regno+1)),
                                 gen_rtx_REG (SFmode,
                                 gen_rtx_REG (SFmode,
                                              DBX_REGISTER_NUMBER (regno))));
                                              DBX_REGISTER_NUMBER (regno))));
}
}
 
 
static enum machine_mode
static enum machine_mode
sh_promote_function_mode (const_tree type, enum machine_mode mode,
sh_promote_function_mode (const_tree type, enum machine_mode mode,
                          int *punsignedp, const_tree funtype,
                          int *punsignedp, const_tree funtype,
                          int for_return ATTRIBUTE_UNUSED)
                          int for_return ATTRIBUTE_UNUSED)
{
{
  if (sh_promote_prototypes (funtype))
  if (sh_promote_prototypes (funtype))
    return promote_mode (type, mode, punsignedp);
    return promote_mode (type, mode, punsignedp);
  else
  else
    return mode;
    return mode;
}
}
 
 
static bool
static bool
sh_promote_prototypes (const_tree type)
sh_promote_prototypes (const_tree type)
{
{
  if (TARGET_HITACHI)
  if (TARGET_HITACHI)
    return 0;
    return 0;
  if (! type)
  if (! type)
    return 1;
    return 1;
  return ! sh_attr_renesas_p (type);
  return ! sh_attr_renesas_p (type);
}
}
 
 
/* Whether an argument must be passed by reference.  On SHcompact, we
/* Whether an argument must be passed by reference.  On SHcompact, we
   pretend arguments wider than 32-bits that would have been passed in
   pretend arguments wider than 32-bits that would have been passed in
   registers are passed by reference, so that an SHmedia trampoline
   registers are passed by reference, so that an SHmedia trampoline
   loads them into the full 64-bits registers.  */
   loads them into the full 64-bits registers.  */
 
 
static int
static int
shcompact_byref (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
shcompact_byref (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
                 const_tree type, bool named)
                 const_tree type, bool named)
{
{
  unsigned HOST_WIDE_INT size;
  unsigned HOST_WIDE_INT size;
 
 
  if (type)
  if (type)
    size = int_size_in_bytes (type);
    size = int_size_in_bytes (type);
  else
  else
    size = GET_MODE_SIZE (mode);
    size = GET_MODE_SIZE (mode);
 
 
  if (cum->arg_count[SH_ARG_INT] < NPARM_REGS (SImode)
  if (cum->arg_count[SH_ARG_INT] < NPARM_REGS (SImode)
      && (!named
      && (!named
          || GET_SH_ARG_CLASS (mode) == SH_ARG_INT
          || GET_SH_ARG_CLASS (mode) == SH_ARG_INT
          || (GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT
          || (GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT
              && cum->arg_count[SH_ARG_FLOAT] >= NPARM_REGS (SFmode)))
              && cum->arg_count[SH_ARG_FLOAT] >= NPARM_REGS (SFmode)))
      && size > 4
      && size > 4
      && !SHCOMPACT_FORCE_ON_STACK (mode, type)
      && !SHCOMPACT_FORCE_ON_STACK (mode, type)
      && !SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
      && !SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
    return size;
    return size;
  else
  else
    return 0;
    return 0;
}
}
 
 
static bool
static bool
sh_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
sh_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                      const_tree type, bool named)
                      const_tree type, bool named)
{
{
  if (targetm.calls.must_pass_in_stack (mode, type))
  if (targetm.calls.must_pass_in_stack (mode, type))
    return true;
    return true;
 
 
  /* ??? std_gimplify_va_arg_expr passes NULL for cum.  That function
  /* ??? std_gimplify_va_arg_expr passes NULL for cum.  That function
     wants to know about pass-by-reference semantics for incoming
     wants to know about pass-by-reference semantics for incoming
     arguments.  */
     arguments.  */
  if (! cum)
  if (! cum)
    return false;
    return false;
 
 
  if (TARGET_SHCOMPACT)
  if (TARGET_SHCOMPACT)
    {
    {
      cum->byref = shcompact_byref (cum, mode, type, named);
      cum->byref = shcompact_byref (cum, mode, type, named);
      return cum->byref != 0;
      return cum->byref != 0;
    }
    }
 
 
  return false;
  return false;
}
}
 
 
static bool
static bool
sh_callee_copies (CUMULATIVE_ARGS *cum, enum machine_mode mode,
sh_callee_copies (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                  const_tree type, bool named ATTRIBUTE_UNUSED)
                  const_tree type, bool named ATTRIBUTE_UNUSED)
{
{
  /* ??? How can it possibly be correct to return true only on the
  /* ??? How can it possibly be correct to return true only on the
     caller side of the equation?  Is there someplace else in the
     caller side of the equation?  Is there someplace else in the
     sh backend that's magically producing the copies?  */
     sh backend that's magically producing the copies?  */
  return (cum->outgoing
  return (cum->outgoing
          && ((mode == BLKmode ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode))
          && ((mode == BLKmode ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode))
              % SH_MIN_ALIGN_FOR_CALLEE_COPY == 0));
              % SH_MIN_ALIGN_FOR_CALLEE_COPY == 0));
}
}
 
 
static int
static int
sh_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
sh_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                      tree type, bool named ATTRIBUTE_UNUSED)
                      tree type, bool named ATTRIBUTE_UNUSED)
{
{
  int words = 0;
  int words = 0;
 
 
  if (!TARGET_SH5
  if (!TARGET_SH5
      && PASS_IN_REG_P (*cum, mode, type)
      && PASS_IN_REG_P (*cum, mode, type)
      && !(TARGET_SH4 || TARGET_SH2A_DOUBLE)
      && !(TARGET_SH4 || TARGET_SH2A_DOUBLE)
      && (ROUND_REG (*cum, mode)
      && (ROUND_REG (*cum, mode)
          + (mode != BLKmode
          + (mode != BLKmode
             ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
             ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
             : ROUND_ADVANCE (int_size_in_bytes (type)))
             : ROUND_ADVANCE (int_size_in_bytes (type)))
          > NPARM_REGS (mode)))
          > NPARM_REGS (mode)))
    words = NPARM_REGS (mode) - ROUND_REG (*cum, mode);
    words = NPARM_REGS (mode) - ROUND_REG (*cum, mode);
 
 
  else if (!TARGET_SHCOMPACT
  else if (!TARGET_SHCOMPACT
           && SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
           && SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
    words = NPARM_REGS (SImode) - cum->arg_count[SH_ARG_INT];
    words = NPARM_REGS (SImode) - cum->arg_count[SH_ARG_INT];
 
 
  return words * UNITS_PER_WORD;
  return words * UNITS_PER_WORD;
}
}
 
 
 
 
/* Define where to put the arguments to a function.
/* Define where to put the arguments to a function.
   Value is zero to push the argument on the stack,
   Value is zero to push the argument on the stack,
   or a hard register in which to store the argument.
   or a hard register in which to store the argument.
 
 
   MODE is the argument's machine mode.
   MODE is the argument's machine mode.
   TYPE is the data type of the argument (as a tree).
   TYPE is the data type of the argument (as a tree).
    This is null for libcalls where that information may
    This is null for libcalls where that information may
    not be available.
    not be available.
   CUM is a variable of type CUMULATIVE_ARGS which gives info about
   CUM is a variable of type CUMULATIVE_ARGS which gives info about
    the preceding args and about the function being called.
    the preceding args and about the function being called.
   NAMED is nonzero if this argument is a named parameter
   NAMED is nonzero if this argument is a named parameter
    (otherwise it is an extra parameter matching an ellipsis).
    (otherwise it is an extra parameter matching an ellipsis).
 
 
   On SH the first args are normally in registers
   On SH the first args are normally in registers
   and the rest are pushed.  Any arg that starts within the first
   and the rest are pushed.  Any arg that starts within the first
   NPARM_REGS words is at least partially passed in a register unless
   NPARM_REGS words is at least partially passed in a register unless
   its data type forbids.  */
   its data type forbids.  */
 
 
 
 
rtx
rtx
sh_function_arg (CUMULATIVE_ARGS *ca, enum machine_mode mode,
sh_function_arg (CUMULATIVE_ARGS *ca, enum machine_mode mode,
                 tree type, int named)
                 tree type, int named)
{
{
  if (! TARGET_SH5 && mode == VOIDmode)
  if (! TARGET_SH5 && mode == VOIDmode)
    return GEN_INT (ca->renesas_abi ? 1 : 0);
    return GEN_INT (ca->renesas_abi ? 1 : 0);
 
 
  if (! TARGET_SH5
  if (! TARGET_SH5
      && PASS_IN_REG_P (*ca, mode, type)
      && PASS_IN_REG_P (*ca, mode, type)
      && (named || ! (TARGET_HITACHI || ca->renesas_abi)))
      && (named || ! (TARGET_HITACHI || ca->renesas_abi)))
    {
    {
      int regno;
      int regno;
 
 
      if (mode == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN
      if (mode == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN
          && (! FUNCTION_ARG_SCmode_WART || (ROUND_REG (*ca, mode) & 1)))
          && (! FUNCTION_ARG_SCmode_WART || (ROUND_REG (*ca, mode) & 1)))
        {
        {
          rtx r1 = gen_rtx_EXPR_LIST (VOIDmode,
          rtx r1 = gen_rtx_EXPR_LIST (VOIDmode,
                                      gen_rtx_REG (SFmode,
                                      gen_rtx_REG (SFmode,
                                                   BASE_ARG_REG (mode)
                                                   BASE_ARG_REG (mode)
                                                   + (ROUND_REG (*ca, mode) ^ 1)),
                                                   + (ROUND_REG (*ca, mode) ^ 1)),
                                      const0_rtx);
                                      const0_rtx);
          rtx r2 = gen_rtx_EXPR_LIST (VOIDmode,
          rtx r2 = gen_rtx_EXPR_LIST (VOIDmode,
                                      gen_rtx_REG (SFmode,
                                      gen_rtx_REG (SFmode,
                                                   BASE_ARG_REG (mode)
                                                   BASE_ARG_REG (mode)
                                                   + ((ROUND_REG (*ca, mode) + 1) ^ 1)),
                                                   + ((ROUND_REG (*ca, mode) + 1) ^ 1)),
                                      GEN_INT (4));
                                      GEN_INT (4));
          return gen_rtx_PARALLEL(SCmode, gen_rtvec(2, r1, r2));
          return gen_rtx_PARALLEL(SCmode, gen_rtvec(2, r1, r2));
        }
        }
 
 
     /* If the alignment of a DF value causes an SF register to be
     /* If the alignment of a DF value causes an SF register to be
        skipped, we will use that skipped register for the next SF
        skipped, we will use that skipped register for the next SF
        value.  */
        value.  */
      if ((TARGET_HITACHI || ca->renesas_abi)
      if ((TARGET_HITACHI || ca->renesas_abi)
          && ca->free_single_fp_reg
          && ca->free_single_fp_reg
          && mode == SFmode)
          && mode == SFmode)
        return gen_rtx_REG (mode, ca->free_single_fp_reg);
        return gen_rtx_REG (mode, ca->free_single_fp_reg);
 
 
      regno = (BASE_ARG_REG (mode) + ROUND_REG (*ca, mode))
      regno = (BASE_ARG_REG (mode) + ROUND_REG (*ca, mode))
               ^ (mode == SFmode && TARGET_SH4
               ^ (mode == SFmode && TARGET_SH4
                  && TARGET_LITTLE_ENDIAN != 0
                  && TARGET_LITTLE_ENDIAN != 0
                  && ! TARGET_HITACHI && ! ca->renesas_abi);
                  && ! TARGET_HITACHI && ! ca->renesas_abi);
      return gen_rtx_REG (mode, regno);
      return gen_rtx_REG (mode, regno);
 
 
    }
    }
 
 
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      if (mode == VOIDmode && TARGET_SHCOMPACT)
      if (mode == VOIDmode && TARGET_SHCOMPACT)
        return GEN_INT (ca->call_cookie);
        return GEN_INT (ca->call_cookie);
 
 
      /* The following test assumes unnamed arguments are promoted to
      /* The following test assumes unnamed arguments are promoted to
         DFmode.  */
         DFmode.  */
      if (mode == SFmode && ca->free_single_fp_reg)
      if (mode == SFmode && ca->free_single_fp_reg)
        return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode, 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)
      if ((GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT)
          && (named || ! ca->prototype_p)
          && (named || ! ca->prototype_p)
          && ca->arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (SFmode))
          && ca->arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (SFmode))
        {
        {
          if (! ca->prototype_p && TARGET_SHMEDIA)
          if (! ca->prototype_p && TARGET_SHMEDIA)
            return SH5_PROTOTYPELESS_FLOAT_ARG (*ca, mode);
            return SH5_PROTOTYPELESS_FLOAT_ARG (*ca, mode);
 
 
          return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode,
          return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode,
                                           FIRST_FP_PARM_REG
                                           FIRST_FP_PARM_REG
                                           + ca->arg_count[(int) SH_ARG_FLOAT]);
                                           + ca->arg_count[(int) SH_ARG_FLOAT]);
        }
        }
 
 
      if (ca->arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode)
      if (ca->arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode)
          && (! TARGET_SHCOMPACT
          && (! TARGET_SHCOMPACT
              || (! SHCOMPACT_FORCE_ON_STACK (mode, type)
              || (! SHCOMPACT_FORCE_ON_STACK (mode, type)
                  && ! SH5_WOULD_BE_PARTIAL_NREGS (*ca, mode,
                  && ! SH5_WOULD_BE_PARTIAL_NREGS (*ca, mode,
                                                   type, named))))
                                                   type, named))))
        {
        {
          return gen_rtx_REG (mode, (FIRST_PARM_REG
          return gen_rtx_REG (mode, (FIRST_PARM_REG
                                       + ca->arg_count[(int) SH_ARG_INT]));
                                       + ca->arg_count[(int) SH_ARG_INT]));
        }
        }
 
 
      return 0;
      return 0;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Update the data in CUM to advance over an argument
/* Update the data in CUM to advance over an argument
   of mode MODE and data type TYPE.
   of mode MODE and data type TYPE.
   (TYPE is null for libcalls where that information may not be
   (TYPE is null for libcalls where that information may not be
   available.)  */
   available.)  */
 
 
void
void
sh_function_arg_advance (CUMULATIVE_ARGS *ca, enum machine_mode mode,
sh_function_arg_advance (CUMULATIVE_ARGS *ca, enum machine_mode mode,
                         tree type, int named)
                         tree type, int named)
{
{
  if (ca->force_mem)
  if (ca->force_mem)
    ca->force_mem = 0;
    ca->force_mem = 0;
  else if (TARGET_SH5)
  else if (TARGET_SH5)
    {
    {
      tree type2 = (ca->byref && type
      tree type2 = (ca->byref && type
                    ? TREE_TYPE (type)
                    ? TREE_TYPE (type)
                    : type);
                    : type);
      enum machine_mode mode2 = (ca->byref && type
      enum machine_mode mode2 = (ca->byref && type
                                 ? TYPE_MODE (type2)
                                 ? TYPE_MODE (type2)
                                 : mode);
                                 : mode);
      int dwords = ((ca->byref
      int dwords = ((ca->byref
                     ? ca->byref
                     ? ca->byref
                     : mode2 == BLKmode
                     : mode2 == BLKmode
                     ? int_size_in_bytes (type2)
                     ? int_size_in_bytes (type2)
                     : GET_MODE_SIZE (mode2)) + 7) / 8;
                     : GET_MODE_SIZE (mode2)) + 7) / 8;
      int numregs = MIN (dwords, NPARM_REGS (SImode)
      int numregs = MIN (dwords, NPARM_REGS (SImode)
                         - ca->arg_count[(int) SH_ARG_INT]);
                         - ca->arg_count[(int) SH_ARG_INT]);
 
 
      if (numregs)
      if (numregs)
        {
        {
          ca->arg_count[(int) SH_ARG_INT] += numregs;
          ca->arg_count[(int) SH_ARG_INT] += numregs;
          if (TARGET_SHCOMPACT
          if (TARGET_SHCOMPACT
              && SHCOMPACT_FORCE_ON_STACK (mode2, type2))
              && SHCOMPACT_FORCE_ON_STACK (mode2, type2))
            {
            {
              ca->call_cookie
              ca->call_cookie
                |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
                |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
                                        - numregs, 1);
                                        - numregs, 1);
              /* N.B. We want this also for outgoing.  */
              /* N.B. We want this also for outgoing.  */
              ca->stack_regs += numregs;
              ca->stack_regs += numregs;
            }
            }
          else if (ca->byref)
          else if (ca->byref)
            {
            {
              if (! ca->outgoing)
              if (! ca->outgoing)
                ca->stack_regs += numregs;
                ca->stack_regs += numregs;
              ca->byref_regs += numregs;
              ca->byref_regs += numregs;
              ca->byref = 0;
              ca->byref = 0;
              do
              do
                ca->call_cookie
                ca->call_cookie
                  |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
                  |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
                                          - numregs, 2);
                                          - numregs, 2);
              while (--numregs);
              while (--numregs);
              ca->call_cookie
              ca->call_cookie
                |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
                |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
                                        - 1, 1);
                                        - 1, 1);
            }
            }
          else if (dwords > numregs)
          else if (dwords > numregs)
            {
            {
              int pushregs = numregs;
              int pushregs = numregs;
 
 
              if (TARGET_SHCOMPACT)
              if (TARGET_SHCOMPACT)
                ca->stack_regs += numregs;
                ca->stack_regs += numregs;
              while (pushregs < NPARM_REGS (SImode) - 1
              while (pushregs < NPARM_REGS (SImode) - 1
                     && (CALL_COOKIE_INT_REG_GET
                     && (CALL_COOKIE_INT_REG_GET
                         (ca->call_cookie,
                         (ca->call_cookie,
                          NPARM_REGS (SImode) - pushregs)
                          NPARM_REGS (SImode) - pushregs)
                         == 1))
                         == 1))
                {
                {
                  ca->call_cookie
                  ca->call_cookie
                    &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
                    &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
                                              - pushregs, 1);
                                              - pushregs, 1);
                  pushregs++;
                  pushregs++;
                }
                }
              if (numregs == NPARM_REGS (SImode))
              if (numregs == NPARM_REGS (SImode))
                ca->call_cookie
                ca->call_cookie
                  |= CALL_COOKIE_INT_REG (0, 1)
                  |= CALL_COOKIE_INT_REG (0, 1)
                  | CALL_COOKIE_STACKSEQ (numregs - 1);
                  | CALL_COOKIE_STACKSEQ (numregs - 1);
              else
              else
                ca->call_cookie
                ca->call_cookie
                  |= CALL_COOKIE_STACKSEQ (numregs);
                  |= CALL_COOKIE_STACKSEQ (numregs);
            }
            }
        }
        }
      if (GET_SH_ARG_CLASS (mode2) == SH_ARG_FLOAT
      if (GET_SH_ARG_CLASS (mode2) == SH_ARG_FLOAT
          && (named || ! ca->prototype_p))
          && (named || ! ca->prototype_p))
        {
        {
          if (mode2 == SFmode && ca->free_single_fp_reg)
          if (mode2 == SFmode && ca->free_single_fp_reg)
            ca->free_single_fp_reg = 0;
            ca->free_single_fp_reg = 0;
          else if (ca->arg_count[(int) SH_ARG_FLOAT]
          else if (ca->arg_count[(int) SH_ARG_FLOAT]
                   < NPARM_REGS (SFmode))
                   < NPARM_REGS (SFmode))
            {
            {
              int numfpregs
              int numfpregs
                = MIN ((GET_MODE_SIZE (mode2) + 7) / 8 * 2,
                = MIN ((GET_MODE_SIZE (mode2) + 7) / 8 * 2,
                       NPARM_REGS (SFmode)
                       NPARM_REGS (SFmode)
                       - ca->arg_count[(int) SH_ARG_FLOAT]);
                       - ca->arg_count[(int) SH_ARG_FLOAT]);
 
 
              ca->arg_count[(int) SH_ARG_FLOAT] += numfpregs;
              ca->arg_count[(int) SH_ARG_FLOAT] += numfpregs;
 
 
              if (TARGET_SHCOMPACT && ! ca->prototype_p)
              if (TARGET_SHCOMPACT && ! ca->prototype_p)
                {
                {
                  if (ca->outgoing && numregs > 0)
                  if (ca->outgoing && numregs > 0)
                    do
                    do
                      {
                      {
                        ca->call_cookie
                        ca->call_cookie
                          |= (CALL_COOKIE_INT_REG
                          |= (CALL_COOKIE_INT_REG
                              (ca->arg_count[(int) SH_ARG_INT]
                              (ca->arg_count[(int) SH_ARG_INT]
                               - numregs + ((numfpregs - 2) / 2),
                               - numregs + ((numfpregs - 2) / 2),
                               4 + (ca->arg_count[(int) SH_ARG_FLOAT]
                               4 + (ca->arg_count[(int) SH_ARG_FLOAT]
                                    - numfpregs) / 2));
                                    - numfpregs) / 2));
                      }
                      }
                    while (numfpregs -= 2);
                    while (numfpregs -= 2);
                }
                }
              else if (mode2 == SFmode && (named)
              else if (mode2 == SFmode && (named)
                       && (ca->arg_count[(int) SH_ARG_FLOAT]
                       && (ca->arg_count[(int) SH_ARG_FLOAT]
                           < NPARM_REGS (SFmode)))
                           < NPARM_REGS (SFmode)))
                ca->free_single_fp_reg
                ca->free_single_fp_reg
                  = FIRST_FP_PARM_REG - numfpregs
                  = FIRST_FP_PARM_REG - numfpregs
                  + ca->arg_count[(int) SH_ARG_FLOAT] + 1;
                  + ca->arg_count[(int) SH_ARG_FLOAT] + 1;
            }
            }
        }
        }
      return;
      return;
    }
    }
 
 
  if ((TARGET_HITACHI || ca->renesas_abi) && TARGET_FPU_DOUBLE)
  if ((TARGET_HITACHI || ca->renesas_abi) && TARGET_FPU_DOUBLE)
    {
    {
      /* Note that we've used the skipped register.  */
      /* Note that we've used the skipped register.  */
      if (mode == SFmode && ca->free_single_fp_reg)
      if (mode == SFmode && ca->free_single_fp_reg)
        {
        {
          ca->free_single_fp_reg = 0;
          ca->free_single_fp_reg = 0;
          return;
          return;
        }
        }
      /* When we have a DF after an SF, there's an SF register that get
      /* 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
         skipped in order to align the DF value.  We note this skipped
         register, because the next SF value will use it, and not the
         register, because the next SF value will use it, and not the
         SF that follows the DF.  */
         SF that follows the DF.  */
      if (mode == DFmode
      if (mode == DFmode
          && ROUND_REG (*ca, DFmode) != ROUND_REG (*ca, SFmode))
          && ROUND_REG (*ca, DFmode) != ROUND_REG (*ca, SFmode))
        {
        {
          ca->free_single_fp_reg = (ROUND_REG (*ca, SFmode)
          ca->free_single_fp_reg = (ROUND_REG (*ca, SFmode)
                                    + BASE_ARG_REG (mode));
                                    + BASE_ARG_REG (mode));
        }
        }
    }
    }
 
 
  if (! ((TARGET_SH4 || TARGET_SH2A) || ca->renesas_abi)
  if (! ((TARGET_SH4 || TARGET_SH2A) || ca->renesas_abi)
      || PASS_IN_REG_P (*ca, mode, type))
      || PASS_IN_REG_P (*ca, mode, type))
    (ca->arg_count[(int) GET_SH_ARG_CLASS (mode)]
    (ca->arg_count[(int) GET_SH_ARG_CLASS (mode)]
     = (ROUND_REG (*ca, mode)
     = (ROUND_REG (*ca, mode)
        + (mode == BLKmode
        + (mode == BLKmode
           ? ROUND_ADVANCE (int_size_in_bytes (type))
           ? ROUND_ADVANCE (int_size_in_bytes (type))
           : ROUND_ADVANCE (GET_MODE_SIZE (mode)))));
           : ROUND_ADVANCE (GET_MODE_SIZE (mode)))));
}
}
 
 
/* The Renesas calling convention doesn't quite fit into this scheme since
/* 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
   the address is passed like an invisible argument, but one that is always
   passed in memory.  */
   passed in memory.  */
static rtx
static rtx
sh_struct_value_rtx (tree fndecl, int incoming ATTRIBUTE_UNUSED)
sh_struct_value_rtx (tree fndecl, int incoming ATTRIBUTE_UNUSED)
{
{
  if (TARGET_HITACHI || sh_attr_renesas_p (fndecl))
  if (TARGET_HITACHI || sh_attr_renesas_p (fndecl))
    return 0;
    return 0;
  return gen_rtx_REG (Pmode, 2);
  return gen_rtx_REG (Pmode, 2);
}
}
 
 
/* Worker function for TARGET_FUNCTION_VALUE.
/* Worker function for TARGET_FUNCTION_VALUE.
 
 
   For the SH, this is like LIBCALL_VALUE, except that we must change the
   For the SH, this is like LIBCALL_VALUE, except that we must change the
   mode like PROMOTE_MODE does.
   mode like PROMOTE_MODE does.
   ??? PROMOTE_MODE is ignored for non-scalar types.  The set of types
   ??? PROMOTE_MODE is ignored for non-scalar types.  The set of types
   tested here has to be kept in sync with the one in explow.c:promote_mode.
   tested here has to be kept in sync with the one in explow.c:promote_mode.
*/
*/
 
 
static rtx
static rtx
sh_function_value (const_tree valtype,
sh_function_value (const_tree valtype,
                   const_tree fn_decl_or_type,
                   const_tree fn_decl_or_type,
                   bool outgoing ATTRIBUTE_UNUSED)
                   bool outgoing ATTRIBUTE_UNUSED)
{
{
  if (fn_decl_or_type
  if (fn_decl_or_type
      && !DECL_P (fn_decl_or_type))
      && !DECL_P (fn_decl_or_type))
    fn_decl_or_type = NULL;
    fn_decl_or_type = NULL;
 
 
  return gen_rtx_REG (
  return gen_rtx_REG (
           ((GET_MODE_CLASS (TYPE_MODE (valtype)) == MODE_INT
           ((GET_MODE_CLASS (TYPE_MODE (valtype)) == MODE_INT
             && GET_MODE_SIZE (TYPE_MODE (valtype)) < 4
             && GET_MODE_SIZE (TYPE_MODE (valtype)) < 4
             && (TREE_CODE (valtype) == INTEGER_TYPE
             && (TREE_CODE (valtype) == INTEGER_TYPE
                 || TREE_CODE (valtype) == ENUMERAL_TYPE
                 || TREE_CODE (valtype) == ENUMERAL_TYPE
                 || TREE_CODE (valtype) == BOOLEAN_TYPE
                 || TREE_CODE (valtype) == BOOLEAN_TYPE
                 || TREE_CODE (valtype) == REAL_TYPE
                 || TREE_CODE (valtype) == REAL_TYPE
                 || TREE_CODE (valtype) == OFFSET_TYPE))
                 || TREE_CODE (valtype) == OFFSET_TYPE))
            && sh_promote_prototypes (fn_decl_or_type)
            && sh_promote_prototypes (fn_decl_or_type)
            ? (TARGET_SHMEDIA64 ? DImode : SImode) : TYPE_MODE (valtype)),
            ? (TARGET_SHMEDIA64 ? DImode : SImode) : TYPE_MODE (valtype)),
           BASE_RETURN_VALUE_REG (TYPE_MODE (valtype)));
           BASE_RETURN_VALUE_REG (TYPE_MODE (valtype)));
}
}
 
 
/* Worker function for TARGET_LIBCALL_VALUE.  */
/* Worker function for TARGET_LIBCALL_VALUE.  */
 
 
static rtx
static rtx
sh_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
sh_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
{
{
  return gen_rtx_REG (mode, BASE_RETURN_VALUE_REG (mode));
  return gen_rtx_REG (mode, BASE_RETURN_VALUE_REG (mode));
}
}
 
 
/* Worker function for FUNCTION_VALUE_REGNO_P.  */
/* Worker function for FUNCTION_VALUE_REGNO_P.  */
 
 
bool
bool
sh_function_value_regno_p (const unsigned int regno)
sh_function_value_regno_p (const unsigned int regno)
{
{
  return ((regno) == FIRST_RET_REG
  return ((regno) == FIRST_RET_REG
          || (TARGET_SH2E && (regno) == FIRST_FP_RET_REG)
          || (TARGET_SH2E && (regno) == FIRST_FP_RET_REG)
          || (TARGET_SHMEDIA_FPU && (regno) == FIRST_FP_RET_REG));
          || (TARGET_SHMEDIA_FPU && (regno) == FIRST_FP_RET_REG));
}
}
 
 
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
 
 
static bool
static bool
sh_return_in_memory (const_tree type, const_tree fndecl)
sh_return_in_memory (const_tree type, const_tree fndecl)
{
{
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      if (TYPE_MODE (type) == BLKmode)
      if (TYPE_MODE (type) == BLKmode)
        return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)) > 8;
        return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)) > 8;
      else
      else
        return GET_MODE_SIZE (TYPE_MODE (type)) > 8;
        return GET_MODE_SIZE (TYPE_MODE (type)) > 8;
    }
    }
  else
  else
    {
    {
      return (TYPE_MODE (type) == BLKmode
      return (TYPE_MODE (type) == BLKmode
              || ((TARGET_HITACHI || sh_attr_renesas_p (fndecl))
              || ((TARGET_HITACHI || sh_attr_renesas_p (fndecl))
                  && TREE_CODE (type) == RECORD_TYPE));
                  && TREE_CODE (type) == RECORD_TYPE));
    }
    }
}
}
 
 
/* We actually emit the code in sh_expand_prologue.  We used to use
/* 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
   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
   doesn't when inlining, when functions are deferred and then emitted
   later.  Fortunately, we already have two flags that are part of struct
   later.  Fortunately, we already have two flags that are part of struct
   function that tell if a function uses varargs or stdarg.  */
   function that tell if a function uses varargs or stdarg.  */
static void
static void
sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
                           enum machine_mode mode,
                           enum machine_mode mode,
                           tree type,
                           tree type,
                           int *pretend_arg_size,
                           int *pretend_arg_size,
                           int second_time ATTRIBUTE_UNUSED)
                           int second_time ATTRIBUTE_UNUSED)
{
{
  gcc_assert (cfun->stdarg);
  gcc_assert (cfun->stdarg);
  if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
  if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
    {
    {
      int named_parm_regs, anon_parm_regs;
      int named_parm_regs, anon_parm_regs;
 
 
      named_parm_regs = (ROUND_REG (*ca, mode)
      named_parm_regs = (ROUND_REG (*ca, mode)
                         + (mode == BLKmode
                         + (mode == BLKmode
                            ? ROUND_ADVANCE (int_size_in_bytes (type))
                            ? ROUND_ADVANCE (int_size_in_bytes (type))
                            : ROUND_ADVANCE (GET_MODE_SIZE (mode))));
                            : ROUND_ADVANCE (GET_MODE_SIZE (mode))));
      anon_parm_regs = NPARM_REGS (SImode) - named_parm_regs;
      anon_parm_regs = NPARM_REGS (SImode) - named_parm_regs;
      if (anon_parm_regs > 0)
      if (anon_parm_regs > 0)
        *pretend_arg_size = anon_parm_regs * 4;
        *pretend_arg_size = anon_parm_regs * 4;
    }
    }
}
}
 
 
static bool
static bool
sh_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
sh_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
{
{
  return TARGET_SH5;
  return TARGET_SH5;
}
}
 
 
static bool
static bool
sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *ca)
sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *ca)
{
{
  return ! (TARGET_HITACHI || ca->renesas_abi) && ! TARGET_SH5;
  return ! (TARGET_HITACHI || ca->renesas_abi) && ! TARGET_SH5;
}
}
 
 
 
 
/* Define the offset between two registers, one to be eliminated, and
/* Define the offset between two registers, one to be eliminated, and
   the other its replacement, at the start of a routine.  */
   the other its replacement, at the start of a routine.  */
 
 
int
int
initial_elimination_offset (int from, int to)
initial_elimination_offset (int from, int to)
{
{
  int regs_saved;
  int regs_saved;
  int regs_saved_rounding = 0;
  int regs_saved_rounding = 0;
  int total_saved_regs_space;
  int total_saved_regs_space;
  int total_auto_space;
  int total_auto_space;
  int save_flags = target_flags;
  int save_flags = target_flags;
  int copy_flags;
  int copy_flags;
  HARD_REG_SET live_regs_mask;
  HARD_REG_SET live_regs_mask;
 
 
  shmedia_space_reserved_for_target_registers = false;
  shmedia_space_reserved_for_target_registers = false;
  regs_saved = calc_live_regs (&live_regs_mask);
  regs_saved = calc_live_regs (&live_regs_mask);
  regs_saved += SHMEDIA_REGS_STACK_ADJUST ();
  regs_saved += SHMEDIA_REGS_STACK_ADJUST ();
 
 
  if (shmedia_reserve_space_for_target_registers_p (regs_saved, &live_regs_mask))
  if (shmedia_reserve_space_for_target_registers_p (regs_saved, &live_regs_mask))
    {
    {
      shmedia_space_reserved_for_target_registers = true;
      shmedia_space_reserved_for_target_registers = true;
      regs_saved += shmedia_target_regs_stack_adjust (&live_regs_mask);
      regs_saved += shmedia_target_regs_stack_adjust (&live_regs_mask);
    }
    }
 
 
  if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT))
  if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT))
    regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
    regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
                           - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT));
                           - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT));
 
 
  total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
  total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
  copy_flags = target_flags;
  copy_flags = target_flags;
  target_flags = save_flags;
  target_flags = save_flags;
 
 
  total_saved_regs_space = regs_saved + regs_saved_rounding;
  total_saved_regs_space = regs_saved + regs_saved_rounding;
 
 
  if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
  if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
    return total_saved_regs_space + total_auto_space
    return total_saved_regs_space + total_auto_space
      + crtl->args.info.byref_regs * 8;
      + crtl->args.info.byref_regs * 8;
 
 
  if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return total_saved_regs_space + total_auto_space
    return total_saved_regs_space + total_auto_space
      + crtl->args.info.byref_regs * 8;
      + crtl->args.info.byref_regs * 8;
 
 
  /* Initial gap between fp and sp is 0.  */
  /* Initial gap between fp and sp is 0.  */
  if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return 0;
    return 0;
 
 
  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return rounded_frame_size (0);
    return rounded_frame_size (0);
 
 
  if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
  if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
    return rounded_frame_size (0);
    return rounded_frame_size (0);
 
 
  gcc_assert (from == RETURN_ADDRESS_POINTER_REGNUM
  gcc_assert (from == RETURN_ADDRESS_POINTER_REGNUM
              && (to == HARD_FRAME_POINTER_REGNUM
              && (to == HARD_FRAME_POINTER_REGNUM
                  || to == STACK_POINTER_REGNUM));
                  || to == STACK_POINTER_REGNUM));
  if (TARGET_SH5)
  if (TARGET_SH5)
    {
    {
      int n = total_saved_regs_space;
      int n = total_saved_regs_space;
      int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
      int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
      save_schedule schedule;
      save_schedule schedule;
      save_entry *entry;
      save_entry *entry;
 
 
      n += total_auto_space;
      n += total_auto_space;
 
 
      /* If it wasn't saved, there's not much we can do.  */
      /* If it wasn't saved, there's not much we can do.  */
      if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
      if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
        return n;
        return n;
 
 
      target_flags = copy_flags;
      target_flags = copy_flags;
 
 
      sh5_schedule_saves (&live_regs_mask, &schedule, n);
      sh5_schedule_saves (&live_regs_mask, &schedule, n);
      for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
      for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
        if (entry->reg == pr_reg)
        if (entry->reg == pr_reg)
          {
          {
            target_flags = save_flags;
            target_flags = save_flags;
            return entry->offset;
            return entry->offset;
          }
          }
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
  else
  else
    return total_auto_space;
    return total_auto_space;
}
}
 
 
/* Parse the -mfixed-range= option string.  */
/* Parse the -mfixed-range= option string.  */
void
void
sh_fix_range (const char *const_str)
sh_fix_range (const char *const_str)
{
{
  int i, first, last;
  int i, first, last;
  char *str, *dash, *comma;
  char *str, *dash, *comma;
 
 
  /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
  /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
     REG2 are either register names or register numbers.  The effect
     REG2 are either register names or register numbers.  The effect
     of this option is to mark the registers in the range from REG1 to
     of this option is to mark the registers in the range from REG1 to
     REG2 as ``fixed'' so they won't be used by the compiler.  */
     REG2 as ``fixed'' so they won't be used by the compiler.  */
 
 
  i = strlen (const_str);
  i = strlen (const_str);
  str = (char *) alloca (i + 1);
  str = (char *) alloca (i + 1);
  memcpy (str, const_str, i + 1);
  memcpy (str, const_str, i + 1);
 
 
  while (1)
  while (1)
    {
    {
      dash = strchr (str, '-');
      dash = strchr (str, '-');
      if (!dash)
      if (!dash)
        {
        {
          warning (0, "value of -mfixed-range must have form REG1-REG2");
          warning (0, "value of -mfixed-range must have form REG1-REG2");
          return;
          return;
        }
        }
      *dash = '\0';
      *dash = '\0';
      comma = strchr (dash + 1, ',');
      comma = strchr (dash + 1, ',');
      if (comma)
      if (comma)
        *comma = '\0';
        *comma = '\0';
 
 
      first = decode_reg_name (str);
      first = decode_reg_name (str);
      if (first < 0)
      if (first < 0)
        {
        {
          warning (0, "unknown register name: %s", str);
          warning (0, "unknown register name: %s", str);
          return;
          return;
        }
        }
 
 
      last = decode_reg_name (dash + 1);
      last = decode_reg_name (dash + 1);
      if (last < 0)
      if (last < 0)
        {
        {
          warning (0, "unknown register name: %s", dash + 1);
          warning (0, "unknown register name: %s", dash + 1);
          return;
          return;
        }
        }
 
 
      *dash = '-';
      *dash = '-';
 
 
      if (first > last)
      if (first > last)
        {
        {
          warning (0, "%s-%s is an empty range", str, dash + 1);
          warning (0, "%s-%s is an empty range", str, dash + 1);
          return;
          return;
        }
        }
 
 
      for (i = first; i <= last; ++i)
      for (i = first; i <= last; ++i)
        fixed_regs[i] = call_used_regs[i] = 1;
        fixed_regs[i] = call_used_regs[i] = 1;
 
 
      if (!comma)
      if (!comma)
        break;
        break;
 
 
      *comma = ',';
      *comma = ',';
      str = comma + 1;
      str = comma + 1;
    }
    }
}
}


/* Insert any deferred function attributes from earlier pragmas.  */
/* Insert any deferred function attributes from earlier pragmas.  */
static void
static void
sh_insert_attributes (tree node, tree *attributes)
sh_insert_attributes (tree node, tree *attributes)
{
{
  tree attrs;
  tree attrs;
 
 
  if (TREE_CODE (node) != FUNCTION_DECL)
  if (TREE_CODE (node) != FUNCTION_DECL)
    return;
    return;
 
 
  /* We are only interested in fields.  */
  /* We are only interested in fields.  */
  if (!DECL_P (node))
  if (!DECL_P (node))
    return;
    return;
 
 
  /* Append the attributes to the deferred attributes.  */
  /* Append the attributes to the deferred attributes.  */
  *sh_deferred_function_attributes_tail = *attributes;
  *sh_deferred_function_attributes_tail = *attributes;
  attrs = sh_deferred_function_attributes;
  attrs = sh_deferred_function_attributes;
  if (!attrs)
  if (!attrs)
    return;
    return;
 
 
  /* Some attributes imply or require the interrupt attribute.  */
  /* Some attributes imply or require the interrupt attribute.  */
  if (!lookup_attribute ("interrupt_handler", attrs)
  if (!lookup_attribute ("interrupt_handler", attrs)
      && !lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (node)))
      && !lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (node)))
    {
    {
      /* If we have a trapa_handler, but no interrupt_handler attribute,
      /* If we have a trapa_handler, but no interrupt_handler attribute,
         insert an interrupt_handler attribute.  */
         insert an interrupt_handler attribute.  */
      if (lookup_attribute ("trapa_handler", attrs) != NULL_TREE)
      if (lookup_attribute ("trapa_handler", attrs) != NULL_TREE)
        /* We can't use sh_pr_interrupt here because that's not in the
        /* We can't use sh_pr_interrupt here because that's not in the
           java frontend.  */
           java frontend.  */
        attrs
        attrs
          = tree_cons (get_identifier("interrupt_handler"), NULL_TREE, attrs);
          = tree_cons (get_identifier("interrupt_handler"), NULL_TREE, attrs);
      /* However, for sp_switch, trap_exit, nosave_low_regs and resbank,
      /* However, for sp_switch, trap_exit, nosave_low_regs and resbank,
         if the interrupt attribute is missing, we ignore the attribute
         if the interrupt attribute is missing, we ignore the attribute
         and warn.  */
         and warn.  */
      else if (lookup_attribute ("sp_switch", attrs)
      else if (lookup_attribute ("sp_switch", attrs)
               || lookup_attribute ("trap_exit", attrs)
               || lookup_attribute ("trap_exit", attrs)
               || lookup_attribute ("nosave_low_regs", attrs)
               || lookup_attribute ("nosave_low_regs", attrs)
               || lookup_attribute ("resbank", attrs))
               || lookup_attribute ("resbank", attrs))
        {
        {
          tree *tail;
          tree *tail;
 
 
          for (tail = attributes; attrs; attrs = TREE_CHAIN (attrs))
          for (tail = attributes; attrs; attrs = TREE_CHAIN (attrs))
            {
            {
              if (is_attribute_p ("sp_switch", TREE_PURPOSE (attrs))
              if (is_attribute_p ("sp_switch", TREE_PURPOSE (attrs))
                  || is_attribute_p ("trap_exit", TREE_PURPOSE (attrs))
                  || is_attribute_p ("trap_exit", TREE_PURPOSE (attrs))
                  || is_attribute_p ("nosave_low_regs", TREE_PURPOSE (attrs))
                  || is_attribute_p ("nosave_low_regs", TREE_PURPOSE (attrs))
                  || is_attribute_p ("resbank", TREE_PURPOSE (attrs)))
                  || is_attribute_p ("resbank", TREE_PURPOSE (attrs)))
                warning (OPT_Wattributes,
                warning (OPT_Wattributes,
                         "%qE attribute only applies to interrupt functions",
                         "%qE attribute only applies to interrupt functions",
                         TREE_PURPOSE (attrs));
                         TREE_PURPOSE (attrs));
              else
              else
                {
                {
                  *tail = tree_cons (TREE_PURPOSE (attrs), NULL_TREE,
                  *tail = tree_cons (TREE_PURPOSE (attrs), NULL_TREE,
                                     NULL_TREE);
                                     NULL_TREE);
                  tail = &TREE_CHAIN (*tail);
                  tail = &TREE_CHAIN (*tail);
                }
                }
            }
            }
          attrs = *attributes;
          attrs = *attributes;
        }
        }
    }
    }
 
 
  /* Install the processed list.  */
  /* Install the processed list.  */
  *attributes = attrs;
  *attributes = attrs;
 
 
  /* Clear deferred attributes.  */
  /* Clear deferred attributes.  */
  sh_deferred_function_attributes = NULL_TREE;
  sh_deferred_function_attributes = NULL_TREE;
  sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
  sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
 
 
  return;
  return;
}
}
 
 
/* Supported attributes:
/* Supported attributes:
 
 
   interrupt_handler -- specifies this function is an interrupt handler.
   interrupt_handler -- specifies this function is an interrupt handler.
 
 
   trapa_handler - like above, but don't save all registers.
   trapa_handler - like above, but don't save all registers.
 
 
   sp_switch -- specifies an alternate stack for an interrupt handler
   sp_switch -- specifies an alternate stack for an interrupt handler
   to run on.
   to run on.
 
 
   trap_exit -- use a trapa to exit an interrupt function instead of
   trap_exit -- use a trapa to exit an interrupt function instead of
   an rte instruction.
   an rte instruction.
 
 
   nosave_low_regs - don't save r0..r7 in an interrupt handler.
   nosave_low_regs - don't save r0..r7 in an interrupt handler.
     This is useful on the SH3 and upwards,
     This is useful on the SH3 and upwards,
     which has a separate set of low regs for User and Supervisor modes.
     which has a separate set of low regs for User and Supervisor modes.
     This should only be used for the lowest level of interrupts.  Higher levels
     This should only be used for the lowest level of interrupts.  Higher levels
     of interrupts must save the registers in case they themselves are
     of interrupts must save the registers in case they themselves are
     interrupted.
     interrupted.
 
 
   renesas -- use Renesas calling/layout conventions (functions and
   renesas -- use Renesas calling/layout conventions (functions and
   structures).
   structures).
 
 
   resbank -- In case of an ISR, use a register bank to save registers
   resbank -- In case of an ISR, use a register bank to save registers
   R0-R14, MACH, MACL, GBR and PR.  This is useful only on SH2A targets.
   R0-R14, MACH, MACL, GBR and PR.  This is useful only on SH2A targets.
*/
*/
 
 
/* Handle a 'resbank' attribute.  */
/* Handle a 'resbank' attribute.  */
static tree
static tree
sh_handle_resbank_handler_attribute (tree * node, tree name,
sh_handle_resbank_handler_attribute (tree * node, tree name,
                                     tree args ATTRIBUTE_UNUSED,
                                     tree args ATTRIBUTE_UNUSED,
                                     int flags ATTRIBUTE_UNUSED,
                                     int flags ATTRIBUTE_UNUSED,
                                     bool * no_add_attrs)
                                     bool * no_add_attrs)
{
{
  if (!TARGET_SH2A)
  if (!TARGET_SH2A)
    {
    {
      warning (OPT_Wattributes, "%qE attribute is supported only for SH2A",
      warning (OPT_Wattributes, "%qE attribute is supported only for SH2A",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  if (TREE_CODE (*node) != FUNCTION_DECL)
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
    {
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* Handle an "interrupt_handler" attribute; arguments as in
/* Handle an "interrupt_handler" attribute; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
static tree
static tree
sh_handle_interrupt_handler_attribute (tree *node, tree name,
sh_handle_interrupt_handler_attribute (tree *node, tree name,
                                       tree args ATTRIBUTE_UNUSED,
                                       tree args ATTRIBUTE_UNUSED,
                                       int flags ATTRIBUTE_UNUSED,
                                       int flags ATTRIBUTE_UNUSED,
                                       bool *no_add_attrs)
                                       bool *no_add_attrs)
{
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
    {
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  else if (TARGET_SHCOMPACT)
  else if (TARGET_SHCOMPACT)
    {
    {
      error ("attribute interrupt_handler is not compatible with -m5-compact");
      error ("attribute interrupt_handler is not compatible with -m5-compact");
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* Handle an 'function_vector' attribute; arguments as in
/* Handle an 'function_vector' attribute; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
static tree
static tree
sh2a_handle_function_vector_handler_attribute (tree * node, tree name,
sh2a_handle_function_vector_handler_attribute (tree * node, tree name,
                                               tree args ATTRIBUTE_UNUSED,
                                               tree args ATTRIBUTE_UNUSED,
                                               int flags ATTRIBUTE_UNUSED,
                                               int flags ATTRIBUTE_UNUSED,
                                               bool * no_add_attrs)
                                               bool * no_add_attrs)
{
{
  if (!TARGET_SH2A)
  if (!TARGET_SH2A)
    {
    {
      warning (OPT_Wattributes, "%qE attribute only applies to SH2A",
      warning (OPT_Wattributes, "%qE attribute only applies to SH2A",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  else if (TREE_CODE (*node) != FUNCTION_DECL)
  else if (TREE_CODE (*node) != FUNCTION_DECL)
    {
    {
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
  else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
    {
    {
      /* The argument must be a constant integer.  */
      /* The argument must be a constant integer.  */
      warning (OPT_Wattributes,
      warning (OPT_Wattributes,
               "%qE attribute argument not an integer constant",
               "%qE attribute argument not an integer constant",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  else if (TREE_INT_CST_LOW (TREE_VALUE (args)) > 255)
  else if (TREE_INT_CST_LOW (TREE_VALUE (args)) > 255)
    {
    {
      /* The argument value must be between 0 to 255.  */
      /* The argument value must be between 0 to 255.  */
      warning (OPT_Wattributes,
      warning (OPT_Wattributes,
               "%qE attribute argument should be between 0 to 255",
               "%qE attribute argument should be between 0 to 255",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* Returns 1 if current function has been assigned the attribute
/* Returns 1 if current function has been assigned the attribute
   'function_vector'.  */
   'function_vector'.  */
int
int
sh2a_is_function_vector_call (rtx x)
sh2a_is_function_vector_call (rtx x)
{
{
  if (GET_CODE (x) == SYMBOL_REF
  if (GET_CODE (x) == SYMBOL_REF
      && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
      && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
    {
    {
      tree tr = SYMBOL_REF_DECL (x);
      tree tr = SYMBOL_REF_DECL (x);
 
 
      if (sh2a_function_vector_p (tr))
      if (sh2a_function_vector_p (tr))
        return 1;
        return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Returns the function vector number, if the the attribute
/* Returns the function vector number, if the the attribute
   'function_vector' is assigned, otherwise returns zero.  */
   'function_vector' is assigned, otherwise returns zero.  */
int
int
sh2a_get_function_vector_number (rtx x)
sh2a_get_function_vector_number (rtx x)
{
{
  int num;
  int num;
  tree list, t;
  tree list, t;
 
 
  if ((GET_CODE (x) == SYMBOL_REF)
  if ((GET_CODE (x) == SYMBOL_REF)
      && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
      && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
    {
    {
      t = SYMBOL_REF_DECL (x);
      t = SYMBOL_REF_DECL (x);
 
 
      if (TREE_CODE (t) != FUNCTION_DECL)
      if (TREE_CODE (t) != FUNCTION_DECL)
        return 0;
        return 0;
 
 
      list = SH_ATTRIBUTES (t);
      list = SH_ATTRIBUTES (t);
      while (list)
      while (list)
        {
        {
          if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
          if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
            {
            {
              num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
              num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
              return num;
              return num;
            }
            }
 
 
          list = TREE_CHAIN (list);
          list = TREE_CHAIN (list);
        }
        }
 
 
      return 0;
      return 0;
    }
    }
  else
  else
    return 0;
    return 0;
}
}
 
 
/* Handle an "sp_switch" attribute; arguments as in
/* Handle an "sp_switch" attribute; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
static tree
static tree
sh_handle_sp_switch_attribute (tree *node, tree name, tree args,
sh_handle_sp_switch_attribute (tree *node, tree name, tree args,
                               int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
                               int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
    {
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
    {
    {
      /* The argument must be a constant string.  */
      /* The argument must be a constant string.  */
      warning (OPT_Wattributes, "%qE attribute argument not a string constant",
      warning (OPT_Wattributes, "%qE attribute argument not a string constant",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* Handle an "trap_exit" attribute; arguments as in
/* Handle an "trap_exit" attribute; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
static tree
static tree
sh_handle_trap_exit_attribute (tree *node, tree name, tree args,
sh_handle_trap_exit_attribute (tree *node, tree name, tree args,
                               int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
                               int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
{
  if (TREE_CODE (*node) != FUNCTION_DECL)
  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
    {
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
      warning (OPT_Wattributes, "%qE attribute only applies to functions",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  /* The argument specifies a trap number to be used in a trapa instruction
  /* The argument specifies a trap number to be used in a trapa instruction
     at function exit (instead of an rte instruction).  */
     at function exit (instead of an rte instruction).  */
  else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
  else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
    {
    {
      /* The argument must be a constant integer.  */
      /* The argument must be a constant integer.  */
      warning (OPT_Wattributes, "%qE attribute argument not an "
      warning (OPT_Wattributes, "%qE attribute argument not an "
               "integer constant", name);
               "integer constant", name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
static tree
static tree
sh_handle_renesas_attribute (tree *node ATTRIBUTE_UNUSED,
sh_handle_renesas_attribute (tree *node ATTRIBUTE_UNUSED,
                             tree name ATTRIBUTE_UNUSED,
                             tree name ATTRIBUTE_UNUSED,
                             tree args ATTRIBUTE_UNUSED,
                             tree args ATTRIBUTE_UNUSED,
                             int flags ATTRIBUTE_UNUSED,
                             int flags ATTRIBUTE_UNUSED,
                             bool *no_add_attrs ATTRIBUTE_UNUSED)
                             bool *no_add_attrs ATTRIBUTE_UNUSED)
{
{
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* True if __attribute__((renesas)) or -mrenesas.  */
/* True if __attribute__((renesas)) or -mrenesas.  */
int
int
sh_attr_renesas_p (const_tree td)
sh_attr_renesas_p (const_tree td)
{
{
  if (TARGET_HITACHI)
  if (TARGET_HITACHI)
    return 1;
    return 1;
  if (td == 0)
  if (td == 0)
    return 0;
    return 0;
  if (DECL_P (td))
  if (DECL_P (td))
    td = TREE_TYPE (td);
    td = TREE_TYPE (td);
  if (td == error_mark_node)
  if (td == error_mark_node)
    return 0;
    return 0;
  return (lookup_attribute ("renesas", TYPE_ATTRIBUTES (td))
  return (lookup_attribute ("renesas", TYPE_ATTRIBUTES (td))
          != NULL_TREE);
          != NULL_TREE);
}
}
 
 
/* True if __attribute__((renesas)) or -mrenesas, for the current
/* True if __attribute__((renesas)) or -mrenesas, for the current
   function.  */
   function.  */
int
int
sh_cfun_attr_renesas_p (void)
sh_cfun_attr_renesas_p (void)
{
{
  return sh_attr_renesas_p (current_function_decl);
  return sh_attr_renesas_p (current_function_decl);
}
}
 
 
int
int
sh_cfun_interrupt_handler_p (void)
sh_cfun_interrupt_handler_p (void)
{
{
  return (lookup_attribute ("interrupt_handler",
  return (lookup_attribute ("interrupt_handler",
                            DECL_ATTRIBUTES (current_function_decl))
                            DECL_ATTRIBUTES (current_function_decl))
          != NULL_TREE);
          != NULL_TREE);
}
}
 
 
/* Returns 1 if FUNC has been assigned the attribute
/* Returns 1 if FUNC has been assigned the attribute
   "function_vector".  */
   "function_vector".  */
int
int
sh2a_function_vector_p (tree func)
sh2a_function_vector_p (tree func)
{
{
  tree list;
  tree list;
  if (TREE_CODE (func) != FUNCTION_DECL)
  if (TREE_CODE (func) != FUNCTION_DECL)
    return 0;
    return 0;
 
 
  list = SH_ATTRIBUTES (func);
  list = SH_ATTRIBUTES (func);
  while (list)
  while (list)
    {
    {
      if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
      if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
        return 1;
        return 1;
 
 
      list = TREE_CHAIN (list);
      list = TREE_CHAIN (list);
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Returns TRUE if given tree has the "resbank" attribute.  */
/* Returns TRUE if given tree has the "resbank" attribute.  */
 
 
int
int
sh_cfun_resbank_handler_p (void)
sh_cfun_resbank_handler_p (void)
{
{
  return ((lookup_attribute ("resbank",
  return ((lookup_attribute ("resbank",
                             DECL_ATTRIBUTES (current_function_decl))
                             DECL_ATTRIBUTES (current_function_decl))
           != NULL_TREE)
           != NULL_TREE)
          && (lookup_attribute ("interrupt_handler",
          && (lookup_attribute ("interrupt_handler",
                                DECL_ATTRIBUTES (current_function_decl))
                                DECL_ATTRIBUTES (current_function_decl))
              != NULL_TREE) && TARGET_SH2A);
              != NULL_TREE) && TARGET_SH2A);
}
}
 
 
/* Implement TARGET_CHECK_PCH_TARGET_FLAGS.  */
/* Implement TARGET_CHECK_PCH_TARGET_FLAGS.  */
 
 
static const char *
static const char *
sh_check_pch_target_flags (int old_flags)
sh_check_pch_target_flags (int old_flags)
{
{
  if ((old_flags ^ target_flags) & (MASK_SH1 | MASK_SH2 | MASK_SH3
  if ((old_flags ^ target_flags) & (MASK_SH1 | MASK_SH2 | MASK_SH3
                                    | MASK_SH_E | MASK_HARD_SH4
                                    | MASK_SH_E | MASK_HARD_SH4
                                    | MASK_FPU_SINGLE | MASK_SH4))
                                    | MASK_FPU_SINGLE | MASK_SH4))
    return _("created and used with different architectures / ABIs");
    return _("created and used with different architectures / ABIs");
  if ((old_flags ^ target_flags) & MASK_HITACHI)
  if ((old_flags ^ target_flags) & MASK_HITACHI)
    return _("created and used with different ABIs");
    return _("created and used with different ABIs");
  if ((old_flags ^ target_flags) & MASK_LITTLE_ENDIAN)
  if ((old_flags ^ target_flags) & MASK_LITTLE_ENDIAN)
    return _("created and used with different endianness");
    return _("created and used with different endianness");
  return NULL;
  return NULL;
}
}


/* Predicates used by the templates.  */
/* Predicates used by the templates.  */
 
 
/* Returns 1 if OP is MACL, MACH or PR.  The input must be a REG rtx.
/* Returns 1 if OP is MACL, MACH or PR.  The input must be a REG rtx.
   Used only in general_movsrc_operand.  */
   Used only in general_movsrc_operand.  */
 
 
int
int
system_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
system_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  switch (REGNO (op))
  switch (REGNO (op))
    {
    {
    case PR_REG:
    case PR_REG:
    case MACL_REG:
    case MACL_REG:
    case MACH_REG:
    case MACH_REG:
      return 1;
      return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Nonzero if OP is a floating point value with value 0.0.  */
/* Nonzero if OP is a floating point value with value 0.0.  */
 
 
int
int
fp_zero_operand (rtx op)
fp_zero_operand (rtx op)
{
{
  REAL_VALUE_TYPE r;
  REAL_VALUE_TYPE r;
 
 
  if (GET_MODE (op) != SFmode)
  if (GET_MODE (op) != SFmode)
    return 0;
    return 0;
 
 
  REAL_VALUE_FROM_CONST_DOUBLE (r, op);
  REAL_VALUE_FROM_CONST_DOUBLE (r, op);
  return REAL_VALUES_EQUAL (r, dconst0) && ! REAL_VALUE_MINUS_ZERO (r);
  return REAL_VALUES_EQUAL (r, dconst0) && ! REAL_VALUE_MINUS_ZERO (r);
}
}
 
 
/* Nonzero if OP is a floating point value with value 1.0.  */
/* Nonzero if OP is a floating point value with value 1.0.  */
 
 
int
int
fp_one_operand (rtx op)
fp_one_operand (rtx op)
{
{
  REAL_VALUE_TYPE r;
  REAL_VALUE_TYPE r;
 
 
  if (GET_MODE (op) != SFmode)
  if (GET_MODE (op) != SFmode)
    return 0;
    return 0;
 
 
  REAL_VALUE_FROM_CONST_DOUBLE (r, op);
  REAL_VALUE_FROM_CONST_DOUBLE (r, op);
  return REAL_VALUES_EQUAL (r, dconst1);
  return REAL_VALUES_EQUAL (r, dconst1);
}
}
 
 
/* In general mode switching is used.  If we are
/* In general mode switching is used.  If we are
   compiling without -mfmovd, movsf_ie isn't taken into account for
   compiling without -mfmovd, movsf_ie isn't taken into account for
   mode switching.  We could check in machine_dependent_reorg for
   mode switching.  We could check in machine_dependent_reorg for
   cases where we know we are in single precision mode, but there is
   cases where we know we are in single precision mode, but there is
   interface to find that out during reload, so we must avoid
   interface to find that out during reload, so we must avoid
   choosing an fldi alternative during reload and thus failing to
   choosing an fldi alternative during reload and thus failing to
   allocate a scratch register for the constant loading.  */
   allocate a scratch register for the constant loading.  */
int
int
fldi_ok (void)
fldi_ok (void)
{
{
  return 1;
  return 1;
}
}
 
 
int
int
tertiary_reload_operand (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);
  enum rtx_code code = GET_CODE (op);
  return code == MEM || (TARGET_SH4 && code == CONST_DOUBLE);
  return code == MEM || (TARGET_SH4 && code == CONST_DOUBLE);
}
}
 
 
/* Return the TLS type for TLS symbols, 0 for otherwise.  */
/* Return the TLS type for TLS symbols, 0 for otherwise.  */
enum tls_model
enum tls_model
tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  if (GET_CODE (op) != SYMBOL_REF)
  if (GET_CODE (op) != SYMBOL_REF)
    return TLS_MODEL_NONE;
    return TLS_MODEL_NONE;
  return SYMBOL_REF_TLS_MODEL (op);
  return SYMBOL_REF_TLS_MODEL (op);
}
}


/* Return the destination address of a branch.  */
/* Return the destination address of a branch.  */
 
 
static int
static int
branch_dest (rtx branch)
branch_dest (rtx branch)
{
{
  rtx dest = SET_SRC (PATTERN (branch));
  rtx dest = SET_SRC (PATTERN (branch));
  int dest_uid;
  int dest_uid;
 
 
  if (GET_CODE (dest) == IF_THEN_ELSE)
  if (GET_CODE (dest) == IF_THEN_ELSE)
    dest = XEXP (dest, 1);
    dest = XEXP (dest, 1);
  dest = XEXP (dest, 0);
  dest = XEXP (dest, 0);
  dest_uid = INSN_UID (dest);
  dest_uid = INSN_UID (dest);
  return INSN_ADDRESSES (dest_uid);
  return INSN_ADDRESSES (dest_uid);
}
}


/* Return nonzero if REG is not used after INSN.
/* Return nonzero if REG is not used after INSN.
   We assume REG is a reload reg, and therefore does
   We assume REG is a reload reg, and therefore does
   not live past labels.  It may live past calls or jumps though.  */
   not live past labels.  It may live past calls or jumps though.  */
int
int
reg_unused_after (rtx reg, rtx insn)
reg_unused_after (rtx reg, rtx insn)
{
{
  enum rtx_code code;
  enum rtx_code code;
  rtx set;
  rtx set;
 
 
  /* If the reg is set by this instruction, then it is safe for our
  /* If the reg is set by this instruction, then it is safe for our
     case.  Disregard the case where this is a store to memory, since
     case.  Disregard the case where this is a store to memory, since
     we are checking a register used in the store address.  */
     we are checking a register used in the store address.  */
  set = single_set (insn);
  set = single_set (insn);
  if (set && !MEM_P (SET_DEST (set))
  if (set && !MEM_P (SET_DEST (set))
      && reg_overlap_mentioned_p (reg, SET_DEST (set)))
      && reg_overlap_mentioned_p (reg, SET_DEST (set)))
    return 1;
    return 1;
 
 
  while ((insn = NEXT_INSN (insn)))
  while ((insn = NEXT_INSN (insn)))
    {
    {
      rtx set;
      rtx set;
      if (!INSN_P (insn))
      if (!INSN_P (insn))
        continue;
        continue;
 
 
      code = GET_CODE (insn);
      code = GET_CODE (insn);
 
 
#if 0
#if 0
      /* If this is a label that existed before reload, then the register
      /* If this is a label that existed before reload, then the register
         if dead here.  However, if this is a label added by reorg, then
         if dead here.  However, if this is a label added by reorg, then
         the register may still be live here.  We can't tell the difference,
         the register may still be live here.  We can't tell the difference,
         so we just ignore labels completely.  */
         so we just ignore labels completely.  */
      if (code == CODE_LABEL)
      if (code == CODE_LABEL)
        return 1;
        return 1;
      /* else */
      /* else */
#endif
#endif
 
 
      if (code == JUMP_INSN)
      if (code == JUMP_INSN)
        return 0;
        return 0;
 
 
      /* If this is a sequence, we must handle them all at once.
      /* If this is a sequence, we must handle them all at once.
         We could have for instance a call that sets the target register,
         We could have for instance a call that sets the target register,
         and an insn in a delay slot that uses the register.  In this case,
         and an insn in a delay slot that uses the register.  In this case,
         we must return 0.  */
         we must return 0.  */
      else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
      else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
        {
        {
          int i;
          int i;
          int retval = 0;
          int retval = 0;
 
 
          for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
          for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
            {
            {
              rtx this_insn = XVECEXP (PATTERN (insn), 0, i);
              rtx this_insn = XVECEXP (PATTERN (insn), 0, i);
              rtx set = single_set (this_insn);
              rtx set = single_set (this_insn);
 
 
              if (CALL_P (this_insn))
              if (CALL_P (this_insn))
                code = CALL_INSN;
                code = CALL_INSN;
              else if (JUMP_P (this_insn))
              else if (JUMP_P (this_insn))
                {
                {
                  if (INSN_ANNULLED_BRANCH_P (this_insn))
                  if (INSN_ANNULLED_BRANCH_P (this_insn))
                    return 0;
                    return 0;
                  code = JUMP_INSN;
                  code = JUMP_INSN;
                }
                }
 
 
              if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
              if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
                return 0;
                return 0;
              if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
              if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
                {
                {
                  if (!MEM_P (SET_DEST (set)))
                  if (!MEM_P (SET_DEST (set)))
                    retval = 1;
                    retval = 1;
                  else
                  else
                    return 0;
                    return 0;
                }
                }
              if (set == 0
              if (set == 0
                  && reg_overlap_mentioned_p (reg, PATTERN (this_insn)))
                  && reg_overlap_mentioned_p (reg, PATTERN (this_insn)))
                return 0;
                return 0;
            }
            }
          if (retval == 1)
          if (retval == 1)
            return 1;
            return 1;
          else if (code == JUMP_INSN)
          else if (code == JUMP_INSN)
            return 0;
            return 0;
        }
        }
 
 
      set = single_set (insn);
      set = single_set (insn);
      if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
      if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
        return 0;
        return 0;
      if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
      if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
        return !MEM_P (SET_DEST (set));
        return !MEM_P (SET_DEST (set));
      if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
      if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
        return 0;
        return 0;
 
 
      if (code == CALL_INSN && call_really_used_regs[REGNO (reg)])
      if (code == CALL_INSN && call_really_used_regs[REGNO (reg)])
        return 1;
        return 1;
    }
    }
  return 1;
  return 1;
}
}


#include "ggc.h"
#include "ggc.h"
 
 
static GTY(()) rtx fpscr_rtx;
static GTY(()) rtx fpscr_rtx;
rtx
rtx
get_fpscr_rtx (void)
get_fpscr_rtx (void)
{
{
  if (! fpscr_rtx)
  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;
      REG_USERVAR_P (fpscr_rtx) = 1;
      mark_user_reg (fpscr_rtx);
      mark_user_reg (fpscr_rtx);
    }
    }
  if (! reload_completed || mdep_reorg_phase != SH_AFTER_MDEP_REORG)
  if (! reload_completed || mdep_reorg_phase != SH_AFTER_MDEP_REORG)
    mark_user_reg (fpscr_rtx);
    mark_user_reg (fpscr_rtx);
  return fpscr_rtx;
  return fpscr_rtx;
}
}
 
 
static GTY(()) tree fpscr_values;
static GTY(()) tree fpscr_values;
 
 
static void
static void
emit_fpu_switch (rtx scratch, int index)
emit_fpu_switch (rtx scratch, int index)
{
{
  rtx dst, src;
  rtx dst, src;
 
 
  if (fpscr_values == NULL)
  if (fpscr_values == NULL)
    {
    {
      tree t;
      tree t;
 
 
      t = build_index_type (integer_one_node);
      t = build_index_type (integer_one_node);
      t = build_array_type (integer_type_node, t);
      t = build_array_type (integer_type_node, t);
      t = build_decl (BUILTINS_LOCATION,
      t = build_decl (BUILTINS_LOCATION,
                      VAR_DECL, get_identifier ("__fpscr_values"), t);
                      VAR_DECL, get_identifier ("__fpscr_values"), t);
      DECL_ARTIFICIAL (t) = 1;
      DECL_ARTIFICIAL (t) = 1;
      DECL_IGNORED_P (t) = 1;
      DECL_IGNORED_P (t) = 1;
      DECL_EXTERNAL (t) = 1;
      DECL_EXTERNAL (t) = 1;
      TREE_STATIC (t) = 1;
      TREE_STATIC (t) = 1;
      TREE_PUBLIC (t) = 1;
      TREE_PUBLIC (t) = 1;
      TREE_USED (t) = 1;
      TREE_USED (t) = 1;
 
 
      fpscr_values = t;
      fpscr_values = t;
    }
    }
 
 
  src = DECL_RTL (fpscr_values);
  src = DECL_RTL (fpscr_values);
  if (!can_create_pseudo_p ())
  if (!can_create_pseudo_p ())
    {
    {
      emit_move_insn (scratch, XEXP (src, 0));
      emit_move_insn (scratch, XEXP (src, 0));
      if (index != 0)
      if (index != 0)
        emit_insn (gen_addsi3 (scratch, scratch, GEN_INT (index * 4)));
        emit_insn (gen_addsi3 (scratch, scratch, GEN_INT (index * 4)));
      src = adjust_automodify_address (src, PSImode, scratch, index * 4);
      src = adjust_automodify_address (src, PSImode, scratch, index * 4);
    }
    }
  else
  else
    src = adjust_address (src, PSImode, index * 4);
    src = adjust_address (src, PSImode, index * 4);
 
 
  dst = get_fpscr_rtx ();
  dst = get_fpscr_rtx ();
  emit_move_insn (dst, src);
  emit_move_insn (dst, src);
}
}
 
 
void
void
emit_sf_insn (rtx pat)
emit_sf_insn (rtx pat)
{
{
  emit_insn (pat);
  emit_insn (pat);
}
}
 
 
void
void
emit_df_insn (rtx pat)
emit_df_insn (rtx pat)
{
{
  emit_insn (pat);
  emit_insn (pat);
}
}
 
 
void
void
expand_sf_unop (rtx (*fun) (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 ()));
  emit_sf_insn ((*fun) (operands[0], operands[1], get_fpscr_rtx ()));
}
}
 
 
void
void
expand_sf_binop (rtx (*fun) (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],
  emit_sf_insn ((*fun) (operands[0], operands[1], operands[2],
                         get_fpscr_rtx ()));
                         get_fpscr_rtx ()));
}
}
 
 
void
void
expand_df_unop (rtx (*fun) (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 ()));
  emit_df_insn ((*fun) (operands[0], operands[1], get_fpscr_rtx ()));
}
}
 
 
void
void
expand_df_binop (rtx (*fun) (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],
  emit_df_insn ((*fun) (operands[0], operands[1], operands[2],
                        get_fpscr_rtx ()));
                        get_fpscr_rtx ()));
}
}


static rtx get_free_reg (HARD_REG_SET);
static rtx get_free_reg (HARD_REG_SET);
 
 
/* This function returns a register to use to load the address to load
/* 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
   the fpscr from.  Currently it always returns r1 or r7, but when we are
   able to use pseudo registers after combine, or have a better mechanism
   able to use pseudo registers after combine, or have a better mechanism
   for choosing a register, it should be done here.  */
   for choosing a register, it should be done here.  */
/* REGS_LIVE is the liveness information for the point for which we
/* REGS_LIVE is the liveness information for the point for which we
   need this allocation.  In some bare-bones exit blocks, r1 is live at the
   need this allocation.  In some bare-bones exit blocks, r1 is live at the
   start.  We can even have all of r0..r3 being live:
   start.  We can even have all of r0..r3 being live:
__complex__ long long f (double d) { if (d == 0) return 2; else return 3; }
__complex__ long long f (double d) { if (d == 0) return 2; else return 3; }
   INSN before which new insns are placed with will clobber the register
   INSN before which new insns are placed with will clobber the register
   we return.  If a basic block consists only of setting the return value
   we return.  If a basic block consists only of setting the return value
   register to a pseudo and using that register, the return value is not
   register to a pseudo and using that register, the return value is not
   live before or after this block, yet we we'll insert our insns right in
   live before or after this block, yet we we'll insert our insns right in
   the middle.  */
   the middle.  */
 
 
static rtx
static rtx
get_free_reg (HARD_REG_SET regs_live)
get_free_reg (HARD_REG_SET regs_live)
{
{
  if (! TEST_HARD_REG_BIT (regs_live, 1))
  if (! TEST_HARD_REG_BIT (regs_live, 1))
    return gen_rtx_REG (Pmode, 1);
    return gen_rtx_REG (Pmode, 1);
 
 
  /* Hard reg 1 is live; since this is a SMALL_REGISTER_CLASSES target,
  /* Hard reg 1 is live; since this is a SMALL_REGISTER_CLASSES target,
     there shouldn't be anything but a jump before the function end.  */
     there shouldn't be anything but a jump before the function end.  */
  gcc_assert (!TEST_HARD_REG_BIT (regs_live, 7));
  gcc_assert (!TEST_HARD_REG_BIT (regs_live, 7));
  return gen_rtx_REG (Pmode, 7);
  return gen_rtx_REG (Pmode, 7);
}
}
 
 
/* This function will set the fpscr from memory.
/* This function will set the fpscr from memory.
   MODE is the mode we are setting it to.  */
   MODE is the mode we are setting it to.  */
void
void
fpscr_set_from_mem (int mode, HARD_REG_SET regs_live)
fpscr_set_from_mem (int mode, HARD_REG_SET regs_live)
{
{
  enum attr_fp_mode fp_mode = (enum attr_fp_mode) mode;
  enum attr_fp_mode fp_mode = (enum attr_fp_mode) mode;
  enum attr_fp_mode norm_mode = ACTUAL_NORMAL_MODE (FP_MODE);
  enum attr_fp_mode norm_mode = ACTUAL_NORMAL_MODE (FP_MODE);
  rtx addr_reg;
  rtx addr_reg;
 
 
  addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
  addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
  emit_fpu_switch (addr_reg, fp_mode == norm_mode);
  emit_fpu_switch (addr_reg, fp_mode == norm_mode);
}
}
 
 
/* Is the given character a logical line separator for the assembler?  */
/* Is the given character a logical line separator for the assembler?  */
#ifndef IS_ASM_LOGICAL_LINE_SEPARATOR
#ifndef IS_ASM_LOGICAL_LINE_SEPARATOR
#define IS_ASM_LOGICAL_LINE_SEPARATOR(C, STR) ((C) == ';')
#define IS_ASM_LOGICAL_LINE_SEPARATOR(C, STR) ((C) == ';')
#endif
#endif
 
 
int
int
sh_insn_length_adjustment (rtx insn)
sh_insn_length_adjustment (rtx insn)
{
{
  /* Instructions with unfilled delay slots take up an extra two bytes for
  /* Instructions with unfilled delay slots take up an extra two bytes for
     the nop in the delay slot.  */
     the nop in the delay slot.  */
  if (((NONJUMP_INSN_P (insn)
  if (((NONJUMP_INSN_P (insn)
        && GET_CODE (PATTERN (insn)) != USE
        && GET_CODE (PATTERN (insn)) != USE
        && GET_CODE (PATTERN (insn)) != CLOBBER)
        && GET_CODE (PATTERN (insn)) != CLOBBER)
       || CALL_P (insn)
       || CALL_P (insn)
       || (JUMP_P (insn) && !JUMP_TABLE_DATA_P (insn)))
       || (JUMP_P (insn) && !JUMP_TABLE_DATA_P (insn)))
      && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (insn)))) != SEQUENCE
      && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (insn)))) != SEQUENCE
      && get_attr_needs_delay_slot (insn) == NEEDS_DELAY_SLOT_YES)
      && get_attr_needs_delay_slot (insn) == NEEDS_DELAY_SLOT_YES)
    return 2;
    return 2;
 
 
  /* SH2e has a bug that prevents the use of annulled branches, so if
  /* SH2e has a bug that prevents the use of annulled branches, so if
     the delay slot is not filled, we'll have to put a NOP in it.  */
     the delay slot is not filled, we'll have to put a NOP in it.  */
  if (sh_cpu_attr == CPU_SH2E
  if (sh_cpu_attr == CPU_SH2E
      && JUMP_P (insn) && !JUMP_TABLE_DATA_P (insn)
      && JUMP_P (insn) && !JUMP_TABLE_DATA_P (insn)
      && get_attr_type (insn) == TYPE_CBRANCH
      && get_attr_type (insn) == TYPE_CBRANCH
      && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (insn)))) != SEQUENCE)
      && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (insn)))) != SEQUENCE)
    return 2;
    return 2;
 
 
  /* sh-dsp parallel processing insn take four bytes instead of two.  */
  /* sh-dsp parallel processing insn take four bytes instead of two.  */
 
 
  if (NONJUMP_INSN_P (insn))
  if (NONJUMP_INSN_P (insn))
    {
    {
      int sum = 0;
      int sum = 0;
      rtx body = PATTERN (insn);
      rtx body = PATTERN (insn);
      const char *templ;
      const char *templ;
      char c;
      char c;
      int maybe_label = 1;
      int maybe_label = 1;
 
 
      if (GET_CODE (body) == ASM_INPUT)
      if (GET_CODE (body) == ASM_INPUT)
        templ = XSTR (body, 0);
        templ = XSTR (body, 0);
      else if (asm_noperands (body) >= 0)
      else if (asm_noperands (body) >= 0)
        templ
        templ
          = decode_asm_operands (body, NULL, NULL, NULL, NULL, NULL);
          = decode_asm_operands (body, NULL, NULL, NULL, NULL, NULL);
      else
      else
        return 0;
        return 0;
      do
      do
        {
        {
          int ppi_adjust = 0;
          int ppi_adjust = 0;
 
 
          do
          do
            c = *templ++;
            c = *templ++;
          while (c == ' ' || c == '\t');
          while (c == ' ' || c == '\t');
          /* all sh-dsp parallel-processing insns start with p.
          /* all sh-dsp parallel-processing insns start with p.
             The only non-ppi sh insn starting with p is pref.
             The only non-ppi sh insn starting with p is pref.
             The only ppi starting with pr is prnd.  */
             The only ppi starting with pr is prnd.  */
          if ((c == 'p' || c == 'P') && strncasecmp ("re", templ, 2))
          if ((c == 'p' || c == 'P') && strncasecmp ("re", templ, 2))
            ppi_adjust = 2;
            ppi_adjust = 2;
          /* The repeat pseudo-insn expands two three insns, a total of
          /* The repeat pseudo-insn expands two three insns, a total of
             six bytes in size.  */
             six bytes in size.  */
          else if ((c == 'r' || c == 'R')
          else if ((c == 'r' || c == 'R')
                   && ! strncasecmp ("epeat", templ, 5))
                   && ! strncasecmp ("epeat", templ, 5))
            ppi_adjust = 4;
            ppi_adjust = 4;
          while (c && c != '\n'
          while (c && c != '\n'
                 && ! IS_ASM_LOGICAL_LINE_SEPARATOR (c, templ))
                 && ! IS_ASM_LOGICAL_LINE_SEPARATOR (c, templ))
            {
            {
              /* If this is a label, it is obviously not a ppi insn.  */
              /* If this is a label, it is obviously not a ppi insn.  */
              if (c == ':' && maybe_label)
              if (c == ':' && maybe_label)
                {
                {
                  ppi_adjust = 0;
                  ppi_adjust = 0;
                  break;
                  break;
                }
                }
              else if (c == '\'' || c == '"')
              else if (c == '\'' || c == '"')
                maybe_label = 0;
                maybe_label = 0;
              c = *templ++;
              c = *templ++;
            }
            }
          sum += ppi_adjust;
          sum += ppi_adjust;
          maybe_label = c != ':';
          maybe_label = c != ':';
        }
        }
      while (c);
      while (c);
      return sum;
      return sum;
    }
    }
  return 0;
  return 0;
}
}


/* Return TRUE for a valid displacement for the REG+disp addressing
/* Return TRUE for a valid displacement for the REG+disp addressing
   with MODE.  */
   with MODE.  */
 
 
/* ??? The SH2e does not have the REG+disp addressing mode when loading values
/* ??? The SH2e does not have the REG+disp addressing mode when loading values
   into the FRx registers.  We implement this by setting the maximum offset
   into the FRx registers.  We implement this by setting the maximum offset
   to zero when the value is SFmode.  This also restricts loading of SFmode
   to zero when the value is SFmode.  This also restricts loading of SFmode
   values into the integer registers, but that can't be helped.  */
   values into the integer registers, but that can't be helped.  */
 
 
/* The SH allows a displacement in a QI or HI amode, but only when the
/* The SH allows a displacement in a QI or HI amode, but only when the
   other operand is R0. GCC doesn't handle this very well, so we forgot
   other operand is R0. GCC doesn't handle this very well, so we forgot
   all of that.
   all of that.
 
 
   A legitimate index for a QI or HI is 0, SI can be any number 0..63,
   A legitimate index for a QI or HI is 0, SI can be any number 0..63,
   DI can be any number 0..60.  */
   DI can be any number 0..60.  */
 
 
bool
bool
sh_legitimate_index_p (enum machine_mode mode, rtx op)
sh_legitimate_index_p (enum machine_mode mode, rtx op)
{
{
  if (CONST_INT_P (op))
  if (CONST_INT_P (op))
    {
    {
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        {
        {
          int size;
          int size;
 
 
          /* Check if this the address of an unaligned load / store.  */
          /* Check if this the address of an unaligned load / store.  */
          if (mode == VOIDmode)
          if (mode == VOIDmode)
            return CONST_OK_FOR_I06 (INTVAL (op));
            return CONST_OK_FOR_I06 (INTVAL (op));
 
 
          size = GET_MODE_SIZE (mode);
          size = GET_MODE_SIZE (mode);
          return (!(INTVAL (op) & (size - 1))
          return (!(INTVAL (op) & (size - 1))
                  && INTVAL (op) >= -512 * size
                  && INTVAL (op) >= -512 * size
                  && INTVAL (op) < 512 * size);
                  && INTVAL (op) < 512 * size);
        }
        }
 
 
      if (TARGET_SH2A)
      if (TARGET_SH2A)
        {
        {
          if (GET_MODE_SIZE (mode) == 1
          if (GET_MODE_SIZE (mode) == 1
                && (unsigned) INTVAL (op) < 4096)
                && (unsigned) INTVAL (op) < 4096)
            return true;
            return true;
        }
        }
 
 
      if ((GET_MODE_SIZE (mode) == 4
      if ((GET_MODE_SIZE (mode) == 4
           && (unsigned) INTVAL (op) < 64
           && (unsigned) INTVAL (op) < 64
           && !(INTVAL (op) & 3)
           && !(INTVAL (op) & 3)
           && !(TARGET_SH2E && mode == SFmode))
           && !(TARGET_SH2E && mode == SFmode))
          || (GET_MODE_SIZE (mode) == 4
          || (GET_MODE_SIZE (mode) == 4
              && (unsigned) INTVAL (op) < 16383
              && (unsigned) INTVAL (op) < 16383
              && !(INTVAL (op) & 3) && TARGET_SH2A))
              && !(INTVAL (op) & 3) && TARGET_SH2A))
        return true;
        return true;
 
 
      if ((GET_MODE_SIZE (mode) == 8
      if ((GET_MODE_SIZE (mode) == 8
           && (unsigned) INTVAL (op) < 60
           && (unsigned) INTVAL (op) < 60
           && !(INTVAL (op) & 3)
           && !(INTVAL (op) & 3)
           && !((TARGET_SH4 || TARGET_SH2A) && mode == DFmode))
           && !((TARGET_SH4 || TARGET_SH2A) && mode == DFmode))
          || ((GET_MODE_SIZE (mode)==8)
          || ((GET_MODE_SIZE (mode)==8)
              && (unsigned) INTVAL (op) < 8192
              && (unsigned) INTVAL (op) < 8192
              && !(INTVAL (op) & (TARGET_SH2A_DOUBLE ? 7 : 3))
              && !(INTVAL (op) & (TARGET_SH2A_DOUBLE ? 7 : 3))
              && (TARGET_SH2A && mode == DFmode)))
              && (TARGET_SH2A && mode == DFmode)))
        return true;
        return true;
    }
    }
 
 
  return false;
  return false;
}
}
 
 
/* Recognize an RTL expression that is a valid memory address for
/* Recognize an RTL expression that is a valid memory address for
   an instruction.
   an instruction.
   The MODE argument is the machine mode for the MEM expression
   The MODE argument is the machine mode for the MEM expression
   that wants to use this address.
   that wants to use this address.
   Allow  REG
   Allow  REG
          REG+disp
          REG+disp
          REG+r0
          REG+r0
          REG++
          REG++
          --REG  */
          --REG  */
 
 
static bool
static bool
sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
{
{
  if (MAYBE_BASE_REGISTER_RTX_P (x, strict))
  if (MAYBE_BASE_REGISTER_RTX_P (x, strict))
    return true;
    return true;
  else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
  else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
           && ! TARGET_SHMEDIA
           && ! TARGET_SHMEDIA
           && MAYBE_BASE_REGISTER_RTX_P (XEXP (x, 0), strict))
           && MAYBE_BASE_REGISTER_RTX_P (XEXP (x, 0), strict))
    return true;
    return true;
  else if (GET_CODE (x) == PLUS
  else if (GET_CODE (x) == PLUS
           && (mode != PSImode || reload_completed))
           && (mode != PSImode || reload_completed))
    {
    {
      rtx xop0 = XEXP (x, 0);
      rtx xop0 = XEXP (x, 0);
      rtx xop1 = XEXP (x, 1);
      rtx xop1 = XEXP (x, 1);
 
 
      if (GET_MODE_SIZE (mode) <= 8
      if (GET_MODE_SIZE (mode) <= 8
          && MAYBE_BASE_REGISTER_RTX_P (xop0, strict)
          && MAYBE_BASE_REGISTER_RTX_P (xop0, strict)
          && sh_legitimate_index_p (mode, xop1))
          && sh_legitimate_index_p (mode, xop1))
        return true;
        return true;
 
 
      if ((ALLOW_INDEXED_ADDRESS || GET_MODE (x) == DImode
      if ((ALLOW_INDEXED_ADDRESS || GET_MODE (x) == DImode
           || ((xop0 == stack_pointer_rtx
           || ((xop0 == stack_pointer_rtx
                || xop0 == hard_frame_pointer_rtx)
                || xop0 == hard_frame_pointer_rtx)
               && REG_P (xop1) && REGNO (xop1) == R0_REG)
               && REG_P (xop1) && REGNO (xop1) == R0_REG)
           || ((xop1 == stack_pointer_rtx
           || ((xop1 == stack_pointer_rtx
                || xop1 == hard_frame_pointer_rtx)
                || xop1 == hard_frame_pointer_rtx)
               && REG_P (xop0) && REGNO (xop0) == R0_REG))
               && REG_P (xop0) && REGNO (xop0) == R0_REG))
          && ((!TARGET_SHMEDIA && GET_MODE_SIZE (mode) <= 4)
          && ((!TARGET_SHMEDIA && GET_MODE_SIZE (mode) <= 4)
              || (TARGET_SHMEDIA && GET_MODE_SIZE (mode) <= 8)
              || (TARGET_SHMEDIA && GET_MODE_SIZE (mode) <= 8)
              || ((TARGET_SH4 || TARGET_SH2A_DOUBLE)
              || ((TARGET_SH4 || TARGET_SH2A_DOUBLE)
                  && TARGET_FMOVD && mode == DFmode)))
                  && TARGET_FMOVD && mode == DFmode)))
        {
        {
          if (MAYBE_BASE_REGISTER_RTX_P (xop1, strict)
          if (MAYBE_BASE_REGISTER_RTX_P (xop1, strict)
              && MAYBE_INDEX_REGISTER_RTX_P (xop0, strict))
              && MAYBE_INDEX_REGISTER_RTX_P (xop0, strict))
            return true;
            return true;
          if (MAYBE_INDEX_REGISTER_RTX_P (xop1, strict)
          if (MAYBE_INDEX_REGISTER_RTX_P (xop1, strict)
              && MAYBE_BASE_REGISTER_RTX_P (xop0, strict))
              && MAYBE_BASE_REGISTER_RTX_P (xop0, strict))
            return true;
            return true;
        }
        }
    }
    }
 
 
  return false;
  return false;
}
}


/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
   isn't protected by a PIC unspec.  */
   isn't protected by a PIC unspec.  */
int
int
nonpic_symbol_mentioned_p (rtx x)
nonpic_symbol_mentioned_p (rtx x)
{
{
  register const char *fmt;
  register const char *fmt;
  register int i;
  register int i;
 
 
  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
      || GET_CODE (x) == PC)
      || GET_CODE (x) == PC)
    return 1;
    return 1;
 
 
  /* We don't want to look into the possible MEM location of a
  /* We don't want to look into the possible MEM location of a
     CONST_DOUBLE, since we're not going to use it, in general.  */
     CONST_DOUBLE, since we're not going to use it, in general.  */
  if (GET_CODE (x) == CONST_DOUBLE)
  if (GET_CODE (x) == CONST_DOUBLE)
    return 0;
    return 0;
 
 
  if (GET_CODE (x) == UNSPEC
  if (GET_CODE (x) == UNSPEC
      && (XINT (x, 1) == UNSPEC_PIC
      && (XINT (x, 1) == UNSPEC_PIC
          || XINT (x, 1) == UNSPEC_GOT
          || XINT (x, 1) == UNSPEC_GOT
          || XINT (x, 1) == UNSPEC_GOTOFF
          || XINT (x, 1) == UNSPEC_GOTOFF
          || XINT (x, 1) == UNSPEC_GOTPLT
          || XINT (x, 1) == UNSPEC_GOTPLT
          || XINT (x, 1) == UNSPEC_GOTTPOFF
          || XINT (x, 1) == UNSPEC_GOTTPOFF
          || XINT (x, 1) == UNSPEC_DTPOFF
          || XINT (x, 1) == UNSPEC_DTPOFF
          || XINT (x, 1) == UNSPEC_TPOFF
          || XINT (x, 1) == UNSPEC_TPOFF
          || XINT (x, 1) == UNSPEC_PLT
          || XINT (x, 1) == UNSPEC_PLT
          || XINT (x, 1) == UNSPEC_SYMOFF
          || XINT (x, 1) == UNSPEC_SYMOFF
          || XINT (x, 1) == UNSPEC_PCREL_SYMOFF))
          || XINT (x, 1) == UNSPEC_PCREL_SYMOFF))
    return 0;
    return 0;
 
 
  fmt = GET_RTX_FORMAT (GET_CODE (x));
  fmt = GET_RTX_FORMAT (GET_CODE (x));
  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
    {
    {
      if (fmt[i] == 'E')
      if (fmt[i] == 'E')
        {
        {
          register int j;
          register int j;
 
 
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
            if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
            if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
              return 1;
              return 1;
        }
        }
      else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
      else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
        return 1;
        return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
   @GOTOFF in `reg'.  */
   @GOTOFF in `reg'.  */
rtx
rtx
legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
                        rtx reg)
                        rtx reg)
{
{
  if (tls_symbolic_operand (orig, Pmode) != TLS_MODEL_NONE)
  if (tls_symbolic_operand (orig, Pmode) != TLS_MODEL_NONE)
    return orig;
    return orig;
 
 
  if (GET_CODE (orig) == LABEL_REF
  if (GET_CODE (orig) == LABEL_REF
      || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
      || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
    {
    {
      if (reg == 0)
      if (reg == 0)
        reg = gen_reg_rtx (Pmode);
        reg = gen_reg_rtx (Pmode);
 
 
      emit_insn (gen_symGOTOFF2reg (reg, orig));
      emit_insn (gen_symGOTOFF2reg (reg, orig));
      return reg;
      return reg;
    }
    }
  else if (GET_CODE (orig) == SYMBOL_REF)
  else if (GET_CODE (orig) == SYMBOL_REF)
    {
    {
      if (reg == 0)
      if (reg == 0)
        reg = gen_reg_rtx (Pmode);
        reg = gen_reg_rtx (Pmode);
 
 
      emit_insn (gen_symGOT2reg (reg, orig));
      emit_insn (gen_symGOT2reg (reg, orig));
      return reg;
      return reg;
    }
    }
  return orig;
  return orig;
}
}
 
 
/* Try machine-dependent ways of modifying an illegitimate address
/* Try machine-dependent ways of modifying an illegitimate address
   to be legitimate.  If we find one, return the new, valid address.
   to be legitimate.  If we find one, return the new, valid address.
   Otherwise, return X.
   Otherwise, return X.
 
 
   For the SH, if X is almost suitable for indexing, but the offset is
   For the SH, if X is almost suitable for indexing, but the offset is
   out of range, convert it into a normal form so that CSE has a chance
   out of range, convert it into a normal form so that CSE has a chance
   of reducing the number of address registers used.  */
   of reducing the number of address registers used.  */
 
 
static rtx
static rtx
sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
{
{
  if (flag_pic)
  if (flag_pic)
    x = legitimize_pic_address (oldx, mode, NULL_RTX);
    x = legitimize_pic_address (oldx, mode, NULL_RTX);
 
 
  if (GET_CODE (x) == PLUS
  if (GET_CODE (x) == PLUS
      && (GET_MODE_SIZE (mode) == 4
      && (GET_MODE_SIZE (mode) == 4
          || GET_MODE_SIZE (mode) == 8)
          || GET_MODE_SIZE (mode) == 8)
      && CONST_INT_P (XEXP (x, 1))
      && CONST_INT_P (XEXP (x, 1))
      && BASE_REGISTER_RTX_P (XEXP (x, 0))
      && BASE_REGISTER_RTX_P (XEXP (x, 0))
      && ! TARGET_SHMEDIA
      && ! TARGET_SHMEDIA
      && ! ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
      && ! ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
      && ! (TARGET_SH2E && mode == SFmode))
      && ! (TARGET_SH2E && mode == SFmode))
    {
    {
      rtx index_rtx = XEXP (x, 1);
      rtx index_rtx = XEXP (x, 1);
      HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base;
      HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base;
      rtx sum;
      rtx sum;
 
 
      /* On rare occasions, we might get an unaligned pointer
      /* On rare occasions, we might get an unaligned pointer
         that is indexed in a way to give an aligned address.
         that is indexed in a way to give an aligned address.
         Therefore, keep the lower two bits in offset_base.  */
         Therefore, keep the lower two bits in offset_base.  */
      /* Instead of offset_base 128..131 use 124..127, so that
      /* Instead of offset_base 128..131 use 124..127, so that
         simple add suffices.  */
         simple add suffices.  */
      if (offset > 127)
      if (offset > 127)
        offset_base = ((offset + 4) & ~60) - 4;
        offset_base = ((offset + 4) & ~60) - 4;
      else
      else
        offset_base = offset & ~60;
        offset_base = offset & ~60;
 
 
      /* Sometimes the normal form does not suit DImode.  We
      /* Sometimes the normal form does not suit DImode.  We
         could avoid that by using smaller ranges, but that
         could avoid that by using smaller ranges, but that
         would give less optimized code when SImode is
         would give less optimized code when SImode is
         prevalent.  */
         prevalent.  */
      if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
      if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
        {
        {
          sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
          sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
                              GEN_INT (offset_base), NULL_RTX, 0,
                              GEN_INT (offset_base), NULL_RTX, 0,
                              OPTAB_LIB_WIDEN);
                              OPTAB_LIB_WIDEN);
 
 
          return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
          return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
        }
        }
    }
    }
 
 
  return x;
  return x;
}
}
 
 
/* Mark the use of a constant in the literal table. If the constant
/* Mark the use of a constant in the literal table. If the constant
   has multiple labels, make it unique.  */
   has multiple labels, make it unique.  */
static rtx
static rtx
mark_constant_pool_use (rtx x)
mark_constant_pool_use (rtx x)
{
{
  rtx insn, lab, pattern;
  rtx insn, lab, pattern;
 
 
  if (x == NULL)
  if (x == NULL)
    return x;
    return x;
 
 
  switch (GET_CODE (x))
  switch (GET_CODE (x))
    {
    {
    case LABEL_REF:
    case LABEL_REF:
      x = XEXP (x, 0);
      x = XEXP (x, 0);
    case CODE_LABEL:
    case CODE_LABEL:
      break;
      break;
    default:
    default:
      return x;
      return x;
    }
    }
 
 
  /* Get the first label in the list of labels for the same constant
  /* Get the first label in the list of labels for the same constant
     and delete another labels in the list.  */
     and delete another labels in the list.  */
  lab = x;
  lab = x;
  for (insn = PREV_INSN (x); insn; insn = PREV_INSN (insn))
  for (insn = PREV_INSN (x); insn; insn = PREV_INSN (insn))
    {
    {
      if (!LABEL_P (insn)
      if (!LABEL_P (insn)
          || LABEL_REFS (insn) != NEXT_INSN (insn))
          || LABEL_REFS (insn) != NEXT_INSN (insn))
        break;
        break;
      lab = insn;
      lab = insn;
    }
    }
 
 
  for (insn = LABEL_REFS (lab); insn; insn = LABEL_REFS (insn))
  for (insn = LABEL_REFS (lab); insn; insn = LABEL_REFS (insn))
    INSN_DELETED_P (insn) = 1;
    INSN_DELETED_P (insn) = 1;
 
 
  /* Mark constants in a window.  */
  /* Mark constants in a window.  */
  for (insn = NEXT_INSN (x); insn; insn = NEXT_INSN (insn))
  for (insn = NEXT_INSN (x); insn; insn = NEXT_INSN (insn))
    {
    {
      if (!NONJUMP_INSN_P (insn))
      if (!NONJUMP_INSN_P (insn))
        continue;
        continue;
 
 
      pattern = PATTERN (insn);
      pattern = PATTERN (insn);
      if (GET_CODE (pattern) != UNSPEC_VOLATILE)
      if (GET_CODE (pattern) != UNSPEC_VOLATILE)
        continue;
        continue;
 
 
      switch (XINT (pattern, 1))
      switch (XINT (pattern, 1))
        {
        {
        case UNSPECV_CONST2:
        case UNSPECV_CONST2:
        case UNSPECV_CONST4:
        case UNSPECV_CONST4:
        case UNSPECV_CONST8:
        case UNSPECV_CONST8:
          XVECEXP (pattern, 0, 1) = const1_rtx;
          XVECEXP (pattern, 0, 1) = const1_rtx;
          break;
          break;
        case UNSPECV_WINDOW_END:
        case UNSPECV_WINDOW_END:
          if (XVECEXP (pattern, 0, 0) == x)
          if (XVECEXP (pattern, 0, 0) == x)
            return lab;
            return lab;
          break;
          break;
        case UNSPECV_CONST_END:
        case UNSPECV_CONST_END:
          return lab;
          return lab;
        default:
        default:
          break;
          break;
        }
        }
    }
    }
 
 
  return lab;
  return lab;
}
}


/* Return true if it's possible to redirect BRANCH1 to the destination
/* Return true if it's possible to redirect BRANCH1 to the destination
   of an unconditional jump BRANCH2.  We only want to do this if the
   of an unconditional jump BRANCH2.  We only want to do this if the
   resulting branch will have a short displacement.  */
   resulting branch will have a short displacement.  */
int
int
sh_can_redirect_branch (rtx branch1, rtx branch2)
sh_can_redirect_branch (rtx branch1, rtx branch2)
{
{
  if (flag_expensive_optimizations && simplejump_p (branch2))
  if (flag_expensive_optimizations && simplejump_p (branch2))
    {
    {
      rtx dest = XEXP (SET_SRC (single_set (branch2)), 0);
      rtx dest = XEXP (SET_SRC (single_set (branch2)), 0);
      rtx insn;
      rtx insn;
      int distance;
      int distance;
 
 
      for (distance = 0, insn = NEXT_INSN (branch1);
      for (distance = 0, insn = NEXT_INSN (branch1);
           insn && distance < 256;
           insn && distance < 256;
           insn = PREV_INSN (insn))
           insn = PREV_INSN (insn))
        {
        {
          if (insn == dest)
          if (insn == dest)
            return 1;
            return 1;
          else
          else
            distance += get_attr_length (insn);
            distance += get_attr_length (insn);
        }
        }
      for (distance = 0, insn = NEXT_INSN (branch1);
      for (distance = 0, insn = NEXT_INSN (branch1);
           insn && distance < 256;
           insn && distance < 256;
           insn = NEXT_INSN (insn))
           insn = NEXT_INSN (insn))
        {
        {
          if (insn == dest)
          if (insn == dest)
            return 1;
            return 1;
          else
          else
            distance += get_attr_length (insn);
            distance += get_attr_length (insn);
        }
        }
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Return nonzero if register old_reg can be renamed to register new_reg.  */
/* Return nonzero if register old_reg can be renamed to register new_reg.  */
int
int
sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
                         unsigned int new_reg)
                         unsigned int new_reg)
{
{
  /* Interrupt functions can only use registers that have already been
  /* Interrupt functions can only use registers that have already been
     saved by the prologue, even if they would normally be
     saved by the prologue, even if they would normally be
     call-clobbered.  */
     call-clobbered.  */
 
 
  if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg))
  if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg))
    return 0;
    return 0;
 
 
  return 1;
  return 1;
}
}
 
 
/* Function to update the integer COST
/* Function to update the integer COST
   based on the relationship between INSN that is dependent on
   based on the relationship between INSN that is dependent on
   DEP_INSN through the dependence LINK.  The default is to make no
   DEP_INSN through the dependence LINK.  The default is to make no
   adjustment to COST.  This can be used for example to specify to
   adjustment to COST.  This can be used for example to specify to
   the scheduler that an output- or anti-dependence does not incur
   the scheduler that an output- or anti-dependence does not incur
   the same cost as a data-dependence.  The return value should be
   the same cost as a data-dependence.  The return value should be
   the new value for COST.  */
   the new value for COST.  */
static int
static int
sh_adjust_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;
  rtx reg, use_pat;
 
 
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    {
    {
      /* On SHmedia, if the dependence is an anti-dependence or
      /* 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)
      if (REG_NOTE_KIND (link) != 0)
        {
        {
          /* However, dependencies between target register loads and
          /* However, dependencies between target register loads and
             uses of the register in a subsequent block that are separated
             uses of the register in a subsequent block that are separated
             by a conditional branch are not modelled - we have to do with
             by a conditional branch are not modelled - we have to do with
             the anti-dependency between the target register load and the
             the anti-dependency between the target register load and the
             conditional branch that ends the current block.  */
             conditional branch that ends the current block.  */
          if (REG_NOTE_KIND (link) == REG_DEP_ANTI
          if (REG_NOTE_KIND (link) == REG_DEP_ANTI
              && GET_CODE (PATTERN (dep_insn)) == SET
              && GET_CODE (PATTERN (dep_insn)) == SET
              && (get_attr_type (dep_insn) == TYPE_PT_MEDIA
              && (get_attr_type (dep_insn) == TYPE_PT_MEDIA
                  || get_attr_type (dep_insn) == TYPE_PTABS_MEDIA)
                  || get_attr_type (dep_insn) == TYPE_PTABS_MEDIA)
              && get_attr_type (insn) == TYPE_CBRANCH_MEDIA)
              && get_attr_type (insn) == TYPE_CBRANCH_MEDIA)
            {
            {
              int orig_cost = cost;
              int orig_cost = cost;
              rtx note = find_reg_note (insn, REG_BR_PROB, 0);
              rtx note = find_reg_note (insn, REG_BR_PROB, 0);
              rtx target = ((! note
              rtx target = ((! note
                             || INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
                             || INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
                            ? insn : JUMP_LABEL (insn));
                            ? insn : JUMP_LABEL (insn));
              /* On the likely path, the branch costs 1, on the unlikely path,
              /* On the likely path, the branch costs 1, on the unlikely path,
                 it costs 3.  */
                 it costs 3.  */
              cost--;
              cost--;
              do
              do
                target = next_active_insn (target);
                target = next_active_insn (target);
              while (target && ! flow_dependent_p (target, dep_insn)
              while (target && ! flow_dependent_p (target, dep_insn)
                     && --cost > 0);
                     && --cost > 0);
              /* If two branches are executed in immediate succession, with the
              /* If two branches are executed in immediate succession, with the
                 first branch properly predicted, this causes a stall at the
                 first branch properly predicted, this causes a stall at the
                 second branch, hence we won't need the target for the
                 second branch, hence we won't need the target for the
                 second branch for two cycles after the launch of the first
                 second branch for two cycles after the launch of the first
                 branch.  */
                 branch.  */
              if (cost > orig_cost - 2)
              if (cost > orig_cost - 2)
                cost = orig_cost - 2;
                cost = orig_cost - 2;
            }
            }
          else
          else
            cost = 0;
            cost = 0;
        }
        }
 
 
      else if (get_attr_is_mac_media (insn)
      else if (get_attr_is_mac_media (insn)
               && get_attr_is_mac_media (dep_insn))
               && get_attr_is_mac_media (dep_insn))
        cost = 1;
        cost = 1;
 
 
      else if (! reload_completed
      else if (! reload_completed
               && GET_CODE (PATTERN (insn)) == SET
               && GET_CODE (PATTERN (insn)) == SET
               && GET_CODE (SET_SRC (PATTERN (insn))) == FLOAT
               && GET_CODE (SET_SRC (PATTERN (insn))) == FLOAT
               && GET_CODE (PATTERN (dep_insn)) == SET
               && GET_CODE (PATTERN (dep_insn)) == SET
               && fp_arith_reg_operand (SET_SRC (PATTERN (dep_insn)), VOIDmode)
               && fp_arith_reg_operand (SET_SRC (PATTERN (dep_insn)), VOIDmode)
               && cost < 4)
               && cost < 4)
        cost = 4;
        cost = 4;
      /* Schedule the ptabs for a casesi_jump_media in preference to stuff
      /* Schedule the ptabs for a casesi_jump_media in preference to stuff
         that is needed at the target.  */
         that is needed at the target.  */
      else if (get_attr_type (insn) == TYPE_JUMP_MEDIA
      else if (get_attr_type (insn) == TYPE_JUMP_MEDIA
               && ! flow_dependent_p (insn, dep_insn))
               && ! flow_dependent_p (insn, dep_insn))
        cost--;
        cost--;
    }
    }
  else if (REG_NOTE_KIND (link) == 0)
  else if (REG_NOTE_KIND (link) == 0)
    {
    {
      enum attr_type type;
      enum attr_type type;
      rtx dep_set;
      rtx dep_set;
 
 
      if (recog_memoized (insn) < 0
      if (recog_memoized (insn) < 0
          || recog_memoized (dep_insn) < 0)
          || recog_memoized (dep_insn) < 0)
        return cost;
        return cost;
 
 
      dep_set = single_set (dep_insn);
      dep_set = single_set (dep_insn);
 
 
      /* The latency that we specify in the scheduling description refers
      /* The latency that we specify in the scheduling description refers
         to the actual output, not to an auto-increment register; for that,
         to the actual output, not to an auto-increment register; for that,
         the latency is one.  */
         the latency is one.  */
      if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
      if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
        {
        {
          rtx set = single_set (insn);
          rtx set = single_set (insn);
 
 
          if (set
          if (set
              && !reg_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
              && !reg_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
              && (!MEM_P (SET_DEST (set))
              && (!MEM_P (SET_DEST (set))
                  || !reg_mentioned_p (SET_DEST (dep_set),
                  || !reg_mentioned_p (SET_DEST (dep_set),
                                       XEXP (SET_DEST (set), 0))))
                                       XEXP (SET_DEST (set), 0))))
            cost = 1;
            cost = 1;
        }
        }
      /* The only input for a call that is timing-critical is the
      /* The only input for a call that is timing-critical is the
         function's address.  */
         function's address.  */
      if (CALL_P (insn))
      if (CALL_P (insn))
        {
        {
          rtx call = PATTERN (insn);
          rtx call = PATTERN (insn);
 
 
          if (GET_CODE (call) == PARALLEL)
          if (GET_CODE (call) == PARALLEL)
            call = XVECEXP (call, 0 ,0);
            call = XVECEXP (call, 0 ,0);
          if (GET_CODE (call) == SET)
          if (GET_CODE (call) == SET)
            call = SET_SRC (call);
            call = SET_SRC (call);
          if (GET_CODE (call) == CALL && MEM_P (XEXP (call, 0))
          if (GET_CODE (call) == CALL && MEM_P (XEXP (call, 0))
                  /* sibcalli_thunk uses a symbol_ref in an unspec.  */
                  /* sibcalli_thunk uses a symbol_ref in an unspec.  */
              && (GET_CODE (XEXP (XEXP (call, 0), 0)) == UNSPEC
              && (GET_CODE (XEXP (XEXP (call, 0), 0)) == UNSPEC
                  || ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn)))
                  || ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn)))
            cost -= TARGET_SH4_300 ? 3 : 6;
            cost -= TARGET_SH4_300 ? 3 : 6;
        }
        }
      /* Likewise, the most timing critical input for an sfuncs call
      /* Likewise, the most timing critical input for an sfuncs call
         is the function address.  However, sfuncs typically start
         is the function address.  However, sfuncs typically start
         using their arguments pretty quickly.
         using their arguments pretty quickly.
         Assume a four cycle delay for SH4 before they are needed.
         Assume a four cycle delay for SH4 before they are needed.
         Cached ST40-300 calls are quicker, so assume only a one
         Cached ST40-300 calls are quicker, so assume only a one
         cycle delay there.
         cycle delay there.
         ??? Maybe we should encode the delays till input registers
         ??? Maybe we should encode the delays till input registers
         are needed by sfuncs into the sfunc call insn.  */
         are needed by sfuncs into the sfunc call insn.  */
      /* All sfunc calls are parallels with at least four components.
      /* All sfunc calls are parallels with at least four components.
         Exploit this to avoid unnecessary calls to sfunc_uses_reg.  */
         Exploit this to avoid unnecessary calls to sfunc_uses_reg.  */
      else if (GET_CODE (PATTERN (insn)) == PARALLEL
      else if (GET_CODE (PATTERN (insn)) == PARALLEL
               && XVECLEN (PATTERN (insn), 0) >= 4
               && XVECLEN (PATTERN (insn), 0) >= 4
               && (reg = sfunc_uses_reg (insn)))
               && (reg = sfunc_uses_reg (insn)))
        {
        {
          if (! reg_set_p (reg, dep_insn))
          if (! reg_set_p (reg, dep_insn))
            cost -= TARGET_SH4_300 ? 1 : 4;
            cost -= TARGET_SH4_300 ? 1 : 4;
        }
        }
      if (TARGET_HARD_SH4 && !TARGET_SH4_300)
      if (TARGET_HARD_SH4 && !TARGET_SH4_300)
        {
        {
          enum attr_type dep_type = get_attr_type (dep_insn);
          enum attr_type dep_type = get_attr_type (dep_insn);
 
 
          if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
          if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
            cost--;
            cost--;
          else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
          else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
                   && (type = get_attr_type (insn)) != TYPE_CALL
                   && (type = get_attr_type (insn)) != TYPE_CALL
                   && type != TYPE_SFUNC)
                   && type != TYPE_SFUNC)
            cost--;
            cost--;
          /* When the preceding instruction loads the shift amount of
          /* When the preceding instruction loads the shift amount of
             the following SHAD/SHLD, the latency of the load is increased
             the following SHAD/SHLD, the latency of the load is increased
             by 1 cycle.  */
             by 1 cycle.  */
          if (get_attr_type (insn) == TYPE_DYN_SHIFT
          if (get_attr_type (insn) == TYPE_DYN_SHIFT
              && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
              && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
              && reg_overlap_mentioned_p (SET_DEST (dep_set),
              && reg_overlap_mentioned_p (SET_DEST (dep_set),
                                          XEXP (SET_SRC (single_set (insn)),
                                          XEXP (SET_SRC (single_set (insn)),
                                                1)))
                                                1)))
            cost++;
            cost++;
          /* When an LS group instruction with a latency of less than
          /* When an LS group instruction with a latency of less than
             3 cycles is followed by a double-precision floating-point
             3 cycles is followed by a double-precision floating-point
             instruction, FIPR, or FTRV, the latency of the first
             instruction, FIPR, or FTRV, the latency of the first
             instruction is increased to 3 cycles.  */
             instruction is increased to 3 cycles.  */
          else if (cost < 3
          else if (cost < 3
                   && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
                   && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
                   && get_attr_dfp_comp (insn) == DFP_COMP_YES)
                   && get_attr_dfp_comp (insn) == DFP_COMP_YES)
            cost = 3;
            cost = 3;
          /* The lsw register of a double-precision computation is ready one
          /* The lsw register of a double-precision computation is ready one
             cycle earlier.  */
             cycle earlier.  */
          else if (reload_completed
          else if (reload_completed
                   && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
                   && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
                   && (use_pat = single_set (insn))
                   && (use_pat = single_set (insn))
                   && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
                   && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
                                      SET_SRC (use_pat)))
                                      SET_SRC (use_pat)))
            cost -= 1;
            cost -= 1;
 
 
          if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
          if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
              && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
              && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
            cost -= 1;
            cost -= 1;
        }
        }
      else if (TARGET_SH4_300)
      else if (TARGET_SH4_300)
        {
        {
          /* Stores need their input register two cycles later.  */
          /* Stores need their input register two cycles later.  */
          if (dep_set && cost >= 1
          if (dep_set && cost >= 1
              && ((type = get_attr_type (insn)) == TYPE_STORE
              && ((type = get_attr_type (insn)) == TYPE_STORE
                  || type == TYPE_PSTORE
                  || type == TYPE_PSTORE
                  || type == TYPE_FSTORE || type == TYPE_MAC_MEM))
                  || type == TYPE_FSTORE || type == TYPE_MAC_MEM))
            {
            {
              rtx set = single_set (insn);
              rtx set = single_set (insn);
 
 
              if (!reg_mentioned_p (SET_SRC (set), XEXP (SET_DEST (set), 0))
              if (!reg_mentioned_p (SET_SRC (set), XEXP (SET_DEST (set), 0))
                  && rtx_equal_p (SET_SRC (set), SET_DEST (dep_set)))
                  && rtx_equal_p (SET_SRC (set), SET_DEST (dep_set)))
                {
                {
                  cost -= 2;
                  cost -= 2;
                  /* But don't reduce the cost below 1 if the address depends
                  /* But don't reduce the cost below 1 if the address depends
                     on a side effect of dep_insn.  */
                     on a side effect of dep_insn.  */
                  if (cost < 1
                  if (cost < 1
                      && modified_in_p (XEXP (SET_DEST (set), 0), dep_insn))
                      && modified_in_p (XEXP (SET_DEST (set), 0), dep_insn))
                    cost = 1;
                    cost = 1;
                }
                }
            }
            }
        }
        }
    }
    }
  /* An anti-dependence penalty of two applies if the first insn is a double
  /* An anti-dependence penalty of two applies if the first insn is a double
     precision fadd / fsub / fmul.  */
     precision fadd / fsub / fmul.  */
  else if (!TARGET_SH4_300
  else if (!TARGET_SH4_300
           && REG_NOTE_KIND (link) == REG_DEP_ANTI
           && REG_NOTE_KIND (link) == REG_DEP_ANTI
           && recog_memoized (dep_insn) >= 0
           && recog_memoized (dep_insn) >= 0
           && (get_attr_type (dep_insn) == TYPE_DFP_ARITH
           && (get_attr_type (dep_insn) == TYPE_DFP_ARITH
               || get_attr_type (dep_insn) == TYPE_DFP_MUL)
               || get_attr_type (dep_insn) == TYPE_DFP_MUL)
           /* A lot of alleged anti-flow dependences are fake,
           /* A lot of alleged anti-flow dependences are fake,
              so check this one is real.  */
              so check this one is real.  */
           && flow_dependent_p (dep_insn, insn))
           && flow_dependent_p (dep_insn, insn))
    cost = 2;
    cost = 2;
 
 
  return cost;
  return cost;
}
}
 
 
/* Check if INSN is flow-dependent on DEP_INSN.  Can also be used to check
/* Check if INSN is flow-dependent on DEP_INSN.  Can also be used to check
   if DEP_INSN is anti-flow dependent on INSN.  */
   if DEP_INSN is anti-flow dependent on INSN.  */
static int
static int
flow_dependent_p (rtx insn, rtx dep_insn)
flow_dependent_p (rtx insn, rtx dep_insn)
{
{
  rtx tmp = PATTERN (insn);
  rtx tmp = PATTERN (insn);
 
 
  note_stores (PATTERN (dep_insn), flow_dependent_p_1, &tmp);
  note_stores (PATTERN (dep_insn), flow_dependent_p_1, &tmp);
  return tmp == NULL_RTX;
  return tmp == NULL_RTX;
}
}
 
 
/* A helper function for flow_dependent_p called through note_stores.  */
/* A helper function for flow_dependent_p called through note_stores.  */
static void
static void
flow_dependent_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
flow_dependent_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
{
{
  rtx * pinsn = (rtx *) data;
  rtx * pinsn = (rtx *) data;
 
 
  if (*pinsn && reg_referenced_p (x, *pinsn))
  if (*pinsn && reg_referenced_p (x, *pinsn))
    *pinsn = NULL_RTX;
    *pinsn = NULL_RTX;
}
}
 
 
/* For use by sh_allocate_initial_value.  Note that sh.md contains some
/* For use by sh_allocate_initial_value.  Note that sh.md contains some
   'special function' patterns (type sfunc) that clobber pr, but that
   'special function' patterns (type sfunc) that clobber pr, but that
   do not look like function calls to leaf_function_p.  Hence we must
   do not look like function calls to leaf_function_p.  Hence we must
   do this extra check.  */
   do this extra check.  */
static int
static int
sh_pr_n_sets (void)
sh_pr_n_sets (void)
{
{
  return DF_REG_DEF_COUNT (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
  return DF_REG_DEF_COUNT (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
}
}
 
 
/* Return where to allocate pseudo for a given hard register initial
/* Return where to allocate pseudo for a given hard register initial
   value.  */
   value.  */
static rtx
static rtx
sh_allocate_initial_value (rtx hard_reg)
sh_allocate_initial_value (rtx hard_reg)
{
{
  rtx x;
  rtx x;
 
 
  if (REGNO (hard_reg) == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG))
  if (REGNO (hard_reg) == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG))
    {
    {
      if (current_function_is_leaf
      if (current_function_is_leaf
          && ! sh_pr_n_sets ()
          && ! sh_pr_n_sets ()
          && ! (TARGET_SHCOMPACT
          && ! (TARGET_SHCOMPACT
                && ((crtl->args.info.call_cookie
                && ((crtl->args.info.call_cookie
                     & ~ CALL_COOKIE_RET_TRAMP (1))
                     & ~ CALL_COOKIE_RET_TRAMP (1))
                    || crtl->saves_all_registers)))
                    || crtl->saves_all_registers)))
        x = hard_reg;
        x = hard_reg;
      else
      else
        x = gen_frame_mem (Pmode, return_address_pointer_rtx);
        x = gen_frame_mem (Pmode, return_address_pointer_rtx);
    }
    }
  else
  else
    x = NULL_RTX;
    x = NULL_RTX;
 
 
  return x;
  return x;
}
}
 
 
/* This function returns "2" to indicate dual issue for the SH4
/* This function returns "2" to indicate dual issue for the SH4
   processor.  To be used by the DFA pipeline description.  */
   processor.  To be used by the DFA pipeline description.  */
static int
static int
sh_issue_rate (void)
sh_issue_rate (void)
{
{
  if (TARGET_SUPERSCALAR)
  if (TARGET_SUPERSCALAR)
    return 2;
    return 2;
  else
  else
    return 1;
    return 1;
}
}
 
 
/* Functions for ready queue reordering for sched1.  */
/* Functions for ready queue reordering for sched1.  */
 
 
/* Get weight for mode for a set x.  */
/* Get weight for mode for a set x.  */
static short
static short
find_set_regmode_weight (rtx x, enum machine_mode mode)
find_set_regmode_weight (rtx x, enum machine_mode mode)
{
{
  if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode))
  if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode))
    return 1;
    return 1;
  if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode))
  if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode))
    {
    {
      if (REG_P (SET_DEST (x)))
      if (REG_P (SET_DEST (x)))
        {
        {
          if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x)))
          if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x)))
            return 1;
            return 1;
          else
          else
            return 0;
            return 0;
        }
        }
      return 1;
      return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Get regmode weight for insn.  */
/* Get regmode weight for insn.  */
static short
static short
find_insn_regmode_weight (rtx insn, enum machine_mode mode)
find_insn_regmode_weight (rtx insn, enum machine_mode mode)
{
{
  short reg_weight = 0;
  short reg_weight = 0;
  rtx x;
  rtx x;
 
 
  /* Increment weight for each register born here.  */
  /* Increment weight for each register born here.  */
  x = PATTERN (insn);
  x = PATTERN (insn);
  reg_weight += find_set_regmode_weight (x, mode);
  reg_weight += find_set_regmode_weight (x, mode);
  if (GET_CODE (x) == PARALLEL)
  if (GET_CODE (x) == PARALLEL)
    {
    {
      int j;
      int j;
      for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
      for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
        {
        {
          x = XVECEXP (PATTERN (insn), 0, j);
          x = XVECEXP (PATTERN (insn), 0, j);
          reg_weight += find_set_regmode_weight (x, mode);
          reg_weight += find_set_regmode_weight (x, mode);
        }
        }
    }
    }
  /* Decrement weight for each register that dies here.  */
  /* Decrement weight for each register that dies here.  */
  for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
  for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
    {
    {
      if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED)
      if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED)
        {
        {
          rtx note = XEXP (x, 0);
          rtx note = XEXP (x, 0);
          if (REG_P (note) && GET_MODE (note) == mode)
          if (REG_P (note) && GET_MODE (note) == mode)
            reg_weight--;
            reg_weight--;
        }
        }
    }
    }
  return reg_weight;
  return reg_weight;
}
}
 
 
/* Calculate regmode weights for all insns of a basic block.  */
/* Calculate regmode weights for all insns of a basic block.  */
static void
static void
find_regmode_weight (basic_block b, enum machine_mode mode)
find_regmode_weight (basic_block b, enum machine_mode mode)
{
{
  rtx insn, next_tail, head, tail;
  rtx insn, next_tail, head, tail;
 
 
  get_ebb_head_tail (b, b, &head, &tail);
  get_ebb_head_tail (b, b, &head, &tail);
  next_tail = NEXT_INSN (tail);
  next_tail = NEXT_INSN (tail);
 
 
  for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
  for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
    {
    {
      /* Handle register life information.  */
      /* Handle register life information.  */
      if (!INSN_P (insn))
      if (!INSN_P (insn))
        continue;
        continue;
 
 
      if (mode == SFmode)
      if (mode == SFmode)
        INSN_REGMODE_WEIGHT (insn, mode) =
        INSN_REGMODE_WEIGHT (insn, mode) =
          find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DFmode);
          find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DFmode);
      else if (mode == SImode)
      else if (mode == SImode)
        INSN_REGMODE_WEIGHT (insn, mode) =
        INSN_REGMODE_WEIGHT (insn, mode) =
          find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DImode);
          find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DImode);
    }
    }
}
}
 
 
/* Comparison function for ready queue sorting.  */
/* Comparison function for ready queue sorting.  */
static int
static int
rank_for_reorder (const void *x, const void *y)
rank_for_reorder (const void *x, const void *y)
{
{
  rtx tmp = *(const rtx *) y;
  rtx tmp = *(const rtx *) y;
  rtx tmp2 = *(const rtx *) x;
  rtx tmp2 = *(const rtx *) x;
 
 
  /* The insn in a schedule group should be issued the first.  */
  /* The insn in a schedule group should be issued the first.  */
  if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
  if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
    return SCHED_GROUP_P (tmp2) ? 1 : -1;
    return SCHED_GROUP_P (tmp2) ? 1 : -1;
 
 
  /* If insns are equally good, sort by INSN_LUID (original insn order), This
  /* If insns are equally good, sort by INSN_LUID (original insn order), This
     minimizes instruction movement, thus minimizing sched's effect on
     minimizes instruction movement, thus minimizing sched's effect on
     register pressure.  */
     register pressure.  */
  return INSN_LUID (tmp) - INSN_LUID (tmp2);
  return INSN_LUID (tmp) - INSN_LUID (tmp2);
}
}
 
 
/* Resort the array A in which only element at index N may be out of order.  */
/* Resort the array A in which only element at index N may be out of order.  */
static void
static void
swap_reorder (rtx *a, int n)
swap_reorder (rtx *a, int n)
{
{
  rtx insn = a[n - 1];
  rtx insn = a[n - 1];
  int i = n - 2;
  int i = n - 2;
 
 
  while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0)
  while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0)
    {
    {
      a[i + 1] = a[i];
      a[i + 1] = a[i];
      i -= 1;
      i -= 1;
    }
    }
  a[i + 1] = insn;
  a[i + 1] = insn;
}
}
 
 
#define SCHED_REORDER(READY, N_READY)                                   \
#define SCHED_REORDER(READY, N_READY)                                   \
  do                                                                    \
  do                                                                    \
    {                                                                   \
    {                                                                   \
      if ((N_READY) == 2)                                               \
      if ((N_READY) == 2)                                               \
        swap_reorder (READY, N_READY);                                  \
        swap_reorder (READY, N_READY);                                  \
      else if ((N_READY) > 2)                                           \
      else if ((N_READY) > 2)                                           \
        qsort (READY, N_READY, sizeof (rtx), rank_for_reorder);         \
        qsort (READY, N_READY, sizeof (rtx), rank_for_reorder);         \
    }                                                                   \
    }                                                                   \
  while (0)
  while (0)
 
 
/* Sort the ready list READY by ascending priority, using the SCHED_REORDER
/* Sort the ready list READY by ascending priority, using the SCHED_REORDER
   macro.  */
   macro.  */
static void
static void
ready_reorder (rtx *ready, int nready)
ready_reorder (rtx *ready, int nready)
{
{
  SCHED_REORDER (ready, nready);
  SCHED_REORDER (ready, nready);
}
}
 
 
/* Count life regions of r0 for a block.  */
/* Count life regions of r0 for a block.  */
static int
static int
find_r0_life_regions (basic_block b)
find_r0_life_regions (basic_block b)
{
{
  rtx end, insn;
  rtx end, insn;
  rtx pset;
  rtx pset;
  rtx r0_reg;
  rtx r0_reg;
  int live;
  int live;
  int set;
  int set;
  int death = 0;
  int death = 0;
 
 
  if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
  if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
    {
    {
      set = 1;
      set = 1;
      live = 1;
      live = 1;
    }
    }
  else
  else
    {
    {
      set = 0;
      set = 0;
      live = 0;
      live = 0;
    }
    }
 
 
  insn = BB_HEAD (b);
  insn = BB_HEAD (b);
  end = BB_END (b);
  end = BB_END (b);
  r0_reg = gen_rtx_REG (SImode, R0_REG);
  r0_reg = gen_rtx_REG (SImode, R0_REG);
  while (1)
  while (1)
    {
    {
      if (INSN_P (insn))
      if (INSN_P (insn))
        {
        {
          if (find_regno_note (insn, REG_DEAD, R0_REG))
          if (find_regno_note (insn, REG_DEAD, R0_REG))
            {
            {
              death++;
              death++;
              live = 0;
              live = 0;
            }
            }
          if (!live
          if (!live
              && (pset = single_set (insn))
              && (pset = single_set (insn))
              && reg_overlap_mentioned_p (r0_reg, SET_DEST (pset))
              && reg_overlap_mentioned_p (r0_reg, SET_DEST (pset))
              && !find_regno_note (insn, REG_UNUSED, R0_REG))
              && !find_regno_note (insn, REG_UNUSED, R0_REG))
            {
            {
              set++;
              set++;
              live = 1;
              live = 1;
            }
            }
        }
        }
      if (insn == end)
      if (insn == end)
        break;
        break;
      insn = NEXT_INSN (insn);
      insn = NEXT_INSN (insn);
    }
    }
  return set - death;
  return set - death;
}
}
 
 
/* Calculate regmode weights for all insns of all basic block.  */
/* Calculate regmode weights for all insns of all basic block.  */
static void
static void
sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
                   int verbose ATTRIBUTE_UNUSED,
                   int verbose ATTRIBUTE_UNUSED,
                   int old_max_uid)
                   int old_max_uid)
{
{
  basic_block b;
  basic_block b;
 
 
  regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short));
  regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short));
  regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short));
  regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short));
  r0_life_regions = 0;
  r0_life_regions = 0;
 
 
  FOR_EACH_BB_REVERSE (b)
  FOR_EACH_BB_REVERSE (b)
  {
  {
    find_regmode_weight (b, SImode);
    find_regmode_weight (b, SImode);
    find_regmode_weight (b, SFmode);
    find_regmode_weight (b, SFmode);
    if (!reload_completed)
    if (!reload_completed)
      r0_life_regions += find_r0_life_regions (b);
      r0_life_regions += find_r0_life_regions (b);
  }
  }
 
 
  CURR_REGMODE_PRESSURE (SImode) = 0;
  CURR_REGMODE_PRESSURE (SImode) = 0;
  CURR_REGMODE_PRESSURE (SFmode) = 0;
  CURR_REGMODE_PRESSURE (SFmode) = 0;
 
 
}
}
 
 
/* Cleanup.  */
/* Cleanup.  */
static void
static void
sh_md_finish_global (FILE *dump ATTRIBUTE_UNUSED,
sh_md_finish_global (FILE *dump ATTRIBUTE_UNUSED,
                     int verbose ATTRIBUTE_UNUSED)
                     int verbose ATTRIBUTE_UNUSED)
{
{
  if (regmode_weight[0])
  if (regmode_weight[0])
    {
    {
      free (regmode_weight[0]);
      free (regmode_weight[0]);
      regmode_weight[0] = NULL;
      regmode_weight[0] = NULL;
    }
    }
  if (regmode_weight[1])
  if (regmode_weight[1])
    {
    {
      free (regmode_weight[1]);
      free (regmode_weight[1]);
      regmode_weight[1] = NULL;
      regmode_weight[1] = NULL;
    }
    }
}
}
 
 
/* The scalar modes supported differs from the default version in TImode
/* The scalar modes supported differs from the default version in TImode
   for 32-bit SHMEDIA.  */
   for 32-bit SHMEDIA.  */
static bool
static bool
sh_scalar_mode_supported_p (enum machine_mode mode)
sh_scalar_mode_supported_p (enum machine_mode mode)
{
{
  if (TARGET_SHMEDIA32 && mode == TImode)
  if (TARGET_SHMEDIA32 && mode == TImode)
    return false;
    return false;
 
 
  return default_scalar_mode_supported_p (mode);
  return default_scalar_mode_supported_p (mode);
}
}
 
 
/* Cache the can_issue_more so that we can return it from reorder2. Also,
/* Cache the can_issue_more so that we can return it from reorder2. Also,
   keep count of register pressures on SImode and SFmode. */
   keep count of register pressures on SImode and SFmode. */
static int
static int
sh_variable_issue (FILE *dump ATTRIBUTE_UNUSED,
sh_variable_issue (FILE *dump ATTRIBUTE_UNUSED,
                   int sched_verbose ATTRIBUTE_UNUSED,
                   int sched_verbose ATTRIBUTE_UNUSED,
                   rtx insn,
                   rtx insn,
                   int can_issue_more)
                   int can_issue_more)
{
{
  if (GET_CODE (PATTERN (insn)) != USE
  if (GET_CODE (PATTERN (insn)) != USE
      && GET_CODE (PATTERN (insn)) != CLOBBER)
      && GET_CODE (PATTERN (insn)) != CLOBBER)
    cached_can_issue_more = can_issue_more - 1;
    cached_can_issue_more = can_issue_more - 1;
  else
  else
    cached_can_issue_more = can_issue_more;
    cached_can_issue_more = can_issue_more;
 
 
  if (reload_completed)
  if (reload_completed)
    return cached_can_issue_more;
    return cached_can_issue_more;
 
 
  CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode);
  CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode);
  CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode);
  CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode);
 
 
  return cached_can_issue_more;
  return cached_can_issue_more;
}
}
 
 
static void
static void
sh_md_init (FILE *dump ATTRIBUTE_UNUSED,
sh_md_init (FILE *dump ATTRIBUTE_UNUSED,
            int verbose ATTRIBUTE_UNUSED,
            int verbose ATTRIBUTE_UNUSED,
            int veclen ATTRIBUTE_UNUSED)
            int veclen ATTRIBUTE_UNUSED)
{
{
  CURR_REGMODE_PRESSURE (SImode) = 0;
  CURR_REGMODE_PRESSURE (SImode) = 0;
  CURR_REGMODE_PRESSURE (SFmode) = 0;
  CURR_REGMODE_PRESSURE (SFmode) = 0;
}
}
 
 
/* Some magic numbers.  */
/* Some magic numbers.  */
/* Pressure on register r0 can lead to spill failures. so avoid sched1 for
/* Pressure on register r0 can lead to spill failures. so avoid sched1 for
   functions that already have high pressure on r0. */
   functions that already have high pressure on r0. */
#define R0_MAX_LIFE_REGIONS 2
#define R0_MAX_LIFE_REGIONS 2
/* Register Pressure thresholds for SImode and SFmode registers.  */
/* Register Pressure thresholds for SImode and SFmode registers.  */
#define SIMODE_MAX_WEIGHT 5
#define SIMODE_MAX_WEIGHT 5
#define SFMODE_MAX_WEIGHT 10
#define SFMODE_MAX_WEIGHT 10
 
 
/* Return true if the pressure is high for MODE.  */
/* Return true if the pressure is high for MODE.  */
static short
static short
high_pressure (enum machine_mode mode)
high_pressure (enum machine_mode mode)
{
{
  /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
  /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
     functions that already have high pressure on r0. */
     functions that already have high pressure on r0. */
   if (r0_life_regions >= R0_MAX_LIFE_REGIONS)
   if (r0_life_regions >= R0_MAX_LIFE_REGIONS)
     return 1;
     return 1;
 
 
  if (mode == SFmode)
  if (mode == SFmode)
    return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT);
    return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT);
  else
  else
    return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT);
    return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT);
}
}
 
 
/* Reorder ready queue if register pressure is high.  */
/* Reorder ready queue if register pressure is high.  */
static int
static int
sh_reorder (FILE *dump ATTRIBUTE_UNUSED,
sh_reorder (FILE *dump ATTRIBUTE_UNUSED,
            int sched_verbose ATTRIBUTE_UNUSED,
            int sched_verbose ATTRIBUTE_UNUSED,
            rtx *ready,
            rtx *ready,
            int *n_readyp,
            int *n_readyp,
            int clock_var ATTRIBUTE_UNUSED)
            int clock_var ATTRIBUTE_UNUSED)
{
{
  if (reload_completed)
  if (reload_completed)
    return sh_issue_rate ();
    return sh_issue_rate ();
 
 
  if (high_pressure (SFmode) || high_pressure (SImode))
  if (high_pressure (SFmode) || high_pressure (SImode))
    {
    {
      ready_reorder (ready, *n_readyp);
      ready_reorder (ready, *n_readyp);
    }
    }
 
 
  return sh_issue_rate ();
  return sh_issue_rate ();
}
}
 
 
/* Skip cycles if the current register pressure is high.  */
/* Skip cycles if the current register pressure is high.  */
static int
static int
sh_reorder2 (FILE *dump ATTRIBUTE_UNUSED,
sh_reorder2 (FILE *dump ATTRIBUTE_UNUSED,
             int sched_verbose ATTRIBUTE_UNUSED,
             int sched_verbose ATTRIBUTE_UNUSED,
             rtx *ready ATTRIBUTE_UNUSED,
             rtx *ready ATTRIBUTE_UNUSED,
             int *n_readyp ATTRIBUTE_UNUSED,
             int *n_readyp ATTRIBUTE_UNUSED,
             int clock_var ATTRIBUTE_UNUSED)
             int clock_var ATTRIBUTE_UNUSED)
{
{
  if (reload_completed)
  if (reload_completed)
    return cached_can_issue_more;
    return cached_can_issue_more;
 
 
  if (high_pressure(SFmode) || high_pressure (SImode))
  if (high_pressure(SFmode) || high_pressure (SImode))
    skip_cycles = 1;
    skip_cycles = 1;
 
 
  return cached_can_issue_more;
  return cached_can_issue_more;
}
}
 
 
/* Skip cycles without sorting the ready queue. This will move insn from
/* 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
   Q->R. If this is the last cycle we are skipping; allow sorting of ready
   queue by sh_reorder.  */
   queue by sh_reorder.  */
 
 
/* Generally, skipping these many cycles are sufficient for all insns to move
/* Generally, skipping these many cycles are sufficient for all insns to move
   from Q -> R.  */
   from Q -> R.  */
#define MAX_SKIPS 8
#define MAX_SKIPS 8
 
 
static int
static int
sh_dfa_new_cycle (FILE *sched_dump ATTRIBUTE_UNUSED,
sh_dfa_new_cycle (FILE *sched_dump ATTRIBUTE_UNUSED,
                  int sched_verbose ATTRIBUTE_UNUSED,
                  int sched_verbose ATTRIBUTE_UNUSED,
                  rtx insn ATTRIBUTE_UNUSED,
                  rtx insn ATTRIBUTE_UNUSED,
                  int last_clock_var,
                  int last_clock_var,
                  int clock_var,
                  int clock_var,
                  int *sort_p)
                  int *sort_p)
{
{
  if (reload_completed)
  if (reload_completed)
    return 0;
    return 0;
 
 
  if (skip_cycles)
  if (skip_cycles)
    {
    {
      if ((clock_var - last_clock_var) < MAX_SKIPS)
      if ((clock_var - last_clock_var) < MAX_SKIPS)
        {
        {
          *sort_p = 0;
          *sort_p = 0;
          return 1;
          return 1;
        }
        }
      /* If this is the last cycle we are skipping, allow reordering of R.  */
      /* If this is the last cycle we are skipping, allow reordering of R.  */
      if ((clock_var - last_clock_var) == MAX_SKIPS)
      if ((clock_var - last_clock_var) == MAX_SKIPS)
        {
        {
          *sort_p = 1;
          *sort_p = 1;
          return 1;
          return 1;
        }
        }
    }
    }
 
 
  skip_cycles = 0;
  skip_cycles = 0;
 
 
  return 0;
  return 0;
}
}
 
 
/* SHmedia requires registers for branches, so we can't generate new
/* SHmedia requires registers for branches, so we can't generate new
   branches past reload.  */
   branches past reload.  */
static bool
static bool
sh_cannot_modify_jumps_p (void)
sh_cannot_modify_jumps_p (void)
{
{
  return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
  return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
}
}
 
 
static enum reg_class
static enum reg_class
sh_target_reg_class (void)
sh_target_reg_class (void)
{
{
  return TARGET_SHMEDIA ? TARGET_REGS : NO_REGS;
  return TARGET_SHMEDIA ? TARGET_REGS : NO_REGS;
}
}
 
 
static bool
static bool
sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen)
sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen)
{
{
  HARD_REG_SET dummy;
  HARD_REG_SET dummy;
#if 0
#if 0
  rtx insn;
  rtx insn;
#endif
#endif
 
 
  if (! shmedia_space_reserved_for_target_registers)
  if (! shmedia_space_reserved_for_target_registers)
    return 0;
    return 0;
  if (after_prologue_epilogue_gen && ! TARGET_SAVE_ALL_TARGET_REGS)
  if (after_prologue_epilogue_gen && ! TARGET_SAVE_ALL_TARGET_REGS)
    return 0;
    return 0;
  if (calc_live_regs (&dummy) >= 6 * 8)
  if (calc_live_regs (&dummy) >= 6 * 8)
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
static bool
static bool
sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
{
{
  return (TARGET_SH5 || TARGET_HITACHI || sh_attr_renesas_p (record_type));
  return (TARGET_SH5 || TARGET_HITACHI || sh_attr_renesas_p (record_type));
}
}


/*
/*
   On the SH1..SH4, the trampoline looks like
   On the SH1..SH4, the trampoline looks like
   2 0002 D202                  mov.l   l2,r2
   2 0002 D202                  mov.l   l2,r2
   1 0000 D301                  mov.l   l1,r3
   1 0000 D301                  mov.l   l1,r3
   3 0004 422B                  jmp     @r2
   3 0004 422B                  jmp     @r2
   4 0006 0009                  nop
   4 0006 0009                  nop
   5 0008 00000000      l1:     .long   area
   5 0008 00000000      l1:     .long   area
   6 000c 00000000      l2:     .long   function
   6 000c 00000000      l2:     .long   function
 
 
   SH5 (compact) uses r1 instead of r3 for the static chain.  */
   SH5 (compact) uses r1 instead of r3 for the static chain.  */
 
 
 
 
/* Emit RTL insns to initialize the variable parts of a trampoline.
/* Emit RTL insns to initialize the variable parts of a trampoline.
   FNADDR is an RTX for the address of the function's pure code.
   FNADDR is an RTX for the address of the function's pure code.
   CXT is an RTX for the static chain value for the function.  */
   CXT is an RTX for the static chain value for the function.  */
 
 
static void
static void
sh_trampoline_init (rtx tramp_mem, tree fndecl, rtx cxt)
sh_trampoline_init (rtx tramp_mem, tree fndecl, rtx cxt)
{
{
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx tramp = force_reg (Pmode, XEXP (tramp_mem, 0));
  rtx tramp = force_reg (Pmode, XEXP (tramp_mem, 0));
 
 
  if (TARGET_SHMEDIA64)
  if (TARGET_SHMEDIA64)
    {
    {
      rtx tramp_templ;
      rtx tramp_templ;
      int fixed_len;
      int fixed_len;
 
 
      rtx movi1 = GEN_INT (0xcc000010);
      rtx movi1 = GEN_INT (0xcc000010);
      rtx shori1 = GEN_INT (0xc8000010);
      rtx shori1 = GEN_INT (0xc8000010);
      rtx src, dst;
      rtx src, dst;
 
 
      /* The following trampoline works within a +- 128 KB range for cxt:
      /* The following trampoline works within a +- 128 KB range for cxt:
         ptb/u cxt,tr1; movi fnaddr >> 48,r0; shori fnaddr >> 32,r0;
         ptb/u cxt,tr1; movi fnaddr >> 48,r0; shori fnaddr >> 32,r0;
         shori fnaddr >> 16,r0; shori fnaddr,r0; ptabs/l r0,tr0
         shori fnaddr >> 16,r0; shori fnaddr,r0; ptabs/l r0,tr0
         gettr tr1,r1; blink tr0,r63  */
         gettr tr1,r1; blink tr0,r63  */
      /* Address rounding makes it hard to compute the exact bounds of the
      /* Address rounding makes it hard to compute the exact bounds of the
         offset for this trampoline, but we have a rather generous offset
         offset for this trampoline, but we have a rather generous offset
         range, so frame_offset should do fine as an upper bound.  */
         range, so frame_offset should do fine as an upper bound.  */
      if (cxt == virtual_stack_vars_rtx && frame_offset < 0x20000)
      if (cxt == virtual_stack_vars_rtx && frame_offset < 0x20000)
        {
        {
          /* ??? could optimize this trampoline initialization
          /* ??? could optimize this trampoline initialization
             by writing DImode words with two insns each.  */
             by writing DImode words with two insns each.  */
          rtx mask = force_reg (DImode, GEN_INT (0x3fffc00));
          rtx mask = force_reg (DImode, GEN_INT (0x3fffc00));
          rtx insn = gen_rtx_MINUS (DImode, cxt, tramp);
          rtx insn = gen_rtx_MINUS (DImode, cxt, tramp);
          insn = gen_rtx_ASHIFT (DImode, insn, GEN_INT (10-2));
          insn = gen_rtx_ASHIFT (DImode, insn, GEN_INT (10-2));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = gen_rtx_AND (DImode, insn, mask);
          /* Or in ptb/u .,tr1 pattern */
          /* Or in ptb/u .,tr1 pattern */
          insn = gen_rtx_IOR (DImode, insn, gen_int_mode (0xec000010, SImode));
          insn = gen_rtx_IOR (DImode, insn, gen_int_mode (0xec000010, SImode));
          insn = force_operand (insn, NULL_RTX);
          insn = force_operand (insn, NULL_RTX);
          insn = gen_lowpart (SImode, insn);
          insn = gen_lowpart (SImode, insn);
          emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX), insn);
          emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX), insn);
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (38));
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (38));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, movi1, insn), NULL_RTX);
          insn = force_operand (gen_rtx_IOR (DImode, movi1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
          insn = gen_lowpart (SImode, insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 4), insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 4), insn);
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (22));
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (22));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
          insn = gen_lowpart (SImode, insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 8), insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 8), insn);
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (6));
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (6));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
          insn = gen_lowpart (SImode, insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 12), insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 12), insn);
          insn = gen_rtx_ASHIFT (DImode, fnaddr, GEN_INT (10));
          insn = gen_rtx_ASHIFT (DImode, fnaddr, GEN_INT (10));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
          insn = gen_lowpart (SImode, insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 16), insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 16), insn);
          emit_move_insn (adjust_address (tramp_mem, SImode, 20),
          emit_move_insn (adjust_address (tramp_mem, SImode, 20),
                          GEN_INT (0x6bf10600));
                          GEN_INT (0x6bf10600));
          emit_move_insn (adjust_address (tramp_mem, SImode, 24),
          emit_move_insn (adjust_address (tramp_mem, SImode, 24),
                          GEN_INT (0x4415fc10));
                          GEN_INT (0x4415fc10));
          emit_move_insn (adjust_address (tramp_mem, SImode, 28),
          emit_move_insn (adjust_address (tramp_mem, SImode, 28),
                          GEN_INT (0x4401fff0));
                          GEN_INT (0x4401fff0));
          emit_insn (gen_ic_invalidate_line (tramp));
          emit_insn (gen_ic_invalidate_line (tramp));
          return;
          return;
        }
        }
      tramp_templ = gen_rtx_SYMBOL_REF (Pmode,"__GCC_nested_trampoline");
      tramp_templ = gen_rtx_SYMBOL_REF (Pmode,"__GCC_nested_trampoline");
      fixed_len = TRAMPOLINE_SIZE - 2 * GET_MODE_SIZE (Pmode);
      fixed_len = TRAMPOLINE_SIZE - 2 * GET_MODE_SIZE (Pmode);
 
 
      tramp_templ = gen_datalabel_ref (tramp_templ);
      tramp_templ = gen_datalabel_ref (tramp_templ);
      dst = tramp_mem;
      dst = tramp_mem;
      src = gen_const_mem (BLKmode, tramp_templ);
      src = gen_const_mem (BLKmode, tramp_templ);
      set_mem_align (dst, 256);
      set_mem_align (dst, 256);
      set_mem_align (src, 64);
      set_mem_align (src, 64);
      emit_block_move (dst, src, GEN_INT (fixed_len), BLOCK_OP_NORMAL);
      emit_block_move (dst, src, GEN_INT (fixed_len), BLOCK_OP_NORMAL);
 
 
      emit_move_insn (adjust_address (tramp_mem, Pmode, fixed_len), fnaddr);
      emit_move_insn (adjust_address (tramp_mem, Pmode, fixed_len), fnaddr);
      emit_move_insn (adjust_address (tramp_mem, Pmode,
      emit_move_insn (adjust_address (tramp_mem, Pmode,
                                      fixed_len + GET_MODE_SIZE (Pmode)),
                                      fixed_len + GET_MODE_SIZE (Pmode)),
                      cxt);
                      cxt);
      emit_insn (gen_ic_invalidate_line (tramp));
      emit_insn (gen_ic_invalidate_line (tramp));
      return;
      return;
    }
    }
  else if (TARGET_SHMEDIA)
  else if (TARGET_SHMEDIA)
    {
    {
      /* movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
      /* movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
         movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63  */
         movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63  */
      rtx quad0 = gen_reg_rtx (DImode), cxtload = gen_reg_rtx (DImode);
      rtx quad0 = gen_reg_rtx (DImode), cxtload = gen_reg_rtx (DImode);
      rtx quad1 = gen_reg_rtx (DImode), quad2 = gen_reg_rtx (DImode);
      rtx quad1 = gen_reg_rtx (DImode), quad2 = gen_reg_rtx (DImode);
      /* movi 0,r1: 0xcc000010 shori 0,r1: c8000010  concatenated,
      /* movi 0,r1: 0xcc000010 shori 0,r1: c8000010  concatenated,
         rotated 10 right, and higher 16 bit of every 32 selected.  */
         rotated 10 right, and higher 16 bit of every 32 selected.  */
      rtx movishori
      rtx movishori
        = force_reg (V2HImode, (simplify_gen_subreg
        = force_reg (V2HImode, (simplify_gen_subreg
                                (V2HImode, GEN_INT (0x4330432), SImode, 0)));
                                (V2HImode, GEN_INT (0x4330432), SImode, 0)));
      rtx ptabs = force_reg (DImode, GEN_INT (0x6bf10600));
      rtx ptabs = force_reg (DImode, GEN_INT (0x6bf10600));
      rtx blink = force_reg (DImode, GEN_INT (0x4401fff0));
      rtx blink = force_reg (DImode, GEN_INT (0x4401fff0));
 
 
      fnaddr = force_reg (SImode, fnaddr);
      fnaddr = force_reg (SImode, fnaddr);
      cxt = force_reg (SImode, cxt);
      cxt = force_reg (SImode, cxt);
      emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, quad0, 0),
      emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, quad0, 0),
                                 gen_rtx_SUBREG (V2HImode, fnaddr, 0),
                                 gen_rtx_SUBREG (V2HImode, fnaddr, 0),
                                 movishori));
                                 movishori));
      emit_insn (gen_rotrdi3_mextr (quad0, quad0,
      emit_insn (gen_rotrdi3_mextr (quad0, quad0,
                                    GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
                                    GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
      emit_insn (gen_ashldi3_media (quad0, quad0, const2_rtx));
      emit_insn (gen_ashldi3_media (quad0, quad0, const2_rtx));
      emit_move_insn (change_address (tramp_mem, DImode, NULL_RTX), quad0);
      emit_move_insn (change_address (tramp_mem, DImode, NULL_RTX), quad0);
      emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0),
      emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0),
                                 gen_rtx_SUBREG (V2HImode, cxt, 0),
                                 gen_rtx_SUBREG (V2HImode, cxt, 0),
                                 movishori));
                                 movishori));
      emit_insn (gen_rotrdi3_mextr (cxtload, cxtload,
      emit_insn (gen_rotrdi3_mextr (cxtload, cxtload,
                                    GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
                                    GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
      emit_insn (gen_ashldi3_media (cxtload, cxtload, const2_rtx));
      emit_insn (gen_ashldi3_media (cxtload, cxtload, const2_rtx));
      if (TARGET_LITTLE_ENDIAN)
      if (TARGET_LITTLE_ENDIAN)
        {
        {
          emit_insn (gen_mshflo_l_di (quad1, ptabs, cxtload));
          emit_insn (gen_mshflo_l_di (quad1, ptabs, cxtload));
          emit_insn (gen_mextr4 (quad2, cxtload, blink));
          emit_insn (gen_mextr4 (quad2, cxtload, blink));
        }
        }
      else
      else
        {
        {
          emit_insn (gen_mextr4 (quad1, cxtload, ptabs));
          emit_insn (gen_mextr4 (quad1, cxtload, ptabs));
          emit_insn (gen_mshflo_l_di (quad2, blink, cxtload));
          emit_insn (gen_mshflo_l_di (quad2, blink, cxtload));
        }
        }
      emit_move_insn (adjust_address (tramp_mem, DImode, 8), quad1);
      emit_move_insn (adjust_address (tramp_mem, DImode, 8), quad1);
      emit_move_insn (adjust_address (tramp_mem, DImode, 16), quad2);
      emit_move_insn (adjust_address (tramp_mem, DImode, 16), quad2);
      emit_insn (gen_ic_invalidate_line (tramp));
      emit_insn (gen_ic_invalidate_line (tramp));
      return;
      return;
    }
    }
  else if (TARGET_SHCOMPACT)
  else if (TARGET_SHCOMPACT)
    {
    {
      emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
      emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
      return;
      return;
    }
    }
  emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX),
  emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX),
                  gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
                  gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
                                SImode));
                                SImode));
  emit_move_insn (adjust_address (tramp_mem, SImode, 4),
  emit_move_insn (adjust_address (tramp_mem, SImode, 4),
                  gen_int_mode (TARGET_LITTLE_ENDIAN ? 0x0009422b : 0x422b0009,
                  gen_int_mode (TARGET_LITTLE_ENDIAN ? 0x0009422b : 0x422b0009,
                                SImode));
                                SImode));
  emit_move_insn (adjust_address (tramp_mem, SImode, 8), cxt);
  emit_move_insn (adjust_address (tramp_mem, SImode, 8), cxt);
  emit_move_insn (adjust_address (tramp_mem, SImode, 12), fnaddr);
  emit_move_insn (adjust_address (tramp_mem, SImode, 12), fnaddr);
  if (TARGET_HARVARD)
  if (TARGET_HARVARD)
    {
    {
      if (!TARGET_INLINE_IC_INVALIDATE
      if (!TARGET_INLINE_IC_INVALIDATE
          || (!(TARGET_SH4A_ARCH || TARGET_SH4_300) && TARGET_USERMODE))
          || (!(TARGET_SH4A_ARCH || TARGET_SH4_300) && TARGET_USERMODE))
        emit_library_call (function_symbol (NULL, "__ic_invalidate",
        emit_library_call (function_symbol (NULL, "__ic_invalidate",
                                            FUNCTION_ORDINARY),
                                            FUNCTION_ORDINARY),
                           LCT_NORMAL, VOIDmode, 1, tramp, SImode);
                           LCT_NORMAL, VOIDmode, 1, tramp, SImode);
      else
      else
        emit_insn (gen_ic_invalidate_line (tramp));
        emit_insn (gen_ic_invalidate_line (tramp));
    }
    }
}
}
 
 
/* On SH5, trampolines are SHmedia code, so add 1 to the address.  */
/* On SH5, trampolines are SHmedia code, so add 1 to the address.  */
 
 
static rtx
static rtx
sh_trampoline_adjust_address (rtx tramp)
sh_trampoline_adjust_address (rtx tramp)
{
{
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    tramp = expand_simple_binop (Pmode, PLUS, tramp, const1_rtx,
    tramp = expand_simple_binop (Pmode, PLUS, tramp, const1_rtx,
                                 gen_reg_rtx (Pmode), 0, OPTAB_LIB_WIDEN);
                                 gen_reg_rtx (Pmode), 0, OPTAB_LIB_WIDEN);
  return tramp;
  return tramp;
}
}
 
 
/* FIXME: This is overly conservative.  A SHcompact function that
/* FIXME: This is overly conservative.  A SHcompact function that
   receives arguments ``by reference'' will have them stored in its
   receives arguments ``by reference'' will have them stored in its
   own stack frame, so it must not pass pointers or references to
   own stack frame, so it must not pass pointers or references to
   these arguments to other functions by means of sibling calls.  */
   these arguments to other functions by means of sibling calls.  */
/* If PIC, we cannot make sibling calls to global functions
/* If PIC, we cannot make sibling calls to global functions
   because the PLT requires r12 to be live.  */
   because the PLT requires r12 to be live.  */
static bool
static bool
sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
{
  return (1
  return (1
          && (! TARGET_SHCOMPACT
          && (! TARGET_SHCOMPACT
              || crtl->args.info.stack_regs == 0)
              || crtl->args.info.stack_regs == 0)
          && ! sh_cfun_interrupt_handler_p ()
          && ! sh_cfun_interrupt_handler_p ()
          && (! flag_pic
          && (! flag_pic
              || (decl && ! TREE_PUBLIC (decl))
              || (decl && ! TREE_PUBLIC (decl))
              || (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
              || (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
}
}


/* Machine specific built-in functions.  */
/* Machine specific built-in functions.  */
 
 
struct builtin_description
struct builtin_description
{
{
  const enum insn_code icode;
  const enum insn_code icode;
  const char *const name;
  const char *const name;
  int signature;
  int signature;
  tree fndecl;
  tree fndecl;
};
};
 
 
/* describe number and signedness of arguments; arg[0] == result
/* describe number and signedness of arguments; arg[0] == result
   (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
   (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
/* 9: 64-bit pointer, 10: 32-bit pointer */
/* 9: 64-bit pointer, 10: 32-bit pointer */
static const char signature_args[][4] =
static const char signature_args[][4] =
{
{
#define SH_BLTIN_V2SI2 0
#define SH_BLTIN_V2SI2 0
  { 4, 4 },
  { 4, 4 },
#define SH_BLTIN_V4HI2 1
#define SH_BLTIN_V4HI2 1
  { 4, 4 },
  { 4, 4 },
#define SH_BLTIN_V2SI3 2
#define SH_BLTIN_V2SI3 2
  { 4, 4, 4 },
  { 4, 4, 4 },
#define SH_BLTIN_V4HI3 3
#define SH_BLTIN_V4HI3 3
  { 4, 4, 4 },
  { 4, 4, 4 },
#define SH_BLTIN_V8QI3 4
#define SH_BLTIN_V8QI3 4
  { 4, 4, 4 },
  { 4, 4, 4 },
#define SH_BLTIN_MAC_HISI 5
#define SH_BLTIN_MAC_HISI 5
  { 1, 4, 4, 1 },
  { 1, 4, 4, 1 },
#define SH_BLTIN_SH_HI 6
#define SH_BLTIN_SH_HI 6
  { 4, 4, 1 },
  { 4, 4, 1 },
#define SH_BLTIN_SH_SI 7
#define SH_BLTIN_SH_SI 7
  { 4, 4, 1 },
  { 4, 4, 1 },
#define SH_BLTIN_V4HI2V2SI 8
#define SH_BLTIN_V4HI2V2SI 8
  { 4, 4, 4 },
  { 4, 4, 4 },
#define SH_BLTIN_V4HI2V8QI 9
#define SH_BLTIN_V4HI2V8QI 9
  { 4, 4, 4 },
  { 4, 4, 4 },
#define SH_BLTIN_SISF 10
#define SH_BLTIN_SISF 10
  { 4, 2 },
  { 4, 2 },
#define SH_BLTIN_LDUA_L 11
#define SH_BLTIN_LDUA_L 11
  { 2, 10 },
  { 2, 10 },
#define SH_BLTIN_LDUA_Q 12
#define SH_BLTIN_LDUA_Q 12
  { 1, 10 },
  { 1, 10 },
#define SH_BLTIN_STUA_L 13
#define SH_BLTIN_STUA_L 13
  { 0, 10, 2 },
  { 0, 10, 2 },
#define SH_BLTIN_STUA_Q 14
#define SH_BLTIN_STUA_Q 14
  { 0, 10, 1 },
  { 0, 10, 1 },
#define SH_BLTIN_LDUA_L64 15
#define SH_BLTIN_LDUA_L64 15
  { 2, 9 },
  { 2, 9 },
#define SH_BLTIN_LDUA_Q64 16
#define SH_BLTIN_LDUA_Q64 16
  { 1, 9 },
  { 1, 9 },
#define SH_BLTIN_STUA_L64 17
#define SH_BLTIN_STUA_L64 17
  { 0, 9, 2 },
  { 0, 9, 2 },
#define SH_BLTIN_STUA_Q64 18
#define SH_BLTIN_STUA_Q64 18
  { 0, 9, 1 },
  { 0, 9, 1 },
#define SH_BLTIN_NUM_SHARED_SIGNATURES 19
#define SH_BLTIN_NUM_SHARED_SIGNATURES 19
#define SH_BLTIN_2 19
#define SH_BLTIN_2 19
#define SH_BLTIN_SU 19
#define SH_BLTIN_SU 19
  { 1, 2 },
  { 1, 2 },
#define SH_BLTIN_3 20
#define SH_BLTIN_3 20
#define SH_BLTIN_SUS 20
#define SH_BLTIN_SUS 20
  { 2, 2, 1 },
  { 2, 2, 1 },
#define SH_BLTIN_PSSV 21
#define SH_BLTIN_PSSV 21
  { 0, 8, 2, 2 },
  { 0, 8, 2, 2 },
#define SH_BLTIN_XXUU 22
#define SH_BLTIN_XXUU 22
#define SH_BLTIN_UUUU 22
#define SH_BLTIN_UUUU 22
  { 1, 1, 1, 1 },
  { 1, 1, 1, 1 },
#define SH_BLTIN_PV 23
#define SH_BLTIN_PV 23
  { 0, 8 },
  { 0, 8 },
};
};
/* mcmv: operands considered unsigned.  */
/* mcmv: operands considered unsigned.  */
/* mmulsum_wq, msad_ubq: result considered unsigned long long.  */
/* mmulsum_wq, msad_ubq: result considered unsigned long long.  */
/* mperm: control value considered unsigned int.  */
/* mperm: control value considered unsigned int.  */
/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int.  */
/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int.  */
/* mshards_q: returns signed short.  */
/* mshards_q: returns signed short.  */
/* nsb: takes long long arg, returns unsigned char.  */
/* nsb: takes long long arg, returns unsigned char.  */
static struct builtin_description bdesc[] =
static struct builtin_description bdesc[] =
{
{
  { CODE_FOR_absv2si2,  "__builtin_absv2si2", SH_BLTIN_V2SI2, 0 },
  { CODE_FOR_absv2si2,  "__builtin_absv2si2", SH_BLTIN_V2SI2, 0 },
  { CODE_FOR_absv4hi2,  "__builtin_absv4hi2", SH_BLTIN_V4HI2, 0 },
  { CODE_FOR_absv4hi2,  "__builtin_absv4hi2", SH_BLTIN_V4HI2, 0 },
  { CODE_FOR_addv2si3,  "__builtin_addv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_addv2si3,  "__builtin_addv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_addv4hi3,  "__builtin_addv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_addv4hi3,  "__builtin_addv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_alloco_i,  "__builtin_sh_media_ALLOCO", SH_BLTIN_PV, 0 },
  { CODE_FOR_alloco_i,  "__builtin_sh_media_ALLOCO", SH_BLTIN_PV, 0 },
  { CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mcmv,      "__builtin_sh_media_MCMV", SH_BLTIN_UUUU, 0 },
  { CODE_FOR_mcmv,      "__builtin_sh_media_MCMV", SH_BLTIN_UUUU, 0 },
  { CODE_FOR_mcnvs_lw,  "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3, 0 },
  { CODE_FOR_mcnvs_lw,  "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3, 0 },
  { CODE_FOR_mcnvs_wb,  "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI, 0 },
  { CODE_FOR_mcnvs_wb,  "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI, 0 },
  { CODE_FOR_mcnvs_wub, "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI, 0 },
  { CODE_FOR_mcnvs_wub, "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI, 0 },
  { CODE_FOR_mextr1,    "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr1,    "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr2,    "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr2,    "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr3,    "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr3,    "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr4,    "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr4,    "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr5,    "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr5,    "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr6,    "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr6,    "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr7,    "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mextr7,    "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mmacfx_wl, "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI, 0 },
  { CODE_FOR_mmacfx_wl, "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI, 0 },
  { CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI, 0 },
  { CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI, 0 },
  { CODE_FOR_mulv2si3,  "__builtin_mulv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mulv2si3,  "__builtin_mulv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mulv4hi3,  "__builtin_mulv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mulv4hi3,  "__builtin_mulv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mmulfx_l,  "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mmulfx_l,  "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mmulfx_w,  "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mmulfx_w,  "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mmulhi_wl, "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI, 0 },
  { CODE_FOR_mmulhi_wl, "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI, 0 },
  { CODE_FOR_mmullo_wl, "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI, 0 },
  { CODE_FOR_mmullo_wl, "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI, 0 },
  { CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU, 0 },
  { CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU, 0 },
  { CODE_FOR_mperm_w,   "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_mperm_w,   "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_msad_ubq,  "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU, 0 },
  { CODE_FOR_msad_ubq,  "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU, 0 },
  { CODE_FOR_mshalds_l, "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_mshalds_l, "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_mshalds_w, "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_mshalds_w, "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_ashrv2si3, "__builtin_ashrv2si3", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_ashrv2si3, "__builtin_ashrv2si3", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_ashrv4hi3, "__builtin_ashrv4hi3", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_ashrv4hi3, "__builtin_ashrv4hi3", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_mshards_q, "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS, 0 },
  { CODE_FOR_mshards_q, "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS, 0 },
  { CODE_FOR_mshfhi_b,  "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mshfhi_b,  "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mshfhi_l,  "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mshfhi_l,  "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mshfhi_w,  "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mshfhi_w,  "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mshflo_b,  "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mshflo_b,  "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_mshflo_l,  "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mshflo_l,  "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_mshflo_w,  "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_mshflo_w,  "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_ashlv2si3, "__builtin_ashlv2si3", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_ashlv2si3, "__builtin_ashlv2si3", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_ashlv4hi3, "__builtin_ashlv4hi3", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_ashlv4hi3, "__builtin_ashlv4hi3", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_lshrv2si3, "__builtin_lshrv2si3", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_lshrv2si3, "__builtin_lshrv2si3", SH_BLTIN_SH_SI, 0 },
  { CODE_FOR_lshrv4hi3, "__builtin_lshrv4hi3", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_lshrv4hi3, "__builtin_lshrv4hi3", SH_BLTIN_SH_HI, 0 },
  { CODE_FOR_subv2si3,  "__builtin_subv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_subv2si3,  "__builtin_subv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_subv4hi3,  "__builtin_subv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_subv4hi3,  "__builtin_subv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3, 0 },
  { CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3, 0 },
  { CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3, 0 },
  { CODE_FOR_fcosa_s,   "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF, 0 },
  { CODE_FOR_fcosa_s,   "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF, 0 },
  { CODE_FOR_fsina_s,   "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF, 0 },
  { CODE_FOR_fsina_s,   "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF, 0 },
  { CODE_FOR_fipr,      "__builtin_sh_media_FIPR_S", SH_BLTIN_3, 0 },
  { CODE_FOR_fipr,      "__builtin_sh_media_FIPR_S", SH_BLTIN_3, 0 },
  { CODE_FOR_ftrv,      "__builtin_sh_media_FTRV_S", SH_BLTIN_3, 0 },
  { CODE_FOR_ftrv,      "__builtin_sh_media_FTRV_S", SH_BLTIN_3, 0 },
  { CODE_FOR_mac_media, "__builtin_sh_media_FMAC_S", SH_BLTIN_3, 0 },
  { CODE_FOR_mac_media, "__builtin_sh_media_FMAC_S", SH_BLTIN_3, 0 },
  { CODE_FOR_sqrtdf2,   "__builtin_sh_media_FSQRT_D", SH_BLTIN_2, 0 },
  { CODE_FOR_sqrtdf2,   "__builtin_sh_media_FSQRT_D", SH_BLTIN_2, 0 },
  { CODE_FOR_sqrtsf2,   "__builtin_sh_media_FSQRT_S", SH_BLTIN_2, 0 },
  { CODE_FOR_sqrtsf2,   "__builtin_sh_media_FSQRT_S", SH_BLTIN_2, 0 },
  { CODE_FOR_fsrra_s,   "__builtin_sh_media_FSRRA_S", SH_BLTIN_2, 0 },
  { CODE_FOR_fsrra_s,   "__builtin_sh_media_FSRRA_S", SH_BLTIN_2, 0 },
  { CODE_FOR_ldhi_l,    "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L, 0 },
  { CODE_FOR_ldhi_l,    "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L, 0 },
  { CODE_FOR_ldhi_q,    "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q, 0 },
  { CODE_FOR_ldhi_q,    "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q, 0 },
  { CODE_FOR_ldlo_l,    "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L, 0 },
  { CODE_FOR_ldlo_l,    "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L, 0 },
  { CODE_FOR_ldlo_q,    "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q, 0 },
  { CODE_FOR_ldlo_q,    "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q, 0 },
  { CODE_FOR_sthi_l,    "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L, 0 },
  { CODE_FOR_sthi_l,    "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L, 0 },
  { CODE_FOR_sthi_q,    "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q, 0 },
  { CODE_FOR_sthi_q,    "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q, 0 },
  { CODE_FOR_stlo_l,    "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L, 0 },
  { CODE_FOR_stlo_l,    "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L, 0 },
  { CODE_FOR_stlo_q,    "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q, 0 },
  { CODE_FOR_stlo_q,    "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q, 0 },
  { CODE_FOR_ldhi_l64,  "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64, 0 },
  { CODE_FOR_ldhi_l64,  "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64, 0 },
  { CODE_FOR_ldhi_q64,  "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64, 0 },
  { CODE_FOR_ldhi_q64,  "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64, 0 },
  { CODE_FOR_ldlo_l64,  "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64, 0 },
  { CODE_FOR_ldlo_l64,  "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64, 0 },
  { CODE_FOR_ldlo_q64,  "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64, 0 },
  { CODE_FOR_ldlo_q64,  "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64, 0 },
  { CODE_FOR_sthi_l64,  "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64, 0 },
  { CODE_FOR_sthi_l64,  "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64, 0 },
  { CODE_FOR_sthi_q64,  "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64, 0 },
  { CODE_FOR_sthi_q64,  "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64, 0 },
  { CODE_FOR_stlo_l64,  "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64, 0 },
  { CODE_FOR_stlo_l64,  "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64, 0 },
  { CODE_FOR_stlo_q64,  "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64, 0 },
  { CODE_FOR_stlo_q64,  "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64, 0 },
  { CODE_FOR_nsb,       "__builtin_sh_media_NSB", SH_BLTIN_SU, 0 },
  { CODE_FOR_nsb,       "__builtin_sh_media_NSB", SH_BLTIN_SU, 0 },
  { CODE_FOR_byterev,   "__builtin_sh_media_BYTEREV", SH_BLTIN_2, 0 },
  { CODE_FOR_byterev,   "__builtin_sh_media_BYTEREV", SH_BLTIN_2, 0 },
  { CODE_FOR_prefetch,  "__builtin_sh_media_PREFO", SH_BLTIN_PSSV, 0 },
  { CODE_FOR_prefetch,  "__builtin_sh_media_PREFO", SH_BLTIN_PSSV, 0 },
};
};
 
 
static void
static void
sh_media_init_builtins (void)
sh_media_init_builtins (void)
{
{
  tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES];
  tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES];
  struct builtin_description *d;
  struct builtin_description *d;
 
 
  memset (shared, 0, sizeof shared);
  memset (shared, 0, sizeof shared);
  for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++)
  for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++)
    {
    {
      tree type, arg_type = 0;
      tree type, arg_type = 0;
      int signature = d->signature;
      int signature = d->signature;
      int i;
      int i;
 
 
      if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature])
      if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature])
        type = shared[signature];
        type = shared[signature];
      else
      else
        {
        {
          int has_result = signature_args[signature][0] != 0;
          int has_result = signature_args[signature][0] != 0;
 
 
          if ((signature_args[signature][1] & 8)
          if ((signature_args[signature][1] & 8)
              && (((signature_args[signature][1] & 1) && TARGET_SHMEDIA32)
              && (((signature_args[signature][1] & 1) && TARGET_SHMEDIA32)
                  || ((signature_args[signature][1] & 2) && TARGET_SHMEDIA64)))
                  || ((signature_args[signature][1] & 2) && TARGET_SHMEDIA64)))
            continue;
            continue;
          if (! TARGET_FPU_ANY
          if (! TARGET_FPU_ANY
              && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
              && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
            continue;
            continue;
          type = void_list_node;
          type = void_list_node;
          for (i = 3; ; i--)
          for (i = 3; ; i--)
            {
            {
              int arg = signature_args[signature][i];
              int arg = signature_args[signature][i];
              int opno = i - 1 + has_result;
              int opno = i - 1 + has_result;
 
 
              if (arg & 8)
              if (arg & 8)
                arg_type = ptr_type_node;
                arg_type = ptr_type_node;
              else if (arg)
              else if (arg)
                arg_type = (*lang_hooks.types.type_for_mode)
                arg_type = (*lang_hooks.types.type_for_mode)
                  (insn_data[d->icode].operand[opno].mode,
                  (insn_data[d->icode].operand[opno].mode,
                   (arg & 1));
                   (arg & 1));
              else if (i)
              else if (i)
                continue;
                continue;
              else
              else
                arg_type = void_type_node;
                arg_type = void_type_node;
              if (i == 0)
              if (i == 0)
                break;
                break;
              type = tree_cons (NULL_TREE, arg_type, type);
              type = tree_cons (NULL_TREE, arg_type, type);
            }
            }
          type = build_function_type (arg_type, type);
          type = build_function_type (arg_type, type);
          if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
          if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
            shared[signature] = type;
            shared[signature] = type;
        }
        }
      d->fndecl =
      d->fndecl =
        add_builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
        add_builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
                              NULL, NULL_TREE);
                              NULL, NULL_TREE);
    }
    }
}
}
 
 
/* Returns the shmedia builtin decl for CODE.  */
/* Returns the shmedia builtin decl for CODE.  */
 
 
static tree
static tree
sh_media_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
sh_media_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
{
{
  if (code >= ARRAY_SIZE (bdesc))
  if (code >= ARRAY_SIZE (bdesc))
    return error_mark_node;
    return error_mark_node;
 
 
  return bdesc[code].fndecl;
  return bdesc[code].fndecl;
}
}
 
 
/* Implements target hook vector_mode_supported_p.  */
/* Implements target hook vector_mode_supported_p.  */
bool
bool
sh_vector_mode_supported_p (enum machine_mode mode)
sh_vector_mode_supported_p (enum machine_mode mode)
{
{
  if (TARGET_FPU_ANY
  if (TARGET_FPU_ANY
      && ((mode == V2SFmode)
      && ((mode == V2SFmode)
          || (mode == V4SFmode)
          || (mode == V4SFmode)
          || (mode == V16SFmode)))
          || (mode == V16SFmode)))
    return true;
    return true;
 
 
  else if (TARGET_SHMEDIA
  else if (TARGET_SHMEDIA
           && ((mode == V8QImode)
           && ((mode == V8QImode)
               || (mode == V2HImode)
               || (mode == V2HImode)
               || (mode == V4HImode)
               || (mode == V4HImode)
               || (mode == V2SImode)))
               || (mode == V2SImode)))
    return true;
    return true;
 
 
  return false;
  return false;
}
}
 
 
/* Implements target hook dwarf_calling_convention.  Return an enum
/* Implements target hook dwarf_calling_convention.  Return an enum
   of dwarf_calling_convention.  */
   of dwarf_calling_convention.  */
int
int
sh_dwarf_calling_convention (const_tree func)
sh_dwarf_calling_convention (const_tree func)
{
{
  if (sh_attr_renesas_p (func))
  if (sh_attr_renesas_p (func))
    return DW_CC_GNU_renesas_sh;
    return DW_CC_GNU_renesas_sh;
 
 
  return DW_CC_normal;
  return DW_CC_normal;
}
}
 
 
static void
static void
sh_init_builtins (void)
sh_init_builtins (void)
{
{
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    sh_media_init_builtins ();
    sh_media_init_builtins ();
}
}
 
 
/* Returns the sh builtin decl for CODE.  */
/* Returns the sh builtin decl for CODE.  */
 
 
static tree
static tree
sh_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
sh_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
{
{
  if (TARGET_SHMEDIA)
  if (TARGET_SHMEDIA)
    return sh_media_builtin_decl (code, initialize_p);
    return sh_media_builtin_decl (code, initialize_p);
 
 
  return error_mark_node;
  return error_mark_node;
}
}
 
 
/* Expand an expression EXP that calls a built-in function,
/* Expand an expression EXP that calls a built-in function,
   with result going to TARGET if that's convenient
   with result going to TARGET if that's convenient
   (and in mode MODE if that's convenient).
   (and in mode MODE if that's convenient).
   SUBTARGET may be used as the target for computing one of EXP's operands.
   SUBTARGET may be used as the target for computing one of EXP's operands.
   IGNORE is nonzero if the value is to be ignored.  */
   IGNORE is nonzero if the value is to be ignored.  */
 
 
static rtx
static rtx
sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
                   enum machine_mode mode ATTRIBUTE_UNUSED, int ignore)
                   enum machine_mode mode ATTRIBUTE_UNUSED, int ignore)
{
{
  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
  unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
  unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
  const struct builtin_description *d = &bdesc[fcode];
  const struct builtin_description *d = &bdesc[fcode];
  enum insn_code icode = d->icode;
  enum insn_code icode = d->icode;
  int signature = d->signature;
  int signature = d->signature;
  enum machine_mode tmode = VOIDmode;
  enum machine_mode tmode = VOIDmode;
  int nop = 0, i;
  int nop = 0, i;
  rtx op[4];
  rtx op[4];
  rtx pat = 0;
  rtx pat = 0;
 
 
  if (signature_args[signature][0])
  if (signature_args[signature][0])
    {
    {
      if (ignore)
      if (ignore)
        return 0;
        return 0;
 
 
      tmode = insn_data[icode].operand[0].mode;
      tmode = insn_data[icode].operand[0].mode;
      if (! target
      if (! target
          || GET_MODE (target) != tmode
          || GET_MODE (target) != tmode
          || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
          || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
        target = gen_reg_rtx (tmode);
        target = gen_reg_rtx (tmode);
      op[nop++] = target;
      op[nop++] = target;
    }
    }
  else
  else
    target = 0;
    target = 0;
 
 
  for (i = 1; i <= 3; i++, nop++)
  for (i = 1; i <= 3; i++, nop++)
    {
    {
      tree arg;
      tree arg;
      enum machine_mode opmode, argmode;
      enum machine_mode opmode, argmode;
      tree optype;
      tree optype;
 
 
      if (! signature_args[signature][i])
      if (! signature_args[signature][i])
        break;
        break;
      arg = CALL_EXPR_ARG (exp, i - 1);
      arg = CALL_EXPR_ARG (exp, i - 1);
      if (arg == error_mark_node)
      if (arg == error_mark_node)
        return const0_rtx;
        return const0_rtx;
      if (signature_args[signature][i] & 8)
      if (signature_args[signature][i] & 8)
        {
        {
          opmode = ptr_mode;
          opmode = ptr_mode;
          optype = ptr_type_node;
          optype = ptr_type_node;
        }
        }
      else
      else
        {
        {
          opmode = insn_data[icode].operand[nop].mode;
          opmode = insn_data[icode].operand[nop].mode;
          optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
          optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
        }
        }
      argmode = TYPE_MODE (TREE_TYPE (arg));
      argmode = TYPE_MODE (TREE_TYPE (arg));
      if (argmode != opmode)
      if (argmode != opmode)
        arg = build1 (NOP_EXPR, optype, arg);
        arg = build1 (NOP_EXPR, optype, arg);
      op[nop] = expand_expr (arg, NULL_RTX, opmode, EXPAND_NORMAL);
      op[nop] = expand_expr (arg, NULL_RTX, opmode, EXPAND_NORMAL);
      if (! (*insn_data[icode].operand[nop].predicate) (op[nop], opmode))
      if (! (*insn_data[icode].operand[nop].predicate) (op[nop], opmode))
        op[nop] = copy_to_mode_reg (opmode, op[nop]);
        op[nop] = copy_to_mode_reg (opmode, op[nop]);
    }
    }
 
 
  switch (nop)
  switch (nop)
    {
    {
    case 1:
    case 1:
      pat = (*insn_data[d->icode].genfun) (op[0]);
      pat = (*insn_data[d->icode].genfun) (op[0]);
      break;
      break;
    case 2:
    case 2:
      pat = (*insn_data[d->icode].genfun) (op[0], op[1]);
      pat = (*insn_data[d->icode].genfun) (op[0], op[1]);
      break;
      break;
    case 3:
    case 3:
      pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2]);
      pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2]);
      break;
      break;
    case 4:
    case 4:
      pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
      pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
      break;
      break;
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
  if (! pat)
  if (! pat)
    return 0;
    return 0;
  emit_insn (pat);
  emit_insn (pat);
  return target;
  return target;
}
}
 
 
void
void
sh_expand_unop_v2sf (enum rtx_code code, rtx op0, rtx op1)
sh_expand_unop_v2sf (enum rtx_code code, rtx op0, rtx op1)
{
{
  rtx sel0 = const0_rtx;
  rtx sel0 = const0_rtx;
  rtx sel1 = const1_rtx;
  rtx sel1 = const1_rtx;
  rtx (*fn) (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);
  rtx op = gen_rtx_fmt_e (code, SFmode, op1);
 
 
  emit_insn ((*fn) (op0, op1, op, sel0, sel0));
  emit_insn ((*fn) (op0, op1, op, sel0, sel0));
  emit_insn ((*fn) (op0, op1, op, sel1, sel1));
  emit_insn ((*fn) (op0, op1, op, sel1, sel1));
}
}
 
 
void
void
sh_expand_binop_v2sf (enum rtx_code code, rtx op0, rtx op1, rtx op2)
sh_expand_binop_v2sf (enum rtx_code code, rtx op0, rtx op1, rtx op2)
{
{
  rtx op = gen_rtx_fmt_ee (code, SFmode, op1, op2);
  rtx op = gen_rtx_fmt_ee (code, SFmode, op1, op2);
 
 
  emit_insn (gen_binary_sf_op0 (op0, op1, op2, op));
  emit_insn (gen_binary_sf_op0 (op0, op1, op2, op));
  emit_insn (gen_binary_sf_op1 (op0, op1, op2, op));
  emit_insn (gen_binary_sf_op1 (op0, op1, op2, op));
}
}
 
 
/* Return true if hard register REGNO can hold a value of machine-mode MODE.
/* Return true if hard register REGNO can hold a value of machine-mode MODE.
   We can allow any mode in any general register.  The special registers
   We can allow any mode in any general register.  The special registers
   only allow SImode.  Don't allow any mode in the PR.
   only allow SImode.  Don't allow any mode in the PR.
 
 
   We cannot hold DCmode values in the XD registers because alter_reg
   We cannot hold DCmode values in the XD registers because alter_reg
   handles subregs of them incorrectly.  We could work around this by
   handles subregs of them incorrectly.  We could work around this by
   spacing the XD registers like the DR registers, but this would require
   spacing the XD registers like the DR registers, but this would require
   additional memory in every compilation to hold larger register vectors.
   additional memory in every compilation to hold larger register vectors.
   We could hold SFmode / SCmode values in XD registers, but that
   We could hold SFmode / SCmode values in XD registers, but that
   would require a tertiary reload when reloading from / to memory,
   would require a tertiary reload when reloading from / to memory,
   and a secondary reload to reload from / to general regs; that
   and a secondary reload to reload from / to general regs; that
   seems to be a loosing proposition.
   seems to be a loosing proposition.
 
 
   We want to allow TImode FP regs so that when V4SFmode is loaded as TImode,
   We want to allow TImode FP regs so that when V4SFmode is loaded as TImode,
   it won't be ferried through GP registers first.  */
   it won't be ferried through GP registers first.  */
 
 
bool
bool
sh_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
sh_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
{
{
  if (SPECIAL_REGISTER_P (regno))
  if (SPECIAL_REGISTER_P (regno))
    return mode == SImode;
    return mode == SImode;
 
 
  if (regno == FPUL_REG)
  if (regno == FPUL_REG)
    return (mode == SImode || mode == SFmode);
    return (mode == SImode || mode == SFmode);
 
 
  if (FP_REGISTER_P (regno) && mode == SFmode)
  if (FP_REGISTER_P (regno) && mode == SFmode)
    return true;
    return true;
 
 
  if (mode == V2SFmode)
  if (mode == V2SFmode)
    {
    {
      if (((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 2 == 0)
      if (((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 2 == 0)
           || GENERAL_REGISTER_P (regno)))
           || GENERAL_REGISTER_P (regno)))
        return true;
        return true;
      else
      else
        return false;
        return false;
    }
    }
 
 
  if (mode == V4SFmode)
  if (mode == V4SFmode)
    {
    {
      if ((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 4 == 0)
      if ((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 4 == 0)
          || GENERAL_REGISTER_P (regno))
          || GENERAL_REGISTER_P (regno))
        return true;
        return true;
      else
      else
        return false;
        return false;
    }
    }
 
 
  if (mode == V16SFmode)
  if (mode == V16SFmode)
    {
    {
      if (TARGET_SHMEDIA)
      if (TARGET_SHMEDIA)
        {
        {
          if (FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 16 == 0)
          if (FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 16 == 0)
            return true;
            return true;
          else
          else
            return false;
            return false;
        }
        }
      else
      else
        return regno == FIRST_XD_REG;
        return regno == FIRST_XD_REG;
    }
    }
 
 
  if (FP_REGISTER_P (regno))
  if (FP_REGISTER_P (regno))
    {
    {
      if (mode == SFmode
      if (mode == SFmode
          || mode == SImode
          || mode == SImode
          || ((TARGET_SH2E || TARGET_SHMEDIA) && mode == SCmode)
          || ((TARGET_SH2E || TARGET_SHMEDIA) && mode == SCmode)
          || ((((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
          || ((((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
               || mode == DCmode
               || mode == DCmode
               || (TARGET_SHMEDIA
               || (TARGET_SHMEDIA
                   && (mode == DFmode || mode == DImode
                   && (mode == DFmode || mode == DImode
                       || mode == V2SFmode || mode == TImode)))
                       || mode == V2SFmode || mode == TImode)))
              && ((regno - FIRST_FP_REG) & 1) == 0)
              && ((regno - FIRST_FP_REG) & 1) == 0)
          || ((TARGET_SH4 || TARGET_SHMEDIA) && mode == TImode
          || ((TARGET_SH4 || TARGET_SHMEDIA) && mode == TImode
              && ((regno - FIRST_FP_REG) & 3) == 0))
              && ((regno - FIRST_FP_REG) & 3) == 0))
        return true;
        return true;
      else
      else
        return false;
        return false;
    }
    }
 
 
  if (XD_REGISTER_P (regno))
  if (XD_REGISTER_P (regno))
    return mode == DFmode;
    return mode == DFmode;
 
 
  if (TARGET_REGISTER_P (regno))
  if (TARGET_REGISTER_P (regno))
    return (mode == DImode || mode == SImode || mode == PDImode);
    return (mode == DImode || mode == SImode || mode == PDImode);
 
 
  if (regno == PR_REG)
  if (regno == PR_REG)
    return mode == SImode;
    return mode == SImode;
 
 
  if (regno == FPSCR_REG)
  if (regno == FPSCR_REG)
    return mode == PSImode;
    return mode == PSImode;
 
 
  /* FIXME.  This works around PR target/37633 for -O0.  */
  /* FIXME.  This works around PR target/37633 for -O0.  */
  if (!optimize && TARGET_SHMEDIA32 && GET_MODE_SIZE (mode) > 4)
  if (!optimize && TARGET_SHMEDIA32 && GET_MODE_SIZE (mode) > 4)
    {
    {
      unsigned int n = GET_MODE_SIZE (mode) / 8;
      unsigned int n = GET_MODE_SIZE (mode) / 8;
 
 
      if (regno >= FIRST_GENERAL_REG + 10 - n + 1
      if (regno >= FIRST_GENERAL_REG + 10 - n + 1
          && regno <= FIRST_GENERAL_REG + 14)
          && regno <= FIRST_GENERAL_REG + 14)
        return false;
        return false;
    }
    }
 
 
  return true;
  return true;
}
}
 
 
/* Return the class of registers for which a mode change from FROM to TO
/* Return the class of registers for which a mode change from FROM to TO
   is invalid.  */
   is invalid.  */
bool
bool
sh_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
sh_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
                             enum reg_class rclass)
                             enum reg_class rclass)
{
{
  /* We want to enable the use of SUBREGs as a means to
  /* We want to enable the use of SUBREGs as a means to
     VEC_SELECT a single element of a vector.  */
     VEC_SELECT a single element of a vector.  */
  if (to == SFmode && VECTOR_MODE_P (from) && GET_MODE_INNER (from) == SFmode)
  if (to == SFmode && VECTOR_MODE_P (from) && GET_MODE_INNER (from) == SFmode)
    return (reg_classes_intersect_p (GENERAL_REGS, rclass));
    return (reg_classes_intersect_p (GENERAL_REGS, rclass));
 
 
  if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
  if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
    {
    {
      if (TARGET_LITTLE_ENDIAN)
      if (TARGET_LITTLE_ENDIAN)
        {
        {
          if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8)
          if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8)
            return reg_classes_intersect_p (DF_REGS, rclass);
            return reg_classes_intersect_p (DF_REGS, rclass);
        }
        }
      else
      else
        {
        {
          if (GET_MODE_SIZE (from) < 8)
          if (GET_MODE_SIZE (from) < 8)
            return reg_classes_intersect_p (DF_HI_REGS, rclass);
            return reg_classes_intersect_p (DF_HI_REGS, rclass);
        }
        }
    }
    }
  return 0;
  return 0;
}
}
 
 
 
 
/* If ADDRESS refers to a CODE_LABEL, add NUSES to the number of times
/* If ADDRESS refers to a CODE_LABEL, add NUSES to the number of times
   that label is used.  */
   that label is used.  */
 
 
void
void
sh_mark_label (rtx address, int nuses)
sh_mark_label (rtx address, int nuses)
{
{
  if (GOTOFF_P (address))
  if (GOTOFF_P (address))
    {
    {
      /* Extract the label or symbol.  */
      /* Extract the label or symbol.  */
      address = XEXP (address, 0);
      address = XEXP (address, 0);
      if (GET_CODE (address) == PLUS)
      if (GET_CODE (address) == PLUS)
        address = XEXP (address, 0);
        address = XEXP (address, 0);
      address = XVECEXP (address, 0, 0);
      address = XVECEXP (address, 0, 0);
    }
    }
  if (GET_CODE (address) == LABEL_REF
  if (GET_CODE (address) == LABEL_REF
      && LABEL_P (XEXP (address, 0)))
      && LABEL_P (XEXP (address, 0)))
    LABEL_NUSES (XEXP (address, 0)) += nuses;
    LABEL_NUSES (XEXP (address, 0)) += nuses;
}
}
 
 
/* Compute extra cost of moving data between one register class
/* Compute extra cost of moving data between one register class
   and another.  */
   and another.  */
 
 
/* If SECONDARY*_RELOAD_CLASS says something about the src/dst pair, regclass
/* If SECONDARY*_RELOAD_CLASS says something about the src/dst pair, regclass
   uses this information.  Hence, the general register <-> floating point
   uses this information.  Hence, the general register <-> floating point
   register information here is not used for SFmode.  */
   register information here is not used for SFmode.  */
 
 
int
int
sh_register_move_cost (enum machine_mode mode,
sh_register_move_cost (enum machine_mode mode,
                       enum reg_class srcclass, enum reg_class dstclass)
                       enum reg_class srcclass, enum reg_class dstclass)
{
{
  if (dstclass == T_REGS || dstclass == PR_REGS)
  if (dstclass == T_REGS || dstclass == PR_REGS)
    return 10;
    return 10;
 
 
  if (dstclass == MAC_REGS && srcclass == MAC_REGS)
  if (dstclass == MAC_REGS && srcclass == MAC_REGS)
    return 4;
    return 4;
 
 
  if (mode == SImode && ! TARGET_SHMEDIA && TARGET_FMOVD
  if (mode == SImode && ! TARGET_SHMEDIA && TARGET_FMOVD
      && REGCLASS_HAS_FP_REG (srcclass)
      && REGCLASS_HAS_FP_REG (srcclass)
      && REGCLASS_HAS_FP_REG (dstclass))
      && REGCLASS_HAS_FP_REG (dstclass))
    return 4;
    return 4;
 
 
  if (REGCLASS_HAS_FP_REG (dstclass) && srcclass == T_REGS)
  if (REGCLASS_HAS_FP_REG (dstclass) && srcclass == T_REGS)
    return ((TARGET_HARD_SH4 && !optimize_size) ? 10 : 7);
    return ((TARGET_HARD_SH4 && !optimize_size) ? 10 : 7);
 
 
  if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
  if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
      || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
      || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
    return 9;
    return 9;
 
 
  if ((REGCLASS_HAS_FP_REG (dstclass)
  if ((REGCLASS_HAS_FP_REG (dstclass)
       && REGCLASS_HAS_GENERAL_REG (srcclass))
       && REGCLASS_HAS_GENERAL_REG (srcclass))
      || (REGCLASS_HAS_GENERAL_REG (dstclass)
      || (REGCLASS_HAS_GENERAL_REG (dstclass)
          && REGCLASS_HAS_FP_REG (srcclass)))
          && REGCLASS_HAS_FP_REG (srcclass)))
    return ((TARGET_SHMEDIA ? 4 : TARGET_FMOVD ? 8 : 12)
    return ((TARGET_SHMEDIA ? 4 : TARGET_FMOVD ? 8 : 12)
            * ((GET_MODE_SIZE (mode) + 7) / 8U));
            * ((GET_MODE_SIZE (mode) + 7) / 8U));
 
 
  if ((dstclass == FPUL_REGS
  if ((dstclass == FPUL_REGS
       && REGCLASS_HAS_GENERAL_REG (srcclass))
       && REGCLASS_HAS_GENERAL_REG (srcclass))
      || (srcclass == FPUL_REGS
      || (srcclass == FPUL_REGS
          && REGCLASS_HAS_GENERAL_REG (dstclass)))
          && REGCLASS_HAS_GENERAL_REG (dstclass)))
    return 5;
    return 5;
 
 
  if ((dstclass == FPUL_REGS
  if ((dstclass == FPUL_REGS
       && (srcclass == PR_REGS || srcclass == MAC_REGS || srcclass == T_REGS))
       && (srcclass == PR_REGS || srcclass == MAC_REGS || srcclass == T_REGS))
      || (srcclass == FPUL_REGS
      || (srcclass == FPUL_REGS
          && (dstclass == PR_REGS || dstclass == MAC_REGS)))
          && (dstclass == PR_REGS || dstclass == MAC_REGS)))
    return 7;
    return 7;
 
 
  if ((srcclass == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
  if ((srcclass == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
      || ((dstclass) == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
      || ((dstclass) == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
    return 20;
    return 20;
 
 
  /* ??? ptabs faults on (value & 0x3) == 0x3  */
  /* ??? ptabs faults on (value & 0x3) == 0x3  */
  if (TARGET_SHMEDIA
  if (TARGET_SHMEDIA
      && ((srcclass) == TARGET_REGS || (srcclass) == SIBCALL_REGS))
      && ((srcclass) == TARGET_REGS || (srcclass) == SIBCALL_REGS))
    {
    {
      if (sh_gettrcost >= 0)
      if (sh_gettrcost >= 0)
        return sh_gettrcost;
        return sh_gettrcost;
      else if (!TARGET_PT_FIXED)
      else if (!TARGET_PT_FIXED)
        return 100;
        return 100;
    }
    }
 
 
  if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
  if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
      || (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
      || (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
  return 4;
  return 4;
 
 
  if (TARGET_SHMEDIA
  if (TARGET_SHMEDIA
      || (TARGET_FMOVD
      || (TARGET_FMOVD
          && ! REGCLASS_HAS_GENERAL_REG (srcclass)
          && ! REGCLASS_HAS_GENERAL_REG (srcclass)
          && ! REGCLASS_HAS_GENERAL_REG (dstclass)))
          && ! REGCLASS_HAS_GENERAL_REG (dstclass)))
    return 2 * ((GET_MODE_SIZE (mode) + 7) / 8U);
    return 2 * ((GET_MODE_SIZE (mode) + 7) / 8U);
 
 
  return 2 * ((GET_MODE_SIZE (mode) + 3) / 4U);
  return 2 * ((GET_MODE_SIZE (mode) + 3) / 4U);
}
}
 
 
static rtx emit_load_ptr (rtx, rtx);
static rtx emit_load_ptr (rtx, rtx);
 
 
static rtx
static rtx
emit_load_ptr (rtx reg, rtx addr)
emit_load_ptr (rtx reg, rtx addr)
{
{
  rtx mem = gen_const_mem (ptr_mode, addr);
  rtx mem = gen_const_mem (ptr_mode, addr);
 
 
  if (Pmode != ptr_mode)
  if (Pmode != ptr_mode)
    mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
    mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
  return emit_move_insn (reg, mem);
  return emit_move_insn (reg, mem);
}
}
 
 
static void
static void
sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
                    HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
                    HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
                    tree function)
                    tree function)
{
{
  CUMULATIVE_ARGS cum;
  CUMULATIVE_ARGS cum;
  int structure_value_byref = 0;
  int structure_value_byref = 0;
  rtx this_rtx, this_value, sibcall, insns, funexp;
  rtx this_rtx, this_value, sibcall, insns, funexp;
  tree funtype = TREE_TYPE (function);
  tree funtype = TREE_TYPE (function);
  int simple_add = CONST_OK_FOR_ADD (delta);
  int simple_add = CONST_OK_FOR_ADD (delta);
  int did_load = 0;
  int did_load = 0;
  rtx scratch0, scratch1, scratch2;
  rtx scratch0, scratch1, scratch2;
  unsigned i;
  unsigned i;
 
 
  reload_completed = 1;
  reload_completed = 1;
  epilogue_completed = 1;
  epilogue_completed = 1;
  current_function_uses_only_leaf_regs = 1;
  current_function_uses_only_leaf_regs = 1;
 
 
  emit_note (NOTE_INSN_PROLOGUE_END);
  emit_note (NOTE_INSN_PROLOGUE_END);
 
 
  /* Find the "this" pointer.  We have such a wide range of ABIs for the
  /* 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.
     SH that it's best to do this completely machine independently.
     "this" is passed as first argument, unless a structure return pointer
     "this" is passed as first argument, unless a structure return pointer
     comes first, in which case "this" comes second.  */
     comes first, in which case "this" comes second.  */
  INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0, 1);
  INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0, 1);
#ifndef PCC_STATIC_STRUCT_RETURN
#ifndef PCC_STATIC_STRUCT_RETURN
  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
    structure_value_byref = 1;
    structure_value_byref = 1;
#endif /* not PCC_STATIC_STRUCT_RETURN */
#endif /* not PCC_STATIC_STRUCT_RETURN */
  if (structure_value_byref && sh_struct_value_rtx (function, 0) == 0)
  if (structure_value_byref && sh_struct_value_rtx (function, 0) == 0)
    {
    {
      tree ptype = build_pointer_type (TREE_TYPE (funtype));
      tree ptype = build_pointer_type (TREE_TYPE (funtype));
 
 
      FUNCTION_ARG_ADVANCE (cum, Pmode, ptype, 1);
      FUNCTION_ARG_ADVANCE (cum, Pmode, ptype, 1);
    }
    }
  this_rtx = FUNCTION_ARG (cum, Pmode, ptr_type_node, 1);
  this_rtx = FUNCTION_ARG (cum, Pmode, ptr_type_node, 1);
 
 
  /* For SHcompact, we only have r0 for a scratch register: r1 is the
  /* For SHcompact, we only have r0 for a scratch register: r1 is the
     static chain pointer (even if you can't have nested virtual functions
     static chain pointer (even if you can't have nested virtual functions
     right now, someone might implement them sometime), and the rest of the
     right now, someone might implement them sometime), and the rest of the
     registers are used for argument passing, are callee-saved, or reserved.  */
     registers are used for argument passing, are callee-saved, or reserved.  */
  /* We need to check call_used_regs / fixed_regs in case -fcall_saved-reg /
  /* We need to check call_used_regs / fixed_regs in case -fcall_saved-reg /
     -ffixed-reg has been used.  */
     -ffixed-reg has been used.  */
  if (! call_used_regs[0] || fixed_regs[0])
  if (! call_used_regs[0] || fixed_regs[0])
    error ("r0 needs to be available as a call-clobbered register");
    error ("r0 needs to be available as a call-clobbered register");
  scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
  scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
  if (! TARGET_SH5)
  if (! TARGET_SH5)
    {
    {
      if (call_used_regs[1] && ! fixed_regs[1])
      if (call_used_regs[1] && ! fixed_regs[1])
        scratch1 = gen_rtx_REG (ptr_mode, 1);
        scratch1 = gen_rtx_REG (ptr_mode, 1);
      /* N.B., if not TARGET_HITACHI, register 2 is used to pass the pointer
      /* N.B., if not TARGET_HITACHI, register 2 is used to pass the pointer
         pointing where to return struct values.  */
         pointing where to return struct values.  */
      if (call_used_regs[3] && ! fixed_regs[3])
      if (call_used_regs[3] && ! fixed_regs[3])
        scratch2 = gen_rtx_REG (Pmode, 3);
        scratch2 = gen_rtx_REG (Pmode, 3);
    }
    }
  else if (TARGET_SHMEDIA)
  else if (TARGET_SHMEDIA)
    {
    {
      for (i = FIRST_GENERAL_REG; i <= LAST_GENERAL_REG; i++)
      for (i = FIRST_GENERAL_REG; i <= LAST_GENERAL_REG; i++)
        if (i != REGNO (scratch0) &&
        if (i != REGNO (scratch0) &&
            call_used_regs[i] && ! fixed_regs[i] && ! FUNCTION_ARG_REGNO_P (i))
            call_used_regs[i] && ! fixed_regs[i] && ! FUNCTION_ARG_REGNO_P (i))
          {
          {
            scratch1 = gen_rtx_REG (ptr_mode, i);
            scratch1 = gen_rtx_REG (ptr_mode, i);
            break;
            break;
          }
          }
      if (scratch1 == scratch0)
      if (scratch1 == scratch0)
        error ("Need a second call-clobbered general purpose register");
        error ("Need a second call-clobbered general purpose register");
      for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
      for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
        if (call_used_regs[i] && ! fixed_regs[i])
        if (call_used_regs[i] && ! fixed_regs[i])
          {
          {
            scratch2 = gen_rtx_REG (Pmode, i);
            scratch2 = gen_rtx_REG (Pmode, i);
            break;
            break;
          }
          }
      if (scratch2 == scratch0)
      if (scratch2 == scratch0)
        error ("Need a call-clobbered target register");
        error ("Need a call-clobbered target register");
    }
    }
 
 
  this_value = plus_constant (this_rtx, delta);
  this_value = plus_constant (this_rtx, delta);
  if (vcall_offset
  if (vcall_offset
      && (simple_add || scratch0 != scratch1)
      && (simple_add || scratch0 != scratch1)
      && strict_memory_address_p (ptr_mode, this_value))
      && strict_memory_address_p (ptr_mode, this_value))
    {
    {
      emit_load_ptr (scratch0, this_value);
      emit_load_ptr (scratch0, this_value);
      did_load = 1;
      did_load = 1;
    }
    }
 
 
  if (!delta)
  if (!delta)
    ; /* Do nothing.  */
    ; /* Do nothing.  */
  else if (simple_add)
  else if (simple_add)
    emit_move_insn (this_rtx, this_value);
    emit_move_insn (this_rtx, this_value);
  else
  else
    {
    {
      emit_move_insn (scratch1, GEN_INT (delta));
      emit_move_insn (scratch1, GEN_INT (delta));
      emit_insn (gen_add2_insn (this_rtx, scratch1));
      emit_insn (gen_add2_insn (this_rtx, scratch1));
    }
    }
 
 
  if (vcall_offset)
  if (vcall_offset)
    {
    {
      rtx offset_addr;
      rtx offset_addr;
 
 
      if (!did_load)
      if (!did_load)
        emit_load_ptr (scratch0, this_rtx);
        emit_load_ptr (scratch0, this_rtx);
 
 
      offset_addr = plus_constant (scratch0, vcall_offset);
      offset_addr = plus_constant (scratch0, vcall_offset);
      if (strict_memory_address_p (ptr_mode, offset_addr))
      if (strict_memory_address_p (ptr_mode, offset_addr))
        ; /* Do nothing.  */
        ; /* Do nothing.  */
      else if (! TARGET_SH5 && scratch0 != scratch1)
      else if (! TARGET_SH5 && scratch0 != scratch1)
        {
        {
          /* scratch0 != scratch1, and we have indexed loads.  Get better
          /* scratch0 != scratch1, and we have indexed loads.  Get better
             schedule by loading the offset into r1 and using an indexed
             schedule by loading the offset into r1 and using an indexed
             load - then the load of r1 can issue before the load from
             load - then the load of r1 can issue before the load from
             (this_rtx + delta) finishes.  */
             (this_rtx + delta) finishes.  */
          emit_move_insn (scratch1, GEN_INT (vcall_offset));
          emit_move_insn (scratch1, GEN_INT (vcall_offset));
          offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1);
          offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1);
        }
        }
      else if (CONST_OK_FOR_ADD (vcall_offset))
      else if (CONST_OK_FOR_ADD (vcall_offset))
        {
        {
          emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset)));
          emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset)));
          offset_addr = scratch0;
          offset_addr = scratch0;
        }
        }
      else if (scratch0 != scratch1)
      else if (scratch0 != scratch1)
        {
        {
          emit_move_insn (scratch1, GEN_INT (vcall_offset));
          emit_move_insn (scratch1, GEN_INT (vcall_offset));
          emit_insn (gen_add2_insn (scratch0, scratch1));
          emit_insn (gen_add2_insn (scratch0, scratch1));
          offset_addr = scratch0;
          offset_addr = scratch0;
        }
        }
      else
      else
        gcc_unreachable (); /* FIXME */
        gcc_unreachable (); /* FIXME */
      emit_load_ptr (scratch0, offset_addr);
      emit_load_ptr (scratch0, offset_addr);
 
 
      if (Pmode != ptr_mode)
      if (Pmode != ptr_mode)
        scratch0 = gen_rtx_TRUNCATE (ptr_mode, scratch0);
        scratch0 = gen_rtx_TRUNCATE (ptr_mode, scratch0);
      emit_insn (gen_add2_insn (this_rtx, scratch0));
      emit_insn (gen_add2_insn (this_rtx, scratch0));
    }
    }
 
 
  /* Generate a tail call to the target function.  */
  /* Generate a tail call to the target function.  */
  if (! TREE_USED (function))
  if (! TREE_USED (function))
    {
    {
      assemble_external (function);
      assemble_external (function);
      TREE_USED (function) = 1;
      TREE_USED (function) = 1;
    }
    }
  funexp = XEXP (DECL_RTL (function), 0);
  funexp = XEXP (DECL_RTL (function), 0);
  /* If the function is overridden, so is the thunk, hence we don't
  /* If the function is overridden, so is the thunk, hence we don't
     need GOT addressing even if this is a public symbol.  */
     need GOT addressing even if this is a public symbol.  */
#if 0
#if 0
  if (TARGET_SH1 && ! flag_weak)
  if (TARGET_SH1 && ! flag_weak)
    sibcall = gen_sibcalli_thunk (funexp, const0_rtx);
    sibcall = gen_sibcalli_thunk (funexp, const0_rtx);
  else
  else
#endif
#endif
  if (TARGET_SH2 && flag_pic)
  if (TARGET_SH2 && flag_pic)
    {
    {
      sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
      sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
      XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
      XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
    }
    }
  else
  else
    {
    {
      if (TARGET_SHMEDIA && flag_pic)
      if (TARGET_SHMEDIA && flag_pic)
        {
        {
          funexp = gen_sym2PIC (funexp);
          funexp = gen_sym2PIC (funexp);
          PUT_MODE (funexp, Pmode);
          PUT_MODE (funexp, Pmode);
        }
        }
      emit_move_insn (scratch2, funexp);
      emit_move_insn (scratch2, funexp);
      funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
      funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
      sibcall = gen_sibcall (funexp, const0_rtx, NULL_RTX);
      sibcall = gen_sibcall (funexp, const0_rtx, NULL_RTX);
    }
    }
  sibcall = emit_call_insn (sibcall);
  sibcall = emit_call_insn (sibcall);
  SIBLING_CALL_P (sibcall) = 1;
  SIBLING_CALL_P (sibcall) = 1;
  use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this_rtx);
  use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this_rtx);
  emit_barrier ();
  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
     the insns emitted.  Note that use_thunk calls
     assemble_start_function and assemble_end_function.  */
     assemble_start_function and assemble_end_function.  */
 
 
  insn_locators_alloc ();
  insn_locators_alloc ();
  insns = get_insns ();
  insns = get_insns ();
 
 
  if (optimize > 0)
  if (optimize > 0)
    {
    {
      if (! cfun->cfg)
      if (! cfun->cfg)
        init_flow (cfun);
        init_flow (cfun);
      split_all_insns_noflow ();
      split_all_insns_noflow ();
    }
    }
 
 
  sh_reorg ();
  sh_reorg ();
 
 
  if (optimize > 0 && flag_delayed_branch)
  if (optimize > 0 && flag_delayed_branch)
    dbr_schedule (insns);
    dbr_schedule (insns);
 
 
  shorten_branches (insns);
  shorten_branches (insns);
  final_start_function (insns, file, 1);
  final_start_function (insns, file, 1);
  final (insns, file, 1);
  final (insns, file, 1);
  final_end_function ();
  final_end_function ();
 
 
  reload_completed = 0;
  reload_completed = 0;
  epilogue_completed = 0;
  epilogue_completed = 0;
}
}
 
 
rtx
rtx
function_symbol (rtx target, const char *name, enum sh_function_kind kind)
function_symbol (rtx target, const char *name, enum sh_function_kind kind)
{
{
  rtx sym;
  rtx sym;
 
 
  /* If this is not an ordinary function, the name usually comes from a
  /* If this is not an ordinary function, the name usually comes from a
     string literal or an sprintf buffer.  Make sure we use the same
     string literal or an sprintf buffer.  Make sure we use the same
     string consistently, so that cse will be able to unify address loads.  */
     string consistently, so that cse will be able to unify address loads.  */
  if (kind != FUNCTION_ORDINARY)
  if (kind != FUNCTION_ORDINARY)
    name = IDENTIFIER_POINTER (get_identifier (name));
    name = IDENTIFIER_POINTER (get_identifier (name));
  sym = gen_rtx_SYMBOL_REF (Pmode, name);
  sym = gen_rtx_SYMBOL_REF (Pmode, name);
  SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
  SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
  if (flag_pic)
  if (flag_pic)
    switch (kind)
    switch (kind)
      {
      {
      case FUNCTION_ORDINARY:
      case FUNCTION_ORDINARY:
        break;
        break;
      case SFUNC_GOT:
      case SFUNC_GOT:
        {
        {
          rtx reg = target ? target : gen_reg_rtx (Pmode);
          rtx reg = target ? target : gen_reg_rtx (Pmode);
 
 
          emit_insn (gen_symGOT2reg (reg, sym));
          emit_insn (gen_symGOT2reg (reg, sym));
          sym = reg;
          sym = reg;
          break;
          break;
        }
        }
      case SFUNC_STATIC:
      case SFUNC_STATIC:
        {
        {
          /* ??? To allow cse to work, we use GOTOFF relocations.
          /* ??? To allow cse to work, we use GOTOFF relocations.
             we could add combiner patterns to transform this into
             we could add combiner patterns to transform this into
             straight pc-relative calls with sym2PIC / bsrf when
             straight pc-relative calls with sym2PIC / bsrf when
             label load and function call are still 1:1 and in the
             label load and function call are still 1:1 and in the
             same basic block during combine.  */
             same basic block during combine.  */
          rtx reg = target ? target : gen_reg_rtx (Pmode);
          rtx reg = target ? target : gen_reg_rtx (Pmode);
 
 
          emit_insn (gen_symGOTOFF2reg (reg, sym));
          emit_insn (gen_symGOTOFF2reg (reg, sym));
          sym = reg;
          sym = reg;
          break;
          break;
        }
        }
      }
      }
  if (target && sym != target)
  if (target && sym != target)
    {
    {
      emit_move_insn (target, sym);
      emit_move_insn (target, sym);
      return target;
      return target;
    }
    }
  return sym;
  return sym;
}
}
 
 
/* Find the number of a general purpose register in S.  */
/* Find the number of a general purpose register in S.  */
static int
static int
scavenge_reg (HARD_REG_SET *s)
scavenge_reg (HARD_REG_SET *s)
{
{
  int r;
  int r;
  for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
  for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
    if (TEST_HARD_REG_BIT (*s, r))
    if (TEST_HARD_REG_BIT (*s, r))
      return r;
      return r;
  return -1;
  return -1;
}
}
 
 
rtx
rtx
sh_get_pr_initial_val (void)
sh_get_pr_initial_val (void)
{
{
  rtx val;
  rtx val;
 
 
  /* ??? Unfortunately, get_hard_reg_initial_val doesn't always work for the
  /* ??? Unfortunately, get_hard_reg_initial_val doesn't always work for the
     PR register on SHcompact, because it might be clobbered by the prologue.
     PR register on SHcompact, because it might be clobbered by the prologue.
     We check first if that is known to be the case.  */
     We check first if that is known to be the case.  */
  if (TARGET_SHCOMPACT
  if (TARGET_SHCOMPACT
      && ((crtl->args.info.call_cookie
      && ((crtl->args.info.call_cookie
           & ~ CALL_COOKIE_RET_TRAMP (1))
           & ~ CALL_COOKIE_RET_TRAMP (1))
          || crtl->saves_all_registers))
          || crtl->saves_all_registers))
    return gen_frame_mem (SImode, return_address_pointer_rtx);
    return gen_frame_mem (SImode, return_address_pointer_rtx);
 
 
  /* If we haven't finished rtl generation, there might be a nonlocal label
  /* If we haven't finished rtl generation, there might be a nonlocal label
     that we haven't seen yet.
     that we haven't seen yet.
     ??? get_hard_reg_initial_val fails if it is called after register
     ??? get_hard_reg_initial_val fails if it is called after register
     allocation has started, unless it has been called before for the
     allocation has started, unless it has been called before for the
     same register.  And even then, we end in trouble if we didn't use
     same register.  And even then, we end in trouble if we didn't use
     the register in the same basic block before.  So call
     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
     get_hard_reg_initial_val now and wrap it in an unspec if we might
     need to replace it.  */
     need to replace it.  */
  /* ??? We also must do this for TARGET_SH1 in general, because otherwise
  /* ??? 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
     combine can put the pseudo returned by get_hard_reg_initial_val into
     instructions that need a general purpose registers, which will fail to
     instructions that need a general purpose registers, which will fail to
     be recognized when the pseudo becomes allocated to PR.  */
     be recognized when the pseudo becomes allocated to PR.  */
  val
  val
    = get_hard_reg_initial_val (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
    = get_hard_reg_initial_val (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
  if (TARGET_SH1)
  if (TARGET_SH1)
    return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA);
    return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA);
  return val;
  return val;
}
}
 
 
int
int
sh_expand_t_scc (rtx operands[])
sh_expand_t_scc (rtx operands[])
{
{
  enum rtx_code code = GET_CODE (operands[1]);
  enum rtx_code code = GET_CODE (operands[1]);
  rtx target = operands[0];
  rtx target = operands[0];
  rtx op0 = operands[2];
  rtx op0 = operands[2];
  rtx op1 = operands[3];
  rtx op1 = operands[3];
  rtx result = target;
  rtx result = target;
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
 
 
  if (!REG_P (op0) || REGNO (op0) != T_REG
  if (!REG_P (op0) || REGNO (op0) != T_REG
      || !CONST_INT_P (op1))
      || !CONST_INT_P (op1))
    return 0;
    return 0;
  if (!REG_P (result))
  if (!REG_P (result))
    result = gen_reg_rtx (SImode);
    result = gen_reg_rtx (SImode);
  val = INTVAL (op1);
  val = INTVAL (op1);
  if ((code == EQ && val == 1) || (code == NE && val == 0))
  if ((code == EQ && val == 1) || (code == NE && val == 0))
    emit_insn (gen_movt (result));
    emit_insn (gen_movt (result));
  else if (TARGET_SH2A && ((code == EQ && val == 0)
  else if (TARGET_SH2A && ((code == EQ && val == 0)
                            || (code == NE && val == 1)))
                            || (code == NE && val == 1)))
    emit_insn (gen_xorsi3_movrt (result));
    emit_insn (gen_xorsi3_movrt (result));
  else if ((code == EQ && val == 0) || (code == NE && val == 1))
  else if ((code == EQ && val == 0) || (code == NE && val == 1))
    {
    {
      emit_clobber (result);
      emit_clobber (result);
      emit_insn (gen_subc (result, result, result));
      emit_insn (gen_subc (result, result, result));
      emit_insn (gen_addsi3 (result, result, const1_rtx));
      emit_insn (gen_addsi3 (result, result, const1_rtx));
    }
    }
  else if (code == EQ || code == NE)
  else if (code == EQ || code == NE)
    emit_insn (gen_move_insn (result, GEN_INT (code == NE)));
    emit_insn (gen_move_insn (result, GEN_INT (code == NE)));
  else
  else
    return 0;
    return 0;
  if (result != target)
  if (result != target)
    emit_move_insn (target, result);
    emit_move_insn (target, result);
  return 1;
  return 1;
}
}
 
 
/* INSN is an sfunc; return the rtx that describes the address used.  */
/* INSN is an sfunc; return the rtx that describes the address used.  */
static rtx
static rtx
extract_sfunc_addr (rtx insn)
extract_sfunc_addr (rtx insn)
{
{
  rtx pattern, part = NULL_RTX;
  rtx pattern, part = NULL_RTX;
  int len, i;
  int len, i;
 
 
  pattern = PATTERN (insn);
  pattern = PATTERN (insn);
  len = XVECLEN (pattern, 0);
  len = XVECLEN (pattern, 0);
  for (i = 0; i < len; i++)
  for (i = 0; i < len; i++)
    {
    {
      part = XVECEXP (pattern, 0, i);
      part = XVECEXP (pattern, 0, i);
      if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == Pmode
      if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == Pmode
          && GENERAL_REGISTER_P (true_regnum (XEXP (part, 0))))
          && GENERAL_REGISTER_P (true_regnum (XEXP (part, 0))))
        return XEXP (part, 0);
        return XEXP (part, 0);
    }
    }
  gcc_assert (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE);
  gcc_assert (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE);
  return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);
  return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);
}
}
 
 
/* Verify that the register in use_sfunc_addr still agrees with the address
/* 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
   used in the sfunc.  This prevents fill_slots_from_thread from changing
   use_sfunc_addr.
   use_sfunc_addr.
   INSN is the use_sfunc_addr instruction, and REG is the register it
   INSN is the use_sfunc_addr instruction, and REG is the register it
   guards.  */
   guards.  */
int
int
check_use_sfunc_addr (rtx insn, rtx reg)
check_use_sfunc_addr (rtx insn, rtx reg)
{
{
  /* Search for the sfunc.  It should really come right after INSN.  */
  /* Search for the sfunc.  It should really come right after INSN.  */
  while ((insn = NEXT_INSN (insn)))
  while ((insn = NEXT_INSN (insn)))
    {
    {
      if (LABEL_P (insn) || JUMP_P (insn))
      if (LABEL_P (insn) || JUMP_P (insn))
        break;
        break;
      if (! INSN_P (insn))
      if (! INSN_P (insn))
        continue;
        continue;
 
 
      if (GET_CODE (PATTERN (insn)) == SEQUENCE)
      if (GET_CODE (PATTERN (insn)) == SEQUENCE)
        insn = XVECEXP (PATTERN (insn), 0, 0);
        insn = XVECEXP (PATTERN (insn), 0, 0);
      if (GET_CODE (PATTERN (insn)) != PARALLEL
      if (GET_CODE (PATTERN (insn)) != PARALLEL
          || get_attr_type (insn) != TYPE_SFUNC)
          || get_attr_type (insn) != TYPE_SFUNC)
        continue;
        continue;
      return rtx_equal_p (extract_sfunc_addr (insn), reg);
      return rtx_equal_p (extract_sfunc_addr (insn), reg);
    }
    }
  gcc_unreachable ();
  gcc_unreachable ();
}
}
 
 
/* This function returns a constant rtx that represents pi / 2**15 in
/* This function returns a constant rtx that represents pi / 2**15 in
   SFmode.  it's used to scale SFmode angles, in radians, to a
   SFmode.  it's used to scale SFmode angles, in radians, to a
   fixed-point signed 16.16-bit fraction of a full circle, i.e., 2*pi
   fixed-point signed 16.16-bit fraction of a full circle, i.e., 2*pi
   maps to 0x10000).  */
   maps to 0x10000).  */
 
 
static GTY(()) rtx sh_fsca_sf2int_rtx;
static GTY(()) rtx sh_fsca_sf2int_rtx;
 
 
rtx
rtx
sh_fsca_sf2int (void)
sh_fsca_sf2int (void)
{
{
  if (! sh_fsca_sf2int_rtx)
  if (! sh_fsca_sf2int_rtx)
    {
    {
      REAL_VALUE_TYPE rv;
      REAL_VALUE_TYPE rv;
 
 
      real_from_string (&rv, "10430.378350470453");
      real_from_string (&rv, "10430.378350470453");
      sh_fsca_sf2int_rtx = const_double_from_real_value (rv, SFmode);
      sh_fsca_sf2int_rtx = const_double_from_real_value (rv, SFmode);
    }
    }
 
 
  return sh_fsca_sf2int_rtx;
  return sh_fsca_sf2int_rtx;
}
}
 
 
/* This function returns a constant rtx that represents pi / 2**15 in
/* This function returns a constant rtx that represents pi / 2**15 in
   DFmode.  it's used to scale DFmode angles, in radians, to a
   DFmode.  it's used to scale DFmode angles, in radians, to a
   fixed-point signed 16.16-bit fraction of a full circle, i.e., 2*pi
   fixed-point signed 16.16-bit fraction of a full circle, i.e., 2*pi
   maps to 0x10000).  */
   maps to 0x10000).  */
 
 
static GTY(()) rtx sh_fsca_df2int_rtx;
static GTY(()) rtx sh_fsca_df2int_rtx;
 
 
rtx
rtx
sh_fsca_df2int (void)
sh_fsca_df2int (void)
{
{
  if (! sh_fsca_df2int_rtx)
  if (! sh_fsca_df2int_rtx)
    {
    {
      REAL_VALUE_TYPE rv;
      REAL_VALUE_TYPE rv;
 
 
      real_from_string (&rv, "10430.378350470453");
      real_from_string (&rv, "10430.378350470453");
      sh_fsca_df2int_rtx = const_double_from_real_value (rv, DFmode);
      sh_fsca_df2int_rtx = const_double_from_real_value (rv, DFmode);
    }
    }
 
 
  return sh_fsca_df2int_rtx;
  return sh_fsca_df2int_rtx;
}
}
 
 
/* This function returns a constant rtx that represents 2**15 / pi in
/* This function returns a constant rtx that represents 2**15 / pi in
   SFmode.  it's used to scale a fixed-point signed 16.16-bit fraction
   SFmode.  it's used to scale a fixed-point signed 16.16-bit fraction
   of a full circle back to a SFmode value, i.e., 0x10000 maps to
   of a full circle back to a SFmode value, i.e., 0x10000 maps to
   2*pi).  */
   2*pi).  */
 
 
static GTY(()) rtx sh_fsca_int2sf_rtx;
static GTY(()) rtx sh_fsca_int2sf_rtx;
 
 
rtx
rtx
sh_fsca_int2sf (void)
sh_fsca_int2sf (void)
{
{
  if (! sh_fsca_int2sf_rtx)
  if (! sh_fsca_int2sf_rtx)
    {
    {
      REAL_VALUE_TYPE rv;
      REAL_VALUE_TYPE rv;
 
 
      real_from_string (&rv, "9.587379924285257e-5");
      real_from_string (&rv, "9.587379924285257e-5");
      sh_fsca_int2sf_rtx = const_double_from_real_value (rv, SFmode);
      sh_fsca_int2sf_rtx = const_double_from_real_value (rv, SFmode);
    }
    }
 
 
  return sh_fsca_int2sf_rtx;
  return sh_fsca_int2sf_rtx;
}
}
 
 
/* Initialize the CUMULATIVE_ARGS structure.  */
/* Initialize the CUMULATIVE_ARGS structure.  */
 
 
void
void
sh_init_cumulative_args (CUMULATIVE_ARGS *  pcum,
sh_init_cumulative_args (CUMULATIVE_ARGS *  pcum,
                         tree               fntype,
                         tree               fntype,
                         rtx                libname ATTRIBUTE_UNUSED,
                         rtx                libname ATTRIBUTE_UNUSED,
                         tree               fndecl,
                         tree               fndecl,
                         signed int         n_named_args,
                         signed int         n_named_args,
                         enum machine_mode  mode)
                         enum machine_mode  mode)
{
{
  pcum->arg_count [(int) SH_ARG_FLOAT] = 0;
  pcum->arg_count [(int) SH_ARG_FLOAT] = 0;
  pcum->free_single_fp_reg = 0;
  pcum->free_single_fp_reg = 0;
  pcum->stack_regs = 0;
  pcum->stack_regs = 0;
  pcum->byref_regs = 0;
  pcum->byref_regs = 0;
  pcum->byref = 0;
  pcum->byref = 0;
  pcum->outgoing = (n_named_args == -1) ? 0 : 1;
  pcum->outgoing = (n_named_args == -1) ? 0 : 1;
 
 
  /* XXX - Should we check TARGET_HITACHI here ???  */
  /* XXX - Should we check TARGET_HITACHI here ???  */
  pcum->renesas_abi = sh_attr_renesas_p (fntype) ? 1 : 0;
  pcum->renesas_abi = sh_attr_renesas_p (fntype) ? 1 : 0;
 
 
  if (fntype)
  if (fntype)
    {
    {
      pcum->force_mem = ((TARGET_HITACHI || pcum->renesas_abi)
      pcum->force_mem = ((TARGET_HITACHI || pcum->renesas_abi)
                         && aggregate_value_p (TREE_TYPE (fntype), fndecl));
                         && aggregate_value_p (TREE_TYPE (fntype), fndecl));
      pcum->prototype_p = TYPE_ARG_TYPES (fntype) ? TRUE : FALSE;
      pcum->prototype_p = TYPE_ARG_TYPES (fntype) ? TRUE : FALSE;
      pcum->arg_count [(int) SH_ARG_INT]
      pcum->arg_count [(int) SH_ARG_INT]
        = TARGET_SH5 && aggregate_value_p (TREE_TYPE (fntype), fndecl);
        = TARGET_SH5 && aggregate_value_p (TREE_TYPE (fntype), fndecl);
 
 
      pcum->call_cookie
      pcum->call_cookie
        = CALL_COOKIE_RET_TRAMP (TARGET_SHCOMPACT
        = CALL_COOKIE_RET_TRAMP (TARGET_SHCOMPACT
                                 && pcum->arg_count [(int) SH_ARG_INT] == 0
                                 && pcum->arg_count [(int) SH_ARG_INT] == 0
                                 && (TYPE_MODE (TREE_TYPE (fntype)) == BLKmode
                                 && (TYPE_MODE (TREE_TYPE (fntype)) == BLKmode
                                     ? int_size_in_bytes (TREE_TYPE (fntype))
                                     ? int_size_in_bytes (TREE_TYPE (fntype))
                                     : GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (fntype)))) > 4
                                     : GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (fntype)))) > 4
                                 && (BASE_RETURN_VALUE_REG (TYPE_MODE (TREE_TYPE (fntype)))
                                 && (BASE_RETURN_VALUE_REG (TYPE_MODE (TREE_TYPE (fntype)))
                                     == FIRST_RET_REG));
                                     == FIRST_RET_REG));
    }
    }
  else
  else
    {
    {
      pcum->arg_count [(int) SH_ARG_INT] = 0;
      pcum->arg_count [(int) SH_ARG_INT] = 0;
      pcum->prototype_p = FALSE;
      pcum->prototype_p = FALSE;
      if (mode != VOIDmode)
      if (mode != VOIDmode)
        {
        {
          pcum->call_cookie =
          pcum->call_cookie =
            CALL_COOKIE_RET_TRAMP (TARGET_SHCOMPACT
            CALL_COOKIE_RET_TRAMP (TARGET_SHCOMPACT
                                   && GET_MODE_SIZE (mode) > 4
                                   && GET_MODE_SIZE (mode) > 4
                                   && BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG);
                                   && BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG);
 
 
          /* If the default ABI is the Renesas ABI then all library
          /* If the default ABI is the Renesas ABI then all library
             calls must assume that the library will be using the
             calls must assume that the library will be using the
             Renesas ABI.  So if the function would return its result
             Renesas ABI.  So if the function would return its result
             in memory then we must force the address of this memory
             in memory then we must force the address of this memory
             block onto the stack.  Ideally we would like to call
             block onto the stack.  Ideally we would like to call
             targetm.calls.return_in_memory() here but we do not have
             targetm.calls.return_in_memory() here but we do not have
             the TYPE or the FNDECL available so we synthesize the
             the TYPE or the FNDECL available so we synthesize the
             contents of that function as best we can.  */
             contents of that function as best we can.  */
          pcum->force_mem =
          pcum->force_mem =
            (TARGET_DEFAULT & MASK_HITACHI)
            (TARGET_DEFAULT & MASK_HITACHI)
            && (mode == BLKmode
            && (mode == BLKmode
                || (GET_MODE_SIZE (mode) > 4
                || (GET_MODE_SIZE (mode) > 4
                    && !(mode == DFmode
                    && !(mode == DFmode
                         && TARGET_FPU_DOUBLE)));
                         && TARGET_FPU_DOUBLE)));
        }
        }
      else
      else
        {
        {
          pcum->call_cookie = 0;
          pcum->call_cookie = 0;
          pcum->force_mem = FALSE;
          pcum->force_mem = FALSE;
        }
        }
    }
    }
}
}
 
 
/* Replace any occurrence of FROM(n) in X with TO(n).  The function does
/* Replace any occurrence of FROM(n) in X with TO(n).  The function does
   not enter into CONST_DOUBLE for the replace.
   not enter into CONST_DOUBLE for the replace.
 
 
   Note that copying is not done so X must not be shared unless all copies
   Note that copying is not done so X must not be shared unless all copies
   are to be modified.
   are to be modified.
 
 
   This is like replace_rtx, except that we operate on N_REPLACEMENTS
   This is like replace_rtx, except that we operate on N_REPLACEMENTS
   replacements simultaneously - FROM(n) is replacements[n*2] and to(n) is
   replacements simultaneously - FROM(n) is replacements[n*2] and to(n) is
   replacements[n*2+1] - and that we take mode changes into account.
   replacements[n*2+1] - and that we take mode changes into account.
 
 
   If a replacement is ambiguous, return NULL_RTX.
   If a replacement is ambiguous, return NULL_RTX.
 
 
   If MODIFY is zero, don't modify any rtl in place,
   If MODIFY is zero, don't modify any rtl in place,
   just return zero or nonzero for failure / success.  */
   just return zero or nonzero for failure / success.  */
 
 
rtx
rtx
replace_n_hard_rtx (rtx x, rtx *replacements, int n_replacements, int modify)
replace_n_hard_rtx (rtx x, rtx *replacements, int n_replacements, int modify)
{
{
  int i, j;
  int i, j;
  const char *fmt;
  const char *fmt;
 
 
  /* The following prevents loops occurrence when we change MEM in
  /* The following prevents loops occurrence when we change MEM in
     CONST_DOUBLE onto the same CONST_DOUBLE.  */
     CONST_DOUBLE onto the same CONST_DOUBLE.  */
  if (x != 0 && GET_CODE (x) == CONST_DOUBLE)
  if (x != 0 && GET_CODE (x) == CONST_DOUBLE)
    return x;
    return x;
 
 
  for (i = n_replacements - 1; i >= 0 ; i--)
  for (i = n_replacements - 1; i >= 0 ; i--)
  if (x == replacements[i*2] && GET_MODE (x) == GET_MODE (replacements[i*2+1]))
  if (x == replacements[i*2] && GET_MODE (x) == GET_MODE (replacements[i*2+1]))
    return replacements[i*2+1];
    return replacements[i*2+1];
 
 
  /* Allow this function to make replacements in EXPR_LISTs.  */
  /* Allow this function to make replacements in EXPR_LISTs.  */
  if (x == 0)
  if (x == 0)
    return 0;
    return 0;
 
 
  if (GET_CODE (x) == SUBREG)
  if (GET_CODE (x) == SUBREG)
    {
    {
      rtx new_rtx = replace_n_hard_rtx (SUBREG_REG (x), replacements,
      rtx new_rtx = replace_n_hard_rtx (SUBREG_REG (x), replacements,
                                    n_replacements, modify);
                                    n_replacements, modify);
 
 
      if (CONST_INT_P (new_rtx))
      if (CONST_INT_P (new_rtx))
        {
        {
          x = simplify_subreg (GET_MODE (x), new_rtx,
          x = simplify_subreg (GET_MODE (x), new_rtx,
                               GET_MODE (SUBREG_REG (x)),
                               GET_MODE (SUBREG_REG (x)),
                               SUBREG_BYTE (x));
                               SUBREG_BYTE (x));
          if (! x)
          if (! x)
            abort ();
            abort ();
        }
        }
      else if (modify)
      else if (modify)
        SUBREG_REG (x) = new_rtx;
        SUBREG_REG (x) = new_rtx;
 
 
      return x;
      return x;
    }
    }
  else if (REG_P (x))
  else if (REG_P (x))
    {
    {
      unsigned regno = REGNO (x);
      unsigned regno = REGNO (x);
      unsigned nregs = (regno < FIRST_PSEUDO_REGISTER
      unsigned nregs = (regno < FIRST_PSEUDO_REGISTER
                        ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
                        ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
      rtx result = NULL_RTX;
      rtx result = NULL_RTX;
 
 
      for (i = n_replacements - 1; i >= 0; i--)
      for (i = n_replacements - 1; i >= 0; i--)
        {
        {
          rtx from = replacements[i*2];
          rtx from = replacements[i*2];
          rtx to = replacements[i*2+1];
          rtx to = replacements[i*2+1];
          unsigned from_regno, from_nregs, to_regno, new_regno;
          unsigned from_regno, from_nregs, to_regno, new_regno;
 
 
          if (!REG_P (from))
          if (!REG_P (from))
            continue;
            continue;
          from_regno = REGNO (from);
          from_regno = REGNO (from);
          from_nregs = (from_regno < FIRST_PSEUDO_REGISTER
          from_nregs = (from_regno < FIRST_PSEUDO_REGISTER
                        ? HARD_REGNO_NREGS (from_regno, GET_MODE (from)) : 1);
                        ? HARD_REGNO_NREGS (from_regno, GET_MODE (from)) : 1);
          if (regno < from_regno + from_nregs && regno + nregs > from_regno)
          if (regno < from_regno + from_nregs && regno + nregs > from_regno)
            {
            {
              if (regno < from_regno
              if (regno < from_regno
                  || regno + nregs > from_regno + nregs
                  || regno + nregs > from_regno + nregs
                  || !REG_P (to)
                  || !REG_P (to)
                  || result)
                  || result)
                return NULL_RTX;
                return NULL_RTX;
              to_regno = REGNO (to);
              to_regno = REGNO (to);
              if (to_regno < FIRST_PSEUDO_REGISTER)
              if (to_regno < FIRST_PSEUDO_REGISTER)
                {
                {
                  new_regno = regno + to_regno - from_regno;
                  new_regno = regno + to_regno - from_regno;
                  if ((unsigned) HARD_REGNO_NREGS (new_regno, GET_MODE (x))
                  if ((unsigned) HARD_REGNO_NREGS (new_regno, GET_MODE (x))
                      != nregs)
                      != nregs)
                    return NULL_RTX;
                    return NULL_RTX;
                  result = gen_rtx_REG (GET_MODE (x), new_regno);
                  result = gen_rtx_REG (GET_MODE (x), new_regno);
                }
                }
              else if (GET_MODE (x) <= GET_MODE (to))
              else if (GET_MODE (x) <= GET_MODE (to))
                result = gen_lowpart_common (GET_MODE (x), to);
                result = gen_lowpart_common (GET_MODE (x), to);
              else
              else
                result = gen_lowpart_SUBREG (GET_MODE (x), to);
                result = gen_lowpart_SUBREG (GET_MODE (x), to);
            }
            }
        }
        }
      return result ? result : x;
      return result ? result : x;
    }
    }
  else if (GET_CODE (x) == ZERO_EXTEND)
  else if (GET_CODE (x) == ZERO_EXTEND)
    {
    {
      rtx new_rtx = replace_n_hard_rtx (XEXP (x, 0), replacements,
      rtx new_rtx = replace_n_hard_rtx (XEXP (x, 0), replacements,
                                    n_replacements, modify);
                                    n_replacements, modify);
 
 
      if (CONST_INT_P (new_rtx))
      if (CONST_INT_P (new_rtx))
        {
        {
          x = simplify_unary_operation (ZERO_EXTEND, GET_MODE (x),
          x = simplify_unary_operation (ZERO_EXTEND, GET_MODE (x),
                                        new_rtx, GET_MODE (XEXP (x, 0)));
                                        new_rtx, GET_MODE (XEXP (x, 0)));
          if (! x)
          if (! x)
            abort ();
            abort ();
        }
        }
      else if (modify)
      else if (modify)
        XEXP (x, 0) = new_rtx;
        XEXP (x, 0) = new_rtx;
 
 
      return x;
      return x;
    }
    }
 
 
  fmt = GET_RTX_FORMAT (GET_CODE (x));
  fmt = GET_RTX_FORMAT (GET_CODE (x));
  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
    {
    {
      rtx new_rtx;
      rtx new_rtx;
 
 
      if (fmt[i] == 'e')
      if (fmt[i] == 'e')
        {
        {
          new_rtx = replace_n_hard_rtx (XEXP (x, i), replacements,
          new_rtx = replace_n_hard_rtx (XEXP (x, i), replacements,
                                    n_replacements, modify);
                                    n_replacements, modify);
          if (!new_rtx)
          if (!new_rtx)
            return NULL_RTX;
            return NULL_RTX;
          if (modify)
          if (modify)
            XEXP (x, i) = new_rtx;
            XEXP (x, i) = new_rtx;
        }
        }
      else if (fmt[i] == 'E')
      else if (fmt[i] == 'E')
        for (j = XVECLEN (x, i) - 1; j >= 0; j--)
        for (j = XVECLEN (x, i) - 1; j >= 0; j--)
          {
          {
            new_rtx = replace_n_hard_rtx (XVECEXP (x, i, j), replacements,
            new_rtx = replace_n_hard_rtx (XVECEXP (x, i, j), replacements,
                                      n_replacements, modify);
                                      n_replacements, modify);
          if (!new_rtx)
          if (!new_rtx)
            return NULL_RTX;
            return NULL_RTX;
            if (modify)
            if (modify)
              XVECEXP (x, i, j) = new_rtx;
              XVECEXP (x, i, j) = new_rtx;
          }
          }
    }
    }
 
 
  return x;
  return x;
}
}
 
 
rtx
rtx
sh_gen_truncate (enum machine_mode mode, rtx x, int need_sign_ext)
sh_gen_truncate (enum machine_mode mode, rtx x, int need_sign_ext)
{
{
  enum rtx_code code = TRUNCATE;
  enum rtx_code code = TRUNCATE;
 
 
  if (GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
  if (GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
    {
    {
      rtx inner = XEXP (x, 0);
      rtx inner = XEXP (x, 0);
      enum machine_mode inner_mode = GET_MODE (inner);
      enum machine_mode inner_mode = GET_MODE (inner);
 
 
      if (inner_mode == mode)
      if (inner_mode == mode)
        return inner;
        return inner;
      else if (GET_MODE_SIZE (inner_mode) >= GET_MODE_SIZE (mode))
      else if (GET_MODE_SIZE (inner_mode) >= GET_MODE_SIZE (mode))
        x = inner;
        x = inner;
      else if (GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (mode)
      else if (GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (mode)
               && (! need_sign_ext || GET_CODE (x) == SIGN_EXTEND))
               && (! need_sign_ext || GET_CODE (x) == SIGN_EXTEND))
        {
        {
          code = GET_CODE (x);
          code = GET_CODE (x);
          x = inner;
          x = inner;
        }
        }
    }
    }
  return gen_rtx_fmt_e (code, mode, x);
  return gen_rtx_fmt_e (code, mode, x);
}
}
 
 
/* called via for_each_rtx after reload, to clean up truncates of
/* called via for_each_rtx after reload, to clean up truncates of
   registers that span multiple actual hard registers.  */
   registers that span multiple actual hard registers.  */
int
int
shmedia_cleanup_truncate (rtx *p, void *n_changes)
shmedia_cleanup_truncate (rtx *p, void *n_changes)
{
{
  rtx x = *p, reg;
  rtx x = *p, reg;
 
 
  if (GET_CODE (x) != TRUNCATE)
  if (GET_CODE (x) != TRUNCATE)
    return 0;
    return 0;
  reg = XEXP (x, 0);
  reg = XEXP (x, 0);
  if (GET_MODE_SIZE (GET_MODE (reg)) > 8 && REG_P (reg))
  if (GET_MODE_SIZE (GET_MODE (reg)) > 8 && REG_P (reg))
    {
    {
      enum machine_mode reg_mode = GET_MODE (reg);
      enum machine_mode reg_mode = GET_MODE (reg);
      XEXP (x, 0) = simplify_subreg (DImode, reg, reg_mode,
      XEXP (x, 0) = simplify_subreg (DImode, reg, reg_mode,
                                     subreg_lowpart_offset (DImode, reg_mode));
                                     subreg_lowpart_offset (DImode, reg_mode));
      *(int*) n_changes += 1;
      *(int*) n_changes += 1;
      return -1;
      return -1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Load and store depend on the highpart of the address.  However,
/* Load and store depend on the highpart of the address.  However,
   set_attr_alternative does not give well-defined results before reload,
   set_attr_alternative does not give well-defined results before reload,
   so we must look at the rtl ourselves to see if any of the feeding
   so we must look at the rtl ourselves to see if any of the feeding
   registers is used in a memref.  */
   registers is used in a memref.  */
 
 
/* Called by sh_contains_memref_p via for_each_rtx.  */
/* Called by sh_contains_memref_p via for_each_rtx.  */
static int
static int
sh_contains_memref_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
sh_contains_memref_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
{
{
  return (MEM_P (*loc));
  return (MEM_P (*loc));
}
}
 
 
/* Return nonzero iff INSN contains a MEM.  */
/* Return nonzero iff INSN contains a MEM.  */
int
int
sh_contains_memref_p (rtx insn)
sh_contains_memref_p (rtx insn)
{
{
  return for_each_rtx (&PATTERN (insn), &sh_contains_memref_p_1, NULL);
  return for_each_rtx (&PATTERN (insn), &sh_contains_memref_p_1, NULL);
}
}
 
 
/* Return nonzero iff INSN loads a banked register.  */
/* Return nonzero iff INSN loads a banked register.  */
int
int
sh_loads_bankedreg_p (rtx insn)
sh_loads_bankedreg_p (rtx insn)
{
{
  if (GET_CODE (PATTERN (insn)) == SET)
  if (GET_CODE (PATTERN (insn)) == SET)
    {
    {
      rtx op = SET_DEST (PATTERN(insn));
      rtx op = SET_DEST (PATTERN(insn));
      if (REG_P (op) && BANKED_REGISTER_P (REGNO (op)))
      if (REG_P (op) && BANKED_REGISTER_P (REGNO (op)))
        return 1;
        return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* FNADDR is the MEM expression from a call expander.  Return an address
/* FNADDR is the MEM expression from a call expander.  Return an address
   to use in an SHmedia insn pattern.  */
   to use in an SHmedia insn pattern.  */
rtx
rtx
shmedia_prepare_call_address (rtx fnaddr, int is_sibcall)
shmedia_prepare_call_address (rtx fnaddr, int is_sibcall)
{
{
  int is_sym;
  int is_sym;
 
 
  fnaddr = XEXP (fnaddr, 0);
  fnaddr = XEXP (fnaddr, 0);
  is_sym = GET_CODE (fnaddr) == SYMBOL_REF;
  is_sym = GET_CODE (fnaddr) == SYMBOL_REF;
  if (flag_pic && is_sym)
  if (flag_pic && is_sym)
    {
    {
      if (! SYMBOL_REF_LOCAL_P (fnaddr))
      if (! SYMBOL_REF_LOCAL_P (fnaddr))
        {
        {
          rtx reg = gen_reg_rtx (Pmode);
          rtx reg = gen_reg_rtx (Pmode);
 
 
          /* We must not use GOTPLT for sibcalls, because PIC_REG
          /* We must not use GOTPLT for sibcalls, because PIC_REG
             must be restored before the PLT code gets to run.  */
             must be restored before the PLT code gets to run.  */
          if (is_sibcall)
          if (is_sibcall)
            emit_insn (gen_symGOT2reg (reg, fnaddr));
            emit_insn (gen_symGOT2reg (reg, fnaddr));
          else
          else
            emit_insn (gen_symGOTPLT2reg (reg, fnaddr));
            emit_insn (gen_symGOTPLT2reg (reg, fnaddr));
          fnaddr = reg;
          fnaddr = reg;
        }
        }
      else
      else
        {
        {
          fnaddr = gen_sym2PIC (fnaddr);
          fnaddr = gen_sym2PIC (fnaddr);
          PUT_MODE (fnaddr, Pmode);
          PUT_MODE (fnaddr, Pmode);
        }
        }
    }
    }
  /* If ptabs might trap, make this visible to the rest of the compiler.
  /* If ptabs might trap, make this visible to the rest of the compiler.
     We generally assume that symbols pertain to valid locations, but
     We generally assume that symbols pertain to valid locations, but
     it is possible to generate invalid symbols with asm or linker tricks.
     it is possible to generate invalid symbols with asm or linker tricks.
     In a list of functions where each returns its successor, an invalid
     In a list of functions where each returns its successor, an invalid
     symbol might denote an empty list.  */
     symbol might denote an empty list.  */
  if (!TARGET_PT_FIXED
  if (!TARGET_PT_FIXED
      && (!is_sym || TARGET_INVALID_SYMBOLS)
      && (!is_sym || TARGET_INVALID_SYMBOLS)
      && (!REG_P (fnaddr) || ! TARGET_REGISTER_P (REGNO (fnaddr))))
      && (!REG_P (fnaddr) || ! TARGET_REGISTER_P (REGNO (fnaddr))))
    {
    {
      rtx tr = gen_reg_rtx (PDImode);
      rtx tr = gen_reg_rtx (PDImode);
 
 
      emit_insn (gen_ptabs (tr, fnaddr));
      emit_insn (gen_ptabs (tr, fnaddr));
      fnaddr = tr;
      fnaddr = tr;
    }
    }
  else if (! target_reg_operand (fnaddr, Pmode))
  else if (! target_reg_operand (fnaddr, Pmode))
    fnaddr = copy_to_mode_reg (Pmode, fnaddr);
    fnaddr = copy_to_mode_reg (Pmode, fnaddr);
  return fnaddr;
  return fnaddr;
}
}
 
 
enum reg_class
enum reg_class
sh_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
sh_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
                     enum machine_mode mode, secondary_reload_info *sri)
                     enum machine_mode mode, secondary_reload_info *sri)
{
{
  if (in_p)
  if (in_p)
    {
    {
      if (REGCLASS_HAS_FP_REG (rclass)
      if (REGCLASS_HAS_FP_REG (rclass)
          && ! TARGET_SHMEDIA
          && ! TARGET_SHMEDIA
          && immediate_operand ((x), mode)
          && immediate_operand ((x), mode)
          && ! ((fp_zero_operand (x) || fp_one_operand (x))
          && ! ((fp_zero_operand (x) || fp_one_operand (x))
                && mode == SFmode && fldi_ok ()))
                && mode == SFmode && fldi_ok ()))
        switch (mode)
        switch (mode)
          {
          {
          case SFmode:
          case SFmode:
            sri->icode = CODE_FOR_reload_insf__frn;
            sri->icode = CODE_FOR_reload_insf__frn;
            return NO_REGS;
            return NO_REGS;
          case DFmode:
          case DFmode:
            sri->icode = CODE_FOR_reload_indf__frn;
            sri->icode = CODE_FOR_reload_indf__frn;
            return NO_REGS;
            return NO_REGS;
          case SImode:
          case SImode:
            /* ??? If we knew that we are in the appropriate mode -
            /* ??? If we knew that we are in the appropriate mode -
               single precision - we could use a reload pattern directly.  */
               single precision - we could use a reload pattern directly.  */
            return FPUL_REGS;
            return FPUL_REGS;
          default:
          default:
            abort ();
            abort ();
          }
          }
      if (rclass == FPUL_REGS
      if (rclass == FPUL_REGS
          && ((REG_P (x)
          && ((REG_P (x)
               && (REGNO (x) == MACL_REG || REGNO (x) == MACH_REG
               && (REGNO (x) == MACL_REG || REGNO (x) == MACH_REG
                   || REGNO (x) == T_REG))
                   || REGNO (x) == T_REG))
              || GET_CODE (x) == PLUS))
              || GET_CODE (x) == PLUS))
        return GENERAL_REGS;
        return GENERAL_REGS;
      if (rclass == FPUL_REGS && immediate_operand (x, mode))
      if (rclass == FPUL_REGS && immediate_operand (x, mode))
        {
        {
          if (satisfies_constraint_I08 (x) || fp_zero_operand (x))
          if (satisfies_constraint_I08 (x) || fp_zero_operand (x))
            return GENERAL_REGS;
            return GENERAL_REGS;
          else if (mode == SFmode)
          else if (mode == SFmode)
            return FP_REGS;
            return FP_REGS;
          sri->icode = CODE_FOR_reload_insi__i_fpul;
          sri->icode = CODE_FOR_reload_insi__i_fpul;
          return NO_REGS;
          return NO_REGS;
        }
        }
      if (rclass == FPSCR_REGS
      if (rclass == FPSCR_REGS
          && ((REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER)
          && ((REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER)
              || (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS)))
              || (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS)))
        return GENERAL_REGS;
        return GENERAL_REGS;
      if (REGCLASS_HAS_FP_REG (rclass)
      if (REGCLASS_HAS_FP_REG (rclass)
          && TARGET_SHMEDIA
          && TARGET_SHMEDIA
          && immediate_operand (x, mode)
          && immediate_operand (x, mode)
          && x != CONST0_RTX (GET_MODE (x))
          && x != CONST0_RTX (GET_MODE (x))
          && GET_MODE (x) != V4SFmode)
          && GET_MODE (x) != V4SFmode)
        return GENERAL_REGS;
        return GENERAL_REGS;
      if ((mode == QImode || mode == HImode)
      if ((mode == QImode || mode == HImode)
          && TARGET_SHMEDIA && inqhi_operand (x, mode))
          && TARGET_SHMEDIA && inqhi_operand (x, mode))
        {
        {
          sri->icode = ((mode == QImode)
          sri->icode = ((mode == QImode)
                        ? CODE_FOR_reload_inqi : CODE_FOR_reload_inhi);
                        ? CODE_FOR_reload_inqi : CODE_FOR_reload_inhi);
          return NO_REGS;
          return NO_REGS;
        }
        }
      if (TARGET_SHMEDIA && rclass == GENERAL_REGS
      if (TARGET_SHMEDIA && rclass == GENERAL_REGS
          && (GET_CODE (x) == LABEL_REF || PIC_ADDR_P (x)))
          && (GET_CODE (x) == LABEL_REF || PIC_ADDR_P (x)))
        return TARGET_REGS;
        return TARGET_REGS;
    } /* end of input-only processing.  */
    } /* end of input-only processing.  */
 
 
  if (((REGCLASS_HAS_FP_REG (rclass)
  if (((REGCLASS_HAS_FP_REG (rclass)
        && (REG_P (x)
        && (REG_P (x)
            && (GENERAL_OR_AP_REGISTER_P (REGNO (x))
            && (GENERAL_OR_AP_REGISTER_P (REGNO (x))
                || (FP_REGISTER_P (REGNO (x)) && mode == SImode
                || (FP_REGISTER_P (REGNO (x)) && mode == SImode
                    && TARGET_FMOVD))))
                    && TARGET_FMOVD))))
       || (REGCLASS_HAS_GENERAL_REG (rclass)
       || (REGCLASS_HAS_GENERAL_REG (rclass)
           && REG_P (x)
           && REG_P (x)
           && FP_REGISTER_P (REGNO (x))))
           && FP_REGISTER_P (REGNO (x))))
      && ! TARGET_SHMEDIA
      && ! TARGET_SHMEDIA
      && (mode == SFmode || mode == SImode))
      && (mode == SFmode || mode == SImode))
    return FPUL_REGS;
    return FPUL_REGS;
  if ((rclass == FPUL_REGS
  if ((rclass == FPUL_REGS
       || (REGCLASS_HAS_FP_REG (rclass)
       || (REGCLASS_HAS_FP_REG (rclass)
           && ! TARGET_SHMEDIA && mode == SImode))
           && ! TARGET_SHMEDIA && mode == SImode))
      && (MEM_P (x)
      && (MEM_P (x)
          || (REG_P (x)
          || (REG_P (x)
              && (REGNO (x) >= FIRST_PSEUDO_REGISTER
              && (REGNO (x) >= FIRST_PSEUDO_REGISTER
                  || REGNO (x) == T_REG
                  || REGNO (x) == T_REG
                  || system_reg_operand (x, VOIDmode)))))
                  || system_reg_operand (x, VOIDmode)))))
    {
    {
      if (rclass == FPUL_REGS)
      if (rclass == FPUL_REGS)
        return GENERAL_REGS;
        return GENERAL_REGS;
      return FPUL_REGS;
      return FPUL_REGS;
    }
    }
  if ((rclass == TARGET_REGS
  if ((rclass == TARGET_REGS
       || (TARGET_SHMEDIA && rclass == SIBCALL_REGS))
       || (TARGET_SHMEDIA && rclass == SIBCALL_REGS))
      && !satisfies_constraint_Csy (x)
      && !satisfies_constraint_Csy (x)
      && (!REG_P (x) || ! GENERAL_REGISTER_P (REGNO (x))))
      && (!REG_P (x) || ! GENERAL_REGISTER_P (REGNO (x))))
    return GENERAL_REGS;
    return GENERAL_REGS;
  if ((rclass == MAC_REGS || rclass == PR_REGS)
  if ((rclass == MAC_REGS || rclass == PR_REGS)
      && REG_P (x) && ! GENERAL_REGISTER_P (REGNO (x))
      && REG_P (x) && ! GENERAL_REGISTER_P (REGNO (x))
      && rclass != REGNO_REG_CLASS (REGNO (x)))
      && rclass != REGNO_REG_CLASS (REGNO (x)))
    return GENERAL_REGS;
    return GENERAL_REGS;
  if (rclass != GENERAL_REGS && REG_P (x)
  if (rclass != GENERAL_REGS && REG_P (x)
      && TARGET_REGISTER_P (REGNO (x)))
      && TARGET_REGISTER_P (REGNO (x)))
    return GENERAL_REGS;
    return GENERAL_REGS;
  return NO_REGS;
  return NO_REGS;
}
}
 
 
enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
 
 
#include "gt-sh.h"
#include "gt-sh.h"
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.