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.0rc2/] [gcc/] [config/] [spu/] [spu.c] - Diff between revs 378 and 384

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

Rev 378 Rev 384
/* Copyright (C) 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
/* Copyright (C) 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 
   This file is free software; you can redistribute it and/or modify it under
   This file is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3 of the License, or (at your option)
   Software Foundation; either version 3 of the License, or (at your option)
   any later version.
   any later version.
 
 
   This file is distributed in the hope that it will be useful, but WITHOUT
   This file is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.
   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 "rtl.h"
#include "rtl.h"
#include "regs.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "real.h"
#include "real.h"
#include "insn-config.h"
#include "insn-config.h"
#include "conditions.h"
#include "conditions.h"
#include "insn-attr.h"
#include "insn-attr.h"
#include "flags.h"
#include "flags.h"
#include "recog.h"
#include "recog.h"
#include "obstack.h"
#include "obstack.h"
#include "tree.h"
#include "tree.h"
#include "expr.h"
#include "expr.h"
#include "optabs.h"
#include "optabs.h"
#include "except.h"
#include "except.h"
#include "function.h"
#include "function.h"
#include "output.h"
#include "output.h"
#include "basic-block.h"
#include "basic-block.h"
#include "integrate.h"
#include "integrate.h"
#include "toplev.h"
#include "toplev.h"
#include "ggc.h"
#include "ggc.h"
#include "hashtab.h"
#include "hashtab.h"
#include "tm_p.h"
#include "tm_p.h"
#include "target.h"
#include "target.h"
#include "target-def.h"
#include "target-def.h"
#include "langhooks.h"
#include "langhooks.h"
#include "reload.h"
#include "reload.h"
#include "cfglayout.h"
#include "cfglayout.h"
#include "sched-int.h"
#include "sched-int.h"
#include "params.h"
#include "params.h"
#include "assert.h"
#include "assert.h"
#include "machmode.h"
#include "machmode.h"
#include "gimple.h"
#include "gimple.h"
#include "tm-constrs.h"
#include "tm-constrs.h"
#include "ddg.h"
#include "ddg.h"
#include "sbitmap.h"
#include "sbitmap.h"
#include "timevar.h"
#include "timevar.h"
#include "df.h"
#include "df.h"
 
 
/* Builtin types, data and prototypes. */
/* Builtin types, data and prototypes. */
 
 
enum spu_builtin_type_index
enum spu_builtin_type_index
{
{
  SPU_BTI_END_OF_PARAMS,
  SPU_BTI_END_OF_PARAMS,
 
 
  /* We create new type nodes for these. */
  /* We create new type nodes for these. */
  SPU_BTI_V16QI,
  SPU_BTI_V16QI,
  SPU_BTI_V8HI,
  SPU_BTI_V8HI,
  SPU_BTI_V4SI,
  SPU_BTI_V4SI,
  SPU_BTI_V2DI,
  SPU_BTI_V2DI,
  SPU_BTI_V4SF,
  SPU_BTI_V4SF,
  SPU_BTI_V2DF,
  SPU_BTI_V2DF,
  SPU_BTI_UV16QI,
  SPU_BTI_UV16QI,
  SPU_BTI_UV8HI,
  SPU_BTI_UV8HI,
  SPU_BTI_UV4SI,
  SPU_BTI_UV4SI,
  SPU_BTI_UV2DI,
  SPU_BTI_UV2DI,
 
 
  /* A 16-byte type. (Implemented with V16QI_type_node) */
  /* A 16-byte type. (Implemented with V16QI_type_node) */
  SPU_BTI_QUADWORD,
  SPU_BTI_QUADWORD,
 
 
  /* These all correspond to intSI_type_node */
  /* These all correspond to intSI_type_node */
  SPU_BTI_7,
  SPU_BTI_7,
  SPU_BTI_S7,
  SPU_BTI_S7,
  SPU_BTI_U7,
  SPU_BTI_U7,
  SPU_BTI_S10,
  SPU_BTI_S10,
  SPU_BTI_S10_4,
  SPU_BTI_S10_4,
  SPU_BTI_U14,
  SPU_BTI_U14,
  SPU_BTI_16,
  SPU_BTI_16,
  SPU_BTI_S16,
  SPU_BTI_S16,
  SPU_BTI_S16_2,
  SPU_BTI_S16_2,
  SPU_BTI_U16,
  SPU_BTI_U16,
  SPU_BTI_U16_2,
  SPU_BTI_U16_2,
  SPU_BTI_U18,
  SPU_BTI_U18,
 
 
  /* These correspond to the standard types */
  /* These correspond to the standard types */
  SPU_BTI_INTQI,
  SPU_BTI_INTQI,
  SPU_BTI_INTHI,
  SPU_BTI_INTHI,
  SPU_BTI_INTSI,
  SPU_BTI_INTSI,
  SPU_BTI_INTDI,
  SPU_BTI_INTDI,
 
 
  SPU_BTI_UINTQI,
  SPU_BTI_UINTQI,
  SPU_BTI_UINTHI,
  SPU_BTI_UINTHI,
  SPU_BTI_UINTSI,
  SPU_BTI_UINTSI,
  SPU_BTI_UINTDI,
  SPU_BTI_UINTDI,
 
 
  SPU_BTI_FLOAT,
  SPU_BTI_FLOAT,
  SPU_BTI_DOUBLE,
  SPU_BTI_DOUBLE,
 
 
  SPU_BTI_VOID,
  SPU_BTI_VOID,
  SPU_BTI_PTR,
  SPU_BTI_PTR,
 
 
  SPU_BTI_MAX
  SPU_BTI_MAX
};
};
 
 
#define V16QI_type_node               (spu_builtin_types[SPU_BTI_V16QI])
#define V16QI_type_node               (spu_builtin_types[SPU_BTI_V16QI])
#define V8HI_type_node                (spu_builtin_types[SPU_BTI_V8HI])
#define V8HI_type_node                (spu_builtin_types[SPU_BTI_V8HI])
#define V4SI_type_node                (spu_builtin_types[SPU_BTI_V4SI])
#define V4SI_type_node                (spu_builtin_types[SPU_BTI_V4SI])
#define V2DI_type_node                (spu_builtin_types[SPU_BTI_V2DI])
#define V2DI_type_node                (spu_builtin_types[SPU_BTI_V2DI])
#define V4SF_type_node                (spu_builtin_types[SPU_BTI_V4SF])
#define V4SF_type_node                (spu_builtin_types[SPU_BTI_V4SF])
#define V2DF_type_node                (spu_builtin_types[SPU_BTI_V2DF])
#define V2DF_type_node                (spu_builtin_types[SPU_BTI_V2DF])
#define unsigned_V16QI_type_node      (spu_builtin_types[SPU_BTI_UV16QI])
#define unsigned_V16QI_type_node      (spu_builtin_types[SPU_BTI_UV16QI])
#define unsigned_V8HI_type_node       (spu_builtin_types[SPU_BTI_UV8HI])
#define unsigned_V8HI_type_node       (spu_builtin_types[SPU_BTI_UV8HI])
#define unsigned_V4SI_type_node       (spu_builtin_types[SPU_BTI_UV4SI])
#define unsigned_V4SI_type_node       (spu_builtin_types[SPU_BTI_UV4SI])
#define unsigned_V2DI_type_node       (spu_builtin_types[SPU_BTI_UV2DI])
#define unsigned_V2DI_type_node       (spu_builtin_types[SPU_BTI_UV2DI])
 
 
static GTY(()) tree spu_builtin_types[SPU_BTI_MAX];
static GTY(()) tree spu_builtin_types[SPU_BTI_MAX];
 
 
struct spu_builtin_range
struct spu_builtin_range
{
{
  int low, high;
  int low, high;
};
};
 
 
static struct spu_builtin_range spu_builtin_range[] = {
static struct spu_builtin_range spu_builtin_range[] = {
  {-0x40ll, 0x7fll},            /* SPU_BTI_7     */
  {-0x40ll, 0x7fll},            /* SPU_BTI_7     */
  {-0x40ll, 0x3fll},            /* SPU_BTI_S7    */
  {-0x40ll, 0x3fll},            /* SPU_BTI_S7    */
  {0ll, 0x7fll},                /* SPU_BTI_U7    */
  {0ll, 0x7fll},                /* SPU_BTI_U7    */
  {-0x200ll, 0x1ffll},          /* SPU_BTI_S10   */
  {-0x200ll, 0x1ffll},          /* SPU_BTI_S10   */
  {-0x2000ll, 0x1fffll},        /* SPU_BTI_S10_4 */
  {-0x2000ll, 0x1fffll},        /* SPU_BTI_S10_4 */
  {0ll, 0x3fffll},              /* SPU_BTI_U14   */
  {0ll, 0x3fffll},              /* SPU_BTI_U14   */
  {-0x8000ll, 0xffffll},        /* SPU_BTI_16    */
  {-0x8000ll, 0xffffll},        /* SPU_BTI_16    */
  {-0x8000ll, 0x7fffll},        /* SPU_BTI_S16   */
  {-0x8000ll, 0x7fffll},        /* SPU_BTI_S16   */
  {-0x20000ll, 0x1ffffll},      /* SPU_BTI_S16_2 */
  {-0x20000ll, 0x1ffffll},      /* SPU_BTI_S16_2 */
  {0ll, 0xffffll},              /* SPU_BTI_U16   */
  {0ll, 0xffffll},              /* SPU_BTI_U16   */
  {0ll, 0x3ffffll},             /* SPU_BTI_U16_2 */
  {0ll, 0x3ffffll},             /* SPU_BTI_U16_2 */
  {0ll, 0x3ffffll},             /* SPU_BTI_U18   */
  {0ll, 0x3ffffll},             /* SPU_BTI_U18   */
};
};
 
 


/*  Target specific attribute specifications.  */
/*  Target specific attribute specifications.  */
char regs_ever_allocated[FIRST_PSEUDO_REGISTER];
char regs_ever_allocated[FIRST_PSEUDO_REGISTER];
 
 
/*  Prototypes and external defs.  */
/*  Prototypes and external defs.  */
static void spu_init_builtins (void);
static void spu_init_builtins (void);
static tree spu_builtin_decl (unsigned, bool);
static tree spu_builtin_decl (unsigned, bool);
static unsigned char spu_scalar_mode_supported_p (enum machine_mode mode);
static unsigned char spu_scalar_mode_supported_p (enum machine_mode mode);
static unsigned char spu_vector_mode_supported_p (enum machine_mode mode);
static unsigned char spu_vector_mode_supported_p (enum machine_mode mode);
static bool spu_legitimate_address_p (enum machine_mode, rtx, bool);
static bool spu_legitimate_address_p (enum machine_mode, rtx, bool);
static bool spu_addr_space_legitimate_address_p (enum machine_mode, rtx,
static bool spu_addr_space_legitimate_address_p (enum machine_mode, rtx,
                                                 bool, addr_space_t);
                                                 bool, addr_space_t);
static rtx adjust_operand (rtx op, HOST_WIDE_INT * start);
static rtx adjust_operand (rtx op, HOST_WIDE_INT * start);
static rtx get_pic_reg (void);
static rtx get_pic_reg (void);
static int need_to_save_reg (int regno, int saving);
static int need_to_save_reg (int regno, int saving);
static rtx frame_emit_store (int regno, rtx addr, HOST_WIDE_INT offset);
static rtx frame_emit_store (int regno, rtx addr, HOST_WIDE_INT offset);
static rtx frame_emit_load (int regno, rtx addr, HOST_WIDE_INT offset);
static rtx frame_emit_load (int regno, rtx addr, HOST_WIDE_INT offset);
static rtx frame_emit_add_imm (rtx dst, rtx src, HOST_WIDE_INT imm,
static rtx frame_emit_add_imm (rtx dst, rtx src, HOST_WIDE_INT imm,
                               rtx scratch);
                               rtx scratch);
static void emit_nop_for_insn (rtx insn);
static void emit_nop_for_insn (rtx insn);
static bool insn_clobbers_hbr (rtx insn);
static bool insn_clobbers_hbr (rtx insn);
static void spu_emit_branch_hint (rtx before, rtx branch, rtx target,
static void spu_emit_branch_hint (rtx before, rtx branch, rtx target,
                                  int distance, sbitmap blocks);
                                  int distance, sbitmap blocks);
static rtx spu_emit_vector_compare (enum rtx_code rcode, rtx op0, rtx op1,
static rtx spu_emit_vector_compare (enum rtx_code rcode, rtx op0, rtx op1,
                                    enum machine_mode dmode);
                                    enum machine_mode dmode);
static rtx get_branch_target (rtx branch);
static rtx get_branch_target (rtx branch);
static void spu_machine_dependent_reorg (void);
static void spu_machine_dependent_reorg (void);
static int spu_sched_issue_rate (void);
static int spu_sched_issue_rate (void);
static int spu_sched_variable_issue (FILE * dump, int verbose, rtx insn,
static int spu_sched_variable_issue (FILE * dump, int verbose, rtx insn,
                                     int can_issue_more);
                                     int can_issue_more);
static int get_pipe (rtx insn);
static int get_pipe (rtx insn);
static int spu_sched_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost);
static int spu_sched_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost);
static void spu_sched_init_global (FILE *, int, int);
static void spu_sched_init_global (FILE *, int, int);
static void spu_sched_init (FILE *, int, int);
static void spu_sched_init (FILE *, int, int);
static int spu_sched_reorder (FILE *, int, rtx *, int *, int);
static int spu_sched_reorder (FILE *, int, rtx *, int *, int);
static tree spu_handle_fndecl_attribute (tree * node, tree name, tree args,
static tree spu_handle_fndecl_attribute (tree * node, tree name, tree args,
                                         int flags,
                                         int flags,
                                         unsigned char *no_add_attrs);
                                         unsigned char *no_add_attrs);
static tree spu_handle_vector_attribute (tree * node, tree name, tree args,
static tree spu_handle_vector_attribute (tree * node, tree name, tree args,
                                         int flags,
                                         int flags,
                                         unsigned char *no_add_attrs);
                                         unsigned char *no_add_attrs);
static int spu_naked_function_p (tree func);
static int spu_naked_function_p (tree func);
static unsigned char spu_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
static unsigned char spu_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                                            const_tree type, unsigned char named);
                                            const_tree type, unsigned char named);
static tree spu_build_builtin_va_list (void);
static tree spu_build_builtin_va_list (void);
static void spu_va_start (tree, rtx);
static void spu_va_start (tree, rtx);
static tree spu_gimplify_va_arg_expr (tree valist, tree type,
static tree spu_gimplify_va_arg_expr (tree valist, tree type,
                                      gimple_seq * pre_p, gimple_seq * post_p);
                                      gimple_seq * pre_p, gimple_seq * post_p);
static int store_with_one_insn_p (rtx mem);
static int store_with_one_insn_p (rtx mem);
static int mem_is_padded_component_ref (rtx x);
static int mem_is_padded_component_ref (rtx x);
static int reg_aligned_for_addr (rtx x);
static int reg_aligned_for_addr (rtx x);
static bool spu_assemble_integer (rtx x, unsigned int size, int aligned_p);
static bool spu_assemble_integer (rtx x, unsigned int size, int aligned_p);
static void spu_asm_globalize_label (FILE * file, const char *name);
static void spu_asm_globalize_label (FILE * file, const char *name);
static unsigned char spu_rtx_costs (rtx x, int code, int outer_code,
static unsigned char spu_rtx_costs (rtx x, int code, int outer_code,
                                    int *total, bool speed);
                                    int *total, bool speed);
static unsigned char spu_function_ok_for_sibcall (tree decl, tree exp);
static unsigned char spu_function_ok_for_sibcall (tree decl, tree exp);
static void spu_init_libfuncs (void);
static void spu_init_libfuncs (void);
static bool spu_return_in_memory (const_tree type, const_tree fntype);
static bool spu_return_in_memory (const_tree type, const_tree fntype);
static void fix_range (const char *);
static void fix_range (const char *);
static void spu_encode_section_info (tree, rtx, int);
static void spu_encode_section_info (tree, rtx, int);
static rtx spu_legitimize_address (rtx, rtx, enum machine_mode);
static rtx spu_legitimize_address (rtx, rtx, enum machine_mode);
static rtx spu_addr_space_legitimize_address (rtx, rtx, enum machine_mode,
static rtx spu_addr_space_legitimize_address (rtx, rtx, enum machine_mode,
                                              addr_space_t);
                                              addr_space_t);
static tree spu_builtin_mul_widen_even (tree);
static tree spu_builtin_mul_widen_even (tree);
static tree spu_builtin_mul_widen_odd (tree);
static tree spu_builtin_mul_widen_odd (tree);
static tree spu_builtin_mask_for_load (void);
static tree spu_builtin_mask_for_load (void);
static int spu_builtin_vectorization_cost (bool);
static int spu_builtin_vectorization_cost (bool);
static bool spu_vector_alignment_reachable (const_tree, bool);
static bool spu_vector_alignment_reachable (const_tree, bool);
static tree spu_builtin_vec_perm (tree, tree *);
static tree spu_builtin_vec_perm (tree, tree *);
static enum machine_mode spu_addr_space_pointer_mode (addr_space_t);
static enum machine_mode spu_addr_space_pointer_mode (addr_space_t);
static enum machine_mode spu_addr_space_address_mode (addr_space_t);
static enum machine_mode spu_addr_space_address_mode (addr_space_t);
static bool spu_addr_space_subset_p (addr_space_t, addr_space_t);
static bool spu_addr_space_subset_p (addr_space_t, addr_space_t);
static rtx spu_addr_space_convert (rtx, tree, tree);
static rtx spu_addr_space_convert (rtx, tree, tree);
static int spu_sms_res_mii (struct ddg *g);
static int spu_sms_res_mii (struct ddg *g);
static void asm_file_start (void);
static void asm_file_start (void);
static unsigned int spu_section_type_flags (tree, const char *, int);
static unsigned int spu_section_type_flags (tree, const char *, int);
static section *spu_select_section (tree, int, unsigned HOST_WIDE_INT);
static section *spu_select_section (tree, int, unsigned HOST_WIDE_INT);
static void spu_unique_section (tree, int);
static void spu_unique_section (tree, int);
static rtx spu_expand_load (rtx, rtx, rtx, int);
static rtx spu_expand_load (rtx, rtx, rtx, int);
static void spu_trampoline_init (rtx, tree, rtx);
static void spu_trampoline_init (rtx, tree, rtx);
 
 
extern const char *reg_names[];
extern const char *reg_names[];
 
 
/* Which instruction set architecture to use.  */
/* Which instruction set architecture to use.  */
int spu_arch;
int spu_arch;
/* Which cpu are we tuning for.  */
/* Which cpu are we tuning for.  */
int spu_tune;
int spu_tune;
 
 
/* The hardware requires 8 insns between a hint and the branch it
/* The hardware requires 8 insns between a hint and the branch it
   effects.  This variable describes how many rtl instructions the
   effects.  This variable describes how many rtl instructions the
   compiler needs to see before inserting a hint, and then the compiler
   compiler needs to see before inserting a hint, and then the compiler
   will insert enough nops to make it at least 8 insns.  The default is
   will insert enough nops to make it at least 8 insns.  The default is
   for the compiler to allow up to 2 nops be emitted.  The nops are
   for the compiler to allow up to 2 nops be emitted.  The nops are
   inserted in pairs, so we round down. */
   inserted in pairs, so we round down. */
int spu_hint_dist = (8*4) - (2*4);
int spu_hint_dist = (8*4) - (2*4);
 
 
/* Determines whether we run variable tracking in machine dependent
/* Determines whether we run variable tracking in machine dependent
   reorganization.  */
   reorganization.  */
static int spu_flag_var_tracking;
static int spu_flag_var_tracking;
 
 
enum spu_immediate {
enum spu_immediate {
  SPU_NONE,
  SPU_NONE,
  SPU_IL,
  SPU_IL,
  SPU_ILA,
  SPU_ILA,
  SPU_ILH,
  SPU_ILH,
  SPU_ILHU,
  SPU_ILHU,
  SPU_ORI,
  SPU_ORI,
  SPU_ORHI,
  SPU_ORHI,
  SPU_ORBI,
  SPU_ORBI,
  SPU_IOHL
  SPU_IOHL
};
};
enum immediate_class
enum immediate_class
{
{
  IC_POOL,                      /* constant pool */
  IC_POOL,                      /* constant pool */
  IC_IL1,                       /* one il* instruction */
  IC_IL1,                       /* one il* instruction */
  IC_IL2,                       /* both ilhu and iohl instructions */
  IC_IL2,                       /* both ilhu and iohl instructions */
  IC_IL1s,                      /* one il* instruction */
  IC_IL1s,                      /* one il* instruction */
  IC_IL2s,                      /* both ilhu and iohl instructions */
  IC_IL2s,                      /* both ilhu and iohl instructions */
  IC_FSMBI,                     /* the fsmbi instruction */
  IC_FSMBI,                     /* the fsmbi instruction */
  IC_CPAT,                      /* one of the c*d instructions */
  IC_CPAT,                      /* one of the c*d instructions */
  IC_FSMBI2                     /* fsmbi plus 1 other instruction */
  IC_FSMBI2                     /* fsmbi plus 1 other instruction */
};
};
 
 
static enum spu_immediate which_immediate_load (HOST_WIDE_INT val);
static enum spu_immediate which_immediate_load (HOST_WIDE_INT val);
static enum spu_immediate which_logical_immediate (HOST_WIDE_INT val);
static enum spu_immediate which_logical_immediate (HOST_WIDE_INT val);
static int cpat_info(unsigned char *arr, int size, int *prun, int *pstart);
static int cpat_info(unsigned char *arr, int size, int *prun, int *pstart);
static enum immediate_class classify_immediate (rtx op,
static enum immediate_class classify_immediate (rtx op,
                                                enum machine_mode mode);
                                                enum machine_mode mode);
 
 
static enum machine_mode spu_unwind_word_mode (void);
static enum machine_mode spu_unwind_word_mode (void);
 
 
static enum machine_mode
static enum machine_mode
spu_libgcc_cmp_return_mode (void);
spu_libgcc_cmp_return_mode (void);
 
 
static enum machine_mode
static enum machine_mode
spu_libgcc_shift_count_mode (void);
spu_libgcc_shift_count_mode (void);
 
 
/* Pointer mode for __ea references.  */
/* Pointer mode for __ea references.  */
#define EAmode (spu_ea_model != 32 ? DImode : SImode)
#define EAmode (spu_ea_model != 32 ? DImode : SImode)
 
 


/*  Table of machine attributes.  */
/*  Table of machine attributes.  */
static const struct attribute_spec spu_attribute_table[] =
static const struct attribute_spec spu_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 } */
  { "naked",          0, 0, true,  false, false, spu_handle_fndecl_attribute },
  { "naked",          0, 0, true,  false, false, spu_handle_fndecl_attribute },
  { "spu_vector",     0, 0, false, true,  false, spu_handle_vector_attribute },
  { "spu_vector",     0, 0, false, true,  false, spu_handle_vector_attribute },
  { NULL,             0, 0, false, false, false, NULL }
  { NULL,             0, 0, false, false, false, NULL }
};
};


/*  TARGET overrides.  */
/*  TARGET overrides.  */
 
 
#undef TARGET_ADDR_SPACE_POINTER_MODE
#undef TARGET_ADDR_SPACE_POINTER_MODE
#define TARGET_ADDR_SPACE_POINTER_MODE spu_addr_space_pointer_mode
#define TARGET_ADDR_SPACE_POINTER_MODE spu_addr_space_pointer_mode
 
 
#undef TARGET_ADDR_SPACE_ADDRESS_MODE
#undef TARGET_ADDR_SPACE_ADDRESS_MODE
#define TARGET_ADDR_SPACE_ADDRESS_MODE spu_addr_space_address_mode
#define TARGET_ADDR_SPACE_ADDRESS_MODE spu_addr_space_address_mode
 
 
#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \
#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \
  spu_addr_space_legitimate_address_p
  spu_addr_space_legitimate_address_p
 
 
#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS spu_addr_space_legitimize_address
#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS spu_addr_space_legitimize_address
 
 
#undef TARGET_ADDR_SPACE_SUBSET_P
#undef TARGET_ADDR_SPACE_SUBSET_P
#define TARGET_ADDR_SPACE_SUBSET_P spu_addr_space_subset_p
#define TARGET_ADDR_SPACE_SUBSET_P spu_addr_space_subset_p
 
 
#undef TARGET_ADDR_SPACE_CONVERT
#undef TARGET_ADDR_SPACE_CONVERT
#define TARGET_ADDR_SPACE_CONVERT spu_addr_space_convert
#define TARGET_ADDR_SPACE_CONVERT spu_addr_space_convert
 
 
#undef TARGET_INIT_BUILTINS
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS spu_init_builtins
#define TARGET_INIT_BUILTINS spu_init_builtins
#undef TARGET_BUILTIN_DECL
#undef TARGET_BUILTIN_DECL
#define TARGET_BUILTIN_DECL spu_builtin_decl
#define TARGET_BUILTIN_DECL spu_builtin_decl
 
 
#undef TARGET_EXPAND_BUILTIN
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN spu_expand_builtin
#define TARGET_EXPAND_BUILTIN spu_expand_builtin
 
 
#undef TARGET_UNWIND_WORD_MODE
#undef TARGET_UNWIND_WORD_MODE
#define TARGET_UNWIND_WORD_MODE spu_unwind_word_mode
#define TARGET_UNWIND_WORD_MODE spu_unwind_word_mode
 
 
#undef TARGET_LEGITIMIZE_ADDRESS
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS spu_legitimize_address
#define TARGET_LEGITIMIZE_ADDRESS spu_legitimize_address
 
 
/* The current assembler doesn't like .4byte foo@ppu, so use the normal .long
/* The current assembler doesn't like .4byte foo@ppu, so use the normal .long
   and .quad for the debugger.  When it is known that the assembler is fixed,
   and .quad for the debugger.  When it is known that the assembler is fixed,
   these can be removed.  */
   these can be removed.  */
#undef TARGET_ASM_UNALIGNED_SI_OP
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP      "\t.long\t"
#define TARGET_ASM_UNALIGNED_SI_OP      "\t.long\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"
 
 
/* The .8byte directive doesn't seem to work well for a 32 bit
/* The .8byte directive doesn't seem to work well for a 32 bit
   architecture. */
   architecture. */
#undef TARGET_ASM_UNALIGNED_DI_OP
#undef TARGET_ASM_UNALIGNED_DI_OP
#define TARGET_ASM_UNALIGNED_DI_OP NULL
#define TARGET_ASM_UNALIGNED_DI_OP NULL
 
 
#undef TARGET_RTX_COSTS
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS spu_rtx_costs
#define TARGET_RTX_COSTS spu_rtx_costs
 
 
#undef TARGET_ADDRESS_COST
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
 
 
#undef TARGET_SCHED_ISSUE_RATE
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE spu_sched_issue_rate
#define TARGET_SCHED_ISSUE_RATE spu_sched_issue_rate
 
 
#undef TARGET_SCHED_INIT_GLOBAL
#undef TARGET_SCHED_INIT_GLOBAL
#define TARGET_SCHED_INIT_GLOBAL spu_sched_init_global
#define TARGET_SCHED_INIT_GLOBAL spu_sched_init_global
 
 
#undef TARGET_SCHED_INIT
#undef TARGET_SCHED_INIT
#define TARGET_SCHED_INIT spu_sched_init
#define TARGET_SCHED_INIT spu_sched_init
 
 
#undef TARGET_SCHED_VARIABLE_ISSUE
#undef TARGET_SCHED_VARIABLE_ISSUE
#define TARGET_SCHED_VARIABLE_ISSUE spu_sched_variable_issue
#define TARGET_SCHED_VARIABLE_ISSUE spu_sched_variable_issue
 
 
#undef TARGET_SCHED_REORDER
#undef TARGET_SCHED_REORDER
#define TARGET_SCHED_REORDER spu_sched_reorder
#define TARGET_SCHED_REORDER spu_sched_reorder
 
 
#undef TARGET_SCHED_REORDER2
#undef TARGET_SCHED_REORDER2
#define TARGET_SCHED_REORDER2 spu_sched_reorder
#define TARGET_SCHED_REORDER2 spu_sched_reorder
 
 
#undef TARGET_SCHED_ADJUST_COST
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST spu_sched_adjust_cost
#define TARGET_SCHED_ADJUST_COST spu_sched_adjust_cost
 
 
#undef  TARGET_ATTRIBUTE_TABLE
#undef  TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE spu_attribute_table
#define TARGET_ATTRIBUTE_TABLE spu_attribute_table
 
 
#undef TARGET_ASM_INTEGER
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER spu_assemble_integer
#define TARGET_ASM_INTEGER spu_assemble_integer
 
 
#undef TARGET_SCALAR_MODE_SUPPORTED_P
#undef TARGET_SCALAR_MODE_SUPPORTED_P
#define TARGET_SCALAR_MODE_SUPPORTED_P  spu_scalar_mode_supported_p
#define TARGET_SCALAR_MODE_SUPPORTED_P  spu_scalar_mode_supported_p
 
 
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P  spu_vector_mode_supported_p
#define TARGET_VECTOR_MODE_SUPPORTED_P  spu_vector_mode_supported_p
 
 
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL spu_function_ok_for_sibcall
#define TARGET_FUNCTION_OK_FOR_SIBCALL spu_function_ok_for_sibcall
 
 
#undef TARGET_ASM_GLOBALIZE_LABEL
#undef TARGET_ASM_GLOBALIZE_LABEL
#define TARGET_ASM_GLOBALIZE_LABEL spu_asm_globalize_label
#define TARGET_ASM_GLOBALIZE_LABEL spu_asm_globalize_label
 
 
#undef TARGET_PASS_BY_REFERENCE
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE spu_pass_by_reference
#define TARGET_PASS_BY_REFERENCE spu_pass_by_reference
 
 
#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_BUILD_BUILTIN_VA_LIST
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST spu_build_builtin_va_list
#define TARGET_BUILD_BUILTIN_VA_LIST spu_build_builtin_va_list
 
 
#undef TARGET_EXPAND_BUILTIN_VA_START
#undef TARGET_EXPAND_BUILTIN_VA_START
#define TARGET_EXPAND_BUILTIN_VA_START spu_va_start
#define TARGET_EXPAND_BUILTIN_VA_START spu_va_start
 
 
#undef TARGET_SETUP_INCOMING_VARARGS
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS spu_setup_incoming_varargs
#define TARGET_SETUP_INCOMING_VARARGS spu_setup_incoming_varargs
 
 
#undef TARGET_MACHINE_DEPENDENT_REORG
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG spu_machine_dependent_reorg
#define TARGET_MACHINE_DEPENDENT_REORG spu_machine_dependent_reorg
 
 
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR spu_gimplify_va_arg_expr
#define TARGET_GIMPLIFY_VA_ARG_EXPR spu_gimplify_va_arg_expr
 
 
#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_INIT_LIBFUNCS
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS spu_init_libfuncs
#define TARGET_INIT_LIBFUNCS spu_init_libfuncs
 
 
#undef TARGET_RETURN_IN_MEMORY
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY spu_return_in_memory
#define TARGET_RETURN_IN_MEMORY spu_return_in_memory
 
 
#undef  TARGET_ENCODE_SECTION_INFO
#undef  TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO spu_encode_section_info
#define TARGET_ENCODE_SECTION_INFO spu_encode_section_info
 
 
#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN
#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN
#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN spu_builtin_mul_widen_even
#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN spu_builtin_mul_widen_even
 
 
#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD
#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD
#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD spu_builtin_mul_widen_odd
#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD spu_builtin_mul_widen_odd
 
 
#undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD
#undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD
#define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD spu_builtin_mask_for_load
#define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD spu_builtin_mask_for_load
 
 
#undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST
#undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST
#define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST spu_builtin_vectorization_cost
#define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST spu_builtin_vectorization_cost
 
 
#undef TARGET_VECTOR_ALIGNMENT_REACHABLE
#undef TARGET_VECTOR_ALIGNMENT_REACHABLE
#define TARGET_VECTOR_ALIGNMENT_REACHABLE spu_vector_alignment_reachable
#define TARGET_VECTOR_ALIGNMENT_REACHABLE spu_vector_alignment_reachable
 
 
#undef TARGET_VECTORIZE_BUILTIN_VEC_PERM
#undef TARGET_VECTORIZE_BUILTIN_VEC_PERM
#define TARGET_VECTORIZE_BUILTIN_VEC_PERM spu_builtin_vec_perm
#define TARGET_VECTORIZE_BUILTIN_VEC_PERM spu_builtin_vec_perm
 
 
#undef TARGET_LIBGCC_CMP_RETURN_MODE
#undef TARGET_LIBGCC_CMP_RETURN_MODE
#define TARGET_LIBGCC_CMP_RETURN_MODE spu_libgcc_cmp_return_mode
#define TARGET_LIBGCC_CMP_RETURN_MODE spu_libgcc_cmp_return_mode
 
 
#undef TARGET_LIBGCC_SHIFT_COUNT_MODE
#undef TARGET_LIBGCC_SHIFT_COUNT_MODE
#define TARGET_LIBGCC_SHIFT_COUNT_MODE spu_libgcc_shift_count_mode
#define TARGET_LIBGCC_SHIFT_COUNT_MODE spu_libgcc_shift_count_mode
 
 
#undef TARGET_SCHED_SMS_RES_MII
#undef TARGET_SCHED_SMS_RES_MII
#define TARGET_SCHED_SMS_RES_MII spu_sms_res_mii
#define TARGET_SCHED_SMS_RES_MII spu_sms_res_mii
 
 
#undef TARGET_ASM_FILE_START
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START asm_file_start
#define TARGET_ASM_FILE_START asm_file_start
 
 
#undef TARGET_SECTION_TYPE_FLAGS
#undef TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS spu_section_type_flags
#define TARGET_SECTION_TYPE_FLAGS spu_section_type_flags
 
 
#undef TARGET_ASM_SELECT_SECTION
#undef TARGET_ASM_SELECT_SECTION
#define TARGET_ASM_SELECT_SECTION  spu_select_section
#define TARGET_ASM_SELECT_SECTION  spu_select_section
 
 
#undef TARGET_ASM_UNIQUE_SECTION
#undef TARGET_ASM_UNIQUE_SECTION
#define TARGET_ASM_UNIQUE_SECTION  spu_unique_section
#define TARGET_ASM_UNIQUE_SECTION  spu_unique_section
 
 
#undef TARGET_LEGITIMATE_ADDRESS_P
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P spu_legitimate_address_p
#define TARGET_LEGITIMATE_ADDRESS_P spu_legitimate_address_p
 
 
#undef TARGET_TRAMPOLINE_INIT
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT spu_trampoline_init
#define TARGET_TRAMPOLINE_INIT spu_trampoline_init
 
 
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;
 
 
void
void
spu_optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
spu_optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
{
{
  /* Override some of the default param values.  With so many registers
  /* Override some of the default param values.  With so many registers
     larger values are better for these params.  */
     larger values are better for these params.  */
  MAX_PENDING_LIST_LENGTH = 128;
  MAX_PENDING_LIST_LENGTH = 128;
 
 
  /* With so many registers this is better on by default. */
  /* With so many registers this is better on by default. */
  flag_rename_registers = 1;
  flag_rename_registers = 1;
}
}
 
 
/* Sometimes certain combinations of command options do not make sense
/* Sometimes certain combinations of command options do not make sense
   on a particular target machine.  You can define a macro
   on a particular target machine.  You can define a macro
   OVERRIDE_OPTIONS to take account of this. This macro, if defined, is
   OVERRIDE_OPTIONS to take account of this. This macro, if defined, is
   executed once just after all the command options have been parsed.  */
   executed once just after all the command options have been parsed.  */
void
void
spu_override_options (void)
spu_override_options (void)
{
{
  /* Small loops will be unpeeled at -O3.  For SPU it is more important
  /* Small loops will be unpeeled at -O3.  For SPU it is more important
     to keep code small by default.  */
     to keep code small by default.  */
  if (!flag_unroll_loops && !flag_peel_loops
  if (!flag_unroll_loops && !flag_peel_loops
      && !PARAM_SET_P (PARAM_MAX_COMPLETELY_PEEL_TIMES))
      && !PARAM_SET_P (PARAM_MAX_COMPLETELY_PEEL_TIMES))
    PARAM_VALUE (PARAM_MAX_COMPLETELY_PEEL_TIMES) = 1;
    PARAM_VALUE (PARAM_MAX_COMPLETELY_PEEL_TIMES) = 1;
 
 
  flag_omit_frame_pointer = 1;
  flag_omit_frame_pointer = 1;
 
 
  /* Functions must be 8 byte aligned so we correctly handle dual issue */
  /* Functions must be 8 byte aligned so we correctly handle dual issue */
  if (align_functions < 8)
  if (align_functions < 8)
    align_functions = 8;
    align_functions = 8;
 
 
  spu_hint_dist = 8*4 - spu_max_nops*4;
  spu_hint_dist = 8*4 - spu_max_nops*4;
  if (spu_hint_dist < 0)
  if (spu_hint_dist < 0)
    spu_hint_dist = 0;
    spu_hint_dist = 0;
 
 
  if (spu_fixed_range_string)
  if (spu_fixed_range_string)
    fix_range (spu_fixed_range_string);
    fix_range (spu_fixed_range_string);
 
 
  /* Determine processor architectural level.  */
  /* Determine processor architectural level.  */
  if (spu_arch_string)
  if (spu_arch_string)
    {
    {
      if (strcmp (&spu_arch_string[0], "cell") == 0)
      if (strcmp (&spu_arch_string[0], "cell") == 0)
        spu_arch = PROCESSOR_CELL;
        spu_arch = PROCESSOR_CELL;
      else if (strcmp (&spu_arch_string[0], "celledp") == 0)
      else if (strcmp (&spu_arch_string[0], "celledp") == 0)
        spu_arch = PROCESSOR_CELLEDP;
        spu_arch = PROCESSOR_CELLEDP;
      else
      else
        error ("Unknown architecture '%s'", &spu_arch_string[0]);
        error ("Unknown architecture '%s'", &spu_arch_string[0]);
    }
    }
 
 
  /* Determine processor to tune for.  */
  /* Determine processor to tune for.  */
  if (spu_tune_string)
  if (spu_tune_string)
    {
    {
      if (strcmp (&spu_tune_string[0], "cell") == 0)
      if (strcmp (&spu_tune_string[0], "cell") == 0)
        spu_tune = PROCESSOR_CELL;
        spu_tune = PROCESSOR_CELL;
      else if (strcmp (&spu_tune_string[0], "celledp") == 0)
      else if (strcmp (&spu_tune_string[0], "celledp") == 0)
        spu_tune = PROCESSOR_CELLEDP;
        spu_tune = PROCESSOR_CELLEDP;
      else
      else
        error ("Unknown architecture '%s'", &spu_tune_string[0]);
        error ("Unknown architecture '%s'", &spu_tune_string[0]);
    }
    }
 
 
  /* Change defaults according to the processor architecture.  */
  /* Change defaults according to the processor architecture.  */
  if (spu_arch == PROCESSOR_CELLEDP)
  if (spu_arch == PROCESSOR_CELLEDP)
    {
    {
      /* If no command line option has been otherwise specified, change
      /* If no command line option has been otherwise specified, change
         the default to -mno-safe-hints on celledp -- only the original
         the default to -mno-safe-hints on celledp -- only the original
         Cell/B.E. processors require this workaround.  */
         Cell/B.E. processors require this workaround.  */
      if (!(target_flags_explicit & MASK_SAFE_HINTS))
      if (!(target_flags_explicit & MASK_SAFE_HINTS))
        target_flags &= ~MASK_SAFE_HINTS;
        target_flags &= ~MASK_SAFE_HINTS;
    }
    }
 
 
  REAL_MODE_FORMAT (SFmode) = &spu_single_format;
  REAL_MODE_FORMAT (SFmode) = &spu_single_format;
}
}


/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
 
 
/* True if MODE is valid for the target.  By "valid", we mean able to
/* True if MODE is valid for the target.  By "valid", we mean able to
   be manipulated in non-trivial ways.  In particular, this means all
   be manipulated in non-trivial ways.  In particular, this means all
   the arithmetic is supported.  */
   the arithmetic is supported.  */
static bool
static bool
spu_scalar_mode_supported_p (enum machine_mode mode)
spu_scalar_mode_supported_p (enum machine_mode mode)
{
{
  switch (mode)
  switch (mode)
    {
    {
    case QImode:
    case QImode:
    case HImode:
    case HImode:
    case SImode:
    case SImode:
    case SFmode:
    case SFmode:
    case DImode:
    case DImode:
    case TImode:
    case TImode:
    case DFmode:
    case DFmode:
      return true;
      return true;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
}
}
 
 
/* Similarly for vector modes.  "Supported" here is less strict.  At
/* Similarly for vector modes.  "Supported" here is less strict.  At
   least some operations are supported; need to check optabs or builtins
   least some operations are supported; need to check optabs or builtins
   for further details.  */
   for further details.  */
static bool
static bool
spu_vector_mode_supported_p (enum machine_mode mode)
spu_vector_mode_supported_p (enum machine_mode mode)
{
{
  switch (mode)
  switch (mode)
    {
    {
    case V16QImode:
    case V16QImode:
    case V8HImode:
    case V8HImode:
    case V4SImode:
    case V4SImode:
    case V2DImode:
    case V2DImode:
    case V4SFmode:
    case V4SFmode:
    case V2DFmode:
    case V2DFmode:
      return true;
      return true;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
}
}
 
 
/* GCC assumes that in a paradoxical SUBREG the inner mode occupies the
/* GCC assumes that in a paradoxical SUBREG the inner mode occupies the
   least significant bytes of the outer mode.  This function returns
   least significant bytes of the outer mode.  This function returns
   TRUE for the SUBREG's where this is correct.  */
   TRUE for the SUBREG's where this is correct.  */
int
int
valid_subreg (rtx op)
valid_subreg (rtx op)
{
{
  enum machine_mode om = GET_MODE (op);
  enum machine_mode om = GET_MODE (op);
  enum machine_mode im = GET_MODE (SUBREG_REG (op));
  enum machine_mode im = GET_MODE (SUBREG_REG (op));
  return om != VOIDmode && im != VOIDmode
  return om != VOIDmode && im != VOIDmode
    && (GET_MODE_SIZE (im) == GET_MODE_SIZE (om)
    && (GET_MODE_SIZE (im) == GET_MODE_SIZE (om)
        || (GET_MODE_SIZE (im) <= 4 && GET_MODE_SIZE (om) <= 4)
        || (GET_MODE_SIZE (im) <= 4 && GET_MODE_SIZE (om) <= 4)
        || (GET_MODE_SIZE (im) >= 16 && GET_MODE_SIZE (om) >= 16));
        || (GET_MODE_SIZE (im) >= 16 && GET_MODE_SIZE (om) >= 16));
}
}
 
 
/* When insv and ext[sz]v ar passed a TI SUBREG, we want to strip it off
/* When insv and ext[sz]v ar passed a TI SUBREG, we want to strip it off
   and adjust the start offset.  */
   and adjust the start offset.  */
static rtx
static rtx
adjust_operand (rtx op, HOST_WIDE_INT * start)
adjust_operand (rtx op, HOST_WIDE_INT * start)
{
{
  enum machine_mode mode;
  enum machine_mode mode;
  int op_size;
  int op_size;
  /* Strip any paradoxical SUBREG.  */
  /* Strip any paradoxical SUBREG.  */
  if (GET_CODE (op) == SUBREG
  if (GET_CODE (op) == SUBREG
      && (GET_MODE_BITSIZE (GET_MODE (op))
      && (GET_MODE_BITSIZE (GET_MODE (op))
          > GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op)))))
          > GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op)))))
    {
    {
      if (start)
      if (start)
        *start -=
        *start -=
          GET_MODE_BITSIZE (GET_MODE (op)) -
          GET_MODE_BITSIZE (GET_MODE (op)) -
          GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op)));
          GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op)));
      op = SUBREG_REG (op);
      op = SUBREG_REG (op);
    }
    }
  /* If it is smaller than SI, assure a SUBREG */
  /* If it is smaller than SI, assure a SUBREG */
  op_size = GET_MODE_BITSIZE (GET_MODE (op));
  op_size = GET_MODE_BITSIZE (GET_MODE (op));
  if (op_size < 32)
  if (op_size < 32)
    {
    {
      if (start)
      if (start)
        *start += 32 - op_size;
        *start += 32 - op_size;
      op_size = 32;
      op_size = 32;
    }
    }
  /* If it is not a MODE_INT (and/or it is smaller than SI) add a SUBREG. */
  /* If it is not a MODE_INT (and/or it is smaller than SI) add a SUBREG. */
  mode = mode_for_size (op_size, MODE_INT, 0);
  mode = mode_for_size (op_size, MODE_INT, 0);
  if (mode != GET_MODE (op))
  if (mode != GET_MODE (op))
    op = gen_rtx_SUBREG (mode, op, 0);
    op = gen_rtx_SUBREG (mode, op, 0);
  return op;
  return op;
}
}
 
 
void
void
spu_expand_extv (rtx ops[], int unsignedp)
spu_expand_extv (rtx ops[], int unsignedp)
{
{
  rtx dst = ops[0], src = ops[1];
  rtx dst = ops[0], src = ops[1];
  HOST_WIDE_INT width = INTVAL (ops[2]);
  HOST_WIDE_INT width = INTVAL (ops[2]);
  HOST_WIDE_INT start = INTVAL (ops[3]);
  HOST_WIDE_INT start = INTVAL (ops[3]);
  HOST_WIDE_INT align_mask;
  HOST_WIDE_INT align_mask;
  rtx s0, s1, mask, r0;
  rtx s0, s1, mask, r0;
 
 
  gcc_assert (REG_P (dst) && GET_MODE (dst) == TImode);
  gcc_assert (REG_P (dst) && GET_MODE (dst) == TImode);
 
 
  if (MEM_P (src))
  if (MEM_P (src))
    {
    {
      /* First, determine if we need 1 TImode load or 2.  We need only 1
      /* First, determine if we need 1 TImode load or 2.  We need only 1
         if the bits being extracted do not cross the alignment boundary
         if the bits being extracted do not cross the alignment boundary
         as determined by the MEM and its address. */
         as determined by the MEM and its address. */
 
 
      align_mask = -MEM_ALIGN (src);
      align_mask = -MEM_ALIGN (src);
      if ((start & align_mask) == ((start + width - 1) & align_mask))
      if ((start & align_mask) == ((start + width - 1) & align_mask))
        {
        {
          /* Alignment is sufficient for 1 load. */
          /* Alignment is sufficient for 1 load. */
          s0 = gen_reg_rtx (TImode);
          s0 = gen_reg_rtx (TImode);
          r0 = spu_expand_load (s0, 0, src, start / 8);
          r0 = spu_expand_load (s0, 0, src, start / 8);
          start &= 7;
          start &= 7;
          if (r0)
          if (r0)
            emit_insn (gen_rotqby_ti (s0, s0, r0));
            emit_insn (gen_rotqby_ti (s0, s0, r0));
        }
        }
      else
      else
        {
        {
          /* Need 2 loads. */
          /* Need 2 loads. */
          s0 = gen_reg_rtx (TImode);
          s0 = gen_reg_rtx (TImode);
          s1 = gen_reg_rtx (TImode);
          s1 = gen_reg_rtx (TImode);
          r0 = spu_expand_load (s0, s1, src, start / 8);
          r0 = spu_expand_load (s0, s1, src, start / 8);
          start &= 7;
          start &= 7;
 
 
          gcc_assert (start + width <= 128);
          gcc_assert (start + width <= 128);
          if (r0)
          if (r0)
            {
            {
              rtx r1 = gen_reg_rtx (SImode);
              rtx r1 = gen_reg_rtx (SImode);
              mask = gen_reg_rtx (TImode);
              mask = gen_reg_rtx (TImode);
              emit_move_insn (mask, GEN_INT (-1));
              emit_move_insn (mask, GEN_INT (-1));
              emit_insn (gen_rotqby_ti (s0, s0, r0));
              emit_insn (gen_rotqby_ti (s0, s0, r0));
              emit_insn (gen_rotqby_ti (s1, s1, r0));
              emit_insn (gen_rotqby_ti (s1, s1, r0));
              if (GET_CODE (r0) == CONST_INT)
              if (GET_CODE (r0) == CONST_INT)
                r1 = GEN_INT (INTVAL (r0) & 15);
                r1 = GEN_INT (INTVAL (r0) & 15);
              else
              else
                emit_insn (gen_andsi3 (r1, r0, GEN_INT (15)));
                emit_insn (gen_andsi3 (r1, r0, GEN_INT (15)));
              emit_insn (gen_shlqby_ti (mask, mask, r1));
              emit_insn (gen_shlqby_ti (mask, mask, r1));
              emit_insn (gen_selb (s0, s1, s0, mask));
              emit_insn (gen_selb (s0, s1, s0, mask));
            }
            }
        }
        }
 
 
    }
    }
  else if (GET_CODE (src) == SUBREG)
  else if (GET_CODE (src) == SUBREG)
    {
    {
      rtx r = SUBREG_REG (src);
      rtx r = SUBREG_REG (src);
      gcc_assert (REG_P (r) && SCALAR_INT_MODE_P (GET_MODE (r)));
      gcc_assert (REG_P (r) && SCALAR_INT_MODE_P (GET_MODE (r)));
      s0 = gen_reg_rtx (TImode);
      s0 = gen_reg_rtx (TImode);
      if (GET_MODE_SIZE (GET_MODE (r)) < GET_MODE_SIZE (TImode))
      if (GET_MODE_SIZE (GET_MODE (r)) < GET_MODE_SIZE (TImode))
        emit_insn (gen_rtx_SET (VOIDmode, s0, gen_rtx_ZERO_EXTEND (TImode, r)));
        emit_insn (gen_rtx_SET (VOIDmode, s0, gen_rtx_ZERO_EXTEND (TImode, r)));
      else
      else
        emit_move_insn (s0, src);
        emit_move_insn (s0, src);
    }
    }
  else
  else
    {
    {
      gcc_assert (REG_P (src) && GET_MODE (src) == TImode);
      gcc_assert (REG_P (src) && GET_MODE (src) == TImode);
      s0 = gen_reg_rtx (TImode);
      s0 = gen_reg_rtx (TImode);
      emit_move_insn (s0, src);
      emit_move_insn (s0, src);
    }
    }
 
 
  /* Now s0 is TImode and contains the bits to extract at start. */
  /* Now s0 is TImode and contains the bits to extract at start. */
 
 
  if (start)
  if (start)
    emit_insn (gen_rotlti3 (s0, s0, GEN_INT (start)));
    emit_insn (gen_rotlti3 (s0, s0, GEN_INT (start)));
 
 
  if (128 - width)
  if (128 - width)
    {
    {
      tree c = build_int_cst (NULL_TREE, 128 - width);
      tree c = build_int_cst (NULL_TREE, 128 - width);
      s0 = expand_shift (RSHIFT_EXPR, TImode, s0, c, s0, unsignedp);
      s0 = expand_shift (RSHIFT_EXPR, TImode, s0, c, s0, unsignedp);
    }
    }
 
 
  emit_move_insn (dst, s0);
  emit_move_insn (dst, s0);
}
}
 
 
void
void
spu_expand_insv (rtx ops[])
spu_expand_insv (rtx ops[])
{
{
  HOST_WIDE_INT width = INTVAL (ops[1]);
  HOST_WIDE_INT width = INTVAL (ops[1]);
  HOST_WIDE_INT start = INTVAL (ops[2]);
  HOST_WIDE_INT start = INTVAL (ops[2]);
  HOST_WIDE_INT maskbits;
  HOST_WIDE_INT maskbits;
  enum machine_mode dst_mode, src_mode;
  enum machine_mode dst_mode, src_mode;
  rtx dst = ops[0], src = ops[3];
  rtx dst = ops[0], src = ops[3];
  int dst_size, src_size;
  int dst_size, src_size;
  rtx mask;
  rtx mask;
  rtx shift_reg;
  rtx shift_reg;
  int shift;
  int shift;
 
 
 
 
  if (GET_CODE (ops[0]) == MEM)
  if (GET_CODE (ops[0]) == MEM)
    dst = gen_reg_rtx (TImode);
    dst = gen_reg_rtx (TImode);
  else
  else
    dst = adjust_operand (dst, &start);
    dst = adjust_operand (dst, &start);
  dst_mode = GET_MODE (dst);
  dst_mode = GET_MODE (dst);
  dst_size = GET_MODE_BITSIZE (GET_MODE (dst));
  dst_size = GET_MODE_BITSIZE (GET_MODE (dst));
 
 
  if (CONSTANT_P (src))
  if (CONSTANT_P (src))
    {
    {
      enum machine_mode m =
      enum machine_mode m =
        (width <= 32 ? SImode : width <= 64 ? DImode : TImode);
        (width <= 32 ? SImode : width <= 64 ? DImode : TImode);
      src = force_reg (m, convert_to_mode (m, src, 0));
      src = force_reg (m, convert_to_mode (m, src, 0));
    }
    }
  src = adjust_operand (src, 0);
  src = adjust_operand (src, 0);
  src_mode = GET_MODE (src);
  src_mode = GET_MODE (src);
  src_size = GET_MODE_BITSIZE (GET_MODE (src));
  src_size = GET_MODE_BITSIZE (GET_MODE (src));
 
 
  mask = gen_reg_rtx (dst_mode);
  mask = gen_reg_rtx (dst_mode);
  shift_reg = gen_reg_rtx (dst_mode);
  shift_reg = gen_reg_rtx (dst_mode);
  shift = dst_size - start - width;
  shift = dst_size - start - width;
 
 
  /* It's not safe to use subreg here because the compiler assumes
  /* It's not safe to use subreg here because the compiler assumes
     that the SUBREG_REG is right justified in the SUBREG. */
     that the SUBREG_REG is right justified in the SUBREG. */
  convert_move (shift_reg, src, 1);
  convert_move (shift_reg, src, 1);
 
 
  if (shift > 0)
  if (shift > 0)
    {
    {
      switch (dst_mode)
      switch (dst_mode)
        {
        {
        case SImode:
        case SImode:
          emit_insn (gen_ashlsi3 (shift_reg, shift_reg, GEN_INT (shift)));
          emit_insn (gen_ashlsi3 (shift_reg, shift_reg, GEN_INT (shift)));
          break;
          break;
        case DImode:
        case DImode:
          emit_insn (gen_ashldi3 (shift_reg, shift_reg, GEN_INT (shift)));
          emit_insn (gen_ashldi3 (shift_reg, shift_reg, GEN_INT (shift)));
          break;
          break;
        case TImode:
        case TImode:
          emit_insn (gen_ashlti3 (shift_reg, shift_reg, GEN_INT (shift)));
          emit_insn (gen_ashlti3 (shift_reg, shift_reg, GEN_INT (shift)));
          break;
          break;
        default:
        default:
          abort ();
          abort ();
        }
        }
    }
    }
  else if (shift < 0)
  else if (shift < 0)
    abort ();
    abort ();
 
 
  switch (dst_size)
  switch (dst_size)
    {
    {
    case 32:
    case 32:
      maskbits = (-1ll << (32 - width - start));
      maskbits = (-1ll << (32 - width - start));
      if (start)
      if (start)
        maskbits += (1ll << (32 - start));
        maskbits += (1ll << (32 - start));
      emit_move_insn (mask, GEN_INT (maskbits));
      emit_move_insn (mask, GEN_INT (maskbits));
      break;
      break;
    case 64:
    case 64:
      maskbits = (-1ll << (64 - width - start));
      maskbits = (-1ll << (64 - width - start));
      if (start)
      if (start)
        maskbits += (1ll << (64 - start));
        maskbits += (1ll << (64 - start));
      emit_move_insn (mask, GEN_INT (maskbits));
      emit_move_insn (mask, GEN_INT (maskbits));
      break;
      break;
    case 128:
    case 128:
      {
      {
        unsigned char arr[16];
        unsigned char arr[16];
        int i = start / 8;
        int i = start / 8;
        memset (arr, 0, sizeof (arr));
        memset (arr, 0, sizeof (arr));
        arr[i] = 0xff >> (start & 7);
        arr[i] = 0xff >> (start & 7);
        for (i++; i <= (start + width - 1) / 8; i++)
        for (i++; i <= (start + width - 1) / 8; i++)
          arr[i] = 0xff;
          arr[i] = 0xff;
        arr[i - 1] &= 0xff << (7 - ((start + width - 1) & 7));
        arr[i - 1] &= 0xff << (7 - ((start + width - 1) & 7));
        emit_move_insn (mask, array_to_constant (TImode, arr));
        emit_move_insn (mask, array_to_constant (TImode, arr));
      }
      }
      break;
      break;
    default:
    default:
      abort ();
      abort ();
    }
    }
  if (GET_CODE (ops[0]) == MEM)
  if (GET_CODE (ops[0]) == MEM)
    {
    {
      rtx low = gen_reg_rtx (SImode);
      rtx low = gen_reg_rtx (SImode);
      rtx rotl = gen_reg_rtx (SImode);
      rtx rotl = gen_reg_rtx (SImode);
      rtx mask0 = gen_reg_rtx (TImode);
      rtx mask0 = gen_reg_rtx (TImode);
      rtx addr;
      rtx addr;
      rtx addr0;
      rtx addr0;
      rtx addr1;
      rtx addr1;
      rtx mem;
      rtx mem;
 
 
      addr = force_reg (Pmode, XEXP (ops[0], 0));
      addr = force_reg (Pmode, XEXP (ops[0], 0));
      addr0 = gen_rtx_AND (Pmode, addr, GEN_INT (-16));
      addr0 = gen_rtx_AND (Pmode, addr, GEN_INT (-16));
      emit_insn (gen_andsi3 (low, addr, GEN_INT (15)));
      emit_insn (gen_andsi3 (low, addr, GEN_INT (15)));
      emit_insn (gen_negsi2 (rotl, low));
      emit_insn (gen_negsi2 (rotl, low));
      emit_insn (gen_rotqby_ti (shift_reg, shift_reg, rotl));
      emit_insn (gen_rotqby_ti (shift_reg, shift_reg, rotl));
      emit_insn (gen_rotqmby_ti (mask0, mask, rotl));
      emit_insn (gen_rotqmby_ti (mask0, mask, rotl));
      mem = change_address (ops[0], TImode, addr0);
      mem = change_address (ops[0], TImode, addr0);
      set_mem_alias_set (mem, 0);
      set_mem_alias_set (mem, 0);
      emit_move_insn (dst, mem);
      emit_move_insn (dst, mem);
      emit_insn (gen_selb (dst, dst, shift_reg, mask0));
      emit_insn (gen_selb (dst, dst, shift_reg, mask0));
      if (start + width > MEM_ALIGN (ops[0]))
      if (start + width > MEM_ALIGN (ops[0]))
        {
        {
          rtx shl = gen_reg_rtx (SImode);
          rtx shl = gen_reg_rtx (SImode);
          rtx mask1 = gen_reg_rtx (TImode);
          rtx mask1 = gen_reg_rtx (TImode);
          rtx dst1 = gen_reg_rtx (TImode);
          rtx dst1 = gen_reg_rtx (TImode);
          rtx mem1;
          rtx mem1;
          addr1 = plus_constant (addr, 16);
          addr1 = plus_constant (addr, 16);
          addr1 = gen_rtx_AND (Pmode, addr1, GEN_INT (-16));
          addr1 = gen_rtx_AND (Pmode, addr1, GEN_INT (-16));
          emit_insn (gen_subsi3 (shl, GEN_INT (16), low));
          emit_insn (gen_subsi3 (shl, GEN_INT (16), low));
          emit_insn (gen_shlqby_ti (mask1, mask, shl));
          emit_insn (gen_shlqby_ti (mask1, mask, shl));
          mem1 = change_address (ops[0], TImode, addr1);
          mem1 = change_address (ops[0], TImode, addr1);
          set_mem_alias_set (mem1, 0);
          set_mem_alias_set (mem1, 0);
          emit_move_insn (dst1, mem1);
          emit_move_insn (dst1, mem1);
          emit_insn (gen_selb (dst1, dst1, shift_reg, mask1));
          emit_insn (gen_selb (dst1, dst1, shift_reg, mask1));
          emit_move_insn (mem1, dst1);
          emit_move_insn (mem1, dst1);
        }
        }
      emit_move_insn (mem, dst);
      emit_move_insn (mem, dst);
    }
    }
  else
  else
    emit_insn (gen_selb (dst, copy_rtx (dst), shift_reg, mask));
    emit_insn (gen_selb (dst, copy_rtx (dst), shift_reg, mask));
}
}
 
 
 
 
int
int
spu_expand_block_move (rtx ops[])
spu_expand_block_move (rtx ops[])
{
{
  HOST_WIDE_INT bytes, align, offset;
  HOST_WIDE_INT bytes, align, offset;
  rtx src, dst, sreg, dreg, target;
  rtx src, dst, sreg, dreg, target;
  int i;
  int i;
  if (GET_CODE (ops[2]) != CONST_INT
  if (GET_CODE (ops[2]) != CONST_INT
      || GET_CODE (ops[3]) != CONST_INT
      || GET_CODE (ops[3]) != CONST_INT
      || INTVAL (ops[2]) > (HOST_WIDE_INT) (MOVE_RATIO (optimize_insn_for_speed_p ()) * 8))
      || INTVAL (ops[2]) > (HOST_WIDE_INT) (MOVE_RATIO (optimize_insn_for_speed_p ()) * 8))
    return 0;
    return 0;
 
 
  bytes = INTVAL (ops[2]);
  bytes = INTVAL (ops[2]);
  align = INTVAL (ops[3]);
  align = INTVAL (ops[3]);
 
 
  if (bytes <= 0)
  if (bytes <= 0)
    return 1;
    return 1;
 
 
  dst = ops[0];
  dst = ops[0];
  src = ops[1];
  src = ops[1];
 
 
  if (align == 16)
  if (align == 16)
    {
    {
      for (offset = 0; offset + 16 <= bytes; offset += 16)
      for (offset = 0; offset + 16 <= bytes; offset += 16)
        {
        {
          dst = adjust_address (ops[0], V16QImode, offset);
          dst = adjust_address (ops[0], V16QImode, offset);
          src = adjust_address (ops[1], V16QImode, offset);
          src = adjust_address (ops[1], V16QImode, offset);
          emit_move_insn (dst, src);
          emit_move_insn (dst, src);
        }
        }
      if (offset < bytes)
      if (offset < bytes)
        {
        {
          rtx mask;
          rtx mask;
          unsigned char arr[16] = { 0 };
          unsigned char arr[16] = { 0 };
          for (i = 0; i < bytes - offset; i++)
          for (i = 0; i < bytes - offset; i++)
            arr[i] = 0xff;
            arr[i] = 0xff;
          dst = adjust_address (ops[0], V16QImode, offset);
          dst = adjust_address (ops[0], V16QImode, offset);
          src = adjust_address (ops[1], V16QImode, offset);
          src = adjust_address (ops[1], V16QImode, offset);
          mask = gen_reg_rtx (V16QImode);
          mask = gen_reg_rtx (V16QImode);
          sreg = gen_reg_rtx (V16QImode);
          sreg = gen_reg_rtx (V16QImode);
          dreg = gen_reg_rtx (V16QImode);
          dreg = gen_reg_rtx (V16QImode);
          target = gen_reg_rtx (V16QImode);
          target = gen_reg_rtx (V16QImode);
          emit_move_insn (mask, array_to_constant (V16QImode, arr));
          emit_move_insn (mask, array_to_constant (V16QImode, arr));
          emit_move_insn (dreg, dst);
          emit_move_insn (dreg, dst);
          emit_move_insn (sreg, src);
          emit_move_insn (sreg, src);
          emit_insn (gen_selb (target, dreg, sreg, mask));
          emit_insn (gen_selb (target, dreg, sreg, mask));
          emit_move_insn (dst, target);
          emit_move_insn (dst, target);
        }
        }
      return 1;
      return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
enum spu_comp_code
enum spu_comp_code
{ SPU_EQ, SPU_GT, SPU_GTU };
{ SPU_EQ, SPU_GT, SPU_GTU };
 
 
int spu_comp_icode[12][3] = {
int spu_comp_icode[12][3] = {
 {CODE_FOR_ceq_qi, CODE_FOR_cgt_qi, CODE_FOR_clgt_qi},
 {CODE_FOR_ceq_qi, CODE_FOR_cgt_qi, CODE_FOR_clgt_qi},
 {CODE_FOR_ceq_hi, CODE_FOR_cgt_hi, CODE_FOR_clgt_hi},
 {CODE_FOR_ceq_hi, CODE_FOR_cgt_hi, CODE_FOR_clgt_hi},
 {CODE_FOR_ceq_si, CODE_FOR_cgt_si, CODE_FOR_clgt_si},
 {CODE_FOR_ceq_si, CODE_FOR_cgt_si, CODE_FOR_clgt_si},
 {CODE_FOR_ceq_di, CODE_FOR_cgt_di, CODE_FOR_clgt_di},
 {CODE_FOR_ceq_di, CODE_FOR_cgt_di, CODE_FOR_clgt_di},
 {CODE_FOR_ceq_ti, CODE_FOR_cgt_ti, CODE_FOR_clgt_ti},
 {CODE_FOR_ceq_ti, CODE_FOR_cgt_ti, CODE_FOR_clgt_ti},
 {CODE_FOR_ceq_sf, CODE_FOR_cgt_sf, 0},
 {CODE_FOR_ceq_sf, CODE_FOR_cgt_sf, 0},
 {CODE_FOR_ceq_df, CODE_FOR_cgt_df, 0},
 {CODE_FOR_ceq_df, CODE_FOR_cgt_df, 0},
 {CODE_FOR_ceq_v16qi, CODE_FOR_cgt_v16qi, CODE_FOR_clgt_v16qi},
 {CODE_FOR_ceq_v16qi, CODE_FOR_cgt_v16qi, CODE_FOR_clgt_v16qi},
 {CODE_FOR_ceq_v8hi,  CODE_FOR_cgt_v8hi,  CODE_FOR_clgt_v8hi},
 {CODE_FOR_ceq_v8hi,  CODE_FOR_cgt_v8hi,  CODE_FOR_clgt_v8hi},
 {CODE_FOR_ceq_v4si,  CODE_FOR_cgt_v4si,  CODE_FOR_clgt_v4si},
 {CODE_FOR_ceq_v4si,  CODE_FOR_cgt_v4si,  CODE_FOR_clgt_v4si},
 {CODE_FOR_ceq_v4sf,  CODE_FOR_cgt_v4sf, 0},
 {CODE_FOR_ceq_v4sf,  CODE_FOR_cgt_v4sf, 0},
 {CODE_FOR_ceq_v2df,  CODE_FOR_cgt_v2df, 0},
 {CODE_FOR_ceq_v2df,  CODE_FOR_cgt_v2df, 0},
};
};
 
 
/* Generate a compare for CODE.  Return a brand-new rtx that represents
/* Generate a compare for CODE.  Return a brand-new rtx that represents
   the result of the compare.   GCC can figure this out too if we don't
   the result of the compare.   GCC can figure this out too if we don't
   provide all variations of compares, but GCC always wants to use
   provide all variations of compares, but GCC always wants to use
   WORD_MODE, we can generate better code in most cases if we do it
   WORD_MODE, we can generate better code in most cases if we do it
   ourselves.  */
   ourselves.  */
void
void
spu_emit_branch_or_set (int is_set, rtx cmp, rtx operands[])
spu_emit_branch_or_set (int is_set, rtx cmp, rtx operands[])
{
{
  int reverse_compare = 0;
  int reverse_compare = 0;
  int reverse_test = 0;
  int reverse_test = 0;
  rtx compare_result, eq_result;
  rtx compare_result, eq_result;
  rtx comp_rtx, eq_rtx;
  rtx comp_rtx, eq_rtx;
  enum machine_mode comp_mode;
  enum machine_mode comp_mode;
  enum machine_mode op_mode;
  enum machine_mode op_mode;
  enum spu_comp_code scode, eq_code;
  enum spu_comp_code scode, eq_code;
  enum insn_code ior_code;
  enum insn_code ior_code;
  enum rtx_code code = GET_CODE (cmp);
  enum rtx_code code = GET_CODE (cmp);
  rtx op0 = XEXP (cmp, 0);
  rtx op0 = XEXP (cmp, 0);
  rtx op1 = XEXP (cmp, 1);
  rtx op1 = XEXP (cmp, 1);
  int index;
  int index;
  int eq_test = 0;
  int eq_test = 0;
 
 
  /* When op1 is a CONST_INT change (X >= C) to (X > C-1),
  /* When op1 is a CONST_INT change (X >= C) to (X > C-1),
     and so on, to keep the constant in operand 1. */
     and so on, to keep the constant in operand 1. */
  if (GET_CODE (op1) == CONST_INT)
  if (GET_CODE (op1) == CONST_INT)
    {
    {
      HOST_WIDE_INT val = INTVAL (op1) - 1;
      HOST_WIDE_INT val = INTVAL (op1) - 1;
      if (trunc_int_for_mode (val, GET_MODE (op0)) == val)
      if (trunc_int_for_mode (val, GET_MODE (op0)) == val)
        switch (code)
        switch (code)
          {
          {
          case GE:
          case GE:
            op1 = GEN_INT (val);
            op1 = GEN_INT (val);
            code = GT;
            code = GT;
            break;
            break;
          case LT:
          case LT:
            op1 = GEN_INT (val);
            op1 = GEN_INT (val);
            code = LE;
            code = LE;
            break;
            break;
          case GEU:
          case GEU:
            op1 = GEN_INT (val);
            op1 = GEN_INT (val);
            code = GTU;
            code = GTU;
            break;
            break;
          case LTU:
          case LTU:
            op1 = GEN_INT (val);
            op1 = GEN_INT (val);
            code = LEU;
            code = LEU;
            break;
            break;
          default:
          default:
            break;
            break;
          }
          }
    }
    }
 
 
  comp_mode = SImode;
  comp_mode = SImode;
  op_mode = GET_MODE (op0);
  op_mode = GET_MODE (op0);
 
 
  switch (code)
  switch (code)
    {
    {
    case GE:
    case GE:
      scode = SPU_GT;
      scode = SPU_GT;
      if (HONOR_NANS (op_mode))
      if (HONOR_NANS (op_mode))
        {
        {
          reverse_compare = 0;
          reverse_compare = 0;
          reverse_test = 0;
          reverse_test = 0;
          eq_test = 1;
          eq_test = 1;
          eq_code = SPU_EQ;
          eq_code = SPU_EQ;
        }
        }
      else
      else
        {
        {
          reverse_compare = 1;
          reverse_compare = 1;
          reverse_test = 1;
          reverse_test = 1;
        }
        }
      break;
      break;
    case LE:
    case LE:
      scode = SPU_GT;
      scode = SPU_GT;
      if (HONOR_NANS (op_mode))
      if (HONOR_NANS (op_mode))
        {
        {
          reverse_compare = 1;
          reverse_compare = 1;
          reverse_test = 0;
          reverse_test = 0;
          eq_test = 1;
          eq_test = 1;
          eq_code = SPU_EQ;
          eq_code = SPU_EQ;
        }
        }
      else
      else
        {
        {
          reverse_compare = 0;
          reverse_compare = 0;
          reverse_test = 1;
          reverse_test = 1;
        }
        }
      break;
      break;
    case LT:
    case LT:
      reverse_compare = 1;
      reverse_compare = 1;
      reverse_test = 0;
      reverse_test = 0;
      scode = SPU_GT;
      scode = SPU_GT;
      break;
      break;
    case GEU:
    case GEU:
      reverse_compare = 1;
      reverse_compare = 1;
      reverse_test = 1;
      reverse_test = 1;
      scode = SPU_GTU;
      scode = SPU_GTU;
      break;
      break;
    case LEU:
    case LEU:
      reverse_compare = 0;
      reverse_compare = 0;
      reverse_test = 1;
      reverse_test = 1;
      scode = SPU_GTU;
      scode = SPU_GTU;
      break;
      break;
    case LTU:
    case LTU:
      reverse_compare = 1;
      reverse_compare = 1;
      reverse_test = 0;
      reverse_test = 0;
      scode = SPU_GTU;
      scode = SPU_GTU;
      break;
      break;
    case NE:
    case NE:
      reverse_compare = 0;
      reverse_compare = 0;
      reverse_test = 1;
      reverse_test = 1;
      scode = SPU_EQ;
      scode = SPU_EQ;
      break;
      break;
 
 
    case EQ:
    case EQ:
      scode = SPU_EQ;
      scode = SPU_EQ;
      break;
      break;
    case GT:
    case GT:
      scode = SPU_GT;
      scode = SPU_GT;
      break;
      break;
    case GTU:
    case GTU:
      scode = SPU_GTU;
      scode = SPU_GTU;
      break;
      break;
    default:
    default:
      scode = SPU_EQ;
      scode = SPU_EQ;
      break;
      break;
    }
    }
 
 
  switch (op_mode)
  switch (op_mode)
    {
    {
    case QImode:
    case QImode:
      index = 0;
      index = 0;
      comp_mode = QImode;
      comp_mode = QImode;
      break;
      break;
    case HImode:
    case HImode:
      index = 1;
      index = 1;
      comp_mode = HImode;
      comp_mode = HImode;
      break;
      break;
    case SImode:
    case SImode:
      index = 2;
      index = 2;
      break;
      break;
    case DImode:
    case DImode:
      index = 3;
      index = 3;
      break;
      break;
    case TImode:
    case TImode:
      index = 4;
      index = 4;
      break;
      break;
    case SFmode:
    case SFmode:
      index = 5;
      index = 5;
      break;
      break;
    case DFmode:
    case DFmode:
      index = 6;
      index = 6;
      break;
      break;
    case V16QImode:
    case V16QImode:
      index = 7;
      index = 7;
      comp_mode = op_mode;
      comp_mode = op_mode;
      break;
      break;
    case V8HImode:
    case V8HImode:
      index = 8;
      index = 8;
      comp_mode = op_mode;
      comp_mode = op_mode;
      break;
      break;
    case V4SImode:
    case V4SImode:
      index = 9;
      index = 9;
      comp_mode = op_mode;
      comp_mode = op_mode;
      break;
      break;
    case V4SFmode:
    case V4SFmode:
      index = 10;
      index = 10;
      comp_mode = V4SImode;
      comp_mode = V4SImode;
      break;
      break;
    case V2DFmode:
    case V2DFmode:
      index = 11;
      index = 11;
      comp_mode = V2DImode;
      comp_mode = V2DImode;
      break;
      break;
    case V2DImode:
    case V2DImode:
    default:
    default:
      abort ();
      abort ();
    }
    }
 
 
  if (GET_MODE (op1) == DFmode
  if (GET_MODE (op1) == DFmode
      && (scode != SPU_GT && scode != SPU_EQ))
      && (scode != SPU_GT && scode != SPU_EQ))
    abort ();
    abort ();
 
 
  if (is_set == 0 && op1 == const0_rtx
  if (is_set == 0 && op1 == const0_rtx
      && (GET_MODE (op0) == SImode
      && (GET_MODE (op0) == SImode
          || GET_MODE (op0) == HImode) && scode == SPU_EQ)
          || GET_MODE (op0) == HImode) && scode == SPU_EQ)
    {
    {
      /* Don't need to set a register with the result when we are
      /* Don't need to set a register with the result when we are
         comparing against zero and branching. */
         comparing against zero and branching. */
      reverse_test = !reverse_test;
      reverse_test = !reverse_test;
      compare_result = op0;
      compare_result = op0;
    }
    }
  else
  else
    {
    {
      compare_result = gen_reg_rtx (comp_mode);
      compare_result = gen_reg_rtx (comp_mode);
 
 
      if (reverse_compare)
      if (reverse_compare)
        {
        {
          rtx t = op1;
          rtx t = op1;
          op1 = op0;
          op1 = op0;
          op0 = t;
          op0 = t;
        }
        }
 
 
      if (spu_comp_icode[index][scode] == 0)
      if (spu_comp_icode[index][scode] == 0)
        abort ();
        abort ();
 
 
      if (!(*insn_data[spu_comp_icode[index][scode]].operand[1].predicate)
      if (!(*insn_data[spu_comp_icode[index][scode]].operand[1].predicate)
          (op0, op_mode))
          (op0, op_mode))
        op0 = force_reg (op_mode, op0);
        op0 = force_reg (op_mode, op0);
      if (!(*insn_data[spu_comp_icode[index][scode]].operand[2].predicate)
      if (!(*insn_data[spu_comp_icode[index][scode]].operand[2].predicate)
          (op1, op_mode))
          (op1, op_mode))
        op1 = force_reg (op_mode, op1);
        op1 = force_reg (op_mode, op1);
      comp_rtx = GEN_FCN (spu_comp_icode[index][scode]) (compare_result,
      comp_rtx = GEN_FCN (spu_comp_icode[index][scode]) (compare_result,
                                                         op0, op1);
                                                         op0, op1);
      if (comp_rtx == 0)
      if (comp_rtx == 0)
        abort ();
        abort ();
      emit_insn (comp_rtx);
      emit_insn (comp_rtx);
 
 
      if (eq_test)
      if (eq_test)
        {
        {
          eq_result = gen_reg_rtx (comp_mode);
          eq_result = gen_reg_rtx (comp_mode);
          eq_rtx = GEN_FCN (spu_comp_icode[index][eq_code]) (eq_result,
          eq_rtx = GEN_FCN (spu_comp_icode[index][eq_code]) (eq_result,
                                                             op0, op1);
                                                             op0, op1);
          if (eq_rtx == 0)
          if (eq_rtx == 0)
            abort ();
            abort ();
          emit_insn (eq_rtx);
          emit_insn (eq_rtx);
          ior_code = ior_optab->handlers[(int)comp_mode].insn_code;
          ior_code = ior_optab->handlers[(int)comp_mode].insn_code;
          gcc_assert (ior_code != CODE_FOR_nothing);
          gcc_assert (ior_code != CODE_FOR_nothing);
          emit_insn (GEN_FCN (ior_code)
          emit_insn (GEN_FCN (ior_code)
                     (compare_result, compare_result, eq_result));
                     (compare_result, compare_result, eq_result));
        }
        }
    }
    }
 
 
  if (is_set == 0)
  if (is_set == 0)
    {
    {
      rtx bcomp;
      rtx bcomp;
      rtx loc_ref;
      rtx loc_ref;
 
 
      /* We don't have branch on QI compare insns, so we convert the
      /* We don't have branch on QI compare insns, so we convert the
         QI compare result to a HI result. */
         QI compare result to a HI result. */
      if (comp_mode == QImode)
      if (comp_mode == QImode)
        {
        {
          rtx old_res = compare_result;
          rtx old_res = compare_result;
          compare_result = gen_reg_rtx (HImode);
          compare_result = gen_reg_rtx (HImode);
          comp_mode = HImode;
          comp_mode = HImode;
          emit_insn (gen_extendqihi2 (compare_result, old_res));
          emit_insn (gen_extendqihi2 (compare_result, old_res));
        }
        }
 
 
      if (reverse_test)
      if (reverse_test)
        bcomp = gen_rtx_EQ (comp_mode, compare_result, const0_rtx);
        bcomp = gen_rtx_EQ (comp_mode, compare_result, const0_rtx);
      else
      else
        bcomp = gen_rtx_NE (comp_mode, compare_result, const0_rtx);
        bcomp = gen_rtx_NE (comp_mode, compare_result, const0_rtx);
 
 
      loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
      loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
                                   gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
                                   gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
                                                         loc_ref, pc_rtx)));
                                                         loc_ref, pc_rtx)));
    }
    }
  else if (is_set == 2)
  else if (is_set == 2)
    {
    {
      rtx target = operands[0];
      rtx target = operands[0];
      int compare_size = GET_MODE_BITSIZE (comp_mode);
      int compare_size = GET_MODE_BITSIZE (comp_mode);
      int target_size = GET_MODE_BITSIZE (GET_MODE (target));
      int target_size = GET_MODE_BITSIZE (GET_MODE (target));
      enum machine_mode mode = mode_for_size (target_size, MODE_INT, 0);
      enum machine_mode mode = mode_for_size (target_size, MODE_INT, 0);
      rtx select_mask;
      rtx select_mask;
      rtx op_t = operands[2];
      rtx op_t = operands[2];
      rtx op_f = operands[3];
      rtx op_f = operands[3];
 
 
      /* The result of the comparison can be SI, HI or QI mode.  Create a
      /* The result of the comparison can be SI, HI or QI mode.  Create a
         mask based on that result. */
         mask based on that result. */
      if (target_size > compare_size)
      if (target_size > compare_size)
        {
        {
          select_mask = gen_reg_rtx (mode);
          select_mask = gen_reg_rtx (mode);
          emit_insn (gen_extend_compare (select_mask, compare_result));
          emit_insn (gen_extend_compare (select_mask, compare_result));
        }
        }
      else if (target_size < compare_size)
      else if (target_size < compare_size)
        select_mask =
        select_mask =
          gen_rtx_SUBREG (mode, compare_result,
          gen_rtx_SUBREG (mode, compare_result,
                          (compare_size - target_size) / BITS_PER_UNIT);
                          (compare_size - target_size) / BITS_PER_UNIT);
      else if (comp_mode != mode)
      else if (comp_mode != mode)
        select_mask = gen_rtx_SUBREG (mode, compare_result, 0);
        select_mask = gen_rtx_SUBREG (mode, compare_result, 0);
      else
      else
        select_mask = compare_result;
        select_mask = compare_result;
 
 
      if (GET_MODE (target) != GET_MODE (op_t)
      if (GET_MODE (target) != GET_MODE (op_t)
          || GET_MODE (target) != GET_MODE (op_f))
          || GET_MODE (target) != GET_MODE (op_f))
        abort ();
        abort ();
 
 
      if (reverse_test)
      if (reverse_test)
        emit_insn (gen_selb (target, op_t, op_f, select_mask));
        emit_insn (gen_selb (target, op_t, op_f, select_mask));
      else
      else
        emit_insn (gen_selb (target, op_f, op_t, select_mask));
        emit_insn (gen_selb (target, op_f, op_t, select_mask));
    }
    }
  else
  else
    {
    {
      rtx target = operands[0];
      rtx target = operands[0];
      if (reverse_test)
      if (reverse_test)
        emit_insn (gen_rtx_SET (VOIDmode, compare_result,
        emit_insn (gen_rtx_SET (VOIDmode, compare_result,
                                gen_rtx_NOT (comp_mode, compare_result)));
                                gen_rtx_NOT (comp_mode, compare_result)));
      if (GET_MODE (target) == SImode && GET_MODE (compare_result) == HImode)
      if (GET_MODE (target) == SImode && GET_MODE (compare_result) == HImode)
        emit_insn (gen_extendhisi2 (target, compare_result));
        emit_insn (gen_extendhisi2 (target, compare_result));
      else if (GET_MODE (target) == SImode
      else if (GET_MODE (target) == SImode
               && GET_MODE (compare_result) == QImode)
               && GET_MODE (compare_result) == QImode)
        emit_insn (gen_extend_compare (target, compare_result));
        emit_insn (gen_extend_compare (target, compare_result));
      else
      else
        emit_move_insn (target, compare_result);
        emit_move_insn (target, compare_result);
    }
    }
}
}
 
 
HOST_WIDE_INT
HOST_WIDE_INT
const_double_to_hwint (rtx x)
const_double_to_hwint (rtx x)
{
{
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  REAL_VALUE_TYPE rv;
  REAL_VALUE_TYPE rv;
  if (GET_MODE (x) == SFmode)
  if (GET_MODE (x) == SFmode)
    {
    {
      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
      REAL_VALUE_TO_TARGET_SINGLE (rv, val);
      REAL_VALUE_TO_TARGET_SINGLE (rv, val);
    }
    }
  else if (GET_MODE (x) == DFmode)
  else if (GET_MODE (x) == DFmode)
    {
    {
      long l[2];
      long l[2];
      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
      REAL_VALUE_TO_TARGET_DOUBLE (rv, l);
      REAL_VALUE_TO_TARGET_DOUBLE (rv, l);
      val = l[0];
      val = l[0];
      val = (val << 32) | (l[1] & 0xffffffff);
      val = (val << 32) | (l[1] & 0xffffffff);
    }
    }
  else
  else
    abort ();
    abort ();
  return val;
  return val;
}
}
 
 
rtx
rtx
hwint_to_const_double (enum machine_mode mode, HOST_WIDE_INT v)
hwint_to_const_double (enum machine_mode mode, HOST_WIDE_INT v)
{
{
  long tv[2];
  long tv[2];
  REAL_VALUE_TYPE rv;
  REAL_VALUE_TYPE rv;
  gcc_assert (mode == SFmode || mode == DFmode);
  gcc_assert (mode == SFmode || mode == DFmode);
 
 
  if (mode == SFmode)
  if (mode == SFmode)
    tv[0] = (v << 32) >> 32;
    tv[0] = (v << 32) >> 32;
  else if (mode == DFmode)
  else if (mode == DFmode)
    {
    {
      tv[1] = (v << 32) >> 32;
      tv[1] = (v << 32) >> 32;
      tv[0] = v >> 32;
      tv[0] = v >> 32;
    }
    }
  real_from_target (&rv, tv, mode);
  real_from_target (&rv, tv, mode);
  return CONST_DOUBLE_FROM_REAL_VALUE (rv, mode);
  return CONST_DOUBLE_FROM_REAL_VALUE (rv, mode);
}
}
 
 
void
void
print_operand_address (FILE * file, register rtx addr)
print_operand_address (FILE * file, register rtx addr)
{
{
  rtx reg;
  rtx reg;
  rtx offset;
  rtx offset;
 
 
  if (GET_CODE (addr) == AND
  if (GET_CODE (addr) == AND
      && GET_CODE (XEXP (addr, 1)) == CONST_INT
      && GET_CODE (XEXP (addr, 1)) == CONST_INT
      && INTVAL (XEXP (addr, 1)) == -16)
      && INTVAL (XEXP (addr, 1)) == -16)
    addr = XEXP (addr, 0);
    addr = XEXP (addr, 0);
 
 
  switch (GET_CODE (addr))
  switch (GET_CODE (addr))
    {
    {
    case REG:
    case REG:
      fprintf (file, "0(%s)", reg_names[REGNO (addr)]);
      fprintf (file, "0(%s)", reg_names[REGNO (addr)]);
      break;
      break;
 
 
    case PLUS:
    case PLUS:
      reg = XEXP (addr, 0);
      reg = XEXP (addr, 0);
      offset = XEXP (addr, 1);
      offset = XEXP (addr, 1);
      if (GET_CODE (offset) == REG)
      if (GET_CODE (offset) == REG)
        {
        {
          fprintf (file, "%s,%s", reg_names[REGNO (reg)],
          fprintf (file, "%s,%s", reg_names[REGNO (reg)],
                   reg_names[REGNO (offset)]);
                   reg_names[REGNO (offset)]);
        }
        }
      else if (GET_CODE (offset) == CONST_INT)
      else if (GET_CODE (offset) == CONST_INT)
        {
        {
          fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)",
          fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)",
                   INTVAL (offset), reg_names[REGNO (reg)]);
                   INTVAL (offset), reg_names[REGNO (reg)]);
        }
        }
      else
      else
        abort ();
        abort ();
      break;
      break;
 
 
    case CONST:
    case CONST:
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
    case CONST_INT:
    case CONST_INT:
      output_addr_const (file, addr);
      output_addr_const (file, addr);
      break;
      break;
 
 
    default:
    default:
      debug_rtx (addr);
      debug_rtx (addr);
      abort ();
      abort ();
    }
    }
}
}
 
 
void
void
print_operand (FILE * file, rtx x, int code)
print_operand (FILE * file, rtx x, int code)
{
{
  enum machine_mode mode = GET_MODE (x);
  enum machine_mode mode = GET_MODE (x);
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  unsigned char arr[16];
  unsigned char arr[16];
  int xcode = GET_CODE (x);
  int xcode = GET_CODE (x);
  int i, info;
  int i, info;
  if (GET_MODE (x) == VOIDmode)
  if (GET_MODE (x) == VOIDmode)
    switch (code)
    switch (code)
      {
      {
      case 'L':                 /* 128 bits, signed */
      case 'L':                 /* 128 bits, signed */
      case 'm':                 /* 128 bits, signed */
      case 'm':                 /* 128 bits, signed */
      case 'T':                 /* 128 bits, signed */
      case 'T':                 /* 128 bits, signed */
      case 't':                 /* 128 bits, signed */
      case 't':                 /* 128 bits, signed */
        mode = TImode;
        mode = TImode;
        break;
        break;
      case 'K':                 /* 64 bits, signed */
      case 'K':                 /* 64 bits, signed */
      case 'k':                 /* 64 bits, signed */
      case 'k':                 /* 64 bits, signed */
      case 'D':                 /* 64 bits, signed */
      case 'D':                 /* 64 bits, signed */
      case 'd':                 /* 64 bits, signed */
      case 'd':                 /* 64 bits, signed */
        mode = DImode;
        mode = DImode;
        break;
        break;
      case 'J':                 /* 32 bits, signed */
      case 'J':                 /* 32 bits, signed */
      case 'j':                 /* 32 bits, signed */
      case 'j':                 /* 32 bits, signed */
      case 's':                 /* 32 bits, signed */
      case 's':                 /* 32 bits, signed */
      case 'S':                 /* 32 bits, signed */
      case 'S':                 /* 32 bits, signed */
        mode = SImode;
        mode = SImode;
        break;
        break;
      }
      }
  switch (code)
  switch (code)
    {
    {
 
 
    case 'j':                   /* 32 bits, signed */
    case 'j':                   /* 32 bits, signed */
    case 'k':                   /* 64 bits, signed */
    case 'k':                   /* 64 bits, signed */
    case 'm':                   /* 128 bits, signed */
    case 'm':                   /* 128 bits, signed */
      if (xcode == CONST_INT
      if (xcode == CONST_INT
          || xcode == CONST_DOUBLE || xcode == CONST_VECTOR)
          || xcode == CONST_DOUBLE || xcode == CONST_VECTOR)
        {
        {
          gcc_assert (logical_immediate_p (x, mode));
          gcc_assert (logical_immediate_p (x, mode));
          constant_to_array (mode, x, arr);
          constant_to_array (mode, x, arr);
          val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
          val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
          val = trunc_int_for_mode (val, SImode);
          val = trunc_int_for_mode (val, SImode);
          switch (which_logical_immediate (val))
          switch (which_logical_immediate (val))
          {
          {
          case SPU_ORI:
          case SPU_ORI:
            break;
            break;
          case SPU_ORHI:
          case SPU_ORHI:
            fprintf (file, "h");
            fprintf (file, "h");
            break;
            break;
          case SPU_ORBI:
          case SPU_ORBI:
            fprintf (file, "b");
            fprintf (file, "b");
            break;
            break;
          default:
          default:
            gcc_unreachable();
            gcc_unreachable();
          }
          }
        }
        }
      else
      else
        gcc_unreachable();
        gcc_unreachable();
      return;
      return;
 
 
    case 'J':                   /* 32 bits, signed */
    case 'J':                   /* 32 bits, signed */
    case 'K':                   /* 64 bits, signed */
    case 'K':                   /* 64 bits, signed */
    case 'L':                   /* 128 bits, signed */
    case 'L':                   /* 128 bits, signed */
      if (xcode == CONST_INT
      if (xcode == CONST_INT
          || xcode == CONST_DOUBLE || xcode == CONST_VECTOR)
          || xcode == CONST_DOUBLE || xcode == CONST_VECTOR)
        {
        {
          gcc_assert (logical_immediate_p (x, mode)
          gcc_assert (logical_immediate_p (x, mode)
                      || iohl_immediate_p (x, mode));
                      || iohl_immediate_p (x, mode));
          constant_to_array (mode, x, arr);
          constant_to_array (mode, x, arr);
          val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
          val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
          val = trunc_int_for_mode (val, SImode);
          val = trunc_int_for_mode (val, SImode);
          switch (which_logical_immediate (val))
          switch (which_logical_immediate (val))
          {
          {
          case SPU_ORI:
          case SPU_ORI:
          case SPU_IOHL:
          case SPU_IOHL:
            break;
            break;
          case SPU_ORHI:
          case SPU_ORHI:
            val = trunc_int_for_mode (val, HImode);
            val = trunc_int_for_mode (val, HImode);
            break;
            break;
          case SPU_ORBI:
          case SPU_ORBI:
            val = trunc_int_for_mode (val, QImode);
            val = trunc_int_for_mode (val, QImode);
            break;
            break;
          default:
          default:
            gcc_unreachable();
            gcc_unreachable();
          }
          }
          fprintf (file, HOST_WIDE_INT_PRINT_DEC, val);
          fprintf (file, HOST_WIDE_INT_PRINT_DEC, val);
        }
        }
      else
      else
        gcc_unreachable();
        gcc_unreachable();
      return;
      return;
 
 
    case 't':                   /* 128 bits, signed */
    case 't':                   /* 128 bits, signed */
    case 'd':                   /* 64 bits, signed */
    case 'd':                   /* 64 bits, signed */
    case 's':                   /* 32 bits, signed */
    case 's':                   /* 32 bits, signed */
      if (CONSTANT_P (x))
      if (CONSTANT_P (x))
        {
        {
          enum immediate_class c = classify_immediate (x, mode);
          enum immediate_class c = classify_immediate (x, mode);
          switch (c)
          switch (c)
            {
            {
            case IC_IL1:
            case IC_IL1:
              constant_to_array (mode, x, arr);
              constant_to_array (mode, x, arr);
              val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
              val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
              val = trunc_int_for_mode (val, SImode);
              val = trunc_int_for_mode (val, SImode);
              switch (which_immediate_load (val))
              switch (which_immediate_load (val))
                {
                {
                case SPU_IL:
                case SPU_IL:
                  break;
                  break;
                case SPU_ILA:
                case SPU_ILA:
                  fprintf (file, "a");
                  fprintf (file, "a");
                  break;
                  break;
                case SPU_ILH:
                case SPU_ILH:
                  fprintf (file, "h");
                  fprintf (file, "h");
                  break;
                  break;
                case SPU_ILHU:
                case SPU_ILHU:
                  fprintf (file, "hu");
                  fprintf (file, "hu");
                  break;
                  break;
                default:
                default:
                  gcc_unreachable ();
                  gcc_unreachable ();
                }
                }
              break;
              break;
            case IC_CPAT:
            case IC_CPAT:
              constant_to_array (mode, x, arr);
              constant_to_array (mode, x, arr);
              cpat_info (arr, GET_MODE_SIZE (mode), &info, 0);
              cpat_info (arr, GET_MODE_SIZE (mode), &info, 0);
              if (info == 1)
              if (info == 1)
                fprintf (file, "b");
                fprintf (file, "b");
              else if (info == 2)
              else if (info == 2)
                fprintf (file, "h");
                fprintf (file, "h");
              else if (info == 4)
              else if (info == 4)
                fprintf (file, "w");
                fprintf (file, "w");
              else if (info == 8)
              else if (info == 8)
                fprintf (file, "d");
                fprintf (file, "d");
              break;
              break;
            case IC_IL1s:
            case IC_IL1s:
              if (xcode == CONST_VECTOR)
              if (xcode == CONST_VECTOR)
                {
                {
                  x = CONST_VECTOR_ELT (x, 0);
                  x = CONST_VECTOR_ELT (x, 0);
                  xcode = GET_CODE (x);
                  xcode = GET_CODE (x);
                }
                }
              if (xcode == SYMBOL_REF || xcode == LABEL_REF || xcode == CONST)
              if (xcode == SYMBOL_REF || xcode == LABEL_REF || xcode == CONST)
                fprintf (file, "a");
                fprintf (file, "a");
              else if (xcode == HIGH)
              else if (xcode == HIGH)
                fprintf (file, "hu");
                fprintf (file, "hu");
              break;
              break;
            case IC_FSMBI:
            case IC_FSMBI:
            case IC_FSMBI2:
            case IC_FSMBI2:
            case IC_IL2:
            case IC_IL2:
            case IC_IL2s:
            case IC_IL2s:
            case IC_POOL:
            case IC_POOL:
              abort ();
              abort ();
            }
            }
        }
        }
      else
      else
        gcc_unreachable ();
        gcc_unreachable ();
      return;
      return;
 
 
    case 'T':                   /* 128 bits, signed */
    case 'T':                   /* 128 bits, signed */
    case 'D':                   /* 64 bits, signed */
    case 'D':                   /* 64 bits, signed */
    case 'S':                   /* 32 bits, signed */
    case 'S':                   /* 32 bits, signed */
      if (CONSTANT_P (x))
      if (CONSTANT_P (x))
        {
        {
          enum immediate_class c = classify_immediate (x, mode);
          enum immediate_class c = classify_immediate (x, mode);
          switch (c)
          switch (c)
            {
            {
            case IC_IL1:
            case IC_IL1:
              constant_to_array (mode, x, arr);
              constant_to_array (mode, x, arr);
              val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
              val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
              val = trunc_int_for_mode (val, SImode);
              val = trunc_int_for_mode (val, SImode);
              switch (which_immediate_load (val))
              switch (which_immediate_load (val))
                {
                {
                case SPU_IL:
                case SPU_IL:
                case SPU_ILA:
                case SPU_ILA:
                  break;
                  break;
                case SPU_ILH:
                case SPU_ILH:
                case SPU_ILHU:
                case SPU_ILHU:
                  val = trunc_int_for_mode (((arr[0] << 8) | arr[1]), HImode);
                  val = trunc_int_for_mode (((arr[0] << 8) | arr[1]), HImode);
                  break;
                  break;
                default:
                default:
                  gcc_unreachable ();
                  gcc_unreachable ();
                }
                }
              fprintf (file, HOST_WIDE_INT_PRINT_DEC, val);
              fprintf (file, HOST_WIDE_INT_PRINT_DEC, val);
              break;
              break;
            case IC_FSMBI:
            case IC_FSMBI:
              constant_to_array (mode, x, arr);
              constant_to_array (mode, x, arr);
              val = 0;
              val = 0;
              for (i = 0; i < 16; i++)
              for (i = 0; i < 16; i++)
                {
                {
                  val <<= 1;
                  val <<= 1;
                  val |= arr[i] & 1;
                  val |= arr[i] & 1;
                }
                }
              print_operand (file, GEN_INT (val), 0);
              print_operand (file, GEN_INT (val), 0);
              break;
              break;
            case IC_CPAT:
            case IC_CPAT:
              constant_to_array (mode, x, arr);
              constant_to_array (mode, x, arr);
              cpat_info (arr, GET_MODE_SIZE (mode), 0, &info);
              cpat_info (arr, GET_MODE_SIZE (mode), 0, &info);
              fprintf (file, HOST_WIDE_INT_PRINT_DEC, (HOST_WIDE_INT)info);
              fprintf (file, HOST_WIDE_INT_PRINT_DEC, (HOST_WIDE_INT)info);
              break;
              break;
            case IC_IL1s:
            case IC_IL1s:
              if (xcode == HIGH)
              if (xcode == HIGH)
                x = XEXP (x, 0);
                x = XEXP (x, 0);
              if (GET_CODE (x) == CONST_VECTOR)
              if (GET_CODE (x) == CONST_VECTOR)
                x = CONST_VECTOR_ELT (x, 0);
                x = CONST_VECTOR_ELT (x, 0);
              output_addr_const (file, x);
              output_addr_const (file, x);
              if (xcode == HIGH)
              if (xcode == HIGH)
                fprintf (file, "@h");
                fprintf (file, "@h");
              break;
              break;
            case IC_IL2:
            case IC_IL2:
            case IC_IL2s:
            case IC_IL2s:
            case IC_FSMBI2:
            case IC_FSMBI2:
            case IC_POOL:
            case IC_POOL:
              abort ();
              abort ();
            }
            }
        }
        }
      else
      else
        gcc_unreachable ();
        gcc_unreachable ();
      return;
      return;
 
 
    case 'C':
    case 'C':
      if (xcode == CONST_INT)
      if (xcode == CONST_INT)
        {
        {
          /* Only 4 least significant bits are relevant for generate
          /* Only 4 least significant bits are relevant for generate
             control word instructions. */
             control word instructions. */
          fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 15);
          fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 15);
          return;
          return;
        }
        }
      break;
      break;
 
 
    case 'M':                   /* print code for c*d */
    case 'M':                   /* print code for c*d */
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        switch (INTVAL (x))
        switch (INTVAL (x))
          {
          {
          case 1:
          case 1:
            fprintf (file, "b");
            fprintf (file, "b");
            break;
            break;
          case 2:
          case 2:
            fprintf (file, "h");
            fprintf (file, "h");
            break;
            break;
          case 4:
          case 4:
            fprintf (file, "w");
            fprintf (file, "w");
            break;
            break;
          case 8:
          case 8:
            fprintf (file, "d");
            fprintf (file, "d");
            break;
            break;
          default:
          default:
            gcc_unreachable();
            gcc_unreachable();
          }
          }
      else
      else
        gcc_unreachable();
        gcc_unreachable();
      return;
      return;
 
 
    case 'N':                   /* Negate the operand */
    case 'N':                   /* Negate the operand */
      if (xcode == CONST_INT)
      if (xcode == CONST_INT)
        fprintf (file, HOST_WIDE_INT_PRINT_DEC, -INTVAL (x));
        fprintf (file, HOST_WIDE_INT_PRINT_DEC, -INTVAL (x));
      else if (xcode == CONST_VECTOR)
      else if (xcode == CONST_VECTOR)
        fprintf (file, HOST_WIDE_INT_PRINT_DEC,
        fprintf (file, HOST_WIDE_INT_PRINT_DEC,
                 -INTVAL (CONST_VECTOR_ELT (x, 0)));
                 -INTVAL (CONST_VECTOR_ELT (x, 0)));
      return;
      return;
 
 
    case 'I':                   /* enable/disable interrupts */
    case 'I':                   /* enable/disable interrupts */
      if (xcode == CONST_INT)
      if (xcode == CONST_INT)
        fprintf (file, "%s",  INTVAL (x) == 0 ? "d" : "e");
        fprintf (file, "%s",  INTVAL (x) == 0 ? "d" : "e");
      return;
      return;
 
 
    case 'b':                   /* branch modifiers */
    case 'b':                   /* branch modifiers */
      if (xcode == REG)
      if (xcode == REG)
        fprintf (file, "%s", GET_MODE (x) == HImode ? "h" : "");
        fprintf (file, "%s", GET_MODE (x) == HImode ? "h" : "");
      else if (COMPARISON_P (x))
      else if (COMPARISON_P (x))
        fprintf (file, "%s", xcode == NE ? "n" : "");
        fprintf (file, "%s", xcode == NE ? "n" : "");
      return;
      return;
 
 
    case 'i':                   /* indirect call */
    case 'i':                   /* indirect call */
      if (xcode == MEM)
      if (xcode == MEM)
        {
        {
          if (GET_CODE (XEXP (x, 0)) == REG)
          if (GET_CODE (XEXP (x, 0)) == REG)
            /* Used in indirect function calls. */
            /* Used in indirect function calls. */
            fprintf (file, "%s", reg_names[REGNO (XEXP (x, 0))]);
            fprintf (file, "%s", reg_names[REGNO (XEXP (x, 0))]);
          else
          else
            output_address (XEXP (x, 0));
            output_address (XEXP (x, 0));
        }
        }
      return;
      return;
 
 
    case 'p':                   /* load/store */
    case 'p':                   /* load/store */
      if (xcode == MEM)
      if (xcode == MEM)
        {
        {
          x = XEXP (x, 0);
          x = XEXP (x, 0);
          xcode = GET_CODE (x);
          xcode = GET_CODE (x);
        }
        }
      if (xcode == AND)
      if (xcode == AND)
        {
        {
          x = XEXP (x, 0);
          x = XEXP (x, 0);
          xcode = GET_CODE (x);
          xcode = GET_CODE (x);
        }
        }
      if (xcode == REG)
      if (xcode == REG)
        fprintf (file, "d");
        fprintf (file, "d");
      else if (xcode == CONST_INT)
      else if (xcode == CONST_INT)
        fprintf (file, "a");
        fprintf (file, "a");
      else if (xcode == CONST || xcode == SYMBOL_REF || xcode == LABEL_REF)
      else if (xcode == CONST || xcode == SYMBOL_REF || xcode == LABEL_REF)
        fprintf (file, "r");
        fprintf (file, "r");
      else if (xcode == PLUS || xcode == LO_SUM)
      else if (xcode == PLUS || xcode == LO_SUM)
        {
        {
          if (GET_CODE (XEXP (x, 1)) == REG)
          if (GET_CODE (XEXP (x, 1)) == REG)
            fprintf (file, "x");
            fprintf (file, "x");
          else
          else
            fprintf (file, "d");
            fprintf (file, "d");
        }
        }
      return;
      return;
 
 
    case 'e':
    case 'e':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val &= 0x7;
      val &= 0x7;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'f':
    case 'f':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val &= 0x1f;
      val &= 0x1f;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'g':
    case 'g':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val &= 0x3f;
      val &= 0x3f;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'h':
    case 'h':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = (val >> 3) & 0x1f;
      val = (val >> 3) & 0x1f;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'E':
    case 'E':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = -val;
      val = -val;
      val &= 0x7;
      val &= 0x7;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'F':
    case 'F':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = -val;
      val = -val;
      val &= 0x1f;
      val &= 0x1f;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'G':
    case 'G':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = -val;
      val = -val;
      val &= 0x3f;
      val &= 0x3f;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'H':
    case 'H':
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = xcode == CONST_INT ? INTVAL (x) : INTVAL (CONST_VECTOR_ELT (x, 0));
      val = -(val & -8ll);
      val = -(val & -8ll);
      val = (val >> 3) & 0x1f;
      val = (val >> 3) & 0x1f;
      output_addr_const (file, GEN_INT (val));
      output_addr_const (file, GEN_INT (val));
      return;
      return;
 
 
    case 'v':
    case 'v':
    case 'w':
    case 'w':
      constant_to_array (mode, x, arr);
      constant_to_array (mode, x, arr);
      val = (((arr[0] << 1) + (arr[1] >> 7)) & 0xff) - 127;
      val = (((arr[0] << 1) + (arr[1] >> 7)) & 0xff) - 127;
      output_addr_const (file, GEN_INT (code == 'w' ? -val : val));
      output_addr_const (file, GEN_INT (code == 'w' ? -val : val));
      return;
      return;
 
 
    case 0:
    case 0:
      if (xcode == REG)
      if (xcode == REG)
        fprintf (file, "%s", reg_names[REGNO (x)]);
        fprintf (file, "%s", reg_names[REGNO (x)]);
      else if (xcode == MEM)
      else if (xcode == MEM)
        output_address (XEXP (x, 0));
        output_address (XEXP (x, 0));
      else if (xcode == CONST_VECTOR)
      else if (xcode == CONST_VECTOR)
        print_operand (file, CONST_VECTOR_ELT (x, 0), 0);
        print_operand (file, CONST_VECTOR_ELT (x, 0), 0);
      else
      else
        output_addr_const (file, x);
        output_addr_const (file, x);
      return;
      return;
 
 
      /* unused letters
      /* unused letters
                      o qr  u   yz
                      o qr  u   yz
        AB            OPQR  UVWXYZ */
        AB            OPQR  UVWXYZ */
    default:
    default:
      output_operand_lossage ("invalid %%xn code");
      output_operand_lossage ("invalid %%xn code");
    }
    }
  gcc_unreachable ();
  gcc_unreachable ();
}
}
 
 
extern char call_used_regs[];
extern char call_used_regs[];
 
 
/* For PIC mode we've reserved PIC_OFFSET_TABLE_REGNUM, which is a
/* For PIC mode we've reserved PIC_OFFSET_TABLE_REGNUM, which is a
   caller saved register.  For leaf functions it is more efficient to
   caller saved register.  For leaf functions it is more efficient to
   use a volatile register because we won't need to save and restore the
   use a volatile register because we won't need to save and restore the
   pic register.  This routine is only valid after register allocation
   pic register.  This routine is only valid after register allocation
   is completed, so we can pick an unused register.  */
   is completed, so we can pick an unused register.  */
static rtx
static rtx
get_pic_reg (void)
get_pic_reg (void)
{
{
  rtx pic_reg = pic_offset_table_rtx;
  rtx pic_reg = pic_offset_table_rtx;
  if (!reload_completed && !reload_in_progress)
  if (!reload_completed && !reload_in_progress)
    abort ();
    abort ();
  if (current_function_is_leaf && !df_regs_ever_live_p (LAST_ARG_REGNUM))
  if (current_function_is_leaf && !df_regs_ever_live_p (LAST_ARG_REGNUM))
    pic_reg = gen_rtx_REG (SImode, LAST_ARG_REGNUM);
    pic_reg = gen_rtx_REG (SImode, LAST_ARG_REGNUM);
  return pic_reg;
  return pic_reg;
}
}
 
 
/* Split constant addresses to handle cases that are too large.
/* Split constant addresses to handle cases that are too large.
   Add in the pic register when in PIC mode.
   Add in the pic register when in PIC mode.
   Split immediates that require more than 1 instruction. */
   Split immediates that require more than 1 instruction. */
int
int
spu_split_immediate (rtx * ops)
spu_split_immediate (rtx * ops)
{
{
  enum machine_mode mode = GET_MODE (ops[0]);
  enum machine_mode mode = GET_MODE (ops[0]);
  enum immediate_class c = classify_immediate (ops[1], mode);
  enum immediate_class c = classify_immediate (ops[1], mode);
 
 
  switch (c)
  switch (c)
    {
    {
    case IC_IL2:
    case IC_IL2:
      {
      {
        unsigned char arrhi[16];
        unsigned char arrhi[16];
        unsigned char arrlo[16];
        unsigned char arrlo[16];
        rtx to, temp, hi, lo;
        rtx to, temp, hi, lo;
        int i;
        int i;
        enum machine_mode imode = mode;
        enum machine_mode imode = mode;
        /* We need to do reals as ints because the constant used in the
        /* We need to do reals as ints because the constant used in the
           IOR might not be a legitimate real constant. */
           IOR might not be a legitimate real constant. */
        imode = int_mode_for_mode (mode);
        imode = int_mode_for_mode (mode);
        constant_to_array (mode, ops[1], arrhi);
        constant_to_array (mode, ops[1], arrhi);
        if (imode != mode)
        if (imode != mode)
          to = simplify_gen_subreg (imode, ops[0], mode, 0);
          to = simplify_gen_subreg (imode, ops[0], mode, 0);
        else
        else
          to = ops[0];
          to = ops[0];
        temp = !can_create_pseudo_p () ? to : gen_reg_rtx (imode);
        temp = !can_create_pseudo_p () ? to : gen_reg_rtx (imode);
        for (i = 0; i < 16; i += 4)
        for (i = 0; i < 16; i += 4)
          {
          {
            arrlo[i + 2] = arrhi[i + 2];
            arrlo[i + 2] = arrhi[i + 2];
            arrlo[i + 3] = arrhi[i + 3];
            arrlo[i + 3] = arrhi[i + 3];
            arrlo[i + 0] = arrlo[i + 1] = 0;
            arrlo[i + 0] = arrlo[i + 1] = 0;
            arrhi[i + 2] = arrhi[i + 3] = 0;
            arrhi[i + 2] = arrhi[i + 3] = 0;
          }
          }
        hi = array_to_constant (imode, arrhi);
        hi = array_to_constant (imode, arrhi);
        lo = array_to_constant (imode, arrlo);
        lo = array_to_constant (imode, arrlo);
        emit_move_insn (temp, hi);
        emit_move_insn (temp, hi);
        emit_insn (gen_rtx_SET
        emit_insn (gen_rtx_SET
                   (VOIDmode, to, gen_rtx_IOR (imode, temp, lo)));
                   (VOIDmode, to, gen_rtx_IOR (imode, temp, lo)));
        return 1;
        return 1;
      }
      }
    case IC_FSMBI2:
    case IC_FSMBI2:
      {
      {
        unsigned char arr_fsmbi[16];
        unsigned char arr_fsmbi[16];
        unsigned char arr_andbi[16];
        unsigned char arr_andbi[16];
        rtx to, reg_fsmbi, reg_and;
        rtx to, reg_fsmbi, reg_and;
        int i;
        int i;
        enum machine_mode imode = mode;
        enum machine_mode imode = mode;
        /* We need to do reals as ints because the constant used in the
        /* We need to do reals as ints because the constant used in the
         * AND might not be a legitimate real constant. */
         * AND might not be a legitimate real constant. */
        imode = int_mode_for_mode (mode);
        imode = int_mode_for_mode (mode);
        constant_to_array (mode, ops[1], arr_fsmbi);
        constant_to_array (mode, ops[1], arr_fsmbi);
        if (imode != mode)
        if (imode != mode)
          to = simplify_gen_subreg(imode, ops[0], GET_MODE (ops[0]), 0);
          to = simplify_gen_subreg(imode, ops[0], GET_MODE (ops[0]), 0);
        else
        else
          to = ops[0];
          to = ops[0];
        for (i = 0; i < 16; i++)
        for (i = 0; i < 16; i++)
          if (arr_fsmbi[i] != 0)
          if (arr_fsmbi[i] != 0)
            {
            {
              arr_andbi[0] = arr_fsmbi[i];
              arr_andbi[0] = arr_fsmbi[i];
              arr_fsmbi[i] = 0xff;
              arr_fsmbi[i] = 0xff;
            }
            }
        for (i = 1; i < 16; i++)
        for (i = 1; i < 16; i++)
          arr_andbi[i] = arr_andbi[0];
          arr_andbi[i] = arr_andbi[0];
        reg_fsmbi = array_to_constant (imode, arr_fsmbi);
        reg_fsmbi = array_to_constant (imode, arr_fsmbi);
        reg_and = array_to_constant (imode, arr_andbi);
        reg_and = array_to_constant (imode, arr_andbi);
        emit_move_insn (to, reg_fsmbi);
        emit_move_insn (to, reg_fsmbi);
        emit_insn (gen_rtx_SET
        emit_insn (gen_rtx_SET
                   (VOIDmode, to, gen_rtx_AND (imode, to, reg_and)));
                   (VOIDmode, to, gen_rtx_AND (imode, to, reg_and)));
        return 1;
        return 1;
      }
      }
    case IC_POOL:
    case IC_POOL:
      if (reload_in_progress || reload_completed)
      if (reload_in_progress || reload_completed)
        {
        {
          rtx mem = force_const_mem (mode, ops[1]);
          rtx mem = force_const_mem (mode, ops[1]);
          if (TARGET_LARGE_MEM)
          if (TARGET_LARGE_MEM)
            {
            {
              rtx addr = gen_rtx_REG (Pmode, REGNO (ops[0]));
              rtx addr = gen_rtx_REG (Pmode, REGNO (ops[0]));
              emit_move_insn (addr, XEXP (mem, 0));
              emit_move_insn (addr, XEXP (mem, 0));
              mem = replace_equiv_address (mem, addr);
              mem = replace_equiv_address (mem, addr);
            }
            }
          emit_move_insn (ops[0], mem);
          emit_move_insn (ops[0], mem);
          return 1;
          return 1;
        }
        }
      break;
      break;
    case IC_IL1s:
    case IC_IL1s:
    case IC_IL2s:
    case IC_IL2s:
      if (reload_completed && GET_CODE (ops[1]) != HIGH)
      if (reload_completed && GET_CODE (ops[1]) != HIGH)
        {
        {
          if (c == IC_IL2s)
          if (c == IC_IL2s)
            {
            {
              emit_move_insn (ops[0], gen_rtx_HIGH (mode, ops[1]));
              emit_move_insn (ops[0], gen_rtx_HIGH (mode, ops[1]));
              emit_move_insn (ops[0], gen_rtx_LO_SUM (mode, ops[0], ops[1]));
              emit_move_insn (ops[0], gen_rtx_LO_SUM (mode, ops[0], ops[1]));
            }
            }
          else if (flag_pic)
          else if (flag_pic)
            emit_insn (gen_pic (ops[0], ops[1]));
            emit_insn (gen_pic (ops[0], ops[1]));
          if (flag_pic)
          if (flag_pic)
            {
            {
              rtx pic_reg = get_pic_reg ();
              rtx pic_reg = get_pic_reg ();
              emit_insn (gen_addsi3 (ops[0], ops[0], pic_reg));
              emit_insn (gen_addsi3 (ops[0], ops[0], pic_reg));
              crtl->uses_pic_offset_table = 1;
              crtl->uses_pic_offset_table = 1;
            }
            }
          return flag_pic || c == IC_IL2s;
          return flag_pic || c == IC_IL2s;
        }
        }
      break;
      break;
    case IC_IL1:
    case IC_IL1:
    case IC_FSMBI:
    case IC_FSMBI:
    case IC_CPAT:
    case IC_CPAT:
      break;
      break;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* SAVING is TRUE when we are generating the actual load and store
/* SAVING is TRUE when we are generating the actual load and store
   instructions for REGNO.  When determining the size of the stack
   instructions for REGNO.  When determining the size of the stack
   needed for saving register we must allocate enough space for the
   needed for saving register we must allocate enough space for the
   worst case, because we don't always have the information early enough
   worst case, because we don't always have the information early enough
   to not allocate it.  But we can at least eliminate the actual loads
   to not allocate it.  But we can at least eliminate the actual loads
   and stores during the prologue/epilogue.  */
   and stores during the prologue/epilogue.  */
static int
static int
need_to_save_reg (int regno, int saving)
need_to_save_reg (int regno, int saving)
{
{
  if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
  if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
    return 1;
    return 1;
  if (flag_pic
  if (flag_pic
      && regno == PIC_OFFSET_TABLE_REGNUM
      && regno == PIC_OFFSET_TABLE_REGNUM
      && (!saving || crtl->uses_pic_offset_table)
      && (!saving || crtl->uses_pic_offset_table)
      && (!saving
      && (!saving
          || !current_function_is_leaf || df_regs_ever_live_p (LAST_ARG_REGNUM)))
          || !current_function_is_leaf || df_regs_ever_live_p (LAST_ARG_REGNUM)))
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
/* This function is only correct starting with local register
/* This function is only correct starting with local register
   allocation */
   allocation */
int
int
spu_saved_regs_size (void)
spu_saved_regs_size (void)
{
{
  int reg_save_size = 0;
  int reg_save_size = 0;
  int regno;
  int regno;
 
 
  for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno)
  for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno)
    if (need_to_save_reg (regno, 0))
    if (need_to_save_reg (regno, 0))
      reg_save_size += 0x10;
      reg_save_size += 0x10;
  return reg_save_size;
  return reg_save_size;
}
}
 
 
static rtx
static rtx
frame_emit_store (int regno, rtx addr, HOST_WIDE_INT offset)
frame_emit_store (int regno, rtx addr, HOST_WIDE_INT offset)
{
{
  rtx reg = gen_rtx_REG (V4SImode, regno);
  rtx reg = gen_rtx_REG (V4SImode, regno);
  rtx mem =
  rtx mem =
    gen_frame_mem (V4SImode, gen_rtx_PLUS (Pmode, addr, GEN_INT (offset)));
    gen_frame_mem (V4SImode, gen_rtx_PLUS (Pmode, addr, GEN_INT (offset)));
  return emit_insn (gen_movv4si (mem, reg));
  return emit_insn (gen_movv4si (mem, reg));
}
}
 
 
static rtx
static rtx
frame_emit_load (int regno, rtx addr, HOST_WIDE_INT offset)
frame_emit_load (int regno, rtx addr, HOST_WIDE_INT offset)
{
{
  rtx reg = gen_rtx_REG (V4SImode, regno);
  rtx reg = gen_rtx_REG (V4SImode, regno);
  rtx mem =
  rtx mem =
    gen_frame_mem (V4SImode, gen_rtx_PLUS (Pmode, addr, GEN_INT (offset)));
    gen_frame_mem (V4SImode, gen_rtx_PLUS (Pmode, addr, GEN_INT (offset)));
  return emit_insn (gen_movv4si (reg, mem));
  return emit_insn (gen_movv4si (reg, mem));
}
}
 
 
/* This happens after reload, so we need to expand it.  */
/* This happens after reload, so we need to expand it.  */
static rtx
static rtx
frame_emit_add_imm (rtx dst, rtx src, HOST_WIDE_INT imm, rtx scratch)
frame_emit_add_imm (rtx dst, rtx src, HOST_WIDE_INT imm, rtx scratch)
{
{
  rtx insn;
  rtx insn;
  if (satisfies_constraint_K (GEN_INT (imm)))
  if (satisfies_constraint_K (GEN_INT (imm)))
    {
    {
      insn = emit_insn (gen_addsi3 (dst, src, GEN_INT (imm)));
      insn = emit_insn (gen_addsi3 (dst, src, GEN_INT (imm)));
    }
    }
  else
  else
    {
    {
      emit_insn (gen_movsi (scratch, gen_int_mode (imm, SImode)));
      emit_insn (gen_movsi (scratch, gen_int_mode (imm, SImode)));
      insn = emit_insn (gen_addsi3 (dst, src, scratch));
      insn = emit_insn (gen_addsi3 (dst, src, scratch));
      if (REGNO (src) == REGNO (scratch))
      if (REGNO (src) == REGNO (scratch))
        abort ();
        abort ();
    }
    }
  return insn;
  return insn;
}
}
 
 
/* Return nonzero if this function is known to have a null epilogue.  */
/* Return nonzero if this function is known to have a null epilogue.  */
 
 
int
int
direct_return (void)
direct_return (void)
{
{
  if (reload_completed)
  if (reload_completed)
    {
    {
      if (cfun->static_chain_decl == 0
      if (cfun->static_chain_decl == 0
          && (spu_saved_regs_size ()
          && (spu_saved_regs_size ()
              + get_frame_size ()
              + get_frame_size ()
              + crtl->outgoing_args_size
              + crtl->outgoing_args_size
              + crtl->args.pretend_args_size == 0)
              + crtl->args.pretend_args_size == 0)
          && current_function_is_leaf)
          && current_function_is_leaf)
        return 1;
        return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/*
/*
   The stack frame looks like this:
   The stack frame looks like this:
         +-------------+
         +-------------+
         |  incoming   |
         |  incoming   |
         |    args     |
         |    args     |
   AP -> +-------------+
   AP -> +-------------+
         | $lr save    |
         | $lr save    |
         +-------------+
         +-------------+
 prev SP | back chain  |
 prev SP | back chain  |
         +-------------+
         +-------------+
         |  var args   |
         |  var args   |
         |  reg save   | crtl->args.pretend_args_size bytes
         |  reg save   | crtl->args.pretend_args_size bytes
         +-------------+
         +-------------+
         |    ...      |
         |    ...      |
         | saved regs  | spu_saved_regs_size() bytes
         | saved regs  | spu_saved_regs_size() bytes
   FP -> +-------------+
   FP -> +-------------+
         |    ...      |
         |    ...      |
         |   vars      | get_frame_size()  bytes
         |   vars      | get_frame_size()  bytes
  HFP -> +-------------+
  HFP -> +-------------+
         |    ...      |
         |    ...      |
         |  outgoing   |
         |  outgoing   |
         |    args     | crtl->outgoing_args_size bytes
         |    args     | crtl->outgoing_args_size bytes
         +-------------+
         +-------------+
         | $lr of next |
         | $lr of next |
         |   frame     |
         |   frame     |
         +-------------+
         +-------------+
         | back chain  |
         | back chain  |
   SP -> +-------------+
   SP -> +-------------+
 
 
*/
*/
void
void
spu_expand_prologue (void)
spu_expand_prologue (void)
{
{
  HOST_WIDE_INT size = get_frame_size (), offset, regno;
  HOST_WIDE_INT size = get_frame_size (), offset, regno;
  HOST_WIDE_INT total_size;
  HOST_WIDE_INT total_size;
  HOST_WIDE_INT saved_regs_size;
  HOST_WIDE_INT saved_regs_size;
  rtx sp_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
  rtx sp_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
  rtx scratch_reg_0, scratch_reg_1;
  rtx scratch_reg_0, scratch_reg_1;
  rtx insn, real;
  rtx insn, real;
 
 
  if (flag_pic && optimize == 0)
  if (flag_pic && optimize == 0)
    crtl->uses_pic_offset_table = 1;
    crtl->uses_pic_offset_table = 1;
 
 
  if (spu_naked_function_p (current_function_decl))
  if (spu_naked_function_p (current_function_decl))
    return;
    return;
 
 
  scratch_reg_0 = gen_rtx_REG (SImode, LAST_ARG_REGNUM + 1);
  scratch_reg_0 = gen_rtx_REG (SImode, LAST_ARG_REGNUM + 1);
  scratch_reg_1 = gen_rtx_REG (SImode, LAST_ARG_REGNUM + 2);
  scratch_reg_1 = gen_rtx_REG (SImode, LAST_ARG_REGNUM + 2);
 
 
  saved_regs_size = spu_saved_regs_size ();
  saved_regs_size = spu_saved_regs_size ();
  total_size = size + saved_regs_size
  total_size = size + saved_regs_size
    + crtl->outgoing_args_size
    + crtl->outgoing_args_size
    + crtl->args.pretend_args_size;
    + crtl->args.pretend_args_size;
 
 
  if (!current_function_is_leaf
  if (!current_function_is_leaf
      || cfun->calls_alloca || total_size > 0)
      || cfun->calls_alloca || total_size > 0)
    total_size += STACK_POINTER_OFFSET;
    total_size += STACK_POINTER_OFFSET;
 
 
  /* Save this first because code after this might use the link
  /* Save this first because code after this might use the link
     register as a scratch register. */
     register as a scratch register. */
  if (!current_function_is_leaf)
  if (!current_function_is_leaf)
    {
    {
      insn = frame_emit_store (LINK_REGISTER_REGNUM, sp_reg, 16);
      insn = frame_emit_store (LINK_REGISTER_REGNUM, sp_reg, 16);
      RTX_FRAME_RELATED_P (insn) = 1;
      RTX_FRAME_RELATED_P (insn) = 1;
    }
    }
 
 
  if (total_size > 0)
  if (total_size > 0)
    {
    {
      offset = -crtl->args.pretend_args_size;
      offset = -crtl->args.pretend_args_size;
      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
        if (need_to_save_reg (regno, 1))
        if (need_to_save_reg (regno, 1))
          {
          {
            offset -= 16;
            offset -= 16;
            insn = frame_emit_store (regno, sp_reg, offset);
            insn = frame_emit_store (regno, sp_reg, offset);
            RTX_FRAME_RELATED_P (insn) = 1;
            RTX_FRAME_RELATED_P (insn) = 1;
          }
          }
    }
    }
 
 
  if (flag_pic && crtl->uses_pic_offset_table)
  if (flag_pic && crtl->uses_pic_offset_table)
    {
    {
      rtx pic_reg = get_pic_reg ();
      rtx pic_reg = get_pic_reg ();
      insn = emit_insn (gen_load_pic_offset (pic_reg, scratch_reg_0));
      insn = emit_insn (gen_load_pic_offset (pic_reg, scratch_reg_0));
      insn = emit_insn (gen_subsi3 (pic_reg, pic_reg, scratch_reg_0));
      insn = emit_insn (gen_subsi3 (pic_reg, pic_reg, scratch_reg_0));
    }
    }
 
 
  if (total_size > 0)
  if (total_size > 0)
    {
    {
      if (flag_stack_check)
      if (flag_stack_check)
        {
        {
          /* We compare against total_size-1 because
          /* We compare against total_size-1 because
             ($sp >= total_size) <=> ($sp > total_size-1) */
             ($sp >= total_size) <=> ($sp > total_size-1) */
          rtx scratch_v4si = gen_rtx_REG (V4SImode, REGNO (scratch_reg_0));
          rtx scratch_v4si = gen_rtx_REG (V4SImode, REGNO (scratch_reg_0));
          rtx sp_v4si = gen_rtx_REG (V4SImode, STACK_POINTER_REGNUM);
          rtx sp_v4si = gen_rtx_REG (V4SImode, STACK_POINTER_REGNUM);
          rtx size_v4si = spu_const (V4SImode, total_size - 1);
          rtx size_v4si = spu_const (V4SImode, total_size - 1);
          if (!satisfies_constraint_K (GEN_INT (total_size - 1)))
          if (!satisfies_constraint_K (GEN_INT (total_size - 1)))
            {
            {
              emit_move_insn (scratch_v4si, size_v4si);
              emit_move_insn (scratch_v4si, size_v4si);
              size_v4si = scratch_v4si;
              size_v4si = scratch_v4si;
            }
            }
          emit_insn (gen_cgt_v4si (scratch_v4si, sp_v4si, size_v4si));
          emit_insn (gen_cgt_v4si (scratch_v4si, sp_v4si, size_v4si));
          emit_insn (gen_vec_extractv4si
          emit_insn (gen_vec_extractv4si
                     (scratch_reg_0, scratch_v4si, GEN_INT (1)));
                     (scratch_reg_0, scratch_v4si, GEN_INT (1)));
          emit_insn (gen_spu_heq (scratch_reg_0, GEN_INT (0)));
          emit_insn (gen_spu_heq (scratch_reg_0, GEN_INT (0)));
        }
        }
 
 
      /* Adjust the stack pointer, and make sure scratch_reg_0 contains
      /* Adjust the stack pointer, and make sure scratch_reg_0 contains
         the value of the previous $sp because we save it as the back
         the value of the previous $sp because we save it as the back
         chain. */
         chain. */
      if (total_size <= 2000)
      if (total_size <= 2000)
        {
        {
          /* In this case we save the back chain first. */
          /* In this case we save the back chain first. */
          insn = frame_emit_store (STACK_POINTER_REGNUM, sp_reg, -total_size);
          insn = frame_emit_store (STACK_POINTER_REGNUM, sp_reg, -total_size);
          insn =
          insn =
            frame_emit_add_imm (sp_reg, sp_reg, -total_size, scratch_reg_0);
            frame_emit_add_imm (sp_reg, sp_reg, -total_size, scratch_reg_0);
        }
        }
      else
      else
        {
        {
          insn = emit_move_insn (scratch_reg_0, sp_reg);
          insn = emit_move_insn (scratch_reg_0, sp_reg);
          insn =
          insn =
            frame_emit_add_imm (sp_reg, sp_reg, -total_size, scratch_reg_1);
            frame_emit_add_imm (sp_reg, sp_reg, -total_size, scratch_reg_1);
        }
        }
      RTX_FRAME_RELATED_P (insn) = 1;
      RTX_FRAME_RELATED_P (insn) = 1;
      real = gen_addsi3 (sp_reg, sp_reg, GEN_INT (-total_size));
      real = gen_addsi3 (sp_reg, sp_reg, GEN_INT (-total_size));
      add_reg_note (insn, REG_FRAME_RELATED_EXPR, real);
      add_reg_note (insn, REG_FRAME_RELATED_EXPR, real);
 
 
      if (total_size > 2000)
      if (total_size > 2000)
        {
        {
          /* Save the back chain ptr */
          /* Save the back chain ptr */
          insn = frame_emit_store (REGNO (scratch_reg_0), sp_reg, 0);
          insn = frame_emit_store (REGNO (scratch_reg_0), sp_reg, 0);
        }
        }
 
 
      if (frame_pointer_needed)
      if (frame_pointer_needed)
        {
        {
          rtx fp_reg = gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM);
          rtx fp_reg = gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM);
          HOST_WIDE_INT fp_offset = STACK_POINTER_OFFSET
          HOST_WIDE_INT fp_offset = STACK_POINTER_OFFSET
            + crtl->outgoing_args_size;
            + crtl->outgoing_args_size;
          /* Set the new frame_pointer */
          /* Set the new frame_pointer */
          insn = frame_emit_add_imm (fp_reg, sp_reg, fp_offset, scratch_reg_0);
          insn = frame_emit_add_imm (fp_reg, sp_reg, fp_offset, scratch_reg_0);
          RTX_FRAME_RELATED_P (insn) = 1;
          RTX_FRAME_RELATED_P (insn) = 1;
          real = gen_addsi3 (fp_reg, sp_reg, GEN_INT (fp_offset));
          real = gen_addsi3 (fp_reg, sp_reg, GEN_INT (fp_offset));
          add_reg_note (insn, REG_FRAME_RELATED_EXPR, real);
          add_reg_note (insn, REG_FRAME_RELATED_EXPR, real);
          REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = STACK_BOUNDARY;
          REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = STACK_BOUNDARY;
        }
        }
    }
    }
 
 
}
}
 
 
void
void
spu_expand_epilogue (bool sibcall_p)
spu_expand_epilogue (bool sibcall_p)
{
{
  int size = get_frame_size (), offset, regno;
  int size = get_frame_size (), offset, regno;
  HOST_WIDE_INT saved_regs_size, total_size;
  HOST_WIDE_INT saved_regs_size, total_size;
  rtx sp_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
  rtx sp_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
  rtx jump, scratch_reg_0;
  rtx jump, scratch_reg_0;
 
 
  if (spu_naked_function_p (current_function_decl))
  if (spu_naked_function_p (current_function_decl))
    return;
    return;
 
 
  scratch_reg_0 = gen_rtx_REG (SImode, LAST_ARG_REGNUM + 1);
  scratch_reg_0 = gen_rtx_REG (SImode, LAST_ARG_REGNUM + 1);
 
 
  saved_regs_size = spu_saved_regs_size ();
  saved_regs_size = spu_saved_regs_size ();
  total_size = size + saved_regs_size
  total_size = size + saved_regs_size
    + crtl->outgoing_args_size
    + crtl->outgoing_args_size
    + crtl->args.pretend_args_size;
    + crtl->args.pretend_args_size;
 
 
  if (!current_function_is_leaf
  if (!current_function_is_leaf
      || cfun->calls_alloca || total_size > 0)
      || cfun->calls_alloca || total_size > 0)
    total_size += STACK_POINTER_OFFSET;
    total_size += STACK_POINTER_OFFSET;
 
 
  if (total_size > 0)
  if (total_size > 0)
    {
    {
      if (cfun->calls_alloca)
      if (cfun->calls_alloca)
        frame_emit_load (STACK_POINTER_REGNUM, sp_reg, 0);
        frame_emit_load (STACK_POINTER_REGNUM, sp_reg, 0);
      else
      else
        frame_emit_add_imm (sp_reg, sp_reg, total_size, scratch_reg_0);
        frame_emit_add_imm (sp_reg, sp_reg, total_size, scratch_reg_0);
 
 
 
 
      if (saved_regs_size > 0)
      if (saved_regs_size > 0)
        {
        {
          offset = -crtl->args.pretend_args_size;
          offset = -crtl->args.pretend_args_size;
          for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
          for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
            if (need_to_save_reg (regno, 1))
            if (need_to_save_reg (regno, 1))
              {
              {
                offset -= 0x10;
                offset -= 0x10;
                frame_emit_load (regno, sp_reg, offset);
                frame_emit_load (regno, sp_reg, offset);
              }
              }
        }
        }
    }
    }
 
 
  if (!current_function_is_leaf)
  if (!current_function_is_leaf)
    frame_emit_load (LINK_REGISTER_REGNUM, sp_reg, 16);
    frame_emit_load (LINK_REGISTER_REGNUM, sp_reg, 16);
 
 
  if (!sibcall_p)
  if (!sibcall_p)
    {
    {
      emit_use (gen_rtx_REG (SImode, LINK_REGISTER_REGNUM));
      emit_use (gen_rtx_REG (SImode, LINK_REGISTER_REGNUM));
      jump = emit_jump_insn (gen__return ());
      jump = emit_jump_insn (gen__return ());
      emit_barrier_after (jump);
      emit_barrier_after (jump);
    }
    }
 
 
}
}
 
 
rtx
rtx
spu_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
spu_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
{
{
  if (count != 0)
  if (count != 0)
    return 0;
    return 0;
  /* This is inefficient because it ends up copying to a save-register
  /* This is inefficient because it ends up copying to a save-register
     which then gets saved even though $lr has already been saved.  But
     which then gets saved even though $lr has already been saved.  But
     it does generate better code for leaf functions and we don't need
     it does generate better code for leaf functions and we don't need
     to use RETURN_ADDRESS_POINTER_REGNUM to get it working.  It's only
     to use RETURN_ADDRESS_POINTER_REGNUM to get it working.  It's only
     used for __builtin_return_address anyway, so maybe we don't care if
     used for __builtin_return_address anyway, so maybe we don't care if
     it's inefficient. */
     it's inefficient. */
  return get_hard_reg_initial_val (Pmode, LINK_REGISTER_REGNUM);
  return get_hard_reg_initial_val (Pmode, LINK_REGISTER_REGNUM);
}
}


 
 
/* Given VAL, generate a constant appropriate for MODE.
/* Given VAL, generate a constant appropriate for MODE.
   If MODE is a vector mode, every element will be VAL.
   If MODE is a vector mode, every element will be VAL.
   For TImode, VAL will be zero extended to 128 bits. */
   For TImode, VAL will be zero extended to 128 bits. */
rtx
rtx
spu_const (enum machine_mode mode, HOST_WIDE_INT val)
spu_const (enum machine_mode mode, HOST_WIDE_INT val)
{
{
  rtx inner;
  rtx inner;
  rtvec v;
  rtvec v;
  int units, i;
  int units, i;
 
 
  gcc_assert (GET_MODE_CLASS (mode) == MODE_INT
  gcc_assert (GET_MODE_CLASS (mode) == MODE_INT
              || GET_MODE_CLASS (mode) == MODE_FLOAT
              || GET_MODE_CLASS (mode) == MODE_FLOAT
              || GET_MODE_CLASS (mode) == MODE_VECTOR_INT
              || GET_MODE_CLASS (mode) == MODE_VECTOR_INT
              || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT);
              || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT);
 
 
  if (GET_MODE_CLASS (mode) == MODE_INT)
  if (GET_MODE_CLASS (mode) == MODE_INT)
    return immed_double_const (val, 0, mode);
    return immed_double_const (val, 0, mode);
 
 
  /* val is the bit representation of the float */
  /* val is the bit representation of the float */
  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
    return hwint_to_const_double (mode, val);
    return hwint_to_const_double (mode, val);
 
 
  if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
  if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
    inner = immed_double_const (val, 0, GET_MODE_INNER (mode));
    inner = immed_double_const (val, 0, GET_MODE_INNER (mode));
  else
  else
    inner = hwint_to_const_double (GET_MODE_INNER (mode), val);
    inner = hwint_to_const_double (GET_MODE_INNER (mode), val);
 
 
  units = GET_MODE_NUNITS (mode);
  units = GET_MODE_NUNITS (mode);
 
 
  v = rtvec_alloc (units);
  v = rtvec_alloc (units);
 
 
  for (i = 0; i < units; ++i)
  for (i = 0; i < units; ++i)
    RTVEC_ELT (v, i) = inner;
    RTVEC_ELT (v, i) = inner;
 
 
  return gen_rtx_CONST_VECTOR (mode, v);
  return gen_rtx_CONST_VECTOR (mode, v);
}
}
 
 
/* Create a MODE vector constant from 4 ints. */
/* Create a MODE vector constant from 4 ints. */
rtx
rtx
spu_const_from_ints(enum machine_mode mode, int a, int b, int c, int d)
spu_const_from_ints(enum machine_mode mode, int a, int b, int c, int d)
{
{
  unsigned char arr[16];
  unsigned char arr[16];
  arr[0] = (a >> 24) & 0xff;
  arr[0] = (a >> 24) & 0xff;
  arr[1] = (a >> 16) & 0xff;
  arr[1] = (a >> 16) & 0xff;
  arr[2] = (a >> 8) & 0xff;
  arr[2] = (a >> 8) & 0xff;
  arr[3] = (a >> 0) & 0xff;
  arr[3] = (a >> 0) & 0xff;
  arr[4] = (b >> 24) & 0xff;
  arr[4] = (b >> 24) & 0xff;
  arr[5] = (b >> 16) & 0xff;
  arr[5] = (b >> 16) & 0xff;
  arr[6] = (b >> 8) & 0xff;
  arr[6] = (b >> 8) & 0xff;
  arr[7] = (b >> 0) & 0xff;
  arr[7] = (b >> 0) & 0xff;
  arr[8] = (c >> 24) & 0xff;
  arr[8] = (c >> 24) & 0xff;
  arr[9] = (c >> 16) & 0xff;
  arr[9] = (c >> 16) & 0xff;
  arr[10] = (c >> 8) & 0xff;
  arr[10] = (c >> 8) & 0xff;
  arr[11] = (c >> 0) & 0xff;
  arr[11] = (c >> 0) & 0xff;
  arr[12] = (d >> 24) & 0xff;
  arr[12] = (d >> 24) & 0xff;
  arr[13] = (d >> 16) & 0xff;
  arr[13] = (d >> 16) & 0xff;
  arr[14] = (d >> 8) & 0xff;
  arr[14] = (d >> 8) & 0xff;
  arr[15] = (d >> 0) & 0xff;
  arr[15] = (d >> 0) & 0xff;
  return array_to_constant(mode, arr);
  return array_to_constant(mode, arr);
}
}


/* branch hint stuff */
/* branch hint stuff */
 
 
/* An array of these is used to propagate hints to predecessor blocks. */
/* An array of these is used to propagate hints to predecessor blocks. */
struct spu_bb_info
struct spu_bb_info
{
{
  rtx prop_jump; /* propagated from another block */
  rtx prop_jump; /* propagated from another block */
  int bb_index;  /* the original block. */
  int bb_index;  /* the original block. */
};
};
static struct spu_bb_info *spu_bb_info;
static struct spu_bb_info *spu_bb_info;
 
 
#define STOP_HINT_P(INSN) \
#define STOP_HINT_P(INSN) \
                (GET_CODE(INSN) == CALL_INSN \
                (GET_CODE(INSN) == CALL_INSN \
                 || INSN_CODE(INSN) == CODE_FOR_divmodsi4 \
                 || INSN_CODE(INSN) == CODE_FOR_divmodsi4 \
                 || INSN_CODE(INSN) == CODE_FOR_udivmodsi4)
                 || INSN_CODE(INSN) == CODE_FOR_udivmodsi4)
 
 
/* 1 when RTX is a hinted branch or its target.  We keep track of
/* 1 when RTX is a hinted branch or its target.  We keep track of
   what has been hinted so the safe-hint code can test it easily.  */
   what has been hinted so the safe-hint code can test it easily.  */
#define HINTED_P(RTX)                                           \
#define HINTED_P(RTX)                                           \
  (RTL_FLAG_CHECK3("HINTED_P", (RTX), CODE_LABEL, JUMP_INSN, CALL_INSN)->unchanging)
  (RTL_FLAG_CHECK3("HINTED_P", (RTX), CODE_LABEL, JUMP_INSN, CALL_INSN)->unchanging)
 
 
/* 1 when RTX is an insn that must be scheduled on an even boundary. */
/* 1 when RTX is an insn that must be scheduled on an even boundary. */
#define SCHED_ON_EVEN_P(RTX)                                            \
#define SCHED_ON_EVEN_P(RTX)                                            \
  (RTL_FLAG_CHECK2("SCHED_ON_EVEN_P", (RTX), JUMP_INSN, CALL_INSN)->in_struct)
  (RTL_FLAG_CHECK2("SCHED_ON_EVEN_P", (RTX), JUMP_INSN, CALL_INSN)->in_struct)
 
 
/* Emit a nop for INSN such that the two will dual issue.  This assumes
/* Emit a nop for INSN such that the two will dual issue.  This assumes
   INSN is 8-byte aligned.  When INSN is inline asm we emit an lnop.
   INSN is 8-byte aligned.  When INSN is inline asm we emit an lnop.
   We check for TImode to handle a MULTI1 insn which has dual issued its
   We check for TImode to handle a MULTI1 insn which has dual issued its
   first instruction.  get_pipe returns -1 for MULTI0, inline asm, or
   first instruction.  get_pipe returns -1 for MULTI0, inline asm, or
   ADDR_VEC insns. */
   ADDR_VEC insns. */
static void
static void
emit_nop_for_insn (rtx insn)
emit_nop_for_insn (rtx insn)
{
{
  int p;
  int p;
  rtx new_insn;
  rtx new_insn;
  p = get_pipe (insn);
  p = get_pipe (insn);
  if ((CALL_P (insn) || JUMP_P (insn)) && SCHED_ON_EVEN_P (insn))
  if ((CALL_P (insn) || JUMP_P (insn)) && SCHED_ON_EVEN_P (insn))
    new_insn = emit_insn_after (gen_lnop (), insn);
    new_insn = emit_insn_after (gen_lnop (), insn);
  else if (p == 1 && GET_MODE (insn) == TImode)
  else if (p == 1 && GET_MODE (insn) == TImode)
    {
    {
      new_insn = emit_insn_before (gen_nopn (GEN_INT (127)), insn);
      new_insn = emit_insn_before (gen_nopn (GEN_INT (127)), insn);
      PUT_MODE (new_insn, TImode);
      PUT_MODE (new_insn, TImode);
      PUT_MODE (insn, VOIDmode);
      PUT_MODE (insn, VOIDmode);
    }
    }
  else
  else
    new_insn = emit_insn_after (gen_lnop (), insn);
    new_insn = emit_insn_after (gen_lnop (), insn);
  recog_memoized (new_insn);
  recog_memoized (new_insn);
}
}
 
 
/* Insert nops in basic blocks to meet dual issue alignment
/* Insert nops in basic blocks to meet dual issue alignment
   requirements.  Also make sure hbrp and hint instructions are at least
   requirements.  Also make sure hbrp and hint instructions are at least
   one cycle apart, possibly inserting a nop.  */
   one cycle apart, possibly inserting a nop.  */
static void
static void
pad_bb(void)
pad_bb(void)
{
{
  rtx insn, next_insn, prev_insn, hbr_insn = 0;
  rtx insn, next_insn, prev_insn, hbr_insn = 0;
  int length;
  int length;
  int addr;
  int addr;
 
 
  /* This sets up INSN_ADDRESSES. */
  /* This sets up INSN_ADDRESSES. */
  shorten_branches (get_insns ());
  shorten_branches (get_insns ());
 
 
  /* Keep track of length added by nops. */
  /* Keep track of length added by nops. */
  length = 0;
  length = 0;
 
 
  prev_insn = 0;
  prev_insn = 0;
  insn = get_insns ();
  insn = get_insns ();
  if (!active_insn_p (insn))
  if (!active_insn_p (insn))
    insn = next_active_insn (insn);
    insn = next_active_insn (insn);
  for (; insn; insn = next_insn)
  for (; insn; insn = next_insn)
    {
    {
      next_insn = next_active_insn (insn);
      next_insn = next_active_insn (insn);
      if (INSN_CODE (insn) == CODE_FOR_iprefetch
      if (INSN_CODE (insn) == CODE_FOR_iprefetch
          || INSN_CODE (insn) == CODE_FOR_hbr)
          || INSN_CODE (insn) == CODE_FOR_hbr)
        {
        {
          if (hbr_insn)
          if (hbr_insn)
            {
            {
              int a0 = INSN_ADDRESSES (INSN_UID (hbr_insn));
              int a0 = INSN_ADDRESSES (INSN_UID (hbr_insn));
              int a1 = INSN_ADDRESSES (INSN_UID (insn));
              int a1 = INSN_ADDRESSES (INSN_UID (insn));
              if ((a1 - a0 == 8 && GET_MODE (insn) != TImode)
              if ((a1 - a0 == 8 && GET_MODE (insn) != TImode)
                  || (a1 - a0 == 4))
                  || (a1 - a0 == 4))
                {
                {
                  prev_insn = emit_insn_before (gen_lnop (), insn);
                  prev_insn = emit_insn_before (gen_lnop (), insn);
                  PUT_MODE (prev_insn, GET_MODE (insn));
                  PUT_MODE (prev_insn, GET_MODE (insn));
                  PUT_MODE (insn, TImode);
                  PUT_MODE (insn, TImode);
                  length += 4;
                  length += 4;
                }
                }
            }
            }
          hbr_insn = insn;
          hbr_insn = insn;
        }
        }
      if (INSN_CODE (insn) == CODE_FOR_blockage)
      if (INSN_CODE (insn) == CODE_FOR_blockage)
        {
        {
          if (GET_MODE (insn) == TImode)
          if (GET_MODE (insn) == TImode)
            PUT_MODE (next_insn, TImode);
            PUT_MODE (next_insn, TImode);
          insn = next_insn;
          insn = next_insn;
          next_insn = next_active_insn (insn);
          next_insn = next_active_insn (insn);
        }
        }
      addr = INSN_ADDRESSES (INSN_UID (insn));
      addr = INSN_ADDRESSES (INSN_UID (insn));
      if ((CALL_P (insn) || JUMP_P (insn)) && SCHED_ON_EVEN_P (insn))
      if ((CALL_P (insn) || JUMP_P (insn)) && SCHED_ON_EVEN_P (insn))
        {
        {
          if (((addr + length) & 7) != 0)
          if (((addr + length) & 7) != 0)
            {
            {
              emit_nop_for_insn (prev_insn);
              emit_nop_for_insn (prev_insn);
              length += 4;
              length += 4;
            }
            }
        }
        }
      else if (GET_MODE (insn) == TImode
      else if (GET_MODE (insn) == TImode
               && ((next_insn && GET_MODE (next_insn) != TImode)
               && ((next_insn && GET_MODE (next_insn) != TImode)
                   || get_attr_type (insn) == TYPE_MULTI0)
                   || get_attr_type (insn) == TYPE_MULTI0)
               && ((addr + length) & 7) != 0)
               && ((addr + length) & 7) != 0)
        {
        {
          /* prev_insn will always be set because the first insn is
          /* prev_insn will always be set because the first insn is
             always 8-byte aligned. */
             always 8-byte aligned. */
          emit_nop_for_insn (prev_insn);
          emit_nop_for_insn (prev_insn);
          length += 4;
          length += 4;
        }
        }
      prev_insn = insn;
      prev_insn = insn;
    }
    }
}
}
 
 


/* Routines for branch hints. */
/* Routines for branch hints. */
 
 
static void
static void
spu_emit_branch_hint (rtx before, rtx branch, rtx target,
spu_emit_branch_hint (rtx before, rtx branch, rtx target,
                      int distance, sbitmap blocks)
                      int distance, sbitmap blocks)
{
{
  rtx branch_label = 0;
  rtx branch_label = 0;
  rtx hint;
  rtx hint;
  rtx insn;
  rtx insn;
  rtx table;
  rtx table;
 
 
  if (before == 0 || branch == 0 || target == 0)
  if (before == 0 || branch == 0 || target == 0)
    return;
    return;
 
 
  /* While scheduling we require hints to be no further than 600, so
  /* While scheduling we require hints to be no further than 600, so
     we need to enforce that here too */
     we need to enforce that here too */
  if (distance > 600)
  if (distance > 600)
    return;
    return;
 
 
  /* If we have a Basic block note, emit it after the basic block note.  */
  /* If we have a Basic block note, emit it after the basic block note.  */
  if (NOTE_INSN_BASIC_BLOCK_P (before))
  if (NOTE_INSN_BASIC_BLOCK_P (before))
    before = NEXT_INSN (before);
    before = NEXT_INSN (before);
 
 
  branch_label = gen_label_rtx ();
  branch_label = gen_label_rtx ();
  LABEL_NUSES (branch_label)++;
  LABEL_NUSES (branch_label)++;
  LABEL_PRESERVE_P (branch_label) = 1;
  LABEL_PRESERVE_P (branch_label) = 1;
  insn = emit_label_before (branch_label, branch);
  insn = emit_label_before (branch_label, branch);
  branch_label = gen_rtx_LABEL_REF (VOIDmode, branch_label);
  branch_label = gen_rtx_LABEL_REF (VOIDmode, branch_label);
  SET_BIT (blocks, BLOCK_FOR_INSN (branch)->index);
  SET_BIT (blocks, BLOCK_FOR_INSN (branch)->index);
 
 
  hint = emit_insn_before (gen_hbr (branch_label, target), before);
  hint = emit_insn_before (gen_hbr (branch_label, target), before);
  recog_memoized (hint);
  recog_memoized (hint);
  HINTED_P (branch) = 1;
  HINTED_P (branch) = 1;
 
 
  if (GET_CODE (target) == LABEL_REF)
  if (GET_CODE (target) == LABEL_REF)
    HINTED_P (XEXP (target, 0)) = 1;
    HINTED_P (XEXP (target, 0)) = 1;
  else if (tablejump_p (branch, 0, &table))
  else if (tablejump_p (branch, 0, &table))
    {
    {
      rtvec vec;
      rtvec vec;
      int j;
      int j;
      if (GET_CODE (PATTERN (table)) == ADDR_VEC)
      if (GET_CODE (PATTERN (table)) == ADDR_VEC)
        vec = XVEC (PATTERN (table), 0);
        vec = XVEC (PATTERN (table), 0);
      else
      else
        vec = XVEC (PATTERN (table), 1);
        vec = XVEC (PATTERN (table), 1);
      for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j)
      for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j)
        HINTED_P (XEXP (RTVEC_ELT (vec, j), 0)) = 1;
        HINTED_P (XEXP (RTVEC_ELT (vec, j), 0)) = 1;
    }
    }
 
 
  if (distance >= 588)
  if (distance >= 588)
    {
    {
      /* Make sure the hint isn't scheduled any earlier than this point,
      /* Make sure the hint isn't scheduled any earlier than this point,
         which could make it too far for the branch offest to fit */
         which could make it too far for the branch offest to fit */
      recog_memoized (emit_insn_before (gen_blockage (), hint));
      recog_memoized (emit_insn_before (gen_blockage (), hint));
    }
    }
  else if (distance <= 8 * 4)
  else if (distance <= 8 * 4)
    {
    {
      /* To guarantee at least 8 insns between the hint and branch we
      /* To guarantee at least 8 insns between the hint and branch we
         insert nops. */
         insert nops. */
      int d;
      int d;
      for (d = distance; d < 8 * 4; d += 4)
      for (d = distance; d < 8 * 4; d += 4)
        {
        {
          insn =
          insn =
            emit_insn_after (gen_nopn_nv (gen_rtx_REG (SImode, 127)), hint);
            emit_insn_after (gen_nopn_nv (gen_rtx_REG (SImode, 127)), hint);
          recog_memoized (insn);
          recog_memoized (insn);
        }
        }
 
 
      /* Make sure any nops inserted aren't scheduled before the hint. */
      /* Make sure any nops inserted aren't scheduled before the hint. */
      recog_memoized (emit_insn_after (gen_blockage (), hint));
      recog_memoized (emit_insn_after (gen_blockage (), hint));
 
 
      /* Make sure any nops inserted aren't scheduled after the call. */
      /* Make sure any nops inserted aren't scheduled after the call. */
      if (CALL_P (branch) && distance < 8 * 4)
      if (CALL_P (branch) && distance < 8 * 4)
        recog_memoized (emit_insn_before (gen_blockage (), branch));
        recog_memoized (emit_insn_before (gen_blockage (), branch));
    }
    }
}
}
 
 
/* Returns 0 if we don't want a hint for this branch.  Otherwise return
/* Returns 0 if we don't want a hint for this branch.  Otherwise return
   the rtx for the branch target. */
   the rtx for the branch target. */
static rtx
static rtx
get_branch_target (rtx branch)
get_branch_target (rtx branch)
{
{
  if (GET_CODE (branch) == JUMP_INSN)
  if (GET_CODE (branch) == JUMP_INSN)
    {
    {
      rtx set, src;
      rtx set, src;
 
 
      /* Return statements */
      /* Return statements */
      if (GET_CODE (PATTERN (branch)) == RETURN)
      if (GET_CODE (PATTERN (branch)) == RETURN)
        return gen_rtx_REG (SImode, LINK_REGISTER_REGNUM);
        return gen_rtx_REG (SImode, LINK_REGISTER_REGNUM);
 
 
      /* jump table */
      /* jump table */
      if (GET_CODE (PATTERN (branch)) == ADDR_VEC
      if (GET_CODE (PATTERN (branch)) == ADDR_VEC
          || GET_CODE (PATTERN (branch)) == ADDR_DIFF_VEC)
          || GET_CODE (PATTERN (branch)) == ADDR_DIFF_VEC)
        return 0;
        return 0;
 
 
     /* ASM GOTOs. */
     /* ASM GOTOs. */
     if (extract_asm_operands (PATTERN (branch)) != NULL)
     if (extract_asm_operands (PATTERN (branch)) != NULL)
        return NULL;
        return NULL;
 
 
      set = single_set (branch);
      set = single_set (branch);
      src = SET_SRC (set);
      src = SET_SRC (set);
      if (GET_CODE (SET_DEST (set)) != PC)
      if (GET_CODE (SET_DEST (set)) != PC)
        abort ();
        abort ();
 
 
      if (GET_CODE (src) == IF_THEN_ELSE)
      if (GET_CODE (src) == IF_THEN_ELSE)
        {
        {
          rtx lab = 0;
          rtx lab = 0;
          rtx note = find_reg_note (branch, REG_BR_PROB, 0);
          rtx note = find_reg_note (branch, REG_BR_PROB, 0);
          if (note)
          if (note)
            {
            {
              /* If the more probable case is not a fall through, then
              /* If the more probable case is not a fall through, then
                 try a branch hint.  */
                 try a branch hint.  */
              HOST_WIDE_INT prob = INTVAL (XEXP (note, 0));
              HOST_WIDE_INT prob = INTVAL (XEXP (note, 0));
              if (prob > (REG_BR_PROB_BASE * 6 / 10)
              if (prob > (REG_BR_PROB_BASE * 6 / 10)
                  && GET_CODE (XEXP (src, 1)) != PC)
                  && GET_CODE (XEXP (src, 1)) != PC)
                lab = XEXP (src, 1);
                lab = XEXP (src, 1);
              else if (prob < (REG_BR_PROB_BASE * 4 / 10)
              else if (prob < (REG_BR_PROB_BASE * 4 / 10)
                       && GET_CODE (XEXP (src, 2)) != PC)
                       && GET_CODE (XEXP (src, 2)) != PC)
                lab = XEXP (src, 2);
                lab = XEXP (src, 2);
            }
            }
          if (lab)
          if (lab)
            {
            {
              if (GET_CODE (lab) == RETURN)
              if (GET_CODE (lab) == RETURN)
                return gen_rtx_REG (SImode, LINK_REGISTER_REGNUM);
                return gen_rtx_REG (SImode, LINK_REGISTER_REGNUM);
              return lab;
              return lab;
            }
            }
          return 0;
          return 0;
        }
        }
 
 
      return src;
      return src;
    }
    }
  else if (GET_CODE (branch) == CALL_INSN)
  else if (GET_CODE (branch) == CALL_INSN)
    {
    {
      rtx call;
      rtx call;
      /* All of our call patterns are in a PARALLEL and the CALL is
      /* All of our call patterns are in a PARALLEL and the CALL is
         the first pattern in the PARALLEL. */
         the first pattern in the PARALLEL. */
      if (GET_CODE (PATTERN (branch)) != PARALLEL)
      if (GET_CODE (PATTERN (branch)) != PARALLEL)
        abort ();
        abort ();
      call = XVECEXP (PATTERN (branch), 0, 0);
      call = XVECEXP (PATTERN (branch), 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)
      if (GET_CODE (call) != CALL)
        abort ();
        abort ();
      return XEXP (XEXP (call, 0), 0);
      return XEXP (XEXP (call, 0), 0);
    }
    }
  return 0;
  return 0;
}
}
 
 
/* The special $hbr register is used to prevent the insn scheduler from
/* The special $hbr register is used to prevent the insn scheduler from
   moving hbr insns across instructions which invalidate them.  It
   moving hbr insns across instructions which invalidate them.  It
   should only be used in a clobber, and this function searches for
   should only be used in a clobber, and this function searches for
   insns which clobber it.  */
   insns which clobber it.  */
static bool
static bool
insn_clobbers_hbr (rtx insn)
insn_clobbers_hbr (rtx insn)
{
{
  if (INSN_P (insn)
  if (INSN_P (insn)
      && GET_CODE (PATTERN (insn)) == PARALLEL)
      && GET_CODE (PATTERN (insn)) == PARALLEL)
    {
    {
      rtx parallel = PATTERN (insn);
      rtx parallel = PATTERN (insn);
      rtx clobber;
      rtx clobber;
      int j;
      int j;
      for (j = XVECLEN (parallel, 0) - 1; j >= 0; j--)
      for (j = XVECLEN (parallel, 0) - 1; j >= 0; j--)
        {
        {
          clobber = XVECEXP (parallel, 0, j);
          clobber = XVECEXP (parallel, 0, j);
          if (GET_CODE (clobber) == CLOBBER
          if (GET_CODE (clobber) == CLOBBER
              && GET_CODE (XEXP (clobber, 0)) == REG
              && GET_CODE (XEXP (clobber, 0)) == REG
              && REGNO (XEXP (clobber, 0)) == HBR_REGNUM)
              && REGNO (XEXP (clobber, 0)) == HBR_REGNUM)
            return 1;
            return 1;
        }
        }
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Search up to 32 insns starting at FIRST:
/* Search up to 32 insns starting at FIRST:
   - at any kind of hinted branch, just return
   - at any kind of hinted branch, just return
   - at any unconditional branch in the first 15 insns, just return
   - at any unconditional branch in the first 15 insns, just return
   - at a call or indirect branch, after the first 15 insns, force it to
   - at a call or indirect branch, after the first 15 insns, force it to
     an even address and return
     an even address and return
   - at any unconditional branch, after the first 15 insns, force it to
   - at any unconditional branch, after the first 15 insns, force it to
     an even address.
     an even address.
   At then end of the search, insert an hbrp within 4 insns of FIRST,
   At then end of the search, insert an hbrp within 4 insns of FIRST,
   and an hbrp within 16 instructions of FIRST.
   and an hbrp within 16 instructions of FIRST.
 */
 */
static void
static void
insert_hbrp_for_ilb_runout (rtx first)
insert_hbrp_for_ilb_runout (rtx first)
{
{
  rtx insn, before_4 = 0, before_16 = 0;
  rtx insn, before_4 = 0, before_16 = 0;
  int addr = 0, length, first_addr = -1;
  int addr = 0, length, first_addr = -1;
  int hbrp_addr0 = 128 * 4, hbrp_addr1 = 128 * 4;
  int hbrp_addr0 = 128 * 4, hbrp_addr1 = 128 * 4;
  int insert_lnop_after = 0;
  int insert_lnop_after = 0;
  for (insn = first; insn; insn = NEXT_INSN (insn))
  for (insn = first; insn; insn = NEXT_INSN (insn))
    if (INSN_P (insn))
    if (INSN_P (insn))
      {
      {
        if (first_addr == -1)
        if (first_addr == -1)
          first_addr = INSN_ADDRESSES (INSN_UID (insn));
          first_addr = INSN_ADDRESSES (INSN_UID (insn));
        addr = INSN_ADDRESSES (INSN_UID (insn)) - first_addr;
        addr = INSN_ADDRESSES (INSN_UID (insn)) - first_addr;
        length = get_attr_length (insn);
        length = get_attr_length (insn);
 
 
        if (before_4 == 0 && addr + length >= 4 * 4)
        if (before_4 == 0 && addr + length >= 4 * 4)
          before_4 = insn;
          before_4 = insn;
        /* We test for 14 instructions because the first hbrp will add
        /* We test for 14 instructions because the first hbrp will add
           up to 2 instructions. */
           up to 2 instructions. */
        if (before_16 == 0 && addr + length >= 14 * 4)
        if (before_16 == 0 && addr + length >= 14 * 4)
          before_16 = insn;
          before_16 = insn;
 
 
        if (INSN_CODE (insn) == CODE_FOR_hbr)
        if (INSN_CODE (insn) == CODE_FOR_hbr)
          {
          {
            /* Make sure an hbrp is at least 2 cycles away from a hint.
            /* Make sure an hbrp is at least 2 cycles away from a hint.
               Insert an lnop after the hbrp when necessary. */
               Insert an lnop after the hbrp when necessary. */
            if (before_4 == 0 && addr > 0)
            if (before_4 == 0 && addr > 0)
              {
              {
                before_4 = insn;
                before_4 = insn;
                insert_lnop_after |= 1;
                insert_lnop_after |= 1;
              }
              }
            else if (before_4 && addr <= 4 * 4)
            else if (before_4 && addr <= 4 * 4)
              insert_lnop_after |= 1;
              insert_lnop_after |= 1;
            if (before_16 == 0 && addr > 10 * 4)
            if (before_16 == 0 && addr > 10 * 4)
              {
              {
                before_16 = insn;
                before_16 = insn;
                insert_lnop_after |= 2;
                insert_lnop_after |= 2;
              }
              }
            else if (before_16 && addr <= 14 * 4)
            else if (before_16 && addr <= 14 * 4)
              insert_lnop_after |= 2;
              insert_lnop_after |= 2;
          }
          }
 
 
        if (INSN_CODE (insn) == CODE_FOR_iprefetch)
        if (INSN_CODE (insn) == CODE_FOR_iprefetch)
          {
          {
            if (addr < hbrp_addr0)
            if (addr < hbrp_addr0)
              hbrp_addr0 = addr;
              hbrp_addr0 = addr;
            else if (addr < hbrp_addr1)
            else if (addr < hbrp_addr1)
              hbrp_addr1 = addr;
              hbrp_addr1 = addr;
          }
          }
 
 
        if (CALL_P (insn) || JUMP_P (insn))
        if (CALL_P (insn) || JUMP_P (insn))
          {
          {
            if (HINTED_P (insn))
            if (HINTED_P (insn))
              return;
              return;
 
 
            /* Any branch after the first 15 insns should be on an even
            /* Any branch after the first 15 insns should be on an even
               address to avoid a special case branch.  There might be
               address to avoid a special case branch.  There might be
               some nops and/or hbrps inserted, so we test after 10
               some nops and/or hbrps inserted, so we test after 10
               insns. */
               insns. */
            if (addr > 10 * 4)
            if (addr > 10 * 4)
              SCHED_ON_EVEN_P (insn) = 1;
              SCHED_ON_EVEN_P (insn) = 1;
          }
          }
 
 
        if (CALL_P (insn) || tablejump_p (insn, 0, 0))
        if (CALL_P (insn) || tablejump_p (insn, 0, 0))
          return;
          return;
 
 
 
 
        if (addr + length >= 32 * 4)
        if (addr + length >= 32 * 4)
          {
          {
            gcc_assert (before_4 && before_16);
            gcc_assert (before_4 && before_16);
            if (hbrp_addr0 > 4 * 4)
            if (hbrp_addr0 > 4 * 4)
              {
              {
                insn =
                insn =
                  emit_insn_before (gen_iprefetch (GEN_INT (1)), before_4);
                  emit_insn_before (gen_iprefetch (GEN_INT (1)), before_4);
                recog_memoized (insn);
                recog_memoized (insn);
                INSN_ADDRESSES_NEW (insn,
                INSN_ADDRESSES_NEW (insn,
                                    INSN_ADDRESSES (INSN_UID (before_4)));
                                    INSN_ADDRESSES (INSN_UID (before_4)));
                PUT_MODE (insn, GET_MODE (before_4));
                PUT_MODE (insn, GET_MODE (before_4));
                PUT_MODE (before_4, TImode);
                PUT_MODE (before_4, TImode);
                if (insert_lnop_after & 1)
                if (insert_lnop_after & 1)
                  {
                  {
                    insn = emit_insn_before (gen_lnop (), before_4);
                    insn = emit_insn_before (gen_lnop (), before_4);
                    recog_memoized (insn);
                    recog_memoized (insn);
                    INSN_ADDRESSES_NEW (insn,
                    INSN_ADDRESSES_NEW (insn,
                                        INSN_ADDRESSES (INSN_UID (before_4)));
                                        INSN_ADDRESSES (INSN_UID (before_4)));
                    PUT_MODE (insn, TImode);
                    PUT_MODE (insn, TImode);
                  }
                  }
              }
              }
            if ((hbrp_addr0 <= 4 * 4 || hbrp_addr0 > 16 * 4)
            if ((hbrp_addr0 <= 4 * 4 || hbrp_addr0 > 16 * 4)
                && hbrp_addr1 > 16 * 4)
                && hbrp_addr1 > 16 * 4)
              {
              {
                insn =
                insn =
                  emit_insn_before (gen_iprefetch (GEN_INT (2)), before_16);
                  emit_insn_before (gen_iprefetch (GEN_INT (2)), before_16);
                recog_memoized (insn);
                recog_memoized (insn);
                INSN_ADDRESSES_NEW (insn,
                INSN_ADDRESSES_NEW (insn,
                                    INSN_ADDRESSES (INSN_UID (before_16)));
                                    INSN_ADDRESSES (INSN_UID (before_16)));
                PUT_MODE (insn, GET_MODE (before_16));
                PUT_MODE (insn, GET_MODE (before_16));
                PUT_MODE (before_16, TImode);
                PUT_MODE (before_16, TImode);
                if (insert_lnop_after & 2)
                if (insert_lnop_after & 2)
                  {
                  {
                    insn = emit_insn_before (gen_lnop (), before_16);
                    insn = emit_insn_before (gen_lnop (), before_16);
                    recog_memoized (insn);
                    recog_memoized (insn);
                    INSN_ADDRESSES_NEW (insn,
                    INSN_ADDRESSES_NEW (insn,
                                        INSN_ADDRESSES (INSN_UID
                                        INSN_ADDRESSES (INSN_UID
                                                        (before_16)));
                                                        (before_16)));
                    PUT_MODE (insn, TImode);
                    PUT_MODE (insn, TImode);
                  }
                  }
              }
              }
            return;
            return;
          }
          }
      }
      }
    else if (BARRIER_P (insn))
    else if (BARRIER_P (insn))
      return;
      return;
 
 
}
}
 
 
/* The SPU might hang when it executes 48 inline instructions after a
/* The SPU might hang when it executes 48 inline instructions after a
   hinted branch jumps to its hinted target.  The beginning of a
   hinted branch jumps to its hinted target.  The beginning of a
   function and the return from a call might have been hinted, and must
   function and the return from a call might have been hinted, and must
   be handled as well.  To prevent a hang we insert 2 hbrps.  The first
   be handled as well.  To prevent a hang we insert 2 hbrps.  The first
   should be within 6 insns of the branch target.  The second should be
   should be within 6 insns of the branch target.  The second should be
   within 22 insns of the branch target.  When determining if hbrps are
   within 22 insns of the branch target.  When determining if hbrps are
   necessary, we look for only 32 inline instructions, because up to to
   necessary, we look for only 32 inline instructions, because up to to
   12 nops and 4 hbrps could be inserted.  Similarily, when inserting
   12 nops and 4 hbrps could be inserted.  Similarily, when inserting
   new hbrps, we insert them within 4 and 16 insns of the target.  */
   new hbrps, we insert them within 4 and 16 insns of the target.  */
static void
static void
insert_hbrp (void)
insert_hbrp (void)
{
{
  rtx insn;
  rtx insn;
  if (TARGET_SAFE_HINTS)
  if (TARGET_SAFE_HINTS)
    {
    {
      shorten_branches (get_insns ());
      shorten_branches (get_insns ());
      /* Insert hbrp at beginning of function */
      /* Insert hbrp at beginning of function */
      insn = next_active_insn (get_insns ());
      insn = next_active_insn (get_insns ());
      if (insn)
      if (insn)
        insert_hbrp_for_ilb_runout (insn);
        insert_hbrp_for_ilb_runout (insn);
      /* Insert hbrp after hinted targets. */
      /* Insert hbrp after hinted targets. */
      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
        if ((LABEL_P (insn) && HINTED_P (insn)) || CALL_P (insn))
        if ((LABEL_P (insn) && HINTED_P (insn)) || CALL_P (insn))
          insert_hbrp_for_ilb_runout (next_active_insn (insn));
          insert_hbrp_for_ilb_runout (next_active_insn (insn));
    }
    }
}
}
 
 
static int in_spu_reorg;
static int in_spu_reorg;
 
 
/* Insert branch hints.  There are no branch optimizations after this
/* Insert branch hints.  There are no branch optimizations after this
   pass, so it's safe to set our branch hints now. */
   pass, so it's safe to set our branch hints now. */
static void
static void
spu_machine_dependent_reorg (void)
spu_machine_dependent_reorg (void)
{
{
  sbitmap blocks;
  sbitmap blocks;
  basic_block bb;
  basic_block bb;
  rtx branch, insn;
  rtx branch, insn;
  rtx branch_target = 0;
  rtx branch_target = 0;
  int branch_addr = 0, insn_addr, required_dist = 0;
  int branch_addr = 0, insn_addr, required_dist = 0;
  int i;
  int i;
  unsigned int j;
  unsigned int j;
 
 
  if (!TARGET_BRANCH_HINTS || optimize == 0)
  if (!TARGET_BRANCH_HINTS || optimize == 0)
    {
    {
      /* We still do it for unoptimized code because an external
      /* We still do it for unoptimized code because an external
         function might have hinted a call or return. */
         function might have hinted a call or return. */
      insert_hbrp ();
      insert_hbrp ();
      pad_bb ();
      pad_bb ();
      return;
      return;
    }
    }
 
 
  blocks = sbitmap_alloc (last_basic_block);
  blocks = sbitmap_alloc (last_basic_block);
  sbitmap_zero (blocks);
  sbitmap_zero (blocks);
 
 
  in_spu_reorg = 1;
  in_spu_reorg = 1;
  compute_bb_for_insn ();
  compute_bb_for_insn ();
 
 
  compact_blocks ();
  compact_blocks ();
 
 
  spu_bb_info =
  spu_bb_info =
    (struct spu_bb_info *) xcalloc (n_basic_blocks,
    (struct spu_bb_info *) xcalloc (n_basic_blocks,
                                    sizeof (struct spu_bb_info));
                                    sizeof (struct spu_bb_info));
 
 
  /* We need exact insn addresses and lengths.  */
  /* We need exact insn addresses and lengths.  */
  shorten_branches (get_insns ());
  shorten_branches (get_insns ());
 
 
  for (i = n_basic_blocks - 1; i >= 0; i--)
  for (i = n_basic_blocks - 1; i >= 0; i--)
    {
    {
      bb = BASIC_BLOCK (i);
      bb = BASIC_BLOCK (i);
      branch = 0;
      branch = 0;
      if (spu_bb_info[i].prop_jump)
      if (spu_bb_info[i].prop_jump)
        {
        {
          branch = spu_bb_info[i].prop_jump;
          branch = spu_bb_info[i].prop_jump;
          branch_target = get_branch_target (branch);
          branch_target = get_branch_target (branch);
          branch_addr = INSN_ADDRESSES (INSN_UID (branch));
          branch_addr = INSN_ADDRESSES (INSN_UID (branch));
          required_dist = spu_hint_dist;
          required_dist = spu_hint_dist;
        }
        }
      /* Search from end of a block to beginning.   In this loop, find
      /* Search from end of a block to beginning.   In this loop, find
         jumps which need a branch and emit them only when:
         jumps which need a branch and emit them only when:
         - it's an indirect branch and we're at the insn which sets
         - it's an indirect branch and we're at the insn which sets
         the register
         the register
         - we're at an insn that will invalidate the hint. e.g., a
         - we're at an insn that will invalidate the hint. e.g., a
         call, another hint insn, inline asm that clobbers $hbr, and
         call, another hint insn, inline asm that clobbers $hbr, and
         some inlined operations (divmodsi4).  Don't consider jumps
         some inlined operations (divmodsi4).  Don't consider jumps
         because they are only at the end of a block and are
         because they are only at the end of a block and are
         considered when we are deciding whether to propagate
         considered when we are deciding whether to propagate
         - we're getting too far away from the branch.  The hbr insns
         - we're getting too far away from the branch.  The hbr insns
         only have a signed 10 bit offset
         only have a signed 10 bit offset
         We go back as far as possible so the branch will be considered
         We go back as far as possible so the branch will be considered
         for propagation when we get to the beginning of the block.  */
         for propagation when we get to the beginning of the block.  */
      for (insn = BB_END (bb); insn; insn = PREV_INSN (insn))
      for (insn = BB_END (bb); insn; insn = PREV_INSN (insn))
        {
        {
          if (INSN_P (insn))
          if (INSN_P (insn))
            {
            {
              insn_addr = INSN_ADDRESSES (INSN_UID (insn));
              insn_addr = INSN_ADDRESSES (INSN_UID (insn));
              if (branch
              if (branch
                  && ((GET_CODE (branch_target) == REG
                  && ((GET_CODE (branch_target) == REG
                       && set_of (branch_target, insn) != NULL_RTX)
                       && set_of (branch_target, insn) != NULL_RTX)
                      || insn_clobbers_hbr (insn)
                      || insn_clobbers_hbr (insn)
                      || branch_addr - insn_addr > 600))
                      || branch_addr - insn_addr > 600))
                {
                {
                  rtx next = NEXT_INSN (insn);
                  rtx next = NEXT_INSN (insn);
                  int next_addr = INSN_ADDRESSES (INSN_UID (next));
                  int next_addr = INSN_ADDRESSES (INSN_UID (next));
                  if (insn != BB_END (bb)
                  if (insn != BB_END (bb)
                      && branch_addr - next_addr >= required_dist)
                      && branch_addr - next_addr >= required_dist)
                    {
                    {
                      if (dump_file)
                      if (dump_file)
                        fprintf (dump_file,
                        fprintf (dump_file,
                                 "hint for %i in block %i before %i\n",
                                 "hint for %i in block %i before %i\n",
                                 INSN_UID (branch), bb->index,
                                 INSN_UID (branch), bb->index,
                                 INSN_UID (next));
                                 INSN_UID (next));
                      spu_emit_branch_hint (next, branch, branch_target,
                      spu_emit_branch_hint (next, branch, branch_target,
                                            branch_addr - next_addr, blocks);
                                            branch_addr - next_addr, blocks);
                    }
                    }
                  branch = 0;
                  branch = 0;
                }
                }
 
 
              /* JUMP_P will only be true at the end of a block.  When
              /* JUMP_P will only be true at the end of a block.  When
                 branch is already set it means we've previously decided
                 branch is already set it means we've previously decided
                 to propagate a hint for that branch into this block. */
                 to propagate a hint for that branch into this block. */
              if (CALL_P (insn) || (JUMP_P (insn) && !branch))
              if (CALL_P (insn) || (JUMP_P (insn) && !branch))
                {
                {
                  branch = 0;
                  branch = 0;
                  if ((branch_target = get_branch_target (insn)))
                  if ((branch_target = get_branch_target (insn)))
                    {
                    {
                      branch = insn;
                      branch = insn;
                      branch_addr = insn_addr;
                      branch_addr = insn_addr;
                      required_dist = spu_hint_dist;
                      required_dist = spu_hint_dist;
                    }
                    }
                }
                }
            }
            }
          if (insn == BB_HEAD (bb))
          if (insn == BB_HEAD (bb))
            break;
            break;
        }
        }
 
 
      if (branch)
      if (branch)
        {
        {
          /* If we haven't emitted a hint for this branch yet, it might
          /* If we haven't emitted a hint for this branch yet, it might
             be profitable to emit it in one of the predecessor blocks,
             be profitable to emit it in one of the predecessor blocks,
             especially for loops.  */
             especially for loops.  */
          rtx bbend;
          rtx bbend;
          basic_block prev = 0, prop = 0, prev2 = 0;
          basic_block prev = 0, prop = 0, prev2 = 0;
          int loop_exit = 0, simple_loop = 0;
          int loop_exit = 0, simple_loop = 0;
          int next_addr = INSN_ADDRESSES (INSN_UID (NEXT_INSN (insn)));
          int next_addr = INSN_ADDRESSES (INSN_UID (NEXT_INSN (insn)));
 
 
          for (j = 0; j < EDGE_COUNT (bb->preds); j++)
          for (j = 0; j < EDGE_COUNT (bb->preds); j++)
            if (EDGE_PRED (bb, j)->flags & EDGE_FALLTHRU)
            if (EDGE_PRED (bb, j)->flags & EDGE_FALLTHRU)
              prev = EDGE_PRED (bb, j)->src;
              prev = EDGE_PRED (bb, j)->src;
            else
            else
              prev2 = EDGE_PRED (bb, j)->src;
              prev2 = EDGE_PRED (bb, j)->src;
 
 
          for (j = 0; j < EDGE_COUNT (bb->succs); j++)
          for (j = 0; j < EDGE_COUNT (bb->succs); j++)
            if (EDGE_SUCC (bb, j)->flags & EDGE_LOOP_EXIT)
            if (EDGE_SUCC (bb, j)->flags & EDGE_LOOP_EXIT)
              loop_exit = 1;
              loop_exit = 1;
            else if (EDGE_SUCC (bb, j)->dest == bb)
            else if (EDGE_SUCC (bb, j)->dest == bb)
              simple_loop = 1;
              simple_loop = 1;
 
 
          /* If this branch is a loop exit then propagate to previous
          /* If this branch is a loop exit then propagate to previous
             fallthru block. This catches the cases when it is a simple
             fallthru block. This catches the cases when it is a simple
             loop or when there is an initial branch into the loop. */
             loop or when there is an initial branch into the loop. */
          if (prev && (loop_exit || simple_loop)
          if (prev && (loop_exit || simple_loop)
              && prev->loop_depth <= bb->loop_depth)
              && prev->loop_depth <= bb->loop_depth)
            prop = prev;
            prop = prev;
 
 
          /* If there is only one adjacent predecessor.  Don't propagate
          /* If there is only one adjacent predecessor.  Don't propagate
             outside this loop.  This loop_depth test isn't perfect, but
             outside this loop.  This loop_depth test isn't perfect, but
             I'm not sure the loop_father member is valid at this point.  */
             I'm not sure the loop_father member is valid at this point.  */
          else if (prev && single_pred_p (bb)
          else if (prev && single_pred_p (bb)
                   && prev->loop_depth == bb->loop_depth)
                   && prev->loop_depth == bb->loop_depth)
            prop = prev;
            prop = prev;
 
 
          /* If this is the JOIN block of a simple IF-THEN then
          /* If this is the JOIN block of a simple IF-THEN then
             propogate the hint to the HEADER block. */
             propogate the hint to the HEADER block. */
          else if (prev && prev2
          else if (prev && prev2
                   && EDGE_COUNT (bb->preds) == 2
                   && EDGE_COUNT (bb->preds) == 2
                   && EDGE_COUNT (prev->preds) == 1
                   && EDGE_COUNT (prev->preds) == 1
                   && EDGE_PRED (prev, 0)->src == prev2
                   && EDGE_PRED (prev, 0)->src == prev2
                   && prev2->loop_depth == bb->loop_depth
                   && prev2->loop_depth == bb->loop_depth
                   && GET_CODE (branch_target) != REG)
                   && GET_CODE (branch_target) != REG)
            prop = prev;
            prop = prev;
 
 
          /* Don't propagate when:
          /* Don't propagate when:
             - this is a simple loop and the hint would be too far
             - this is a simple loop and the hint would be too far
             - this is not a simple loop and there are 16 insns in
             - this is not a simple loop and there are 16 insns in
             this block already
             this block already
             - the predecessor block ends in a branch that will be
             - the predecessor block ends in a branch that will be
             hinted
             hinted
             - the predecessor block ends in an insn that invalidates
             - the predecessor block ends in an insn that invalidates
             the hint */
             the hint */
          if (prop
          if (prop
              && prop->index >= 0
              && prop->index >= 0
              && (bbend = BB_END (prop))
              && (bbend = BB_END (prop))
              && branch_addr - INSN_ADDRESSES (INSN_UID (bbend)) <
              && branch_addr - INSN_ADDRESSES (INSN_UID (bbend)) <
              (simple_loop ? 600 : 16 * 4) && get_branch_target (bbend) == 0
              (simple_loop ? 600 : 16 * 4) && get_branch_target (bbend) == 0
              && (JUMP_P (bbend) || !insn_clobbers_hbr (bbend)))
              && (JUMP_P (bbend) || !insn_clobbers_hbr (bbend)))
            {
            {
              if (dump_file)
              if (dump_file)
                fprintf (dump_file, "propagate from %i to %i (loop depth %i) "
                fprintf (dump_file, "propagate from %i to %i (loop depth %i) "
                         "for %i (loop_exit %i simple_loop %i dist %i)\n",
                         "for %i (loop_exit %i simple_loop %i dist %i)\n",
                         bb->index, prop->index, bb->loop_depth,
                         bb->index, prop->index, bb->loop_depth,
                         INSN_UID (branch), loop_exit, simple_loop,
                         INSN_UID (branch), loop_exit, simple_loop,
                         branch_addr - INSN_ADDRESSES (INSN_UID (bbend)));
                         branch_addr - INSN_ADDRESSES (INSN_UID (bbend)));
 
 
              spu_bb_info[prop->index].prop_jump = branch;
              spu_bb_info[prop->index].prop_jump = branch;
              spu_bb_info[prop->index].bb_index = i;
              spu_bb_info[prop->index].bb_index = i;
            }
            }
          else if (branch_addr - next_addr >= required_dist)
          else if (branch_addr - next_addr >= required_dist)
            {
            {
              if (dump_file)
              if (dump_file)
                fprintf (dump_file, "hint for %i in block %i before %i\n",
                fprintf (dump_file, "hint for %i in block %i before %i\n",
                         INSN_UID (branch), bb->index,
                         INSN_UID (branch), bb->index,
                         INSN_UID (NEXT_INSN (insn)));
                         INSN_UID (NEXT_INSN (insn)));
              spu_emit_branch_hint (NEXT_INSN (insn), branch, branch_target,
              spu_emit_branch_hint (NEXT_INSN (insn), branch, branch_target,
                                    branch_addr - next_addr, blocks);
                                    branch_addr - next_addr, blocks);
            }
            }
          branch = 0;
          branch = 0;
        }
        }
    }
    }
  free (spu_bb_info);
  free (spu_bb_info);
 
 
  if (!sbitmap_empty_p (blocks))
  if (!sbitmap_empty_p (blocks))
    find_many_sub_basic_blocks (blocks);
    find_many_sub_basic_blocks (blocks);
 
 
  /* We have to schedule to make sure alignment is ok. */
  /* We have to schedule to make sure alignment is ok. */
  FOR_EACH_BB (bb) bb->flags &= ~BB_DISABLE_SCHEDULE;
  FOR_EACH_BB (bb) bb->flags &= ~BB_DISABLE_SCHEDULE;
 
 
  /* The hints need to be scheduled, so call it again. */
  /* The hints need to be scheduled, so call it again. */
  schedule_insns ();
  schedule_insns ();
 
 
  insert_hbrp ();
  insert_hbrp ();
 
 
  pad_bb ();
  pad_bb ();
 
 
  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
    if (NONJUMP_INSN_P (insn) && INSN_CODE (insn) == CODE_FOR_hbr)
    if (NONJUMP_INSN_P (insn) && INSN_CODE (insn) == CODE_FOR_hbr)
      {
      {
        /* Adjust the LABEL_REF in a hint when we have inserted a nop
        /* Adjust the LABEL_REF in a hint when we have inserted a nop
           between its branch label and the branch .  We don't move the
           between its branch label and the branch .  We don't move the
           label because GCC expects it at the beginning of the block. */
           label because GCC expects it at the beginning of the block. */
        rtx unspec = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
        rtx unspec = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
        rtx label_ref = XVECEXP (unspec, 0, 0);
        rtx label_ref = XVECEXP (unspec, 0, 0);
        rtx label = XEXP (label_ref, 0);
        rtx label = XEXP (label_ref, 0);
        rtx branch;
        rtx branch;
        int offset = 0;
        int offset = 0;
        for (branch = NEXT_INSN (label);
        for (branch = NEXT_INSN (label);
             !JUMP_P (branch) && !CALL_P (branch);
             !JUMP_P (branch) && !CALL_P (branch);
             branch = NEXT_INSN (branch))
             branch = NEXT_INSN (branch))
          if (NONJUMP_INSN_P (branch))
          if (NONJUMP_INSN_P (branch))
            offset += get_attr_length (branch);
            offset += get_attr_length (branch);
        if (offset > 0)
        if (offset > 0)
          XVECEXP (unspec, 0, 0) = plus_constant (label_ref, offset);
          XVECEXP (unspec, 0, 0) = plus_constant (label_ref, offset);
      }
      }
 
 
  if (spu_flag_var_tracking)
  if (spu_flag_var_tracking)
    {
    {
      df_analyze ();
      df_analyze ();
      timevar_push (TV_VAR_TRACKING);
      timevar_push (TV_VAR_TRACKING);
      variable_tracking_main ();
      variable_tracking_main ();
      timevar_pop (TV_VAR_TRACKING);
      timevar_pop (TV_VAR_TRACKING);
      df_finish_pass (false);
      df_finish_pass (false);
    }
    }
 
 
  free_bb_for_insn ();
  free_bb_for_insn ();
 
 
  in_spu_reorg = 0;
  in_spu_reorg = 0;
}
}


 
 
/* Insn scheduling routines, primarily for dual issue. */
/* Insn scheduling routines, primarily for dual issue. */
static int
static int
spu_sched_issue_rate (void)
spu_sched_issue_rate (void)
{
{
  return 2;
  return 2;
}
}
 
 
static int
static int
uses_ls_unit(rtx insn)
uses_ls_unit(rtx insn)
{
{
  rtx set = single_set (insn);
  rtx set = single_set (insn);
  if (set != 0
  if (set != 0
      && (GET_CODE (SET_DEST (set)) == MEM
      && (GET_CODE (SET_DEST (set)) == MEM
          || GET_CODE (SET_SRC (set)) == MEM))
          || GET_CODE (SET_SRC (set)) == MEM))
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
static int
static int
get_pipe (rtx insn)
get_pipe (rtx insn)
{
{
  enum attr_type t;
  enum attr_type t;
  /* Handle inline asm */
  /* Handle inline asm */
  if (INSN_CODE (insn) == -1)
  if (INSN_CODE (insn) == -1)
    return -1;
    return -1;
  t = get_attr_type (insn);
  t = get_attr_type (insn);
  switch (t)
  switch (t)
    {
    {
    case TYPE_CONVERT:
    case TYPE_CONVERT:
      return -2;
      return -2;
    case TYPE_MULTI0:
    case TYPE_MULTI0:
      return -1;
      return -1;
 
 
    case TYPE_FX2:
    case TYPE_FX2:
    case TYPE_FX3:
    case TYPE_FX3:
    case TYPE_SPR:
    case TYPE_SPR:
    case TYPE_NOP:
    case TYPE_NOP:
    case TYPE_FXB:
    case TYPE_FXB:
    case TYPE_FPD:
    case TYPE_FPD:
    case TYPE_FP6:
    case TYPE_FP6:
    case TYPE_FP7:
    case TYPE_FP7:
      return 0;
      return 0;
 
 
    case TYPE_LNOP:
    case TYPE_LNOP:
    case TYPE_SHUF:
    case TYPE_SHUF:
    case TYPE_LOAD:
    case TYPE_LOAD:
    case TYPE_STORE:
    case TYPE_STORE:
    case TYPE_BR:
    case TYPE_BR:
    case TYPE_MULTI1:
    case TYPE_MULTI1:
    case TYPE_HBR:
    case TYPE_HBR:
    case TYPE_IPREFETCH:
    case TYPE_IPREFETCH:
      return 1;
      return 1;
    default:
    default:
      abort ();
      abort ();
    }
    }
}
}
 
 
 
 
/* haifa-sched.c has a static variable that keeps track of the current
/* haifa-sched.c has a static variable that keeps track of the current
   cycle.  It is passed to spu_sched_reorder, and we record it here for
   cycle.  It is passed to spu_sched_reorder, and we record it here for
   use by spu_sched_variable_issue.  It won't be accurate if the
   use by spu_sched_variable_issue.  It won't be accurate if the
   scheduler updates it's clock_var between the two calls. */
   scheduler updates it's clock_var between the two calls. */
static int clock_var;
static int clock_var;
 
 
/* This is used to keep track of insn alignment.  Set to 0 at the
/* This is used to keep track of insn alignment.  Set to 0 at the
   beginning of each block and increased by the "length" attr of each
   beginning of each block and increased by the "length" attr of each
   insn scheduled. */
   insn scheduled. */
static int spu_sched_length;
static int spu_sched_length;
 
 
/* Record when we've issued pipe0 and pipe1 insns so we can reorder the
/* Record when we've issued pipe0 and pipe1 insns so we can reorder the
   ready list appropriately in spu_sched_reorder(). */
   ready list appropriately in spu_sched_reorder(). */
static int pipe0_clock;
static int pipe0_clock;
static int pipe1_clock;
static int pipe1_clock;
 
 
static int prev_clock_var;
static int prev_clock_var;
 
 
static int prev_priority;
static int prev_priority;
 
 
/* The SPU needs to load the next ilb sometime during the execution of
/* The SPU needs to load the next ilb sometime during the execution of
   the previous ilb.  There is a potential conflict if every cycle has a
   the previous ilb.  There is a potential conflict if every cycle has a
   load or store.  To avoid the conflict we make sure the load/store
   load or store.  To avoid the conflict we make sure the load/store
   unit is free for at least one cycle during the execution of insns in
   unit is free for at least one cycle during the execution of insns in
   the previous ilb. */
   the previous ilb. */
static int spu_ls_first;
static int spu_ls_first;
static int prev_ls_clock;
static int prev_ls_clock;
 
 
static void
static void
spu_sched_init_global (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
spu_sched_init_global (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
                       int max_ready ATTRIBUTE_UNUSED)
                       int max_ready ATTRIBUTE_UNUSED)
{
{
  spu_sched_length = 0;
  spu_sched_length = 0;
}
}
 
 
static void
static void
spu_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
spu_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
                int max_ready ATTRIBUTE_UNUSED)
                int max_ready ATTRIBUTE_UNUSED)
{
{
  if (align_labels > 4 || align_loops > 4 || align_jumps > 4)
  if (align_labels > 4 || align_loops > 4 || align_jumps > 4)
    {
    {
      /* When any block might be at least 8-byte aligned, assume they
      /* When any block might be at least 8-byte aligned, assume they
         will all be at least 8-byte aligned to make sure dual issue
         will all be at least 8-byte aligned to make sure dual issue
         works out correctly. */
         works out correctly. */
      spu_sched_length = 0;
      spu_sched_length = 0;
    }
    }
  spu_ls_first = INT_MAX;
  spu_ls_first = INT_MAX;
  clock_var = -1;
  clock_var = -1;
  prev_ls_clock = -1;
  prev_ls_clock = -1;
  pipe0_clock = -1;
  pipe0_clock = -1;
  pipe1_clock = -1;
  pipe1_clock = -1;
  prev_clock_var = -1;
  prev_clock_var = -1;
  prev_priority = -1;
  prev_priority = -1;
}
}
 
 
static int
static int
spu_sched_variable_issue (FILE *file ATTRIBUTE_UNUSED,
spu_sched_variable_issue (FILE *file ATTRIBUTE_UNUSED,
                          int verbose ATTRIBUTE_UNUSED, rtx insn, int more)
                          int verbose ATTRIBUTE_UNUSED, rtx insn, int more)
{
{
  int len;
  int len;
  int p;
  int p;
  if (GET_CODE (PATTERN (insn)) == USE
  if (GET_CODE (PATTERN (insn)) == USE
      || GET_CODE (PATTERN (insn)) == CLOBBER
      || GET_CODE (PATTERN (insn)) == CLOBBER
      || (len = get_attr_length (insn)) == 0)
      || (len = get_attr_length (insn)) == 0)
    return more;
    return more;
 
 
  spu_sched_length += len;
  spu_sched_length += len;
 
 
  /* Reset on inline asm */
  /* Reset on inline asm */
  if (INSN_CODE (insn) == -1)
  if (INSN_CODE (insn) == -1)
    {
    {
      spu_ls_first = INT_MAX;
      spu_ls_first = INT_MAX;
      pipe0_clock = -1;
      pipe0_clock = -1;
      pipe1_clock = -1;
      pipe1_clock = -1;
      return 0;
      return 0;
    }
    }
  p = get_pipe (insn);
  p = get_pipe (insn);
  if (p == 0)
  if (p == 0)
    pipe0_clock = clock_var;
    pipe0_clock = clock_var;
  else
  else
    pipe1_clock = clock_var;
    pipe1_clock = clock_var;
 
 
  if (in_spu_reorg)
  if (in_spu_reorg)
    {
    {
      if (clock_var - prev_ls_clock > 1
      if (clock_var - prev_ls_clock > 1
          || INSN_CODE (insn) == CODE_FOR_iprefetch)
          || INSN_CODE (insn) == CODE_FOR_iprefetch)
        spu_ls_first = INT_MAX;
        spu_ls_first = INT_MAX;
      if (uses_ls_unit (insn))
      if (uses_ls_unit (insn))
        {
        {
          if (spu_ls_first == INT_MAX)
          if (spu_ls_first == INT_MAX)
            spu_ls_first = spu_sched_length;
            spu_ls_first = spu_sched_length;
          prev_ls_clock = clock_var;
          prev_ls_clock = clock_var;
        }
        }
 
 
      /* The scheduler hasn't inserted the nop, but we will later on.
      /* The scheduler hasn't inserted the nop, but we will later on.
         Include those nops in spu_sched_length. */
         Include those nops in spu_sched_length. */
      if (prev_clock_var == clock_var && (spu_sched_length & 7))
      if (prev_clock_var == clock_var && (spu_sched_length & 7))
        spu_sched_length += 4;
        spu_sched_length += 4;
      prev_clock_var = clock_var;
      prev_clock_var = clock_var;
 
 
      /* more is -1 when called from spu_sched_reorder for new insns
      /* more is -1 when called from spu_sched_reorder for new insns
         that don't have INSN_PRIORITY */
         that don't have INSN_PRIORITY */
      if (more >= 0)
      if (more >= 0)
        prev_priority = INSN_PRIORITY (insn);
        prev_priority = INSN_PRIORITY (insn);
    }
    }
 
 
  /* Always try issueing more insns.  spu_sched_reorder will decide
  /* Always try issueing more insns.  spu_sched_reorder will decide
     when the cycle should be advanced. */
     when the cycle should be advanced. */
  return 1;
  return 1;
}
}
 
 
/* This function is called for both TARGET_SCHED_REORDER and
/* This function is called for both TARGET_SCHED_REORDER and
   TARGET_SCHED_REORDER2.  */
   TARGET_SCHED_REORDER2.  */
static int
static int
spu_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
spu_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
                   rtx *ready, int *nreadyp, int clock)
                   rtx *ready, int *nreadyp, int clock)
{
{
  int i, nready = *nreadyp;
  int i, nready = *nreadyp;
  int pipe_0, pipe_1, pipe_hbrp, pipe_ls, schedule_i;
  int pipe_0, pipe_1, pipe_hbrp, pipe_ls, schedule_i;
  rtx insn;
  rtx insn;
 
 
  clock_var = clock;
  clock_var = clock;
 
 
  if (nready <= 0 || pipe1_clock >= clock)
  if (nready <= 0 || pipe1_clock >= clock)
    return 0;
    return 0;
 
 
  /* Find any rtl insns that don't generate assembly insns and schedule
  /* Find any rtl insns that don't generate assembly insns and schedule
     them first. */
     them first. */
  for (i = nready - 1; i >= 0; i--)
  for (i = nready - 1; i >= 0; i--)
    {
    {
      insn = ready[i];
      insn = ready[i];
      if (INSN_CODE (insn) == -1
      if (INSN_CODE (insn) == -1
          || INSN_CODE (insn) == CODE_FOR_blockage
          || INSN_CODE (insn) == CODE_FOR_blockage
          || (INSN_P (insn) && get_attr_length (insn) == 0))
          || (INSN_P (insn) && get_attr_length (insn) == 0))
        {
        {
          ready[i] = ready[nready - 1];
          ready[i] = ready[nready - 1];
          ready[nready - 1] = insn;
          ready[nready - 1] = insn;
          return 1;
          return 1;
        }
        }
    }
    }
 
 
  pipe_0 = pipe_1 = pipe_hbrp = pipe_ls = schedule_i = -1;
  pipe_0 = pipe_1 = pipe_hbrp = pipe_ls = schedule_i = -1;
  for (i = 0; i < nready; i++)
  for (i = 0; i < nready; i++)
    if (INSN_CODE (ready[i]) != -1)
    if (INSN_CODE (ready[i]) != -1)
      {
      {
        insn = ready[i];
        insn = ready[i];
        switch (get_attr_type (insn))
        switch (get_attr_type (insn))
          {
          {
          default:
          default:
          case TYPE_MULTI0:
          case TYPE_MULTI0:
          case TYPE_CONVERT:
          case TYPE_CONVERT:
          case TYPE_FX2:
          case TYPE_FX2:
          case TYPE_FX3:
          case TYPE_FX3:
          case TYPE_SPR:
          case TYPE_SPR:
          case TYPE_NOP:
          case TYPE_NOP:
          case TYPE_FXB:
          case TYPE_FXB:
          case TYPE_FPD:
          case TYPE_FPD:
          case TYPE_FP6:
          case TYPE_FP6:
          case TYPE_FP7:
          case TYPE_FP7:
            pipe_0 = i;
            pipe_0 = i;
            break;
            break;
          case TYPE_LOAD:
          case TYPE_LOAD:
          case TYPE_STORE:
          case TYPE_STORE:
            pipe_ls = i;
            pipe_ls = i;
          case TYPE_LNOP:
          case TYPE_LNOP:
          case TYPE_SHUF:
          case TYPE_SHUF:
          case TYPE_BR:
          case TYPE_BR:
          case TYPE_MULTI1:
          case TYPE_MULTI1:
          case TYPE_HBR:
          case TYPE_HBR:
            pipe_1 = i;
            pipe_1 = i;
            break;
            break;
          case TYPE_IPREFETCH:
          case TYPE_IPREFETCH:
            pipe_hbrp = i;
            pipe_hbrp = i;
            break;
            break;
          }
          }
      }
      }
 
 
  /* In the first scheduling phase, schedule loads and stores together
  /* In the first scheduling phase, schedule loads and stores together
     to increase the chance they will get merged during postreload CSE. */
     to increase the chance they will get merged during postreload CSE. */
  if (!reload_completed && pipe_ls >= 0)
  if (!reload_completed && pipe_ls >= 0)
    {
    {
      insn = ready[pipe_ls];
      insn = ready[pipe_ls];
      ready[pipe_ls] = ready[nready - 1];
      ready[pipe_ls] = ready[nready - 1];
      ready[nready - 1] = insn;
      ready[nready - 1] = insn;
      return 1;
      return 1;
    }
    }
 
 
  /* If there is an hbrp ready, prefer it over other pipe 1 insns. */
  /* If there is an hbrp ready, prefer it over other pipe 1 insns. */
  if (pipe_hbrp >= 0)
  if (pipe_hbrp >= 0)
    pipe_1 = pipe_hbrp;
    pipe_1 = pipe_hbrp;
 
 
  /* When we have loads/stores in every cycle of the last 15 insns and
  /* When we have loads/stores in every cycle of the last 15 insns and
     we are about to schedule another load/store, emit an hbrp insn
     we are about to schedule another load/store, emit an hbrp insn
     instead. */
     instead. */
  if (in_spu_reorg
  if (in_spu_reorg
      && spu_sched_length - spu_ls_first >= 4 * 15
      && spu_sched_length - spu_ls_first >= 4 * 15
      && !(pipe0_clock < clock && pipe_0 >= 0) && pipe_1 == pipe_ls)
      && !(pipe0_clock < clock && pipe_0 >= 0) && pipe_1 == pipe_ls)
    {
    {
      insn = sched_emit_insn (gen_iprefetch (GEN_INT (3)));
      insn = sched_emit_insn (gen_iprefetch (GEN_INT (3)));
      recog_memoized (insn);
      recog_memoized (insn);
      if (pipe0_clock < clock)
      if (pipe0_clock < clock)
        PUT_MODE (insn, TImode);
        PUT_MODE (insn, TImode);
      spu_sched_variable_issue (file, verbose, insn, -1);
      spu_sched_variable_issue (file, verbose, insn, -1);
      return 0;
      return 0;
    }
    }
 
 
  /* In general, we want to emit nops to increase dual issue, but dual
  /* In general, we want to emit nops to increase dual issue, but dual
     issue isn't faster when one of the insns could be scheduled later
     issue isn't faster when one of the insns could be scheduled later
     without effecting the critical path.  We look at INSN_PRIORITY to
     without effecting the critical path.  We look at INSN_PRIORITY to
     make a good guess, but it isn't perfect so -mdual-nops=n can be
     make a good guess, but it isn't perfect so -mdual-nops=n can be
     used to effect it. */
     used to effect it. */
  if (in_spu_reorg && spu_dual_nops < 10)
  if (in_spu_reorg && spu_dual_nops < 10)
    {
    {
      /* When we are at an even address and we are not issueing nops to
      /* When we are at an even address and we are not issueing nops to
         improve scheduling then we need to advance the cycle.  */
         improve scheduling then we need to advance the cycle.  */
      if ((spu_sched_length & 7) == 0 && prev_clock_var == clock
      if ((spu_sched_length & 7) == 0 && prev_clock_var == clock
          && (spu_dual_nops == 0
          && (spu_dual_nops == 0
              || (pipe_1 != -1
              || (pipe_1 != -1
                  && prev_priority >
                  && prev_priority >
                  INSN_PRIORITY (ready[pipe_1]) + spu_dual_nops)))
                  INSN_PRIORITY (ready[pipe_1]) + spu_dual_nops)))
        return 0;
        return 0;
 
 
      /* When at an odd address, schedule the highest priority insn
      /* When at an odd address, schedule the highest priority insn
         without considering pipeline. */
         without considering pipeline. */
      if ((spu_sched_length & 7) == 4 && prev_clock_var != clock
      if ((spu_sched_length & 7) == 4 && prev_clock_var != clock
          && (spu_dual_nops == 0
          && (spu_dual_nops == 0
              || (prev_priority >
              || (prev_priority >
                  INSN_PRIORITY (ready[nready - 1]) + spu_dual_nops)))
                  INSN_PRIORITY (ready[nready - 1]) + spu_dual_nops)))
        return 1;
        return 1;
    }
    }
 
 
 
 
  /* We haven't issued a pipe0 insn yet this cycle, if there is a
  /* We haven't issued a pipe0 insn yet this cycle, if there is a
     pipe0 insn in the ready list, schedule it. */
     pipe0 insn in the ready list, schedule it. */
  if (pipe0_clock < clock && pipe_0 >= 0)
  if (pipe0_clock < clock && pipe_0 >= 0)
    schedule_i = pipe_0;
    schedule_i = pipe_0;
 
 
  /* Either we've scheduled a pipe0 insn already or there is no pipe0
  /* Either we've scheduled a pipe0 insn already or there is no pipe0
     insn to schedule.  Put a pipe1 insn at the front of the ready list. */
     insn to schedule.  Put a pipe1 insn at the front of the ready list. */
  else
  else
    schedule_i = pipe_1;
    schedule_i = pipe_1;
 
 
  if (schedule_i > -1)
  if (schedule_i > -1)
    {
    {
      insn = ready[schedule_i];
      insn = ready[schedule_i];
      ready[schedule_i] = ready[nready - 1];
      ready[schedule_i] = ready[nready - 1];
      ready[nready - 1] = insn;
      ready[nready - 1] = insn;
      return 1;
      return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* INSN is dependent on DEP_INSN. */
/* INSN is dependent on DEP_INSN. */
static int
static int
spu_sched_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
spu_sched_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
{
  rtx set;
  rtx set;
 
 
  /* The blockage pattern is used to prevent instructions from being
  /* The blockage pattern is used to prevent instructions from being
     moved across it and has no cost. */
     moved across it and has no cost. */
  if (INSN_CODE (insn) == CODE_FOR_blockage
  if (INSN_CODE (insn) == CODE_FOR_blockage
      || INSN_CODE (dep_insn) == CODE_FOR_blockage)
      || INSN_CODE (dep_insn) == CODE_FOR_blockage)
    return 0;
    return 0;
 
 
  if ((INSN_P (insn) && get_attr_length (insn) == 0)
  if ((INSN_P (insn) && get_attr_length (insn) == 0)
      || (INSN_P (dep_insn) && get_attr_length (dep_insn) == 0))
      || (INSN_P (dep_insn) && get_attr_length (dep_insn) == 0))
    return 0;
    return 0;
 
 
  /* Make sure hbrps are spread out. */
  /* Make sure hbrps are spread out. */
  if (INSN_CODE (insn) == CODE_FOR_iprefetch
  if (INSN_CODE (insn) == CODE_FOR_iprefetch
      && INSN_CODE (dep_insn) == CODE_FOR_iprefetch)
      && INSN_CODE (dep_insn) == CODE_FOR_iprefetch)
    return 8;
    return 8;
 
 
  /* Make sure hints and hbrps are 2 cycles apart. */
  /* Make sure hints and hbrps are 2 cycles apart. */
  if ((INSN_CODE (insn) == CODE_FOR_iprefetch
  if ((INSN_CODE (insn) == CODE_FOR_iprefetch
       || INSN_CODE (insn) == CODE_FOR_hbr)
       || INSN_CODE (insn) == CODE_FOR_hbr)
       && (INSN_CODE (dep_insn) == CODE_FOR_iprefetch
       && (INSN_CODE (dep_insn) == CODE_FOR_iprefetch
           || INSN_CODE (dep_insn) == CODE_FOR_hbr))
           || INSN_CODE (dep_insn) == CODE_FOR_hbr))
    return 2;
    return 2;
 
 
  /* An hbrp has no real dependency on other insns. */
  /* An hbrp has no real dependency on other insns. */
  if (INSN_CODE (insn) == CODE_FOR_iprefetch
  if (INSN_CODE (insn) == CODE_FOR_iprefetch
      || INSN_CODE (dep_insn) == CODE_FOR_iprefetch)
      || INSN_CODE (dep_insn) == CODE_FOR_iprefetch)
    return 0;
    return 0;
 
 
  /* Assuming that it is unlikely an argument register will be used in
  /* Assuming that it is unlikely an argument register will be used in
     the first cycle of the called function, we reduce the cost for
     the first cycle of the called function, we reduce the cost for
     slightly better scheduling of dep_insn.  When not hinted, the
     slightly better scheduling of dep_insn.  When not hinted, the
     mispredicted branch would hide the cost as well.  */
     mispredicted branch would hide the cost as well.  */
  if (CALL_P (insn))
  if (CALL_P (insn))
  {
  {
    rtx target = get_branch_target (insn);
    rtx target = get_branch_target (insn);
    if (GET_CODE (target) != REG || !set_of (target, insn))
    if (GET_CODE (target) != REG || !set_of (target, insn))
      return cost - 2;
      return cost - 2;
    return cost;
    return cost;
  }
  }
 
 
  /* And when returning from a function, let's assume the return values
  /* And when returning from a function, let's assume the return values
     are completed sooner too. */
     are completed sooner too. */
  if (CALL_P (dep_insn))
  if (CALL_P (dep_insn))
    return cost - 2;
    return cost - 2;
 
 
  /* Make sure an instruction that loads from the back chain is schedule
  /* Make sure an instruction that loads from the back chain is schedule
     away from the return instruction so a hint is more likely to get
     away from the return instruction so a hint is more likely to get
     issued. */
     issued. */
  if (INSN_CODE (insn) == CODE_FOR__return
  if (INSN_CODE (insn) == CODE_FOR__return
      && (set = single_set (dep_insn))
      && (set = single_set (dep_insn))
      && GET_CODE (SET_DEST (set)) == REG
      && GET_CODE (SET_DEST (set)) == REG
      && REGNO (SET_DEST (set)) == LINK_REGISTER_REGNUM)
      && REGNO (SET_DEST (set)) == LINK_REGISTER_REGNUM)
    return 20;
    return 20;
 
 
  /* The dfa scheduler sets cost to 0 for all anti-dependencies and the
  /* The dfa scheduler sets cost to 0 for all anti-dependencies and the
     scheduler makes every insn in a block anti-dependent on the final
     scheduler makes every insn in a block anti-dependent on the final
     jump_insn.  We adjust here so higher cost insns will get scheduled
     jump_insn.  We adjust here so higher cost insns will get scheduled
     earlier. */
     earlier. */
  if (JUMP_P (insn) && REG_NOTE_KIND (link) == REG_DEP_ANTI)
  if (JUMP_P (insn) && REG_NOTE_KIND (link) == REG_DEP_ANTI)
    return insn_cost (dep_insn) - 3;
    return insn_cost (dep_insn) - 3;
 
 
  return cost;
  return cost;
}
}


/* Create a CONST_DOUBLE from a string.  */
/* Create a CONST_DOUBLE from a string.  */
struct rtx_def *
struct rtx_def *
spu_float_const (const char *string, enum machine_mode mode)
spu_float_const (const char *string, enum machine_mode mode)
{
{
  REAL_VALUE_TYPE value;
  REAL_VALUE_TYPE value;
  value = REAL_VALUE_ATOF (string, mode);
  value = REAL_VALUE_ATOF (string, mode);
  return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
  return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
}
}
 
 
int
int
spu_constant_address_p (rtx x)
spu_constant_address_p (rtx x)
{
{
  return (GET_CODE (x) == LABEL_REF || GET_CODE (x) == SYMBOL_REF
  return (GET_CODE (x) == LABEL_REF || GET_CODE (x) == SYMBOL_REF
          || GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST
          || GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST
          || GET_CODE (x) == HIGH);
          || GET_CODE (x) == HIGH);
}
}
 
 
static enum spu_immediate
static enum spu_immediate
which_immediate_load (HOST_WIDE_INT val)
which_immediate_load (HOST_WIDE_INT val)
{
{
  gcc_assert (val == trunc_int_for_mode (val, SImode));
  gcc_assert (val == trunc_int_for_mode (val, SImode));
 
 
  if (val >= -0x8000 && val <= 0x7fff)
  if (val >= -0x8000 && val <= 0x7fff)
    return SPU_IL;
    return SPU_IL;
  if (val >= 0 && val <= 0x3ffff)
  if (val >= 0 && val <= 0x3ffff)
    return SPU_ILA;
    return SPU_ILA;
  if ((val & 0xffff) == ((val >> 16) & 0xffff))
  if ((val & 0xffff) == ((val >> 16) & 0xffff))
    return SPU_ILH;
    return SPU_ILH;
  if ((val & 0xffff) == 0)
  if ((val & 0xffff) == 0)
    return SPU_ILHU;
    return SPU_ILHU;
 
 
  return SPU_NONE;
  return SPU_NONE;
}
}
 
 
/* Return true when OP can be loaded by one of the il instructions, or
/* Return true when OP can be loaded by one of the il instructions, or
   when flow2 is not completed and OP can be loaded using ilhu and iohl. */
   when flow2 is not completed and OP can be loaded using ilhu and iohl. */
int
int
immediate_load_p (rtx op, enum machine_mode mode)
immediate_load_p (rtx op, enum machine_mode mode)
{
{
  if (CONSTANT_P (op))
  if (CONSTANT_P (op))
    {
    {
      enum immediate_class c = classify_immediate (op, mode);
      enum immediate_class c = classify_immediate (op, mode);
      return c == IC_IL1 || c == IC_IL1s
      return c == IC_IL1 || c == IC_IL1s
             || (!epilogue_completed && (c == IC_IL2 || c == IC_IL2s));
             || (!epilogue_completed && (c == IC_IL2 || c == IC_IL2s));
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Return true if the first SIZE bytes of arr is a constant that can be
/* Return true if the first SIZE bytes of arr is a constant that can be
   generated with cbd, chd, cwd or cdd.  When non-NULL, PRUN and PSTART
   generated with cbd, chd, cwd or cdd.  When non-NULL, PRUN and PSTART
   represent the size and offset of the instruction to use. */
   represent the size and offset of the instruction to use. */
static int
static int
cpat_info(unsigned char *arr, int size, int *prun, int *pstart)
cpat_info(unsigned char *arr, int size, int *prun, int *pstart)
{
{
  int cpat, run, i, start;
  int cpat, run, i, start;
  cpat = 1;
  cpat = 1;
  run = 0;
  run = 0;
  start = -1;
  start = -1;
  for (i = 0; i < size && cpat; i++)
  for (i = 0; i < size && cpat; i++)
    if (arr[i] != i+16)
    if (arr[i] != i+16)
      {
      {
        if (!run)
        if (!run)
          {
          {
            start = i;
            start = i;
            if (arr[i] == 3)
            if (arr[i] == 3)
              run = 1;
              run = 1;
            else if (arr[i] == 2 && arr[i+1] == 3)
            else if (arr[i] == 2 && arr[i+1] == 3)
              run = 2;
              run = 2;
            else if (arr[i] == 0)
            else if (arr[i] == 0)
              {
              {
                while (arr[i+run] == run && i+run < 16)
                while (arr[i+run] == run && i+run < 16)
                  run++;
                  run++;
                if (run != 4 && run != 8)
                if (run != 4 && run != 8)
                  cpat = 0;
                  cpat = 0;
              }
              }
            else
            else
              cpat = 0;
              cpat = 0;
            if ((i & (run-1)) != 0)
            if ((i & (run-1)) != 0)
              cpat = 0;
              cpat = 0;
            i += run;
            i += run;
          }
          }
        else
        else
          cpat = 0;
          cpat = 0;
      }
      }
  if (cpat && (run || size < 16))
  if (cpat && (run || size < 16))
    {
    {
      if (run == 0)
      if (run == 0)
        run = 1;
        run = 1;
      if (prun)
      if (prun)
        *prun = run;
        *prun = run;
      if (pstart)
      if (pstart)
        *pstart = start == -1 ? 16-run : start;
        *pstart = start == -1 ? 16-run : start;
      return 1;
      return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* OP is a CONSTANT_P.  Determine what instructions can be used to load
/* OP is a CONSTANT_P.  Determine what instructions can be used to load
   it into a register.  MODE is only valid when OP is a CONST_INT. */
   it into a register.  MODE is only valid when OP is a CONST_INT. */
static enum immediate_class
static enum immediate_class
classify_immediate (rtx op, enum machine_mode mode)
classify_immediate (rtx op, enum machine_mode mode)
{
{
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  unsigned char arr[16];
  unsigned char arr[16];
  int i, j, repeated, fsmbi, repeat;
  int i, j, repeated, fsmbi, repeat;
 
 
  gcc_assert (CONSTANT_P (op));
  gcc_assert (CONSTANT_P (op));
 
 
  if (GET_MODE (op) != VOIDmode)
  if (GET_MODE (op) != VOIDmode)
    mode = GET_MODE (op);
    mode = GET_MODE (op);
 
 
  /* A V4SI const_vector with all identical symbols is ok. */
  /* A V4SI const_vector with all identical symbols is ok. */
  if (!flag_pic
  if (!flag_pic
      && mode == V4SImode
      && mode == V4SImode
      && GET_CODE (op) == CONST_VECTOR
      && GET_CODE (op) == CONST_VECTOR
      && GET_CODE (CONST_VECTOR_ELT (op, 0)) != CONST_INT
      && GET_CODE (CONST_VECTOR_ELT (op, 0)) != CONST_INT
      && GET_CODE (CONST_VECTOR_ELT (op, 0)) != CONST_DOUBLE
      && GET_CODE (CONST_VECTOR_ELT (op, 0)) != CONST_DOUBLE
      && CONST_VECTOR_ELT (op, 0) == CONST_VECTOR_ELT (op, 1)
      && CONST_VECTOR_ELT (op, 0) == CONST_VECTOR_ELT (op, 1)
      && CONST_VECTOR_ELT (op, 1) == CONST_VECTOR_ELT (op, 2)
      && CONST_VECTOR_ELT (op, 1) == CONST_VECTOR_ELT (op, 2)
      && CONST_VECTOR_ELT (op, 2) == CONST_VECTOR_ELT (op, 3))
      && CONST_VECTOR_ELT (op, 2) == CONST_VECTOR_ELT (op, 3))
    op = CONST_VECTOR_ELT (op, 0);
    op = CONST_VECTOR_ELT (op, 0);
 
 
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case SYMBOL_REF:
    case SYMBOL_REF:
    case LABEL_REF:
    case LABEL_REF:
      return TARGET_LARGE_MEM ? IC_IL2s : IC_IL1s;
      return TARGET_LARGE_MEM ? IC_IL2s : IC_IL1s;
 
 
    case CONST:
    case CONST:
      /* We can never know if the resulting address fits in 18 bits and can be
      /* We can never know if the resulting address fits in 18 bits and can be
         loaded with ila.  For now, assume the address will not overflow if
         loaded with ila.  For now, assume the address will not overflow if
         the displacement is "small" (fits 'K' constraint).  */
         the displacement is "small" (fits 'K' constraint).  */
      if (!TARGET_LARGE_MEM && GET_CODE (XEXP (op, 0)) == PLUS)
      if (!TARGET_LARGE_MEM && GET_CODE (XEXP (op, 0)) == PLUS)
        {
        {
          rtx sym = XEXP (XEXP (op, 0), 0);
          rtx sym = XEXP (XEXP (op, 0), 0);
          rtx cst = XEXP (XEXP (op, 0), 1);
          rtx cst = XEXP (XEXP (op, 0), 1);
 
 
          if (GET_CODE (sym) == SYMBOL_REF
          if (GET_CODE (sym) == SYMBOL_REF
              && GET_CODE (cst) == CONST_INT
              && GET_CODE (cst) == CONST_INT
              && satisfies_constraint_K (cst))
              && satisfies_constraint_K (cst))
            return IC_IL1s;
            return IC_IL1s;
        }
        }
      return IC_IL2s;
      return IC_IL2s;
 
 
    case HIGH:
    case HIGH:
      return IC_IL1s;
      return IC_IL1s;
 
 
    case CONST_VECTOR:
    case CONST_VECTOR:
      for (i = 0; i < GET_MODE_NUNITS (mode); i++)
      for (i = 0; i < GET_MODE_NUNITS (mode); i++)
        if (GET_CODE (CONST_VECTOR_ELT (op, i)) != CONST_INT
        if (GET_CODE (CONST_VECTOR_ELT (op, i)) != CONST_INT
            && GET_CODE (CONST_VECTOR_ELT (op, i)) != CONST_DOUBLE)
            && GET_CODE (CONST_VECTOR_ELT (op, i)) != CONST_DOUBLE)
          return IC_POOL;
          return IC_POOL;
      /* Fall through. */
      /* Fall through. */
 
 
    case CONST_INT:
    case CONST_INT:
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      constant_to_array (mode, op, arr);
      constant_to_array (mode, op, arr);
 
 
      /* Check that each 4-byte slot is identical. */
      /* Check that each 4-byte slot is identical. */
      repeated = 1;
      repeated = 1;
      for (i = 4; i < 16; i += 4)
      for (i = 4; i < 16; i += 4)
        for (j = 0; j < 4; j++)
        for (j = 0; j < 4; j++)
          if (arr[j] != arr[i + j])
          if (arr[j] != arr[i + j])
            repeated = 0;
            repeated = 0;
 
 
      if (repeated)
      if (repeated)
        {
        {
          val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
          val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
          val = trunc_int_for_mode (val, SImode);
          val = trunc_int_for_mode (val, SImode);
 
 
          if (which_immediate_load (val) != SPU_NONE)
          if (which_immediate_load (val) != SPU_NONE)
            return IC_IL1;
            return IC_IL1;
        }
        }
 
 
      /* Any mode of 2 bytes or smaller can be loaded with an il
      /* Any mode of 2 bytes or smaller can be loaded with an il
         instruction. */
         instruction. */
      gcc_assert (GET_MODE_SIZE (mode) > 2);
      gcc_assert (GET_MODE_SIZE (mode) > 2);
 
 
      fsmbi = 1;
      fsmbi = 1;
      repeat = 0;
      repeat = 0;
      for (i = 0; i < 16 && fsmbi; i++)
      for (i = 0; i < 16 && fsmbi; i++)
        if (arr[i] != 0 && repeat == 0)
        if (arr[i] != 0 && repeat == 0)
          repeat = arr[i];
          repeat = arr[i];
        else if (arr[i] != 0 && arr[i] != repeat)
        else if (arr[i] != 0 && arr[i] != repeat)
          fsmbi = 0;
          fsmbi = 0;
      if (fsmbi)
      if (fsmbi)
        return repeat == 0xff ? IC_FSMBI : IC_FSMBI2;
        return repeat == 0xff ? IC_FSMBI : IC_FSMBI2;
 
 
      if (cpat_info (arr, GET_MODE_SIZE (mode), 0, 0))
      if (cpat_info (arr, GET_MODE_SIZE (mode), 0, 0))
        return IC_CPAT;
        return IC_CPAT;
 
 
      if (repeated)
      if (repeated)
        return IC_IL2;
        return IC_IL2;
 
 
      return IC_POOL;
      return IC_POOL;
    default:
    default:
      break;
      break;
    }
    }
  gcc_unreachable ();
  gcc_unreachable ();
}
}
 
 
static enum spu_immediate
static enum spu_immediate
which_logical_immediate (HOST_WIDE_INT val)
which_logical_immediate (HOST_WIDE_INT val)
{
{
  gcc_assert (val == trunc_int_for_mode (val, SImode));
  gcc_assert (val == trunc_int_for_mode (val, SImode));
 
 
  if (val >= -0x200 && val <= 0x1ff)
  if (val >= -0x200 && val <= 0x1ff)
    return SPU_ORI;
    return SPU_ORI;
  if (val >= 0 && val <= 0xffff)
  if (val >= 0 && val <= 0xffff)
    return SPU_IOHL;
    return SPU_IOHL;
  if ((val & 0xffff) == ((val >> 16) & 0xffff))
  if ((val & 0xffff) == ((val >> 16) & 0xffff))
    {
    {
      val = trunc_int_for_mode (val, HImode);
      val = trunc_int_for_mode (val, HImode);
      if (val >= -0x200 && val <= 0x1ff)
      if (val >= -0x200 && val <= 0x1ff)
        return SPU_ORHI;
        return SPU_ORHI;
      if ((val & 0xff) == ((val >> 8) & 0xff))
      if ((val & 0xff) == ((val >> 8) & 0xff))
        {
        {
          val = trunc_int_for_mode (val, QImode);
          val = trunc_int_for_mode (val, QImode);
          if (val >= -0x200 && val <= 0x1ff)
          if (val >= -0x200 && val <= 0x1ff)
            return SPU_ORBI;
            return SPU_ORBI;
        }
        }
    }
    }
  return SPU_NONE;
  return SPU_NONE;
}
}
 
 
/* Return TRUE when X, a CONST_VECTOR, only contains CONST_INTs or
/* Return TRUE when X, a CONST_VECTOR, only contains CONST_INTs or
   CONST_DOUBLEs. */
   CONST_DOUBLEs. */
static int
static int
const_vector_immediate_p (rtx x)
const_vector_immediate_p (rtx x)
{
{
  int i;
  int i;
  gcc_assert (GET_CODE (x) == CONST_VECTOR);
  gcc_assert (GET_CODE (x) == CONST_VECTOR);
  for (i = 0; i < GET_MODE_NUNITS (GET_MODE (x)); i++)
  for (i = 0; i < GET_MODE_NUNITS (GET_MODE (x)); i++)
    if (GET_CODE (CONST_VECTOR_ELT (x, i)) != CONST_INT
    if (GET_CODE (CONST_VECTOR_ELT (x, i)) != CONST_INT
        && GET_CODE (CONST_VECTOR_ELT (x, i)) != CONST_DOUBLE)
        && GET_CODE (CONST_VECTOR_ELT (x, i)) != CONST_DOUBLE)
      return 0;
      return 0;
  return 1;
  return 1;
}
}
 
 
int
int
logical_immediate_p (rtx op, enum machine_mode mode)
logical_immediate_p (rtx op, enum machine_mode mode)
{
{
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  unsigned char arr[16];
  unsigned char arr[16];
  int i, j;
  int i, j;
 
 
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
              || GET_CODE (op) == CONST_VECTOR);
              || GET_CODE (op) == CONST_VECTOR);
 
 
  if (GET_CODE (op) == CONST_VECTOR
  if (GET_CODE (op) == CONST_VECTOR
      && !const_vector_immediate_p (op))
      && !const_vector_immediate_p (op))
    return 0;
    return 0;
 
 
  if (GET_MODE (op) != VOIDmode)
  if (GET_MODE (op) != VOIDmode)
    mode = GET_MODE (op);
    mode = GET_MODE (op);
 
 
  constant_to_array (mode, op, arr);
  constant_to_array (mode, op, arr);
 
 
  /* Check that bytes are repeated. */
  /* Check that bytes are repeated. */
  for (i = 4; i < 16; i += 4)
  for (i = 4; i < 16; i += 4)
    for (j = 0; j < 4; j++)
    for (j = 0; j < 4; j++)
      if (arr[j] != arr[i + j])
      if (arr[j] != arr[i + j])
        return 0;
        return 0;
 
 
  val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
  val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
  val = trunc_int_for_mode (val, SImode);
  val = trunc_int_for_mode (val, SImode);
 
 
  i = which_logical_immediate (val);
  i = which_logical_immediate (val);
  return i != SPU_NONE && i != SPU_IOHL;
  return i != SPU_NONE && i != SPU_IOHL;
}
}
 
 
int
int
iohl_immediate_p (rtx op, enum machine_mode mode)
iohl_immediate_p (rtx op, enum machine_mode mode)
{
{
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  unsigned char arr[16];
  unsigned char arr[16];
  int i, j;
  int i, j;
 
 
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
              || GET_CODE (op) == CONST_VECTOR);
              || GET_CODE (op) == CONST_VECTOR);
 
 
  if (GET_CODE (op) == CONST_VECTOR
  if (GET_CODE (op) == CONST_VECTOR
      && !const_vector_immediate_p (op))
      && !const_vector_immediate_p (op))
    return 0;
    return 0;
 
 
  if (GET_MODE (op) != VOIDmode)
  if (GET_MODE (op) != VOIDmode)
    mode = GET_MODE (op);
    mode = GET_MODE (op);
 
 
  constant_to_array (mode, op, arr);
  constant_to_array (mode, op, arr);
 
 
  /* Check that bytes are repeated. */
  /* Check that bytes are repeated. */
  for (i = 4; i < 16; i += 4)
  for (i = 4; i < 16; i += 4)
    for (j = 0; j < 4; j++)
    for (j = 0; j < 4; j++)
      if (arr[j] != arr[i + j])
      if (arr[j] != arr[i + j])
        return 0;
        return 0;
 
 
  val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
  val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
  val = trunc_int_for_mode (val, SImode);
  val = trunc_int_for_mode (val, SImode);
 
 
  return val >= 0 && val <= 0xffff;
  return val >= 0 && val <= 0xffff;
}
}
 
 
int
int
arith_immediate_p (rtx op, enum machine_mode mode,
arith_immediate_p (rtx op, enum machine_mode mode,
                   HOST_WIDE_INT low, HOST_WIDE_INT high)
                   HOST_WIDE_INT low, HOST_WIDE_INT high)
{
{
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  unsigned char arr[16];
  unsigned char arr[16];
  int bytes, i, j;
  int bytes, i, j;
 
 
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
              || GET_CODE (op) == CONST_VECTOR);
              || GET_CODE (op) == CONST_VECTOR);
 
 
  if (GET_CODE (op) == CONST_VECTOR
  if (GET_CODE (op) == CONST_VECTOR
      && !const_vector_immediate_p (op))
      && !const_vector_immediate_p (op))
    return 0;
    return 0;
 
 
  if (GET_MODE (op) != VOIDmode)
  if (GET_MODE (op) != VOIDmode)
    mode = GET_MODE (op);
    mode = GET_MODE (op);
 
 
  constant_to_array (mode, op, arr);
  constant_to_array (mode, op, arr);
 
 
  if (VECTOR_MODE_P (mode))
  if (VECTOR_MODE_P (mode))
    mode = GET_MODE_INNER (mode);
    mode = GET_MODE_INNER (mode);
 
 
  bytes = GET_MODE_SIZE (mode);
  bytes = GET_MODE_SIZE (mode);
  mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0);
  mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0);
 
 
  /* Check that bytes are repeated. */
  /* Check that bytes are repeated. */
  for (i = bytes; i < 16; i += bytes)
  for (i = bytes; i < 16; i += bytes)
    for (j = 0; j < bytes; j++)
    for (j = 0; j < bytes; j++)
      if (arr[j] != arr[i + j])
      if (arr[j] != arr[i + j])
        return 0;
        return 0;
 
 
  val = arr[0];
  val = arr[0];
  for (j = 1; j < bytes; j++)
  for (j = 1; j < bytes; j++)
    val = (val << 8) | arr[j];
    val = (val << 8) | arr[j];
 
 
  val = trunc_int_for_mode (val, mode);
  val = trunc_int_for_mode (val, mode);
 
 
  return val >= low && val <= high;
  return val >= low && val <= high;
}
}
 
 
/* TRUE when op is an immediate and an exact power of 2, and given that
/* TRUE when op is an immediate and an exact power of 2, and given that
   OP is 2^scale, scale >= LOW && scale <= HIGH.  When OP is a vector,
   OP is 2^scale, scale >= LOW && scale <= HIGH.  When OP is a vector,
   all entries must be the same. */
   all entries must be the same. */
bool
bool
exp2_immediate_p (rtx op, enum machine_mode mode, int low, int high)
exp2_immediate_p (rtx op, enum machine_mode mode, int low, int high)
{
{
  enum machine_mode int_mode;
  enum machine_mode int_mode;
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  unsigned char arr[16];
  unsigned char arr[16];
  int bytes, i, j;
  int bytes, i, j;
 
 
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
  gcc_assert (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE
              || GET_CODE (op) == CONST_VECTOR);
              || GET_CODE (op) == CONST_VECTOR);
 
 
  if (GET_CODE (op) == CONST_VECTOR
  if (GET_CODE (op) == CONST_VECTOR
      && !const_vector_immediate_p (op))
      && !const_vector_immediate_p (op))
    return 0;
    return 0;
 
 
  if (GET_MODE (op) != VOIDmode)
  if (GET_MODE (op) != VOIDmode)
    mode = GET_MODE (op);
    mode = GET_MODE (op);
 
 
  constant_to_array (mode, op, arr);
  constant_to_array (mode, op, arr);
 
 
  if (VECTOR_MODE_P (mode))
  if (VECTOR_MODE_P (mode))
    mode = GET_MODE_INNER (mode);
    mode = GET_MODE_INNER (mode);
 
 
  bytes = GET_MODE_SIZE (mode);
  bytes = GET_MODE_SIZE (mode);
  int_mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0);
  int_mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0);
 
 
  /* Check that bytes are repeated. */
  /* Check that bytes are repeated. */
  for (i = bytes; i < 16; i += bytes)
  for (i = bytes; i < 16; i += bytes)
    for (j = 0; j < bytes; j++)
    for (j = 0; j < bytes; j++)
      if (arr[j] != arr[i + j])
      if (arr[j] != arr[i + j])
        return 0;
        return 0;
 
 
  val = arr[0];
  val = arr[0];
  for (j = 1; j < bytes; j++)
  for (j = 1; j < bytes; j++)
    val = (val << 8) | arr[j];
    val = (val << 8) | arr[j];
 
 
  val = trunc_int_for_mode (val, int_mode);
  val = trunc_int_for_mode (val, int_mode);
 
 
  /* Currently, we only handle SFmode */
  /* Currently, we only handle SFmode */
  gcc_assert (mode == SFmode);
  gcc_assert (mode == SFmode);
  if (mode == SFmode)
  if (mode == SFmode)
    {
    {
      int exp = (val >> 23) - 127;
      int exp = (val >> 23) - 127;
      return val > 0 && (val & 0x007fffff) == 0
      return val > 0 && (val & 0x007fffff) == 0
             &&  exp >= low && exp <= high;
             &&  exp >= low && exp <= high;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
/* Return true if X is a SYMBOL_REF to an __ea qualified variable.  */
/* Return true if X is a SYMBOL_REF to an __ea qualified variable.  */
 
 
static int
static int
ea_symbol_ref (rtx *px, void *data ATTRIBUTE_UNUSED)
ea_symbol_ref (rtx *px, void *data ATTRIBUTE_UNUSED)
{
{
  rtx x = *px;
  rtx x = *px;
  tree decl;
  tree decl;
 
 
  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS)
  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS)
    {
    {
      rtx plus = XEXP (x, 0);
      rtx plus = XEXP (x, 0);
      rtx op0 = XEXP (plus, 0);
      rtx op0 = XEXP (plus, 0);
      rtx op1 = XEXP (plus, 1);
      rtx op1 = XEXP (plus, 1);
      if (GET_CODE (op1) == CONST_INT)
      if (GET_CODE (op1) == CONST_INT)
        x = op0;
        x = op0;
    }
    }
 
 
  return (GET_CODE (x) == SYMBOL_REF
  return (GET_CODE (x) == SYMBOL_REF
          && (decl = SYMBOL_REF_DECL (x)) != 0
          && (decl = SYMBOL_REF_DECL (x)) != 0
          && TREE_CODE (decl) == VAR_DECL
          && TREE_CODE (decl) == VAR_DECL
          && TYPE_ADDR_SPACE (TREE_TYPE (decl)));
          && TYPE_ADDR_SPACE (TREE_TYPE (decl)));
}
}
 
 
/* We accept:
/* We accept:
   - any 32-bit constant (SImode, SFmode)
   - any 32-bit constant (SImode, SFmode)
   - any constant that can be generated with fsmbi (any mode)
   - any constant that can be generated with fsmbi (any mode)
   - a 64-bit constant where the high and low bits are identical
   - a 64-bit constant where the high and low bits are identical
     (DImode, DFmode)
     (DImode, DFmode)
   - a 128-bit constant where the four 32-bit words match.  */
   - a 128-bit constant where the four 32-bit words match.  */
int
int
spu_legitimate_constant_p (rtx x)
spu_legitimate_constant_p (rtx x)
{
{
  if (GET_CODE (x) == HIGH)
  if (GET_CODE (x) == HIGH)
    x = XEXP (x, 0);
    x = XEXP (x, 0);
 
 
  /* Reject any __ea qualified reference.  These can't appear in
  /* Reject any __ea qualified reference.  These can't appear in
     instructions but must be forced to the constant pool.  */
     instructions but must be forced to the constant pool.  */
  if (for_each_rtx (&x, ea_symbol_ref, 0))
  if (for_each_rtx (&x, ea_symbol_ref, 0))
    return 0;
    return 0;
 
 
  /* V4SI with all identical symbols is valid. */
  /* V4SI with all identical symbols is valid. */
  if (!flag_pic
  if (!flag_pic
      && GET_MODE (x) == V4SImode
      && GET_MODE (x) == V4SImode
      && (GET_CODE (CONST_VECTOR_ELT (x, 0)) == SYMBOL_REF
      && (GET_CODE (CONST_VECTOR_ELT (x, 0)) == SYMBOL_REF
          || GET_CODE (CONST_VECTOR_ELT (x, 0)) == LABEL_REF
          || GET_CODE (CONST_VECTOR_ELT (x, 0)) == LABEL_REF
          || GET_CODE (CONST_VECTOR_ELT (x, 0)) == CONST))
          || GET_CODE (CONST_VECTOR_ELT (x, 0)) == CONST))
    return CONST_VECTOR_ELT (x, 0) == CONST_VECTOR_ELT (x, 1)
    return CONST_VECTOR_ELT (x, 0) == CONST_VECTOR_ELT (x, 1)
           && CONST_VECTOR_ELT (x, 1) == CONST_VECTOR_ELT (x, 2)
           && CONST_VECTOR_ELT (x, 1) == CONST_VECTOR_ELT (x, 2)
           && CONST_VECTOR_ELT (x, 2) == CONST_VECTOR_ELT (x, 3);
           && CONST_VECTOR_ELT (x, 2) == CONST_VECTOR_ELT (x, 3);
 
 
  if (GET_CODE (x) == CONST_VECTOR
  if (GET_CODE (x) == CONST_VECTOR
      && !const_vector_immediate_p (x))
      && !const_vector_immediate_p (x))
    return 0;
    return 0;
  return 1;
  return 1;
}
}
 
 
/* Valid address are:
/* Valid address are:
   - symbol_ref, label_ref, const
   - symbol_ref, label_ref, const
   - reg
   - reg
   - reg + const_int, where const_int is 16 byte aligned
   - reg + const_int, where const_int is 16 byte aligned
   - reg + reg, alignment doesn't matter
   - reg + reg, alignment doesn't matter
  The alignment matters in the reg+const case because lqd and stqd
  The alignment matters in the reg+const case because lqd and stqd
  ignore the 4 least significant bits of the const.  We only care about
  ignore the 4 least significant bits of the const.  We only care about
  16 byte modes because the expand phase will change all smaller MEM
  16 byte modes because the expand phase will change all smaller MEM
  references to TImode.  */
  references to TImode.  */
static bool
static bool
spu_legitimate_address_p (enum machine_mode mode,
spu_legitimate_address_p (enum machine_mode mode,
                          rtx x, bool reg_ok_strict)
                          rtx x, bool reg_ok_strict)
{
{
  int aligned = GET_MODE_SIZE (mode) >= 16;
  int aligned = GET_MODE_SIZE (mode) >= 16;
  if (aligned
  if (aligned
      && GET_CODE (x) == AND
      && GET_CODE (x) == AND
      && GET_CODE (XEXP (x, 1)) == CONST_INT
      && GET_CODE (XEXP (x, 1)) == CONST_INT
      && INTVAL (XEXP (x, 1)) == (HOST_WIDE_INT) - 16)
      && INTVAL (XEXP (x, 1)) == (HOST_WIDE_INT) - 16)
    x = XEXP (x, 0);
    x = XEXP (x, 0);
  switch (GET_CODE (x))
  switch (GET_CODE (x))
    {
    {
    case LABEL_REF:
    case LABEL_REF:
      return !TARGET_LARGE_MEM;
      return !TARGET_LARGE_MEM;
 
 
    case SYMBOL_REF:
    case SYMBOL_REF:
    case CONST:
    case CONST:
      /* Keep __ea references until reload so that spu_expand_mov can see them
      /* Keep __ea references until reload so that spu_expand_mov can see them
         in MEMs.  */
         in MEMs.  */
      if (ea_symbol_ref (&x, 0))
      if (ea_symbol_ref (&x, 0))
        return !reload_in_progress && !reload_completed;
        return !reload_in_progress && !reload_completed;
      return !TARGET_LARGE_MEM;
      return !TARGET_LARGE_MEM;
 
 
    case CONST_INT:
    case CONST_INT:
      return INTVAL (x) >= 0 && INTVAL (x) <= 0x3ffff;
      return INTVAL (x) >= 0 && INTVAL (x) <= 0x3ffff;
 
 
    case SUBREG:
    case SUBREG:
      x = XEXP (x, 0);
      x = XEXP (x, 0);
      if (REG_P (x))
      if (REG_P (x))
        return 0;
        return 0;
 
 
    case REG:
    case REG:
      return INT_REG_OK_FOR_BASE_P (x, reg_ok_strict);
      return INT_REG_OK_FOR_BASE_P (x, reg_ok_strict);
 
 
    case PLUS:
    case PLUS:
    case LO_SUM:
    case LO_SUM:
      {
      {
        rtx op0 = XEXP (x, 0);
        rtx op0 = XEXP (x, 0);
        rtx op1 = XEXP (x, 1);
        rtx op1 = XEXP (x, 1);
        if (GET_CODE (op0) == SUBREG)
        if (GET_CODE (op0) == SUBREG)
          op0 = XEXP (op0, 0);
          op0 = XEXP (op0, 0);
        if (GET_CODE (op1) == SUBREG)
        if (GET_CODE (op1) == SUBREG)
          op1 = XEXP (op1, 0);
          op1 = XEXP (op1, 0);
        if (GET_CODE (op0) == REG
        if (GET_CODE (op0) == REG
            && INT_REG_OK_FOR_BASE_P (op0, reg_ok_strict)
            && INT_REG_OK_FOR_BASE_P (op0, reg_ok_strict)
            && GET_CODE (op1) == CONST_INT
            && GET_CODE (op1) == CONST_INT
            && INTVAL (op1) >= -0x2000
            && INTVAL (op1) >= -0x2000
            && INTVAL (op1) <= 0x1fff
            && INTVAL (op1) <= 0x1fff
            && (!aligned || (INTVAL (op1) & 15) == 0))
            && (!aligned || (INTVAL (op1) & 15) == 0))
          return TRUE;
          return TRUE;
        if (GET_CODE (op0) == REG
        if (GET_CODE (op0) == REG
            && INT_REG_OK_FOR_BASE_P (op0, reg_ok_strict)
            && INT_REG_OK_FOR_BASE_P (op0, reg_ok_strict)
            && GET_CODE (op1) == REG
            && GET_CODE (op1) == REG
            && INT_REG_OK_FOR_INDEX_P (op1, reg_ok_strict))
            && INT_REG_OK_FOR_INDEX_P (op1, reg_ok_strict))
          return TRUE;
          return TRUE;
      }
      }
      break;
      break;
 
 
    default:
    default:
      break;
      break;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
/* Like spu_legitimate_address_p, except with named addresses.  */
/* Like spu_legitimate_address_p, except with named addresses.  */
static bool
static bool
spu_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
spu_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
                                     bool reg_ok_strict, addr_space_t as)
                                     bool reg_ok_strict, addr_space_t as)
{
{
  if (as == ADDR_SPACE_EA)
  if (as == ADDR_SPACE_EA)
    return (REG_P (x) && (GET_MODE (x) == EAmode));
    return (REG_P (x) && (GET_MODE (x) == EAmode));
 
 
  else if (as != ADDR_SPACE_GENERIC)
  else if (as != ADDR_SPACE_GENERIC)
    gcc_unreachable ();
    gcc_unreachable ();
 
 
  return spu_legitimate_address_p (mode, x, reg_ok_strict);
  return spu_legitimate_address_p (mode, x, reg_ok_strict);
}
}
 
 
/* When the address is reg + const_int, force the const_int into a
/* When the address is reg + const_int, force the const_int into a
   register.  */
   register.  */
rtx
rtx
spu_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
spu_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
                        enum machine_mode mode ATTRIBUTE_UNUSED)
                        enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  rtx op0, op1;
  rtx op0, op1;
  /* Make sure both operands are registers.  */
  /* Make sure both operands are registers.  */
  if (GET_CODE (x) == PLUS)
  if (GET_CODE (x) == PLUS)
    {
    {
      op0 = XEXP (x, 0);
      op0 = XEXP (x, 0);
      op1 = XEXP (x, 1);
      op1 = XEXP (x, 1);
      if (ALIGNED_SYMBOL_REF_P (op0))
      if (ALIGNED_SYMBOL_REF_P (op0))
        {
        {
          op0 = force_reg (Pmode, op0);
          op0 = force_reg (Pmode, op0);
          mark_reg_pointer (op0, 128);
          mark_reg_pointer (op0, 128);
        }
        }
      else if (GET_CODE (op0) != REG)
      else if (GET_CODE (op0) != REG)
        op0 = force_reg (Pmode, op0);
        op0 = force_reg (Pmode, op0);
      if (ALIGNED_SYMBOL_REF_P (op1))
      if (ALIGNED_SYMBOL_REF_P (op1))
        {
        {
          op1 = force_reg (Pmode, op1);
          op1 = force_reg (Pmode, op1);
          mark_reg_pointer (op1, 128);
          mark_reg_pointer (op1, 128);
        }
        }
      else if (GET_CODE (op1) != REG)
      else if (GET_CODE (op1) != REG)
        op1 = force_reg (Pmode, op1);
        op1 = force_reg (Pmode, op1);
      x = gen_rtx_PLUS (Pmode, op0, op1);
      x = gen_rtx_PLUS (Pmode, op0, op1);
    }
    }
  return x;
  return x;
}
}
 
 
/* Like spu_legitimate_address, except with named address support.  */
/* Like spu_legitimate_address, except with named address support.  */
static rtx
static rtx
spu_addr_space_legitimize_address (rtx x, rtx oldx, enum machine_mode mode,
spu_addr_space_legitimize_address (rtx x, rtx oldx, enum machine_mode mode,
                                   addr_space_t as)
                                   addr_space_t as)
{
{
  if (as != ADDR_SPACE_GENERIC)
  if (as != ADDR_SPACE_GENERIC)
    return x;
    return x;
 
 
  return spu_legitimize_address (x, oldx, mode);
  return spu_legitimize_address (x, oldx, mode);
}
}
 
 
/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
static tree
static tree
spu_handle_fndecl_attribute (tree * node,
spu_handle_fndecl_attribute (tree * node,
                             tree name,
                             tree name,
                             tree args ATTRIBUTE_UNUSED,
                             tree args ATTRIBUTE_UNUSED,
                             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 (0, "%qE attribute only applies to functions",
      warning (0, "%qE attribute only applies to functions",
               name);
               name);
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* Handle the "vector" attribute.  */
/* Handle the "vector" attribute.  */
static tree
static tree
spu_handle_vector_attribute (tree * node, tree name,
spu_handle_vector_attribute (tree * node, tree name,
                             tree args ATTRIBUTE_UNUSED,
                             tree args ATTRIBUTE_UNUSED,
                             int flags ATTRIBUTE_UNUSED, bool * no_add_attrs)
                             int flags ATTRIBUTE_UNUSED, bool * no_add_attrs)
{
{
  tree type = *node, result = NULL_TREE;
  tree type = *node, result = NULL_TREE;
  enum machine_mode mode;
  enum machine_mode mode;
  int unsigned_p;
  int unsigned_p;
 
 
  while (POINTER_TYPE_P (type)
  while (POINTER_TYPE_P (type)
         || TREE_CODE (type) == FUNCTION_TYPE
         || TREE_CODE (type) == FUNCTION_TYPE
         || TREE_CODE (type) == METHOD_TYPE || TREE_CODE (type) == ARRAY_TYPE)
         || TREE_CODE (type) == METHOD_TYPE || TREE_CODE (type) == ARRAY_TYPE)
    type = TREE_TYPE (type);
    type = TREE_TYPE (type);
 
 
  mode = TYPE_MODE (type);
  mode = TYPE_MODE (type);
 
 
  unsigned_p = TYPE_UNSIGNED (type);
  unsigned_p = TYPE_UNSIGNED (type);
  switch (mode)
  switch (mode)
    {
    {
    case DImode:
    case DImode:
      result = (unsigned_p ? unsigned_V2DI_type_node : V2DI_type_node);
      result = (unsigned_p ? unsigned_V2DI_type_node : V2DI_type_node);
      break;
      break;
    case SImode:
    case SImode:
      result = (unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node);
      result = (unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node);
      break;
      break;
    case HImode:
    case HImode:
      result = (unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node);
      result = (unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node);
      break;
      break;
    case QImode:
    case QImode:
      result = (unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node);
      result = (unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node);
      break;
      break;
    case SFmode:
    case SFmode:
      result = V4SF_type_node;
      result = V4SF_type_node;
      break;
      break;
    case DFmode:
    case DFmode:
      result = V2DF_type_node;
      result = V2DF_type_node;
      break;
      break;
    default:
    default:
      break;
      break;
    }
    }
 
 
  /* Propagate qualifiers attached to the element type
  /* Propagate qualifiers attached to the element type
     onto the vector type.  */
     onto the vector type.  */
  if (result && result != type && TYPE_QUALS (type))
  if (result && result != type && TYPE_QUALS (type))
    result = build_qualified_type (result, TYPE_QUALS (type));
    result = build_qualified_type (result, TYPE_QUALS (type));
 
 
  *no_add_attrs = true;         /* No need to hang on to the attribute.  */
  *no_add_attrs = true;         /* No need to hang on to the attribute.  */
 
 
  if (!result)
  if (!result)
    warning (0, "%qE attribute ignored", name);
    warning (0, "%qE attribute ignored", name);
  else
  else
    *node = lang_hooks.types.reconstruct_complex_type (*node, result);
    *node = lang_hooks.types.reconstruct_complex_type (*node, result);
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* Return nonzero if FUNC is a naked function.  */
/* Return nonzero if FUNC is a naked function.  */
static int
static int
spu_naked_function_p (tree func)
spu_naked_function_p (tree func)
{
{
  tree a;
  tree a;
 
 
  if (TREE_CODE (func) != FUNCTION_DECL)
  if (TREE_CODE (func) != FUNCTION_DECL)
    abort ();
    abort ();
 
 
  a = lookup_attribute ("naked", DECL_ATTRIBUTES (func));
  a = lookup_attribute ("naked", DECL_ATTRIBUTES (func));
  return a != NULL_TREE;
  return a != NULL_TREE;
}
}
 
 
int
int
spu_initial_elimination_offset (int from, int to)
spu_initial_elimination_offset (int from, int to)
{
{
  int saved_regs_size = spu_saved_regs_size ();
  int saved_regs_size = spu_saved_regs_size ();
  int sp_offset = 0;
  int sp_offset = 0;
  if (!current_function_is_leaf || crtl->outgoing_args_size
  if (!current_function_is_leaf || crtl->outgoing_args_size
      || get_frame_size () || saved_regs_size)
      || get_frame_size () || saved_regs_size)
    sp_offset = STACK_POINTER_OFFSET;
    sp_offset = STACK_POINTER_OFFSET;
  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return get_frame_size () + crtl->outgoing_args_size + sp_offset;
    return get_frame_size () + crtl->outgoing_args_size + sp_offset;
  else if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
  else if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
    return get_frame_size ();
    return get_frame_size ();
  else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return sp_offset + crtl->outgoing_args_size
    return sp_offset + crtl->outgoing_args_size
      + get_frame_size () + saved_regs_size + STACK_POINTER_OFFSET;
      + get_frame_size () + saved_regs_size + STACK_POINTER_OFFSET;
  else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
  else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
    return get_frame_size () + saved_regs_size + sp_offset;
    return get_frame_size () + saved_regs_size + sp_offset;
  else
  else
    gcc_unreachable ();
    gcc_unreachable ();
}
}
 
 
rtx
rtx
spu_function_value (const_tree type, const_tree func ATTRIBUTE_UNUSED)
spu_function_value (const_tree type, const_tree func ATTRIBUTE_UNUSED)
{
{
  enum machine_mode mode = TYPE_MODE (type);
  enum machine_mode mode = TYPE_MODE (type);
  int byte_size = ((mode == BLKmode)
  int byte_size = ((mode == BLKmode)
                   ? int_size_in_bytes (type) : GET_MODE_SIZE (mode));
                   ? int_size_in_bytes (type) : GET_MODE_SIZE (mode));
 
 
  /* Make sure small structs are left justified in a register. */
  /* Make sure small structs are left justified in a register. */
  if ((mode == BLKmode || (type && AGGREGATE_TYPE_P (type)))
  if ((mode == BLKmode || (type && AGGREGATE_TYPE_P (type)))
      && byte_size <= UNITS_PER_WORD * MAX_REGISTER_RETURN && byte_size > 0)
      && byte_size <= UNITS_PER_WORD * MAX_REGISTER_RETURN && byte_size > 0)
    {
    {
      enum machine_mode smode;
      enum machine_mode smode;
      rtvec v;
      rtvec v;
      int i;
      int i;
      int nregs = (byte_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
      int nregs = (byte_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
      int n = byte_size / UNITS_PER_WORD;
      int n = byte_size / UNITS_PER_WORD;
      v = rtvec_alloc (nregs);
      v = rtvec_alloc (nregs);
      for (i = 0; i < n; i++)
      for (i = 0; i < n; i++)
        {
        {
          RTVEC_ELT (v, i) = gen_rtx_EXPR_LIST (VOIDmode,
          RTVEC_ELT (v, i) = gen_rtx_EXPR_LIST (VOIDmode,
                                                gen_rtx_REG (TImode,
                                                gen_rtx_REG (TImode,
                                                             FIRST_RETURN_REGNUM
                                                             FIRST_RETURN_REGNUM
                                                             + i),
                                                             + i),
                                                GEN_INT (UNITS_PER_WORD * i));
                                                GEN_INT (UNITS_PER_WORD * i));
          byte_size -= UNITS_PER_WORD;
          byte_size -= UNITS_PER_WORD;
        }
        }
 
 
      if (n < nregs)
      if (n < nregs)
        {
        {
          if (byte_size < 4)
          if (byte_size < 4)
            byte_size = 4;
            byte_size = 4;
          smode =
          smode =
            smallest_mode_for_size (byte_size * BITS_PER_UNIT, MODE_INT);
            smallest_mode_for_size (byte_size * BITS_PER_UNIT, MODE_INT);
          RTVEC_ELT (v, n) =
          RTVEC_ELT (v, n) =
            gen_rtx_EXPR_LIST (VOIDmode,
            gen_rtx_EXPR_LIST (VOIDmode,
                               gen_rtx_REG (smode, FIRST_RETURN_REGNUM + n),
                               gen_rtx_REG (smode, FIRST_RETURN_REGNUM + n),
                               GEN_INT (UNITS_PER_WORD * n));
                               GEN_INT (UNITS_PER_WORD * n));
        }
        }
      return gen_rtx_PARALLEL (mode, v);
      return gen_rtx_PARALLEL (mode, v);
    }
    }
  return gen_rtx_REG (mode, FIRST_RETURN_REGNUM);
  return gen_rtx_REG (mode, FIRST_RETURN_REGNUM);
}
}
 
 
rtx
rtx
spu_function_arg (CUMULATIVE_ARGS cum,
spu_function_arg (CUMULATIVE_ARGS cum,
                  enum machine_mode mode,
                  enum machine_mode mode,
                  tree type, int named ATTRIBUTE_UNUSED)
                  tree type, int named ATTRIBUTE_UNUSED)
{
{
  int byte_size;
  int byte_size;
 
 
  if (cum >= MAX_REGISTER_ARGS)
  if (cum >= MAX_REGISTER_ARGS)
    return 0;
    return 0;
 
 
  byte_size = ((mode == BLKmode)
  byte_size = ((mode == BLKmode)
               ? int_size_in_bytes (type) : GET_MODE_SIZE (mode));
               ? int_size_in_bytes (type) : GET_MODE_SIZE (mode));
 
 
  /* The ABI does not allow parameters to be passed partially in
  /* The ABI does not allow parameters to be passed partially in
     reg and partially in stack. */
     reg and partially in stack. */
  if ((cum + (byte_size + 15) / 16) > MAX_REGISTER_ARGS)
  if ((cum + (byte_size + 15) / 16) > MAX_REGISTER_ARGS)
    return 0;
    return 0;
 
 
  /* Make sure small structs are left justified in a register. */
  /* Make sure small structs are left justified in a register. */
  if ((mode == BLKmode || (type && AGGREGATE_TYPE_P (type)))
  if ((mode == BLKmode || (type && AGGREGATE_TYPE_P (type)))
      && byte_size < UNITS_PER_WORD && byte_size > 0)
      && byte_size < UNITS_PER_WORD && byte_size > 0)
    {
    {
      enum machine_mode smode;
      enum machine_mode smode;
      rtx gr_reg;
      rtx gr_reg;
      if (byte_size < 4)
      if (byte_size < 4)
        byte_size = 4;
        byte_size = 4;
      smode = smallest_mode_for_size (byte_size * BITS_PER_UNIT, MODE_INT);
      smode = smallest_mode_for_size (byte_size * BITS_PER_UNIT, MODE_INT);
      gr_reg = gen_rtx_EXPR_LIST (VOIDmode,
      gr_reg = gen_rtx_EXPR_LIST (VOIDmode,
                                  gen_rtx_REG (smode, FIRST_ARG_REGNUM + cum),
                                  gen_rtx_REG (smode, FIRST_ARG_REGNUM + cum),
                                  const0_rtx);
                                  const0_rtx);
      return gen_rtx_PARALLEL (mode, gen_rtvec (1, gr_reg));
      return gen_rtx_PARALLEL (mode, gen_rtvec (1, gr_reg));
    }
    }
  else
  else
    return gen_rtx_REG (mode, FIRST_ARG_REGNUM + cum);
    return gen_rtx_REG (mode, FIRST_ARG_REGNUM + cum);
}
}
 
 
/* Variable sized types are passed by reference.  */
/* Variable sized types are passed by reference.  */
static bool
static bool
spu_pass_by_reference (CUMULATIVE_ARGS * cum ATTRIBUTE_UNUSED,
spu_pass_by_reference (CUMULATIVE_ARGS * cum ATTRIBUTE_UNUSED,
                       enum machine_mode mode ATTRIBUTE_UNUSED,
                       enum machine_mode mode ATTRIBUTE_UNUSED,
                       const_tree type, bool named ATTRIBUTE_UNUSED)
                       const_tree type, bool named ATTRIBUTE_UNUSED)
{
{
  return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
  return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
}
}


 
 
/* Var args. */
/* Var args. */
 
 
/* Create and return the va_list datatype.
/* Create and return the va_list datatype.
 
 
   On SPU, va_list is an array type equivalent to
   On SPU, va_list is an array type equivalent to
 
 
      typedef struct __va_list_tag
      typedef struct __va_list_tag
        {
        {
            void *__args __attribute__((__aligned(16)));
            void *__args __attribute__((__aligned(16)));
            void *__skip __attribute__((__aligned(16)));
            void *__skip __attribute__((__aligned(16)));
 
 
        } va_list[1];
        } va_list[1];
 
 
   where __args points to the arg that will be returned by the next
   where __args points to the arg that will be returned by the next
   va_arg(), and __skip points to the previous stack frame such that
   va_arg(), and __skip points to the previous stack frame such that
   when __args == __skip we should advance __args by 32 bytes. */
   when __args == __skip we should advance __args by 32 bytes. */
static tree
static tree
spu_build_builtin_va_list (void)
spu_build_builtin_va_list (void)
{
{
  tree f_args, f_skip, record, type_decl;
  tree f_args, f_skip, record, type_decl;
  bool owp;
  bool owp;
 
 
  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
 
 
  type_decl =
  type_decl =
    build_decl (BUILTINS_LOCATION,
    build_decl (BUILTINS_LOCATION,
                TYPE_DECL, get_identifier ("__va_list_tag"), record);
                TYPE_DECL, get_identifier ("__va_list_tag"), record);
 
 
  f_args = build_decl (BUILTINS_LOCATION,
  f_args = build_decl (BUILTINS_LOCATION,
                       FIELD_DECL, get_identifier ("__args"), ptr_type_node);
                       FIELD_DECL, get_identifier ("__args"), ptr_type_node);
  f_skip = build_decl (BUILTINS_LOCATION,
  f_skip = build_decl (BUILTINS_LOCATION,
                       FIELD_DECL, get_identifier ("__skip"), ptr_type_node);
                       FIELD_DECL, get_identifier ("__skip"), ptr_type_node);
 
 
  DECL_FIELD_CONTEXT (f_args) = record;
  DECL_FIELD_CONTEXT (f_args) = record;
  DECL_ALIGN (f_args) = 128;
  DECL_ALIGN (f_args) = 128;
  DECL_USER_ALIGN (f_args) = 1;
  DECL_USER_ALIGN (f_args) = 1;
 
 
  DECL_FIELD_CONTEXT (f_skip) = record;
  DECL_FIELD_CONTEXT (f_skip) = record;
  DECL_ALIGN (f_skip) = 128;
  DECL_ALIGN (f_skip) = 128;
  DECL_USER_ALIGN (f_skip) = 1;
  DECL_USER_ALIGN (f_skip) = 1;
 
 
  TREE_CHAIN (record) = type_decl;
  TREE_CHAIN (record) = type_decl;
  TYPE_NAME (record) = type_decl;
  TYPE_NAME (record) = type_decl;
  TYPE_FIELDS (record) = f_args;
  TYPE_FIELDS (record) = f_args;
  TREE_CHAIN (f_args) = f_skip;
  TREE_CHAIN (f_args) = f_skip;
 
 
  /* We know this is being padded and we want it too.  It is an internal
  /* We know this is being padded and we want it too.  It is an internal
     type so hide the warnings from the user. */
     type so hide the warnings from the user. */
  owp = warn_padded;
  owp = warn_padded;
  warn_padded = false;
  warn_padded = false;
 
 
  layout_type (record);
  layout_type (record);
 
 
  warn_padded = owp;
  warn_padded = owp;
 
 
  /* The correct type is an array type of one element.  */
  /* The correct type is an array type of one element.  */
  return build_array_type (record, build_index_type (size_zero_node));
  return build_array_type (record, build_index_type (size_zero_node));
}
}
 
 
/* Implement va_start by filling the va_list structure VALIST.
/* Implement va_start by filling the va_list structure VALIST.
   NEXTARG points to the first anonymous stack argument.
   NEXTARG points to the first anonymous stack argument.
 
 
   The following global variables are used to initialize
   The following global variables are used to initialize
   the va_list structure:
   the va_list structure:
 
 
     crtl->args.info;
     crtl->args.info;
       the CUMULATIVE_ARGS for this function
       the CUMULATIVE_ARGS for this function
 
 
     crtl->args.arg_offset_rtx:
     crtl->args.arg_offset_rtx:
       holds the offset of the first anonymous stack argument
       holds the offset of the first anonymous stack argument
       (relative to the virtual arg pointer).  */
       (relative to the virtual arg pointer).  */
 
 
static void
static void
spu_va_start (tree valist, rtx nextarg)
spu_va_start (tree valist, rtx nextarg)
{
{
  tree f_args, f_skip;
  tree f_args, f_skip;
  tree args, skip, t;
  tree args, skip, t;
 
 
  f_args = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
  f_args = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
  f_skip = TREE_CHAIN (f_args);
  f_skip = TREE_CHAIN (f_args);
 
 
  valist = build_va_arg_indirect_ref (valist);
  valist = build_va_arg_indirect_ref (valist);
  args =
  args =
    build3 (COMPONENT_REF, TREE_TYPE (f_args), valist, f_args, NULL_TREE);
    build3 (COMPONENT_REF, TREE_TYPE (f_args), valist, f_args, NULL_TREE);
  skip =
  skip =
    build3 (COMPONENT_REF, TREE_TYPE (f_skip), valist, f_skip, NULL_TREE);
    build3 (COMPONENT_REF, TREE_TYPE (f_skip), valist, f_skip, NULL_TREE);
 
 
  /* Find the __args area.  */
  /* Find the __args area.  */
  t = make_tree (TREE_TYPE (args), nextarg);
  t = make_tree (TREE_TYPE (args), nextarg);
  if (crtl->args.pretend_args_size > 0)
  if (crtl->args.pretend_args_size > 0)
    t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (args), t,
    t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (args), t,
                size_int (-STACK_POINTER_OFFSET));
                size_int (-STACK_POINTER_OFFSET));
  t = build2 (MODIFY_EXPR, TREE_TYPE (args), args, t);
  t = build2 (MODIFY_EXPR, TREE_TYPE (args), args, t);
  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);
 
 
  /* Find the __skip area.  */
  /* Find the __skip area.  */
  t = make_tree (TREE_TYPE (skip), virtual_incoming_args_rtx);
  t = make_tree (TREE_TYPE (skip), virtual_incoming_args_rtx);
  t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (skip), t,
  t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (skip), t,
              size_int (crtl->args.pretend_args_size
              size_int (crtl->args.pretend_args_size
                         - STACK_POINTER_OFFSET));
                         - STACK_POINTER_OFFSET));
  t = build2 (MODIFY_EXPR, TREE_TYPE (skip), skip, t);
  t = build2 (MODIFY_EXPR, TREE_TYPE (skip), skip, t);
  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);
}
}
 
 
/* Gimplify va_arg by updating the va_list structure
/* Gimplify va_arg by updating the va_list structure
   VALIST as required to retrieve an argument of type
   VALIST as required to retrieve an argument of type
   TYPE, and returning that argument.
   TYPE, and returning that argument.
 
 
   ret = va_arg(VALIST, TYPE);
   ret = va_arg(VALIST, TYPE);
 
 
   generates code equivalent to:
   generates code equivalent to:
 
 
    paddedsize = (sizeof(TYPE) + 15) & -16;
    paddedsize = (sizeof(TYPE) + 15) & -16;
    if (VALIST.__args + paddedsize > VALIST.__skip
    if (VALIST.__args + paddedsize > VALIST.__skip
        && VALIST.__args <= VALIST.__skip)
        && VALIST.__args <= VALIST.__skip)
      addr = VALIST.__skip + 32;
      addr = VALIST.__skip + 32;
    else
    else
      addr = VALIST.__args;
      addr = VALIST.__args;
    VALIST.__args = addr + paddedsize;
    VALIST.__args = addr + paddedsize;
    ret = *(TYPE *)addr;
    ret = *(TYPE *)addr;
 */
 */
static tree
static tree
spu_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
spu_gimplify_va_arg_expr (tree valist, tree type, gimple_seq * pre_p,
                          gimple_seq * post_p ATTRIBUTE_UNUSED)
                          gimple_seq * post_p ATTRIBUTE_UNUSED)
{
{
  tree f_args, f_skip;
  tree f_args, f_skip;
  tree args, skip;
  tree args, skip;
  HOST_WIDE_INT size, rsize;
  HOST_WIDE_INT size, rsize;
  tree paddedsize, addr, tmp;
  tree paddedsize, addr, tmp;
  bool pass_by_reference_p;
  bool pass_by_reference_p;
 
 
  f_args = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
  f_args = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
  f_skip = TREE_CHAIN (f_args);
  f_skip = TREE_CHAIN (f_args);
 
 
  valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
  valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
  args =
  args =
    build3 (COMPONENT_REF, TREE_TYPE (f_args), valist, f_args, NULL_TREE);
    build3 (COMPONENT_REF, TREE_TYPE (f_args), valist, f_args, NULL_TREE);
  skip =
  skip =
    build3 (COMPONENT_REF, TREE_TYPE (f_skip), valist, f_skip, NULL_TREE);
    build3 (COMPONENT_REF, TREE_TYPE (f_skip), valist, f_skip, NULL_TREE);
 
 
  addr = create_tmp_var (ptr_type_node, "va_arg");
  addr = create_tmp_var (ptr_type_node, "va_arg");
 
 
  /* if an object is dynamically sized, a pointer to it is passed
  /* if an object is dynamically sized, a pointer to it is passed
     instead of the object itself. */
     instead of the object itself. */
  pass_by_reference_p = spu_pass_by_reference (NULL, TYPE_MODE (type), type,
  pass_by_reference_p = spu_pass_by_reference (NULL, TYPE_MODE (type), type,
                                               false);
                                               false);
  if (pass_by_reference_p)
  if (pass_by_reference_p)
    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) * UNITS_PER_WORD;
  rsize = ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD) * UNITS_PER_WORD;
 
 
  /* build conditional expression to calculate addr. The expression
  /* build conditional expression to calculate addr. The expression
     will be gimplified later. */
     will be gimplified later. */
  paddedsize = size_int (rsize);
  paddedsize = size_int (rsize);
  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node, unshare_expr (args), paddedsize);
  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node, unshare_expr (args), paddedsize);
  tmp = build2 (TRUTH_AND_EXPR, boolean_type_node,
  tmp = build2 (TRUTH_AND_EXPR, boolean_type_node,
                build2 (GT_EXPR, boolean_type_node, tmp, unshare_expr (skip)),
                build2 (GT_EXPR, boolean_type_node, tmp, unshare_expr (skip)),
                build2 (LE_EXPR, boolean_type_node, unshare_expr (args),
                build2 (LE_EXPR, boolean_type_node, unshare_expr (args),
                unshare_expr (skip)));
                unshare_expr (skip)));
 
 
  tmp = build3 (COND_EXPR, ptr_type_node, tmp,
  tmp = build3 (COND_EXPR, ptr_type_node, tmp,
                build2 (POINTER_PLUS_EXPR, ptr_type_node, unshare_expr (skip),
                build2 (POINTER_PLUS_EXPR, ptr_type_node, unshare_expr (skip),
                        size_int (32)), unshare_expr (args));
                        size_int (32)), unshare_expr (args));
 
 
  gimplify_assign (addr, tmp, pre_p);
  gimplify_assign (addr, tmp, pre_p);
 
 
  /* update VALIST.__args */
  /* update VALIST.__args */
  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node, addr, paddedsize);
  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node, addr, paddedsize);
  gimplify_assign (unshare_expr (args), tmp, pre_p);
  gimplify_assign (unshare_expr (args), tmp, pre_p);
 
 
  addr = fold_convert (build_pointer_type_for_mode (type, ptr_mode, true),
  addr = fold_convert (build_pointer_type_for_mode (type, ptr_mode, true),
                       addr);
                       addr);
 
 
  if (pass_by_reference_p)
  if (pass_by_reference_p)
    addr = build_va_arg_indirect_ref (addr);
    addr = build_va_arg_indirect_ref (addr);
 
 
  return build_va_arg_indirect_ref (addr);
  return build_va_arg_indirect_ref (addr);
}
}
 
 
/* Save parameter registers starting with the register that corresponds
/* Save parameter registers starting with the register that corresponds
   to the first unnamed parameters.  If the first unnamed parameter is
   to the first unnamed parameters.  If the first unnamed parameter is
   in the stack then save no registers.  Set pretend_args_size to the
   in the stack then save no registers.  Set pretend_args_size to the
   amount of space needed to save the registers. */
   amount of space needed to save the registers. */
void
void
spu_setup_incoming_varargs (CUMULATIVE_ARGS * cum, enum machine_mode mode,
spu_setup_incoming_varargs (CUMULATIVE_ARGS * cum, enum machine_mode mode,
                            tree type, int *pretend_size, int no_rtl)
                            tree type, int *pretend_size, int no_rtl)
{
{
  if (!no_rtl)
  if (!no_rtl)
    {
    {
      rtx tmp;
      rtx tmp;
      int regno;
      int regno;
      int offset;
      int offset;
      int ncum = *cum;
      int ncum = *cum;
 
 
      /* cum currently points to the last named argument, we want to
      /* cum currently points to the last named argument, we want to
         start at the next argument. */
         start at the next argument. */
      FUNCTION_ARG_ADVANCE (ncum, mode, type, 1);
      FUNCTION_ARG_ADVANCE (ncum, mode, type, 1);
 
 
      offset = -STACK_POINTER_OFFSET;
      offset = -STACK_POINTER_OFFSET;
      for (regno = ncum; regno < MAX_REGISTER_ARGS; regno++)
      for (regno = ncum; regno < MAX_REGISTER_ARGS; regno++)
        {
        {
          tmp = gen_frame_mem (V4SImode,
          tmp = gen_frame_mem (V4SImode,
                               plus_constant (virtual_incoming_args_rtx,
                               plus_constant (virtual_incoming_args_rtx,
                                              offset));
                                              offset));
          emit_move_insn (tmp,
          emit_move_insn (tmp,
                          gen_rtx_REG (V4SImode, FIRST_ARG_REGNUM + regno));
                          gen_rtx_REG (V4SImode, FIRST_ARG_REGNUM + regno));
          offset += 16;
          offset += 16;
        }
        }
      *pretend_size = offset + STACK_POINTER_OFFSET;
      *pretend_size = offset + STACK_POINTER_OFFSET;
    }
    }
}
}


void
void
spu_conditional_register_usage (void)
spu_conditional_register_usage (void)
{
{
  if (flag_pic)
  if (flag_pic)
    {
    {
      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
    }
    }
}
}
 
 
/* This is called any time we inspect the alignment of a register for
/* This is called any time we inspect the alignment of a register for
   addresses.  */
   addresses.  */
static int
static int
reg_aligned_for_addr (rtx x)
reg_aligned_for_addr (rtx x)
{
{
  int regno =
  int regno =
    REGNO (x) < FIRST_PSEUDO_REGISTER ? ORIGINAL_REGNO (x) : REGNO (x);
    REGNO (x) < FIRST_PSEUDO_REGISTER ? ORIGINAL_REGNO (x) : REGNO (x);
  return REGNO_POINTER_ALIGN (regno) >= 128;
  return REGNO_POINTER_ALIGN (regno) >= 128;
}
}
 
 
/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
   into its SYMBOL_REF_FLAGS.  */
   into its SYMBOL_REF_FLAGS.  */
static void
static void
spu_encode_section_info (tree decl, rtx rtl, int first)
spu_encode_section_info (tree decl, rtx rtl, int first)
{
{
  default_encode_section_info (decl, rtl, first);
  default_encode_section_info (decl, rtl, first);
 
 
  /* If a variable has a forced alignment to < 16 bytes, mark it with
  /* If a variable has a forced alignment to < 16 bytes, mark it with
     SYMBOL_FLAG_ALIGN1.  */
     SYMBOL_FLAG_ALIGN1.  */
  if (TREE_CODE (decl) == VAR_DECL
  if (TREE_CODE (decl) == VAR_DECL
      && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 128)
      && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 128)
    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
}
}
 
 
/* Return TRUE if we are certain the mem refers to a complete object
/* Return TRUE if we are certain the mem refers to a complete object
   which is both 16-byte aligned and padded to a 16-byte boundary.  This
   which is both 16-byte aligned and padded to a 16-byte boundary.  This
   would make it safe to store with a single instruction.
   would make it safe to store with a single instruction.
   We guarantee the alignment and padding for static objects by aligning
   We guarantee the alignment and padding for static objects by aligning
   all of them to 16-bytes. (DATA_ALIGNMENT and CONSTANT_ALIGNMENT.)
   all of them to 16-bytes. (DATA_ALIGNMENT and CONSTANT_ALIGNMENT.)
   FIXME: We currently cannot guarantee this for objects on the stack
   FIXME: We currently cannot guarantee this for objects on the stack
   because assign_parm_setup_stack calls assign_stack_local with the
   because assign_parm_setup_stack calls assign_stack_local with the
   alignment of the parameter mode and in that case the alignment never
   alignment of the parameter mode and in that case the alignment never
   gets adjusted by LOCAL_ALIGNMENT. */
   gets adjusted by LOCAL_ALIGNMENT. */
static int
static int
store_with_one_insn_p (rtx mem)
store_with_one_insn_p (rtx mem)
{
{
  enum machine_mode mode = GET_MODE (mem);
  enum machine_mode mode = GET_MODE (mem);
  rtx addr = XEXP (mem, 0);
  rtx addr = XEXP (mem, 0);
  if (mode == BLKmode)
  if (mode == BLKmode)
    return 0;
    return 0;
  if (GET_MODE_SIZE (mode) >= 16)
  if (GET_MODE_SIZE (mode) >= 16)
    return 1;
    return 1;
  /* Only static objects. */
  /* Only static objects. */
  if (GET_CODE (addr) == SYMBOL_REF)
  if (GET_CODE (addr) == SYMBOL_REF)
    {
    {
      /* We use the associated declaration to make sure the access is
      /* We use the associated declaration to make sure the access is
         referring to the whole object.
         referring to the whole object.
         We check both MEM_EXPR and and SYMBOL_REF_DECL.  I'm not sure
         We check both MEM_EXPR and and SYMBOL_REF_DECL.  I'm not sure
         if it is necessary.  Will there be cases where one exists, and
         if it is necessary.  Will there be cases where one exists, and
         the other does not?  Will there be cases where both exist, but
         the other does not?  Will there be cases where both exist, but
         have different types?  */
         have different types?  */
      tree decl = MEM_EXPR (mem);
      tree decl = MEM_EXPR (mem);
      if (decl
      if (decl
          && TREE_CODE (decl) == VAR_DECL
          && TREE_CODE (decl) == VAR_DECL
          && GET_MODE (mem) == TYPE_MODE (TREE_TYPE (decl)))
          && GET_MODE (mem) == TYPE_MODE (TREE_TYPE (decl)))
        return 1;
        return 1;
      decl = SYMBOL_REF_DECL (addr);
      decl = SYMBOL_REF_DECL (addr);
      if (decl
      if (decl
          && TREE_CODE (decl) == VAR_DECL
          && TREE_CODE (decl) == VAR_DECL
          && GET_MODE (mem) == TYPE_MODE (TREE_TYPE (decl)))
          && GET_MODE (mem) == TYPE_MODE (TREE_TYPE (decl)))
        return 1;
        return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Return 1 when the address is not valid for a simple load and store as
/* Return 1 when the address is not valid for a simple load and store as
   required by the '_mov*' patterns.   We could make this less strict
   required by the '_mov*' patterns.   We could make this less strict
   for loads, but we prefer mem's to look the same so they are more
   for loads, but we prefer mem's to look the same so they are more
   likely to be merged.  */
   likely to be merged.  */
static int
static int
address_needs_split (rtx mem)
address_needs_split (rtx mem)
{
{
  if (GET_MODE_SIZE (GET_MODE (mem)) < 16
  if (GET_MODE_SIZE (GET_MODE (mem)) < 16
      && (GET_MODE_SIZE (GET_MODE (mem)) < 4
      && (GET_MODE_SIZE (GET_MODE (mem)) < 4
          || !(store_with_one_insn_p (mem)
          || !(store_with_one_insn_p (mem)
               || mem_is_padded_component_ref (mem))))
               || mem_is_padded_component_ref (mem))))
    return 1;
    return 1;
 
 
  return 0;
  return 0;
}
}
 
 
static GTY(()) rtx cache_fetch;           /* __cache_fetch function */
static GTY(()) rtx cache_fetch;           /* __cache_fetch function */
static GTY(()) rtx cache_fetch_dirty;     /* __cache_fetch_dirty function */
static GTY(()) rtx cache_fetch_dirty;     /* __cache_fetch_dirty function */
static alias_set_type ea_alias_set = -1;  /* alias set for __ea memory */
static alias_set_type ea_alias_set = -1;  /* alias set for __ea memory */
 
 
/* MEM is known to be an __ea qualified memory access.  Emit a call to
/* MEM is known to be an __ea qualified memory access.  Emit a call to
   fetch the ppu memory to local store, and return its address in local
   fetch the ppu memory to local store, and return its address in local
   store.  */
   store.  */
 
 
static void
static void
ea_load_store (rtx mem, bool is_store, rtx ea_addr, rtx data_addr)
ea_load_store (rtx mem, bool is_store, rtx ea_addr, rtx data_addr)
{
{
  if (is_store)
  if (is_store)
    {
    {
      rtx ndirty = GEN_INT (GET_MODE_SIZE (GET_MODE (mem)));
      rtx ndirty = GEN_INT (GET_MODE_SIZE (GET_MODE (mem)));
      if (!cache_fetch_dirty)
      if (!cache_fetch_dirty)
        cache_fetch_dirty = init_one_libfunc ("__cache_fetch_dirty");
        cache_fetch_dirty = init_one_libfunc ("__cache_fetch_dirty");
      emit_library_call_value (cache_fetch_dirty, data_addr, LCT_NORMAL, Pmode,
      emit_library_call_value (cache_fetch_dirty, data_addr, LCT_NORMAL, Pmode,
                               2, ea_addr, EAmode, ndirty, SImode);
                               2, ea_addr, EAmode, ndirty, SImode);
    }
    }
  else
  else
    {
    {
      if (!cache_fetch)
      if (!cache_fetch)
        cache_fetch = init_one_libfunc ("__cache_fetch");
        cache_fetch = init_one_libfunc ("__cache_fetch");
      emit_library_call_value (cache_fetch, data_addr, LCT_NORMAL, Pmode,
      emit_library_call_value (cache_fetch, data_addr, LCT_NORMAL, Pmode,
                               1, ea_addr, EAmode);
                               1, ea_addr, EAmode);
    }
    }
}
}
 
 
/* Like ea_load_store, but do the cache tag comparison and, for stores,
/* Like ea_load_store, but do the cache tag comparison and, for stores,
   dirty bit marking, inline.
   dirty bit marking, inline.
 
 
   The cache control data structure is an array of
   The cache control data structure is an array of
 
 
   struct __cache_tag_array
   struct __cache_tag_array
     {
     {
        unsigned int tag_lo[4];
        unsigned int tag_lo[4];
        unsigned int tag_hi[4];
        unsigned int tag_hi[4];
        void *data_pointer[4];
        void *data_pointer[4];
        int reserved[4];
        int reserved[4];
        vector unsigned short dirty_bits[4];
        vector unsigned short dirty_bits[4];
     }  */
     }  */
 
 
static void
static void
ea_load_store_inline (rtx mem, bool is_store, rtx ea_addr, rtx data_addr)
ea_load_store_inline (rtx mem, bool is_store, rtx ea_addr, rtx data_addr)
{
{
  rtx ea_addr_si;
  rtx ea_addr_si;
  HOST_WIDE_INT v;
  HOST_WIDE_INT v;
  rtx tag_size_sym = gen_rtx_SYMBOL_REF (Pmode, "__cache_tag_array_size");
  rtx tag_size_sym = gen_rtx_SYMBOL_REF (Pmode, "__cache_tag_array_size");
  rtx tag_arr_sym = gen_rtx_SYMBOL_REF (Pmode, "__cache_tag_array");
  rtx tag_arr_sym = gen_rtx_SYMBOL_REF (Pmode, "__cache_tag_array");
  rtx index_mask = gen_reg_rtx (SImode);
  rtx index_mask = gen_reg_rtx (SImode);
  rtx tag_arr = gen_reg_rtx (Pmode);
  rtx tag_arr = gen_reg_rtx (Pmode);
  rtx splat_mask = gen_reg_rtx (TImode);
  rtx splat_mask = gen_reg_rtx (TImode);
  rtx splat = gen_reg_rtx (V4SImode);
  rtx splat = gen_reg_rtx (V4SImode);
  rtx splat_hi = NULL_RTX;
  rtx splat_hi = NULL_RTX;
  rtx tag_index = gen_reg_rtx (Pmode);
  rtx tag_index = gen_reg_rtx (Pmode);
  rtx block_off = gen_reg_rtx (SImode);
  rtx block_off = gen_reg_rtx (SImode);
  rtx tag_addr = gen_reg_rtx (Pmode);
  rtx tag_addr = gen_reg_rtx (Pmode);
  rtx tag = gen_reg_rtx (V4SImode);
  rtx tag = gen_reg_rtx (V4SImode);
  rtx cache_tag = gen_reg_rtx (V4SImode);
  rtx cache_tag = gen_reg_rtx (V4SImode);
  rtx cache_tag_hi = NULL_RTX;
  rtx cache_tag_hi = NULL_RTX;
  rtx cache_ptrs = gen_reg_rtx (TImode);
  rtx cache_ptrs = gen_reg_rtx (TImode);
  rtx cache_ptrs_si = gen_reg_rtx (SImode);
  rtx cache_ptrs_si = gen_reg_rtx (SImode);
  rtx tag_equal = gen_reg_rtx (V4SImode);
  rtx tag_equal = gen_reg_rtx (V4SImode);
  rtx tag_equal_hi = NULL_RTX;
  rtx tag_equal_hi = NULL_RTX;
  rtx tag_eq_pack = gen_reg_rtx (V4SImode);
  rtx tag_eq_pack = gen_reg_rtx (V4SImode);
  rtx tag_eq_pack_si = gen_reg_rtx (SImode);
  rtx tag_eq_pack_si = gen_reg_rtx (SImode);
  rtx eq_index = gen_reg_rtx (SImode);
  rtx eq_index = gen_reg_rtx (SImode);
  rtx bcomp, hit_label, hit_ref, cont_label, insn;
  rtx bcomp, hit_label, hit_ref, cont_label, insn;
 
 
  if (spu_ea_model != 32)
  if (spu_ea_model != 32)
    {
    {
      splat_hi = gen_reg_rtx (V4SImode);
      splat_hi = gen_reg_rtx (V4SImode);
      cache_tag_hi = gen_reg_rtx (V4SImode);
      cache_tag_hi = gen_reg_rtx (V4SImode);
      tag_equal_hi = gen_reg_rtx (V4SImode);
      tag_equal_hi = gen_reg_rtx (V4SImode);
    }
    }
 
 
  emit_move_insn (index_mask, plus_constant (tag_size_sym, -128));
  emit_move_insn (index_mask, plus_constant (tag_size_sym, -128));
  emit_move_insn (tag_arr, tag_arr_sym);
  emit_move_insn (tag_arr, tag_arr_sym);
  v = 0x0001020300010203LL;
  v = 0x0001020300010203LL;
  emit_move_insn (splat_mask, immed_double_const (v, v, TImode));
  emit_move_insn (splat_mask, immed_double_const (v, v, TImode));
  ea_addr_si = ea_addr;
  ea_addr_si = ea_addr;
  if (spu_ea_model != 32)
  if (spu_ea_model != 32)
    ea_addr_si = convert_to_mode (SImode, ea_addr, 1);
    ea_addr_si = convert_to_mode (SImode, ea_addr, 1);
 
 
  /* tag_index = ea_addr & (tag_array_size - 128)  */
  /* tag_index = ea_addr & (tag_array_size - 128)  */
  emit_insn (gen_andsi3 (tag_index, ea_addr_si, index_mask));
  emit_insn (gen_andsi3 (tag_index, ea_addr_si, index_mask));
 
 
  /* splat ea_addr to all 4 slots.  */
  /* splat ea_addr to all 4 slots.  */
  emit_insn (gen_shufb (splat, ea_addr_si, ea_addr_si, splat_mask));
  emit_insn (gen_shufb (splat, ea_addr_si, ea_addr_si, splat_mask));
  /* Similarly for high 32 bits of ea_addr.  */
  /* Similarly for high 32 bits of ea_addr.  */
  if (spu_ea_model != 32)
  if (spu_ea_model != 32)
    emit_insn (gen_shufb (splat_hi, ea_addr, ea_addr, splat_mask));
    emit_insn (gen_shufb (splat_hi, ea_addr, ea_addr, splat_mask));
 
 
  /* block_off = ea_addr & 127  */
  /* block_off = ea_addr & 127  */
  emit_insn (gen_andsi3 (block_off, ea_addr_si, spu_const (SImode, 127)));
  emit_insn (gen_andsi3 (block_off, ea_addr_si, spu_const (SImode, 127)));
 
 
  /* tag_addr = tag_arr + tag_index  */
  /* tag_addr = tag_arr + tag_index  */
  emit_insn (gen_addsi3 (tag_addr, tag_arr, tag_index));
  emit_insn (gen_addsi3 (tag_addr, tag_arr, tag_index));
 
 
  /* Read cache tags.  */
  /* Read cache tags.  */
  emit_move_insn (cache_tag, gen_rtx_MEM (V4SImode, tag_addr));
  emit_move_insn (cache_tag, gen_rtx_MEM (V4SImode, tag_addr));
  if (spu_ea_model != 32)
  if (spu_ea_model != 32)
    emit_move_insn (cache_tag_hi, gen_rtx_MEM (V4SImode,
    emit_move_insn (cache_tag_hi, gen_rtx_MEM (V4SImode,
                                               plus_constant (tag_addr, 16)));
                                               plus_constant (tag_addr, 16)));
 
 
  /* tag = ea_addr & -128  */
  /* tag = ea_addr & -128  */
  emit_insn (gen_andv4si3 (tag, splat, spu_const (V4SImode, -128)));
  emit_insn (gen_andv4si3 (tag, splat, spu_const (V4SImode, -128)));
 
 
  /* Read all four cache data pointers.  */
  /* Read all four cache data pointers.  */
  emit_move_insn (cache_ptrs, gen_rtx_MEM (TImode,
  emit_move_insn (cache_ptrs, gen_rtx_MEM (TImode,
                                           plus_constant (tag_addr, 32)));
                                           plus_constant (tag_addr, 32)));
 
 
  /* Compare tags.  */
  /* Compare tags.  */
  emit_insn (gen_ceq_v4si (tag_equal, tag, cache_tag));
  emit_insn (gen_ceq_v4si (tag_equal, tag, cache_tag));
  if (spu_ea_model != 32)
  if (spu_ea_model != 32)
    {
    {
      emit_insn (gen_ceq_v4si (tag_equal_hi, splat_hi, cache_tag_hi));
      emit_insn (gen_ceq_v4si (tag_equal_hi, splat_hi, cache_tag_hi));
      emit_insn (gen_andv4si3 (tag_equal, tag_equal, tag_equal_hi));
      emit_insn (gen_andv4si3 (tag_equal, tag_equal, tag_equal_hi));
    }
    }
 
 
  /* At most one of the tags compare equal, so tag_equal has one
  /* At most one of the tags compare equal, so tag_equal has one
     32-bit slot set to all 1's, with the other slots all zero.
     32-bit slot set to all 1's, with the other slots all zero.
     gbb picks off low bit from each byte in the 128-bit registers,
     gbb picks off low bit from each byte in the 128-bit registers,
     so tag_eq_pack is one of 0xf000, 0x0f00, 0x00f0, 0x000f, assuming
     so tag_eq_pack is one of 0xf000, 0x0f00, 0x00f0, 0x000f, assuming
     we have a hit.  */
     we have a hit.  */
  emit_insn (gen_spu_gbb (tag_eq_pack, spu_gen_subreg (V16QImode, tag_equal)));
  emit_insn (gen_spu_gbb (tag_eq_pack, spu_gen_subreg (V16QImode, tag_equal)));
  emit_insn (gen_spu_convert (tag_eq_pack_si, tag_eq_pack));
  emit_insn (gen_spu_convert (tag_eq_pack_si, tag_eq_pack));
 
 
  /* So counting leading zeros will set eq_index to 16, 20, 24 or 28.  */
  /* So counting leading zeros will set eq_index to 16, 20, 24 or 28.  */
  emit_insn (gen_clzsi2 (eq_index, tag_eq_pack_si));
  emit_insn (gen_clzsi2 (eq_index, tag_eq_pack_si));
 
 
  /* Allowing us to rotate the corresponding cache data pointer to slot0.
  /* Allowing us to rotate the corresponding cache data pointer to slot0.
     (rotating eq_index mod 16 bytes).  */
     (rotating eq_index mod 16 bytes).  */
  emit_insn (gen_rotqby_ti (cache_ptrs, cache_ptrs, eq_index));
  emit_insn (gen_rotqby_ti (cache_ptrs, cache_ptrs, eq_index));
  emit_insn (gen_spu_convert (cache_ptrs_si, cache_ptrs));
  emit_insn (gen_spu_convert (cache_ptrs_si, cache_ptrs));
 
 
  /* Add block offset to form final data address.  */
  /* Add block offset to form final data address.  */
  emit_insn (gen_addsi3 (data_addr, cache_ptrs_si, block_off));
  emit_insn (gen_addsi3 (data_addr, cache_ptrs_si, block_off));
 
 
  /* Check that we did hit.  */
  /* Check that we did hit.  */
  hit_label = gen_label_rtx ();
  hit_label = gen_label_rtx ();
  hit_ref = gen_rtx_LABEL_REF (VOIDmode, hit_label);
  hit_ref = gen_rtx_LABEL_REF (VOIDmode, hit_label);
  bcomp = gen_rtx_NE (SImode, tag_eq_pack_si, const0_rtx);
  bcomp = gen_rtx_NE (SImode, tag_eq_pack_si, const0_rtx);
  insn = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
  insn = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
                                      gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
                                      gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
                                                            hit_ref, pc_rtx)));
                                                            hit_ref, pc_rtx)));
  /* Say that this branch is very likely to happen.  */
  /* Say that this branch is very likely to happen.  */
  v = REG_BR_PROB_BASE - REG_BR_PROB_BASE / 100 - 1;
  v = REG_BR_PROB_BASE - REG_BR_PROB_BASE / 100 - 1;
  REG_NOTES (insn)
  REG_NOTES (insn)
    = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (v), REG_NOTES (insn));
    = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (v), REG_NOTES (insn));
 
 
  ea_load_store (mem, is_store, ea_addr, data_addr);
  ea_load_store (mem, is_store, ea_addr, data_addr);
  cont_label = gen_label_rtx ();
  cont_label = gen_label_rtx ();
  emit_jump_insn (gen_jump (cont_label));
  emit_jump_insn (gen_jump (cont_label));
  emit_barrier ();
  emit_barrier ();
 
 
  emit_label (hit_label);
  emit_label (hit_label);
 
 
  if (is_store)
  if (is_store)
    {
    {
      HOST_WIDE_INT v_hi;
      HOST_WIDE_INT v_hi;
      rtx dirty_bits = gen_reg_rtx (TImode);
      rtx dirty_bits = gen_reg_rtx (TImode);
      rtx dirty_off = gen_reg_rtx (SImode);
      rtx dirty_off = gen_reg_rtx (SImode);
      rtx dirty_128 = gen_reg_rtx (TImode);
      rtx dirty_128 = gen_reg_rtx (TImode);
      rtx neg_block_off = gen_reg_rtx (SImode);
      rtx neg_block_off = gen_reg_rtx (SImode);
 
 
      /* Set up mask with one dirty bit per byte of the mem we are
      /* Set up mask with one dirty bit per byte of the mem we are
         writing, starting from top bit.  */
         writing, starting from top bit.  */
      v_hi = v = -1;
      v_hi = v = -1;
      v <<= (128 - GET_MODE_SIZE (GET_MODE (mem))) & 63;
      v <<= (128 - GET_MODE_SIZE (GET_MODE (mem))) & 63;
      if ((128 - GET_MODE_SIZE (GET_MODE (mem))) >= 64)
      if ((128 - GET_MODE_SIZE (GET_MODE (mem))) >= 64)
        {
        {
          v_hi = v;
          v_hi = v;
          v = 0;
          v = 0;
        }
        }
      emit_move_insn (dirty_bits, immed_double_const (v, v_hi, TImode));
      emit_move_insn (dirty_bits, immed_double_const (v, v_hi, TImode));
 
 
      /* Form index into cache dirty_bits.  eq_index is one of
      /* Form index into cache dirty_bits.  eq_index is one of
         0x10, 0x14, 0x18 or 0x1c.  Multiplying by 4 gives us
         0x10, 0x14, 0x18 or 0x1c.  Multiplying by 4 gives us
         0x40, 0x50, 0x60 or 0x70 which just happens to be the
         0x40, 0x50, 0x60 or 0x70 which just happens to be the
         offset to each of the four dirty_bits elements.  */
         offset to each of the four dirty_bits elements.  */
      emit_insn (gen_ashlsi3 (dirty_off, eq_index, spu_const (SImode, 2)));
      emit_insn (gen_ashlsi3 (dirty_off, eq_index, spu_const (SImode, 2)));
 
 
      emit_insn (gen_spu_lqx (dirty_128, tag_addr, dirty_off));
      emit_insn (gen_spu_lqx (dirty_128, tag_addr, dirty_off));
 
 
      /* Rotate bit mask to proper bit.  */
      /* Rotate bit mask to proper bit.  */
      emit_insn (gen_negsi2 (neg_block_off, block_off));
      emit_insn (gen_negsi2 (neg_block_off, block_off));
      emit_insn (gen_rotqbybi_ti (dirty_bits, dirty_bits, neg_block_off));
      emit_insn (gen_rotqbybi_ti (dirty_bits, dirty_bits, neg_block_off));
      emit_insn (gen_rotqbi_ti (dirty_bits, dirty_bits, neg_block_off));
      emit_insn (gen_rotqbi_ti (dirty_bits, dirty_bits, neg_block_off));
 
 
      /* Or in the new dirty bits.  */
      /* Or in the new dirty bits.  */
      emit_insn (gen_iorti3 (dirty_128, dirty_bits, dirty_128));
      emit_insn (gen_iorti3 (dirty_128, dirty_bits, dirty_128));
 
 
      /* Store.  */
      /* Store.  */
      emit_insn (gen_spu_stqx (dirty_128, tag_addr, dirty_off));
      emit_insn (gen_spu_stqx (dirty_128, tag_addr, dirty_off));
    }
    }
 
 
  emit_label (cont_label);
  emit_label (cont_label);
}
}
 
 
static rtx
static rtx
expand_ea_mem (rtx mem, bool is_store)
expand_ea_mem (rtx mem, bool is_store)
{
{
  rtx ea_addr;
  rtx ea_addr;
  rtx data_addr = gen_reg_rtx (Pmode);
  rtx data_addr = gen_reg_rtx (Pmode);
  rtx new_mem;
  rtx new_mem;
 
 
  ea_addr = force_reg (EAmode, XEXP (mem, 0));
  ea_addr = force_reg (EAmode, XEXP (mem, 0));
  if (optimize_size || optimize == 0)
  if (optimize_size || optimize == 0)
    ea_load_store (mem, is_store, ea_addr, data_addr);
    ea_load_store (mem, is_store, ea_addr, data_addr);
  else
  else
    ea_load_store_inline (mem, is_store, ea_addr, data_addr);
    ea_load_store_inline (mem, is_store, ea_addr, data_addr);
 
 
  if (ea_alias_set == -1)
  if (ea_alias_set == -1)
    ea_alias_set = new_alias_set ();
    ea_alias_set = new_alias_set ();
 
 
  /* We generate a new MEM RTX to refer to the copy of the data
  /* We generate a new MEM RTX to refer to the copy of the data
     in the cache.  We do not copy memory attributes (except the
     in the cache.  We do not copy memory attributes (except the
     alignment) from the original MEM, as they may no longer apply
     alignment) from the original MEM, as they may no longer apply
     to the cache copy.  */
     to the cache copy.  */
  new_mem = gen_rtx_MEM (GET_MODE (mem), data_addr);
  new_mem = gen_rtx_MEM (GET_MODE (mem), data_addr);
  set_mem_alias_set (new_mem, ea_alias_set);
  set_mem_alias_set (new_mem, ea_alias_set);
  set_mem_align (new_mem, MIN (MEM_ALIGN (mem), 128 * 8));
  set_mem_align (new_mem, MIN (MEM_ALIGN (mem), 128 * 8));
 
 
  return new_mem;
  return new_mem;
}
}
 
 
int
int
spu_expand_mov (rtx * ops, enum machine_mode mode)
spu_expand_mov (rtx * ops, enum machine_mode mode)
{
{
  if (GET_CODE (ops[0]) == SUBREG && !valid_subreg (ops[0]))
  if (GET_CODE (ops[0]) == SUBREG && !valid_subreg (ops[0]))
    abort ();
    abort ();
 
 
  if (GET_CODE (ops[1]) == SUBREG && !valid_subreg (ops[1]))
  if (GET_CODE (ops[1]) == SUBREG && !valid_subreg (ops[1]))
    {
    {
      rtx from = SUBREG_REG (ops[1]);
      rtx from = SUBREG_REG (ops[1]);
      enum machine_mode imode = int_mode_for_mode (GET_MODE (from));
      enum machine_mode imode = int_mode_for_mode (GET_MODE (from));
 
 
      gcc_assert (GET_MODE_CLASS (mode) == MODE_INT
      gcc_assert (GET_MODE_CLASS (mode) == MODE_INT
                  && GET_MODE_CLASS (imode) == MODE_INT
                  && GET_MODE_CLASS (imode) == MODE_INT
                  && subreg_lowpart_p (ops[1]));
                  && subreg_lowpart_p (ops[1]));
 
 
      if (GET_MODE_SIZE (imode) < 4)
      if (GET_MODE_SIZE (imode) < 4)
        imode = SImode;
        imode = SImode;
      if (imode != GET_MODE (from))
      if (imode != GET_MODE (from))
        from = gen_rtx_SUBREG (imode, from, 0);
        from = gen_rtx_SUBREG (imode, from, 0);
 
 
      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (imode))
      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (imode))
        {
        {
          enum insn_code icode = convert_optab_handler (trunc_optab, mode, imode)->insn_code;
          enum insn_code icode = convert_optab_handler (trunc_optab, mode, imode)->insn_code;
          emit_insn (GEN_FCN (icode) (ops[0], from));
          emit_insn (GEN_FCN (icode) (ops[0], from));
        }
        }
      else
      else
        emit_insn (gen_extend_insn (ops[0], from, mode, imode, 1));
        emit_insn (gen_extend_insn (ops[0], from, mode, imode, 1));
      return 1;
      return 1;
    }
    }
 
 
  /* At least one of the operands needs to be a register. */
  /* At least one of the operands needs to be a register. */
  if ((reload_in_progress | reload_completed) == 0
  if ((reload_in_progress | reload_completed) == 0
      && !register_operand (ops[0], mode) && !register_operand (ops[1], mode))
      && !register_operand (ops[0], mode) && !register_operand (ops[1], mode))
    {
    {
      rtx temp = force_reg (mode, ops[1]);
      rtx temp = force_reg (mode, ops[1]);
      emit_move_insn (ops[0], temp);
      emit_move_insn (ops[0], temp);
      return 1;
      return 1;
    }
    }
  if (reload_in_progress || reload_completed)
  if (reload_in_progress || reload_completed)
    {
    {
      if (CONSTANT_P (ops[1]))
      if (CONSTANT_P (ops[1]))
        return spu_split_immediate (ops);
        return spu_split_immediate (ops);
      return 0;
      return 0;
    }
    }
 
 
  /* Catch the SImode immediates greater than 0x7fffffff, and sign
  /* Catch the SImode immediates greater than 0x7fffffff, and sign
     extend them. */
     extend them. */
  if (GET_CODE (ops[1]) == CONST_INT)
  if (GET_CODE (ops[1]) == CONST_INT)
    {
    {
      HOST_WIDE_INT val = trunc_int_for_mode (INTVAL (ops[1]), mode);
      HOST_WIDE_INT val = trunc_int_for_mode (INTVAL (ops[1]), mode);
      if (val != INTVAL (ops[1]))
      if (val != INTVAL (ops[1]))
        {
        {
          emit_move_insn (ops[0], GEN_INT (val));
          emit_move_insn (ops[0], GEN_INT (val));
          return 1;
          return 1;
        }
        }
    }
    }
  if (MEM_P (ops[0]))
  if (MEM_P (ops[0]))
    {
    {
      if (MEM_ADDR_SPACE (ops[0]))
      if (MEM_ADDR_SPACE (ops[0]))
        ops[0] = expand_ea_mem (ops[0], true);
        ops[0] = expand_ea_mem (ops[0], true);
      return spu_split_store (ops);
      return spu_split_store (ops);
    }
    }
  if (MEM_P (ops[1]))
  if (MEM_P (ops[1]))
    {
    {
      if (MEM_ADDR_SPACE (ops[1]))
      if (MEM_ADDR_SPACE (ops[1]))
        ops[1] = expand_ea_mem (ops[1], false);
        ops[1] = expand_ea_mem (ops[1], false);
      return spu_split_load (ops);
      return spu_split_load (ops);
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
static void
static void
spu_convert_move (rtx dst, rtx src)
spu_convert_move (rtx dst, rtx src)
{
{
  enum machine_mode mode = GET_MODE (dst);
  enum machine_mode mode = GET_MODE (dst);
  enum machine_mode int_mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0);
  enum machine_mode int_mode = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 0);
  rtx reg;
  rtx reg;
  gcc_assert (GET_MODE (src) == TImode);
  gcc_assert (GET_MODE (src) == TImode);
  reg = int_mode != mode ? gen_reg_rtx (int_mode) : dst;
  reg = int_mode != mode ? gen_reg_rtx (int_mode) : dst;
  emit_insn (gen_rtx_SET (VOIDmode, reg,
  emit_insn (gen_rtx_SET (VOIDmode, reg,
               gen_rtx_TRUNCATE (int_mode,
               gen_rtx_TRUNCATE (int_mode,
                 gen_rtx_LSHIFTRT (TImode, src,
                 gen_rtx_LSHIFTRT (TImode, src,
                   GEN_INT (int_mode == DImode ? 64 : 96)))));
                   GEN_INT (int_mode == DImode ? 64 : 96)))));
  if (int_mode != mode)
  if (int_mode != mode)
    {
    {
      reg = simplify_gen_subreg (mode, reg, int_mode, 0);
      reg = simplify_gen_subreg (mode, reg, int_mode, 0);
      emit_move_insn (dst, reg);
      emit_move_insn (dst, reg);
    }
    }
}
}
 
 
/* Load TImode values into DST0 and DST1 (when it is non-NULL) using
/* Load TImode values into DST0 and DST1 (when it is non-NULL) using
   the address from SRC and SRC+16.  Return a REG or CONST_INT that
   the address from SRC and SRC+16.  Return a REG or CONST_INT that
   specifies how many bytes to rotate the loaded registers, plus any
   specifies how many bytes to rotate the loaded registers, plus any
   extra from EXTRA_ROTQBY.  The address and rotate amounts are
   extra from EXTRA_ROTQBY.  The address and rotate amounts are
   normalized to improve merging of loads and rotate computations. */
   normalized to improve merging of loads and rotate computations. */
static rtx
static rtx
spu_expand_load (rtx dst0, rtx dst1, rtx src, int extra_rotby)
spu_expand_load (rtx dst0, rtx dst1, rtx src, int extra_rotby)
{
{
  rtx addr = XEXP (src, 0);
  rtx addr = XEXP (src, 0);
  rtx p0, p1, rot, addr0, addr1;
  rtx p0, p1, rot, addr0, addr1;
  int rot_amt;
  int rot_amt;
 
 
  rot = 0;
  rot = 0;
  rot_amt = 0;
  rot_amt = 0;
 
 
  if (MEM_ALIGN (src) >= 128)
  if (MEM_ALIGN (src) >= 128)
    /* Address is already aligned; simply perform a TImode load.  */ ;
    /* Address is already aligned; simply perform a TImode load.  */ ;
  else if (GET_CODE (addr) == PLUS)
  else if (GET_CODE (addr) == PLUS)
    {
    {
      /* 8 cases:
      /* 8 cases:
         aligned reg   + aligned reg     => lqx
         aligned reg   + aligned reg     => lqx
         aligned reg   + unaligned reg   => lqx, rotqby
         aligned reg   + unaligned reg   => lqx, rotqby
         aligned reg   + aligned const   => lqd
         aligned reg   + aligned const   => lqd
         aligned reg   + unaligned const => lqd, rotqbyi
         aligned reg   + unaligned const => lqd, rotqbyi
         unaligned reg + aligned reg     => lqx, rotqby
         unaligned reg + aligned reg     => lqx, rotqby
         unaligned reg + unaligned reg   => lqx, a, rotqby (1 scratch)
         unaligned reg + unaligned reg   => lqx, a, rotqby (1 scratch)
         unaligned reg + aligned const   => lqd, rotqby
         unaligned reg + aligned const   => lqd, rotqby
         unaligned reg + unaligned const -> not allowed by legitimate address
         unaligned reg + unaligned const -> not allowed by legitimate address
       */
       */
      p0 = XEXP (addr, 0);
      p0 = XEXP (addr, 0);
      p1 = XEXP (addr, 1);
      p1 = XEXP (addr, 1);
      if (!reg_aligned_for_addr (p0))
      if (!reg_aligned_for_addr (p0))
        {
        {
          if (REG_P (p1) && !reg_aligned_for_addr (p1))
          if (REG_P (p1) && !reg_aligned_for_addr (p1))
            {
            {
              rot = gen_reg_rtx (SImode);
              rot = gen_reg_rtx (SImode);
              emit_insn (gen_addsi3 (rot, p0, p1));
              emit_insn (gen_addsi3 (rot, p0, p1));
            }
            }
          else if (GET_CODE (p1) == CONST_INT && (INTVAL (p1) & 15))
          else if (GET_CODE (p1) == CONST_INT && (INTVAL (p1) & 15))
            {
            {
              if (INTVAL (p1) > 0
              if (INTVAL (p1) > 0
                  && REG_POINTER (p0)
                  && REG_POINTER (p0)
                  && INTVAL (p1) * BITS_PER_UNIT
                  && INTVAL (p1) * BITS_PER_UNIT
                     < REGNO_POINTER_ALIGN (REGNO (p0)))
                     < REGNO_POINTER_ALIGN (REGNO (p0)))
                {
                {
                  rot = gen_reg_rtx (SImode);
                  rot = gen_reg_rtx (SImode);
                  emit_insn (gen_addsi3 (rot, p0, p1));
                  emit_insn (gen_addsi3 (rot, p0, p1));
                  addr = p0;
                  addr = p0;
                }
                }
              else
              else
                {
                {
                  rtx x = gen_reg_rtx (SImode);
                  rtx x = gen_reg_rtx (SImode);
                  emit_move_insn (x, p1);
                  emit_move_insn (x, p1);
                  if (!spu_arith_operand (p1, SImode))
                  if (!spu_arith_operand (p1, SImode))
                    p1 = x;
                    p1 = x;
                  rot = gen_reg_rtx (SImode);
                  rot = gen_reg_rtx (SImode);
                  emit_insn (gen_addsi3 (rot, p0, p1));
                  emit_insn (gen_addsi3 (rot, p0, p1));
                  addr = gen_rtx_PLUS (Pmode, p0, x);
                  addr = gen_rtx_PLUS (Pmode, p0, x);
                }
                }
            }
            }
          else
          else
            rot = p0;
            rot = p0;
        }
        }
      else
      else
        {
        {
          if (GET_CODE (p1) == CONST_INT && (INTVAL (p1) & 15))
          if (GET_CODE (p1) == CONST_INT && (INTVAL (p1) & 15))
            {
            {
              rot_amt = INTVAL (p1) & 15;
              rot_amt = INTVAL (p1) & 15;
              if (INTVAL (p1) & -16)
              if (INTVAL (p1) & -16)
                {
                {
                  p1 = GEN_INT (INTVAL (p1) & -16);
                  p1 = GEN_INT (INTVAL (p1) & -16);
                  addr = gen_rtx_PLUS (SImode, p0, p1);
                  addr = gen_rtx_PLUS (SImode, p0, p1);
                }
                }
              else
              else
                addr = p0;
                addr = p0;
            }
            }
          else if (REG_P (p1) && !reg_aligned_for_addr (p1))
          else if (REG_P (p1) && !reg_aligned_for_addr (p1))
            rot = p1;
            rot = p1;
        }
        }
    }
    }
  else if (REG_P (addr))
  else if (REG_P (addr))
    {
    {
      if (!reg_aligned_for_addr (addr))
      if (!reg_aligned_for_addr (addr))
        rot = addr;
        rot = addr;
    }
    }
  else if (GET_CODE (addr) == CONST)
  else if (GET_CODE (addr) == CONST)
    {
    {
      if (GET_CODE (XEXP (addr, 0)) == PLUS
      if (GET_CODE (XEXP (addr, 0)) == PLUS
          && ALIGNED_SYMBOL_REF_P (XEXP (XEXP (addr, 0), 0))
          && ALIGNED_SYMBOL_REF_P (XEXP (XEXP (addr, 0), 0))
          && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
          && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
        {
        {
          rot_amt = INTVAL (XEXP (XEXP (addr, 0), 1));
          rot_amt = INTVAL (XEXP (XEXP (addr, 0), 1));
          if (rot_amt & -16)
          if (rot_amt & -16)
            addr = gen_rtx_CONST (Pmode,
            addr = gen_rtx_CONST (Pmode,
                                  gen_rtx_PLUS (Pmode,
                                  gen_rtx_PLUS (Pmode,
                                                XEXP (XEXP (addr, 0), 0),
                                                XEXP (XEXP (addr, 0), 0),
                                                GEN_INT (rot_amt & -16)));
                                                GEN_INT (rot_amt & -16)));
          else
          else
            addr = XEXP (XEXP (addr, 0), 0);
            addr = XEXP (XEXP (addr, 0), 0);
        }
        }
      else
      else
        {
        {
          rot = gen_reg_rtx (Pmode);
          rot = gen_reg_rtx (Pmode);
          emit_move_insn (rot, addr);
          emit_move_insn (rot, addr);
        }
        }
    }
    }
  else if (GET_CODE (addr) == CONST_INT)
  else if (GET_CODE (addr) == CONST_INT)
    {
    {
      rot_amt = INTVAL (addr);
      rot_amt = INTVAL (addr);
      addr = GEN_INT (rot_amt & -16);
      addr = GEN_INT (rot_amt & -16);
    }
    }
  else if (!ALIGNED_SYMBOL_REF_P (addr))
  else if (!ALIGNED_SYMBOL_REF_P (addr))
    {
    {
      rot = gen_reg_rtx (Pmode);
      rot = gen_reg_rtx (Pmode);
      emit_move_insn (rot, addr);
      emit_move_insn (rot, addr);
    }
    }
 
 
  rot_amt += extra_rotby;
  rot_amt += extra_rotby;
 
 
  rot_amt &= 15;
  rot_amt &= 15;
 
 
  if (rot && rot_amt)
  if (rot && rot_amt)
    {
    {
      rtx x = gen_reg_rtx (SImode);
      rtx x = gen_reg_rtx (SImode);
      emit_insn (gen_addsi3 (x, rot, GEN_INT (rot_amt)));
      emit_insn (gen_addsi3 (x, rot, GEN_INT (rot_amt)));
      rot = x;
      rot = x;
      rot_amt = 0;
      rot_amt = 0;
    }
    }
  if (!rot && rot_amt)
  if (!rot && rot_amt)
    rot = GEN_INT (rot_amt);
    rot = GEN_INT (rot_amt);
 
 
  addr0 = copy_rtx (addr);
  addr0 = copy_rtx (addr);
  addr0 = gen_rtx_AND (SImode, copy_rtx (addr), GEN_INT (-16));
  addr0 = gen_rtx_AND (SImode, copy_rtx (addr), GEN_INT (-16));
  emit_insn (gen__movti (dst0, change_address (src, TImode, addr0)));
  emit_insn (gen__movti (dst0, change_address (src, TImode, addr0)));
 
 
  if (dst1)
  if (dst1)
    {
    {
      addr1 = plus_constant (copy_rtx (addr), 16);
      addr1 = plus_constant (copy_rtx (addr), 16);
      addr1 = gen_rtx_AND (SImode, addr1, GEN_INT (-16));
      addr1 = gen_rtx_AND (SImode, addr1, GEN_INT (-16));
      emit_insn (gen__movti (dst1, change_address (src, TImode, addr1)));
      emit_insn (gen__movti (dst1, change_address (src, TImode, addr1)));
    }
    }
 
 
  return rot;
  return rot;
}
}
 
 
int
int
spu_split_load (rtx * ops)
spu_split_load (rtx * ops)
{
{
  enum machine_mode mode = GET_MODE (ops[0]);
  enum machine_mode mode = GET_MODE (ops[0]);
  rtx addr, load, rot;
  rtx addr, load, rot;
  int rot_amt;
  int rot_amt;
 
 
  if (GET_MODE_SIZE (mode) >= 16)
  if (GET_MODE_SIZE (mode) >= 16)
    return 0;
    return 0;
 
 
  addr = XEXP (ops[1], 0);
  addr = XEXP (ops[1], 0);
  gcc_assert (GET_CODE (addr) != AND);
  gcc_assert (GET_CODE (addr) != AND);
 
 
  if (!address_needs_split (ops[1]))
  if (!address_needs_split (ops[1]))
    {
    {
      ops[1] = change_address (ops[1], TImode, addr);
      ops[1] = change_address (ops[1], TImode, addr);
      load = gen_reg_rtx (TImode);
      load = gen_reg_rtx (TImode);
      emit_insn (gen__movti (load, ops[1]));
      emit_insn (gen__movti (load, ops[1]));
      spu_convert_move (ops[0], load);
      spu_convert_move (ops[0], load);
      return 1;
      return 1;
    }
    }
 
 
  rot_amt = GET_MODE_SIZE (mode) < 4 ? GET_MODE_SIZE (mode) - 4 : 0;
  rot_amt = GET_MODE_SIZE (mode) < 4 ? GET_MODE_SIZE (mode) - 4 : 0;
 
 
  load = gen_reg_rtx (TImode);
  load = gen_reg_rtx (TImode);
  rot = spu_expand_load (load, 0, ops[1], rot_amt);
  rot = spu_expand_load (load, 0, ops[1], rot_amt);
 
 
  if (rot)
  if (rot)
    emit_insn (gen_rotqby_ti (load, load, rot));
    emit_insn (gen_rotqby_ti (load, load, rot));
 
 
  spu_convert_move (ops[0], load);
  spu_convert_move (ops[0], load);
  return 1;
  return 1;
}
}
 
 
int
int
spu_split_store (rtx * ops)
spu_split_store (rtx * ops)
{
{
  enum machine_mode mode = GET_MODE (ops[0]);
  enum machine_mode mode = GET_MODE (ops[0]);
  rtx reg;
  rtx reg;
  rtx addr, p0, p1, p1_lo, smem;
  rtx addr, p0, p1, p1_lo, smem;
  int aform;
  int aform;
  int scalar;
  int scalar;
 
 
  if (GET_MODE_SIZE (mode) >= 16)
  if (GET_MODE_SIZE (mode) >= 16)
    return 0;
    return 0;
 
 
  addr = XEXP (ops[0], 0);
  addr = XEXP (ops[0], 0);
  gcc_assert (GET_CODE (addr) != AND);
  gcc_assert (GET_CODE (addr) != AND);
 
 
  if (!address_needs_split (ops[0]))
  if (!address_needs_split (ops[0]))
    {
    {
      reg = gen_reg_rtx (TImode);
      reg = gen_reg_rtx (TImode);
      emit_insn (gen_spu_convert (reg, ops[1]));
      emit_insn (gen_spu_convert (reg, ops[1]));
      ops[0] = change_address (ops[0], TImode, addr);
      ops[0] = change_address (ops[0], TImode, addr);
      emit_move_insn (ops[0], reg);
      emit_move_insn (ops[0], reg);
      return 1;
      return 1;
    }
    }
 
 
  if (GET_CODE (addr) == PLUS)
  if (GET_CODE (addr) == PLUS)
    {
    {
      /* 8 cases:
      /* 8 cases:
         aligned reg   + aligned reg     => lqx, c?x, shuf, stqx
         aligned reg   + aligned reg     => lqx, c?x, shuf, stqx
         aligned reg   + unaligned reg   => lqx, c?x, shuf, stqx
         aligned reg   + unaligned reg   => lqx, c?x, shuf, stqx
         aligned reg   + aligned const   => lqd, c?d, shuf, stqx
         aligned reg   + aligned const   => lqd, c?d, shuf, stqx
         aligned reg   + unaligned const => lqd, c?d, shuf, stqx
         aligned reg   + unaligned const => lqd, c?d, shuf, stqx
         unaligned reg + aligned reg     => lqx, c?x, shuf, stqx
         unaligned reg + aligned reg     => lqx, c?x, shuf, stqx
         unaligned reg + unaligned reg   => lqx, c?x, shuf, stqx
         unaligned reg + unaligned reg   => lqx, c?x, shuf, stqx
         unaligned reg + aligned const   => lqd, c?d, shuf, stqx
         unaligned reg + aligned const   => lqd, c?d, shuf, stqx
         unaligned reg + unaligned const -> lqx, c?d, shuf, stqx
         unaligned reg + unaligned const -> lqx, c?d, shuf, stqx
       */
       */
      aform = 0;
      aform = 0;
      p0 = XEXP (addr, 0);
      p0 = XEXP (addr, 0);
      p1 = p1_lo = XEXP (addr, 1);
      p1 = p1_lo = XEXP (addr, 1);
      if (REG_P (p0) && GET_CODE (p1) == CONST_INT)
      if (REG_P (p0) && GET_CODE (p1) == CONST_INT)
        {
        {
          p1_lo = GEN_INT (INTVAL (p1) & 15);
          p1_lo = GEN_INT (INTVAL (p1) & 15);
          if (reg_aligned_for_addr (p0))
          if (reg_aligned_for_addr (p0))
            {
            {
              p1 = GEN_INT (INTVAL (p1) & -16);
              p1 = GEN_INT (INTVAL (p1) & -16);
              if (p1 == const0_rtx)
              if (p1 == const0_rtx)
                addr = p0;
                addr = p0;
              else
              else
                addr = gen_rtx_PLUS (SImode, p0, p1);
                addr = gen_rtx_PLUS (SImode, p0, p1);
            }
            }
          else
          else
            {
            {
              rtx x = gen_reg_rtx (SImode);
              rtx x = gen_reg_rtx (SImode);
              emit_move_insn (x, p1);
              emit_move_insn (x, p1);
              addr = gen_rtx_PLUS (SImode, p0, x);
              addr = gen_rtx_PLUS (SImode, p0, x);
            }
            }
        }
        }
    }
    }
  else if (REG_P (addr))
  else if (REG_P (addr))
    {
    {
      aform = 0;
      aform = 0;
      p0 = addr;
      p0 = addr;
      p1 = p1_lo = const0_rtx;
      p1 = p1_lo = const0_rtx;
    }
    }
  else
  else
    {
    {
      aform = 1;
      aform = 1;
      p0 = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
      p0 = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
      p1 = 0;                    /* aform doesn't use p1 */
      p1 = 0;                    /* aform doesn't use p1 */
      p1_lo = addr;
      p1_lo = addr;
      if (ALIGNED_SYMBOL_REF_P (addr))
      if (ALIGNED_SYMBOL_REF_P (addr))
        p1_lo = const0_rtx;
        p1_lo = const0_rtx;
      else if (GET_CODE (addr) == CONST
      else if (GET_CODE (addr) == CONST
               && GET_CODE (XEXP (addr, 0)) == PLUS
               && GET_CODE (XEXP (addr, 0)) == PLUS
               && ALIGNED_SYMBOL_REF_P (XEXP (XEXP (addr, 0), 0))
               && ALIGNED_SYMBOL_REF_P (XEXP (XEXP (addr, 0), 0))
               && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
               && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
        {
        {
          HOST_WIDE_INT v = INTVAL (XEXP (XEXP (addr, 0), 1));
          HOST_WIDE_INT v = INTVAL (XEXP (XEXP (addr, 0), 1));
          if ((v & -16) != 0)
          if ((v & -16) != 0)
            addr = gen_rtx_CONST (Pmode,
            addr = gen_rtx_CONST (Pmode,
                                  gen_rtx_PLUS (Pmode,
                                  gen_rtx_PLUS (Pmode,
                                                XEXP (XEXP (addr, 0), 0),
                                                XEXP (XEXP (addr, 0), 0),
                                                GEN_INT (v & -16)));
                                                GEN_INT (v & -16)));
          else
          else
            addr = XEXP (XEXP (addr, 0), 0);
            addr = XEXP (XEXP (addr, 0), 0);
          p1_lo = GEN_INT (v & 15);
          p1_lo = GEN_INT (v & 15);
        }
        }
      else if (GET_CODE (addr) == CONST_INT)
      else if (GET_CODE (addr) == CONST_INT)
        {
        {
          p1_lo = GEN_INT (INTVAL (addr) & 15);
          p1_lo = GEN_INT (INTVAL (addr) & 15);
          addr = GEN_INT (INTVAL (addr) & -16);
          addr = GEN_INT (INTVAL (addr) & -16);
        }
        }
      else
      else
        {
        {
          p1_lo = gen_reg_rtx (SImode);
          p1_lo = gen_reg_rtx (SImode);
          emit_move_insn (p1_lo, addr);
          emit_move_insn (p1_lo, addr);
        }
        }
    }
    }
 
 
  reg = gen_reg_rtx (TImode);
  reg = gen_reg_rtx (TImode);
 
 
  scalar = store_with_one_insn_p (ops[0]);
  scalar = store_with_one_insn_p (ops[0]);
  if (!scalar)
  if (!scalar)
    {
    {
      /* We could copy the flags from the ops[0] MEM to mem here,
      /* We could copy the flags from the ops[0] MEM to mem here,
         We don't because we want this load to be optimized away if
         We don't because we want this load to be optimized away if
         possible, and copying the flags will prevent that in certain
         possible, and copying the flags will prevent that in certain
         cases, e.g. consider the volatile flag. */
         cases, e.g. consider the volatile flag. */
 
 
      rtx pat = gen_reg_rtx (TImode);
      rtx pat = gen_reg_rtx (TImode);
      rtx lmem = change_address (ops[0], TImode, copy_rtx (addr));
      rtx lmem = change_address (ops[0], TImode, copy_rtx (addr));
      set_mem_alias_set (lmem, 0);
      set_mem_alias_set (lmem, 0);
      emit_insn (gen_movti (reg, lmem));
      emit_insn (gen_movti (reg, lmem));
 
 
      if (!p0 || reg_aligned_for_addr (p0))
      if (!p0 || reg_aligned_for_addr (p0))
        p0 = stack_pointer_rtx;
        p0 = stack_pointer_rtx;
      if (!p1_lo)
      if (!p1_lo)
        p1_lo = const0_rtx;
        p1_lo = const0_rtx;
 
 
      emit_insn (gen_cpat (pat, p0, p1_lo, GEN_INT (GET_MODE_SIZE (mode))));
      emit_insn (gen_cpat (pat, p0, p1_lo, GEN_INT (GET_MODE_SIZE (mode))));
      emit_insn (gen_shufb (reg, ops[1], reg, pat));
      emit_insn (gen_shufb (reg, ops[1], reg, pat));
    }
    }
  else
  else
    {
    {
      if (GET_CODE (ops[1]) == REG)
      if (GET_CODE (ops[1]) == REG)
        emit_insn (gen_spu_convert (reg, ops[1]));
        emit_insn (gen_spu_convert (reg, ops[1]));
      else if (GET_CODE (ops[1]) == SUBREG)
      else if (GET_CODE (ops[1]) == SUBREG)
        emit_insn (gen_spu_convert (reg, SUBREG_REG (ops[1])));
        emit_insn (gen_spu_convert (reg, SUBREG_REG (ops[1])));
      else
      else
        abort ();
        abort ();
    }
    }
 
 
  if (GET_MODE_SIZE (mode) < 4 && scalar)
  if (GET_MODE_SIZE (mode) < 4 && scalar)
    emit_insn (gen_ashlti3
    emit_insn (gen_ashlti3
               (reg, reg, GEN_INT (32 - GET_MODE_BITSIZE (mode))));
               (reg, reg, GEN_INT (32 - GET_MODE_BITSIZE (mode))));
 
 
  smem = change_address (ops[0], TImode, copy_rtx (addr));
  smem = change_address (ops[0], TImode, copy_rtx (addr));
  /* We can't use the previous alias set because the memory has changed
  /* We can't use the previous alias set because the memory has changed
     size and can potentially overlap objects of other types.  */
     size and can potentially overlap objects of other types.  */
  set_mem_alias_set (smem, 0);
  set_mem_alias_set (smem, 0);
 
 
  emit_insn (gen_movti (smem, reg));
  emit_insn (gen_movti (smem, reg));
  return 1;
  return 1;
}
}
 
 
/* Return TRUE if X is MEM which is a struct member reference
/* Return TRUE if X is MEM which is a struct member reference
   and the member can safely be loaded and stored with a single
   and the member can safely be loaded and stored with a single
   instruction because it is padded. */
   instruction because it is padded. */
static int
static int
mem_is_padded_component_ref (rtx x)
mem_is_padded_component_ref (rtx x)
{
{
  tree t = MEM_EXPR (x);
  tree t = MEM_EXPR (x);
  tree r;
  tree r;
  if (!t || TREE_CODE (t) != COMPONENT_REF)
  if (!t || TREE_CODE (t) != COMPONENT_REF)
    return 0;
    return 0;
  t = TREE_OPERAND (t, 1);
  t = TREE_OPERAND (t, 1);
  if (!t || TREE_CODE (t) != FIELD_DECL
  if (!t || TREE_CODE (t) != FIELD_DECL
      || DECL_ALIGN (t) < 128 || AGGREGATE_TYPE_P (TREE_TYPE (t)))
      || DECL_ALIGN (t) < 128 || AGGREGATE_TYPE_P (TREE_TYPE (t)))
    return 0;
    return 0;
  /* Only do this for RECORD_TYPEs, not UNION_TYPEs. */
  /* Only do this for RECORD_TYPEs, not UNION_TYPEs. */
  r = DECL_FIELD_CONTEXT (t);
  r = DECL_FIELD_CONTEXT (t);
  if (!r || TREE_CODE (r) != RECORD_TYPE)
  if (!r || TREE_CODE (r) != RECORD_TYPE)
    return 0;
    return 0;
  /* Make sure they are the same mode */
  /* Make sure they are the same mode */
  if (GET_MODE (x) != TYPE_MODE (TREE_TYPE (t)))
  if (GET_MODE (x) != TYPE_MODE (TREE_TYPE (t)))
    return 0;
    return 0;
  /* If there are no following fields then the field alignment assures
  /* If there are no following fields then the field alignment assures
     the structure is padded to the alignment which means this field is
     the structure is padded to the alignment which means this field is
     padded too.  */
     padded too.  */
  if (TREE_CHAIN (t) == 0)
  if (TREE_CHAIN (t) == 0)
    return 1;
    return 1;
  /* If the following field is also aligned then this field will be
  /* If the following field is also aligned then this field will be
     padded. */
     padded. */
  t = TREE_CHAIN (t);
  t = TREE_CHAIN (t);
  if (TREE_CODE (t) == FIELD_DECL && DECL_ALIGN (t) >= 128)
  if (TREE_CODE (t) == FIELD_DECL && DECL_ALIGN (t) >= 128)
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
/* Parse the -mfixed-range= option string.  */
/* Parse the -mfixed-range= option string.  */
static void
static void
fix_range (const char *const_str)
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;
    }
    }
}
}
 
 
/* Return TRUE if x is a CONST_INT, CONST_DOUBLE or CONST_VECTOR that
/* Return TRUE if x is a CONST_INT, CONST_DOUBLE or CONST_VECTOR that
   can be generated using the fsmbi instruction. */
   can be generated using the fsmbi instruction. */
int
int
fsmbi_const_p (rtx x)
fsmbi_const_p (rtx x)
{
{
  if (CONSTANT_P (x))
  if (CONSTANT_P (x))
    {
    {
      /* We can always choose TImode for CONST_INT because the high bits
      /* We can always choose TImode for CONST_INT because the high bits
         of an SImode will always be all 1s, i.e., valid for fsmbi. */
         of an SImode will always be all 1s, i.e., valid for fsmbi. */
      enum immediate_class c = classify_immediate (x, TImode);
      enum immediate_class c = classify_immediate (x, TImode);
      return c == IC_FSMBI || (!epilogue_completed && c == IC_FSMBI2);
      return c == IC_FSMBI || (!epilogue_completed && c == IC_FSMBI2);
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Return TRUE if x is a CONST_INT, CONST_DOUBLE or CONST_VECTOR that
/* Return TRUE if x is a CONST_INT, CONST_DOUBLE or CONST_VECTOR that
   can be generated using the cbd, chd, cwd or cdd instruction. */
   can be generated using the cbd, chd, cwd or cdd instruction. */
int
int
cpat_const_p (rtx x, enum machine_mode mode)
cpat_const_p (rtx x, enum machine_mode mode)
{
{
  if (CONSTANT_P (x))
  if (CONSTANT_P (x))
    {
    {
      enum immediate_class c = classify_immediate (x, mode);
      enum immediate_class c = classify_immediate (x, mode);
      return c == IC_CPAT;
      return c == IC_CPAT;
    }
    }
  return 0;
  return 0;
}
}
 
 
rtx
rtx
gen_cpat_const (rtx * ops)
gen_cpat_const (rtx * ops)
{
{
  unsigned char dst[16];
  unsigned char dst[16];
  int i, offset, shift, isize;
  int i, offset, shift, isize;
  if (GET_CODE (ops[3]) != CONST_INT
  if (GET_CODE (ops[3]) != CONST_INT
      || GET_CODE (ops[2]) != CONST_INT
      || GET_CODE (ops[2]) != CONST_INT
      || (GET_CODE (ops[1]) != CONST_INT
      || (GET_CODE (ops[1]) != CONST_INT
          && GET_CODE (ops[1]) != REG))
          && GET_CODE (ops[1]) != REG))
    return 0;
    return 0;
  if (GET_CODE (ops[1]) == REG
  if (GET_CODE (ops[1]) == REG
      && (!REG_POINTER (ops[1])
      && (!REG_POINTER (ops[1])
          || REGNO_POINTER_ALIGN (ORIGINAL_REGNO (ops[1])) < 128))
          || REGNO_POINTER_ALIGN (ORIGINAL_REGNO (ops[1])) < 128))
    return 0;
    return 0;
 
 
  for (i = 0; i < 16; i++)
  for (i = 0; i < 16; i++)
    dst[i] = i + 16;
    dst[i] = i + 16;
  isize = INTVAL (ops[3]);
  isize = INTVAL (ops[3]);
  if (isize == 1)
  if (isize == 1)
    shift = 3;
    shift = 3;
  else if (isize == 2)
  else if (isize == 2)
    shift = 2;
    shift = 2;
  else
  else
    shift = 0;
    shift = 0;
  offset = (INTVAL (ops[2]) +
  offset = (INTVAL (ops[2]) +
            (GET_CODE (ops[1]) ==
            (GET_CODE (ops[1]) ==
             CONST_INT ? INTVAL (ops[1]) : 0)) & 15;
             CONST_INT ? INTVAL (ops[1]) : 0)) & 15;
  for (i = 0; i < isize; i++)
  for (i = 0; i < isize; i++)
    dst[offset + i] = i + shift;
    dst[offset + i] = i + shift;
  return array_to_constant (TImode, dst);
  return array_to_constant (TImode, dst);
}
}
 
 
/* Convert a CONST_INT, CONST_DOUBLE, or CONST_VECTOR into a 16 byte
/* Convert a CONST_INT, CONST_DOUBLE, or CONST_VECTOR into a 16 byte
   array.  Use MODE for CONST_INT's.  When the constant's mode is smaller
   array.  Use MODE for CONST_INT's.  When the constant's mode is smaller
   than 16 bytes, the value is repeated across the rest of the array. */
   than 16 bytes, the value is repeated across the rest of the array. */
void
void
constant_to_array (enum machine_mode mode, rtx x, unsigned char arr[16])
constant_to_array (enum machine_mode mode, rtx x, unsigned char arr[16])
{
{
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
  int i, j, first;
  int i, j, first;
 
 
  memset (arr, 0, 16);
  memset (arr, 0, 16);
  mode = GET_MODE (x) != VOIDmode ? GET_MODE (x) : mode;
  mode = GET_MODE (x) != VOIDmode ? GET_MODE (x) : mode;
  if (GET_CODE (x) == CONST_INT
  if (GET_CODE (x) == CONST_INT
      || (GET_CODE (x) == CONST_DOUBLE
      || (GET_CODE (x) == CONST_DOUBLE
          && (mode == SFmode || mode == DFmode)))
          && (mode == SFmode || mode == DFmode)))
    {
    {
      gcc_assert (mode != VOIDmode && mode != BLKmode);
      gcc_assert (mode != VOIDmode && mode != BLKmode);
 
 
      if (GET_CODE (x) == CONST_DOUBLE)
      if (GET_CODE (x) == CONST_DOUBLE)
        val = const_double_to_hwint (x);
        val = const_double_to_hwint (x);
      else
      else
        val = INTVAL (x);
        val = INTVAL (x);
      first = GET_MODE_SIZE (mode) - 1;
      first = GET_MODE_SIZE (mode) - 1;
      for (i = first; i >= 0; i--)
      for (i = first; i >= 0; i--)
        {
        {
          arr[i] = val & 0xff;
          arr[i] = val & 0xff;
          val >>= 8;
          val >>= 8;
        }
        }
      /* Splat the constant across the whole array. */
      /* Splat the constant across the whole array. */
      for (j = 0, i = first + 1; i < 16; i++)
      for (j = 0, i = first + 1; i < 16; i++)
        {
        {
          arr[i] = arr[j];
          arr[i] = arr[j];
          j = (j == first) ? 0 : j + 1;
          j = (j == first) ? 0 : j + 1;
        }
        }
    }
    }
  else if (GET_CODE (x) == CONST_DOUBLE)
  else if (GET_CODE (x) == CONST_DOUBLE)
    {
    {
      val = CONST_DOUBLE_LOW (x);
      val = CONST_DOUBLE_LOW (x);
      for (i = 15; i >= 8; i--)
      for (i = 15; i >= 8; i--)
        {
        {
          arr[i] = val & 0xff;
          arr[i] = val & 0xff;
          val >>= 8;
          val >>= 8;
        }
        }
      val = CONST_DOUBLE_HIGH (x);
      val = CONST_DOUBLE_HIGH (x);
      for (i = 7; i >= 0; i--)
      for (i = 7; i >= 0; i--)
        {
        {
          arr[i] = val & 0xff;
          arr[i] = val & 0xff;
          val >>= 8;
          val >>= 8;
        }
        }
    }
    }
  else if (GET_CODE (x) == CONST_VECTOR)
  else if (GET_CODE (x) == CONST_VECTOR)
    {
    {
      int units;
      int units;
      rtx elt;
      rtx elt;
      mode = GET_MODE_INNER (mode);
      mode = GET_MODE_INNER (mode);
      units = CONST_VECTOR_NUNITS (x);
      units = CONST_VECTOR_NUNITS (x);
      for (i = 0; i < units; i++)
      for (i = 0; i < units; i++)
        {
        {
          elt = CONST_VECTOR_ELT (x, i);
          elt = CONST_VECTOR_ELT (x, i);
          if (GET_CODE (elt) == CONST_INT || GET_CODE (elt) == CONST_DOUBLE)
          if (GET_CODE (elt) == CONST_INT || GET_CODE (elt) == CONST_DOUBLE)
            {
            {
              if (GET_CODE (elt) == CONST_DOUBLE)
              if (GET_CODE (elt) == CONST_DOUBLE)
                val = const_double_to_hwint (elt);
                val = const_double_to_hwint (elt);
              else
              else
                val = INTVAL (elt);
                val = INTVAL (elt);
              first = GET_MODE_SIZE (mode) - 1;
              first = GET_MODE_SIZE (mode) - 1;
              if (first + i * GET_MODE_SIZE (mode) > 16)
              if (first + i * GET_MODE_SIZE (mode) > 16)
                abort ();
                abort ();
              for (j = first; j >= 0; j--)
              for (j = first; j >= 0; j--)
                {
                {
                  arr[j + i * GET_MODE_SIZE (mode)] = val & 0xff;
                  arr[j + i * GET_MODE_SIZE (mode)] = val & 0xff;
                  val >>= 8;
                  val >>= 8;
                }
                }
            }
            }
        }
        }
    }
    }
  else
  else
    gcc_unreachable();
    gcc_unreachable();
}
}
 
 
/* Convert a 16 byte array to a constant of mode MODE.  When MODE is
/* Convert a 16 byte array to a constant of mode MODE.  When MODE is
   smaller than 16 bytes, use the bytes that would represent that value
   smaller than 16 bytes, use the bytes that would represent that value
   in a register, e.g., for QImode return the value of arr[3].  */
   in a register, e.g., for QImode return the value of arr[3].  */
rtx
rtx
array_to_constant (enum machine_mode mode, const unsigned char arr[16])
array_to_constant (enum machine_mode mode, const unsigned char arr[16])
{
{
  enum machine_mode inner_mode;
  enum machine_mode inner_mode;
  rtvec v;
  rtvec v;
  int units, size, i, j, k;
  int units, size, i, j, k;
  HOST_WIDE_INT val;
  HOST_WIDE_INT val;
 
 
  if (GET_MODE_CLASS (mode) == MODE_INT
  if (GET_MODE_CLASS (mode) == MODE_INT
      && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
      && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
    {
    {
      j = GET_MODE_SIZE (mode);
      j = GET_MODE_SIZE (mode);
      i = j < 4 ? 4 - j : 0;
      i = j < 4 ? 4 - j : 0;
      for (val = 0; i < j; i++)
      for (val = 0; i < j; i++)
        val = (val << 8) | arr[i];
        val = (val << 8) | arr[i];
      val = trunc_int_for_mode (val, mode);
      val = trunc_int_for_mode (val, mode);
      return GEN_INT (val);
      return GEN_INT (val);
    }
    }
 
 
  if (mode == TImode)
  if (mode == TImode)
    {
    {
      HOST_WIDE_INT high;
      HOST_WIDE_INT high;
      for (i = high = 0; i < 8; i++)
      for (i = high = 0; i < 8; i++)
        high = (high << 8) | arr[i];
        high = (high << 8) | arr[i];
      for (i = 8, val = 0; i < 16; i++)
      for (i = 8, val = 0; i < 16; i++)
        val = (val << 8) | arr[i];
        val = (val << 8) | arr[i];
      return immed_double_const (val, high, TImode);
      return immed_double_const (val, high, TImode);
    }
    }
  if (mode == SFmode)
  if (mode == SFmode)
    {
    {
      val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
      val = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
      val = trunc_int_for_mode (val, SImode);
      val = trunc_int_for_mode (val, SImode);
      return hwint_to_const_double (SFmode, val);
      return hwint_to_const_double (SFmode, val);
    }
    }
  if (mode == DFmode)
  if (mode == DFmode)
    {
    {
      for (i = 0, val = 0; i < 8; i++)
      for (i = 0, val = 0; i < 8; i++)
        val = (val << 8) | arr[i];
        val = (val << 8) | arr[i];
      return hwint_to_const_double (DFmode, val);
      return hwint_to_const_double (DFmode, val);
    }
    }
 
 
  if (!VECTOR_MODE_P (mode))
  if (!VECTOR_MODE_P (mode))
    abort ();
    abort ();
 
 
  units = GET_MODE_NUNITS (mode);
  units = GET_MODE_NUNITS (mode);
  size = GET_MODE_UNIT_SIZE (mode);
  size = GET_MODE_UNIT_SIZE (mode);
  inner_mode = GET_MODE_INNER (mode);
  inner_mode = GET_MODE_INNER (mode);
  v = rtvec_alloc (units);
  v = rtvec_alloc (units);
 
 
  for (k = i = 0; i < units; ++i)
  for (k = i = 0; i < units; ++i)
    {
    {
      val = 0;
      val = 0;
      for (j = 0; j < size; j++, k++)
      for (j = 0; j < size; j++, k++)
        val = (val << 8) | arr[k];
        val = (val << 8) | arr[k];
 
 
      if (GET_MODE_CLASS (inner_mode) == MODE_FLOAT)
      if (GET_MODE_CLASS (inner_mode) == MODE_FLOAT)
        RTVEC_ELT (v, i) = hwint_to_const_double (inner_mode, val);
        RTVEC_ELT (v, i) = hwint_to_const_double (inner_mode, val);
      else
      else
        RTVEC_ELT (v, i) = GEN_INT (trunc_int_for_mode (val, inner_mode));
        RTVEC_ELT (v, i) = GEN_INT (trunc_int_for_mode (val, inner_mode));
    }
    }
  if (k > 16)
  if (k > 16)
    abort ();
    abort ();
 
 
  return gen_rtx_CONST_VECTOR (mode, v);
  return gen_rtx_CONST_VECTOR (mode, v);
}
}
 
 
static void
static void
reloc_diagnostic (rtx x)
reloc_diagnostic (rtx x)
{
{
  tree decl = 0;
  tree decl = 0;
  if (!flag_pic || !(TARGET_WARN_RELOC || TARGET_ERROR_RELOC))
  if (!flag_pic || !(TARGET_WARN_RELOC || TARGET_ERROR_RELOC))
    return;
    return;
 
 
  if (GET_CODE (x) == SYMBOL_REF)
  if (GET_CODE (x) == SYMBOL_REF)
    decl = SYMBOL_REF_DECL (x);
    decl = SYMBOL_REF_DECL (x);
  else if (GET_CODE (x) == CONST
  else if (GET_CODE (x) == CONST
           && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)
           && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)
    decl = SYMBOL_REF_DECL (XEXP (XEXP (x, 0), 0));
    decl = SYMBOL_REF_DECL (XEXP (XEXP (x, 0), 0));
 
 
  /* SYMBOL_REF_DECL is not necessarily a DECL. */
  /* SYMBOL_REF_DECL is not necessarily a DECL. */
  if (decl && !DECL_P (decl))
  if (decl && !DECL_P (decl))
    decl = 0;
    decl = 0;
 
 
  /* The decl could be a string constant.  */
  /* The decl could be a string constant.  */
  if (decl && DECL_P (decl))
  if (decl && DECL_P (decl))
    {
    {
      location_t loc;
      location_t loc;
      /* We use last_assemble_variable_decl to get line information.  It's
      /* We use last_assemble_variable_decl to get line information.  It's
         not always going to be right and might not even be close, but will
         not always going to be right and might not even be close, but will
         be right for the more common cases. */
         be right for the more common cases. */
      if (!last_assemble_variable_decl || in_section == ctors_section)
      if (!last_assemble_variable_decl || in_section == ctors_section)
        loc = DECL_SOURCE_LOCATION (decl);
        loc = DECL_SOURCE_LOCATION (decl);
      else
      else
        loc = DECL_SOURCE_LOCATION (last_assemble_variable_decl);
        loc = DECL_SOURCE_LOCATION (last_assemble_variable_decl);
 
 
      if (TARGET_WARN_RELOC)
      if (TARGET_WARN_RELOC)
        warning_at (loc, 0,
        warning_at (loc, 0,
                    "creating run-time relocation for %qD", decl);
                    "creating run-time relocation for %qD", decl);
      else
      else
        error_at (loc,
        error_at (loc,
                  "creating run-time relocation for %qD", decl);
                  "creating run-time relocation for %qD", decl);
    }
    }
  else
  else
    {
    {
      if (TARGET_WARN_RELOC)
      if (TARGET_WARN_RELOC)
        warning_at (input_location, 0, "creating run-time relocation");
        warning_at (input_location, 0, "creating run-time relocation");
      else
      else
        error_at (input_location, "creating run-time relocation");
        error_at (input_location, "creating run-time relocation");
    }
    }
}
}
 
 
/* Hook into assemble_integer so we can generate an error for run-time
/* Hook into assemble_integer so we can generate an error for run-time
   relocations.  The SPU ABI disallows them. */
   relocations.  The SPU ABI disallows them. */
static bool
static bool
spu_assemble_integer (rtx x, unsigned int size, int aligned_p)
spu_assemble_integer (rtx x, unsigned int size, int aligned_p)
{
{
  /* By default run-time relocations aren't supported, but we allow them
  /* By default run-time relocations aren't supported, but we allow them
     in case users support it in their own run-time loader.  And we provide
     in case users support it in their own run-time loader.  And we provide
     a warning for those users that don't.  */
     a warning for those users that don't.  */
  if ((GET_CODE (x) == SYMBOL_REF)
  if ((GET_CODE (x) == SYMBOL_REF)
      || GET_CODE (x) == LABEL_REF || GET_CODE (x) == CONST)
      || GET_CODE (x) == LABEL_REF || GET_CODE (x) == CONST)
    reloc_diagnostic (x);
    reloc_diagnostic (x);
 
 
  return default_assemble_integer (x, size, aligned_p);
  return default_assemble_integer (x, size, aligned_p);
}
}
 
 
static void
static void
spu_asm_globalize_label (FILE * file, const char *name)
spu_asm_globalize_label (FILE * file, const char *name)
{
{
  fputs ("\t.global\t", file);
  fputs ("\t.global\t", file);
  assemble_name (file, name);
  assemble_name (file, name);
  fputs ("\n", file);
  fputs ("\n", file);
}
}
 
 
static bool
static bool
spu_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total,
spu_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total,
               bool speed ATTRIBUTE_UNUSED)
               bool speed ATTRIBUTE_UNUSED)
{
{
  enum machine_mode mode = GET_MODE (x);
  enum machine_mode mode = GET_MODE (x);
  int cost = COSTS_N_INSNS (2);
  int cost = COSTS_N_INSNS (2);
 
 
  /* Folding to a CONST_VECTOR will use extra space but there might
  /* Folding to a CONST_VECTOR will use extra space but there might
     be only a small savings in cycles.  We'd like to use a CONST_VECTOR
     be only a small savings in cycles.  We'd like to use a CONST_VECTOR
     only if it allows us to fold away multiple insns.  Changing the cost
     only if it allows us to fold away multiple insns.  Changing the cost
     of a CONST_VECTOR here (or in CONST_COSTS) doesn't help though
     of a CONST_VECTOR here (or in CONST_COSTS) doesn't help though
     because this cost will only be compared against a single insn.
     because this cost will only be compared against a single insn.
     if (code == CONST_VECTOR)
     if (code == CONST_VECTOR)
       return (LEGITIMATE_CONSTANT_P(x)) ? cost : COSTS_N_INSNS(6);
       return (LEGITIMATE_CONSTANT_P(x)) ? cost : COSTS_N_INSNS(6);
   */
   */
 
 
  /* Use defaults for float operations.  Not accurate but good enough. */
  /* Use defaults for float operations.  Not accurate but good enough. */
  if (mode == DFmode)
  if (mode == DFmode)
    {
    {
      *total = COSTS_N_INSNS (13);
      *total = COSTS_N_INSNS (13);
      return true;
      return true;
    }
    }
  if (mode == SFmode)
  if (mode == SFmode)
    {
    {
      *total = COSTS_N_INSNS (6);
      *total = COSTS_N_INSNS (6);
      return true;
      return true;
    }
    }
  switch (code)
  switch (code)
    {
    {
    case CONST_INT:
    case CONST_INT:
      if (satisfies_constraint_K (x))
      if (satisfies_constraint_K (x))
        *total = 0;
        *total = 0;
      else if (INTVAL (x) >= -0x80000000ll && INTVAL (x) <= 0xffffffffll)
      else if (INTVAL (x) >= -0x80000000ll && INTVAL (x) <= 0xffffffffll)
        *total = COSTS_N_INSNS (1);
        *total = COSTS_N_INSNS (1);
      else
      else
        *total = COSTS_N_INSNS (3);
        *total = COSTS_N_INSNS (3);
      return true;
      return true;
 
 
    case CONST:
    case CONST:
      *total = COSTS_N_INSNS (3);
      *total = COSTS_N_INSNS (3);
      return true;
      return true;
 
 
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
      *total = COSTS_N_INSNS (0);
      *total = COSTS_N_INSNS (0);
      return true;
      return true;
 
 
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      *total = COSTS_N_INSNS (5);
      *total = COSTS_N_INSNS (5);
      return true;
      return true;
 
 
    case FLOAT_EXTEND:
    case FLOAT_EXTEND:
    case FLOAT_TRUNCATE:
    case FLOAT_TRUNCATE:
    case FLOAT:
    case FLOAT:
    case UNSIGNED_FLOAT:
    case UNSIGNED_FLOAT:
    case FIX:
    case FIX:
    case UNSIGNED_FIX:
    case UNSIGNED_FIX:
      *total = COSTS_N_INSNS (7);
      *total = COSTS_N_INSNS (7);
      return true;
      return true;
 
 
    case PLUS:
    case PLUS:
      if (mode == TImode)
      if (mode == TImode)
        {
        {
          *total = COSTS_N_INSNS (9);
          *total = COSTS_N_INSNS (9);
          return true;
          return true;
        }
        }
      break;
      break;
 
 
    case MULT:
    case MULT:
      cost =
      cost =
        GET_CODE (XEXP (x, 0)) ==
        GET_CODE (XEXP (x, 0)) ==
        REG ? COSTS_N_INSNS (12) : COSTS_N_INSNS (7);
        REG ? COSTS_N_INSNS (12) : COSTS_N_INSNS (7);
      if (mode == SImode && GET_CODE (XEXP (x, 0)) == REG)
      if (mode == SImode && GET_CODE (XEXP (x, 0)) == REG)
        {
        {
          if (GET_CODE (XEXP (x, 1)) == CONST_INT)
          if (GET_CODE (XEXP (x, 1)) == CONST_INT)
            {
            {
              HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
              HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
              cost = COSTS_N_INSNS (14);
              cost = COSTS_N_INSNS (14);
              if ((val & 0xffff) == 0)
              if ((val & 0xffff) == 0)
                cost = COSTS_N_INSNS (9);
                cost = COSTS_N_INSNS (9);
              else if (val > 0 && val < 0x10000)
              else if (val > 0 && val < 0x10000)
                cost = COSTS_N_INSNS (11);
                cost = COSTS_N_INSNS (11);
            }
            }
        }
        }
      *total = cost;
      *total = cost;
      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 ROTATE:
    case ROTATE:
    case ROTATERT:
    case ROTATERT:
    case ASHIFT:
    case ASHIFT:
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
      *total = COSTS_N_INSNS (4);
      *total = COSTS_N_INSNS (4);
      return true;
      return true;
    case UNSPEC:
    case UNSPEC:
      if (XINT (x, 1) == UNSPEC_CONVERT)
      if (XINT (x, 1) == UNSPEC_CONVERT)
        *total = COSTS_N_INSNS (0);
        *total = COSTS_N_INSNS (0);
      else
      else
        *total = COSTS_N_INSNS (4);
        *total = COSTS_N_INSNS (4);
      return true;
      return true;
    }
    }
  /* Scale cost by mode size.  Except when initializing (cfun->decl == 0). */
  /* Scale cost by mode size.  Except when initializing (cfun->decl == 0). */
  if (GET_MODE_CLASS (mode) == MODE_INT
  if (GET_MODE_CLASS (mode) == MODE_INT
      && GET_MODE_SIZE (mode) > GET_MODE_SIZE (SImode) && cfun && cfun->decl)
      && GET_MODE_SIZE (mode) > GET_MODE_SIZE (SImode) && cfun && cfun->decl)
    cost = cost * (GET_MODE_SIZE (mode) / GET_MODE_SIZE (SImode))
    cost = cost * (GET_MODE_SIZE (mode) / GET_MODE_SIZE (SImode))
      * (GET_MODE_SIZE (mode) / GET_MODE_SIZE (SImode));
      * (GET_MODE_SIZE (mode) / GET_MODE_SIZE (SImode));
  *total = cost;
  *total = cost;
  return true;
  return true;
}
}
 
 
static enum machine_mode
static enum machine_mode
spu_unwind_word_mode (void)
spu_unwind_word_mode (void)
{
{
  return SImode;
  return SImode;
}
}
 
 
/* Decide whether we can make a sibling call to a function.  DECL is the
/* Decide whether we can make a sibling call to a function.  DECL is the
   declaration of the function being targeted by the call and EXP is the
   declaration of the function being targeted by the call and EXP is the
   CALL_EXPR representing the call.  */
   CALL_EXPR representing the call.  */
static bool
static bool
spu_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
spu_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
{
  return decl && !TARGET_LARGE_MEM;
  return decl && !TARGET_LARGE_MEM;
}
}
 
 
/* We need to correctly update the back chain pointer and the Available
/* We need to correctly update the back chain pointer and the Available
   Stack Size (which is in the second slot of the sp register.) */
   Stack Size (which is in the second slot of the sp register.) */
void
void
spu_allocate_stack (rtx op0, rtx op1)
spu_allocate_stack (rtx op0, rtx op1)
{
{
  HOST_WIDE_INT v;
  HOST_WIDE_INT v;
  rtx chain = gen_reg_rtx (V4SImode);
  rtx chain = gen_reg_rtx (V4SImode);
  rtx stack_bot = gen_frame_mem (V4SImode, stack_pointer_rtx);
  rtx stack_bot = gen_frame_mem (V4SImode, stack_pointer_rtx);
  rtx sp = gen_reg_rtx (V4SImode);
  rtx sp = gen_reg_rtx (V4SImode);
  rtx splatted = gen_reg_rtx (V4SImode);
  rtx splatted = gen_reg_rtx (V4SImode);
  rtx pat = gen_reg_rtx (TImode);
  rtx pat = gen_reg_rtx (TImode);
 
 
  /* copy the back chain so we can save it back again. */
  /* copy the back chain so we can save it back again. */
  emit_move_insn (chain, stack_bot);
  emit_move_insn (chain, stack_bot);
 
 
  op1 = force_reg (SImode, op1);
  op1 = force_reg (SImode, op1);
 
 
  v = 0x1020300010203ll;
  v = 0x1020300010203ll;
  emit_move_insn (pat, immed_double_const (v, v, TImode));
  emit_move_insn (pat, immed_double_const (v, v, TImode));
  emit_insn (gen_shufb (splatted, op1, op1, pat));
  emit_insn (gen_shufb (splatted, op1, op1, pat));
 
 
  emit_insn (gen_spu_convert (sp, stack_pointer_rtx));
  emit_insn (gen_spu_convert (sp, stack_pointer_rtx));
  emit_insn (gen_subv4si3 (sp, sp, splatted));
  emit_insn (gen_subv4si3 (sp, sp, splatted));
 
 
  if (flag_stack_check)
  if (flag_stack_check)
    {
    {
      rtx avail = gen_reg_rtx(SImode);
      rtx avail = gen_reg_rtx(SImode);
      rtx result = gen_reg_rtx(SImode);
      rtx result = gen_reg_rtx(SImode);
      emit_insn (gen_vec_extractv4si (avail, sp, GEN_INT (1)));
      emit_insn (gen_vec_extractv4si (avail, sp, GEN_INT (1)));
      emit_insn (gen_cgt_si(result, avail, GEN_INT (-1)));
      emit_insn (gen_cgt_si(result, avail, GEN_INT (-1)));
      emit_insn (gen_spu_heq (result, GEN_INT(0) ));
      emit_insn (gen_spu_heq (result, GEN_INT(0) ));
    }
    }
 
 
  emit_insn (gen_spu_convert (stack_pointer_rtx, sp));
  emit_insn (gen_spu_convert (stack_pointer_rtx, sp));
 
 
  emit_move_insn (stack_bot, chain);
  emit_move_insn (stack_bot, chain);
 
 
  emit_move_insn (op0, virtual_stack_dynamic_rtx);
  emit_move_insn (op0, virtual_stack_dynamic_rtx);
}
}
 
 
void
void
spu_restore_stack_nonlocal (rtx op0 ATTRIBUTE_UNUSED, rtx op1)
spu_restore_stack_nonlocal (rtx op0 ATTRIBUTE_UNUSED, rtx op1)
{
{
  static unsigned char arr[16] =
  static unsigned char arr[16] =
    { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 };
    { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 };
  rtx temp = gen_reg_rtx (SImode);
  rtx temp = gen_reg_rtx (SImode);
  rtx temp2 = gen_reg_rtx (SImode);
  rtx temp2 = gen_reg_rtx (SImode);
  rtx temp3 = gen_reg_rtx (V4SImode);
  rtx temp3 = gen_reg_rtx (V4SImode);
  rtx temp4 = gen_reg_rtx (V4SImode);
  rtx temp4 = gen_reg_rtx (V4SImode);
  rtx pat = gen_reg_rtx (TImode);
  rtx pat = gen_reg_rtx (TImode);
  rtx sp = gen_rtx_REG (V4SImode, STACK_POINTER_REGNUM);
  rtx sp = gen_rtx_REG (V4SImode, STACK_POINTER_REGNUM);
 
 
  /* Restore the backchain from the first word, sp from the second.  */
  /* Restore the backchain from the first word, sp from the second.  */
  emit_move_insn (temp2, adjust_address_nv (op1, SImode, 0));
  emit_move_insn (temp2, adjust_address_nv (op1, SImode, 0));
  emit_move_insn (temp, adjust_address_nv (op1, SImode, 4));
  emit_move_insn (temp, adjust_address_nv (op1, SImode, 4));
 
 
  emit_move_insn (pat, array_to_constant (TImode, arr));
  emit_move_insn (pat, array_to_constant (TImode, arr));
 
 
  /* Compute Available Stack Size for sp */
  /* Compute Available Stack Size for sp */
  emit_insn (gen_subsi3 (temp, temp, stack_pointer_rtx));
  emit_insn (gen_subsi3 (temp, temp, stack_pointer_rtx));
  emit_insn (gen_shufb (temp3, temp, temp, pat));
  emit_insn (gen_shufb (temp3, temp, temp, pat));
 
 
  /* Compute Available Stack Size for back chain */
  /* Compute Available Stack Size for back chain */
  emit_insn (gen_subsi3 (temp2, temp2, stack_pointer_rtx));
  emit_insn (gen_subsi3 (temp2, temp2, stack_pointer_rtx));
  emit_insn (gen_shufb (temp4, temp2, temp2, pat));
  emit_insn (gen_shufb (temp4, temp2, temp2, pat));
  emit_insn (gen_addv4si3 (temp4, sp, temp4));
  emit_insn (gen_addv4si3 (temp4, sp, temp4));
 
 
  emit_insn (gen_addv4si3 (sp, sp, temp3));
  emit_insn (gen_addv4si3 (sp, sp, temp3));
  emit_move_insn (gen_frame_mem (V4SImode, stack_pointer_rtx), temp4);
  emit_move_insn (gen_frame_mem (V4SImode, stack_pointer_rtx), temp4);
}
}
 
 
static void
static void
spu_init_libfuncs (void)
spu_init_libfuncs (void)
{
{
  set_optab_libfunc (smul_optab, DImode, "__muldi3");
  set_optab_libfunc (smul_optab, DImode, "__muldi3");
  set_optab_libfunc (sdiv_optab, DImode, "__divdi3");
  set_optab_libfunc (sdiv_optab, DImode, "__divdi3");
  set_optab_libfunc (smod_optab, DImode, "__moddi3");
  set_optab_libfunc (smod_optab, DImode, "__moddi3");
  set_optab_libfunc (udiv_optab, DImode, "__udivdi3");
  set_optab_libfunc (udiv_optab, DImode, "__udivdi3");
  set_optab_libfunc (umod_optab, DImode, "__umoddi3");
  set_optab_libfunc (umod_optab, DImode, "__umoddi3");
  set_optab_libfunc (udivmod_optab, DImode, "__udivmoddi4");
  set_optab_libfunc (udivmod_optab, DImode, "__udivmoddi4");
  set_optab_libfunc (ffs_optab, DImode, "__ffsdi2");
  set_optab_libfunc (ffs_optab, DImode, "__ffsdi2");
  set_optab_libfunc (clz_optab, DImode, "__clzdi2");
  set_optab_libfunc (clz_optab, DImode, "__clzdi2");
  set_optab_libfunc (ctz_optab, DImode, "__ctzdi2");
  set_optab_libfunc (ctz_optab, DImode, "__ctzdi2");
  set_optab_libfunc (popcount_optab, DImode, "__popcountdi2");
  set_optab_libfunc (popcount_optab, DImode, "__popcountdi2");
  set_optab_libfunc (parity_optab, DImode, "__paritydi2");
  set_optab_libfunc (parity_optab, DImode, "__paritydi2");
 
 
  set_conv_libfunc (ufloat_optab, DFmode, SImode, "__float_unssidf");
  set_conv_libfunc (ufloat_optab, DFmode, SImode, "__float_unssidf");
  set_conv_libfunc (ufloat_optab, DFmode, DImode, "__float_unsdidf");
  set_conv_libfunc (ufloat_optab, DFmode, DImode, "__float_unsdidf");
 
 
  set_optab_libfunc (smul_optab, TImode, "__multi3");
  set_optab_libfunc (smul_optab, TImode, "__multi3");
  set_optab_libfunc (sdiv_optab, TImode, "__divti3");
  set_optab_libfunc (sdiv_optab, TImode, "__divti3");
  set_optab_libfunc (smod_optab, TImode, "__modti3");
  set_optab_libfunc (smod_optab, TImode, "__modti3");
  set_optab_libfunc (udiv_optab, TImode, "__udivti3");
  set_optab_libfunc (udiv_optab, TImode, "__udivti3");
  set_optab_libfunc (umod_optab, TImode, "__umodti3");
  set_optab_libfunc (umod_optab, TImode, "__umodti3");
  set_optab_libfunc (udivmod_optab, TImode, "__udivmodti4");
  set_optab_libfunc (udivmod_optab, TImode, "__udivmodti4");
}
}
 
 
/* Make a subreg, stripping any existing subreg.  We could possibly just
/* Make a subreg, stripping any existing subreg.  We could possibly just
   call simplify_subreg, but in this case we know what we want. */
   call simplify_subreg, but in this case we know what we want. */
rtx
rtx
spu_gen_subreg (enum machine_mode mode, rtx x)
spu_gen_subreg (enum machine_mode mode, rtx x)
{
{
  if (GET_CODE (x) == SUBREG)
  if (GET_CODE (x) == SUBREG)
    x = SUBREG_REG (x);
    x = SUBREG_REG (x);
  if (GET_MODE (x) == mode)
  if (GET_MODE (x) == mode)
    return x;
    return x;
  return gen_rtx_SUBREG (mode, x, 0);
  return gen_rtx_SUBREG (mode, x, 0);
}
}
 
 
static bool
static bool
spu_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
spu_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
{
  return (TYPE_MODE (type) == BLKmode
  return (TYPE_MODE (type) == BLKmode
          && ((type) == 0
          && ((type) == 0
              || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
              || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
              || int_size_in_bytes (type) >
              || int_size_in_bytes (type) >
              (MAX_REGISTER_RETURN * UNITS_PER_WORD)));
              (MAX_REGISTER_RETURN * UNITS_PER_WORD)));
}
}


/* Create the built-in types and functions */
/* Create the built-in types and functions */
 
 
enum spu_function_code
enum spu_function_code
{
{
#define DEF_BUILTIN(fcode, icode, name, type, params) fcode,
#define DEF_BUILTIN(fcode, icode, name, type, params) fcode,
#include "spu-builtins.def"
#include "spu-builtins.def"
#undef DEF_BUILTIN
#undef DEF_BUILTIN
   NUM_SPU_BUILTINS
   NUM_SPU_BUILTINS
};
};
 
 
extern GTY(()) struct spu_builtin_description spu_builtins[NUM_SPU_BUILTINS];
extern GTY(()) struct spu_builtin_description spu_builtins[NUM_SPU_BUILTINS];
 
 
struct spu_builtin_description spu_builtins[] = {
struct spu_builtin_description spu_builtins[] = {
#define DEF_BUILTIN(fcode, icode, name, type, params) \
#define DEF_BUILTIN(fcode, icode, name, type, params) \
  {fcode, icode, name, type, params, NULL_TREE},
  {fcode, icode, name, type, params, NULL_TREE},
#include "spu-builtins.def"
#include "spu-builtins.def"
#undef DEF_BUILTIN
#undef DEF_BUILTIN
};
};
 
 
/* Returns the rs6000 builtin decl for CODE.  */
/* Returns the rs6000 builtin decl for CODE.  */
 
 
static tree
static tree
spu_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
spu_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
{
{
  if (code >= NUM_SPU_BUILTINS)
  if (code >= NUM_SPU_BUILTINS)
    return error_mark_node;
    return error_mark_node;
 
 
  return spu_builtins[code].fndecl;
  return spu_builtins[code].fndecl;
}
}
 
 
 
 
static void
static void
spu_init_builtins (void)
spu_init_builtins (void)
{
{
  struct spu_builtin_description *d;
  struct spu_builtin_description *d;
  unsigned int i;
  unsigned int i;
 
 
  V16QI_type_node = build_vector_type (intQI_type_node, 16);
  V16QI_type_node = build_vector_type (intQI_type_node, 16);
  V8HI_type_node = build_vector_type (intHI_type_node, 8);
  V8HI_type_node = build_vector_type (intHI_type_node, 8);
  V4SI_type_node = build_vector_type (intSI_type_node, 4);
  V4SI_type_node = build_vector_type (intSI_type_node, 4);
  V2DI_type_node = build_vector_type (intDI_type_node, 2);
  V2DI_type_node = build_vector_type (intDI_type_node, 2);
  V4SF_type_node = build_vector_type (float_type_node, 4);
  V4SF_type_node = build_vector_type (float_type_node, 4);
  V2DF_type_node = build_vector_type (double_type_node, 2);
  V2DF_type_node = build_vector_type (double_type_node, 2);
 
 
  unsigned_V16QI_type_node = build_vector_type (unsigned_intQI_type_node, 16);
  unsigned_V16QI_type_node = build_vector_type (unsigned_intQI_type_node, 16);
  unsigned_V8HI_type_node = build_vector_type (unsigned_intHI_type_node, 8);
  unsigned_V8HI_type_node = build_vector_type (unsigned_intHI_type_node, 8);
  unsigned_V4SI_type_node = build_vector_type (unsigned_intSI_type_node, 4);
  unsigned_V4SI_type_node = build_vector_type (unsigned_intSI_type_node, 4);
  unsigned_V2DI_type_node = build_vector_type (unsigned_intDI_type_node, 2);
  unsigned_V2DI_type_node = build_vector_type (unsigned_intDI_type_node, 2);
 
 
  spu_builtin_types[SPU_BTI_QUADWORD] = V16QI_type_node;
  spu_builtin_types[SPU_BTI_QUADWORD] = V16QI_type_node;
 
 
  spu_builtin_types[SPU_BTI_7] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_7] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S7] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S7] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U7] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U7] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S10] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S10] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S10_4] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S10_4] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U14] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U14] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_16] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_16] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S16] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S16] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S16_2] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_S16_2] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U16] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U16] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U16_2] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U16_2] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U18] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_U18] = global_trees[TI_INTSI_TYPE];
 
 
  spu_builtin_types[SPU_BTI_INTQI] = global_trees[TI_INTQI_TYPE];
  spu_builtin_types[SPU_BTI_INTQI] = global_trees[TI_INTQI_TYPE];
  spu_builtin_types[SPU_BTI_INTHI] = global_trees[TI_INTHI_TYPE];
  spu_builtin_types[SPU_BTI_INTHI] = global_trees[TI_INTHI_TYPE];
  spu_builtin_types[SPU_BTI_INTSI] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_INTSI] = global_trees[TI_INTSI_TYPE];
  spu_builtin_types[SPU_BTI_INTDI] = global_trees[TI_INTDI_TYPE];
  spu_builtin_types[SPU_BTI_INTDI] = global_trees[TI_INTDI_TYPE];
  spu_builtin_types[SPU_BTI_UINTQI] = global_trees[TI_UINTQI_TYPE];
  spu_builtin_types[SPU_BTI_UINTQI] = global_trees[TI_UINTQI_TYPE];
  spu_builtin_types[SPU_BTI_UINTHI] = global_trees[TI_UINTHI_TYPE];
  spu_builtin_types[SPU_BTI_UINTHI] = global_trees[TI_UINTHI_TYPE];
  spu_builtin_types[SPU_BTI_UINTSI] = global_trees[TI_UINTSI_TYPE];
  spu_builtin_types[SPU_BTI_UINTSI] = global_trees[TI_UINTSI_TYPE];
  spu_builtin_types[SPU_BTI_UINTDI] = global_trees[TI_UINTDI_TYPE];
  spu_builtin_types[SPU_BTI_UINTDI] = global_trees[TI_UINTDI_TYPE];
 
 
  spu_builtin_types[SPU_BTI_FLOAT] = global_trees[TI_FLOAT_TYPE];
  spu_builtin_types[SPU_BTI_FLOAT] = global_trees[TI_FLOAT_TYPE];
  spu_builtin_types[SPU_BTI_DOUBLE] = global_trees[TI_DOUBLE_TYPE];
  spu_builtin_types[SPU_BTI_DOUBLE] = global_trees[TI_DOUBLE_TYPE];
 
 
  spu_builtin_types[SPU_BTI_VOID] = global_trees[TI_VOID_TYPE];
  spu_builtin_types[SPU_BTI_VOID] = global_trees[TI_VOID_TYPE];
 
 
  spu_builtin_types[SPU_BTI_PTR] =
  spu_builtin_types[SPU_BTI_PTR] =
    build_pointer_type (build_qualified_type
    build_pointer_type (build_qualified_type
                        (void_type_node,
                        (void_type_node,
                         TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
                         TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
 
 
  /* For each builtin we build a new prototype.  The tree code will make
  /* For each builtin we build a new prototype.  The tree code will make
     sure nodes are shared. */
     sure nodes are shared. */
  for (i = 0, d = spu_builtins; i < NUM_SPU_BUILTINS; i++, d++)
  for (i = 0, d = spu_builtins; i < NUM_SPU_BUILTINS; i++, d++)
    {
    {
      tree p;
      tree p;
      char name[64];            /* build_function will make a copy. */
      char name[64];            /* build_function will make a copy. */
      int parm;
      int parm;
 
 
      if (d->name == 0)
      if (d->name == 0)
        continue;
        continue;
 
 
      /* Find last parm.  */
      /* Find last parm.  */
      for (parm = 1; d->parm[parm] != SPU_BTI_END_OF_PARAMS; parm++)
      for (parm = 1; d->parm[parm] != SPU_BTI_END_OF_PARAMS; parm++)
        ;
        ;
 
 
      p = void_list_node;
      p = void_list_node;
      while (parm > 1)
      while (parm > 1)
        p = tree_cons (NULL_TREE, spu_builtin_types[d->parm[--parm]], p);
        p = tree_cons (NULL_TREE, spu_builtin_types[d->parm[--parm]], p);
 
 
      p = build_function_type (spu_builtin_types[d->parm[0]], p);
      p = build_function_type (spu_builtin_types[d->parm[0]], p);
 
 
      sprintf (name, "__builtin_%s", d->name);
      sprintf (name, "__builtin_%s", d->name);
      d->fndecl =
      d->fndecl =
        add_builtin_function (name, p, END_BUILTINS + i, BUILT_IN_MD,
        add_builtin_function (name, p, END_BUILTINS + i, BUILT_IN_MD,
                              NULL, NULL_TREE);
                              NULL, NULL_TREE);
      if (d->fcode == SPU_MASK_FOR_LOAD)
      if (d->fcode == SPU_MASK_FOR_LOAD)
        TREE_READONLY (d->fndecl) = 1;
        TREE_READONLY (d->fndecl) = 1;
 
 
      /* These builtins don't throw.  */
      /* These builtins don't throw.  */
      TREE_NOTHROW (d->fndecl) = 1;
      TREE_NOTHROW (d->fndecl) = 1;
    }
    }
}
}
 
 
void
void
spu_restore_stack_block (rtx op0 ATTRIBUTE_UNUSED, rtx op1)
spu_restore_stack_block (rtx op0 ATTRIBUTE_UNUSED, rtx op1)
{
{
  static unsigned char arr[16] =
  static unsigned char arr[16] =
    { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 };
    { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 };
 
 
  rtx temp = gen_reg_rtx (Pmode);
  rtx temp = gen_reg_rtx (Pmode);
  rtx temp2 = gen_reg_rtx (V4SImode);
  rtx temp2 = gen_reg_rtx (V4SImode);
  rtx temp3 = gen_reg_rtx (V4SImode);
  rtx temp3 = gen_reg_rtx (V4SImode);
  rtx pat = gen_reg_rtx (TImode);
  rtx pat = gen_reg_rtx (TImode);
  rtx sp = gen_rtx_REG (V4SImode, STACK_POINTER_REGNUM);
  rtx sp = gen_rtx_REG (V4SImode, STACK_POINTER_REGNUM);
 
 
  emit_move_insn (pat, array_to_constant (TImode, arr));
  emit_move_insn (pat, array_to_constant (TImode, arr));
 
 
  /* Restore the sp.  */
  /* Restore the sp.  */
  emit_move_insn (temp, op1);
  emit_move_insn (temp, op1);
  emit_move_insn (temp2, gen_frame_mem (V4SImode, stack_pointer_rtx));
  emit_move_insn (temp2, gen_frame_mem (V4SImode, stack_pointer_rtx));
 
 
  /* Compute available stack size for sp.  */
  /* Compute available stack size for sp.  */
  emit_insn (gen_subsi3 (temp, temp, stack_pointer_rtx));
  emit_insn (gen_subsi3 (temp, temp, stack_pointer_rtx));
  emit_insn (gen_shufb (temp3, temp, temp, pat));
  emit_insn (gen_shufb (temp3, temp, temp, pat));
 
 
  emit_insn (gen_addv4si3 (sp, sp, temp3));
  emit_insn (gen_addv4si3 (sp, sp, temp3));
  emit_move_insn (gen_frame_mem (V4SImode, stack_pointer_rtx), temp2);
  emit_move_insn (gen_frame_mem (V4SImode, stack_pointer_rtx), temp2);
}
}
 
 
int
int
spu_safe_dma (HOST_WIDE_INT channel)
spu_safe_dma (HOST_WIDE_INT channel)
{
{
  return TARGET_SAFE_DMA && channel >= 21 && channel <= 27;
  return TARGET_SAFE_DMA && channel >= 21 && channel <= 27;
}
}
 
 
void
void
spu_builtin_splats (rtx ops[])
spu_builtin_splats (rtx ops[])
{
{
  enum machine_mode mode = GET_MODE (ops[0]);
  enum machine_mode mode = GET_MODE (ops[0]);
  if (GET_CODE (ops[1]) == CONST_INT || GET_CODE (ops[1]) == CONST_DOUBLE)
  if (GET_CODE (ops[1]) == CONST_INT || GET_CODE (ops[1]) == CONST_DOUBLE)
    {
    {
      unsigned char arr[16];
      unsigned char arr[16];
      constant_to_array (GET_MODE_INNER (mode), ops[1], arr);
      constant_to_array (GET_MODE_INNER (mode), ops[1], arr);
      emit_move_insn (ops[0], array_to_constant (mode, arr));
      emit_move_insn (ops[0], array_to_constant (mode, arr));
    }
    }
  else
  else
    {
    {
      rtx reg = gen_reg_rtx (TImode);
      rtx reg = gen_reg_rtx (TImode);
      rtx shuf;
      rtx shuf;
      if (GET_CODE (ops[1]) != REG
      if (GET_CODE (ops[1]) != REG
          && GET_CODE (ops[1]) != SUBREG)
          && GET_CODE (ops[1]) != SUBREG)
        ops[1] = force_reg (GET_MODE_INNER (mode), ops[1]);
        ops[1] = force_reg (GET_MODE_INNER (mode), ops[1]);
      switch (mode)
      switch (mode)
        {
        {
        case V2DImode:
        case V2DImode:
        case V2DFmode:
        case V2DFmode:
          shuf =
          shuf =
            immed_double_const (0x0001020304050607ll, 0x1011121314151617ll,
            immed_double_const (0x0001020304050607ll, 0x1011121314151617ll,
                                TImode);
                                TImode);
          break;
          break;
        case V4SImode:
        case V4SImode:
        case V4SFmode:
        case V4SFmode:
          shuf =
          shuf =
            immed_double_const (0x0001020300010203ll, 0x0001020300010203ll,
            immed_double_const (0x0001020300010203ll, 0x0001020300010203ll,
                                TImode);
                                TImode);
          break;
          break;
        case V8HImode:
        case V8HImode:
          shuf =
          shuf =
            immed_double_const (0x0203020302030203ll, 0x0203020302030203ll,
            immed_double_const (0x0203020302030203ll, 0x0203020302030203ll,
                                TImode);
                                TImode);
          break;
          break;
        case V16QImode:
        case V16QImode:
          shuf =
          shuf =
            immed_double_const (0x0303030303030303ll, 0x0303030303030303ll,
            immed_double_const (0x0303030303030303ll, 0x0303030303030303ll,
                                TImode);
                                TImode);
          break;
          break;
        default:
        default:
          abort ();
          abort ();
        }
        }
      emit_move_insn (reg, shuf);
      emit_move_insn (reg, shuf);
      emit_insn (gen_shufb (ops[0], ops[1], ops[1], reg));
      emit_insn (gen_shufb (ops[0], ops[1], ops[1], reg));
    }
    }
}
}
 
 
void
void
spu_builtin_extract (rtx ops[])
spu_builtin_extract (rtx ops[])
{
{
  enum machine_mode mode;
  enum machine_mode mode;
  rtx rot, from, tmp;
  rtx rot, from, tmp;
 
 
  mode = GET_MODE (ops[1]);
  mode = GET_MODE (ops[1]);
 
 
  if (GET_CODE (ops[2]) == CONST_INT)
  if (GET_CODE (ops[2]) == CONST_INT)
    {
    {
      switch (mode)
      switch (mode)
        {
        {
        case V16QImode:
        case V16QImode:
          emit_insn (gen_vec_extractv16qi (ops[0], ops[1], ops[2]));
          emit_insn (gen_vec_extractv16qi (ops[0], ops[1], ops[2]));
          break;
          break;
        case V8HImode:
        case V8HImode:
          emit_insn (gen_vec_extractv8hi (ops[0], ops[1], ops[2]));
          emit_insn (gen_vec_extractv8hi (ops[0], ops[1], ops[2]));
          break;
          break;
        case V4SFmode:
        case V4SFmode:
          emit_insn (gen_vec_extractv4sf (ops[0], ops[1], ops[2]));
          emit_insn (gen_vec_extractv4sf (ops[0], ops[1], ops[2]));
          break;
          break;
        case V4SImode:
        case V4SImode:
          emit_insn (gen_vec_extractv4si (ops[0], ops[1], ops[2]));
          emit_insn (gen_vec_extractv4si (ops[0], ops[1], ops[2]));
          break;
          break;
        case V2DImode:
        case V2DImode:
          emit_insn (gen_vec_extractv2di (ops[0], ops[1], ops[2]));
          emit_insn (gen_vec_extractv2di (ops[0], ops[1], ops[2]));
          break;
          break;
        case V2DFmode:
        case V2DFmode:
          emit_insn (gen_vec_extractv2df (ops[0], ops[1], ops[2]));
          emit_insn (gen_vec_extractv2df (ops[0], ops[1], ops[2]));
          break;
          break;
        default:
        default:
          abort ();
          abort ();
        }
        }
      return;
      return;
    }
    }
 
 
  from = spu_gen_subreg (TImode, ops[1]);
  from = spu_gen_subreg (TImode, ops[1]);
  rot = gen_reg_rtx (TImode);
  rot = gen_reg_rtx (TImode);
  tmp = gen_reg_rtx (SImode);
  tmp = gen_reg_rtx (SImode);
 
 
  switch (mode)
  switch (mode)
    {
    {
    case V16QImode:
    case V16QImode:
      emit_insn (gen_addsi3 (tmp, ops[2], GEN_INT (-3)));
      emit_insn (gen_addsi3 (tmp, ops[2], GEN_INT (-3)));
      break;
      break;
    case V8HImode:
    case V8HImode:
      emit_insn (gen_addsi3 (tmp, ops[2], ops[2]));
      emit_insn (gen_addsi3 (tmp, ops[2], ops[2]));
      emit_insn (gen_addsi3 (tmp, tmp, GEN_INT (-2)));
      emit_insn (gen_addsi3 (tmp, tmp, GEN_INT (-2)));
      break;
      break;
    case V4SFmode:
    case V4SFmode:
    case V4SImode:
    case V4SImode:
      emit_insn (gen_ashlsi3 (tmp, ops[2], GEN_INT (2)));
      emit_insn (gen_ashlsi3 (tmp, ops[2], GEN_INT (2)));
      break;
      break;
    case V2DImode:
    case V2DImode:
    case V2DFmode:
    case V2DFmode:
      emit_insn (gen_ashlsi3 (tmp, ops[2], GEN_INT (3)));
      emit_insn (gen_ashlsi3 (tmp, ops[2], GEN_INT (3)));
      break;
      break;
    default:
    default:
      abort ();
      abort ();
    }
    }
  emit_insn (gen_rotqby_ti (rot, from, tmp));
  emit_insn (gen_rotqby_ti (rot, from, tmp));
 
 
  emit_insn (gen_spu_convert (ops[0], rot));
  emit_insn (gen_spu_convert (ops[0], rot));
}
}
 
 
void
void
spu_builtin_insert (rtx ops[])
spu_builtin_insert (rtx ops[])
{
{
  enum machine_mode mode = GET_MODE (ops[0]);
  enum machine_mode mode = GET_MODE (ops[0]);
  enum machine_mode imode = GET_MODE_INNER (mode);
  enum machine_mode imode = GET_MODE_INNER (mode);
  rtx mask = gen_reg_rtx (TImode);
  rtx mask = gen_reg_rtx (TImode);
  rtx offset;
  rtx offset;
 
 
  if (GET_CODE (ops[3]) == CONST_INT)
  if (GET_CODE (ops[3]) == CONST_INT)
    offset = GEN_INT (INTVAL (ops[3]) * GET_MODE_SIZE (imode));
    offset = GEN_INT (INTVAL (ops[3]) * GET_MODE_SIZE (imode));
  else
  else
    {
    {
      offset = gen_reg_rtx (SImode);
      offset = gen_reg_rtx (SImode);
      emit_insn (gen_mulsi3
      emit_insn (gen_mulsi3
                 (offset, ops[3], GEN_INT (GET_MODE_SIZE (imode))));
                 (offset, ops[3], GEN_INT (GET_MODE_SIZE (imode))));
    }
    }
  emit_insn (gen_cpat
  emit_insn (gen_cpat
             (mask, stack_pointer_rtx, offset,
             (mask, stack_pointer_rtx, offset,
              GEN_INT (GET_MODE_SIZE (imode))));
              GEN_INT (GET_MODE_SIZE (imode))));
  emit_insn (gen_shufb (ops[0], ops[1], ops[2], mask));
  emit_insn (gen_shufb (ops[0], ops[1], ops[2], mask));
}
}
 
 
void
void
spu_builtin_promote (rtx ops[])
spu_builtin_promote (rtx ops[])
{
{
  enum machine_mode mode, imode;
  enum machine_mode mode, imode;
  rtx rot, from, offset;
  rtx rot, from, offset;
  HOST_WIDE_INT pos;
  HOST_WIDE_INT pos;
 
 
  mode = GET_MODE (ops[0]);
  mode = GET_MODE (ops[0]);
  imode = GET_MODE_INNER (mode);
  imode = GET_MODE_INNER (mode);
 
 
  from = gen_reg_rtx (TImode);
  from = gen_reg_rtx (TImode);
  rot = spu_gen_subreg (TImode, ops[0]);
  rot = spu_gen_subreg (TImode, ops[0]);
 
 
  emit_insn (gen_spu_convert (from, ops[1]));
  emit_insn (gen_spu_convert (from, ops[1]));
 
 
  if (GET_CODE (ops[2]) == CONST_INT)
  if (GET_CODE (ops[2]) == CONST_INT)
    {
    {
      pos = -GET_MODE_SIZE (imode) * INTVAL (ops[2]);
      pos = -GET_MODE_SIZE (imode) * INTVAL (ops[2]);
      if (GET_MODE_SIZE (imode) < 4)
      if (GET_MODE_SIZE (imode) < 4)
        pos += 4 - GET_MODE_SIZE (imode);
        pos += 4 - GET_MODE_SIZE (imode);
      offset = GEN_INT (pos & 15);
      offset = GEN_INT (pos & 15);
    }
    }
  else
  else
    {
    {
      offset = gen_reg_rtx (SImode);
      offset = gen_reg_rtx (SImode);
      switch (mode)
      switch (mode)
        {
        {
        case V16QImode:
        case V16QImode:
          emit_insn (gen_subsi3 (offset, GEN_INT (3), ops[2]));
          emit_insn (gen_subsi3 (offset, GEN_INT (3), ops[2]));
          break;
          break;
        case V8HImode:
        case V8HImode:
          emit_insn (gen_subsi3 (offset, GEN_INT (1), ops[2]));
          emit_insn (gen_subsi3 (offset, GEN_INT (1), ops[2]));
          emit_insn (gen_addsi3 (offset, offset, offset));
          emit_insn (gen_addsi3 (offset, offset, offset));
          break;
          break;
        case V4SFmode:
        case V4SFmode:
        case V4SImode:
        case V4SImode:
          emit_insn (gen_subsi3 (offset, GEN_INT (0), ops[2]));
          emit_insn (gen_subsi3 (offset, GEN_INT (0), ops[2]));
          emit_insn (gen_ashlsi3 (offset, offset, GEN_INT (2)));
          emit_insn (gen_ashlsi3 (offset, offset, GEN_INT (2)));
          break;
          break;
        case V2DImode:
        case V2DImode:
        case V2DFmode:
        case V2DFmode:
          emit_insn (gen_ashlsi3 (offset, ops[2], GEN_INT (3)));
          emit_insn (gen_ashlsi3 (offset, ops[2], GEN_INT (3)));
          break;
          break;
        default:
        default:
          abort ();
          abort ();
        }
        }
    }
    }
  emit_insn (gen_rotqby_ti (rot, from, offset));
  emit_insn (gen_rotqby_ti (rot, from, offset));
}
}
 
 
static void
static void
spu_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
spu_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
{
{
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx shuf = gen_reg_rtx (V4SImode);
  rtx shuf = gen_reg_rtx (V4SImode);
  rtx insn = gen_reg_rtx (V4SImode);
  rtx insn = gen_reg_rtx (V4SImode);
  rtx shufc;
  rtx shufc;
  rtx insnc;
  rtx insnc;
  rtx mem;
  rtx mem;
 
 
  fnaddr = force_reg (SImode, fnaddr);
  fnaddr = force_reg (SImode, fnaddr);
  cxt = force_reg (SImode, cxt);
  cxt = force_reg (SImode, cxt);
 
 
  if (TARGET_LARGE_MEM)
  if (TARGET_LARGE_MEM)
    {
    {
      rtx rotl = gen_reg_rtx (V4SImode);
      rtx rotl = gen_reg_rtx (V4SImode);
      rtx mask = gen_reg_rtx (V4SImode);
      rtx mask = gen_reg_rtx (V4SImode);
      rtx bi = gen_reg_rtx (SImode);
      rtx bi = gen_reg_rtx (SImode);
      static unsigned char const shufa[16] = {
      static unsigned char const shufa[16] = {
        2, 3, 0, 1, 18, 19, 16, 17,
        2, 3, 0, 1, 18, 19, 16, 17,
        0, 1, 2, 3, 16, 17, 18, 19
        0, 1, 2, 3, 16, 17, 18, 19
      };
      };
      static unsigned char const insna[16] = {
      static unsigned char const insna[16] = {
        0x41, 0, 0, 79,
        0x41, 0, 0, 79,
        0x41, 0, 0, STATIC_CHAIN_REGNUM,
        0x41, 0, 0, STATIC_CHAIN_REGNUM,
        0x60, 0x80, 0, 79,
        0x60, 0x80, 0, 79,
        0x60, 0x80, 0, STATIC_CHAIN_REGNUM
        0x60, 0x80, 0, STATIC_CHAIN_REGNUM
      };
      };
 
 
      shufc = force_reg (TImode, array_to_constant (TImode, shufa));
      shufc = force_reg (TImode, array_to_constant (TImode, shufa));
      insnc = force_reg (V4SImode, array_to_constant (V4SImode, insna));
      insnc = force_reg (V4SImode, array_to_constant (V4SImode, insna));
 
 
      emit_insn (gen_shufb (shuf, fnaddr, cxt, shufc));
      emit_insn (gen_shufb (shuf, fnaddr, cxt, shufc));
      emit_insn (gen_vrotlv4si3 (rotl, shuf, spu_const (V4SImode, 7)));
      emit_insn (gen_vrotlv4si3 (rotl, shuf, spu_const (V4SImode, 7)));
      emit_insn (gen_movv4si (mask, spu_const (V4SImode, 0xffff << 7)));
      emit_insn (gen_movv4si (mask, spu_const (V4SImode, 0xffff << 7)));
      emit_insn (gen_selb (insn, insnc, rotl, mask));
      emit_insn (gen_selb (insn, insnc, rotl, mask));
 
 
      mem = adjust_address (m_tramp, V4SImode, 0);
      mem = adjust_address (m_tramp, V4SImode, 0);
      emit_move_insn (mem, insn);
      emit_move_insn (mem, insn);
 
 
      emit_move_insn (bi, GEN_INT (0x35000000 + (79 << 7)));
      emit_move_insn (bi, GEN_INT (0x35000000 + (79 << 7)));
      mem = adjust_address (m_tramp, Pmode, 16);
      mem = adjust_address (m_tramp, Pmode, 16);
      emit_move_insn (mem, bi);
      emit_move_insn (mem, bi);
    }
    }
  else
  else
    {
    {
      rtx scxt = gen_reg_rtx (SImode);
      rtx scxt = gen_reg_rtx (SImode);
      rtx sfnaddr = gen_reg_rtx (SImode);
      rtx sfnaddr = gen_reg_rtx (SImode);
      static unsigned char const insna[16] = {
      static unsigned char const insna[16] = {
        0x42, 0, 0, STATIC_CHAIN_REGNUM,
        0x42, 0, 0, STATIC_CHAIN_REGNUM,
        0x30, 0, 0, 0,
        0x30, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0
        0, 0, 0, 0
      };
      };
 
 
      shufc = gen_reg_rtx (TImode);
      shufc = gen_reg_rtx (TImode);
      insnc = force_reg (V4SImode, array_to_constant (V4SImode, insna));
      insnc = force_reg (V4SImode, array_to_constant (V4SImode, insna));
 
 
      /* By or'ing all of cxt with the ila opcode we are assuming cxt
      /* By or'ing all of cxt with the ila opcode we are assuming cxt
         fits 18 bits and the last 4 are zeros.  This will be true if
         fits 18 bits and the last 4 are zeros.  This will be true if
         the stack pointer is initialized to 0x3fff0 at program start,
         the stack pointer is initialized to 0x3fff0 at program start,
         otherwise the ila instruction will be garbage. */
         otherwise the ila instruction will be garbage. */
 
 
      emit_insn (gen_ashlsi3 (scxt, cxt, GEN_INT (7)));
      emit_insn (gen_ashlsi3 (scxt, cxt, GEN_INT (7)));
      emit_insn (gen_ashlsi3 (sfnaddr, fnaddr, GEN_INT (5)));
      emit_insn (gen_ashlsi3 (sfnaddr, fnaddr, GEN_INT (5)));
      emit_insn (gen_cpat
      emit_insn (gen_cpat
                 (shufc, stack_pointer_rtx, GEN_INT (4), GEN_INT (4)));
                 (shufc, stack_pointer_rtx, GEN_INT (4), GEN_INT (4)));
      emit_insn (gen_shufb (shuf, sfnaddr, scxt, shufc));
      emit_insn (gen_shufb (shuf, sfnaddr, scxt, shufc));
      emit_insn (gen_iorv4si3 (insn, insnc, shuf));
      emit_insn (gen_iorv4si3 (insn, insnc, shuf));
 
 
      mem = adjust_address (m_tramp, V4SImode, 0);
      mem = adjust_address (m_tramp, V4SImode, 0);
      emit_move_insn (mem, insn);
      emit_move_insn (mem, insn);
    }
    }
  emit_insn (gen_sync ());
  emit_insn (gen_sync ());
}
}
 
 
void
void
spu_expand_sign_extend (rtx ops[])
spu_expand_sign_extend (rtx ops[])
{
{
  unsigned char arr[16];
  unsigned char arr[16];
  rtx pat = gen_reg_rtx (TImode);
  rtx pat = gen_reg_rtx (TImode);
  rtx sign, c;
  rtx sign, c;
  int i, last;
  int i, last;
  last = GET_MODE (ops[0]) == DImode ? 7 : 15;
  last = GET_MODE (ops[0]) == DImode ? 7 : 15;
  if (GET_MODE (ops[1]) == QImode)
  if (GET_MODE (ops[1]) == QImode)
    {
    {
      sign = gen_reg_rtx (HImode);
      sign = gen_reg_rtx (HImode);
      emit_insn (gen_extendqihi2 (sign, ops[1]));
      emit_insn (gen_extendqihi2 (sign, ops[1]));
      for (i = 0; i < 16; i++)
      for (i = 0; i < 16; i++)
        arr[i] = 0x12;
        arr[i] = 0x12;
      arr[last] = 0x13;
      arr[last] = 0x13;
    }
    }
  else
  else
    {
    {
      for (i = 0; i < 16; i++)
      for (i = 0; i < 16; i++)
        arr[i] = 0x10;
        arr[i] = 0x10;
      switch (GET_MODE (ops[1]))
      switch (GET_MODE (ops[1]))
        {
        {
        case HImode:
        case HImode:
          sign = gen_reg_rtx (SImode);
          sign = gen_reg_rtx (SImode);
          emit_insn (gen_extendhisi2 (sign, ops[1]));
          emit_insn (gen_extendhisi2 (sign, ops[1]));
          arr[last] = 0x03;
          arr[last] = 0x03;
          arr[last - 1] = 0x02;
          arr[last - 1] = 0x02;
          break;
          break;
        case SImode:
        case SImode:
          sign = gen_reg_rtx (SImode);
          sign = gen_reg_rtx (SImode);
          emit_insn (gen_ashrsi3 (sign, ops[1], GEN_INT (31)));
          emit_insn (gen_ashrsi3 (sign, ops[1], GEN_INT (31)));
          for (i = 0; i < 4; i++)
          for (i = 0; i < 4; i++)
            arr[last - i] = 3 - i;
            arr[last - i] = 3 - i;
          break;
          break;
        case DImode:
        case DImode:
          sign = gen_reg_rtx (SImode);
          sign = gen_reg_rtx (SImode);
          c = gen_reg_rtx (SImode);
          c = gen_reg_rtx (SImode);
          emit_insn (gen_spu_convert (c, ops[1]));
          emit_insn (gen_spu_convert (c, ops[1]));
          emit_insn (gen_ashrsi3 (sign, c, GEN_INT (31)));
          emit_insn (gen_ashrsi3 (sign, c, GEN_INT (31)));
          for (i = 0; i < 8; i++)
          for (i = 0; i < 8; i++)
            arr[last - i] = 7 - i;
            arr[last - i] = 7 - i;
          break;
          break;
        default:
        default:
          abort ();
          abort ();
        }
        }
    }
    }
  emit_move_insn (pat, array_to_constant (TImode, arr));
  emit_move_insn (pat, array_to_constant (TImode, arr));
  emit_insn (gen_shufb (ops[0], ops[1], sign, pat));
  emit_insn (gen_shufb (ops[0], ops[1], sign, pat));
}
}
 
 
/* expand vector initialization. If there are any constant parts,
/* expand vector initialization. If there are any constant parts,
   load constant parts first. Then load any non-constant parts.  */
   load constant parts first. Then load any non-constant parts.  */
void
void
spu_expand_vector_init (rtx target, rtx vals)
spu_expand_vector_init (rtx target, rtx vals)
{
{
  enum machine_mode mode = GET_MODE (target);
  enum machine_mode mode = GET_MODE (target);
  int n_elts = GET_MODE_NUNITS (mode);
  int n_elts = GET_MODE_NUNITS (mode);
  int n_var = 0;
  int n_var = 0;
  bool all_same = true;
  bool all_same = true;
  rtx first, x = NULL_RTX, first_constant = NULL_RTX;
  rtx first, x = NULL_RTX, first_constant = NULL_RTX;
  int i;
  int i;
 
 
  first = XVECEXP (vals, 0, 0);
  first = XVECEXP (vals, 0, 0);
  for (i = 0; i < n_elts; ++i)
  for (i = 0; i < n_elts; ++i)
    {
    {
      x = XVECEXP (vals, 0, i);
      x = XVECEXP (vals, 0, i);
      if (!(CONST_INT_P (x)
      if (!(CONST_INT_P (x)
            || GET_CODE (x) == CONST_DOUBLE
            || GET_CODE (x) == CONST_DOUBLE
            || GET_CODE (x) == CONST_FIXED))
            || GET_CODE (x) == CONST_FIXED))
        ++n_var;
        ++n_var;
      else
      else
        {
        {
          if (first_constant == NULL_RTX)
          if (first_constant == NULL_RTX)
            first_constant = x;
            first_constant = x;
        }
        }
      if (i > 0 && !rtx_equal_p (x, first))
      if (i > 0 && !rtx_equal_p (x, first))
        all_same = false;
        all_same = false;
    }
    }
 
 
  /* if all elements are the same, use splats to repeat elements */
  /* if all elements are the same, use splats to repeat elements */
  if (all_same)
  if (all_same)
    {
    {
      if (!CONSTANT_P (first)
      if (!CONSTANT_P (first)
          && !register_operand (first, GET_MODE (x)))
          && !register_operand (first, GET_MODE (x)))
        first = force_reg (GET_MODE (first), first);
        first = force_reg (GET_MODE (first), first);
      emit_insn (gen_spu_splats (target, first));
      emit_insn (gen_spu_splats (target, first));
      return;
      return;
    }
    }
 
 
  /* load constant parts */
  /* load constant parts */
  if (n_var != n_elts)
  if (n_var != n_elts)
    {
    {
      if (n_var == 0)
      if (n_var == 0)
        {
        {
          emit_move_insn (target,
          emit_move_insn (target,
                          gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
                          gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
        }
        }
      else
      else
        {
        {
          rtx constant_parts_rtx = copy_rtx (vals);
          rtx constant_parts_rtx = copy_rtx (vals);
 
 
          gcc_assert (first_constant != NULL_RTX);
          gcc_assert (first_constant != NULL_RTX);
          /* fill empty slots with the first constant, this increases
          /* fill empty slots with the first constant, this increases
             our chance of using splats in the recursive call below. */
             our chance of using splats in the recursive call below. */
          for (i = 0; i < n_elts; ++i)
          for (i = 0; i < n_elts; ++i)
            {
            {
              x = XVECEXP (constant_parts_rtx, 0, i);
              x = XVECEXP (constant_parts_rtx, 0, i);
              if (!(CONST_INT_P (x)
              if (!(CONST_INT_P (x)
                    || GET_CODE (x) == CONST_DOUBLE
                    || GET_CODE (x) == CONST_DOUBLE
                    || GET_CODE (x) == CONST_FIXED))
                    || GET_CODE (x) == CONST_FIXED))
                XVECEXP (constant_parts_rtx, 0, i) = first_constant;
                XVECEXP (constant_parts_rtx, 0, i) = first_constant;
            }
            }
 
 
          spu_expand_vector_init (target, constant_parts_rtx);
          spu_expand_vector_init (target, constant_parts_rtx);
        }
        }
    }
    }
 
 
  /* load variable parts */
  /* load variable parts */
  if (n_var != 0)
  if (n_var != 0)
    {
    {
      rtx insert_operands[4];
      rtx insert_operands[4];
 
 
      insert_operands[0] = target;
      insert_operands[0] = target;
      insert_operands[2] = target;
      insert_operands[2] = target;
      for (i = 0; i < n_elts; ++i)
      for (i = 0; i < n_elts; ++i)
        {
        {
          x = XVECEXP (vals, 0, i);
          x = XVECEXP (vals, 0, i);
          if (!(CONST_INT_P (x)
          if (!(CONST_INT_P (x)
                || GET_CODE (x) == CONST_DOUBLE
                || GET_CODE (x) == CONST_DOUBLE
                || GET_CODE (x) == CONST_FIXED))
                || GET_CODE (x) == CONST_FIXED))
            {
            {
              if (!register_operand (x, GET_MODE (x)))
              if (!register_operand (x, GET_MODE (x)))
                x = force_reg (GET_MODE (x), x);
                x = force_reg (GET_MODE (x), x);
              insert_operands[1] = x;
              insert_operands[1] = x;
              insert_operands[3] = GEN_INT (i);
              insert_operands[3] = GEN_INT (i);
              spu_builtin_insert (insert_operands);
              spu_builtin_insert (insert_operands);
            }
            }
        }
        }
    }
    }
}
}
 
 
/* Return insn index for the vector compare instruction for given CODE,
/* Return insn index for the vector compare instruction for given CODE,
   and DEST_MODE, OP_MODE. Return -1 if valid insn is not available.  */
   and DEST_MODE, OP_MODE. Return -1 if valid insn is not available.  */
 
 
static int
static int
get_vec_cmp_insn (enum rtx_code code,
get_vec_cmp_insn (enum rtx_code code,
                  enum machine_mode dest_mode,
                  enum machine_mode dest_mode,
                  enum machine_mode op_mode)
                  enum machine_mode op_mode)
 
 
{
{
  switch (code)
  switch (code)
    {
    {
    case EQ:
    case EQ:
      if (dest_mode == V16QImode && op_mode == V16QImode)
      if (dest_mode == V16QImode && op_mode == V16QImode)
        return CODE_FOR_ceq_v16qi;
        return CODE_FOR_ceq_v16qi;
      if (dest_mode == V8HImode && op_mode == V8HImode)
      if (dest_mode == V8HImode && op_mode == V8HImode)
        return CODE_FOR_ceq_v8hi;
        return CODE_FOR_ceq_v8hi;
      if (dest_mode == V4SImode && op_mode == V4SImode)
      if (dest_mode == V4SImode && op_mode == V4SImode)
        return CODE_FOR_ceq_v4si;
        return CODE_FOR_ceq_v4si;
      if (dest_mode == V4SImode && op_mode == V4SFmode)
      if (dest_mode == V4SImode && op_mode == V4SFmode)
        return CODE_FOR_ceq_v4sf;
        return CODE_FOR_ceq_v4sf;
      if (dest_mode == V2DImode && op_mode == V2DFmode)
      if (dest_mode == V2DImode && op_mode == V2DFmode)
        return CODE_FOR_ceq_v2df;
        return CODE_FOR_ceq_v2df;
      break;
      break;
    case GT:
    case GT:
      if (dest_mode == V16QImode && op_mode == V16QImode)
      if (dest_mode == V16QImode && op_mode == V16QImode)
        return CODE_FOR_cgt_v16qi;
        return CODE_FOR_cgt_v16qi;
      if (dest_mode == V8HImode && op_mode == V8HImode)
      if (dest_mode == V8HImode && op_mode == V8HImode)
        return CODE_FOR_cgt_v8hi;
        return CODE_FOR_cgt_v8hi;
      if (dest_mode == V4SImode && op_mode == V4SImode)
      if (dest_mode == V4SImode && op_mode == V4SImode)
        return CODE_FOR_cgt_v4si;
        return CODE_FOR_cgt_v4si;
      if (dest_mode == V4SImode && op_mode == V4SFmode)
      if (dest_mode == V4SImode && op_mode == V4SFmode)
        return CODE_FOR_cgt_v4sf;
        return CODE_FOR_cgt_v4sf;
      if (dest_mode == V2DImode && op_mode == V2DFmode)
      if (dest_mode == V2DImode && op_mode == V2DFmode)
        return CODE_FOR_cgt_v2df;
        return CODE_FOR_cgt_v2df;
      break;
      break;
    case GTU:
    case GTU:
      if (dest_mode == V16QImode && op_mode == V16QImode)
      if (dest_mode == V16QImode && op_mode == V16QImode)
        return CODE_FOR_clgt_v16qi;
        return CODE_FOR_clgt_v16qi;
      if (dest_mode == V8HImode && op_mode == V8HImode)
      if (dest_mode == V8HImode && op_mode == V8HImode)
        return CODE_FOR_clgt_v8hi;
        return CODE_FOR_clgt_v8hi;
      if (dest_mode == V4SImode && op_mode == V4SImode)
      if (dest_mode == V4SImode && op_mode == V4SImode)
        return CODE_FOR_clgt_v4si;
        return CODE_FOR_clgt_v4si;
      break;
      break;
    default:
    default:
      break;
      break;
    }
    }
  return -1;
  return -1;
}
}
 
 
/* Emit vector compare for operands OP0 and OP1 using code RCODE.
/* Emit vector compare for operands OP0 and OP1 using code RCODE.
   DMODE is expected destination mode. This is a recursive function.  */
   DMODE is expected destination mode. This is a recursive function.  */
 
 
static rtx
static rtx
spu_emit_vector_compare (enum rtx_code rcode,
spu_emit_vector_compare (enum rtx_code rcode,
                         rtx op0, rtx op1,
                         rtx op0, rtx op1,
                         enum machine_mode dmode)
                         enum machine_mode dmode)
{
{
  int vec_cmp_insn;
  int vec_cmp_insn;
  rtx mask;
  rtx mask;
  enum machine_mode dest_mode;
  enum machine_mode dest_mode;
  enum machine_mode op_mode = GET_MODE (op1);
  enum machine_mode op_mode = GET_MODE (op1);
 
 
  gcc_assert (GET_MODE (op0) == GET_MODE (op1));
  gcc_assert (GET_MODE (op0) == GET_MODE (op1));
 
 
  /* Floating point vector compare instructions uses destination V4SImode.
  /* Floating point vector compare instructions uses destination V4SImode.
     Double floating point vector compare instructions uses destination V2DImode.
     Double floating point vector compare instructions uses destination V2DImode.
     Move destination to appropriate mode later.  */
     Move destination to appropriate mode later.  */
  if (dmode == V4SFmode)
  if (dmode == V4SFmode)
    dest_mode = V4SImode;
    dest_mode = V4SImode;
  else if (dmode == V2DFmode)
  else if (dmode == V2DFmode)
    dest_mode = V2DImode;
    dest_mode = V2DImode;
  else
  else
    dest_mode = dmode;
    dest_mode = dmode;
 
 
  mask = gen_reg_rtx (dest_mode);
  mask = gen_reg_rtx (dest_mode);
  vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode);
  vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode);
 
 
  if (vec_cmp_insn == -1)
  if (vec_cmp_insn == -1)
    {
    {
      bool swap_operands = false;
      bool swap_operands = false;
      bool try_again = false;
      bool try_again = false;
      switch (rcode)
      switch (rcode)
        {
        {
        case LT:
        case LT:
          rcode = GT;
          rcode = GT;
          swap_operands = true;
          swap_operands = true;
          try_again = true;
          try_again = true;
          break;
          break;
        case LTU:
        case LTU:
          rcode = GTU;
          rcode = GTU;
          swap_operands = true;
          swap_operands = true;
          try_again = true;
          try_again = true;
          break;
          break;
        case NE:
        case NE:
          /* Treat A != B as ~(A==B).  */
          /* Treat A != B as ~(A==B).  */
          {
          {
            enum insn_code nor_code;
            enum insn_code nor_code;
            rtx eq_rtx = spu_emit_vector_compare (EQ, op0, op1, dest_mode);
            rtx eq_rtx = spu_emit_vector_compare (EQ, op0, op1, dest_mode);
            nor_code = optab_handler (one_cmpl_optab, (int)dest_mode)->insn_code;
            nor_code = optab_handler (one_cmpl_optab, (int)dest_mode)->insn_code;
            gcc_assert (nor_code != CODE_FOR_nothing);
            gcc_assert (nor_code != CODE_FOR_nothing);
            emit_insn (GEN_FCN (nor_code) (mask, eq_rtx));
            emit_insn (GEN_FCN (nor_code) (mask, eq_rtx));
            if (dmode != dest_mode)
            if (dmode != dest_mode)
              {
              {
                rtx temp = gen_reg_rtx (dest_mode);
                rtx temp = gen_reg_rtx (dest_mode);
                convert_move (temp, mask, 0);
                convert_move (temp, mask, 0);
                return temp;
                return temp;
              }
              }
            return mask;
            return mask;
          }
          }
          break;
          break;
        case GE:
        case GE:
        case GEU:
        case GEU:
        case LE:
        case LE:
        case LEU:
        case LEU:
          /* Try GT/GTU/LT/LTU OR EQ */
          /* Try GT/GTU/LT/LTU OR EQ */
          {
          {
            rtx c_rtx, eq_rtx;
            rtx c_rtx, eq_rtx;
            enum insn_code ior_code;
            enum insn_code ior_code;
            enum rtx_code new_code;
            enum rtx_code new_code;
 
 
            switch (rcode)
            switch (rcode)
              {
              {
              case GE:  new_code = GT;  break;
              case GE:  new_code = GT;  break;
              case GEU: new_code = GTU; break;
              case GEU: new_code = GTU; break;
              case LE:  new_code = LT;  break;
              case LE:  new_code = LT;  break;
              case LEU: new_code = LTU; break;
              case LEU: new_code = LTU; break;
              default:
              default:
                gcc_unreachable ();
                gcc_unreachable ();
              }
              }
 
 
            c_rtx = spu_emit_vector_compare (new_code, op0, op1, dest_mode);
            c_rtx = spu_emit_vector_compare (new_code, op0, op1, dest_mode);
            eq_rtx = spu_emit_vector_compare (EQ, op0, op1, dest_mode);
            eq_rtx = spu_emit_vector_compare (EQ, op0, op1, dest_mode);
 
 
            ior_code = optab_handler (ior_optab, (int)dest_mode)->insn_code;
            ior_code = optab_handler (ior_optab, (int)dest_mode)->insn_code;
            gcc_assert (ior_code != CODE_FOR_nothing);
            gcc_assert (ior_code != CODE_FOR_nothing);
            emit_insn (GEN_FCN (ior_code) (mask, c_rtx, eq_rtx));
            emit_insn (GEN_FCN (ior_code) (mask, c_rtx, eq_rtx));
            if (dmode != dest_mode)
            if (dmode != dest_mode)
              {
              {
                rtx temp = gen_reg_rtx (dest_mode);
                rtx temp = gen_reg_rtx (dest_mode);
                convert_move (temp, mask, 0);
                convert_move (temp, mask, 0);
                return temp;
                return temp;
              }
              }
            return mask;
            return mask;
          }
          }
          break;
          break;
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
      /* You only get two chances.  */
      /* You only get two chances.  */
      if (try_again)
      if (try_again)
          vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode);
          vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode);
 
 
      gcc_assert (vec_cmp_insn != -1);
      gcc_assert (vec_cmp_insn != -1);
 
 
      if (swap_operands)
      if (swap_operands)
        {
        {
          rtx tmp;
          rtx tmp;
          tmp = op0;
          tmp = op0;
          op0 = op1;
          op0 = op1;
          op1 = tmp;
          op1 = tmp;
        }
        }
    }
    }
 
 
  emit_insn (GEN_FCN (vec_cmp_insn) (mask, op0, op1));
  emit_insn (GEN_FCN (vec_cmp_insn) (mask, op0, op1));
  if (dmode != dest_mode)
  if (dmode != dest_mode)
    {
    {
      rtx temp = gen_reg_rtx (dest_mode);
      rtx temp = gen_reg_rtx (dest_mode);
      convert_move (temp, mask, 0);
      convert_move (temp, mask, 0);
      return temp;
      return temp;
    }
    }
  return mask;
  return mask;
}
}
 
 
 
 
/* Emit vector conditional expression.
/* Emit vector conditional expression.
   DEST is destination. OP1 and OP2 are two VEC_COND_EXPR operands.
   DEST is destination. OP1 and OP2 are two VEC_COND_EXPR operands.
   CC_OP0 and CC_OP1 are the two operands for the relation operation COND.  */
   CC_OP0 and CC_OP1 are the two operands for the relation operation COND.  */
 
 
int
int
spu_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2,
spu_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2,
                           rtx cond, rtx cc_op0, rtx cc_op1)
                           rtx cond, rtx cc_op0, rtx cc_op1)
{
{
  enum machine_mode dest_mode = GET_MODE (dest);
  enum machine_mode dest_mode = GET_MODE (dest);
  enum rtx_code rcode = GET_CODE (cond);
  enum rtx_code rcode = GET_CODE (cond);
  rtx mask;
  rtx mask;
 
 
  /* Get the vector mask for the given relational operations.  */
  /* Get the vector mask for the given relational operations.  */
  mask = spu_emit_vector_compare (rcode, cc_op0, cc_op1, dest_mode);
  mask = spu_emit_vector_compare (rcode, cc_op0, cc_op1, dest_mode);
 
 
  emit_insn(gen_selb (dest, op2, op1, mask));
  emit_insn(gen_selb (dest, op2, op1, mask));
 
 
  return 1;
  return 1;
}
}
 
 
static rtx
static rtx
spu_force_reg (enum machine_mode mode, rtx op)
spu_force_reg (enum machine_mode mode, rtx op)
{
{
  rtx x, r;
  rtx x, r;
  if (GET_MODE (op) == VOIDmode || GET_MODE (op) == BLKmode)
  if (GET_MODE (op) == VOIDmode || GET_MODE (op) == BLKmode)
    {
    {
      if ((SCALAR_INT_MODE_P (mode) && GET_CODE (op) == CONST_INT)
      if ((SCALAR_INT_MODE_P (mode) && GET_CODE (op) == CONST_INT)
          || GET_MODE (op) == BLKmode)
          || GET_MODE (op) == BLKmode)
        return force_reg (mode, convert_to_mode (mode, op, 0));
        return force_reg (mode, convert_to_mode (mode, op, 0));
      abort ();
      abort ();
    }
    }
 
 
  r = force_reg (GET_MODE (op), op);
  r = force_reg (GET_MODE (op), op);
  if (GET_MODE_SIZE (GET_MODE (op)) == GET_MODE_SIZE (mode))
  if (GET_MODE_SIZE (GET_MODE (op)) == GET_MODE_SIZE (mode))
    {
    {
      x = simplify_gen_subreg (mode, r, GET_MODE (op), 0);
      x = simplify_gen_subreg (mode, r, GET_MODE (op), 0);
      if (x)
      if (x)
        return x;
        return x;
    }
    }
 
 
  x = gen_reg_rtx (mode);
  x = gen_reg_rtx (mode);
  emit_insn (gen_spu_convert (x, r));
  emit_insn (gen_spu_convert (x, r));
  return x;
  return x;
}
}
 
 
static void
static void
spu_check_builtin_parm (struct spu_builtin_description *d, rtx op, int p)
spu_check_builtin_parm (struct spu_builtin_description *d, rtx op, int p)
{
{
  HOST_WIDE_INT v = 0;
  HOST_WIDE_INT v = 0;
  int lsbits;
  int lsbits;
  /* Check the range of immediate operands. */
  /* Check the range of immediate operands. */
  if (p >= SPU_BTI_7 && p <= SPU_BTI_U18)
  if (p >= SPU_BTI_7 && p <= SPU_BTI_U18)
    {
    {
      int range = p - SPU_BTI_7;
      int range = p - SPU_BTI_7;
 
 
      if (!CONSTANT_P (op))
      if (!CONSTANT_P (op))
        error ("%s expects an integer literal in the range [%d, %d].",
        error ("%s expects an integer literal in the range [%d, %d].",
               d->name,
               d->name,
               spu_builtin_range[range].low, spu_builtin_range[range].high);
               spu_builtin_range[range].low, spu_builtin_range[range].high);
 
 
      if (GET_CODE (op) == CONST
      if (GET_CODE (op) == CONST
          && (GET_CODE (XEXP (op, 0)) == PLUS
          && (GET_CODE (XEXP (op, 0)) == PLUS
              || GET_CODE (XEXP (op, 0)) == MINUS))
              || GET_CODE (XEXP (op, 0)) == MINUS))
        {
        {
          v = INTVAL (XEXP (XEXP (op, 0), 1));
          v = INTVAL (XEXP (XEXP (op, 0), 1));
          op = XEXP (XEXP (op, 0), 0);
          op = XEXP (XEXP (op, 0), 0);
        }
        }
      else if (GET_CODE (op) == CONST_INT)
      else if (GET_CODE (op) == CONST_INT)
        v = INTVAL (op);
        v = INTVAL (op);
      else if (GET_CODE (op) == CONST_VECTOR
      else if (GET_CODE (op) == CONST_VECTOR
               && GET_CODE (CONST_VECTOR_ELT (op, 0)) == CONST_INT)
               && GET_CODE (CONST_VECTOR_ELT (op, 0)) == CONST_INT)
        v = INTVAL (CONST_VECTOR_ELT (op, 0));
        v = INTVAL (CONST_VECTOR_ELT (op, 0));
 
 
      /* The default for v is 0 which is valid in every range. */
      /* The default for v is 0 which is valid in every range. */
      if (v < spu_builtin_range[range].low
      if (v < spu_builtin_range[range].low
          || v > spu_builtin_range[range].high)
          || v > spu_builtin_range[range].high)
        error ("%s expects an integer literal in the range [%d, %d]. ("
        error ("%s expects an integer literal in the range [%d, %d]. ("
               HOST_WIDE_INT_PRINT_DEC ")",
               HOST_WIDE_INT_PRINT_DEC ")",
               d->name,
               d->name,
               spu_builtin_range[range].low, spu_builtin_range[range].high,
               spu_builtin_range[range].low, spu_builtin_range[range].high,
               v);
               v);
 
 
      switch (p)
      switch (p)
        {
        {
        case SPU_BTI_S10_4:
        case SPU_BTI_S10_4:
          lsbits = 4;
          lsbits = 4;
          break;
          break;
        case SPU_BTI_U16_2:
        case SPU_BTI_U16_2:
          /* This is only used in lqa, and stqa.  Even though the insns
          /* This is only used in lqa, and stqa.  Even though the insns
             encode 16 bits of the address (all but the 2 least
             encode 16 bits of the address (all but the 2 least
             significant), only 14 bits are used because it is masked to
             significant), only 14 bits are used because it is masked to
             be 16 byte aligned. */
             be 16 byte aligned. */
          lsbits = 4;
          lsbits = 4;
          break;
          break;
        case SPU_BTI_S16_2:
        case SPU_BTI_S16_2:
          /* This is used for lqr and stqr. */
          /* This is used for lqr and stqr. */
          lsbits = 2;
          lsbits = 2;
          break;
          break;
        default:
        default:
          lsbits = 0;
          lsbits = 0;
        }
        }
 
 
      if (GET_CODE (op) == LABEL_REF
      if (GET_CODE (op) == LABEL_REF
          || (GET_CODE (op) == SYMBOL_REF
          || (GET_CODE (op) == SYMBOL_REF
              && SYMBOL_REF_FUNCTION_P (op))
              && SYMBOL_REF_FUNCTION_P (op))
          || (v & ((1 << lsbits) - 1)) != 0)
          || (v & ((1 << lsbits) - 1)) != 0)
        warning (0, "%d least significant bits of %s are ignored.", lsbits,
        warning (0, "%d least significant bits of %s are ignored.", lsbits,
                 d->name);
                 d->name);
    }
    }
}
}
 
 
 
 
static int
static int
expand_builtin_args (struct spu_builtin_description *d, tree exp,
expand_builtin_args (struct spu_builtin_description *d, tree exp,
                     rtx target, rtx ops[])
                     rtx target, rtx ops[])
{
{
  enum insn_code icode = (enum insn_code) d->icode;
  enum insn_code icode = (enum insn_code) d->icode;
  int i = 0, a;
  int i = 0, a;
 
 
  /* Expand the arguments into rtl. */
  /* Expand the arguments into rtl. */
 
 
  if (d->parm[0] != SPU_BTI_VOID)
  if (d->parm[0] != SPU_BTI_VOID)
    ops[i++] = target;
    ops[i++] = target;
 
 
  for (a = 0; d->parm[a+1] != SPU_BTI_END_OF_PARAMS; i++, a++)
  for (a = 0; d->parm[a+1] != SPU_BTI_END_OF_PARAMS; i++, a++)
    {
    {
      tree arg = CALL_EXPR_ARG (exp, a);
      tree arg = CALL_EXPR_ARG (exp, a);
      if (arg == 0)
      if (arg == 0)
        abort ();
        abort ();
      ops[i] = expand_expr (arg, NULL_RTX, VOIDmode, EXPAND_NORMAL);
      ops[i] = expand_expr (arg, NULL_RTX, VOIDmode, EXPAND_NORMAL);
    }
    }
 
 
  /* The insn pattern may have additional operands (SCRATCH).
  /* The insn pattern may have additional operands (SCRATCH).
     Return the number of actual non-SCRATCH operands.  */
     Return the number of actual non-SCRATCH operands.  */
  gcc_assert (i <= insn_data[icode].n_operands);
  gcc_assert (i <= insn_data[icode].n_operands);
  return i;
  return i;
}
}
 
 
static rtx
static rtx
spu_expand_builtin_1 (struct spu_builtin_description *d,
spu_expand_builtin_1 (struct spu_builtin_description *d,
                      tree exp, rtx target)
                      tree exp, rtx target)
{
{
  rtx pat;
  rtx pat;
  rtx ops[8];
  rtx ops[8];
  enum insn_code icode = (enum insn_code) d->icode;
  enum insn_code icode = (enum insn_code) d->icode;
  enum machine_mode mode, tmode;
  enum machine_mode mode, tmode;
  int i, p;
  int i, p;
  int n_operands;
  int n_operands;
  tree return_type;
  tree return_type;
 
 
  /* Set up ops[] with values from arglist. */
  /* Set up ops[] with values from arglist. */
  n_operands = expand_builtin_args (d, exp, target, ops);
  n_operands = expand_builtin_args (d, exp, target, ops);
 
 
  /* Handle the target operand which must be operand 0. */
  /* Handle the target operand which must be operand 0. */
  i = 0;
  i = 0;
  if (d->parm[0] != SPU_BTI_VOID)
  if (d->parm[0] != SPU_BTI_VOID)
    {
    {
 
 
      /* We prefer the mode specified for the match_operand otherwise
      /* We prefer the mode specified for the match_operand otherwise
         use the mode from the builtin function prototype. */
         use the mode from the builtin function prototype. */
      tmode = insn_data[d->icode].operand[0].mode;
      tmode = insn_data[d->icode].operand[0].mode;
      if (tmode == VOIDmode)
      if (tmode == VOIDmode)
        tmode = TYPE_MODE (spu_builtin_types[d->parm[0]]);
        tmode = TYPE_MODE (spu_builtin_types[d->parm[0]]);
 
 
      /* Try to use target because not using it can lead to extra copies
      /* Try to use target because not using it can lead to extra copies
         and when we are using all of the registers extra copies leads
         and when we are using all of the registers extra copies leads
         to extra spills.  */
         to extra spills.  */
      if (target && GET_CODE (target) == REG && GET_MODE (target) == tmode)
      if (target && GET_CODE (target) == REG && GET_MODE (target) == tmode)
        ops[0] = target;
        ops[0] = target;
      else
      else
        target = ops[0] = gen_reg_rtx (tmode);
        target = ops[0] = gen_reg_rtx (tmode);
 
 
      if (!(*insn_data[icode].operand[0].predicate) (ops[0], tmode))
      if (!(*insn_data[icode].operand[0].predicate) (ops[0], tmode))
        abort ();
        abort ();
 
 
      i++;
      i++;
    }
    }
 
 
  if (d->fcode == SPU_MASK_FOR_LOAD)
  if (d->fcode == SPU_MASK_FOR_LOAD)
    {
    {
      enum machine_mode mode = insn_data[icode].operand[1].mode;
      enum machine_mode mode = insn_data[icode].operand[1].mode;
      tree arg;
      tree arg;
      rtx addr, op, pat;
      rtx addr, op, pat;
 
 
      /* get addr */
      /* get addr */
      arg = CALL_EXPR_ARG (exp, 0);
      arg = CALL_EXPR_ARG (exp, 0);
      gcc_assert (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE);
      gcc_assert (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE);
      op = expand_expr (arg, NULL_RTX, Pmode, EXPAND_NORMAL);
      op = expand_expr (arg, NULL_RTX, Pmode, EXPAND_NORMAL);
      addr = memory_address (mode, op);
      addr = memory_address (mode, op);
 
 
      /* negate addr */
      /* negate addr */
      op = gen_reg_rtx (GET_MODE (addr));
      op = gen_reg_rtx (GET_MODE (addr));
      emit_insn (gen_rtx_SET (VOIDmode, op,
      emit_insn (gen_rtx_SET (VOIDmode, op,
                 gen_rtx_NEG (GET_MODE (addr), addr)));
                 gen_rtx_NEG (GET_MODE (addr), addr)));
      op = gen_rtx_MEM (mode, op);
      op = gen_rtx_MEM (mode, op);
 
 
      pat = GEN_FCN (icode) (target, op);
      pat = GEN_FCN (icode) (target, op);
      if (!pat)
      if (!pat)
        return 0;
        return 0;
      emit_insn (pat);
      emit_insn (pat);
      return target;
      return target;
    }
    }
 
 
  /* Ignore align_hint, but still expand it's args in case they have
  /* Ignore align_hint, but still expand it's args in case they have
     side effects. */
     side effects. */
  if (icode == CODE_FOR_spu_align_hint)
  if (icode == CODE_FOR_spu_align_hint)
    return 0;
    return 0;
 
 
  /* Handle the rest of the operands. */
  /* Handle the rest of the operands. */
  for (p = 1; i < n_operands; i++, p++)
  for (p = 1; i < n_operands; i++, p++)
    {
    {
      if (insn_data[d->icode].operand[i].mode != VOIDmode)
      if (insn_data[d->icode].operand[i].mode != VOIDmode)
        mode = insn_data[d->icode].operand[i].mode;
        mode = insn_data[d->icode].operand[i].mode;
      else
      else
        mode = TYPE_MODE (spu_builtin_types[d->parm[i]]);
        mode = TYPE_MODE (spu_builtin_types[d->parm[i]]);
 
 
      /* mode can be VOIDmode here for labels */
      /* mode can be VOIDmode here for labels */
 
 
      /* For specific intrinsics with an immediate operand, e.g.,
      /* For specific intrinsics with an immediate operand, e.g.,
         si_ai(), we sometimes need to convert the scalar argument to a
         si_ai(), we sometimes need to convert the scalar argument to a
         vector argument by splatting the scalar. */
         vector argument by splatting the scalar. */
      if (VECTOR_MODE_P (mode)
      if (VECTOR_MODE_P (mode)
          && (GET_CODE (ops[i]) == CONST_INT
          && (GET_CODE (ops[i]) == CONST_INT
              || GET_MODE_CLASS (GET_MODE (ops[i])) == MODE_INT
              || GET_MODE_CLASS (GET_MODE (ops[i])) == MODE_INT
              || GET_MODE_CLASS (GET_MODE (ops[i])) == MODE_FLOAT))
              || GET_MODE_CLASS (GET_MODE (ops[i])) == MODE_FLOAT))
        {
        {
          if (GET_CODE (ops[i]) == CONST_INT)
          if (GET_CODE (ops[i]) == CONST_INT)
            ops[i] = spu_const (mode, INTVAL (ops[i]));
            ops[i] = spu_const (mode, INTVAL (ops[i]));
          else
          else
            {
            {
              rtx reg = gen_reg_rtx (mode);
              rtx reg = gen_reg_rtx (mode);
              enum machine_mode imode = GET_MODE_INNER (mode);
              enum machine_mode imode = GET_MODE_INNER (mode);
              if (!spu_nonmem_operand (ops[i], GET_MODE (ops[i])))
              if (!spu_nonmem_operand (ops[i], GET_MODE (ops[i])))
                ops[i] = force_reg (GET_MODE (ops[i]), ops[i]);
                ops[i] = force_reg (GET_MODE (ops[i]), ops[i]);
              if (imode != GET_MODE (ops[i]))
              if (imode != GET_MODE (ops[i]))
                ops[i] = convert_to_mode (imode, ops[i],
                ops[i] = convert_to_mode (imode, ops[i],
                                          TYPE_UNSIGNED (spu_builtin_types
                                          TYPE_UNSIGNED (spu_builtin_types
                                                         [d->parm[i]]));
                                                         [d->parm[i]]));
              emit_insn (gen_spu_splats (reg, ops[i]));
              emit_insn (gen_spu_splats (reg, ops[i]));
              ops[i] = reg;
              ops[i] = reg;
            }
            }
        }
        }
 
 
      spu_check_builtin_parm (d, ops[i], d->parm[p]);
      spu_check_builtin_parm (d, ops[i], d->parm[p]);
 
 
      if (!(*insn_data[icode].operand[i].predicate) (ops[i], mode))
      if (!(*insn_data[icode].operand[i].predicate) (ops[i], mode))
        ops[i] = spu_force_reg (mode, ops[i]);
        ops[i] = spu_force_reg (mode, ops[i]);
    }
    }
 
 
  switch (n_operands)
  switch (n_operands)
    {
    {
    case 0:
    case 0:
      pat = GEN_FCN (icode) (0);
      pat = GEN_FCN (icode) (0);
      break;
      break;
    case 1:
    case 1:
      pat = GEN_FCN (icode) (ops[0]);
      pat = GEN_FCN (icode) (ops[0]);
      break;
      break;
    case 2:
    case 2:
      pat = GEN_FCN (icode) (ops[0], ops[1]);
      pat = GEN_FCN (icode) (ops[0], ops[1]);
      break;
      break;
    case 3:
    case 3:
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2]);
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2]);
      break;
      break;
    case 4:
    case 4:
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]);
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]);
      break;
      break;
    case 5:
    case 5:
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3], ops[4]);
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3], ops[4]);
      break;
      break;
    case 6:
    case 6:
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3], ops[4], ops[5]);
      pat = GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3], ops[4], ops[5]);
      break;
      break;
    default:
    default:
      abort ();
      abort ();
    }
    }
 
 
  if (!pat)
  if (!pat)
    abort ();
    abort ();
 
 
  if (d->type == B_CALL || d->type == B_BISLED)
  if (d->type == B_CALL || d->type == B_BISLED)
    emit_call_insn (pat);
    emit_call_insn (pat);
  else if (d->type == B_JUMP)
  else if (d->type == B_JUMP)
    {
    {
      emit_jump_insn (pat);
      emit_jump_insn (pat);
      emit_barrier ();
      emit_barrier ();
    }
    }
  else
  else
    emit_insn (pat);
    emit_insn (pat);
 
 
  return_type = spu_builtin_types[d->parm[0]];
  return_type = spu_builtin_types[d->parm[0]];
  if (d->parm[0] != SPU_BTI_VOID
  if (d->parm[0] != SPU_BTI_VOID
      && GET_MODE (target) != TYPE_MODE (return_type))
      && GET_MODE (target) != TYPE_MODE (return_type))
    {
    {
      /* target is the return value.  It should always be the mode of
      /* target is the return value.  It should always be the mode of
         the builtin function prototype. */
         the builtin function prototype. */
      target = spu_force_reg (TYPE_MODE (return_type), target);
      target = spu_force_reg (TYPE_MODE (return_type), target);
    }
    }
 
 
  return target;
  return target;
}
}
 
 
rtx
rtx
spu_expand_builtin (tree exp,
spu_expand_builtin (tree exp,
                    rtx target,
                    rtx target,
                    rtx subtarget ATTRIBUTE_UNUSED,
                    rtx subtarget ATTRIBUTE_UNUSED,
                    enum machine_mode mode ATTRIBUTE_UNUSED,
                    enum machine_mode mode ATTRIBUTE_UNUSED,
                    int ignore ATTRIBUTE_UNUSED)
                    int ignore ATTRIBUTE_UNUSED)
{
{
  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) - END_BUILTINS;
  unsigned int fcode = DECL_FUNCTION_CODE (fndecl) - END_BUILTINS;
  struct spu_builtin_description *d;
  struct spu_builtin_description *d;
 
 
  if (fcode < NUM_SPU_BUILTINS)
  if (fcode < NUM_SPU_BUILTINS)
    {
    {
      d = &spu_builtins[fcode];
      d = &spu_builtins[fcode];
 
 
      return spu_expand_builtin_1 (d, exp, target);
      return spu_expand_builtin_1 (d, exp, target);
    }
    }
  abort ();
  abort ();
}
}
 
 
/* Implement targetm.vectorize.builtin_mul_widen_even.  */
/* Implement targetm.vectorize.builtin_mul_widen_even.  */
static tree
static tree
spu_builtin_mul_widen_even (tree type)
spu_builtin_mul_widen_even (tree type)
{
{
  switch (TYPE_MODE (type))
  switch (TYPE_MODE (type))
    {
    {
    case V8HImode:
    case V8HImode:
      if (TYPE_UNSIGNED (type))
      if (TYPE_UNSIGNED (type))
        return spu_builtins[SPU_MULE_0].fndecl;
        return spu_builtins[SPU_MULE_0].fndecl;
      else
      else
        return spu_builtins[SPU_MULE_1].fndecl;
        return spu_builtins[SPU_MULE_1].fndecl;
      break;
      break;
    default:
    default:
      return NULL_TREE;
      return NULL_TREE;
    }
    }
}
}
 
 
/* Implement targetm.vectorize.builtin_mul_widen_odd.  */
/* Implement targetm.vectorize.builtin_mul_widen_odd.  */
static tree
static tree
spu_builtin_mul_widen_odd (tree type)
spu_builtin_mul_widen_odd (tree type)
{
{
  switch (TYPE_MODE (type))
  switch (TYPE_MODE (type))
    {
    {
    case V8HImode:
    case V8HImode:
      if (TYPE_UNSIGNED (type))
      if (TYPE_UNSIGNED (type))
        return spu_builtins[SPU_MULO_1].fndecl;
        return spu_builtins[SPU_MULO_1].fndecl;
      else
      else
        return spu_builtins[SPU_MULO_0].fndecl;
        return spu_builtins[SPU_MULO_0].fndecl;
      break;
      break;
    default:
    default:
      return NULL_TREE;
      return NULL_TREE;
    }
    }
}
}
 
 
/* Implement targetm.vectorize.builtin_mask_for_load.  */
/* Implement targetm.vectorize.builtin_mask_for_load.  */
static tree
static tree
spu_builtin_mask_for_load (void)
spu_builtin_mask_for_load (void)
{
{
  struct spu_builtin_description *d = &spu_builtins[SPU_MASK_FOR_LOAD];
  struct spu_builtin_description *d = &spu_builtins[SPU_MASK_FOR_LOAD];
  gcc_assert (d);
  gcc_assert (d);
  return d->fndecl;
  return d->fndecl;
}
}
 
 
/* Implement targetm.vectorize.builtin_vectorization_cost.  */
/* Implement targetm.vectorize.builtin_vectorization_cost.  */
static int
static int
spu_builtin_vectorization_cost (bool runtime_test)
spu_builtin_vectorization_cost (bool runtime_test)
{
{
  /* If the branch of the runtime test is taken - i.e. - the vectorized
  /* If the branch of the runtime test is taken - i.e. - the vectorized
     version is skipped - this incurs a misprediction cost (because the
     version is skipped - this incurs a misprediction cost (because the
     vectorized version is expected to be the fall-through).  So we subtract
     vectorized version is expected to be the fall-through).  So we subtract
     the latency of a mispredicted branch from the costs that are incurred
     the latency of a mispredicted branch from the costs that are incurred
     when the vectorized version is executed.  */
     when the vectorized version is executed.  */
  if (runtime_test)
  if (runtime_test)
    return -19;
    return -19;
  else
  else
    return 0;
    return 0;
}
}
 
 
/* Return true iff, data reference of TYPE can reach vector alignment (16)
/* Return true iff, data reference of TYPE can reach vector alignment (16)
   after applying N number of iterations.  This routine does not determine
   after applying N number of iterations.  This routine does not determine
   how may iterations are required to reach desired alignment.  */
   how may iterations are required to reach desired alignment.  */
 
 
static bool
static bool
spu_vector_alignment_reachable (const_tree type ATTRIBUTE_UNUSED, bool is_packed)
spu_vector_alignment_reachable (const_tree type ATTRIBUTE_UNUSED, bool is_packed)
{
{
  if (is_packed)
  if (is_packed)
    return false;
    return false;
 
 
  /* All other types are naturally aligned.  */
  /* All other types are naturally aligned.  */
  return true;
  return true;
}
}
 
 
/* Implement targetm.vectorize.builtin_vec_perm.  */
/* Implement targetm.vectorize.builtin_vec_perm.  */
tree
tree
spu_builtin_vec_perm (tree type, tree *mask_element_type)
spu_builtin_vec_perm (tree type, tree *mask_element_type)
{
{
  struct spu_builtin_description *d;
  struct spu_builtin_description *d;
 
 
  *mask_element_type = unsigned_char_type_node;
  *mask_element_type = unsigned_char_type_node;
 
 
  switch (TYPE_MODE (type))
  switch (TYPE_MODE (type))
    {
    {
    case V16QImode:
    case V16QImode:
      if (TYPE_UNSIGNED (type))
      if (TYPE_UNSIGNED (type))
        d = &spu_builtins[SPU_SHUFFLE_0];
        d = &spu_builtins[SPU_SHUFFLE_0];
      else
      else
        d = &spu_builtins[SPU_SHUFFLE_1];
        d = &spu_builtins[SPU_SHUFFLE_1];
      break;
      break;
 
 
    case V8HImode:
    case V8HImode:
      if (TYPE_UNSIGNED (type))
      if (TYPE_UNSIGNED (type))
        d = &spu_builtins[SPU_SHUFFLE_2];
        d = &spu_builtins[SPU_SHUFFLE_2];
      else
      else
        d = &spu_builtins[SPU_SHUFFLE_3];
        d = &spu_builtins[SPU_SHUFFLE_3];
      break;
      break;
 
 
    case V4SImode:
    case V4SImode:
      if (TYPE_UNSIGNED (type))
      if (TYPE_UNSIGNED (type))
        d = &spu_builtins[SPU_SHUFFLE_4];
        d = &spu_builtins[SPU_SHUFFLE_4];
      else
      else
        d = &spu_builtins[SPU_SHUFFLE_5];
        d = &spu_builtins[SPU_SHUFFLE_5];
      break;
      break;
 
 
    case V2DImode:
    case V2DImode:
      if (TYPE_UNSIGNED (type))
      if (TYPE_UNSIGNED (type))
        d = &spu_builtins[SPU_SHUFFLE_6];
        d = &spu_builtins[SPU_SHUFFLE_6];
      else
      else
        d = &spu_builtins[SPU_SHUFFLE_7];
        d = &spu_builtins[SPU_SHUFFLE_7];
      break;
      break;
 
 
    case V4SFmode:
    case V4SFmode:
      d = &spu_builtins[SPU_SHUFFLE_8];
      d = &spu_builtins[SPU_SHUFFLE_8];
      break;
      break;
 
 
    case V2DFmode:
    case V2DFmode:
      d = &spu_builtins[SPU_SHUFFLE_9];
      d = &spu_builtins[SPU_SHUFFLE_9];
      break;
      break;
 
 
    default:
    default:
      return NULL_TREE;
      return NULL_TREE;
    }
    }
 
 
  gcc_assert (d);
  gcc_assert (d);
  return d->fndecl;
  return d->fndecl;
}
}
 
 
/* Return the appropriate mode for a named address pointer.  */
/* Return the appropriate mode for a named address pointer.  */
static enum machine_mode
static enum machine_mode
spu_addr_space_pointer_mode (addr_space_t addrspace)
spu_addr_space_pointer_mode (addr_space_t addrspace)
{
{
  switch (addrspace)
  switch (addrspace)
    {
    {
    case ADDR_SPACE_GENERIC:
    case ADDR_SPACE_GENERIC:
      return ptr_mode;
      return ptr_mode;
    case ADDR_SPACE_EA:
    case ADDR_SPACE_EA:
      return EAmode;
      return EAmode;
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
/* Return the appropriate mode for a named address address.  */
/* Return the appropriate mode for a named address address.  */
static enum machine_mode
static enum machine_mode
spu_addr_space_address_mode (addr_space_t addrspace)
spu_addr_space_address_mode (addr_space_t addrspace)
{
{
  switch (addrspace)
  switch (addrspace)
    {
    {
    case ADDR_SPACE_GENERIC:
    case ADDR_SPACE_GENERIC:
      return Pmode;
      return Pmode;
    case ADDR_SPACE_EA:
    case ADDR_SPACE_EA:
      return EAmode;
      return EAmode;
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
/* Determine if one named address space is a subset of another.  */
/* Determine if one named address space is a subset of another.  */
 
 
static bool
static bool
spu_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
spu_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
{
{
  gcc_assert (subset == ADDR_SPACE_GENERIC || subset == ADDR_SPACE_EA);
  gcc_assert (subset == ADDR_SPACE_GENERIC || subset == ADDR_SPACE_EA);
  gcc_assert (superset == ADDR_SPACE_GENERIC || superset == ADDR_SPACE_EA);
  gcc_assert (superset == ADDR_SPACE_GENERIC || superset == ADDR_SPACE_EA);
 
 
  if (subset == superset)
  if (subset == superset)
    return true;
    return true;
 
 
  /* If we have -mno-address-space-conversion, treat __ea and generic as not
  /* If we have -mno-address-space-conversion, treat __ea and generic as not
     being subsets but instead as disjoint address spaces.  */
     being subsets but instead as disjoint address spaces.  */
  else if (!TARGET_ADDRESS_SPACE_CONVERSION)
  else if (!TARGET_ADDRESS_SPACE_CONVERSION)
    return false;
    return false;
 
 
  else
  else
    return (subset == ADDR_SPACE_GENERIC && superset == ADDR_SPACE_EA);
    return (subset == ADDR_SPACE_GENERIC && superset == ADDR_SPACE_EA);
}
}
 
 
/* Convert from one address space to another.  */
/* Convert from one address space to another.  */
static rtx
static rtx
spu_addr_space_convert (rtx op, tree from_type, tree to_type)
spu_addr_space_convert (rtx op, tree from_type, tree to_type)
{
{
  addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
  addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
  addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
  addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
 
 
  gcc_assert (from_as == ADDR_SPACE_GENERIC || from_as == ADDR_SPACE_EA);
  gcc_assert (from_as == ADDR_SPACE_GENERIC || from_as == ADDR_SPACE_EA);
  gcc_assert (to_as == ADDR_SPACE_GENERIC || to_as == ADDR_SPACE_EA);
  gcc_assert (to_as == ADDR_SPACE_GENERIC || to_as == ADDR_SPACE_EA);
 
 
  if (to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_EA)
  if (to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_EA)
    {
    {
      rtx result, ls;
      rtx result, ls;
 
 
      ls = gen_const_mem (DImode,
      ls = gen_const_mem (DImode,
                          gen_rtx_SYMBOL_REF (Pmode, "__ea_local_store"));
                          gen_rtx_SYMBOL_REF (Pmode, "__ea_local_store"));
      set_mem_align (ls, 128);
      set_mem_align (ls, 128);
 
 
      result = gen_reg_rtx (Pmode);
      result = gen_reg_rtx (Pmode);
      ls = force_reg (Pmode, convert_modes (Pmode, DImode, ls, 1));
      ls = force_reg (Pmode, convert_modes (Pmode, DImode, ls, 1));
      op = force_reg (Pmode, convert_modes (Pmode, EAmode, op, 1));
      op = force_reg (Pmode, convert_modes (Pmode, EAmode, op, 1));
      ls = emit_conditional_move (ls, NE, op, const0_rtx, Pmode,
      ls = emit_conditional_move (ls, NE, op, const0_rtx, Pmode,
                                          ls, const0_rtx, Pmode, 1);
                                          ls, const0_rtx, Pmode, 1);
 
 
      emit_insn (gen_subsi3 (result, op, ls));
      emit_insn (gen_subsi3 (result, op, ls));
 
 
      return result;
      return result;
    }
    }
 
 
  else if (to_as == ADDR_SPACE_EA && from_as == ADDR_SPACE_GENERIC)
  else if (to_as == ADDR_SPACE_EA && from_as == ADDR_SPACE_GENERIC)
    {
    {
      rtx result, ls;
      rtx result, ls;
 
 
      ls = gen_const_mem (DImode,
      ls = gen_const_mem (DImode,
                          gen_rtx_SYMBOL_REF (Pmode, "__ea_local_store"));
                          gen_rtx_SYMBOL_REF (Pmode, "__ea_local_store"));
      set_mem_align (ls, 128);
      set_mem_align (ls, 128);
 
 
      result = gen_reg_rtx (EAmode);
      result = gen_reg_rtx (EAmode);
      ls = force_reg (EAmode, convert_modes (EAmode, DImode, ls, 1));
      ls = force_reg (EAmode, convert_modes (EAmode, DImode, ls, 1));
      op = force_reg (Pmode, op);
      op = force_reg (Pmode, op);
      ls = emit_conditional_move (ls, NE, op, const0_rtx, Pmode,
      ls = emit_conditional_move (ls, NE, op, const0_rtx, Pmode,
                                          ls, const0_rtx, EAmode, 1);
                                          ls, const0_rtx, EAmode, 1);
      op = force_reg (EAmode, convert_modes (EAmode, Pmode, op, 1));
      op = force_reg (EAmode, convert_modes (EAmode, Pmode, op, 1));
 
 
      if (EAmode == SImode)
      if (EAmode == SImode)
        emit_insn (gen_addsi3 (result, op, ls));
        emit_insn (gen_addsi3 (result, op, ls));
      else
      else
        emit_insn (gen_adddi3 (result, op, ls));
        emit_insn (gen_adddi3 (result, op, ls));
 
 
      return result;
      return result;
    }
    }
 
 
  else
  else
    gcc_unreachable ();
    gcc_unreachable ();
}
}
 
 
 
 
/* Count the total number of instructions in each pipe and return the
/* Count the total number of instructions in each pipe and return the
   maximum, which is used as the Minimum Iteration Interval (MII)
   maximum, which is used as the Minimum Iteration Interval (MII)
   in the modulo scheduler.  get_pipe() will return -2, -1, 0, or 1.
   in the modulo scheduler.  get_pipe() will return -2, -1, 0, or 1.
   -2 are instructions that can go in pipe0 or pipe1.  */
   -2 are instructions that can go in pipe0 or pipe1.  */
static int
static int
spu_sms_res_mii (struct ddg *g)
spu_sms_res_mii (struct ddg *g)
{
{
  int i;
  int i;
  unsigned t[4] = {0, 0, 0, 0};
  unsigned t[4] = {0, 0, 0, 0};
 
 
  for (i = 0; i < g->num_nodes; i++)
  for (i = 0; i < g->num_nodes; i++)
    {
    {
      rtx insn = g->nodes[i].insn;
      rtx insn = g->nodes[i].insn;
      int p = get_pipe (insn) + 2;
      int p = get_pipe (insn) + 2;
 
 
      assert (p >= 0);
      assert (p >= 0);
      assert (p < 4);
      assert (p < 4);
 
 
      t[p]++;
      t[p]++;
      if (dump_file && INSN_P (insn))
      if (dump_file && INSN_P (insn))
            fprintf (dump_file, "i%d %s %d %d\n",
            fprintf (dump_file, "i%d %s %d %d\n",
                     INSN_UID (insn),
                     INSN_UID (insn),
                     insn_data[INSN_CODE(insn)].name,
                     insn_data[INSN_CODE(insn)].name,
                     p, t[p]);
                     p, t[p]);
    }
    }
  if (dump_file)
  if (dump_file)
    fprintf (dump_file, "%d %d %d %d\n", t[0], t[1], t[2], t[3]);
    fprintf (dump_file, "%d %d %d %d\n", t[0], t[1], t[2], t[3]);
 
 
  return MAX ((t[0] + t[2] + t[3] + 1) / 2, MAX (t[2], t[3]));
  return MAX ((t[0] + t[2] + t[3] + 1) / 2, MAX (t[2], t[3]));
}
}
 
 
 
 
void
void
spu_init_expanders (void)
spu_init_expanders (void)
{
{
  if (cfun)
  if (cfun)
    {
    {
      rtx r0, r1;
      rtx r0, r1;
      /* HARD_FRAME_REGISTER is only 128 bit aligned when
      /* HARD_FRAME_REGISTER is only 128 bit aligned when
         frame_pointer_needed is true.  We don't know that until we're
         frame_pointer_needed is true.  We don't know that until we're
         expanding the prologue. */
         expanding the prologue. */
      REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 8;
      REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 8;
 
 
      /* A number of passes use LAST_VIRTUAL_REGISTER+1 and
      /* A number of passes use LAST_VIRTUAL_REGISTER+1 and
         LAST_VIRTUAL_REGISTER+2 to test the back-end.  We want them
         LAST_VIRTUAL_REGISTER+2 to test the back-end.  We want them
         to be treated as aligned, so generate them here. */
         to be treated as aligned, so generate them here. */
      r0 = gen_reg_rtx (SImode);
      r0 = gen_reg_rtx (SImode);
      r1 = gen_reg_rtx (SImode);
      r1 = gen_reg_rtx (SImode);
      mark_reg_pointer (r0, 128);
      mark_reg_pointer (r0, 128);
      mark_reg_pointer (r1, 128);
      mark_reg_pointer (r1, 128);
      gcc_assert (REGNO (r0) == LAST_VIRTUAL_REGISTER + 1
      gcc_assert (REGNO (r0) == LAST_VIRTUAL_REGISTER + 1
                  && REGNO (r1) == LAST_VIRTUAL_REGISTER + 2);
                  && REGNO (r1) == LAST_VIRTUAL_REGISTER + 2);
    }
    }
}
}
 
 
static enum machine_mode
static enum machine_mode
spu_libgcc_cmp_return_mode (void)
spu_libgcc_cmp_return_mode (void)
{
{
 
 
/* For SPU word mode is TI mode so it is better to use SImode
/* For SPU word mode is TI mode so it is better to use SImode
   for compare returns.  */
   for compare returns.  */
  return SImode;
  return SImode;
}
}
 
 
static enum machine_mode
static enum machine_mode
spu_libgcc_shift_count_mode (void)
spu_libgcc_shift_count_mode (void)
{
{
/* For SPU word mode is TI mode so it is better to use SImode
/* For SPU word mode is TI mode so it is better to use SImode
   for shift counts.  */
   for shift counts.  */
  return SImode;
  return SImode;
}
}
 
 
/* An early place to adjust some flags after GCC has finished processing
/* An early place to adjust some flags after GCC has finished processing
 * them. */
 * them. */
static void
static void
asm_file_start (void)
asm_file_start (void)
{
{
  /* Variable tracking should be run after all optimizations which
  /* Variable tracking should be run after all optimizations which
     change order of insns.  It also needs a valid CFG. */
     change order of insns.  It also needs a valid CFG. */
  spu_flag_var_tracking = flag_var_tracking;
  spu_flag_var_tracking = flag_var_tracking;
  flag_var_tracking = 0;
  flag_var_tracking = 0;
 
 
  default_file_start ();
  default_file_start ();
}
}
 
 
/* Implement targetm.section_type_flags.  */
/* Implement targetm.section_type_flags.  */
static unsigned int
static unsigned int
spu_section_type_flags (tree decl, const char *name, int reloc)
spu_section_type_flags (tree decl, const char *name, int reloc)
{
{
  /* .toe needs to have type @nobits.  */
  /* .toe needs to have type @nobits.  */
  if (strcmp (name, ".toe") == 0)
  if (strcmp (name, ".toe") == 0)
    return SECTION_BSS;
    return SECTION_BSS;
  /* Don't load _ea into the current address space.  */
  /* Don't load _ea into the current address space.  */
  if (strcmp (name, "._ea") == 0)
  if (strcmp (name, "._ea") == 0)
    return SECTION_WRITE | SECTION_DEBUG;
    return SECTION_WRITE | SECTION_DEBUG;
  return default_section_type_flags (decl, name, reloc);
  return default_section_type_flags (decl, name, reloc);
}
}
 
 
/* Implement targetm.select_section.  */
/* Implement targetm.select_section.  */
static section *
static section *
spu_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
spu_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
{
{
  /* Variables and constants defined in the __ea address space
  /* Variables and constants defined in the __ea address space
     go into a special section named "._ea".  */
     go into a special section named "._ea".  */
  if (TREE_TYPE (decl) != error_mark_node
  if (TREE_TYPE (decl) != error_mark_node
      && TYPE_ADDR_SPACE (TREE_TYPE (decl)) == ADDR_SPACE_EA)
      && TYPE_ADDR_SPACE (TREE_TYPE (decl)) == ADDR_SPACE_EA)
    {
    {
      /* We might get called with string constants, but get_named_section
      /* We might get called with string constants, but get_named_section
         doesn't like them as they are not DECLs.  Also, we need to set
         doesn't like them as they are not DECLs.  Also, we need to set
         flags in that case.  */
         flags in that case.  */
      if (!DECL_P (decl))
      if (!DECL_P (decl))
        return get_section ("._ea", SECTION_WRITE | SECTION_DEBUG, NULL);
        return get_section ("._ea", SECTION_WRITE | SECTION_DEBUG, NULL);
 
 
      return get_named_section (decl, "._ea", reloc);
      return get_named_section (decl, "._ea", reloc);
    }
    }
 
 
  return default_elf_select_section (decl, reloc, align);
  return default_elf_select_section (decl, reloc, align);
}
}
 
 
/* Implement targetm.unique_section.  */
/* Implement targetm.unique_section.  */
static void
static void
spu_unique_section (tree decl, int reloc)
spu_unique_section (tree decl, int reloc)
{
{
  /* We don't support unique section names in the __ea address
  /* We don't support unique section names in the __ea address
     space for now.  */
     space for now.  */
  if (TREE_TYPE (decl) != error_mark_node
  if (TREE_TYPE (decl) != error_mark_node
      && TYPE_ADDR_SPACE (TREE_TYPE (decl)) != 0)
      && TYPE_ADDR_SPACE (TREE_TYPE (decl)) != 0)
    return;
    return;
 
 
  default_unique_section (decl, reloc);
  default_unique_section (decl, reloc);
}
}
 
 
/* Generate a constant or register which contains 2^SCALE.  We assume
/* Generate a constant or register which contains 2^SCALE.  We assume
   the result is valid for MODE.  Currently, MODE must be V4SFmode and
   the result is valid for MODE.  Currently, MODE must be V4SFmode and
   SCALE must be SImode. */
   SCALE must be SImode. */
rtx
rtx
spu_gen_exp2 (enum machine_mode mode, rtx scale)
spu_gen_exp2 (enum machine_mode mode, rtx scale)
{
{
  gcc_assert (mode == V4SFmode);
  gcc_assert (mode == V4SFmode);
  gcc_assert (GET_MODE (scale) == SImode || GET_CODE (scale) == CONST_INT);
  gcc_assert (GET_MODE (scale) == SImode || GET_CODE (scale) == CONST_INT);
  if (GET_CODE (scale) != CONST_INT)
  if (GET_CODE (scale) != CONST_INT)
    {
    {
      /* unsigned int exp = (127 + scale) << 23;
      /* unsigned int exp = (127 + scale) << 23;
        __vector float m = (__vector float) spu_splats (exp); */
        __vector float m = (__vector float) spu_splats (exp); */
      rtx reg = force_reg (SImode, scale);
      rtx reg = force_reg (SImode, scale);
      rtx exp = gen_reg_rtx (SImode);
      rtx exp = gen_reg_rtx (SImode);
      rtx mul = gen_reg_rtx (mode);
      rtx mul = gen_reg_rtx (mode);
      emit_insn (gen_addsi3 (exp, reg, GEN_INT (127)));
      emit_insn (gen_addsi3 (exp, reg, GEN_INT (127)));
      emit_insn (gen_ashlsi3 (exp, exp, GEN_INT (23)));
      emit_insn (gen_ashlsi3 (exp, exp, GEN_INT (23)));
      emit_insn (gen_spu_splats (mul, gen_rtx_SUBREG (GET_MODE_INNER (mode), exp, 0)));
      emit_insn (gen_spu_splats (mul, gen_rtx_SUBREG (GET_MODE_INNER (mode), exp, 0)));
      return mul;
      return mul;
    }
    }
  else
  else
    {
    {
      HOST_WIDE_INT exp = 127 + INTVAL (scale);
      HOST_WIDE_INT exp = 127 + INTVAL (scale);
      unsigned char arr[16];
      unsigned char arr[16];
      arr[0] = arr[4] = arr[8] = arr[12] = exp >> 1;
      arr[0] = arr[4] = arr[8] = arr[12] = exp >> 1;
      arr[1] = arr[5] = arr[9] = arr[13] = exp << 7;
      arr[1] = arr[5] = arr[9] = arr[13] = exp << 7;
      arr[2] = arr[6] = arr[10] = arr[14] = 0;
      arr[2] = arr[6] = arr[10] = arr[14] = 0;
      arr[3] = arr[7] = arr[11] = arr[15] = 0;
      arr[3] = arr[7] = arr[11] = arr[15] = 0;
      return array_to_constant (mode, arr);
      return array_to_constant (mode, arr);
    }
    }
}
}
 
 
/* After reload, just change the convert into a move instruction
/* After reload, just change the convert into a move instruction
   or a dead instruction. */
   or a dead instruction. */
void
void
spu_split_convert (rtx ops[])
spu_split_convert (rtx ops[])
{
{
  if (REGNO (ops[0]) == REGNO (ops[1]))
  if (REGNO (ops[0]) == REGNO (ops[1]))
    emit_note (NOTE_INSN_DELETED);
    emit_note (NOTE_INSN_DELETED);
  else
  else
    {
    {
      /* Use TImode always as this might help hard reg copyprop.  */
      /* Use TImode always as this might help hard reg copyprop.  */
      rtx op0 = gen_rtx_REG (TImode, REGNO (ops[0]));
      rtx op0 = gen_rtx_REG (TImode, REGNO (ops[0]));
      rtx op1 = gen_rtx_REG (TImode, REGNO (ops[1]));
      rtx op1 = gen_rtx_REG (TImode, REGNO (ops[1]));
      emit_insn (gen_move_insn (op0, op1));
      emit_insn (gen_move_insn (op0, op1));
    }
    }
}
}
 
 
void
void
spu_function_profiler (FILE * file, int labelno)
spu_function_profiler (FILE * file, int labelno)
{
{
  fprintf (file, "# profile\n");
  fprintf (file, "# profile\n");
  fprintf (file, "brsl $75,  _mcount\n");
  fprintf (file, "brsl $75,  _mcount\n");
}
}
 
 
#include "gt-spu.h"
#include "gt-spu.h"
 
 

powered by: WebSVN 2.1.0

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