/* 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"
|
|
|