diff options
Diffstat (limited to 'gcc/config/frv')
-rw-r--r-- | gcc/config/frv/cmovd.c | 51 | ||||
-rw-r--r-- | gcc/config/frv/cmovh.c | 47 | ||||
-rw-r--r-- | gcc/config/frv/cmovw.c | 51 | ||||
-rw-r--r-- | gcc/config/frv/constraints.md | 174 | ||||
-rw-r--r-- | gcc/config/frv/frv-asm.h | 48 | ||||
-rw-r--r-- | gcc/config/frv/frv-modes.def | 34 | ||||
-rw-r--r-- | gcc/config/frv/frv-protos.h | 195 | ||||
-rw-r--r-- | gcc/config/frv/frv.c | 9680 | ||||
-rw-r--r-- | gcc/config/frv/frv.h | 2188 | ||||
-rw-r--r-- | gcc/config/frv/frv.md | 8022 | ||||
-rw-r--r-- | gcc/config/frv/frv.opt | 199 | ||||
-rw-r--r-- | gcc/config/frv/frvbegin.c | 157 | ||||
-rw-r--r-- | gcc/config/frv/frvend.c | 70 | ||||
-rw-r--r-- | gcc/config/frv/lib1funcs.asm | 269 | ||||
-rw-r--r-- | gcc/config/frv/libgcc-frv.ver | 73 | ||||
-rw-r--r-- | gcc/config/frv/linux.h | 75 | ||||
-rw-r--r-- | gcc/config/frv/modi.c | 4 | ||||
-rw-r--r-- | gcc/config/frv/predicates.md | 1543 | ||||
-rw-r--r-- | gcc/config/frv/t-frv | 113 | ||||
-rw-r--r-- | gcc/config/frv/t-linux | 33 | ||||
-rw-r--r-- | gcc/config/frv/uitod.c | 4 | ||||
-rw-r--r-- | gcc/config/frv/uitof.c | 4 | ||||
-rw-r--r-- | gcc/config/frv/ulltod.c | 4 | ||||
-rw-r--r-- | gcc/config/frv/ulltof.c | 4 | ||||
-rw-r--r-- | gcc/config/frv/umodi.c | 4 |
25 files changed, 23046 insertions, 0 deletions
diff --git a/gcc/config/frv/cmovd.c b/gcc/config/frv/cmovd.c new file mode 100644 index 000000000..e46070aac --- /dev/null +++ b/gcc/config/frv/cmovd.c @@ -0,0 +1,51 @@ +/* Move double-word library function. + Copyright (C) 2000, 2003, 2009 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +void +__cmovd (long long *dest, const long long *src, unsigned len) +{ + unsigned i; + unsigned num = len >> 3; + unsigned xlen = len & ~7; + char *dest_byte = (char *)dest; + const char *src_byte = (const char *)src; + + if (dest_byte < src_byte || dest_byte > src_byte+len) + { + for (i = 0; i < num; i++) + dest[i] = src[i]; + + while (len > xlen) + { + dest_byte[xlen] = src_byte[xlen]; + xlen++; + } + } + else + { + while (len-- > 0) + dest_byte[len] = src_byte[len]; + } +} diff --git a/gcc/config/frv/cmovh.c b/gcc/config/frv/cmovh.c new file mode 100644 index 000000000..6b0901d95 --- /dev/null +++ b/gcc/config/frv/cmovh.c @@ -0,0 +1,47 @@ +/* Move half-word library function. + Copyright (C) 2000, 2003, 2009 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +void +__cmovh (short *dest, const short *src, unsigned len) +{ + unsigned i; + unsigned num = len >> 1; + char *dest_byte = (char *)dest; + const char *src_byte = (const char *)src; + + if (dest_byte < src_byte || dest_byte > src_byte+len) + { + for (i = 0; i < num; i++) + dest[i] = src[i]; + + if ((len & 1) != 0) + dest_byte[len-1] = src_byte[len-1]; + } + else + { + while (len-- > 0) + dest_byte[len] = src_byte[len]; + } +} diff --git a/gcc/config/frv/cmovw.c b/gcc/config/frv/cmovw.c new file mode 100644 index 000000000..f27db75aa --- /dev/null +++ b/gcc/config/frv/cmovw.c @@ -0,0 +1,51 @@ +/* Move word library function. + Copyright (C) 2000, 2003, 2009 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +void +__cmovw (int *dest, const int *src, unsigned len) +{ + unsigned i; + unsigned num = len >> 2; + unsigned xlen = len & ~3; + char *dest_byte = (char *)dest; + const char *src_byte = (const char *)src; + + if (dest_byte < src_byte || dest_byte > src_byte+len) + { + for (i = 0; i < num; i++) + dest[i] = src[i]; + + while (len > xlen) + { + dest_byte[xlen] = src_byte[xlen]; + xlen++; + } + } + else + { + while (len-- > 0) + dest_byte[len] = src_byte[len]; + } +} diff --git a/gcc/config/frv/constraints.md b/gcc/config/frv/constraints.md new file mode 100644 index 000000000..792706b03 --- /dev/null +++ b/gcc/config/frv/constraints.md @@ -0,0 +1,174 @@ +;; Constraint definitions for FRV. +;; Copyright (C) 2001 Free Software Foundation, Inc. +;; +;; This file is part of GCC. +;; +;; GCC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; GCC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;; Register constraints. +(define_register_constraint "a" "ACC_REGS" + "@internal") + +(define_register_constraint "b" "EVEN_ACC_REGS" + "@internal") + +(define_register_constraint "c" "CC_REGS" + "@internal") + +(define_register_constraint "d" "GPR_REGS" + "@internal") + +(define_register_constraint "e" "EVEN_REGS" + "@internal") + +(define_register_constraint "f" "FPR_REGS" + "@internal") + +(define_register_constraint "h" "FEVEN_REGS" + "@internal") + +(define_register_constraint "l" "LR_REG" + "@internal") + +(define_register_constraint "q" "QUAD_REGS" + "@internal") + +(define_register_constraint "t" "ICC_REGS" + "@internal") + +(define_register_constraint "u" "FCC_REGS" + "@internal") + +(define_register_constraint "v" "ICR_REGS" + "@internal") + +(define_register_constraint "w" "FCR_REGS" + "@internal") + +(define_register_constraint "x" "QUAD_FPR_REGS" + "@internal") + +(define_register_constraint "y" "LCR_REG" + "@internal") + +(define_register_constraint "z" "SPR_REGS" + "@internal") + +(define_register_constraint "A" "QUAD_ACC_REGS" + "@internal") + +(define_register_constraint "B" "ACCG_REGS" + "@internal") + +(define_register_constraint "C" "CR_REGS" + "@internal") + +(define_register_constraint "D89" "GR89_REGS" + "@internal") + +(define_register_constraint "D09" "GR9_REGS" + "@internal") + +(define_register_constraint "D08" "GR8_REGS" + "@internal") + +(define_register_constraint "D14" "FDPIC_FPTR_REGS" + "@internal") + +(define_register_constraint "D15" "FDPIC_REGS" + "@internal") + +(define_register_constraint "W" "FDPIC_CALL_REGS" + "@internal") + +(define_register_constraint "Z" "FDPIC_REGS" + "@internal") + +;; Integer constraints. +(define_constraint "I" + "A signed 6-bit immediate." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -32, 31)"))) + +(define_constraint "J" + "A signed 10-bit immediate." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -512, 511)"))) + +(define_constraint "K" + "@internal" + ;; Unused. + (and (match_code "const_int") + (match_test "0"))) + +(define_constraint "L" + "A signed 16-bit immediate." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -32768, 32767)"))) + +(define_constraint "M" + "An unsigned 16-bit immediate." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, 0, 65535)"))) + +(define_constraint "N" + "A signed 12-bit immediate that is negative." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, -2048, -1)"))) + +(define_constraint "O" + "Zero." + (and (match_code "const_int") + (match_test "ival == 0"))) + +(define_constraint "P" + "A signed 12-bit immediate that is positive." + (and (match_code "const_int") + (match_test "IN_RANGE (ival, 1, 2047)"))) + +;; Floating-point constraints. +(define_constraint "G" + "Floating-point zero." + (and (match_code "const_double") + (ior (and (match_test "mode == VOIDmode") + (match_test "hval == 0 && lval == 0")) + (and (match_test "mode == SFmode || mode == DFmode") + (match_test "op == CONST0_RTX (mode)"))))) + +(define_constraint "H" + "@internal" + ;; Unused. + (and (match_code "const_double") + (match_test "0"))) + +(define_constraint "Q" + "12-bit relocations." + (match_test "got12_operand (op, mode)")) + +(define_memory_constraint "R" + "Double word memory ops that take one instruction." + (match_test "dbl_memory_one_insn_operand (op, mode)")) + +(define_constraint "S" + "SYMBOL_REF." + (match_test "CONSTANT_P (op) && call_operand (op, VOIDmode)")) + +(define_memory_constraint "T" + "Double word memory ops that take two instructions." + (match_test "dbl_memory_two_insn_operand (op, mode)")) + +(define_memory_constraint "U" + "Memory operand for conditional execution." + (match_test "condexec_memory_operand (op, mode)")) diff --git a/gcc/config/frv/frv-asm.h b/gcc/config/frv/frv-asm.h new file mode 100644 index 000000000..c36440df8 --- /dev/null +++ b/gcc/config/frv/frv-asm.h @@ -0,0 +1,48 @@ +/* Assembler Support. + Copyright (C) 2000, 2007 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation * either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +/* P(INSN): Emit INSN.P for VLIW machines, otherwise emit plain INSN. + P2(INSN): Emit INSN.P on the FR500 and above, otherwise emit plain INSN. */ +#ifdef __FRV_VLIW__ +#ifdef __STDC__ +#define P(A) A.p +#else +#define P(A) A/**/.p +#endif +#if __FRV_VLIW__ > 2 +#define P2(A) P(A) +#else +#define P2(A) A +#endif +#else +#define P(A) A +#define P2(A) A +#endif + +/* Add underscore if necessary to external name. */ +#ifdef __FRV_UNDERSCORE__ +#ifdef __STDC__ +#define EXT(NAME) _##NAME +#else +#define EXT(NAME) _/**/NAME +#endif +#else +#define EXT(NAME) NAME +#endif diff --git a/gcc/config/frv/frv-modes.def b/gcc/config/frv/frv-modes.def new file mode 100644 index 000000000..ca0094a05 --- /dev/null +++ b/gcc/config/frv/frv-modes.def @@ -0,0 +1,34 @@ +/* Definitions of target machine for GNU compiler for FRV. + Copyright (C) 2002, 2004, 2007 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* On the FRV, the CC modes used are: + + CCmode set ICCs from comparing signed integers + CC_UNSmode set ICCs from comparing unsigned integers + CC_NZmode set ICCs for comparisons that just need the Z and N flags + CC_FPmode set FCCs from comparing floating point + CC_CCRmode set CCRs to do conditional execution */ + +CC_MODE (CC_UNS); +CC_MODE (CC_NZ); +CC_MODE (CC_FP); +CC_MODE (CC_CCR); + +VECTOR_MODE (INT, QI, 4); /* V4QI */ +VECTOR_MODE (INT, SI, 4); /* V4SI */ diff --git a/gcc/config/frv/frv-protos.h b/gcc/config/frv/frv-protos.h new file mode 100644 index 000000000..04a3f9242 --- /dev/null +++ b/gcc/config/frv/frv-protos.h @@ -0,0 +1,195 @@ +/* Frv prototypes. + Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* CPU type. This must be identical to the cpu enumeration in frv.md. */ +typedef enum frv_cpu +{ + FRV_CPU_GENERIC, + FRV_CPU_FR550, + FRV_CPU_FR500, + FRV_CPU_FR450, + FRV_CPU_FR405, + FRV_CPU_FR400, + FRV_CPU_FR300, + FRV_CPU_SIMPLE, + FRV_CPU_TOMCAT +} frv_cpu_t; + +extern frv_cpu_t frv_cpu_type; /* value of -mcpu= */ + +/* Define functions defined in frv.c */ +extern void frv_expand_prologue (void); +extern void frv_expand_epilogue (bool); +extern frv_stack_t *frv_stack_info (void); +extern void frv_debug_stack (frv_stack_t *); +extern int frv_initial_elimination_offset (int, int); + +#ifdef RTX_CODE +extern int frv_legitimate_address_p_1 (enum machine_mode, rtx, + int, int, int); +extern rtx frv_find_base_term (rtx); + +#ifdef TREE_CODE +extern void frv_init_cumulative_args (CUMULATIVE_ARGS *, tree, + rtx, tree, int); + +extern bool frv_function_value_regno_p (const unsigned int); +#endif /* TREE_CODE */ + +extern int frv_expand_block_move (rtx *); +extern int frv_expand_block_clear (rtx *); +extern rtx frv_dynamic_chain_address (rtx); +extern rtx frv_return_addr_rtx (int, rtx); +extern rtx frv_index_memory (rtx, enum machine_mode, int); +extern const char *frv_asm_output_opcode + (FILE *, const char *); +extern void frv_final_prescan_insn (rtx, rtx *, int); +extern void frv_emit_move (enum machine_mode, rtx, rtx); +extern int frv_emit_movsi (rtx, rtx); +extern const char *output_move_single (rtx *, rtx); +extern const char *output_move_double (rtx *, rtx); +extern const char *output_condmove_single + (rtx *, rtx); +extern int frv_emit_cond_branch (rtx *); +extern int frv_emit_scc (rtx *); +extern rtx frv_split_scc (rtx, rtx, rtx, rtx, HOST_WIDE_INT); +extern int frv_emit_cond_move (rtx, rtx, rtx, rtx); +extern rtx frv_split_cond_move (rtx *); +extern rtx frv_split_minmax (rtx *); +extern rtx frv_split_abs (rtx *); +extern void frv_split_double_load (rtx, rtx); +extern void frv_split_double_store (rtx, rtx); +#ifdef BB_HEAD +extern void frv_ifcvt_init_extra_fields (ce_if_block_t *); +extern void frv_ifcvt_modify_tests (ce_if_block_t *, rtx *, rtx *); +extern void frv_ifcvt_modify_multiple_tests + (ce_if_block_t *, basic_block, + rtx *, rtx *); +extern rtx frv_ifcvt_modify_insn (ce_if_block_t *, rtx, rtx); +extern void frv_ifcvt_modify_final (ce_if_block_t *); +extern void frv_ifcvt_modify_cancel (ce_if_block_t *); +#endif +extern enum reg_class frv_secondary_reload_class + (enum reg_class, + enum machine_mode, rtx); +extern int frv_hard_regno_mode_ok (int, enum machine_mode); +extern int frv_hard_regno_nregs (int, enum machine_mode); +extern int frv_class_max_nregs (enum reg_class rclass, + enum machine_mode mode); +extern int frv_legitimate_constant_p (rtx); +extern enum machine_mode frv_select_cc_mode (enum rtx_code, rtx, rtx); +#endif /* RTX_CODE */ + +extern int frv_trampoline_size (void); +extern int direct_return_p (void); +extern int frv_issue_rate (void); +extern int frv_acc_group (rtx); + +#ifdef TREE_CODE +extern int frv_adjust_field_align (tree, int); +#endif + +#ifdef RTX_CODE +extern int integer_register_operand (rtx, enum machine_mode); +extern int frv_load_operand (rtx, enum machine_mode); +extern int gpr_or_fpr_operand (rtx, enum machine_mode); +extern int gpr_no_subreg_operand (rtx, enum machine_mode); +extern int gpr_or_int6_operand (rtx, enum machine_mode); +extern int fpr_or_int6_operand (rtx, enum machine_mode); +extern int gpr_or_int_operand (rtx, enum machine_mode); +extern int gpr_or_int12_operand (rtx, enum machine_mode); +extern int gpr_fpr_or_int12_operand (rtx, enum machine_mode); +extern int gpr_or_int10_operand (rtx, enum machine_mode); +extern int move_source_operand (rtx, enum machine_mode); +extern int move_destination_operand (rtx, enum machine_mode); +extern int condexec_source_operand (rtx, enum machine_mode); +extern int condexec_dest_operand (rtx, enum machine_mode); +extern int lr_operand (rtx, enum machine_mode); +extern int gpr_or_memory_operand (rtx, enum machine_mode); +extern int fpr_or_memory_operand (rtx, enum machine_mode); +extern int reg_or_0_operand (rtx, enum machine_mode); +extern int fcc_operand (rtx, enum machine_mode); +extern int icc_operand (rtx, enum machine_mode); +extern int cc_operand (rtx, enum machine_mode); +extern int fcr_operand (rtx, enum machine_mode); +extern int icr_operand (rtx, enum machine_mode); +extern int cr_operand (rtx, enum machine_mode); +extern int call_operand (rtx, enum machine_mode); +extern int fpr_operand (rtx, enum machine_mode); +extern int even_reg_operand (rtx, enum machine_mode); +extern int odd_reg_operand (rtx, enum machine_mode); +extern int even_gpr_operand (rtx, enum machine_mode); +extern int odd_gpr_operand (rtx, enum machine_mode); +extern int quad_fpr_operand (rtx, enum machine_mode); +extern int even_fpr_operand (rtx, enum machine_mode); +extern int odd_fpr_operand (rtx, enum machine_mode); +extern int dbl_memory_one_insn_operand (rtx, enum machine_mode); +extern int dbl_memory_two_insn_operand (rtx, enum machine_mode); +extern int int12_operand (rtx, enum machine_mode); +extern int int6_operand (rtx, enum machine_mode); +extern int int5_operand (rtx, enum machine_mode); +extern int uint5_operand (rtx, enum machine_mode); +extern int uint4_operand (rtx, enum machine_mode); +extern int uint1_operand (rtx, enum machine_mode); +extern int int_2word_operand (rtx, enum machine_mode); +extern int pic_register_operand (rtx, enum machine_mode); +extern int pic_symbolic_operand (rtx, enum machine_mode); +extern int small_data_register_operand (rtx, enum machine_mode); +extern int small_data_symbolic_operand (rtx, enum machine_mode); +extern int upper_int16_operand (rtx, enum machine_mode); +extern int uint16_operand (rtx, enum machine_mode); +extern int symbolic_operand (rtx, enum machine_mode); +extern int relational_operator (rtx, enum machine_mode); +extern int signed_relational_operator (rtx, enum machine_mode); +extern int unsigned_relational_operator (rtx, enum machine_mode); +extern int float_relational_operator (rtx, enum machine_mode); +extern int ccr_eqne_operator (rtx, enum machine_mode); +extern int minmax_operator (rtx, enum machine_mode); +extern int condexec_si_binary_operator (rtx, enum machine_mode); +extern int condexec_si_media_operator (rtx, enum machine_mode); +extern int condexec_si_divide_operator (rtx, enum machine_mode); +extern int condexec_si_unary_operator (rtx, enum machine_mode); +extern int condexec_sf_conv_operator (rtx, enum machine_mode); +extern int condexec_sf_add_operator (rtx, enum machine_mode); +extern int condexec_memory_operand (rtx, enum machine_mode); +extern int intop_compare_operator (rtx, enum machine_mode); +extern int acc_operand (rtx, enum machine_mode); +extern int even_acc_operand (rtx, enum machine_mode); +extern int quad_acc_operand (rtx, enum machine_mode); +extern int accg_operand (rtx, enum machine_mode); +extern rtx frv_matching_accg_for_acc (rtx); +extern void frv_expand_fdpic_call (rtx *, bool, bool); +extern rtx frv_gen_GPsym2reg (rtx, rtx); +extern int frv_legitimate_memory_operand (rtx, enum machine_mode, int); + +/* Information about a relocation unspec. SYMBOL is the relocation symbol + (a SYMBOL_REF or LABEL_REF), RELOC is the type of relocation and OFFSET + is the constant addend. */ +struct frv_unspec { + rtx symbol; + int reloc; + HOST_WIDE_INT offset; +}; + +extern bool frv_const_unspec_p (rtx, struct frv_unspec *); + +#endif + diff --git a/gcc/config/frv/frv.c b/gcc/config/frv/frv.c new file mode 100644 index 000000000..229b9feb7 --- /dev/null +++ b/gcc/config/frv/frv.c @@ -0,0 +1,9680 @@ +/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, + 2008, 2009, 2010 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "recog.h" +#include "reload.h" +#include "expr.h" +#include "obstack.h" +#include "except.h" +#include "function.h" +#include "optabs.h" +#include "diagnostic-core.h" +#include "basic-block.h" +#include "tm_p.h" +#include "ggc.h" +#include "target.h" +#include "target-def.h" +#include "targhooks.h" +#include "integrate.h" +#include "langhooks.h" +#include "df.h" + +#ifndef FRV_INLINE +#define FRV_INLINE inline +#endif + +/* The maximum number of distinct NOP patterns. There are three: + nop, fnop and mnop. */ +#define NUM_NOP_PATTERNS 3 + +/* Classification of instructions and units: integer, floating-point/media, + branch and control. */ +enum frv_insn_group { GROUP_I, GROUP_FM, GROUP_B, GROUP_C, NUM_GROUPS }; + +/* The DFA names of the units, in packet order. */ +static const char *const frv_unit_names[] = +{ + "c", + "i0", "f0", + "i1", "f1", + "i2", "f2", + "i3", "f3", + "b0", "b1" +}; + +/* The classification of each unit in frv_unit_names[]. */ +static const enum frv_insn_group frv_unit_groups[ARRAY_SIZE (frv_unit_names)] = +{ + GROUP_C, + GROUP_I, GROUP_FM, + GROUP_I, GROUP_FM, + GROUP_I, GROUP_FM, + GROUP_I, GROUP_FM, + GROUP_B, GROUP_B +}; + +/* Return the DFA unit code associated with the Nth unit of integer + or floating-point group GROUP, */ +#define NTH_UNIT(GROUP, N) frv_unit_codes[(GROUP) + (N) * 2 + 1] + +/* Return the number of integer or floating-point unit UNIT + (1 for I1, 2 for F2, etc.). */ +#define UNIT_NUMBER(UNIT) (((UNIT) - 1) / 2) + +/* The DFA unit number for each unit in frv_unit_names[]. */ +static int frv_unit_codes[ARRAY_SIZE (frv_unit_names)]; + +/* FRV_TYPE_TO_UNIT[T] is the last unit in frv_unit_names[] that can issue + an instruction of type T. The value is ARRAY_SIZE (frv_unit_names) if + no instruction of type T has been seen. */ +static unsigned int frv_type_to_unit[TYPE_UNKNOWN + 1]; + +/* An array of dummy nop INSNs, one for each type of nop that the + target supports. */ +static GTY(()) rtx frv_nops[NUM_NOP_PATTERNS]; + +/* The number of nop instructions in frv_nops[]. */ +static unsigned int frv_num_nops; + + /* The type of access. FRV_IO_UNKNOWN means the access can be either + a read or a write. */ +enum frv_io_type { FRV_IO_UNKNOWN, FRV_IO_READ, FRV_IO_WRITE }; + +/* Information about one __builtin_read or __builtin_write access, or + the combination of several such accesses. The most general value + is all-zeros (an unknown access to an unknown address). */ +struct frv_io { + enum frv_io_type type; + + /* The constant address being accessed, or zero if not known. */ + HOST_WIDE_INT const_address; + + /* The run-time address, as used in operand 0 of the membar pattern. */ + rtx var_address; +}; + +/* Return true if instruction INSN should be packed with the following + instruction. */ +#define PACKING_FLAG_P(INSN) (GET_MODE (INSN) == TImode) + +/* Set the value of PACKING_FLAG_P(INSN). */ +#define SET_PACKING_FLAG(INSN) PUT_MODE (INSN, TImode) +#define CLEAR_PACKING_FLAG(INSN) PUT_MODE (INSN, VOIDmode) + +/* Loop with REG set to each hard register in rtx X. */ +#define FOR_EACH_REGNO(REG, X) \ + for (REG = REGNO (X); \ + REG < REGNO (X) + HARD_REGNO_NREGS (REGNO (X), GET_MODE (X)); \ + REG++) + +/* This structure contains machine specific function data. */ +struct GTY(()) machine_function +{ + /* True if we have created an rtx that relies on the stack frame. */ + int frame_needed; + + /* True if this function contains at least one __builtin_{read,write}*. */ + bool has_membar_p; +}; + +/* Temporary register allocation support structure. */ +typedef struct frv_tmp_reg_struct + { + HARD_REG_SET regs; /* possible registers to allocate */ + int next_reg[N_REG_CLASSES]; /* next register to allocate per class */ + } +frv_tmp_reg_t; + +/* Register state information for VLIW re-packing phase. */ +#define REGSTATE_CC_MASK 0x07 /* Mask to isolate CCn for cond exec */ +#define REGSTATE_MODIFIED 0x08 /* reg modified in current VLIW insn */ +#define REGSTATE_IF_TRUE 0x10 /* reg modified in cond exec true */ +#define REGSTATE_IF_FALSE 0x20 /* reg modified in cond exec false */ + +#define REGSTATE_IF_EITHER (REGSTATE_IF_TRUE | REGSTATE_IF_FALSE) + +typedef unsigned char regstate_t; + +/* Used in frv_frame_accessor_t to indicate the direction of a register-to- + memory move. */ +enum frv_stack_op +{ + FRV_LOAD, + FRV_STORE +}; + +/* Information required by frv_frame_access. */ +typedef struct +{ + /* This field is FRV_LOAD if registers are to be loaded from the stack and + FRV_STORE if they should be stored onto the stack. FRV_STORE implies + the move is being done by the prologue code while FRV_LOAD implies it + is being done by the epilogue. */ + enum frv_stack_op op; + + /* The base register to use when accessing the stack. This may be the + frame pointer, stack pointer, or a temporary. The choice of register + depends on which part of the frame is being accessed and how big the + frame is. */ + rtx base; + + /* The offset of BASE from the bottom of the current frame, in bytes. */ + int base_offset; +} frv_frame_accessor_t; + +/* Conditional execution support gathered together in one structure. */ +typedef struct + { + /* Linked list of insns to add if the conditional execution conversion was + successful. Each link points to an EXPR_LIST which points to the pattern + of the insn to add, and the insn to be inserted before. */ + rtx added_insns_list; + + /* Identify which registers are safe to allocate for if conversions to + conditional execution. We keep the last allocated register in the + register classes between COND_EXEC statements. This will mean we allocate + different registers for each different COND_EXEC group if we can. This + might allow the scheduler to intermix two different COND_EXEC sections. */ + frv_tmp_reg_t tmp_reg; + + /* For nested IFs, identify which CC registers are used outside of setting + via a compare isnsn, and using via a check insn. This will allow us to + know if we can rewrite the register to use a different register that will + be paired with the CR register controlling the nested IF-THEN blocks. */ + HARD_REG_SET nested_cc_ok_rewrite; + + /* Temporary registers allocated to hold constants during conditional + execution. */ + rtx scratch_regs[FIRST_PSEUDO_REGISTER]; + + /* Current number of temp registers available. */ + int cur_scratch_regs; + + /* Number of nested conditional execution blocks. */ + int num_nested_cond_exec; + + /* Map of insns that set up constants in scratch registers. */ + bitmap scratch_insns_bitmap; + + /* Conditional execution test register (CC0..CC7). */ + rtx cr_reg; + + /* Conditional execution compare register that is paired with cr_reg, so that + nested compares can be done. The csubcc and caddcc instructions don't + have enough bits to specify both a CC register to be set and a CR register + to do the test on, so the same bit number is used for both. Needless to + say, this is rather inconvenient for GCC. */ + rtx nested_cc_reg; + + /* Extra CR registers used for &&, ||. */ + rtx extra_int_cr; + rtx extra_fp_cr; + + /* Previous CR used in nested if, to make sure we are dealing with the same + nested if as the previous statement. */ + rtx last_nested_if_cr; + } +frv_ifcvt_t; + +static /* GTY(()) */ frv_ifcvt_t frv_ifcvt; + +/* Map register number to smallest register class. */ +enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER]; + +/* Cached value of frv_stack_info. */ +static frv_stack_t *frv_stack_cache = (frv_stack_t *)0; + +/* -mcpu= support */ +frv_cpu_t frv_cpu_type = CPU_TYPE; /* value of -mcpu= */ + +/* Forward references */ + +static bool frv_handle_option (size_t, const char *, int); +static void frv_option_override (void); +static bool frv_legitimate_address_p (enum machine_mode, rtx, bool); +static int frv_default_flags_for_cpu (void); +static int frv_string_begins_with (const_tree, const char *); +static FRV_INLINE bool frv_small_data_reloc_p (rtx, int); +static void frv_print_operand (FILE *, rtx, int); +static void frv_print_operand_address (FILE *, rtx); +static bool frv_print_operand_punct_valid_p (unsigned char code); +static void frv_print_operand_memory_reference_reg + (FILE *, rtx); +static void frv_print_operand_memory_reference (FILE *, rtx, int); +static int frv_print_operand_jump_hint (rtx); +static const char *comparison_string (enum rtx_code, rtx); +static rtx frv_function_value (const_tree, const_tree, + bool); +static rtx frv_libcall_value (enum machine_mode, + const_rtx); +static FRV_INLINE int frv_regno_ok_for_base_p (int, int); +static rtx single_set_pattern (rtx); +static int frv_function_contains_far_jump (void); +static rtx frv_alloc_temp_reg (frv_tmp_reg_t *, + enum reg_class, + enum machine_mode, + int, int); +static rtx frv_frame_offset_rtx (int); +static rtx frv_frame_mem (enum machine_mode, rtx, int); +static rtx frv_dwarf_store (rtx, int); +static void frv_frame_insn (rtx, rtx); +static void frv_frame_access (frv_frame_accessor_t*, + rtx, int); +static void frv_frame_access_multi (frv_frame_accessor_t*, + frv_stack_t *, int); +static void frv_frame_access_standard_regs (enum frv_stack_op, + frv_stack_t *); +static struct machine_function *frv_init_machine_status (void); +static rtx frv_int_to_acc (enum insn_code, int, rtx); +static enum machine_mode frv_matching_accg_mode (enum machine_mode); +static rtx frv_read_argument (tree, unsigned int); +static rtx frv_read_iacc_argument (enum machine_mode, tree, unsigned int); +static int frv_check_constant_argument (enum insn_code, int, rtx); +static rtx frv_legitimize_target (enum insn_code, rtx); +static rtx frv_legitimize_argument (enum insn_code, int, rtx); +static rtx frv_legitimize_tls_address (rtx, enum tls_model); +static rtx frv_legitimize_address (rtx, rtx, enum machine_mode); +static rtx frv_expand_set_builtin (enum insn_code, tree, rtx); +static rtx frv_expand_unop_builtin (enum insn_code, tree, rtx); +static rtx frv_expand_binop_builtin (enum insn_code, tree, rtx); +static rtx frv_expand_cut_builtin (enum insn_code, tree, rtx); +static rtx frv_expand_binopimm_builtin (enum insn_code, tree, rtx); +static rtx frv_expand_voidbinop_builtin (enum insn_code, tree); +static rtx frv_expand_int_void2arg (enum insn_code, tree); +static rtx frv_expand_prefetches (enum insn_code, tree); +static rtx frv_expand_voidtriop_builtin (enum insn_code, tree); +static rtx frv_expand_voidaccop_builtin (enum insn_code, tree); +static rtx frv_expand_mclracc_builtin (tree); +static rtx frv_expand_mrdacc_builtin (enum insn_code, tree); +static rtx frv_expand_mwtacc_builtin (enum insn_code, tree); +static rtx frv_expand_noargs_builtin (enum insn_code); +static void frv_split_iacc_move (rtx, rtx); +static rtx frv_emit_comparison (enum rtx_code, rtx, rtx); +static int frv_clear_registers_used (rtx *, void *); +static void frv_ifcvt_add_insn (rtx, rtx, int); +static rtx frv_ifcvt_rewrite_mem (rtx, enum machine_mode, rtx); +static rtx frv_ifcvt_load_value (rtx, rtx); +static int frv_acc_group_1 (rtx *, void *); +static unsigned int frv_insn_unit (rtx); +static bool frv_issues_to_branch_unit_p (rtx); +static int frv_cond_flags (rtx); +static bool frv_regstate_conflict_p (regstate_t, regstate_t); +static int frv_registers_conflict_p_1 (rtx *, void *); +static bool frv_registers_conflict_p (rtx); +static void frv_registers_update_1 (rtx, const_rtx, void *); +static void frv_registers_update (rtx); +static void frv_start_packet (void); +static void frv_start_packet_block (void); +static void frv_finish_packet (void (*) (void)); +static bool frv_pack_insn_p (rtx); +static void frv_add_insn_to_packet (rtx); +static void frv_insert_nop_in_packet (rtx); +static bool frv_for_each_packet (void (*) (void)); +static bool frv_sort_insn_group_1 (enum frv_insn_group, + unsigned int, unsigned int, + unsigned int, unsigned int, + state_t); +static int frv_compare_insns (const void *, const void *); +static void frv_sort_insn_group (enum frv_insn_group); +static void frv_reorder_packet (void); +static void frv_fill_unused_units (enum frv_insn_group); +static void frv_align_label (void); +static void frv_reorg_packet (void); +static void frv_register_nop (rtx); +static void frv_reorg (void); +static void frv_pack_insns (void); +static void frv_function_prologue (FILE *, HOST_WIDE_INT); +static void frv_function_epilogue (FILE *, HOST_WIDE_INT); +static bool frv_assemble_integer (rtx, unsigned, int); +static void frv_init_builtins (void); +static rtx frv_expand_builtin (tree, rtx, rtx, enum machine_mode, int); +static void frv_init_libfuncs (void); +static bool frv_in_small_data_p (const_tree); +static void frv_asm_output_mi_thunk + (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); +static void frv_setup_incoming_varargs (CUMULATIVE_ARGS *, + enum machine_mode, + tree, int *, int); +static rtx frv_expand_builtin_saveregs (void); +static void frv_expand_builtin_va_start (tree, rtx); +static bool frv_rtx_costs (rtx, int, int, int*, bool); +static int frv_register_move_cost (enum machine_mode, + reg_class_t, reg_class_t); +static int frv_memory_move_cost (enum machine_mode, + reg_class_t, bool); +static void frv_asm_out_constructor (rtx, int); +static void frv_asm_out_destructor (rtx, int); +static bool frv_function_symbol_referenced_p (rtx); +static bool frv_cannot_force_const_mem (rtx); +static const char *unspec_got_name (int); +static void frv_output_const_unspec (FILE *, + const struct frv_unspec *); +static bool frv_function_ok_for_sibcall (tree, tree); +static rtx frv_struct_value_rtx (tree, int); +static bool frv_must_pass_in_stack (enum machine_mode mode, const_tree type); +static int frv_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); +static rtx frv_function_arg (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static rtx frv_function_incoming_arg (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static void frv_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static unsigned int frv_function_arg_boundary (enum machine_mode, + const_tree); +static void frv_output_dwarf_dtprel (FILE *, int, rtx) + ATTRIBUTE_UNUSED; +static reg_class_t frv_secondary_reload (bool, rtx, reg_class_t, + enum machine_mode, + secondary_reload_info *); +static bool frv_frame_pointer_required (void); +static bool frv_can_eliminate (const int, const int); +static void frv_conditional_register_usage (void); +static void frv_trampoline_init (rtx, tree, rtx); +static bool frv_class_likely_spilled_p (reg_class_t); + +/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */ +static const struct default_options frv_option_optimization_table[] = + { + { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 }, + { OPT_LEVELS_NONE, 0, NULL, 0 } + }; + +/* Allow us to easily change the default for -malloc-cc. */ +#ifndef DEFAULT_NO_ALLOC_CC +#define MASK_DEFAULT_ALLOC_CC MASK_ALLOC_CC +#else +#define MASK_DEFAULT_ALLOC_CC 0 +#endif + +/* Initialize the GCC target structure. */ +#undef TARGET_PRINT_OPERAND +#define TARGET_PRINT_OPERAND frv_print_operand +#undef TARGET_PRINT_OPERAND_ADDRESS +#define TARGET_PRINT_OPERAND_ADDRESS frv_print_operand_address +#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P +#define TARGET_PRINT_OPERAND_PUNCT_VALID_P frv_print_operand_punct_valid_p +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE frv_function_prologue +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE frv_function_epilogue +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER frv_assemble_integer +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS \ + (MASK_DEFAULT_ALLOC_CC \ + | MASK_COND_MOVE \ + | MASK_SCC \ + | MASK_COND_EXEC \ + | MASK_VLIW_BRANCH \ + | MASK_MULTI_CE \ + | MASK_NESTED_CE) +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION frv_handle_option +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE frv_option_override +#undef TARGET_OPTION_OPTIMIZATION_TABLE +#define TARGET_OPTION_OPTIMIZATION_TABLE frv_option_optimization_table +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS frv_init_builtins +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN frv_expand_builtin +#undef TARGET_INIT_LIBFUNCS +#define TARGET_INIT_LIBFUNCS frv_init_libfuncs +#undef TARGET_IN_SMALL_DATA_P +#define TARGET_IN_SMALL_DATA_P frv_in_small_data_p +#undef TARGET_REGISTER_MOVE_COST +#define TARGET_REGISTER_MOVE_COST frv_register_move_cost +#undef TARGET_MEMORY_MOVE_COST +#define TARGET_MEMORY_MOVE_COST frv_memory_move_cost +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS frv_rtx_costs +#undef TARGET_ASM_CONSTRUCTOR +#define TARGET_ASM_CONSTRUCTOR frv_asm_out_constructor +#undef TARGET_ASM_DESTRUCTOR +#define TARGET_ASM_DESTRUCTOR frv_asm_out_destructor + +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK frv_asm_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall + +#undef TARGET_SCHED_ISSUE_RATE +#define TARGET_SCHED_ISSUE_RATE frv_issue_rate + +#undef TARGET_LEGITIMIZE_ADDRESS +#define TARGET_LEGITIMIZE_ADDRESS frv_legitimize_address + +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL frv_function_ok_for_sibcall +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM frv_cannot_force_const_mem + +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS HAVE_AS_TLS + +#undef TARGET_STRUCT_VALUE_RTX +#define TARGET_STRUCT_VALUE_RTX frv_struct_value_rtx +#undef TARGET_MUST_PASS_IN_STACK +#define TARGET_MUST_PASS_IN_STACK frv_must_pass_in_stack +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES frv_arg_partial_bytes +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG frv_function_arg +#undef TARGET_FUNCTION_INCOMING_ARG +#define TARGET_FUNCTION_INCOMING_ARG frv_function_incoming_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE frv_function_arg_advance +#undef TARGET_FUNCTION_ARG_BOUNDARY +#define TARGET_FUNCTION_ARG_BOUNDARY frv_function_arg_boundary + +#undef TARGET_EXPAND_BUILTIN_SAVEREGS +#define TARGET_EXPAND_BUILTIN_SAVEREGS frv_expand_builtin_saveregs +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS frv_setup_incoming_varargs +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG frv_reorg + +#undef TARGET_EXPAND_BUILTIN_VA_START +#define TARGET_EXPAND_BUILTIN_VA_START frv_expand_builtin_va_start + +#if HAVE_AS_TLS +#undef TARGET_ASM_OUTPUT_DWARF_DTPREL +#define TARGET_ASM_OUTPUT_DWARF_DTPREL frv_output_dwarf_dtprel +#endif + +#undef TARGET_CLASS_LIKELY_SPILLED_P +#define TARGET_CLASS_LIKELY_SPILLED_P frv_class_likely_spilled_p + +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD frv_secondary_reload + +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P frv_legitimate_address_p + +#undef TARGET_FRAME_POINTER_REQUIRED +#define TARGET_FRAME_POINTER_REQUIRED frv_frame_pointer_required + +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE frv_can_eliminate + +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE frv_conditional_register_usage + +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT frv_trampoline_init + +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE frv_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE frv_libcall_value + +struct gcc_target targetm = TARGET_INITIALIZER; + +#define FRV_SYMBOL_REF_TLS_P(RTX) \ + (GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (RTX) != 0) + + +/* Any function call that satisfies the machine-independent + requirements is eligible on FR-V. */ + +static bool +frv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, + tree exp ATTRIBUTE_UNUSED) +{ + return true; +} + +/* Return true if SYMBOL is a small data symbol and relocation RELOC + can be used to access it directly in a load or store. */ + +static FRV_INLINE bool +frv_small_data_reloc_p (rtx symbol, int reloc) +{ + return (GET_CODE (symbol) == SYMBOL_REF + && SYMBOL_REF_SMALL_P (symbol) + && (!TARGET_FDPIC || flag_pic == 1) + && (reloc == R_FRV_GOTOFF12 || reloc == R_FRV_GPREL12)); +} + +/* Return true if X is a valid relocation unspec. If it is, fill in UNSPEC + appropriately. */ + +bool +frv_const_unspec_p (rtx x, struct frv_unspec *unspec) +{ + if (GET_CODE (x) == CONST) + { + unspec->offset = 0; + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + unspec->offset += INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + } + if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_GOT) + { + unspec->symbol = XVECEXP (x, 0, 0); + unspec->reloc = INTVAL (XVECEXP (x, 0, 1)); + + if (unspec->offset == 0) + return true; + + if (frv_small_data_reloc_p (unspec->symbol, unspec->reloc) + && unspec->offset > 0 + && unspec->offset < g_switch_value) + return true; + } + } + return false; +} + +/* Decide whether we can force certain constants to memory. If we + decide we can't, the caller should be able to cope with it in + another way. + + We never allow constants to be forced into memory for TARGET_FDPIC. + This is necessary for several reasons: + + 1. Since LEGITIMATE_CONSTANT_P rejects constant pool addresses, the + target-independent code will try to force them into the constant + pool, thus leading to infinite recursion. + + 2. We can never introduce new constant pool references during reload. + Any such reference would require use of the pseudo FDPIC register. + + 3. We can't represent a constant added to a function pointer (which is + not the same as a pointer to a function+constant). + + 4. In many cases, it's more efficient to calculate the constant in-line. */ + +static bool +frv_cannot_force_const_mem (rtx x ATTRIBUTE_UNUSED) +{ + return TARGET_FDPIC; +} + +/* Implement TARGET_HANDLE_OPTION. */ + +static bool +frv_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) +{ + switch (code) + { + case OPT_mcpu_: + if (strcmp (arg, "simple") == 0) + frv_cpu_type = FRV_CPU_SIMPLE; + else if (strcmp (arg, "tomcat") == 0) + frv_cpu_type = FRV_CPU_TOMCAT; + else if (strcmp (arg, "fr550") == 0) + frv_cpu_type = FRV_CPU_FR550; + else if (strcmp (arg, "fr500") == 0) + frv_cpu_type = FRV_CPU_FR500; + else if (strcmp (arg, "fr450") == 0) + frv_cpu_type = FRV_CPU_FR450; + else if (strcmp (arg, "fr405") == 0) + frv_cpu_type = FRV_CPU_FR405; + else if (strcmp (arg, "fr400") == 0) + frv_cpu_type = FRV_CPU_FR400; + else if (strcmp (arg, "fr300") == 0) + frv_cpu_type = FRV_CPU_FR300; + else if (strcmp (arg, "frv") == 0) + frv_cpu_type = FRV_CPU_GENERIC; + else + return false; + return true; + + default: + return true; + } +} + +static int +frv_default_flags_for_cpu (void) +{ + switch (frv_cpu_type) + { + case FRV_CPU_GENERIC: + return MASK_DEFAULT_FRV; + + case FRV_CPU_FR550: + return MASK_DEFAULT_FR550; + + case FRV_CPU_FR500: + case FRV_CPU_TOMCAT: + return MASK_DEFAULT_FR500; + + case FRV_CPU_FR450: + return MASK_DEFAULT_FR450; + + case FRV_CPU_FR405: + case FRV_CPU_FR400: + return MASK_DEFAULT_FR400; + + case FRV_CPU_FR300: + case FRV_CPU_SIMPLE: + return MASK_DEFAULT_SIMPLE; + + default: + gcc_unreachable (); + } +} + +/* Implement TARGET_OPTION_OVERRIDE. */ + +static void +frv_option_override (void) +{ + int regno; + unsigned int i; + + target_flags |= (frv_default_flags_for_cpu () & ~target_flags_explicit); + + /* -mlibrary-pic sets -fPIC and -G0 and also suppresses warnings from the + linker about linking pic and non-pic code. */ + if (TARGET_LIBPIC) + { + if (!flag_pic) /* -fPIC */ + flag_pic = 2; + + if (!global_options_set.x_g_switch_value) /* -G0 */ + { + g_switch_value = 0; + } + } + + /* A C expression whose value is a register class containing hard + register REGNO. In general there is more than one such class; + choose a class which is "minimal", meaning that no smaller class + also contains the register. */ + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + enum reg_class rclass; + + if (GPR_P (regno)) + { + int gpr_reg = regno - GPR_FIRST; + + if (gpr_reg == GR8_REG) + rclass = GR8_REGS; + + else if (gpr_reg == GR9_REG) + rclass = GR9_REGS; + + else if (gpr_reg == GR14_REG) + rclass = FDPIC_FPTR_REGS; + + else if (gpr_reg == FDPIC_REGNO) + rclass = FDPIC_REGS; + + else if ((gpr_reg & 3) == 0) + rclass = QUAD_REGS; + + else if ((gpr_reg & 1) == 0) + rclass = EVEN_REGS; + + else + rclass = GPR_REGS; + } + + else if (FPR_P (regno)) + { + int fpr_reg = regno - GPR_FIRST; + if ((fpr_reg & 3) == 0) + rclass = QUAD_FPR_REGS; + + else if ((fpr_reg & 1) == 0) + rclass = FEVEN_REGS; + + else + rclass = FPR_REGS; + } + + else if (regno == LR_REGNO) + rclass = LR_REG; + + else if (regno == LCR_REGNO) + rclass = LCR_REG; + + else if (ICC_P (regno)) + rclass = ICC_REGS; + + else if (FCC_P (regno)) + rclass = FCC_REGS; + + else if (ICR_P (regno)) + rclass = ICR_REGS; + + else if (FCR_P (regno)) + rclass = FCR_REGS; + + else if (ACC_P (regno)) + { + int r = regno - ACC_FIRST; + if ((r & 3) == 0) + rclass = QUAD_ACC_REGS; + else if ((r & 1) == 0) + rclass = EVEN_ACC_REGS; + else + rclass = ACC_REGS; + } + + else if (ACCG_P (regno)) + rclass = ACCG_REGS; + + else + rclass = NO_REGS; + + regno_reg_class[regno] = rclass; + } + + /* Check for small data option */ + if (!global_options_set.x_g_switch_value && !TARGET_LIBPIC) + g_switch_value = SDATA_DEFAULT_SIZE; + + /* There is no single unaligned SI op for PIC code. Sometimes we + need to use ".4byte" and sometimes we need to use ".picptr". + See frv_assemble_integer for details. */ + if (flag_pic || TARGET_FDPIC) + targetm.asm_out.unaligned_op.si = 0; + + if ((target_flags_explicit & MASK_LINKED_FP) == 0) + target_flags |= MASK_LINKED_FP; + + if ((target_flags_explicit & MASK_OPTIMIZE_MEMBAR) == 0) + target_flags |= MASK_OPTIMIZE_MEMBAR; + + for (i = 0; i < ARRAY_SIZE (frv_unit_names); i++) + frv_unit_codes[i] = get_cpu_unit_code (frv_unit_names[i]); + + for (i = 0; i < ARRAY_SIZE (frv_type_to_unit); i++) + frv_type_to_unit[i] = ARRAY_SIZE (frv_unit_codes); + + init_machine_status = frv_init_machine_status; +} + + +/* Return true if NAME (a STRING_CST node) begins with PREFIX. */ + +static int +frv_string_begins_with (const_tree name, const char *prefix) +{ + const int prefix_len = strlen (prefix); + + /* Remember: NAME's length includes the null terminator. */ + return (TREE_STRING_LENGTH (name) > prefix_len + && strncmp (TREE_STRING_POINTER (name), prefix, prefix_len) == 0); +} + +/* Zero or more C statements that may conditionally modify two variables + `fixed_regs' and `call_used_regs' (both of type `char []') after they have + been initialized from the two preceding macros. + + This is necessary in case the fixed or call-clobbered registers depend on + target flags. + + You need not define this macro if it has no work to do. + + If the usage of an entire class of registers depends on the target flags, + you may indicate this to GCC by using this macro to modify `fixed_regs' and + `call_used_regs' to 1 for each of the registers in the classes which should + not be used by GCC. Also define the macro `REG_CLASS_FROM_LETTER' to return + `NO_REGS' if it is called with a letter for a class that shouldn't be used. + + (However, if this class is not included in `GENERAL_REGS' and all of the + insn patterns whose constraints permit this class are controlled by target + switches, then GCC will automatically avoid using these registers when the + target switches are opposed to them.) */ + +static void +frv_conditional_register_usage (void) +{ + int i; + + for (i = GPR_FIRST + NUM_GPRS; i <= GPR_LAST; i++) + fixed_regs[i] = call_used_regs[i] = 1; + + for (i = FPR_FIRST + NUM_FPRS; i <= FPR_LAST; i++) + fixed_regs[i] = call_used_regs[i] = 1; + + /* Reserve the registers used for conditional execution. At present, we need + 1 ICC and 1 ICR register. */ + fixed_regs[ICC_TEMP] = call_used_regs[ICC_TEMP] = 1; + fixed_regs[ICR_TEMP] = call_used_regs[ICR_TEMP] = 1; + + if (TARGET_FIXED_CC) + { + fixed_regs[ICC_FIRST] = call_used_regs[ICC_FIRST] = 1; + fixed_regs[FCC_FIRST] = call_used_regs[FCC_FIRST] = 1; + fixed_regs[ICR_FIRST] = call_used_regs[ICR_FIRST] = 1; + fixed_regs[FCR_FIRST] = call_used_regs[FCR_FIRST] = 1; + } + + if (TARGET_FDPIC) + fixed_regs[GPR_FIRST + 16] = fixed_regs[GPR_FIRST + 17] = + call_used_regs[GPR_FIRST + 16] = call_used_regs[GPR_FIRST + 17] = 0; + +#if 0 + /* If -fpic, SDA_BASE_REG is the PIC register. */ + if (g_switch_value == 0 && !flag_pic) + fixed_regs[SDA_BASE_REG] = call_used_regs[SDA_BASE_REG] = 0; + + if (!flag_pic) + fixed_regs[PIC_REGNO] = call_used_regs[PIC_REGNO] = 0; +#endif +} + + +/* + * Compute the stack frame layout + * + * Register setup: + * +---------------+-----------------------+-----------------------+ + * |Register |type |caller-save/callee-save| + * +---------------+-----------------------+-----------------------+ + * |GR0 |Zero register | - | + * |GR1 |Stack pointer(SP) | - | + * |GR2 |Frame pointer(FP) | - | + * |GR3 |Hidden parameter | caller save | + * |GR4-GR7 | - | caller save | + * |GR8-GR13 |Argument register | caller save | + * |GR14-GR15 | - | caller save | + * |GR16-GR31 | - | callee save | + * |GR32-GR47 | - | caller save | + * |GR48-GR63 | - | callee save | + * |FR0-FR15 | - | caller save | + * |FR16-FR31 | - | callee save | + * |FR32-FR47 | - | caller save | + * |FR48-FR63 | - | callee save | + * +---------------+-----------------------+-----------------------+ + * + * Stack frame setup: + * Low + * SP-> |-----------------------------------| + * | Argument area | + * |-----------------------------------| + * | Register save area | + * |-----------------------------------| + * | Local variable save area | + * FP-> |-----------------------------------| + * | Old FP | + * |-----------------------------------| + * | Hidden parameter save area | + * |-----------------------------------| + * | Return address(LR) storage area | + * |-----------------------------------| + * | Padding for alignment | + * |-----------------------------------| + * | Register argument area | + * OLD SP-> |-----------------------------------| + * | Parameter area | + * |-----------------------------------| + * High + * + * Argument area/Parameter area: + * + * When a function is called, this area is used for argument transfer. When + * the argument is set up by the caller function, this area is referred to as + * the argument area. When the argument is referenced by the callee function, + * this area is referred to as the parameter area. The area is allocated when + * all arguments cannot be placed on the argument register at the time of + * argument transfer. + * + * Register save area: + * + * This is a register save area that must be guaranteed for the caller + * function. This area is not secured when the register save operation is not + * needed. + * + * Local variable save area: + * + * This is the area for local variables and temporary variables. + * + * Old FP: + * + * This area stores the FP value of the caller function. + * + * Hidden parameter save area: + * + * This area stores the start address of the return value storage + * area for a struct/union return function. + * When a struct/union is used as the return value, the caller + * function stores the return value storage area start address in + * register GR3 and passes it to the caller function. + * The callee function interprets the address stored in the GR3 + * as the return value storage area start address. + * When register GR3 needs to be saved into memory, the callee + * function saves it in the hidden parameter save area. This + * area is not secured when the save operation is not needed. + * + * Return address(LR) storage area: + * + * This area saves the LR. The LR stores the address of a return to the caller + * function for the purpose of function calling. + * + * Argument register area: + * + * This area saves the argument register. This area is not secured when the + * save operation is not needed. + * + * Argument: + * + * Arguments, the count of which equals the count of argument registers (6 + * words), are positioned in registers GR8 to GR13 and delivered to the callee + * function. When a struct/union return function is called, the return value + * area address is stored in register GR3. Arguments not placed in the + * argument registers will be stored in the stack argument area for transfer + * purposes. When an 8-byte type argument is to be delivered using registers, + * it is divided into two and placed in two registers for transfer. When + * argument registers must be saved to memory, the callee function secures an + * argument register save area in the stack. In this case, a continuous + * argument register save area must be established in the parameter area. The + * argument register save area must be allocated as needed to cover the size of + * the argument register to be saved. If the function has a variable count of + * arguments, it saves all argument registers in the argument register save + * area. + * + * Argument Extension Format: + * + * When an argument is to be stored in the stack, its type is converted to an + * extended type in accordance with the individual argument type. The argument + * is freed by the caller function after the return from the callee function is + * made. + * + * +-----------------------+---------------+------------------------+ + * | Argument Type |Extended Type |Stack Storage Size(byte)| + * +-----------------------+---------------+------------------------+ + * |char |int | 4 | + * |signed char |int | 4 | + * |unsigned char |int | 4 | + * |[signed] short int |int | 4 | + * |unsigned short int |int | 4 | + * |[signed] int |No extension | 4 | + * |unsigned int |No extension | 4 | + * |[signed] long int |No extension | 4 | + * |unsigned long int |No extension | 4 | + * |[signed] long long int |No extension | 8 | + * |unsigned long long int |No extension | 8 | + * |float |double | 8 | + * |double |No extension | 8 | + * |long double |No extension | 8 | + * |pointer |No extension | 4 | + * |struct/union |- | 4 (*1) | + * +-----------------------+---------------+------------------------+ + * + * When a struct/union is to be delivered as an argument, the caller copies it + * to the local variable area and delivers the address of that area. + * + * Return Value: + * + * +-------------------------------+----------------------+ + * |Return Value Type |Return Value Interface| + * +-------------------------------+----------------------+ + * |void |None | + * |[signed|unsigned] char |GR8 | + * |[signed|unsigned] short int |GR8 | + * |[signed|unsigned] int |GR8 | + * |[signed|unsigned] long int |GR8 | + * |pointer |GR8 | + * |[signed|unsigned] long long int|GR8 & GR9 | + * |float |GR8 | + * |double |GR8 & GR9 | + * |long double |GR8 & GR9 | + * |struct/union |(*1) | + * +-------------------------------+----------------------+ + * + * When a struct/union is used as the return value, the caller function stores + * the start address of the return value storage area into GR3 and then passes + * it to the callee function. The callee function interprets GR3 as the start + * address of the return value storage area. When this address needs to be + * saved in memory, the callee function secures the hidden parameter save area + * and saves the address in that area. + */ + +frv_stack_t * +frv_stack_info (void) +{ + static frv_stack_t info, zero_info; + frv_stack_t *info_ptr = &info; + tree fndecl = current_function_decl; + int varargs_p = 0; + tree cur_arg; + tree next_arg; + int range; + int alignment; + int offset; + + /* If we've already calculated the values and reload is complete, + just return now. */ + if (frv_stack_cache) + return frv_stack_cache; + + /* Zero all fields. */ + info = zero_info; + + /* Set up the register range information. */ + info_ptr->regs[STACK_REGS_GPR].name = "gpr"; + info_ptr->regs[STACK_REGS_GPR].first = LAST_ARG_REGNUM + 1; + info_ptr->regs[STACK_REGS_GPR].last = GPR_LAST; + info_ptr->regs[STACK_REGS_GPR].dword_p = TRUE; + + info_ptr->regs[STACK_REGS_FPR].name = "fpr"; + info_ptr->regs[STACK_REGS_FPR].first = FPR_FIRST; + info_ptr->regs[STACK_REGS_FPR].last = FPR_LAST; + info_ptr->regs[STACK_REGS_FPR].dword_p = TRUE; + + info_ptr->regs[STACK_REGS_LR].name = "lr"; + info_ptr->regs[STACK_REGS_LR].first = LR_REGNO; + info_ptr->regs[STACK_REGS_LR].last = LR_REGNO; + info_ptr->regs[STACK_REGS_LR].special_p = 1; + + info_ptr->regs[STACK_REGS_CC].name = "cc"; + info_ptr->regs[STACK_REGS_CC].first = CC_FIRST; + info_ptr->regs[STACK_REGS_CC].last = CC_LAST; + info_ptr->regs[STACK_REGS_CC].field_p = TRUE; + + info_ptr->regs[STACK_REGS_LCR].name = "lcr"; + info_ptr->regs[STACK_REGS_LCR].first = LCR_REGNO; + info_ptr->regs[STACK_REGS_LCR].last = LCR_REGNO; + + info_ptr->regs[STACK_REGS_STDARG].name = "stdarg"; + info_ptr->regs[STACK_REGS_STDARG].first = FIRST_ARG_REGNUM; + info_ptr->regs[STACK_REGS_STDARG].last = LAST_ARG_REGNUM; + info_ptr->regs[STACK_REGS_STDARG].dword_p = 1; + info_ptr->regs[STACK_REGS_STDARG].special_p = 1; + + info_ptr->regs[STACK_REGS_STRUCT].name = "struct"; + info_ptr->regs[STACK_REGS_STRUCT].first = FRV_STRUCT_VALUE_REGNUM; + info_ptr->regs[STACK_REGS_STRUCT].last = FRV_STRUCT_VALUE_REGNUM; + info_ptr->regs[STACK_REGS_STRUCT].special_p = 1; + + info_ptr->regs[STACK_REGS_FP].name = "fp"; + info_ptr->regs[STACK_REGS_FP].first = FRAME_POINTER_REGNUM; + info_ptr->regs[STACK_REGS_FP].last = FRAME_POINTER_REGNUM; + info_ptr->regs[STACK_REGS_FP].special_p = 1; + + /* Determine if this is a stdarg function. If so, allocate space to store + the 6 arguments. */ + if (cfun->stdarg) + varargs_p = 1; + + else + { + /* Find the last argument, and see if it is __builtin_va_alist. */ + for (cur_arg = DECL_ARGUMENTS (fndecl); cur_arg != (tree)0; cur_arg = next_arg) + { + next_arg = DECL_CHAIN (cur_arg); + if (next_arg == (tree)0) + { + if (DECL_NAME (cur_arg) + && !strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), "__builtin_va_alist")) + varargs_p = 1; + + break; + } + } + } + + /* Iterate over all of the register ranges. */ + for (range = 0; range < STACK_REGS_MAX; range++) + { + frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]); + int first = reg_ptr->first; + int last = reg_ptr->last; + int size_1word = 0; + int size_2words = 0; + int regno; + + /* Calculate which registers need to be saved & save area size. */ + switch (range) + { + default: + for (regno = first; regno <= last; regno++) + { + if ((df_regs_ever_live_p (regno) && !call_used_regs[regno]) + || (crtl->calls_eh_return + && (regno >= FIRST_EH_REGNUM && regno <= LAST_EH_REGNUM)) + || (!TARGET_FDPIC && flag_pic + && crtl->uses_pic_offset_table && regno == PIC_REGNO)) + { + info_ptr->save_p[regno] = REG_SAVE_1WORD; + size_1word += UNITS_PER_WORD; + } + } + break; + + /* Calculate whether we need to create a frame after everything else + has been processed. */ + case STACK_REGS_FP: + break; + + case STACK_REGS_LR: + if (df_regs_ever_live_p (LR_REGNO) + || profile_flag + /* This is set for __builtin_return_address, etc. */ + || cfun->machine->frame_needed + || (TARGET_LINKED_FP && frame_pointer_needed) + || (!TARGET_FDPIC && flag_pic + && crtl->uses_pic_offset_table)) + { + info_ptr->save_p[LR_REGNO] = REG_SAVE_1WORD; + size_1word += UNITS_PER_WORD; + } + break; + + case STACK_REGS_STDARG: + if (varargs_p) + { + /* If this is a stdarg function with a non varardic + argument split between registers and the stack, + adjust the saved registers downward. */ + last -= (ADDR_ALIGN (crtl->args.pretend_args_size, UNITS_PER_WORD) + / UNITS_PER_WORD); + + for (regno = first; regno <= last; regno++) + { + info_ptr->save_p[regno] = REG_SAVE_1WORD; + size_1word += UNITS_PER_WORD; + } + + info_ptr->stdarg_size = size_1word; + } + break; + + case STACK_REGS_STRUCT: + if (cfun->returns_struct) + { + info_ptr->save_p[FRV_STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD; + size_1word += UNITS_PER_WORD; + } + break; + } + + + if (size_1word) + { + /* If this is a field, it only takes one word. */ + if (reg_ptr->field_p) + size_1word = UNITS_PER_WORD; + + /* Determine which register pairs can be saved together. */ + else if (reg_ptr->dword_p && TARGET_DWORD) + { + for (regno = first; regno < last; regno += 2) + { + if (info_ptr->save_p[regno] && info_ptr->save_p[regno+1]) + { + size_2words += 2 * UNITS_PER_WORD; + size_1word -= 2 * UNITS_PER_WORD; + info_ptr->save_p[regno] = REG_SAVE_2WORDS; + info_ptr->save_p[regno+1] = REG_SAVE_NO_SAVE; + } + } + } + + reg_ptr->size_1word = size_1word; + reg_ptr->size_2words = size_2words; + + if (! reg_ptr->special_p) + { + info_ptr->regs_size_1word += size_1word; + info_ptr->regs_size_2words += size_2words; + } + } + } + + /* Set up the sizes of each each field in the frame body, making the sizes + of each be divisible by the size of a dword if dword operations might + be used, or the size of a word otherwise. */ + alignment = (TARGET_DWORD? 2 * UNITS_PER_WORD : UNITS_PER_WORD); + + info_ptr->parameter_size = ADDR_ALIGN (crtl->outgoing_args_size, alignment); + info_ptr->regs_size = ADDR_ALIGN (info_ptr->regs_size_2words + + info_ptr->regs_size_1word, + alignment); + info_ptr->vars_size = ADDR_ALIGN (get_frame_size (), alignment); + + info_ptr->pretend_size = crtl->args.pretend_args_size; + + /* Work out the size of the frame, excluding the header. Both the frame + body and register parameter area will be dword-aligned. */ + info_ptr->total_size + = (ADDR_ALIGN (info_ptr->parameter_size + + info_ptr->regs_size + + info_ptr->vars_size, + 2 * UNITS_PER_WORD) + + ADDR_ALIGN (info_ptr->pretend_size + + info_ptr->stdarg_size, + 2 * UNITS_PER_WORD)); + + /* See if we need to create a frame at all, if so add header area. */ + if (info_ptr->total_size > 0 + || frame_pointer_needed + || info_ptr->regs[STACK_REGS_LR].size_1word > 0 + || info_ptr->regs[STACK_REGS_STRUCT].size_1word > 0) + { + offset = info_ptr->parameter_size; + info_ptr->header_size = 4 * UNITS_PER_WORD; + info_ptr->total_size += 4 * UNITS_PER_WORD; + + /* Calculate the offsets to save normal register pairs. */ + for (range = 0; range < STACK_REGS_MAX; range++) + { + frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]); + if (! reg_ptr->special_p) + { + int first = reg_ptr->first; + int last = reg_ptr->last; + int regno; + + for (regno = first; regno <= last; regno++) + if (info_ptr->save_p[regno] == REG_SAVE_2WORDS + && regno != FRAME_POINTER_REGNUM + && (regno < FIRST_ARG_REGNUM + || regno > LAST_ARG_REGNUM)) + { + info_ptr->reg_offset[regno] = offset; + offset += 2 * UNITS_PER_WORD; + } + } + } + + /* Calculate the offsets to save normal single registers. */ + for (range = 0; range < STACK_REGS_MAX; range++) + { + frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]); + if (! reg_ptr->special_p) + { + int first = reg_ptr->first; + int last = reg_ptr->last; + int regno; + + for (regno = first; regno <= last; regno++) + if (info_ptr->save_p[regno] == REG_SAVE_1WORD + && regno != FRAME_POINTER_REGNUM + && (regno < FIRST_ARG_REGNUM + || regno > LAST_ARG_REGNUM)) + { + info_ptr->reg_offset[regno] = offset; + offset += UNITS_PER_WORD; + } + } + } + + /* Calculate the offset to save the local variables at. */ + offset = ADDR_ALIGN (offset, alignment); + if (info_ptr->vars_size) + { + info_ptr->vars_offset = offset; + offset += info_ptr->vars_size; + } + + /* Align header to a dword-boundary. */ + offset = ADDR_ALIGN (offset, 2 * UNITS_PER_WORD); + + /* Calculate the offsets in the fixed frame. */ + info_ptr->save_p[FRAME_POINTER_REGNUM] = REG_SAVE_1WORD; + info_ptr->reg_offset[FRAME_POINTER_REGNUM] = offset; + info_ptr->regs[STACK_REGS_FP].size_1word = UNITS_PER_WORD; + + info_ptr->save_p[LR_REGNO] = REG_SAVE_1WORD; + info_ptr->reg_offset[LR_REGNO] = offset + 2*UNITS_PER_WORD; + info_ptr->regs[STACK_REGS_LR].size_1word = UNITS_PER_WORD; + + if (cfun->returns_struct) + { + info_ptr->save_p[FRV_STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD; + info_ptr->reg_offset[FRV_STRUCT_VALUE_REGNUM] = offset + UNITS_PER_WORD; + info_ptr->regs[STACK_REGS_STRUCT].size_1word = UNITS_PER_WORD; + } + + /* Calculate the offsets to store the arguments passed in registers + for stdarg functions. The register pairs are first and the single + register if any is last. The register save area starts on a + dword-boundary. */ + if (info_ptr->stdarg_size) + { + int first = info_ptr->regs[STACK_REGS_STDARG].first; + int last = info_ptr->regs[STACK_REGS_STDARG].last; + int regno; + + /* Skip the header. */ + offset += 4 * UNITS_PER_WORD; + for (regno = first; regno <= last; regno++) + { + if (info_ptr->save_p[regno] == REG_SAVE_2WORDS) + { + info_ptr->reg_offset[regno] = offset; + offset += 2 * UNITS_PER_WORD; + } + else if (info_ptr->save_p[regno] == REG_SAVE_1WORD) + { + info_ptr->reg_offset[regno] = offset; + offset += UNITS_PER_WORD; + } + } + } + } + + if (reload_completed) + frv_stack_cache = info_ptr; + + return info_ptr; +} + + +/* Print the information about the frv stack offsets, etc. when debugging. */ + +void +frv_debug_stack (frv_stack_t *info) +{ + int range; + + if (!info) + info = frv_stack_info (); + + fprintf (stderr, "\nStack information for function %s:\n", + ((current_function_decl && DECL_NAME (current_function_decl)) + ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl)) + : "<unknown>")); + + fprintf (stderr, "\ttotal_size\t= %6d\n", info->total_size); + fprintf (stderr, "\tvars_size\t= %6d\n", info->vars_size); + fprintf (stderr, "\tparam_size\t= %6d\n", info->parameter_size); + fprintf (stderr, "\tregs_size\t= %6d, 1w = %3d, 2w = %3d\n", + info->regs_size, info->regs_size_1word, info->regs_size_2words); + + fprintf (stderr, "\theader_size\t= %6d\n", info->header_size); + fprintf (stderr, "\tpretend_size\t= %6d\n", info->pretend_size); + fprintf (stderr, "\tvars_offset\t= %6d\n", info->vars_offset); + fprintf (stderr, "\tregs_offset\t= %6d\n", info->regs_offset); + + for (range = 0; range < STACK_REGS_MAX; range++) + { + frv_stack_regs_t *regs = &(info->regs[range]); + if ((regs->size_1word + regs->size_2words) > 0) + { + int first = regs->first; + int last = regs->last; + int regno; + + fprintf (stderr, "\t%s\tsize\t= %6d, 1w = %3d, 2w = %3d, save =", + regs->name, regs->size_1word + regs->size_2words, + regs->size_1word, regs->size_2words); + + for (regno = first; regno <= last; regno++) + { + if (info->save_p[regno] == REG_SAVE_1WORD) + fprintf (stderr, " %s (%d)", reg_names[regno], + info->reg_offset[regno]); + + else if (info->save_p[regno] == REG_SAVE_2WORDS) + fprintf (stderr, " %s-%s (%d)", reg_names[regno], + reg_names[regno+1], info->reg_offset[regno]); + } + + fputc ('\n', stderr); + } + } + + fflush (stderr); +} + + + + +/* Used during final to control the packing of insns. The value is + 1 if the current instruction should be packed with the next one, + 0 if it shouldn't or -1 if packing is disabled altogether. */ + +static int frv_insn_packing_flag; + +/* True if the current function contains a far jump. */ + +static int +frv_function_contains_far_jump (void) +{ + rtx insn = get_insns (); + while (insn != NULL + && !(GET_CODE (insn) == JUMP_INSN + /* Ignore tablejump patterns. */ + && GET_CODE (PATTERN (insn)) != ADDR_VEC + && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC + && get_attr_far_jump (insn) == FAR_JUMP_YES)) + insn = NEXT_INSN (insn); + return (insn != NULL); +} + +/* For the FRV, this function makes sure that a function with far jumps + will return correctly. It also does the VLIW packing. */ + +static void +frv_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) +{ + /* If no frame was created, check whether the function uses a call + instruction to implement a far jump. If so, save the link in gr3 and + replace all returns to LR with returns to GR3. GR3 is used because it + is call-clobbered, because is not available to the register allocator, + and because all functions that take a hidden argument pointer will have + a stack frame. */ + if (frv_stack_info ()->total_size == 0 && frv_function_contains_far_jump ()) + { + rtx insn; + + /* Just to check that the above comment is true. */ + gcc_assert (!df_regs_ever_live_p (GPR_FIRST + 3)); + + /* Generate the instruction that saves the link register. */ + fprintf (file, "\tmovsg lr,gr3\n"); + + /* Replace the LR with GR3 in *return_internal patterns. The insn + will now return using jmpl @(gr3,0) rather than bralr. We cannot + simply emit a different assembly directive because bralr and jmpl + execute in different units. */ + for (insn = get_insns(); insn != NULL; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == JUMP_INSN) + { + rtx pattern = PATTERN (insn); + if (GET_CODE (pattern) == PARALLEL + && XVECLEN (pattern, 0) >= 2 + && GET_CODE (XVECEXP (pattern, 0, 0)) == RETURN + && GET_CODE (XVECEXP (pattern, 0, 1)) == USE) + { + rtx address = XEXP (XVECEXP (pattern, 0, 1), 0); + if (GET_CODE (address) == REG && REGNO (address) == LR_REGNO) + SET_REGNO (address, GPR_FIRST + 3); + } + } + } + + frv_pack_insns (); + + /* Allow the garbage collector to free the nops created by frv_reorg. */ + memset (frv_nops, 0, sizeof (frv_nops)); +} + + +/* Return the next available temporary register in a given class. */ + +static rtx +frv_alloc_temp_reg ( + frv_tmp_reg_t *info, /* which registers are available */ + enum reg_class rclass, /* register class desired */ + enum machine_mode mode, /* mode to allocate register with */ + int mark_as_used, /* register not available after allocation */ + int no_abort) /* return NULL instead of aborting */ +{ + int regno = info->next_reg[ (int)rclass ]; + int orig_regno = regno; + HARD_REG_SET *reg_in_class = ®_class_contents[ (int)rclass ]; + int i, nr; + + for (;;) + { + if (TEST_HARD_REG_BIT (*reg_in_class, regno) + && TEST_HARD_REG_BIT (info->regs, regno)) + break; + + if (++regno >= FIRST_PSEUDO_REGISTER) + regno = 0; + if (regno == orig_regno) + { + gcc_assert (no_abort); + return NULL_RTX; + } + } + + nr = HARD_REGNO_NREGS (regno, mode); + info->next_reg[ (int)rclass ] = regno + nr; + + if (mark_as_used) + for (i = 0; i < nr; i++) + CLEAR_HARD_REG_BIT (info->regs, regno+i); + + return gen_rtx_REG (mode, regno); +} + + +/* Return an rtx with the value OFFSET, which will either be a register or a + signed 12-bit integer. It can be used as the second operand in an "add" + instruction, or as the index in a load or store. + + The function returns a constant rtx if OFFSET is small enough, otherwise + it loads the constant into register OFFSET_REGNO and returns that. */ +static rtx +frv_frame_offset_rtx (int offset) +{ + rtx offset_rtx = GEN_INT (offset); + if (IN_RANGE (offset, -2048, 2047)) + return offset_rtx; + else + { + rtx reg_rtx = gen_rtx_REG (SImode, OFFSET_REGNO); + if (IN_RANGE (offset, -32768, 32767)) + emit_insn (gen_movsi (reg_rtx, offset_rtx)); + else + { + emit_insn (gen_movsi_high (reg_rtx, offset_rtx)); + emit_insn (gen_movsi_lo_sum (reg_rtx, offset_rtx)); + } + return reg_rtx; + } +} + +/* Generate (mem:MODE (plus:Pmode BASE (frv_frame_offset OFFSET)))). The + prologue and epilogue uses such expressions to access the stack. */ +static rtx +frv_frame_mem (enum machine_mode mode, rtx base, int offset) +{ + return gen_rtx_MEM (mode, gen_rtx_PLUS (Pmode, + base, + frv_frame_offset_rtx (offset))); +} + +/* Generate a frame-related expression: + + (set REG (mem (plus (sp) (const_int OFFSET)))). + + Such expressions are used in FRAME_RELATED_EXPR notes for more complex + instructions. Marking the expressions as frame-related is superfluous if + the note contains just a single set. But if the note contains a PARALLEL + or SEQUENCE that has several sets, each set must be individually marked + as frame-related. */ +static rtx +frv_dwarf_store (rtx reg, int offset) +{ + rtx set = gen_rtx_SET (VOIDmode, + gen_rtx_MEM (GET_MODE (reg), + plus_constant (stack_pointer_rtx, + offset)), + reg); + RTX_FRAME_RELATED_P (set) = 1; + return set; +} + +/* Emit a frame-related instruction whose pattern is PATTERN. The + instruction is the last in a sequence that cumulatively performs the + operation described by DWARF_PATTERN. The instruction is marked as + frame-related and has a REG_FRAME_RELATED_EXPR note containing + DWARF_PATTERN. */ +static void +frv_frame_insn (rtx pattern, rtx dwarf_pattern) +{ + rtx insn = emit_insn (pattern); + RTX_FRAME_RELATED_P (insn) = 1; + REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, + dwarf_pattern, + REG_NOTES (insn)); +} + +/* Emit instructions that transfer REG to or from the memory location (sp + + STACK_OFFSET). The register is stored in memory if ACCESSOR->OP is + FRV_STORE and loaded if it is FRV_LOAD. Only the prologue uses this + function to store registers and only the epilogue uses it to load them. + + The caller sets up ACCESSOR so that BASE is equal to (sp + BASE_OFFSET). + The generated instruction will use BASE as its base register. BASE may + simply be the stack pointer, but if several accesses are being made to a + region far away from the stack pointer, it may be more efficient to set + up a temporary instead. + + Store instructions will be frame-related and will be annotated with the + overall effect of the store. Load instructions will be followed by a + (use) to prevent later optimizations from zapping them. + + The function takes care of the moves to and from SPRs, using TEMP_REGNO + as a temporary in such cases. */ +static void +frv_frame_access (frv_frame_accessor_t *accessor, rtx reg, int stack_offset) +{ + enum machine_mode mode = GET_MODE (reg); + rtx mem = frv_frame_mem (mode, + accessor->base, + stack_offset - accessor->base_offset); + + if (accessor->op == FRV_LOAD) + { + if (SPR_P (REGNO (reg))) + { + rtx temp = gen_rtx_REG (mode, TEMP_REGNO); + emit_insn (gen_rtx_SET (VOIDmode, temp, mem)); + emit_insn (gen_rtx_SET (VOIDmode, reg, temp)); + } + else + { + /* We cannot use reg+reg addressing for DImode access. */ + if (mode == DImode + && GET_CODE (XEXP (mem, 0)) == PLUS + && GET_CODE (XEXP (XEXP (mem, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (mem, 0), 1)) == REG) + { + rtx temp = gen_rtx_REG (SImode, TEMP_REGNO); + + emit_move_insn (temp, + gen_rtx_PLUS (SImode, XEXP (XEXP (mem, 0), 0), + XEXP (XEXP (mem, 0), 1))); + mem = gen_rtx_MEM (DImode, temp); + } + emit_insn (gen_rtx_SET (VOIDmode, reg, mem)); + } + emit_use (reg); + } + else + { + if (SPR_P (REGNO (reg))) + { + rtx temp = gen_rtx_REG (mode, TEMP_REGNO); + emit_insn (gen_rtx_SET (VOIDmode, temp, reg)); + frv_frame_insn (gen_rtx_SET (Pmode, mem, temp), + frv_dwarf_store (reg, stack_offset)); + } + else if (mode == DImode) + { + /* For DImode saves, the dwarf2 version needs to be a SEQUENCE + with a separate save for each register. */ + rtx reg1 = gen_rtx_REG (SImode, REGNO (reg)); + rtx reg2 = gen_rtx_REG (SImode, REGNO (reg) + 1); + rtx set1 = frv_dwarf_store (reg1, stack_offset); + rtx set2 = frv_dwarf_store (reg2, stack_offset + 4); + + /* Also we cannot use reg+reg addressing. */ + if (GET_CODE (XEXP (mem, 0)) == PLUS + && GET_CODE (XEXP (XEXP (mem, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (mem, 0), 1)) == REG) + { + rtx temp = gen_rtx_REG (SImode, TEMP_REGNO); + emit_move_insn (temp, + gen_rtx_PLUS (SImode, XEXP (XEXP (mem, 0), 0), + XEXP (XEXP (mem, 0), 1))); + mem = gen_rtx_MEM (DImode, temp); + } + + frv_frame_insn (gen_rtx_SET (Pmode, mem, reg), + gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, set1, set2))); + } + else + frv_frame_insn (gen_rtx_SET (Pmode, mem, reg), + frv_dwarf_store (reg, stack_offset)); + } +} + +/* A function that uses frv_frame_access to transfer a group of registers to + or from the stack. ACCESSOR is passed directly to frv_frame_access, INFO + is the stack information generated by frv_stack_info, and REG_SET is the + number of the register set to transfer. */ +static void +frv_frame_access_multi (frv_frame_accessor_t *accessor, + frv_stack_t *info, + int reg_set) +{ + frv_stack_regs_t *regs_info; + int regno; + + regs_info = &info->regs[reg_set]; + for (regno = regs_info->first; regno <= regs_info->last; regno++) + if (info->save_p[regno]) + frv_frame_access (accessor, + info->save_p[regno] == REG_SAVE_2WORDS + ? gen_rtx_REG (DImode, regno) + : gen_rtx_REG (SImode, regno), + info->reg_offset[regno]); +} + +/* Save or restore callee-saved registers that are kept outside the frame + header. The function saves the registers if OP is FRV_STORE and restores + them if OP is FRV_LOAD. INFO is the stack information generated by + frv_stack_info. */ +static void +frv_frame_access_standard_regs (enum frv_stack_op op, frv_stack_t *info) +{ + frv_frame_accessor_t accessor; + + accessor.op = op; + accessor.base = stack_pointer_rtx; + accessor.base_offset = 0; + frv_frame_access_multi (&accessor, info, STACK_REGS_GPR); + frv_frame_access_multi (&accessor, info, STACK_REGS_FPR); + frv_frame_access_multi (&accessor, info, STACK_REGS_LCR); +} + + +/* Called after register allocation to add any instructions needed for the + prologue. Using a prologue insn is favored compared to putting all of the + instructions in the TARGET_ASM_FUNCTION_PROLOGUE target hook, since + it allows the scheduler to intermix instructions with the saves of + the caller saved registers. In some cases, it might be necessary + to emit a barrier instruction as the last insn to prevent such + scheduling. + + Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1 + so that the debug info generation code can handle them properly. */ +void +frv_expand_prologue (void) +{ + frv_stack_t *info = frv_stack_info (); + rtx sp = stack_pointer_rtx; + rtx fp = frame_pointer_rtx; + frv_frame_accessor_t accessor; + + if (TARGET_DEBUG_STACK) + frv_debug_stack (info); + + if (info->total_size == 0) + return; + + /* We're interested in three areas of the frame here: + + A: the register save area + B: the old FP + C: the header after B + + If the frame pointer isn't used, we'll have to set up A, B and C + using the stack pointer. If the frame pointer is used, we'll access + them as follows: + + A: set up using sp + B: set up using sp or a temporary (see below) + C: set up using fp + + We set up B using the stack pointer if the frame is small enough. + Otherwise, it's more efficient to copy the old stack pointer into a + temporary and use that. + + Note that it's important to make sure the prologue and epilogue use the + same registers to access A and C, since doing otherwise will confuse + the aliasing code. */ + + /* Set up ACCESSOR for accessing region B above. If the frame pointer + isn't used, the same method will serve for C. */ + accessor.op = FRV_STORE; + if (frame_pointer_needed && info->total_size > 2048) + { + accessor.base = gen_rtx_REG (Pmode, OLD_SP_REGNO); + accessor.base_offset = info->total_size; + emit_insn (gen_movsi (accessor.base, sp)); + } + else + { + accessor.base = stack_pointer_rtx; + accessor.base_offset = 0; + } + + /* Allocate the stack space. */ + { + rtx asm_offset = frv_frame_offset_rtx (-info->total_size); + rtx dwarf_offset = GEN_INT (-info->total_size); + + frv_frame_insn (gen_stack_adjust (sp, sp, asm_offset), + gen_rtx_SET (Pmode, + sp, + gen_rtx_PLUS (Pmode, sp, dwarf_offset))); + } + + /* If the frame pointer is needed, store the old one at (sp + FP_OFFSET) + and point the new one to that location. */ + if (frame_pointer_needed) + { + int fp_offset = info->reg_offset[FRAME_POINTER_REGNUM]; + + /* ASM_SRC and DWARF_SRC both point to the frame header. ASM_SRC is + based on ACCESSOR.BASE but DWARF_SRC is always based on the stack + pointer. */ + rtx asm_src = plus_constant (accessor.base, + fp_offset - accessor.base_offset); + rtx dwarf_src = plus_constant (sp, fp_offset); + + /* Store the old frame pointer at (sp + FP_OFFSET). */ + frv_frame_access (&accessor, fp, fp_offset); + + /* Set up the new frame pointer. */ + frv_frame_insn (gen_rtx_SET (VOIDmode, fp, asm_src), + gen_rtx_SET (VOIDmode, fp, dwarf_src)); + + /* Access region C from the frame pointer. */ + accessor.base = fp; + accessor.base_offset = fp_offset; + } + + /* Set up region C. */ + frv_frame_access_multi (&accessor, info, STACK_REGS_STRUCT); + frv_frame_access_multi (&accessor, info, STACK_REGS_LR); + frv_frame_access_multi (&accessor, info, STACK_REGS_STDARG); + + /* Set up region A. */ + frv_frame_access_standard_regs (FRV_STORE, info); + + /* If this is a varargs/stdarg function, issue a blockage to prevent the + scheduler from moving loads before the stores saving the registers. */ + if (info->stdarg_size > 0) + emit_insn (gen_blockage ()); + + /* Set up pic register/small data register for this function. */ + if (!TARGET_FDPIC && flag_pic && crtl->uses_pic_offset_table) + emit_insn (gen_pic_prologue (gen_rtx_REG (Pmode, PIC_REGNO), + gen_rtx_REG (Pmode, LR_REGNO), + gen_rtx_REG (SImode, OFFSET_REGNO))); +} + + +/* Under frv, all of the work is done via frv_expand_epilogue, but + this function provides a convenient place to do cleanup. */ + +static void +frv_function_epilogue (FILE *file ATTRIBUTE_UNUSED, + HOST_WIDE_INT size ATTRIBUTE_UNUSED) +{ + frv_stack_cache = (frv_stack_t *)0; + + /* Zap last used registers for conditional execution. */ + memset (&frv_ifcvt.tmp_reg, 0, sizeof (frv_ifcvt.tmp_reg)); + + /* Release the bitmap of created insns. */ + BITMAP_FREE (frv_ifcvt.scratch_insns_bitmap); +} + + +/* Called after register allocation to add any instructions needed for the + epilogue. Using an epilogue insn is favored compared to putting all of the + instructions in the TARGET_ASM_FUNCTION_PROLOGUE target hook, since + it allows the scheduler to intermix instructions with the saves of + the caller saved registers. In some cases, it might be necessary + to emit a barrier instruction as the last insn to prevent such + scheduling. */ + +void +frv_expand_epilogue (bool emit_return) +{ + frv_stack_t *info = frv_stack_info (); + rtx fp = frame_pointer_rtx; + rtx sp = stack_pointer_rtx; + rtx return_addr; + int fp_offset; + + fp_offset = info->reg_offset[FRAME_POINTER_REGNUM]; + + /* Restore the stack pointer to its original value if alloca or the like + is used. */ + if (! current_function_sp_is_unchanging) + emit_insn (gen_addsi3 (sp, fp, frv_frame_offset_rtx (-fp_offset))); + + /* Restore the callee-saved registers that were used in this function. */ + frv_frame_access_standard_regs (FRV_LOAD, info); + + /* Set RETURN_ADDR to the address we should return to. Set it to NULL if + no return instruction should be emitted. */ + if (info->save_p[LR_REGNO]) + { + int lr_offset; + rtx mem; + + /* Use the same method to access the link register's slot as we did in + the prologue. In other words, use the frame pointer if available, + otherwise use the stack pointer. + + LR_OFFSET is the offset of the link register's slot from the start + of the frame and MEM is a memory rtx for it. */ + lr_offset = info->reg_offset[LR_REGNO]; + if (frame_pointer_needed) + mem = frv_frame_mem (Pmode, fp, lr_offset - fp_offset); + else + mem = frv_frame_mem (Pmode, sp, lr_offset); + + /* Load the old link register into a GPR. */ + return_addr = gen_rtx_REG (Pmode, TEMP_REGNO); + emit_insn (gen_rtx_SET (VOIDmode, return_addr, mem)); + } + else + return_addr = gen_rtx_REG (Pmode, LR_REGNO); + + /* Restore the old frame pointer. Emit a USE afterwards to make sure + the load is preserved. */ + if (frame_pointer_needed) + { + emit_insn (gen_rtx_SET (VOIDmode, fp, gen_rtx_MEM (Pmode, fp))); + emit_use (fp); + } + + /* Deallocate the stack frame. */ + if (info->total_size != 0) + { + rtx offset = frv_frame_offset_rtx (info->total_size); + emit_insn (gen_stack_adjust (sp, sp, offset)); + } + + /* If this function uses eh_return, add the final stack adjustment now. */ + if (crtl->calls_eh_return) + emit_insn (gen_stack_adjust (sp, sp, EH_RETURN_STACKADJ_RTX)); + + if (emit_return) + emit_jump_insn (gen_epilogue_return (return_addr)); + else + { + rtx lr = return_addr; + + if (REGNO (return_addr) != LR_REGNO) + { + lr = gen_rtx_REG (Pmode, LR_REGNO); + emit_move_insn (lr, return_addr); + } + + emit_use (lr); + } +} + + +/* Worker function for TARGET_ASM_OUTPUT_MI_THUNK. */ + +static void +frv_asm_output_mi_thunk (FILE *file, + tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, + HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, + tree function) +{ + const char *name_func = XSTR (XEXP (DECL_RTL (function), 0), 0); + const char *name_arg0 = reg_names[FIRST_ARG_REGNUM]; + const char *name_jmp = reg_names[JUMP_REGNO]; + const char *parallel = (frv_issue_rate () > 1 ? ".p" : ""); + + /* Do the add using an addi if possible. */ + if (IN_RANGE (delta, -2048, 2047)) + fprintf (file, "\taddi %s,#%d,%s\n", name_arg0, (int) delta, name_arg0); + else + { + const char *const name_add = reg_names[TEMP_REGNO]; + fprintf (file, "\tsethi%s #hi(" HOST_WIDE_INT_PRINT_DEC "),%s\n", + parallel, delta, name_add); + fprintf (file, "\tsetlo #lo(" HOST_WIDE_INT_PRINT_DEC "),%s\n", + delta, name_add); + fprintf (file, "\tadd %s,%s,%s\n", name_add, name_arg0, name_arg0); + } + + if (TARGET_FDPIC) + { + const char *name_pic = reg_names[FDPIC_REGNO]; + name_jmp = reg_names[FDPIC_FPTR_REGNO]; + + if (flag_pic != 1) + { + fprintf (file, "\tsethi%s #gotofffuncdeschi(", parallel); + assemble_name (file, name_func); + fprintf (file, "),%s\n", name_jmp); + + fprintf (file, "\tsetlo #gotofffuncdesclo("); + assemble_name (file, name_func); + fprintf (file, "),%s\n", name_jmp); + + fprintf (file, "\tldd @(%s,%s), %s\n", name_jmp, name_pic, name_jmp); + } + else + { + fprintf (file, "\tlddo @(%s,#gotofffuncdesc12(", name_pic); + assemble_name (file, name_func); + fprintf (file, "\t)), %s\n", name_jmp); + } + } + else if (!flag_pic) + { + fprintf (file, "\tsethi%s #hi(", parallel); + assemble_name (file, name_func); + fprintf (file, "),%s\n", name_jmp); + + fprintf (file, "\tsetlo #lo("); + assemble_name (file, name_func); + fprintf (file, "),%s\n", name_jmp); + } + else + { + /* Use JUMP_REGNO as a temporary PIC register. */ + const char *name_lr = reg_names[LR_REGNO]; + const char *name_gppic = name_jmp; + const char *name_tmp = reg_names[TEMP_REGNO]; + + fprintf (file, "\tmovsg %s,%s\n", name_lr, name_tmp); + fprintf (file, "\tcall 1f\n"); + fprintf (file, "1:\tmovsg %s,%s\n", name_lr, name_gppic); + fprintf (file, "\tmovgs %s,%s\n", name_tmp, name_lr); + fprintf (file, "\tsethi%s #gprelhi(1b),%s\n", parallel, name_tmp); + fprintf (file, "\tsetlo #gprello(1b),%s\n", name_tmp); + fprintf (file, "\tsub %s,%s,%s\n", name_gppic, name_tmp, name_gppic); + + fprintf (file, "\tsethi%s #gprelhi(", parallel); + assemble_name (file, name_func); + fprintf (file, "),%s\n", name_tmp); + + fprintf (file, "\tsetlo #gprello("); + assemble_name (file, name_func); + fprintf (file, "),%s\n", name_tmp); + + fprintf (file, "\tadd %s,%s,%s\n", name_gppic, name_tmp, name_jmp); + } + + /* Jump to the function address. */ + fprintf (file, "\tjmpl @(%s,%s)\n", name_jmp, reg_names[GPR_FIRST+0]); +} + + + +/* On frv, create a frame whenever we need to create stack. */ + +static bool +frv_frame_pointer_required (void) +{ + /* If we forgoing the usual linkage requirements, we only need + a frame pointer if the stack pointer might change. */ + if (!TARGET_LINKED_FP) + return !current_function_sp_is_unchanging; + + if (! current_function_is_leaf) + return true; + + if (get_frame_size () != 0) + return true; + + if (cfun->stdarg) + return true; + + if (!current_function_sp_is_unchanging) + return true; + + if (!TARGET_FDPIC && flag_pic && crtl->uses_pic_offset_table) + return true; + + if (profile_flag) + return true; + + if (cfun->machine->frame_needed) + return true; + + return false; +} + + +/* Worker function for TARGET_CAN_ELIMINATE. */ + +bool +frv_can_eliminate (const int from, const int to) +{ + return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM + ? ! frame_pointer_needed + : true); +} + +/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It specifies the + initial difference between the specified pair of registers. This macro must + be defined if `ELIMINABLE_REGS' is defined. */ + +/* See frv_stack_info for more details on the frv stack frame. */ + +int +frv_initial_elimination_offset (int from, int to) +{ + frv_stack_t *info = frv_stack_info (); + int ret = 0; + + if (to == STACK_POINTER_REGNUM && from == ARG_POINTER_REGNUM) + ret = info->total_size - info->pretend_size; + + else if (to == STACK_POINTER_REGNUM && from == FRAME_POINTER_REGNUM) + ret = info->reg_offset[FRAME_POINTER_REGNUM]; + + else if (to == FRAME_POINTER_REGNUM && from == ARG_POINTER_REGNUM) + ret = (info->total_size + - info->reg_offset[FRAME_POINTER_REGNUM] + - info->pretend_size); + + else + gcc_unreachable (); + + if (TARGET_DEBUG_STACK) + fprintf (stderr, "Eliminate %s to %s by adding %d\n", + reg_names [from], reg_names[to], ret); + + return ret; +} + + +/* Worker function for TARGET_SETUP_INCOMING_VARARGS. */ + +static void +frv_setup_incoming_varargs (CUMULATIVE_ARGS *cum, + enum machine_mode mode, + tree type ATTRIBUTE_UNUSED, + int *pretend_size, + int second_time) +{ + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "setup_vararg: words = %2d, mode = %4s, pretend_size = %d, second_time = %d\n", + *cum, GET_MODE_NAME (mode), *pretend_size, second_time); +} + + +/* Worker function for TARGET_EXPAND_BUILTIN_SAVEREGS. */ + +static rtx +frv_expand_builtin_saveregs (void) +{ + int offset = UNITS_PER_WORD * FRV_NUM_ARG_REGS; + + if (TARGET_DEBUG_ARG) + fprintf (stderr, "expand_builtin_saveregs: offset from ap = %d\n", + offset); + + return gen_rtx_PLUS (Pmode, virtual_incoming_args_rtx, GEN_INT (- offset)); +} + + +/* Expand __builtin_va_start to do the va_start macro. */ + +static void +frv_expand_builtin_va_start (tree valist, rtx nextarg) +{ + tree t; + int num = crtl->args.info - FIRST_ARG_REGNUM - FRV_NUM_ARG_REGS; + + nextarg = gen_rtx_PLUS (Pmode, virtual_incoming_args_rtx, + GEN_INT (UNITS_PER_WORD * num)); + + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "va_start: args_info = %d, num = %d\n", + crtl->args.info, num); + + debug_rtx (nextarg); + } + + t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, + fold_convert (TREE_TYPE (valist), + make_tree (sizetype, nextarg))); + TREE_SIDE_EFFECTS (t) = 1; + + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); +} + + +/* Expand a block move operation, and return 1 if successful. Return 0 + if we should let the compiler generate normal code. + + operands[0] is the destination + operands[1] is the source + operands[2] is the length + operands[3] is the alignment */ + +/* Maximum number of loads to do before doing the stores */ +#ifndef MAX_MOVE_REG +#define MAX_MOVE_REG 4 +#endif + +/* Maximum number of total loads to do. */ +#ifndef TOTAL_MOVE_REG +#define TOTAL_MOVE_REG 8 +#endif + +int +frv_expand_block_move (rtx operands[]) +{ + rtx orig_dest = operands[0]; + rtx orig_src = operands[1]; + rtx bytes_rtx = operands[2]; + rtx align_rtx = operands[3]; + int constp = (GET_CODE (bytes_rtx) == CONST_INT); + int align; + int bytes; + int offset; + int num_reg; + int i; + rtx src_reg; + rtx dest_reg; + rtx src_addr; + rtx dest_addr; + rtx src_mem; + rtx dest_mem; + rtx tmp_reg; + rtx stores[MAX_MOVE_REG]; + int move_bytes; + enum machine_mode mode; + + /* If this is not a fixed size move, just call memcpy. */ + if (! constp) + return FALSE; + + /* This should be a fixed size alignment. */ + gcc_assert (GET_CODE (align_rtx) == CONST_INT); + + align = INTVAL (align_rtx); + + /* Anything to move? */ + bytes = INTVAL (bytes_rtx); + if (bytes <= 0) + return TRUE; + + /* Don't support real large moves. */ + if (bytes > TOTAL_MOVE_REG*align) + return FALSE; + + /* Move the address into scratch registers. */ + dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0)); + src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); + + num_reg = offset = 0; + for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes)) + { + /* Calculate the correct offset for src/dest. */ + if (offset == 0) + { + src_addr = src_reg; + dest_addr = dest_reg; + } + else + { + src_addr = plus_constant (src_reg, offset); + dest_addr = plus_constant (dest_reg, offset); + } + + /* Generate the appropriate load and store, saving the stores + for later. */ + if (bytes >= 4 && align >= 4) + mode = SImode; + else if (bytes >= 2 && align >= 2) + mode = HImode; + else + mode = QImode; + + move_bytes = GET_MODE_SIZE (mode); + tmp_reg = gen_reg_rtx (mode); + src_mem = change_address (orig_src, mode, src_addr); + dest_mem = change_address (orig_dest, mode, dest_addr); + emit_insn (gen_rtx_SET (VOIDmode, tmp_reg, src_mem)); + stores[num_reg++] = gen_rtx_SET (VOIDmode, dest_mem, tmp_reg); + + if (num_reg >= MAX_MOVE_REG) + { + for (i = 0; i < num_reg; i++) + emit_insn (stores[i]); + num_reg = 0; + } + } + + for (i = 0; i < num_reg; i++) + emit_insn (stores[i]); + + return TRUE; +} + + +/* Expand a block clear operation, and return 1 if successful. Return 0 + if we should let the compiler generate normal code. + + operands[0] is the destination + operands[1] is the length + operands[3] is the alignment */ + +int +frv_expand_block_clear (rtx operands[]) +{ + rtx orig_dest = operands[0]; + rtx bytes_rtx = operands[1]; + rtx align_rtx = operands[3]; + int constp = (GET_CODE (bytes_rtx) == CONST_INT); + int align; + int bytes; + int offset; + rtx dest_reg; + rtx dest_addr; + rtx dest_mem; + int clear_bytes; + enum machine_mode mode; + + /* If this is not a fixed size move, just call memcpy. */ + if (! constp) + return FALSE; + + /* This should be a fixed size alignment. */ + gcc_assert (GET_CODE (align_rtx) == CONST_INT); + + align = INTVAL (align_rtx); + + /* Anything to move? */ + bytes = INTVAL (bytes_rtx); + if (bytes <= 0) + return TRUE; + + /* Don't support real large clears. */ + if (bytes > TOTAL_MOVE_REG*align) + return FALSE; + + /* Move the address into a scratch register. */ + dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0)); + + offset = 0; + for ( ; bytes > 0; (bytes -= clear_bytes), (offset += clear_bytes)) + { + /* Calculate the correct offset for src/dest. */ + dest_addr = ((offset == 0) + ? dest_reg + : plus_constant (dest_reg, offset)); + + /* Generate the appropriate store of gr0. */ + if (bytes >= 4 && align >= 4) + mode = SImode; + else if (bytes >= 2 && align >= 2) + mode = HImode; + else + mode = QImode; + + clear_bytes = GET_MODE_SIZE (mode); + dest_mem = change_address (orig_dest, mode, dest_addr); + emit_insn (gen_rtx_SET (VOIDmode, dest_mem, const0_rtx)); + } + + return TRUE; +} + + +/* The following variable is used to output modifiers of assembler + code of the current output insn. */ + +static rtx *frv_insn_operands; + +/* The following function is used to add assembler insn code suffix .p + if it is necessary. */ + +const char * +frv_asm_output_opcode (FILE *f, const char *ptr) +{ + int c; + + if (frv_insn_packing_flag <= 0) + return ptr; + + for (; *ptr && *ptr != ' ' && *ptr != '\t';) + { + c = *ptr++; + if (c == '%' && ((*ptr >= 'a' && *ptr <= 'z') + || (*ptr >= 'A' && *ptr <= 'Z'))) + { + int letter = *ptr++; + + c = atoi (ptr); + frv_print_operand (f, frv_insn_operands [c], letter); + while ((c = *ptr) >= '0' && c <= '9') + ptr++; + } + else + fputc (c, f); + } + + fprintf (f, ".p"); + + return ptr; +} + +/* Set up the packing bit for the current output insn. Note that this + function is not called for asm insns. */ + +void +frv_final_prescan_insn (rtx insn, rtx *opvec, + int noperands ATTRIBUTE_UNUSED) +{ + if (INSN_P (insn)) + { + if (frv_insn_packing_flag >= 0) + { + frv_insn_operands = opvec; + frv_insn_packing_flag = PACKING_FLAG_P (insn); + } + else if (recog_memoized (insn) >= 0 + && get_attr_acc_group (insn) == ACC_GROUP_ODD) + /* Packing optimizations have been disabled, but INSN can only + be issued in M1. Insert an mnop in M0. */ + fprintf (asm_out_file, "\tmnop.p\n"); + } +} + + + +/* A C expression whose value is RTL representing the address in a stack frame + where the pointer to the caller's frame is stored. Assume that FRAMEADDR is + an RTL expression for the address of the stack frame itself. + + If you don't define this macro, the default is to return the value of + FRAMEADDR--that is, the stack frame address is also the address of the stack + word that points to the previous frame. */ + +/* The default is correct, but we need to make sure the frame gets created. */ +rtx +frv_dynamic_chain_address (rtx frame) +{ + cfun->machine->frame_needed = 1; + return frame; +} + + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame, after the + prologue. FRAMEADDR is the frame pointer of the COUNT frame, or the frame + pointer of the COUNT - 1 frame if `RETURN_ADDR_IN_PREVIOUS_FRAME' is + defined. + + The value of the expression must always be the correct address when COUNT is + zero, but may be `NULL_RTX' if there is not way to determine the return + address of other frames. */ + +rtx +frv_return_addr_rtx (int count, rtx frame) +{ + if (count != 0) + return const0_rtx; + cfun->machine->frame_needed = 1; + return gen_rtx_MEM (Pmode, plus_constant (frame, 8)); +} + +/* Given a memory reference MEMREF, interpret the referenced memory as + an array of MODE values, and return a reference to the element + specified by INDEX. Assume that any pre-modification implicit in + MEMREF has already happened. + + MEMREF must be a legitimate operand for modes larger than SImode. + frv_legitimate_address_p forbids register+register addresses, which + this function cannot handle. */ +rtx +frv_index_memory (rtx memref, enum machine_mode mode, int index) +{ + rtx base = XEXP (memref, 0); + if (GET_CODE (base) == PRE_MODIFY) + base = XEXP (base, 0); + return change_address (memref, mode, + plus_constant (base, index * GET_MODE_SIZE (mode))); +} + + +/* Print a memory address as an operand to reference that memory location. */ +static void +frv_print_operand_address (FILE * stream, rtx x) +{ + if (GET_CODE (x) == MEM) + x = XEXP (x, 0); + + switch (GET_CODE (x)) + { + case REG: + fputs (reg_names [ REGNO (x)], stream); + return; + + case CONST_INT: + fprintf (stream, "%ld", (long) INTVAL (x)); + return; + + case SYMBOL_REF: + assemble_name (stream, XSTR (x, 0)); + return; + + case LABEL_REF: + case CONST: + output_addr_const (stream, x); + return; + + case PLUS: + /* Poorly constructed asm statements can trigger this alternative. + See gcc/testsuite/gcc.dg/asm-4.c for an example. */ + frv_print_operand_memory_reference (stream, x, 0); + return; + + default: + break; + } + + fatal_insn ("bad insn to frv_print_operand_address:", x); +} + + +static void +frv_print_operand_memory_reference_reg (FILE * stream, rtx x) +{ + int regno = true_regnum (x); + if (GPR_P (regno)) + fputs (reg_names[regno], stream); + else + fatal_insn ("bad register to frv_print_operand_memory_reference_reg:", x); +} + +/* Print a memory reference suitable for the ld/st instructions. */ + +static void +frv_print_operand_memory_reference (FILE * stream, rtx x, int addr_offset) +{ + struct frv_unspec unspec; + rtx x0 = NULL_RTX; + rtx x1 = NULL_RTX; + + switch (GET_CODE (x)) + { + case SUBREG: + case REG: + x0 = x; + break; + + case PRE_MODIFY: /* (pre_modify (reg) (plus (reg) (reg))) */ + x0 = XEXP (x, 0); + x1 = XEXP (XEXP (x, 1), 1); + break; + + case CONST_INT: + x1 = x; + break; + + case PLUS: + x0 = XEXP (x, 0); + x1 = XEXP (x, 1); + if (GET_CODE (x0) == CONST_INT) + { + x0 = XEXP (x, 1); + x1 = XEXP (x, 0); + } + break; + + default: + fatal_insn ("bad insn to frv_print_operand_memory_reference:", x); + break; + + } + + if (addr_offset) + { + if (!x1) + x1 = const0_rtx; + else if (GET_CODE (x1) != CONST_INT) + fatal_insn ("bad insn to frv_print_operand_memory_reference:", x); + } + + fputs ("@(", stream); + if (!x0) + fputs (reg_names[GPR_R0], stream); + else if (GET_CODE (x0) == REG || GET_CODE (x0) == SUBREG) + frv_print_operand_memory_reference_reg (stream, x0); + else + fatal_insn ("bad insn to frv_print_operand_memory_reference:", x); + + fputs (",", stream); + if (!x1) + fputs (reg_names [GPR_R0], stream); + + else + { + switch (GET_CODE (x1)) + { + case SUBREG: + case REG: + frv_print_operand_memory_reference_reg (stream, x1); + break; + + case CONST_INT: + fprintf (stream, "%ld", (long) (INTVAL (x1) + addr_offset)); + break; + + case CONST: + if (!frv_const_unspec_p (x1, &unspec)) + fatal_insn ("bad insn to frv_print_operand_memory_reference:", x1); + frv_output_const_unspec (stream, &unspec); + break; + + default: + fatal_insn ("bad insn to frv_print_operand_memory_reference:", x); + } + } + + fputs (")", stream); +} + + +/* Return 2 for likely branches and 0 for non-likely branches */ + +#define FRV_JUMP_LIKELY 2 +#define FRV_JUMP_NOT_LIKELY 0 + +static int +frv_print_operand_jump_hint (rtx insn) +{ + rtx note; + rtx labelref; + int ret; + HOST_WIDE_INT prob = -1; + enum { UNKNOWN, BACKWARD, FORWARD } jump_type = UNKNOWN; + + gcc_assert (GET_CODE (insn) == JUMP_INSN); + + /* Assume any non-conditional jump is likely. */ + if (! any_condjump_p (insn)) + ret = FRV_JUMP_LIKELY; + + else + { + labelref = condjump_label (insn); + if (labelref) + { + rtx label = XEXP (labelref, 0); + jump_type = (insn_current_address > INSN_ADDRESSES (INSN_UID (label)) + ? BACKWARD + : FORWARD); + } + + note = find_reg_note (insn, REG_BR_PROB, 0); + if (!note) + ret = ((jump_type == BACKWARD) ? FRV_JUMP_LIKELY : FRV_JUMP_NOT_LIKELY); + + else + { + prob = INTVAL (XEXP (note, 0)); + ret = ((prob >= (REG_BR_PROB_BASE / 2)) + ? FRV_JUMP_LIKELY + : FRV_JUMP_NOT_LIKELY); + } + } + +#if 0 + if (TARGET_DEBUG) + { + char *direction; + + switch (jump_type) + { + default: + case UNKNOWN: direction = "unknown jump direction"; break; + case BACKWARD: direction = "jump backward"; break; + case FORWARD: direction = "jump forward"; break; + } + + fprintf (stderr, + "%s: uid %ld, %s, probability = %ld, max prob. = %ld, hint = %d\n", + IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), + (long)INSN_UID (insn), direction, (long)prob, + (long)REG_BR_PROB_BASE, ret); + } +#endif + + return ret; +} + + +/* Return the comparison operator to use for CODE given that the ICC + register is OP0. */ + +static const char * +comparison_string (enum rtx_code code, rtx op0) +{ + bool is_nz_p = GET_MODE (op0) == CC_NZmode; + switch (code) + { + default: output_operand_lossage ("bad condition code"); + case EQ: return "eq"; + case NE: return "ne"; + case LT: return is_nz_p ? "n" : "lt"; + case LE: return "le"; + case GT: return "gt"; + case GE: return is_nz_p ? "p" : "ge"; + case LTU: return is_nz_p ? "no" : "c"; + case LEU: return is_nz_p ? "eq" : "ls"; + case GTU: return is_nz_p ? "ne" : "hi"; + case GEU: return is_nz_p ? "ra" : "nc"; + } +} + +/* Print an operand to an assembler instruction. + + `%' followed by a letter and a digit says to output an operand in an + alternate fashion. Four letters have standard, built-in meanings + described below. The hook `TARGET_PRINT_OPERAND' can define + additional letters with nonstandard meanings. + + `%cDIGIT' can be used to substitute an operand that is a constant value + without the syntax that normally indicates an immediate operand. + + `%nDIGIT' is like `%cDIGIT' except that the value of the constant is negated + before printing. + + `%aDIGIT' can be used to substitute an operand as if it were a memory + reference, with the actual operand treated as the address. This may be + useful when outputting a "load address" instruction, because often the + assembler syntax for such an instruction requires you to write the operand + as if it were a memory reference. + + `%lDIGIT' is used to substitute a `label_ref' into a jump instruction. + + `%=' outputs a number which is unique to each instruction in the entire + compilation. This is useful for making local labels to be referred to more + than once in a single template that generates multiple assembler + instructions. + + `%' followed by a punctuation character specifies a substitution that + does not use an operand. Only one case is standard: `%%' outputs a + `%' into the assembler code. Other nonstandard cases can be defined + in the `TARGET_PRINT_OPERAND' hook. You must also define which + punctuation characters are valid with the + `TARGET_PRINT_OPERAND_PUNCT_VALID_P' hook. */ + +static void +frv_print_operand (FILE * file, rtx x, int code) +{ + struct frv_unspec unspec; + HOST_WIDE_INT value; + int offset; + + if (code != 0 && !ISALPHA (code)) + value = 0; + + else if (GET_CODE (x) == CONST_INT) + value = INTVAL (x); + + else if (GET_CODE (x) == CONST_DOUBLE) + { + if (GET_MODE (x) == SFmode) + { + REAL_VALUE_TYPE rv; + long l; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, x); + REAL_VALUE_TO_TARGET_SINGLE (rv, l); + value = l; + } + + else if (GET_MODE (x) == VOIDmode) + value = CONST_DOUBLE_LOW (x); + + else + fatal_insn ("bad insn in frv_print_operand, bad const_double", x); + } + + else + value = 0; + + switch (code) + { + + case '.': + /* Output r0. */ + fputs (reg_names[GPR_R0], file); + break; + + case '#': + fprintf (file, "%d", frv_print_operand_jump_hint (current_output_insn)); + break; + + case '@': + /* Output small data area base register (gr16). */ + fputs (reg_names[SDA_BASE_REG], file); + break; + + case '~': + /* Output pic register (gr17). */ + fputs (reg_names[PIC_REGNO], file); + break; + + case '*': + /* Output the temporary integer CCR register. */ + fputs (reg_names[ICR_TEMP], file); + break; + + case '&': + /* Output the temporary integer CC register. */ + fputs (reg_names[ICC_TEMP], file); + break; + + /* case 'a': print an address. */ + + case 'C': + /* Print appropriate test for integer branch false operation. */ + fputs (comparison_string (reverse_condition (GET_CODE (x)), + XEXP (x, 0)), file); + break; + + case 'c': + /* Print appropriate test for integer branch true operation. */ + fputs (comparison_string (GET_CODE (x), XEXP (x, 0)), file); + break; + + case 'e': + /* Print 1 for a NE and 0 for an EQ to give the final argument + for a conditional instruction. */ + if (GET_CODE (x) == NE) + fputs ("1", file); + + else if (GET_CODE (x) == EQ) + fputs ("0", file); + + else + fatal_insn ("bad insn to frv_print_operand, 'e' modifier:", x); + break; + + case 'F': + /* Print appropriate test for floating point branch false operation. */ + switch (GET_CODE (x)) + { + default: + fatal_insn ("bad insn to frv_print_operand, 'F' modifier:", x); + + case EQ: fputs ("ne", file); break; + case NE: fputs ("eq", file); break; + case LT: fputs ("uge", file); break; + case LE: fputs ("ug", file); break; + case GT: fputs ("ule", file); break; + case GE: fputs ("ul", file); break; + } + break; + + case 'f': + /* Print appropriate test for floating point branch true operation. */ + switch (GET_CODE (x)) + { + default: + fatal_insn ("bad insn to frv_print_operand, 'f' modifier:", x); + + case EQ: fputs ("eq", file); break; + case NE: fputs ("ne", file); break; + case LT: fputs ("lt", file); break; + case LE: fputs ("le", file); break; + case GT: fputs ("gt", file); break; + case GE: fputs ("ge", file); break; + } + break; + + case 'g': + /* Print appropriate GOT function. */ + if (GET_CODE (x) != CONST_INT) + fatal_insn ("bad insn to frv_print_operand, 'g' modifier:", x); + fputs (unspec_got_name (INTVAL (x)), file); + break; + + case 'I': + /* Print 'i' if the operand is a constant, or is a memory reference that + adds a constant. */ + if (GET_CODE (x) == MEM) + x = ((GET_CODE (XEXP (x, 0)) == PLUS) + ? XEXP (XEXP (x, 0), 1) + : XEXP (x, 0)); + else if (GET_CODE (x) == PLUS) + x = XEXP (x, 1); + + switch (GET_CODE (x)) + { + default: + break; + + case CONST_INT: + case SYMBOL_REF: + case CONST: + fputs ("i", file); + break; + } + break; + + case 'i': + /* For jump instructions, print 'i' if the operand is a constant or + is an expression that adds a constant. */ + if (GET_CODE (x) == CONST_INT) + fputs ("i", file); + + else + { + if (GET_CODE (x) == CONST_INT + || (GET_CODE (x) == PLUS + && (GET_CODE (XEXP (x, 1)) == CONST_INT + || GET_CODE (XEXP (x, 0)) == CONST_INT))) + fputs ("i", file); + } + break; + + case 'L': + /* Print the lower register of a double word register pair */ + if (GET_CODE (x) == REG) + fputs (reg_names[ REGNO (x)+1 ], file); + else + fatal_insn ("bad insn to frv_print_operand, 'L' modifier:", x); + break; + + /* case 'l': print a LABEL_REF. */ + + case 'M': + case 'N': + /* Print a memory reference for ld/st/jmp, %N prints a memory reference + for the second word of double memory operations. */ + offset = (code == 'M') ? 0 : UNITS_PER_WORD; + switch (GET_CODE (x)) + { + default: + fatal_insn ("bad insn to frv_print_operand, 'M/N' modifier:", x); + + case MEM: + frv_print_operand_memory_reference (file, XEXP (x, 0), offset); + break; + + case REG: + case SUBREG: + case CONST_INT: + case PLUS: + case SYMBOL_REF: + frv_print_operand_memory_reference (file, x, offset); + break; + } + break; + + case 'O': + /* Print the opcode of a command. */ + switch (GET_CODE (x)) + { + default: + fatal_insn ("bad insn to frv_print_operand, 'O' modifier:", x); + + case PLUS: fputs ("add", file); break; + case MINUS: fputs ("sub", file); break; + case AND: fputs ("and", file); break; + case IOR: fputs ("or", file); break; + case XOR: fputs ("xor", file); break; + case ASHIFT: fputs ("sll", file); break; + case ASHIFTRT: fputs ("sra", file); break; + case LSHIFTRT: fputs ("srl", file); break; + } + break; + + /* case 'n': negate and print a constant int. */ + + case 'P': + /* Print PIC label using operand as the number. */ + if (GET_CODE (x) != CONST_INT) + fatal_insn ("bad insn to frv_print_operand, P modifier:", x); + + fprintf (file, ".LCF%ld", (long)INTVAL (x)); + break; + + case 'U': + /* Print 'u' if the operand is a update load/store. */ + if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PRE_MODIFY) + fputs ("u", file); + break; + + case 'z': + /* If value is 0, print gr0, otherwise it must be a register. */ + if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0) + fputs (reg_names[GPR_R0], file); + + else if (GET_CODE (x) == REG) + fputs (reg_names [REGNO (x)], file); + + else + fatal_insn ("bad insn in frv_print_operand, z case", x); + break; + + case 'x': + /* Print constant in hex. */ + if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE) + { + fprintf (file, "%s0x%.4lx", IMMEDIATE_PREFIX, (long) value); + break; + } + + /* Fall through. */ + + case '\0': + if (GET_CODE (x) == REG) + fputs (reg_names [REGNO (x)], file); + + else if (GET_CODE (x) == CONST_INT + || GET_CODE (x) == CONST_DOUBLE) + fprintf (file, "%s%ld", IMMEDIATE_PREFIX, (long) value); + + else if (frv_const_unspec_p (x, &unspec)) + frv_output_const_unspec (file, &unspec); + + else if (GET_CODE (x) == MEM) + frv_print_operand_address (file, XEXP (x, 0)); + + else if (CONSTANT_ADDRESS_P (x)) + frv_print_operand_address (file, x); + + else + fatal_insn ("bad insn in frv_print_operand, 0 case", x); + + break; + + default: + fatal_insn ("frv_print_operand: unknown code", x); + break; + } + + return; +} + +static bool +frv_print_operand_punct_valid_p (unsigned char code) +{ + return (code == '.' || code == '#' || code == '@' || code == '~' + || code == '*' || code == '&'); +} + + +/* A C statement (sans semicolon) for initializing the variable CUM for the + state at the beginning of the argument list. The variable has type + `CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type + of the function which will receive the args, or 0 if the args are to a + compiler support library function. The value of INDIRECT is nonzero when + processing an indirect call, for example a call through a function pointer. + The value of INDIRECT is zero for a call to an explicitly named function, a + library function call, or when `INIT_CUMULATIVE_ARGS' is used to find + arguments for the function being compiled. + + When processing a call to a compiler support library function, LIBNAME + identifies which one. It is a `symbol_ref' rtx which contains the name of + the function, as a string. LIBNAME is 0 when an ordinary C function call is + being processed. Thus, each time this macro is called, either LIBNAME or + FNTYPE is nonzero, but never both of them at once. */ + +void +frv_init_cumulative_args (CUMULATIVE_ARGS *cum, + tree fntype, + rtx libname, + tree fndecl, + int incoming) +{ + *cum = FIRST_ARG_REGNUM; + + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "\ninit_cumulative_args:"); + if (!fndecl && fntype) + fputs (" indirect", stderr); + + if (incoming) + fputs (" incoming", stderr); + + if (fntype) + { + tree ret_type = TREE_TYPE (fntype); + fprintf (stderr, " return=%s,", + tree_code_name[ (int)TREE_CODE (ret_type) ]); + } + + if (libname && GET_CODE (libname) == SYMBOL_REF) + fprintf (stderr, " libname=%s", XSTR (libname, 0)); + + if (cfun->returns_struct) + fprintf (stderr, " return-struct"); + + putc ('\n', stderr); + } +} + + +/* Return true if we should pass an argument on the stack rather than + in registers. */ + +static bool +frv_must_pass_in_stack (enum machine_mode mode, const_tree type) +{ + if (mode == BLKmode) + return true; + if (type == NULL) + return false; + return AGGREGATE_TYPE_P (type); +} + +/* If defined, a C expression that gives the alignment boundary, in bits, of an + argument with the specified mode and type. If it is not defined, + `PARM_BOUNDARY' is used for all arguments. */ + +static unsigned int +frv_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED, + const_tree type ATTRIBUTE_UNUSED) +{ + return BITS_PER_WORD; +} + +static rtx +frv_function_arg_1 (const CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, bool named, + bool incoming ATTRIBUTE_UNUSED) +{ + enum machine_mode xmode = (mode == BLKmode) ? SImode : mode; + int arg_num = *cum; + rtx ret; + const char *debstr; + + /* Return a marker for use in the call instruction. */ + if (xmode == VOIDmode) + { + ret = const0_rtx; + debstr = "<0>"; + } + + else if (arg_num <= LAST_ARG_REGNUM) + { + ret = gen_rtx_REG (xmode, arg_num); + debstr = reg_names[arg_num]; + } + + else + { + ret = NULL_RTX; + debstr = "memory"; + } + + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "function_arg: words = %2d, mode = %4s, named = %d, size = %3d, arg = %s\n", + arg_num, GET_MODE_NAME (mode), named, GET_MODE_SIZE (mode), debstr); + + return ret; +} + +static rtx +frv_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named) +{ + return frv_function_arg_1 (cum, mode, type, named, false); +} + +static rtx +frv_function_incoming_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named) +{ + return frv_function_arg_1 (cum, mode, type, named, true); +} + + +/* A C statement (sans semicolon) to update the summarizer variable CUM to + advance past an argument in the argument list. The values MODE, TYPE and + NAMED describe that argument. Once this is done, the variable CUM is + suitable for analyzing the *following* argument with `FUNCTION_ARG', etc. + + This macro need not do anything if the argument in question was passed on + the stack. The compiler knows how to track the amount of stack space used + for arguments without any special help. */ + +static void +frv_function_arg_advance (CUMULATIVE_ARGS *cum, + enum machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, + bool named) +{ + enum machine_mode xmode = (mode == BLKmode) ? SImode : mode; + int bytes = GET_MODE_SIZE (xmode); + int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + int arg_num = *cum; + + *cum = arg_num + words; + + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "function_adv: words = %2d, mode = %4s, named = %d, size = %3d\n", + arg_num, GET_MODE_NAME (mode), named, words * UNITS_PER_WORD); +} + + +/* A C expression for the number of words, at the beginning of an argument, + must be put in registers. The value must be zero for arguments that are + passed entirely in registers or that are entirely pushed on the stack. + + On some machines, certain arguments must be passed partially in registers + and partially in memory. On these machines, typically the first N words of + arguments are passed in registers, and the rest on the stack. If a + multi-word argument (a `double' or a structure) crosses that boundary, its + first few words must be passed in registers and the rest must be pushed. + This macro tells the compiler when this occurs, and how many of the words + should go in registers. + + `FUNCTION_ARG' for these arguments should return the first register to be + used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for + the called function. */ + +static int +frv_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type ATTRIBUTE_UNUSED, bool named ATTRIBUTE_UNUSED) +{ + enum machine_mode xmode = (mode == BLKmode) ? SImode : mode; + int bytes = GET_MODE_SIZE (xmode); + int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + int arg_num = *cum; + int ret; + + ret = ((arg_num <= LAST_ARG_REGNUM && arg_num + words > LAST_ARG_REGNUM+1) + ? LAST_ARG_REGNUM - arg_num + 1 + : 0); + ret *= UNITS_PER_WORD; + + if (TARGET_DEBUG_ARG && ret) + fprintf (stderr, "frv_arg_partial_bytes: %d\n", ret); + + return ret; +} + + +/* Implements TARGET_FUNCTION_VALUE. */ + +static rtx +frv_function_value (const_tree valtype, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (TYPE_MODE (valtype), RETURN_VALUE_REGNUM); +} + + +/* Implements TARGET_LIBCALL_VALUE. */ + +static rtx +frv_libcall_value (enum machine_mode mode, + const_rtx fun ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, RETURN_VALUE_REGNUM); +} + + +/* Implements FUNCTION_VALUE_REGNO_P. */ + +bool +frv_function_value_regno_p (const unsigned int regno) +{ + return (regno == RETURN_VALUE_REGNUM); +} + +/* Return true if a register is ok to use as a base or index register. */ + +static FRV_INLINE int +frv_regno_ok_for_base_p (int regno, int strict_p) +{ + if (GPR_P (regno)) + return TRUE; + + if (strict_p) + return (reg_renumber[regno] >= 0 && GPR_P (reg_renumber[regno])); + + if (regno == ARG_POINTER_REGNUM) + return TRUE; + + return (regno >= FIRST_PSEUDO_REGISTER); +} + + +/* A C compound statement with a conditional `goto LABEL;' executed if X (an + RTX) is a legitimate memory address on the target machine for a memory + operand of mode MODE. + + It usually pays to define several simpler macros to serve as subroutines for + this one. Otherwise it may be too complicated to understand. + + This macro must exist in two variants: a strict variant and a non-strict + one. The strict variant is used in the reload pass. It must be defined so + that any pseudo-register that has not been allocated a hard register is + considered a memory reference. In contexts where some kind of register is + required, a pseudo-register with no hard register must be rejected. + + The non-strict variant is used in other passes. It must be defined to + accept all pseudo-registers in every context where some kind of register is + required. + + Compiler source files that want to use the strict variant of this macro + define the macro `REG_OK_STRICT'. You should use an `#ifdef REG_OK_STRICT' + conditional to define the strict variant in that case and the non-strict + variant otherwise. + + Normally, constant addresses which are the sum of a `symbol_ref' and an + integer are stored inside a `const' RTX to mark them as constant. + Therefore, there is no need to recognize such sums specifically as + legitimate addresses. Normally you would simply recognize any `const' as + legitimate. + + Usually `TARGET_PRINT_OPERAND_ADDRESS' is not prepared to handle + constant sums that are not marked with `const'. It assumes that a + naked `plus' indicates indexing. If so, then you *must* reject such + naked constant sums as illegitimate addresses, so that none of them + will be given to `TARGET_PRINT_OPERAND_ADDRESS'. */ + +int +frv_legitimate_address_p_1 (enum machine_mode mode, + rtx x, + int strict_p, + int condexec_p, + int allow_double_reg_p) +{ + rtx x0, x1; + int ret = 0; + HOST_WIDE_INT value; + unsigned regno0; + + if (FRV_SYMBOL_REF_TLS_P (x)) + return 0; + + switch (GET_CODE (x)) + { + default: + break; + + case SUBREG: + x = SUBREG_REG (x); + if (GET_CODE (x) != REG) + break; + + /* Fall through. */ + + case REG: + ret = frv_regno_ok_for_base_p (REGNO (x), strict_p); + break; + + case PRE_MODIFY: + x0 = XEXP (x, 0); + x1 = XEXP (x, 1); + if (GET_CODE (x0) != REG + || ! frv_regno_ok_for_base_p (REGNO (x0), strict_p) + || GET_CODE (x1) != PLUS + || ! rtx_equal_p (x0, XEXP (x1, 0)) + || GET_CODE (XEXP (x1, 1)) != REG + || ! frv_regno_ok_for_base_p (REGNO (XEXP (x1, 1)), strict_p)) + break; + + ret = 1; + break; + + case CONST_INT: + /* 12-bit immediate */ + if (condexec_p) + ret = FALSE; + else + { + ret = IN_RANGE (INTVAL (x), -2048, 2047); + + /* If we can't use load/store double operations, make sure we can + address the second word. */ + if (ret && GET_MODE_SIZE (mode) > UNITS_PER_WORD) + ret = IN_RANGE (INTVAL (x) + GET_MODE_SIZE (mode) - 1, + -2048, 2047); + } + break; + + case PLUS: + x0 = XEXP (x, 0); + x1 = XEXP (x, 1); + + if (GET_CODE (x0) == SUBREG) + x0 = SUBREG_REG (x0); + + if (GET_CODE (x0) != REG) + break; + + regno0 = REGNO (x0); + if (!frv_regno_ok_for_base_p (regno0, strict_p)) + break; + + switch (GET_CODE (x1)) + { + default: + break; + + case SUBREG: + x1 = SUBREG_REG (x1); + if (GET_CODE (x1) != REG) + break; + + /* Fall through. */ + + case REG: + /* Do not allow reg+reg addressing for modes > 1 word if we + can't depend on having move double instructions. */ + if (!allow_double_reg_p && GET_MODE_SIZE (mode) > UNITS_PER_WORD) + ret = FALSE; + else + ret = frv_regno_ok_for_base_p (REGNO (x1), strict_p); + break; + + case CONST_INT: + /* 12-bit immediate */ + if (condexec_p) + ret = FALSE; + else + { + value = INTVAL (x1); + ret = IN_RANGE (value, -2048, 2047); + + /* If we can't use load/store double operations, make sure we can + address the second word. */ + if (ret && GET_MODE_SIZE (mode) > UNITS_PER_WORD) + ret = IN_RANGE (value + GET_MODE_SIZE (mode) - 1, -2048, 2047); + } + break; + + case CONST: + if (!condexec_p && got12_operand (x1, VOIDmode)) + ret = TRUE; + break; + + } + break; + } + + if (TARGET_DEBUG_ADDR) + { + fprintf (stderr, "\n========== legitimate_address_p, mode = %s, result = %d, addresses are %sstrict%s\n", + GET_MODE_NAME (mode), ret, (strict_p) ? "" : "not ", + (condexec_p) ? ", inside conditional code" : ""); + debug_rtx (x); + } + + return ret; +} + +bool +frv_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p) +{ + return frv_legitimate_address_p_1 (mode, x, strict_p, FALSE, FALSE); +} + +/* Given an ADDR, generate code to inline the PLT. */ +static rtx +gen_inlined_tls_plt (rtx addr) +{ + rtx retval, dest; + rtx picreg = get_hard_reg_initial_val (Pmode, FDPIC_REG); + + + dest = gen_reg_rtx (DImode); + + if (flag_pic == 1) + { + /* + -fpic version: + + lddi.p @(gr15, #gottlsdesc12(ADDR)), gr8 + calll #gettlsoff(ADDR)@(gr8, gr0) + */ + emit_insn (gen_tls_lddi (dest, addr, picreg)); + } + else + { + /* + -fPIC version: + + sethi.p #gottlsdeschi(ADDR), gr8 + setlo #gottlsdesclo(ADDR), gr8 + ldd #tlsdesc(ADDR)@(gr15, gr8), gr8 + calll #gettlsoff(ADDR)@(gr8, gr0) + */ + rtx reguse = gen_reg_rtx (Pmode); + emit_insn (gen_tlsoff_hilo (reguse, addr, GEN_INT (R_FRV_GOTTLSDESCHI))); + emit_insn (gen_tls_tlsdesc_ldd (dest, picreg, reguse, addr)); + } + + retval = gen_reg_rtx (Pmode); + emit_insn (gen_tls_indirect_call (retval, addr, dest, picreg)); + return retval; +} + +/* Emit a TLSMOFF or TLSMOFF12 offset, depending on -mTLS. Returns + the destination address. */ +static rtx +gen_tlsmoff (rtx addr, rtx reg) +{ + rtx dest = gen_reg_rtx (Pmode); + + if (TARGET_BIG_TLS) + { + /* sethi.p #tlsmoffhi(x), grA + setlo #tlsmofflo(x), grA + */ + dest = gen_reg_rtx (Pmode); + emit_insn (gen_tlsoff_hilo (dest, addr, + GEN_INT (R_FRV_TLSMOFFHI))); + dest = gen_rtx_PLUS (Pmode, dest, reg); + } + else + { + /* addi grB, #tlsmoff12(x), grC + -or- + ld/st @(grB, #tlsmoff12(x)), grC + */ + dest = gen_reg_rtx (Pmode); + emit_insn (gen_symGOTOFF2reg_i (dest, addr, reg, + GEN_INT (R_FRV_TLSMOFF12))); + } + return dest; +} + +/* Generate code for a TLS address. */ +static rtx +frv_legitimize_tls_address (rtx addr, enum tls_model model) +{ + rtx dest, tp = gen_rtx_REG (Pmode, 29); + rtx picreg = get_hard_reg_initial_val (Pmode, 15); + + switch (model) + { + case TLS_MODEL_INITIAL_EXEC: + if (flag_pic == 1) + { + /* -fpic version. + ldi @(gr15, #gottlsoff12(x)), gr5 + */ + dest = gen_reg_rtx (Pmode); + emit_insn (gen_tls_load_gottlsoff12 (dest, addr, picreg)); + dest = gen_rtx_PLUS (Pmode, tp, dest); + } + else + { + /* -fPIC or anything else. + + sethi.p #gottlsoffhi(x), gr14 + setlo #gottlsofflo(x), gr14 + ld #tlsoff(x)@(gr15, gr14), gr9 + */ + rtx tmp = gen_reg_rtx (Pmode); + dest = gen_reg_rtx (Pmode); + emit_insn (gen_tlsoff_hilo (tmp, addr, + GEN_INT (R_FRV_GOTTLSOFF_HI))); + + emit_insn (gen_tls_tlsoff_ld (dest, picreg, tmp, addr)); + dest = gen_rtx_PLUS (Pmode, tp, dest); + } + break; + case TLS_MODEL_LOCAL_DYNAMIC: + { + rtx reg, retval; + + if (TARGET_INLINE_PLT) + retval = gen_inlined_tls_plt (GEN_INT (0)); + else + { + /* call #gettlsoff(0) */ + retval = gen_reg_rtx (Pmode); + emit_insn (gen_call_gettlsoff (retval, GEN_INT (0), picreg)); + } + + reg = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, reg, + gen_rtx_PLUS (Pmode, + retval, tp))); + + dest = gen_tlsmoff (addr, reg); + + /* + dest = gen_reg_rtx (Pmode); + emit_insn (gen_tlsoff_hilo (dest, addr, + GEN_INT (R_FRV_TLSMOFFHI))); + dest = gen_rtx_PLUS (Pmode, dest, reg); + */ + break; + } + case TLS_MODEL_LOCAL_EXEC: + dest = gen_tlsmoff (addr, gen_rtx_REG (Pmode, 29)); + break; + case TLS_MODEL_GLOBAL_DYNAMIC: + { + rtx retval; + + if (TARGET_INLINE_PLT) + retval = gen_inlined_tls_plt (addr); + else + { + /* call #gettlsoff(x) */ + retval = gen_reg_rtx (Pmode); + emit_insn (gen_call_gettlsoff (retval, addr, picreg)); + } + dest = gen_rtx_PLUS (Pmode, retval, tp); + break; + } + default: + gcc_unreachable (); + } + + return dest; +} + +rtx +frv_legitimize_address (rtx x, + rtx oldx ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED) +{ + if (GET_CODE (x) == SYMBOL_REF) + { + enum tls_model model = SYMBOL_REF_TLS_MODEL (x); + if (model != 0) + return frv_legitimize_tls_address (x, model); + } + + return x; +} + +/* Test whether a local function descriptor is canonical, i.e., + whether we can use FUNCDESC_GOTOFF to compute the address of the + function. */ + +static bool +frv_local_funcdesc_p (rtx fnx) +{ + tree fn; + enum symbol_visibility vis; + bool ret; + + if (! SYMBOL_REF_LOCAL_P (fnx)) + return FALSE; + + fn = SYMBOL_REF_DECL (fnx); + + if (! fn) + return FALSE; + + vis = DECL_VISIBILITY (fn); + + if (vis == VISIBILITY_PROTECTED) + /* Private function descriptors for protected functions are not + canonical. Temporarily change the visibility to global. */ + vis = VISIBILITY_DEFAULT; + else if (flag_shlib) + /* If we're already compiling for a shared library (that, unlike + executables, can't assume that the existence of a definition + implies local binding), we can skip the re-testing. */ + return TRUE; + + ret = default_binds_local_p_1 (fn, flag_pic); + + DECL_VISIBILITY (fn) = vis; + + return ret; +} + +/* Load the _gp symbol into DEST. SRC is supposed to be the FDPIC + register. */ + +rtx +frv_gen_GPsym2reg (rtx dest, rtx src) +{ + tree gp = get_identifier ("_gp"); + rtx gp_sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (gp)); + + return gen_symGOT2reg (dest, gp_sym, src, GEN_INT (R_FRV_GOT12)); +} + +static const char * +unspec_got_name (int i) +{ + switch (i) + { + case R_FRV_GOT12: return "got12"; + case R_FRV_GOTHI: return "gothi"; + case R_FRV_GOTLO: return "gotlo"; + case R_FRV_FUNCDESC: return "funcdesc"; + case R_FRV_FUNCDESC_GOT12: return "gotfuncdesc12"; + case R_FRV_FUNCDESC_GOTHI: return "gotfuncdeschi"; + case R_FRV_FUNCDESC_GOTLO: return "gotfuncdesclo"; + case R_FRV_FUNCDESC_VALUE: return "funcdescvalue"; + case R_FRV_FUNCDESC_GOTOFF12: return "gotofffuncdesc12"; + case R_FRV_FUNCDESC_GOTOFFHI: return "gotofffuncdeschi"; + case R_FRV_FUNCDESC_GOTOFFLO: return "gotofffuncdesclo"; + case R_FRV_GOTOFF12: return "gotoff12"; + case R_FRV_GOTOFFHI: return "gotoffhi"; + case R_FRV_GOTOFFLO: return "gotofflo"; + case R_FRV_GPREL12: return "gprel12"; + case R_FRV_GPRELHI: return "gprelhi"; + case R_FRV_GPRELLO: return "gprello"; + case R_FRV_GOTTLSOFF_HI: return "gottlsoffhi"; + case R_FRV_GOTTLSOFF_LO: return "gottlsofflo"; + case R_FRV_TLSMOFFHI: return "tlsmoffhi"; + case R_FRV_TLSMOFFLO: return "tlsmofflo"; + case R_FRV_TLSMOFF12: return "tlsmoff12"; + case R_FRV_TLSDESCHI: return "tlsdeschi"; + case R_FRV_TLSDESCLO: return "tlsdesclo"; + case R_FRV_GOTTLSDESCHI: return "gottlsdeschi"; + case R_FRV_GOTTLSDESCLO: return "gottlsdesclo"; + default: gcc_unreachable (); + } +} + +/* Write the assembler syntax for UNSPEC to STREAM. Note that any offset + is added inside the relocation operator. */ + +static void +frv_output_const_unspec (FILE *stream, const struct frv_unspec *unspec) +{ + fprintf (stream, "#%s(", unspec_got_name (unspec->reloc)); + output_addr_const (stream, plus_constant (unspec->symbol, unspec->offset)); + fputs (")", stream); +} + +/* Implement FIND_BASE_TERM. See whether ORIG_X represents #gprel12(foo) + or #gotoff12(foo) for some small data symbol foo. If so, return foo, + otherwise return ORIG_X. */ + +rtx +frv_find_base_term (rtx x) +{ + struct frv_unspec unspec; + + if (frv_const_unspec_p (x, &unspec) + && frv_small_data_reloc_p (unspec.symbol, unspec.reloc)) + return plus_constant (unspec.symbol, unspec.offset); + + return x; +} + +/* Return 1 if operand is a valid FRV address. CONDEXEC_P is true if + the operand is used by a predicated instruction. */ + +int +frv_legitimate_memory_operand (rtx op, enum machine_mode mode, int condexec_p) +{ + return ((GET_MODE (op) == mode || mode == VOIDmode) + && GET_CODE (op) == MEM + && frv_legitimate_address_p_1 (mode, XEXP (op, 0), + reload_completed, condexec_p, FALSE)); +} + +void +frv_expand_fdpic_call (rtx *operands, bool ret_value, bool sibcall) +{ + rtx lr = gen_rtx_REG (Pmode, LR_REGNO); + rtx picreg = get_hard_reg_initial_val (SImode, FDPIC_REG); + rtx c, rvrtx=0; + rtx addr; + + if (ret_value) + { + rvrtx = operands[0]; + operands ++; + } + + addr = XEXP (operands[0], 0); + + /* Inline PLTs if we're optimizing for speed. We'd like to inline + any calls that would involve a PLT, but can't tell, since we + don't know whether an extern function is going to be provided by + a separate translation unit or imported from a separate module. + When compiling for shared libraries, if the function has default + visibility, we assume it's overridable, so we inline the PLT, but + for executables, we don't really have a way to make a good + decision: a function is as likely to be imported from a shared + library as it is to be defined in the executable itself. We + assume executables will get global functions defined locally, + whereas shared libraries will have them potentially overridden, + so we only inline PLTs when compiling for shared libraries. + + In order to mark a function as local to a shared library, any + non-default visibility attribute suffices. Unfortunately, + there's no simple way to tag a function declaration as ``in a + different module'', which we could then use to trigger PLT + inlining on executables. There's -minline-plt, but it affects + all external functions, so one would have to also mark function + declarations available in the same module with non-default + visibility, which is advantageous in itself. */ + if (GET_CODE (addr) == SYMBOL_REF + && ((!SYMBOL_REF_LOCAL_P (addr) && TARGET_INLINE_PLT) + || sibcall)) + { + rtx x, dest; + dest = gen_reg_rtx (SImode); + if (flag_pic != 1) + x = gen_symGOTOFF2reg_hilo (dest, addr, OUR_FDPIC_REG, + GEN_INT (R_FRV_FUNCDESC_GOTOFF12)); + else + x = gen_symGOTOFF2reg (dest, addr, OUR_FDPIC_REG, + GEN_INT (R_FRV_FUNCDESC_GOTOFF12)); + emit_insn (x); + crtl->uses_pic_offset_table = TRUE; + addr = dest; + } + else if (GET_CODE (addr) == SYMBOL_REF) + { + /* These are always either local, or handled through a local + PLT. */ + if (ret_value) + c = gen_call_value_fdpicsi (rvrtx, addr, operands[1], + operands[2], picreg, lr); + else + c = gen_call_fdpicsi (addr, operands[1], operands[2], picreg, lr); + emit_call_insn (c); + return; + } + else if (! ldd_address_operand (addr, Pmode)) + addr = force_reg (Pmode, addr); + + picreg = gen_reg_rtx (DImode); + emit_insn (gen_movdi_ldd (picreg, addr)); + + if (sibcall && ret_value) + c = gen_sibcall_value_fdpicdi (rvrtx, picreg, const0_rtx); + else if (sibcall) + c = gen_sibcall_fdpicdi (picreg, const0_rtx); + else if (ret_value) + c = gen_call_value_fdpicdi (rvrtx, picreg, const0_rtx, lr); + else + c = gen_call_fdpicdi (picreg, const0_rtx, lr); + emit_call_insn (c); +} + +/* Look for a SYMBOL_REF of a function in an rtx. We always want to + process these separately from any offsets, such that we add any + offsets to the function descriptor (the actual pointer), not to the + function address. */ + +static bool +frv_function_symbol_referenced_p (rtx x) +{ + const char *format; + int length; + int j; + + if (GET_CODE (x) == SYMBOL_REF) + return SYMBOL_REF_FUNCTION_P (x); + + length = GET_RTX_LENGTH (GET_CODE (x)); + format = GET_RTX_FORMAT (GET_CODE (x)); + + for (j = 0; j < length; ++j) + { + switch (format[j]) + { + case 'e': + if (frv_function_symbol_referenced_p (XEXP (x, j))) + return TRUE; + break; + + case 'V': + case 'E': + if (XVEC (x, j) != 0) + { + int k; + for (k = 0; k < XVECLEN (x, j); ++k) + if (frv_function_symbol_referenced_p (XVECEXP (x, j, k))) + return TRUE; + } + break; + + default: + /* Nothing to do. */ + break; + } + } + + return FALSE; +} + +/* Return true if the memory operand is one that can be conditionally + executed. */ + +int +condexec_memory_operand (rtx op, enum machine_mode mode) +{ + enum machine_mode op_mode = GET_MODE (op); + rtx addr; + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (op_mode) + { + default: + return FALSE; + + case QImode: + case HImode: + case SImode: + case SFmode: + break; + } + + if (GET_CODE (op) != MEM) + return FALSE; + + addr = XEXP (op, 0); + return frv_legitimate_address_p_1 (mode, addr, reload_completed, TRUE, FALSE); +} + +/* Return true if the bare return instruction can be used outside of the + epilog code. For frv, we only do it if there was no stack allocation. */ + +int +direct_return_p (void) +{ + frv_stack_t *info; + + if (!reload_completed) + return FALSE; + + info = frv_stack_info (); + return (info->total_size == 0); +} + + +void +frv_emit_move (enum machine_mode mode, rtx dest, rtx src) +{ + if (GET_CODE (src) == SYMBOL_REF) + { + enum tls_model model = SYMBOL_REF_TLS_MODEL (src); + if (model != 0) + src = frv_legitimize_tls_address (src, model); + } + + switch (mode) + { + case SImode: + if (frv_emit_movsi (dest, src)) + return; + break; + + case QImode: + case HImode: + case DImode: + case SFmode: + case DFmode: + if (!reload_in_progress + && !reload_completed + && !register_operand (dest, mode) + && !reg_or_0_operand (src, mode)) + src = copy_to_mode_reg (mode, src); + break; + + default: + gcc_unreachable (); + } + + emit_insn (gen_rtx_SET (VOIDmode, dest, src)); +} + +/* Emit code to handle a MOVSI, adding in the small data register or pic + register if needed to load up addresses. Return TRUE if the appropriate + instructions are emitted. */ + +int +frv_emit_movsi (rtx dest, rtx src) +{ + int base_regno = -1; + int unspec = 0; + rtx sym = src; + struct frv_unspec old_unspec; + + if (!reload_in_progress + && !reload_completed + && !register_operand (dest, SImode) + && (!reg_or_0_operand (src, SImode) + /* Virtual registers will almost always be replaced by an + add instruction, so expose this to CSE by copying to + an intermediate register. */ + || (GET_CODE (src) == REG + && IN_RANGE (REGNO (src), + FIRST_VIRTUAL_REGISTER, + LAST_VIRTUAL_POINTER_REGISTER)))) + { + emit_insn (gen_rtx_SET (VOIDmode, dest, copy_to_mode_reg (SImode, src))); + return TRUE; + } + + /* Explicitly add in the PIC or small data register if needed. */ + switch (GET_CODE (src)) + { + default: + break; + + case LABEL_REF: + handle_label: + if (TARGET_FDPIC) + { + /* Using GPREL12, we use a single GOT entry for all symbols + in read-only sections, but trade sequences such as: + + sethi #gothi(label), gr# + setlo #gotlo(label), gr# + ld @(gr15,gr#), gr# + + for + + ld @(gr15,#got12(_gp)), gr# + sethi #gprelhi(label), gr## + setlo #gprello(label), gr## + add gr#, gr##, gr## + + We may often be able to share gr# for multiple + computations of GPREL addresses, and we may often fold + the final add into the pair of registers of a load or + store instruction, so it's often profitable. Even when + optimizing for size, we're trading a GOT entry for an + additional instruction, which trades GOT space + (read-write) for code size (read-only, shareable), as + long as the symbol is not used in more than two different + locations. + + With -fpie/-fpic, we'd be trading a single load for a + sequence of 4 instructions, because the offset of the + label can't be assumed to be addressable with 12 bits, so + we don't do this. */ + if (TARGET_GPREL_RO) + unspec = R_FRV_GPREL12; + else + unspec = R_FRV_GOT12; + } + else if (flag_pic) + base_regno = PIC_REGNO; + + break; + + case CONST: + if (frv_const_unspec_p (src, &old_unspec)) + break; + + if (TARGET_FDPIC && frv_function_symbol_referenced_p (XEXP (src, 0))) + { + handle_whatever: + src = force_reg (GET_MODE (XEXP (src, 0)), XEXP (src, 0)); + emit_move_insn (dest, src); + return TRUE; + } + else + { + sym = XEXP (sym, 0); + if (GET_CODE (sym) == PLUS + && GET_CODE (XEXP (sym, 0)) == SYMBOL_REF + && GET_CODE (XEXP (sym, 1)) == CONST_INT) + sym = XEXP (sym, 0); + if (GET_CODE (sym) == SYMBOL_REF) + goto handle_sym; + else if (GET_CODE (sym) == LABEL_REF) + goto handle_label; + else + goto handle_whatever; + } + break; + + case SYMBOL_REF: + handle_sym: + if (TARGET_FDPIC) + { + enum tls_model model = SYMBOL_REF_TLS_MODEL (sym); + + if (model != 0) + { + src = frv_legitimize_tls_address (src, model); + emit_move_insn (dest, src); + return TRUE; + } + + if (SYMBOL_REF_FUNCTION_P (sym)) + { + if (frv_local_funcdesc_p (sym)) + unspec = R_FRV_FUNCDESC_GOTOFF12; + else + unspec = R_FRV_FUNCDESC_GOT12; + } + else + { + if (CONSTANT_POOL_ADDRESS_P (sym)) + switch (GET_CODE (get_pool_constant (sym))) + { + case CONST: + case SYMBOL_REF: + case LABEL_REF: + if (flag_pic) + { + unspec = R_FRV_GOTOFF12; + break; + } + /* Fall through. */ + default: + if (TARGET_GPREL_RO) + unspec = R_FRV_GPREL12; + else + unspec = R_FRV_GOT12; + break; + } + else if (SYMBOL_REF_LOCAL_P (sym) + && !SYMBOL_REF_EXTERNAL_P (sym) + && SYMBOL_REF_DECL (sym) + && (!DECL_P (SYMBOL_REF_DECL (sym)) + || !DECL_COMMON (SYMBOL_REF_DECL (sym)))) + { + tree decl = SYMBOL_REF_DECL (sym); + tree init = TREE_CODE (decl) == VAR_DECL + ? DECL_INITIAL (decl) + : TREE_CODE (decl) == CONSTRUCTOR + ? decl : 0; + int reloc = 0; + bool named_section, readonly; + + if (init && init != error_mark_node) + reloc = compute_reloc_for_constant (init); + + named_section = TREE_CODE (decl) == VAR_DECL + && lookup_attribute ("section", DECL_ATTRIBUTES (decl)); + readonly = decl_readonly_section (decl, reloc); + + if (named_section) + unspec = R_FRV_GOT12; + else if (!readonly) + unspec = R_FRV_GOTOFF12; + else if (readonly && TARGET_GPREL_RO) + unspec = R_FRV_GPREL12; + else + unspec = R_FRV_GOT12; + } + else + unspec = R_FRV_GOT12; + } + } + + else if (SYMBOL_REF_SMALL_P (sym)) + base_regno = SDA_BASE_REG; + + else if (flag_pic) + base_regno = PIC_REGNO; + + break; + } + + if (base_regno >= 0) + { + if (GET_CODE (sym) == SYMBOL_REF && SYMBOL_REF_SMALL_P (sym)) + emit_insn (gen_symGOTOFF2reg (dest, src, + gen_rtx_REG (Pmode, base_regno), + GEN_INT (R_FRV_GPREL12))); + else + emit_insn (gen_symGOTOFF2reg_hilo (dest, src, + gen_rtx_REG (Pmode, base_regno), + GEN_INT (R_FRV_GPREL12))); + if (base_regno == PIC_REGNO) + crtl->uses_pic_offset_table = TRUE; + return TRUE; + } + + if (unspec) + { + rtx x; + + /* Since OUR_FDPIC_REG is a pseudo register, we can't safely introduce + new uses of it once reload has begun. */ + gcc_assert (!reload_in_progress && !reload_completed); + + switch (unspec) + { + case R_FRV_GOTOFF12: + if (!frv_small_data_reloc_p (sym, unspec)) + x = gen_symGOTOFF2reg_hilo (dest, src, OUR_FDPIC_REG, + GEN_INT (unspec)); + else + x = gen_symGOTOFF2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec)); + break; + case R_FRV_GPREL12: + if (!frv_small_data_reloc_p (sym, unspec)) + x = gen_symGPREL2reg_hilo (dest, src, OUR_FDPIC_REG, + GEN_INT (unspec)); + else + x = gen_symGPREL2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec)); + break; + case R_FRV_FUNCDESC_GOTOFF12: + if (flag_pic != 1) + x = gen_symGOTOFF2reg_hilo (dest, src, OUR_FDPIC_REG, + GEN_INT (unspec)); + else + x = gen_symGOTOFF2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec)); + break; + default: + if (flag_pic != 1) + x = gen_symGOT2reg_hilo (dest, src, OUR_FDPIC_REG, + GEN_INT (unspec)); + else + x = gen_symGOT2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec)); + break; + } + emit_insn (x); + crtl->uses_pic_offset_table = TRUE; + return TRUE; + } + + + return FALSE; +} + + +/* Return a string to output a single word move. */ + +const char * +output_move_single (rtx operands[], rtx insn) +{ + rtx dest = operands[0]; + rtx src = operands[1]; + + if (GET_CODE (dest) == REG) + { + int dest_regno = REGNO (dest); + enum machine_mode mode = GET_MODE (dest); + + if (GPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* gpr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + return "mov %1, %0"; + + else if (FPR_P (src_regno)) + return "movfg %1, %0"; + + else if (SPR_P (src_regno)) + return "movsg %1, %0"; + } + + else if (GET_CODE (src) == MEM) + { + /* gpr <- memory */ + switch (mode) + { + default: + break; + + case QImode: + return "ldsb%I1%U1 %M1,%0"; + + case HImode: + return "ldsh%I1%U1 %M1,%0"; + + case SImode: + case SFmode: + return "ld%I1%U1 %M1, %0"; + } + } + + else if (GET_CODE (src) == CONST_INT + || GET_CODE (src) == CONST_DOUBLE) + { + /* gpr <- integer/floating constant */ + HOST_WIDE_INT value; + + if (GET_CODE (src) == CONST_INT) + value = INTVAL (src); + + else if (mode == SFmode) + { + REAL_VALUE_TYPE rv; + long l; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, src); + REAL_VALUE_TO_TARGET_SINGLE (rv, l); + value = l; + } + + else + value = CONST_DOUBLE_LOW (src); + + if (IN_RANGE (value, -32768, 32767)) + return "setlos %1, %0"; + + return "#"; + } + + else if (GET_CODE (src) == SYMBOL_REF + || GET_CODE (src) == LABEL_REF + || GET_CODE (src) == CONST) + { + return "#"; + } + } + + else if (FPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* fpr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + return "movgf %1, %0"; + + else if (FPR_P (src_regno)) + { + if (TARGET_HARD_FLOAT) + return "fmovs %1, %0"; + else + return "mor %1, %1, %0"; + } + } + + else if (GET_CODE (src) == MEM) + { + /* fpr <- memory */ + switch (mode) + { + default: + break; + + case QImode: + return "ldbf%I1%U1 %M1,%0"; + + case HImode: + return "ldhf%I1%U1 %M1,%0"; + + case SImode: + case SFmode: + return "ldf%I1%U1 %M1, %0"; + } + } + + else if (ZERO_P (src)) + return "movgf %., %0"; + } + + else if (SPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* spr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + return "movgs %1, %0"; + } + else if (ZERO_P (src)) + return "movgs %., %0"; + } + } + + else if (GET_CODE (dest) == MEM) + { + if (GET_CODE (src) == REG) + { + int src_regno = REGNO (src); + enum machine_mode mode = GET_MODE (dest); + + if (GPR_P (src_regno)) + { + switch (mode) + { + default: + break; + + case QImode: + return "stb%I0%U0 %1, %M0"; + + case HImode: + return "sth%I0%U0 %1, %M0"; + + case SImode: + case SFmode: + return "st%I0%U0 %1, %M0"; + } + } + + else if (FPR_P (src_regno)) + { + switch (mode) + { + default: + break; + + case QImode: + return "stbf%I0%U0 %1, %M0"; + + case HImode: + return "sthf%I0%U0 %1, %M0"; + + case SImode: + case SFmode: + return "stf%I0%U0 %1, %M0"; + } + } + } + + else if (ZERO_P (src)) + { + switch (GET_MODE (dest)) + { + default: + break; + + case QImode: + return "stb%I0%U0 %., %M0"; + + case HImode: + return "sth%I0%U0 %., %M0"; + + case SImode: + case SFmode: + return "st%I0%U0 %., %M0"; + } + } + } + + fatal_insn ("bad output_move_single operand", insn); + return ""; +} + + +/* Return a string to output a double word move. */ + +const char * +output_move_double (rtx operands[], rtx insn) +{ + rtx dest = operands[0]; + rtx src = operands[1]; + enum machine_mode mode = GET_MODE (dest); + + if (GET_CODE (dest) == REG) + { + int dest_regno = REGNO (dest); + + if (GPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* gpr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + return "#"; + + else if (FPR_P (src_regno)) + { + if (((dest_regno - GPR_FIRST) & 1) == 0 + && ((src_regno - FPR_FIRST) & 1) == 0) + return "movfgd %1, %0"; + + return "#"; + } + } + + else if (GET_CODE (src) == MEM) + { + /* gpr <- memory */ + if (dbl_memory_one_insn_operand (src, mode)) + return "ldd%I1%U1 %M1, %0"; + + return "#"; + } + + else if (GET_CODE (src) == CONST_INT + || GET_CODE (src) == CONST_DOUBLE) + return "#"; + } + + else if (FPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* fpr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + { + if (((dest_regno - FPR_FIRST) & 1) == 0 + && ((src_regno - GPR_FIRST) & 1) == 0) + return "movgfd %1, %0"; + + return "#"; + } + + else if (FPR_P (src_regno)) + { + if (TARGET_DOUBLE + && ((dest_regno - FPR_FIRST) & 1) == 0 + && ((src_regno - FPR_FIRST) & 1) == 0) + return "fmovd %1, %0"; + + return "#"; + } + } + + else if (GET_CODE (src) == MEM) + { + /* fpr <- memory */ + if (dbl_memory_one_insn_operand (src, mode)) + return "lddf%I1%U1 %M1, %0"; + + return "#"; + } + + else if (ZERO_P (src)) + return "#"; + } + } + + else if (GET_CODE (dest) == MEM) + { + if (GET_CODE (src) == REG) + { + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + { + if (((src_regno - GPR_FIRST) & 1) == 0 + && dbl_memory_one_insn_operand (dest, mode)) + return "std%I0%U0 %1, %M0"; + + return "#"; + } + + if (FPR_P (src_regno)) + { + if (((src_regno - FPR_FIRST) & 1) == 0 + && dbl_memory_one_insn_operand (dest, mode)) + return "stdf%I0%U0 %1, %M0"; + + return "#"; + } + } + + else if (ZERO_P (src)) + { + if (dbl_memory_one_insn_operand (dest, mode)) + return "std%I0%U0 %., %M0"; + + return "#"; + } + } + + fatal_insn ("bad output_move_double operand", insn); + return ""; +} + + +/* Return a string to output a single word conditional move. + Operand0 -- EQ/NE of ccr register and 0 + Operand1 -- CCR register + Operand2 -- destination + Operand3 -- source */ + +const char * +output_condmove_single (rtx operands[], rtx insn) +{ + rtx dest = operands[2]; + rtx src = operands[3]; + + if (GET_CODE (dest) == REG) + { + int dest_regno = REGNO (dest); + enum machine_mode mode = GET_MODE (dest); + + if (GPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* gpr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + return "cmov %z3, %2, %1, %e0"; + + else if (FPR_P (src_regno)) + return "cmovfg %3, %2, %1, %e0"; + } + + else if (GET_CODE (src) == MEM) + { + /* gpr <- memory */ + switch (mode) + { + default: + break; + + case QImode: + return "cldsb%I3%U3 %M3, %2, %1, %e0"; + + case HImode: + return "cldsh%I3%U3 %M3, %2, %1, %e0"; + + case SImode: + case SFmode: + return "cld%I3%U3 %M3, %2, %1, %e0"; + } + } + + else if (ZERO_P (src)) + return "cmov %., %2, %1, %e0"; + } + + else if (FPR_P (dest_regno)) + { + if (GET_CODE (src) == REG) + { + /* fpr <- some sort of register */ + int src_regno = REGNO (src); + + if (GPR_P (src_regno)) + return "cmovgf %3, %2, %1, %e0"; + + else if (FPR_P (src_regno)) + { + if (TARGET_HARD_FLOAT) + return "cfmovs %3,%2,%1,%e0"; + else + return "cmor %3, %3, %2, %1, %e0"; + } + } + + else if (GET_CODE (src) == MEM) + { + /* fpr <- memory */ + if (mode == SImode || mode == SFmode) + return "cldf%I3%U3 %M3, %2, %1, %e0"; + } + + else if (ZERO_P (src)) + return "cmovgf %., %2, %1, %e0"; + } + } + + else if (GET_CODE (dest) == MEM) + { + if (GET_CODE (src) == REG) + { + int src_regno = REGNO (src); + enum machine_mode mode = GET_MODE (dest); + + if (GPR_P (src_regno)) + { + switch (mode) + { + default: + break; + + case QImode: + return "cstb%I2%U2 %3, %M2, %1, %e0"; + + case HImode: + return "csth%I2%U2 %3, %M2, %1, %e0"; + + case SImode: + case SFmode: + return "cst%I2%U2 %3, %M2, %1, %e0"; + } + } + + else if (FPR_P (src_regno) && (mode == SImode || mode == SFmode)) + return "cstf%I2%U2 %3, %M2, %1, %e0"; + } + + else if (ZERO_P (src)) + { + enum machine_mode mode = GET_MODE (dest); + switch (mode) + { + default: + break; + + case QImode: + return "cstb%I2%U2 %., %M2, %1, %e0"; + + case HImode: + return "csth%I2%U2 %., %M2, %1, %e0"; + + case SImode: + case SFmode: + return "cst%I2%U2 %., %M2, %1, %e0"; + } + } + } + + fatal_insn ("bad output_condmove_single operand", insn); + return ""; +} + + +/* Emit the appropriate code to do a comparison, returning the register the + comparison was done it. */ + +static rtx +frv_emit_comparison (enum rtx_code test, rtx op0, rtx op1) +{ + enum machine_mode cc_mode; + rtx cc_reg; + + /* Floating point doesn't have comparison against a constant. */ + if (GET_MODE (op0) == CC_FPmode && GET_CODE (op1) != REG) + op1 = force_reg (GET_MODE (op0), op1); + + /* Possibly disable using anything but a fixed register in order to work + around cse moving comparisons past function calls. */ + cc_mode = SELECT_CC_MODE (test, op0, op1); + cc_reg = ((TARGET_ALLOC_CC) + ? gen_reg_rtx (cc_mode) + : gen_rtx_REG (cc_mode, + (cc_mode == CC_FPmode) ? FCC_FIRST : ICC_FIRST)); + + emit_insn (gen_rtx_SET (VOIDmode, cc_reg, + gen_rtx_COMPARE (cc_mode, op0, op1))); + + return cc_reg; +} + + +/* Emit code for a conditional branch. + XXX: I originally wanted to add a clobber of a CCR register to use in + conditional execution, but that confuses the rest of the compiler. */ + +int +frv_emit_cond_branch (rtx operands[]) +{ + rtx test_rtx; + rtx label_ref; + rtx if_else; + enum rtx_code test = GET_CODE (operands[0]); + rtx cc_reg = frv_emit_comparison (test, operands[1], operands[2]); + enum machine_mode cc_mode = GET_MODE (cc_reg); + + /* Branches generate: + (set (pc) + (if_then_else (<test>, <cc_reg>, (const_int 0)) + (label_ref <branch_label>) + (pc))) */ + label_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]); + test_rtx = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx); + if_else = gen_rtx_IF_THEN_ELSE (cc_mode, test_rtx, label_ref, pc_rtx); + emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, if_else)); + return TRUE; +} + + +/* Emit code to set a gpr to 1/0 based on a comparison. */ + +int +frv_emit_scc (rtx operands[]) +{ + rtx set; + rtx test_rtx; + rtx clobber; + rtx cr_reg; + enum rtx_code test = GET_CODE (operands[1]); + rtx cc_reg = frv_emit_comparison (test, operands[2], operands[3]); + + /* SCC instructions generate: + (parallel [(set <target> (<test>, <cc_reg>, (const_int 0)) + (clobber (<ccr_reg>))]) */ + test_rtx = gen_rtx_fmt_ee (test, SImode, cc_reg, const0_rtx); + set = gen_rtx_SET (VOIDmode, operands[0], test_rtx); + + cr_reg = ((TARGET_ALLOC_CC) + ? gen_reg_rtx (CC_CCRmode) + : gen_rtx_REG (CC_CCRmode, + ((GET_MODE (cc_reg) == CC_FPmode) + ? FCR_FIRST + : ICR_FIRST))); + + clobber = gen_rtx_CLOBBER (VOIDmode, cr_reg); + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber))); + return TRUE; +} + + +/* Split a SCC instruction into component parts, returning a SEQUENCE to hold + the separate insns. */ + +rtx +frv_split_scc (rtx dest, rtx test, rtx cc_reg, rtx cr_reg, HOST_WIDE_INT value) +{ + rtx ret; + + start_sequence (); + + /* Set the appropriate CCR bit. */ + emit_insn (gen_rtx_SET (VOIDmode, + cr_reg, + gen_rtx_fmt_ee (GET_CODE (test), + GET_MODE (cr_reg), + cc_reg, + const0_rtx))); + + /* Move the value into the destination. */ + emit_move_insn (dest, GEN_INT (value)); + + /* Move 0 into the destination if the test failed */ + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_EQ (GET_MODE (cr_reg), + cr_reg, + const0_rtx), + gen_rtx_SET (VOIDmode, dest, const0_rtx))); + + /* Finish up, return sequence. */ + ret = get_insns (); + end_sequence (); + return ret; +} + + +/* Emit the code for a conditional move, return TRUE if we could do the + move. */ + +int +frv_emit_cond_move (rtx dest, rtx test_rtx, rtx src1, rtx src2) +{ + rtx set; + rtx clobber_cc; + rtx test2; + rtx cr_reg; + rtx if_rtx; + enum rtx_code test = GET_CODE (test_rtx); + rtx cc_reg = frv_emit_comparison (test, + XEXP (test_rtx, 0), XEXP (test_rtx, 1)); + enum machine_mode cc_mode = GET_MODE (cc_reg); + + /* Conditional move instructions generate: + (parallel [(set <target> + (if_then_else (<test> <cc_reg> (const_int 0)) + <src1> + <src2>)) + (clobber (<ccr_reg>))]) */ + + /* Handle various cases of conditional move involving two constants. */ + if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT) + { + HOST_WIDE_INT value1 = INTVAL (src1); + HOST_WIDE_INT value2 = INTVAL (src2); + + /* Having 0 as one of the constants can be done by loading the other + constant, and optionally moving in gr0. */ + if (value1 == 0 || value2 == 0) + ; + + /* If the first value is within an addi range and also the difference + between the two fits in an addi's range, load up the difference, then + conditionally move in 0, and then unconditionally add the first + value. */ + else if (IN_RANGE (value1, -2048, 2047) + && IN_RANGE (value2 - value1, -2048, 2047)) + ; + + /* If neither condition holds, just force the constant into a + register. */ + else + { + src1 = force_reg (GET_MODE (dest), src1); + src2 = force_reg (GET_MODE (dest), src2); + } + } + + /* If one value is a register, insure the other value is either 0 or a + register. */ + else + { + if (GET_CODE (src1) == CONST_INT && INTVAL (src1) != 0) + src1 = force_reg (GET_MODE (dest), src1); + + if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0) + src2 = force_reg (GET_MODE (dest), src2); + } + + test2 = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx); + if_rtx = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), test2, src1, src2); + + set = gen_rtx_SET (VOIDmode, dest, if_rtx); + + cr_reg = ((TARGET_ALLOC_CC) + ? gen_reg_rtx (CC_CCRmode) + : gen_rtx_REG (CC_CCRmode, + (cc_mode == CC_FPmode) ? FCR_FIRST : ICR_FIRST)); + + clobber_cc = gen_rtx_CLOBBER (VOIDmode, cr_reg); + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber_cc))); + return TRUE; +} + + +/* Split a conditional move into constituent parts, returning a SEQUENCE + containing all of the insns. */ + +rtx +frv_split_cond_move (rtx operands[]) +{ + rtx dest = operands[0]; + rtx test = operands[1]; + rtx cc_reg = operands[2]; + rtx src1 = operands[3]; + rtx src2 = operands[4]; + rtx cr_reg = operands[5]; + rtx ret; + enum machine_mode cr_mode = GET_MODE (cr_reg); + + start_sequence (); + + /* Set the appropriate CCR bit. */ + emit_insn (gen_rtx_SET (VOIDmode, + cr_reg, + gen_rtx_fmt_ee (GET_CODE (test), + GET_MODE (cr_reg), + cc_reg, + const0_rtx))); + + /* Handle various cases of conditional move involving two constants. */ + if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT) + { + HOST_WIDE_INT value1 = INTVAL (src1); + HOST_WIDE_INT value2 = INTVAL (src2); + + /* Having 0 as one of the constants can be done by loading the other + constant, and optionally moving in gr0. */ + if (value1 == 0) + { + emit_move_insn (dest, src2); + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (cr_mode, cr_reg, + const0_rtx), + gen_rtx_SET (VOIDmode, dest, src1))); + } + + else if (value2 == 0) + { + emit_move_insn (dest, src1); + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_EQ (cr_mode, cr_reg, + const0_rtx), + gen_rtx_SET (VOIDmode, dest, src2))); + } + + /* If the first value is within an addi range and also the difference + between the two fits in an addi's range, load up the difference, then + conditionally move in 0, and then unconditionally add the first + value. */ + else if (IN_RANGE (value1, -2048, 2047) + && IN_RANGE (value2 - value1, -2048, 2047)) + { + rtx dest_si = ((GET_MODE (dest) == SImode) + ? dest + : gen_rtx_SUBREG (SImode, dest, 0)); + + emit_move_insn (dest_si, GEN_INT (value2 - value1)); + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (cr_mode, cr_reg, + const0_rtx), + gen_rtx_SET (VOIDmode, dest_si, + const0_rtx))); + emit_insn (gen_addsi3 (dest_si, dest_si, src1)); + } + + else + gcc_unreachable (); + } + else + { + /* Emit the conditional move for the test being true if needed. */ + if (! rtx_equal_p (dest, src1)) + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (cr_mode, cr_reg, const0_rtx), + gen_rtx_SET (VOIDmode, dest, src1))); + + /* Emit the conditional move for the test being false if needed. */ + if (! rtx_equal_p (dest, src2)) + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_EQ (cr_mode, cr_reg, const0_rtx), + gen_rtx_SET (VOIDmode, dest, src2))); + } + + /* Finish up, return sequence. */ + ret = get_insns (); + end_sequence (); + return ret; +} + + +/* Split (set DEST SOURCE), where DEST is a double register and SOURCE is a + memory location that is not known to be dword-aligned. */ +void +frv_split_double_load (rtx dest, rtx source) +{ + int regno = REGNO (dest); + rtx dest1 = gen_highpart (SImode, dest); + rtx dest2 = gen_lowpart (SImode, dest); + rtx address = XEXP (source, 0); + + /* If the address is pre-modified, load the lower-numbered register + first, then load the other register using an integer offset from + the modified base register. This order should always be safe, + since the pre-modification cannot affect the same registers as the + load does. + + The situation for other loads is more complicated. Loading one + of the registers could affect the value of ADDRESS, so we must + be careful which order we do them in. */ + if (GET_CODE (address) == PRE_MODIFY + || ! refers_to_regno_p (regno, regno + 1, address, NULL)) + { + /* It is safe to load the lower-numbered register first. */ + emit_move_insn (dest1, change_address (source, SImode, NULL)); + emit_move_insn (dest2, frv_index_memory (source, SImode, 1)); + } + else + { + /* ADDRESS is not pre-modified and the address depends on the + lower-numbered register. Load the higher-numbered register + first. */ + emit_move_insn (dest2, frv_index_memory (source, SImode, 1)); + emit_move_insn (dest1, change_address (source, SImode, NULL)); + } +} + +/* Split (set DEST SOURCE), where DEST refers to a dword memory location + and SOURCE is either a double register or the constant zero. */ +void +frv_split_double_store (rtx dest, rtx source) +{ + rtx dest1 = change_address (dest, SImode, NULL); + rtx dest2 = frv_index_memory (dest, SImode, 1); + if (ZERO_P (source)) + { + emit_move_insn (dest1, CONST0_RTX (SImode)); + emit_move_insn (dest2, CONST0_RTX (SImode)); + } + else + { + emit_move_insn (dest1, gen_highpart (SImode, source)); + emit_move_insn (dest2, gen_lowpart (SImode, source)); + } +} + + +/* Split a min/max operation returning a SEQUENCE containing all of the + insns. */ + +rtx +frv_split_minmax (rtx operands[]) +{ + rtx dest = operands[0]; + rtx minmax = operands[1]; + rtx src1 = operands[2]; + rtx src2 = operands[3]; + rtx cc_reg = operands[4]; + rtx cr_reg = operands[5]; + rtx ret; + enum rtx_code test_code; + enum machine_mode cr_mode = GET_MODE (cr_reg); + + start_sequence (); + + /* Figure out which test to use. */ + switch (GET_CODE (minmax)) + { + default: + gcc_unreachable (); + + case SMIN: test_code = LT; break; + case SMAX: test_code = GT; break; + case UMIN: test_code = LTU; break; + case UMAX: test_code = GTU; break; + } + + /* Issue the compare instruction. */ + emit_insn (gen_rtx_SET (VOIDmode, + cc_reg, + gen_rtx_COMPARE (GET_MODE (cc_reg), + src1, src2))); + + /* Set the appropriate CCR bit. */ + emit_insn (gen_rtx_SET (VOIDmode, + cr_reg, + gen_rtx_fmt_ee (test_code, + GET_MODE (cr_reg), + cc_reg, + const0_rtx))); + + /* If are taking the min/max of a nonzero constant, load that first, and + then do a conditional move of the other value. */ + if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0) + { + gcc_assert (!rtx_equal_p (dest, src1)); + + emit_move_insn (dest, src2); + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (cr_mode, cr_reg, const0_rtx), + gen_rtx_SET (VOIDmode, dest, src1))); + } + + /* Otherwise, do each half of the move. */ + else + { + /* Emit the conditional move for the test being true if needed. */ + if (! rtx_equal_p (dest, src1)) + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (cr_mode, cr_reg, const0_rtx), + gen_rtx_SET (VOIDmode, dest, src1))); + + /* Emit the conditional move for the test being false if needed. */ + if (! rtx_equal_p (dest, src2)) + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_EQ (cr_mode, cr_reg, const0_rtx), + gen_rtx_SET (VOIDmode, dest, src2))); + } + + /* Finish up, return sequence. */ + ret = get_insns (); + end_sequence (); + return ret; +} + + +/* Split an integer abs operation returning a SEQUENCE containing all of the + insns. */ + +rtx +frv_split_abs (rtx operands[]) +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx cc_reg = operands[2]; + rtx cr_reg = operands[3]; + rtx ret; + + start_sequence (); + + /* Issue the compare < 0 instruction. */ + emit_insn (gen_rtx_SET (VOIDmode, + cc_reg, + gen_rtx_COMPARE (CCmode, src, const0_rtx))); + + /* Set the appropriate CCR bit. */ + emit_insn (gen_rtx_SET (VOIDmode, + cr_reg, + gen_rtx_fmt_ee (LT, CC_CCRmode, cc_reg, const0_rtx))); + + /* Emit the conditional negate if the value is negative. */ + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (CC_CCRmode, cr_reg, const0_rtx), + gen_negsi2 (dest, src))); + + /* Emit the conditional move for the test being false if needed. */ + if (! rtx_equal_p (dest, src)) + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_EQ (CC_CCRmode, cr_reg, const0_rtx), + gen_rtx_SET (VOIDmode, dest, src))); + + /* Finish up, return sequence. */ + ret = get_insns (); + end_sequence (); + return ret; +} + + +/* An internal function called by for_each_rtx to clear in a hard_reg set each + register used in an insn. */ + +static int +frv_clear_registers_used (rtx *ptr, void *data) +{ + if (GET_CODE (*ptr) == REG) + { + int regno = REGNO (*ptr); + HARD_REG_SET *p_regs = (HARD_REG_SET *)data; + + if (regno < FIRST_PSEUDO_REGISTER) + { + int reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (*ptr)); + + while (regno < reg_max) + { + CLEAR_HARD_REG_BIT (*p_regs, regno); + regno++; + } + } + } + + return 0; +} + + +/* Initialize the extra fields provided by IFCVT_EXTRA_FIELDS. */ + +/* On the FR-V, we don't have any extra fields per se, but it is useful hook to + initialize the static storage. */ +void +frv_ifcvt_init_extra_fields (ce_if_block_t *ce_info ATTRIBUTE_UNUSED) +{ + frv_ifcvt.added_insns_list = NULL_RTX; + frv_ifcvt.cur_scratch_regs = 0; + frv_ifcvt.num_nested_cond_exec = 0; + frv_ifcvt.cr_reg = NULL_RTX; + frv_ifcvt.nested_cc_reg = NULL_RTX; + frv_ifcvt.extra_int_cr = NULL_RTX; + frv_ifcvt.extra_fp_cr = NULL_RTX; + frv_ifcvt.last_nested_if_cr = NULL_RTX; +} + + +/* Internal function to add a potential insn to the list of insns to be inserted + if the conditional execution conversion is successful. */ + +static void +frv_ifcvt_add_insn (rtx pattern, rtx insn, int before_p) +{ + rtx link = alloc_EXPR_LIST (VOIDmode, pattern, insn); + + link->jump = before_p; /* Mark to add this before or after insn. */ + frv_ifcvt.added_insns_list = alloc_EXPR_LIST (VOIDmode, link, + frv_ifcvt.added_insns_list); + + if (TARGET_DEBUG_COND_EXEC) + { + fprintf (stderr, + "\n:::::::::: frv_ifcvt_add_insn: add the following %s insn %d:\n", + (before_p) ? "before" : "after", + (int)INSN_UID (insn)); + + debug_rtx (pattern); + } +} + + +/* A C expression to modify the code described by the conditional if + information CE_INFO, possibly updating the tests in TRUE_EXPR, and + FALSE_EXPR for converting if-then and if-then-else code to conditional + instructions. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if the + tests cannot be converted. */ + +void +frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false) +{ + basic_block test_bb = ce_info->test_bb; /* test basic block */ + basic_block then_bb = ce_info->then_bb; /* THEN */ + basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */ + basic_block join_bb = ce_info->join_bb; /* join block or NULL */ + rtx true_expr = *p_true; + rtx cr; + rtx cc; + rtx nested_cc; + enum machine_mode mode = GET_MODE (true_expr); + int j; + basic_block *bb; + int num_bb; + frv_tmp_reg_t *tmp_reg = &frv_ifcvt.tmp_reg; + rtx check_insn; + rtx sub_cond_exec_reg; + enum rtx_code code; + enum rtx_code code_true; + enum rtx_code code_false; + enum reg_class cc_class; + enum reg_class cr_class; + int cc_first; + int cc_last; + reg_set_iterator rsi; + + /* Make sure we are only dealing with hard registers. Also honor the + -mno-cond-exec switch, and -mno-nested-cond-exec switches if + applicable. */ + if (!reload_completed || !TARGET_COND_EXEC + || (!TARGET_NESTED_CE && ce_info->pass > 1)) + goto fail; + + /* Figure out which registers we can allocate for our own purposes. Only + consider registers that are not preserved across function calls and are + not fixed. However, allow the ICC/ICR temporary registers to be allocated + if we did not need to use them in reloading other registers. */ + memset (&tmp_reg->regs, 0, sizeof (tmp_reg->regs)); + COPY_HARD_REG_SET (tmp_reg->regs, call_used_reg_set); + AND_COMPL_HARD_REG_SET (tmp_reg->regs, fixed_reg_set); + SET_HARD_REG_BIT (tmp_reg->regs, ICC_TEMP); + SET_HARD_REG_BIT (tmp_reg->regs, ICR_TEMP); + + /* If this is a nested IF, we need to discover whether the CC registers that + are set/used inside of the block are used anywhere else. If not, we can + change them to be the CC register that is paired with the CR register that + controls the outermost IF block. */ + if (ce_info->pass > 1) + { + CLEAR_HARD_REG_SET (frv_ifcvt.nested_cc_ok_rewrite); + for (j = CC_FIRST; j <= CC_LAST; j++) + if (TEST_HARD_REG_BIT (tmp_reg->regs, j)) + { + if (REGNO_REG_SET_P (df_get_live_in (then_bb), j)) + continue; + + if (else_bb + && REGNO_REG_SET_P (df_get_live_in (else_bb), j)) + continue; + + if (join_bb + && REGNO_REG_SET_P (df_get_live_in (join_bb), j)) + continue; + + SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j); + } + } + + for (j = 0; j < frv_ifcvt.cur_scratch_regs; j++) + frv_ifcvt.scratch_regs[j] = NULL_RTX; + + frv_ifcvt.added_insns_list = NULL_RTX; + frv_ifcvt.cur_scratch_regs = 0; + + bb = (basic_block *) alloca ((2 + ce_info->num_multiple_test_blocks) + * sizeof (basic_block)); + + if (join_bb) + { + unsigned int regno; + + /* Remove anything live at the beginning of the join block from being + available for allocation. */ + EXECUTE_IF_SET_IN_REG_SET (df_get_live_in (join_bb), 0, regno, rsi) + { + if (regno < FIRST_PSEUDO_REGISTER) + CLEAR_HARD_REG_BIT (tmp_reg->regs, regno); + } + } + + /* Add in all of the blocks in multiple &&/|| blocks to be scanned. */ + num_bb = 0; + if (ce_info->num_multiple_test_blocks) + { + basic_block multiple_test_bb = ce_info->last_test_bb; + + while (multiple_test_bb != test_bb) + { + bb[num_bb++] = multiple_test_bb; + multiple_test_bb = EDGE_PRED (multiple_test_bb, 0)->src; + } + } + + /* Add in the THEN and ELSE blocks to be scanned. */ + bb[num_bb++] = then_bb; + if (else_bb) + bb[num_bb++] = else_bb; + + sub_cond_exec_reg = NULL_RTX; + frv_ifcvt.num_nested_cond_exec = 0; + + /* Scan all of the blocks for registers that must not be allocated. */ + for (j = 0; j < num_bb; j++) + { + rtx last_insn = BB_END (bb[j]); + rtx insn = BB_HEAD (bb[j]); + unsigned int regno; + + if (dump_file) + fprintf (dump_file, "Scanning %s block %d, start %d, end %d\n", + (bb[j] == else_bb) ? "else" : ((bb[j] == then_bb) ? "then" : "test"), + (int) bb[j]->index, + (int) INSN_UID (BB_HEAD (bb[j])), + (int) INSN_UID (BB_END (bb[j]))); + + /* Anything live at the beginning of the block is obviously unavailable + for allocation. */ + EXECUTE_IF_SET_IN_REG_SET (df_get_live_in (bb[j]), 0, regno, rsi) + { + if (regno < FIRST_PSEUDO_REGISTER) + CLEAR_HARD_REG_BIT (tmp_reg->regs, regno); + } + + /* Loop through the insns in the block. */ + for (;;) + { + /* Mark any new registers that are created as being unavailable for + allocation. Also see if the CC register used in nested IFs can be + reallocated. */ + if (INSN_P (insn)) + { + rtx pattern; + rtx set; + int skip_nested_if = FALSE; + + for_each_rtx (&PATTERN (insn), frv_clear_registers_used, + (void *)&tmp_reg->regs); + + pattern = PATTERN (insn); + if (GET_CODE (pattern) == COND_EXEC) + { + rtx reg = XEXP (COND_EXEC_TEST (pattern), 0); + + if (reg != sub_cond_exec_reg) + { + sub_cond_exec_reg = reg; + frv_ifcvt.num_nested_cond_exec++; + } + } + + set = single_set_pattern (pattern); + if (set) + { + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + + if (GET_CODE (dest) == REG) + { + int regno = REGNO (dest); + enum rtx_code src_code = GET_CODE (src); + + if (CC_P (regno) && src_code == COMPARE) + skip_nested_if = TRUE; + + else if (CR_P (regno) + && (src_code == IF_THEN_ELSE + || COMPARISON_P (src))) + skip_nested_if = TRUE; + } + } + + if (! skip_nested_if) + for_each_rtx (&PATTERN (insn), frv_clear_registers_used, + (void *)&frv_ifcvt.nested_cc_ok_rewrite); + } + + if (insn == last_insn) + break; + + insn = NEXT_INSN (insn); + } + } + + /* If this is a nested if, rewrite the CC registers that are available to + include the ones that can be rewritten, to increase the chance of being + able to allocate a paired CC/CR register combination. */ + if (ce_info->pass > 1) + { + for (j = CC_FIRST; j <= CC_LAST; j++) + if (TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j)) + SET_HARD_REG_BIT (tmp_reg->regs, j); + else + CLEAR_HARD_REG_BIT (tmp_reg->regs, j); + } + + if (dump_file) + { + int num_gprs = 0; + fprintf (dump_file, "Available GPRs: "); + + for (j = GPR_FIRST; j <= GPR_LAST; j++) + if (TEST_HARD_REG_BIT (tmp_reg->regs, j)) + { + fprintf (dump_file, " %d [%s]", j, reg_names[j]); + if (++num_gprs > GPR_TEMP_NUM+2) + break; + } + + fprintf (dump_file, "%s\nAvailable CRs: ", + (num_gprs > GPR_TEMP_NUM+2) ? " ..." : ""); + + for (j = CR_FIRST; j <= CR_LAST; j++) + if (TEST_HARD_REG_BIT (tmp_reg->regs, j)) + fprintf (dump_file, " %d [%s]", j, reg_names[j]); + + fputs ("\n", dump_file); + + if (ce_info->pass > 1) + { + fprintf (dump_file, "Modifiable CCs: "); + for (j = CC_FIRST; j <= CC_LAST; j++) + if (TEST_HARD_REG_BIT (tmp_reg->regs, j)) + fprintf (dump_file, " %d [%s]", j, reg_names[j]); + + fprintf (dump_file, "\n%d nested COND_EXEC statements\n", + frv_ifcvt.num_nested_cond_exec); + } + } + + /* Allocate the appropriate temporary condition code register. Try to + allocate the ICR/FCR register that corresponds to the ICC/FCC register so + that conditional cmp's can be done. */ + if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode) + { + cr_class = ICR_REGS; + cc_class = ICC_REGS; + cc_first = ICC_FIRST; + cc_last = ICC_LAST; + } + else if (mode == CC_FPmode) + { + cr_class = FCR_REGS; + cc_class = FCC_REGS; + cc_first = FCC_FIRST; + cc_last = FCC_LAST; + } + else + { + cc_first = cc_last = 0; + cr_class = cc_class = NO_REGS; + } + + cc = XEXP (true_expr, 0); + nested_cc = cr = NULL_RTX; + if (cc_class != NO_REGS) + { + /* For nested IFs and &&/||, see if we can find a CC and CR register pair + so we can execute a csubcc/caddcc/cfcmps instruction. */ + int cc_regno; + + for (cc_regno = cc_first; cc_regno <= cc_last; cc_regno++) + { + int cr_regno = cc_regno - CC_FIRST + CR_FIRST; + + if (TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cc_regno) + && TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cr_regno)) + { + frv_ifcvt.tmp_reg.next_reg[ (int)cr_class ] = cr_regno; + cr = frv_alloc_temp_reg (tmp_reg, cr_class, CC_CCRmode, TRUE, + TRUE); + + frv_ifcvt.tmp_reg.next_reg[ (int)cc_class ] = cc_regno; + nested_cc = frv_alloc_temp_reg (tmp_reg, cc_class, CCmode, + TRUE, TRUE); + break; + } + } + } + + if (! cr) + { + if (dump_file) + fprintf (dump_file, "Could not allocate a CR temporary register\n"); + + goto fail; + } + + if (dump_file) + fprintf (dump_file, + "Will use %s for conditional execution, %s for nested comparisons\n", + reg_names[ REGNO (cr)], + (nested_cc) ? reg_names[ REGNO (nested_cc) ] : "<none>"); + + /* Set the CCR bit. Note for integer tests, we reverse the condition so that + in an IF-THEN-ELSE sequence, we are testing the TRUE case against the CCR + bit being true. We don't do this for floating point, because of NaNs. */ + code = GET_CODE (true_expr); + if (GET_MODE (cc) != CC_FPmode) + { + code = reverse_condition (code); + code_true = EQ; + code_false = NE; + } + else + { + code_true = NE; + code_false = EQ; + } + + check_insn = gen_rtx_SET (VOIDmode, cr, + gen_rtx_fmt_ee (code, CC_CCRmode, cc, const0_rtx)); + + /* Record the check insn to be inserted later. */ + frv_ifcvt_add_insn (check_insn, BB_END (test_bb), TRUE); + + /* Update the tests. */ + frv_ifcvt.cr_reg = cr; + frv_ifcvt.nested_cc_reg = nested_cc; + *p_true = gen_rtx_fmt_ee (code_true, CC_CCRmode, cr, const0_rtx); + *p_false = gen_rtx_fmt_ee (code_false, CC_CCRmode, cr, const0_rtx); + return; + + /* Fail, don't do this conditional execution. */ + fail: + *p_true = NULL_RTX; + *p_false = NULL_RTX; + if (dump_file) + fprintf (dump_file, "Disabling this conditional execution.\n"); + + return; +} + + +/* A C expression to modify the code described by the conditional if + information CE_INFO, for the basic block BB, possibly updating the tests in + TRUE_EXPR, and FALSE_EXPR for converting the && and || parts of if-then or + if-then-else code to conditional instructions. Set either TRUE_EXPR or + FALSE_EXPR to a null pointer if the tests cannot be converted. */ + +/* p_true and p_false are given expressions of the form: + + (and (eq:CC_CCR (reg:CC_CCR) + (const_int 0)) + (eq:CC (reg:CC) + (const_int 0))) */ + +void +frv_ifcvt_modify_multiple_tests (ce_if_block_t *ce_info, + basic_block bb, + rtx *p_true, + rtx *p_false) +{ + rtx old_true = XEXP (*p_true, 0); + rtx old_false = XEXP (*p_false, 0); + rtx true_expr = XEXP (*p_true, 1); + rtx false_expr = XEXP (*p_false, 1); + rtx test_expr; + rtx old_test; + rtx cr = XEXP (old_true, 0); + rtx check_insn; + rtx new_cr = NULL_RTX; + rtx *p_new_cr = (rtx *)0; + rtx if_else; + rtx compare; + rtx cc; + enum reg_class cr_class; + enum machine_mode mode = GET_MODE (true_expr); + rtx (*logical_func)(rtx, rtx, rtx); + + if (TARGET_DEBUG_COND_EXEC) + { + fprintf (stderr, + "\n:::::::::: frv_ifcvt_modify_multiple_tests, before modification for %s\ntrue insn:\n", + ce_info->and_and_p ? "&&" : "||"); + + debug_rtx (*p_true); + + fputs ("\nfalse insn:\n", stderr); + debug_rtx (*p_false); + } + + if (!TARGET_MULTI_CE) + goto fail; + + if (GET_CODE (cr) != REG) + goto fail; + + if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode) + { + cr_class = ICR_REGS; + p_new_cr = &frv_ifcvt.extra_int_cr; + } + else if (mode == CC_FPmode) + { + cr_class = FCR_REGS; + p_new_cr = &frv_ifcvt.extra_fp_cr; + } + else + goto fail; + + /* Allocate a temp CR, reusing a previously allocated temp CR if we have 3 or + more &&/|| tests. */ + new_cr = *p_new_cr; + if (! new_cr) + { + new_cr = *p_new_cr = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, cr_class, + CC_CCRmode, TRUE, TRUE); + if (! new_cr) + goto fail; + } + + if (ce_info->and_and_p) + { + old_test = old_false; + test_expr = true_expr; + logical_func = (GET_CODE (old_true) == EQ) ? gen_andcr : gen_andncr; + *p_true = gen_rtx_NE (CC_CCRmode, cr, const0_rtx); + *p_false = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx); + } + else + { + old_test = old_false; + test_expr = false_expr; + logical_func = (GET_CODE (old_false) == EQ) ? gen_orcr : gen_orncr; + *p_true = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx); + *p_false = gen_rtx_NE (CC_CCRmode, cr, const0_rtx); + } + + /* First add the andcr/andncr/orcr/orncr, which will be added after the + conditional check instruction, due to frv_ifcvt_add_insn being a LIFO + stack. */ + frv_ifcvt_add_insn ((*logical_func) (cr, cr, new_cr), BB_END (bb), TRUE); + + /* Now add the conditional check insn. */ + cc = XEXP (test_expr, 0); + compare = gen_rtx_fmt_ee (GET_CODE (test_expr), CC_CCRmode, cc, const0_rtx); + if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, old_test, compare, const0_rtx); + + check_insn = gen_rtx_SET (VOIDmode, new_cr, if_else); + + /* Add the new check insn to the list of check insns that need to be + inserted. */ + frv_ifcvt_add_insn (check_insn, BB_END (bb), TRUE); + + if (TARGET_DEBUG_COND_EXEC) + { + fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, after modification\ntrue insn:\n", + stderr); + + debug_rtx (*p_true); + + fputs ("\nfalse insn:\n", stderr); + debug_rtx (*p_false); + } + + return; + + fail: + *p_true = *p_false = NULL_RTX; + + /* If we allocated a CR register, release it. */ + if (new_cr) + { + CLEAR_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, REGNO (new_cr)); + *p_new_cr = NULL_RTX; + } + + if (TARGET_DEBUG_COND_EXEC) + fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, failed.\n", stderr); + + return; +} + + +/* Return a register which will be loaded with a value if an IF block is + converted to conditional execution. This is used to rewrite instructions + that use constants to ones that just use registers. */ + +static rtx +frv_ifcvt_load_value (rtx value, rtx insn ATTRIBUTE_UNUSED) +{ + int num_alloc = frv_ifcvt.cur_scratch_regs; + int i; + rtx reg; + + /* We know gr0 == 0, so replace any errant uses. */ + if (value == const0_rtx) + return gen_rtx_REG (SImode, GPR_FIRST); + + /* First search all registers currently loaded to see if we have an + applicable constant. */ + if (CONSTANT_P (value) + || (GET_CODE (value) == REG && REGNO (value) == LR_REGNO)) + { + for (i = 0; i < num_alloc; i++) + { + if (rtx_equal_p (SET_SRC (frv_ifcvt.scratch_regs[i]), value)) + return SET_DEST (frv_ifcvt.scratch_regs[i]); + } + } + + /* Have we exhausted the number of registers available? */ + if (num_alloc >= GPR_TEMP_NUM) + { + if (dump_file) + fprintf (dump_file, "Too many temporary registers allocated\n"); + + return NULL_RTX; + } + + /* Allocate the new register. */ + reg = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, GPR_REGS, SImode, TRUE, TRUE); + if (! reg) + { + if (dump_file) + fputs ("Could not find a scratch register\n", dump_file); + + return NULL_RTX; + } + + frv_ifcvt.cur_scratch_regs++; + frv_ifcvt.scratch_regs[num_alloc] = gen_rtx_SET (VOIDmode, reg, value); + + if (dump_file) + { + if (GET_CODE (value) == CONST_INT) + fprintf (dump_file, "Register %s will hold %ld\n", + reg_names[ REGNO (reg)], (long)INTVAL (value)); + + else if (GET_CODE (value) == REG && REGNO (value) == LR_REGNO) + fprintf (dump_file, "Register %s will hold LR\n", + reg_names[ REGNO (reg)]); + + else + fprintf (dump_file, "Register %s will hold a saved value\n", + reg_names[ REGNO (reg)]); + } + + return reg; +} + + +/* Update a MEM used in conditional code that might contain an offset to put + the offset into a scratch register, so that the conditional load/store + operations can be used. This function returns the original pointer if the + MEM is valid to use in conditional code, NULL if we can't load up the offset + into a temporary register, or the new MEM if we were successful. */ + +static rtx +frv_ifcvt_rewrite_mem (rtx mem, enum machine_mode mode, rtx insn) +{ + rtx addr = XEXP (mem, 0); + + if (!frv_legitimate_address_p_1 (mode, addr, reload_completed, TRUE, FALSE)) + { + if (GET_CODE (addr) == PLUS) + { + rtx addr_op0 = XEXP (addr, 0); + rtx addr_op1 = XEXP (addr, 1); + + if (GET_CODE (addr_op0) == REG && CONSTANT_P (addr_op1)) + { + rtx reg = frv_ifcvt_load_value (addr_op1, insn); + if (!reg) + return NULL_RTX; + + addr = gen_rtx_PLUS (Pmode, addr_op0, reg); + } + + else + return NULL_RTX; + } + + else if (CONSTANT_P (addr)) + addr = frv_ifcvt_load_value (addr, insn); + + else + return NULL_RTX; + + if (addr == NULL_RTX) + return NULL_RTX; + + else if (XEXP (mem, 0) != addr) + return change_address (mem, mode, addr); + } + + return mem; +} + + +/* Given a PATTERN, return a SET expression if this PATTERN has only a single + SET, possibly conditionally executed. It may also have CLOBBERs, USEs. */ + +static rtx +single_set_pattern (rtx pattern) +{ + rtx set; + int i; + + if (GET_CODE (pattern) == COND_EXEC) + pattern = COND_EXEC_CODE (pattern); + + if (GET_CODE (pattern) == SET) + return pattern; + + else if (GET_CODE (pattern) == PARALLEL) + { + for (i = 0, set = 0; i < XVECLEN (pattern, 0); i++) + { + rtx sub = XVECEXP (pattern, 0, i); + + switch (GET_CODE (sub)) + { + case USE: + case CLOBBER: + break; + + case SET: + if (set) + return 0; + else + set = sub; + break; + + default: + return 0; + } + } + return set; + } + + return 0; +} + + +/* A C expression to modify the code described by the conditional if + information CE_INFO with the new PATTERN in INSN. If PATTERN is a null + pointer after the IFCVT_MODIFY_INSN macro executes, it is assumed that that + insn cannot be converted to be executed conditionally. */ + +rtx +frv_ifcvt_modify_insn (ce_if_block_t *ce_info, + rtx pattern, + rtx insn) +{ + rtx orig_ce_pattern = pattern; + rtx set; + rtx op0; + rtx op1; + rtx test; + + gcc_assert (GET_CODE (pattern) == COND_EXEC); + + test = COND_EXEC_TEST (pattern); + if (GET_CODE (test) == AND) + { + rtx cr = frv_ifcvt.cr_reg; + rtx test_reg; + + op0 = XEXP (test, 0); + if (! rtx_equal_p (cr, XEXP (op0, 0))) + goto fail; + + op1 = XEXP (test, 1); + test_reg = XEXP (op1, 0); + if (GET_CODE (test_reg) != REG) + goto fail; + + /* Is this the first nested if block in this sequence? If so, generate + an andcr or andncr. */ + if (! frv_ifcvt.last_nested_if_cr) + { + rtx and_op; + + frv_ifcvt.last_nested_if_cr = test_reg; + if (GET_CODE (op0) == NE) + and_op = gen_andcr (test_reg, cr, test_reg); + else + and_op = gen_andncr (test_reg, cr, test_reg); + + frv_ifcvt_add_insn (and_op, insn, TRUE); + } + + /* If this isn't the first statement in the nested if sequence, see if we + are dealing with the same register. */ + else if (! rtx_equal_p (test_reg, frv_ifcvt.last_nested_if_cr)) + goto fail; + + COND_EXEC_TEST (pattern) = test = op1; + } + + /* If this isn't a nested if, reset state variables. */ + else + { + frv_ifcvt.last_nested_if_cr = NULL_RTX; + } + + set = single_set_pattern (pattern); + if (set) + { + rtx dest = SET_DEST (set); + rtx src = SET_SRC (set); + enum machine_mode mode = GET_MODE (dest); + + /* Check for normal binary operators. */ + if (mode == SImode && ARITHMETIC_P (src)) + { + op0 = XEXP (src, 0); + op1 = XEXP (src, 1); + + if (integer_register_operand (op0, SImode) && CONSTANT_P (op1)) + { + op1 = frv_ifcvt_load_value (op1, insn); + if (op1) + COND_EXEC_CODE (pattern) + = gen_rtx_SET (VOIDmode, dest, gen_rtx_fmt_ee (GET_CODE (src), + GET_MODE (src), + op0, op1)); + else + goto fail; + } + } + + /* For multiply by a constant, we need to handle the sign extending + correctly. Add a USE of the value after the multiply to prevent flow + from cratering because only one register out of the two were used. */ + else if (mode == DImode && GET_CODE (src) == MULT) + { + op0 = XEXP (src, 0); + op1 = XEXP (src, 1); + if (GET_CODE (op0) == SIGN_EXTEND && GET_CODE (op1) == CONST_INT) + { + op1 = frv_ifcvt_load_value (op1, insn); + if (op1) + { + op1 = gen_rtx_SIGN_EXTEND (DImode, op1); + COND_EXEC_CODE (pattern) + = gen_rtx_SET (VOIDmode, dest, + gen_rtx_MULT (DImode, op0, op1)); + } + else + goto fail; + } + + frv_ifcvt_add_insn (gen_use (dest), insn, FALSE); + } + + /* If we are just loading a constant created for a nested conditional + execution statement, just load the constant without any conditional + execution, since we know that the constant will not interfere with any + other registers. */ + else if (frv_ifcvt.scratch_insns_bitmap + && bitmap_bit_p (frv_ifcvt.scratch_insns_bitmap, + INSN_UID (insn)) + && REG_P (SET_DEST (set)) + /* We must not unconditionally set a scratch reg chosen + for a nested if-converted block if its incoming + value from the TEST block (or the result of the THEN + branch) could/should propagate to the JOIN block. + It suffices to test whether the register is live at + the JOIN point: if it's live there, we can infer + that we set it in the former JOIN block of the + nested if-converted block (otherwise it wouldn't + have been available as a scratch register), and it + is either propagated through or set in the other + conditional block. It's probably not worth trying + to catch the latter case, and it could actually + limit scheduling of the combined block quite + severely. */ + && ce_info->join_bb + && ! (REGNO_REG_SET_P (df_get_live_in (ce_info->join_bb), + REGNO (SET_DEST (set)))) + /* Similarly, we must not unconditionally set a reg + used as scratch in the THEN branch if the same reg + is live in the ELSE branch. */ + && (! ce_info->else_bb + || BLOCK_FOR_INSN (insn) == ce_info->else_bb + || ! (REGNO_REG_SET_P (df_get_live_in (ce_info->else_bb), + REGNO (SET_DEST (set)))))) + pattern = set; + + else if (mode == QImode || mode == HImode || mode == SImode + || mode == SFmode) + { + int changed_p = FALSE; + + /* Check for just loading up a constant */ + if (CONSTANT_P (src) && integer_register_operand (dest, mode)) + { + src = frv_ifcvt_load_value (src, insn); + if (!src) + goto fail; + + changed_p = TRUE; + } + + /* See if we need to fix up stores */ + if (GET_CODE (dest) == MEM) + { + rtx new_mem = frv_ifcvt_rewrite_mem (dest, mode, insn); + + if (!new_mem) + goto fail; + + else if (new_mem != dest) + { + changed_p = TRUE; + dest = new_mem; + } + } + + /* See if we need to fix up loads */ + if (GET_CODE (src) == MEM) + { + rtx new_mem = frv_ifcvt_rewrite_mem (src, mode, insn); + + if (!new_mem) + goto fail; + + else if (new_mem != src) + { + changed_p = TRUE; + src = new_mem; + } + } + + /* If either src or destination changed, redo SET. */ + if (changed_p) + COND_EXEC_CODE (pattern) = gen_rtx_SET (VOIDmode, dest, src); + } + + /* Rewrite a nested set cccr in terms of IF_THEN_ELSE. Also deal with + rewriting the CC register to be the same as the paired CC/CR register + for nested ifs. */ + else if (mode == CC_CCRmode && COMPARISON_P (src)) + { + int regno = REGNO (XEXP (src, 0)); + rtx if_else; + + if (ce_info->pass > 1 + && regno != (int)REGNO (frv_ifcvt.nested_cc_reg) + && TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, regno)) + { + src = gen_rtx_fmt_ee (GET_CODE (src), + CC_CCRmode, + frv_ifcvt.nested_cc_reg, + XEXP (src, 1)); + } + + if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, test, src, const0_rtx); + pattern = gen_rtx_SET (VOIDmode, dest, if_else); + } + + /* Remap a nested compare instruction to use the paired CC/CR reg. */ + else if (ce_info->pass > 1 + && GET_CODE (dest) == REG + && CC_P (REGNO (dest)) + && REGNO (dest) != REGNO (frv_ifcvt.nested_cc_reg) + && TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, + REGNO (dest)) + && GET_CODE (src) == COMPARE) + { + PUT_MODE (frv_ifcvt.nested_cc_reg, GET_MODE (dest)); + COND_EXEC_CODE (pattern) + = gen_rtx_SET (VOIDmode, frv_ifcvt.nested_cc_reg, copy_rtx (src)); + } + } + + if (TARGET_DEBUG_COND_EXEC) + { + rtx orig_pattern = PATTERN (insn); + + PATTERN (insn) = pattern; + fprintf (stderr, + "\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn after modification:\n", + ce_info->pass); + + debug_rtx (insn); + PATTERN (insn) = orig_pattern; + } + + return pattern; + + fail: + if (TARGET_DEBUG_COND_EXEC) + { + rtx orig_pattern = PATTERN (insn); + + PATTERN (insn) = orig_ce_pattern; + fprintf (stderr, + "\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn could not be modified:\n", + ce_info->pass); + + debug_rtx (insn); + PATTERN (insn) = orig_pattern; + } + + return NULL_RTX; +} + + +/* A C expression to perform any final machine dependent modifications in + converting code to conditional execution in the code described by the + conditional if information CE_INFO. */ + +void +frv_ifcvt_modify_final (ce_if_block_t *ce_info ATTRIBUTE_UNUSED) +{ + rtx existing_insn; + rtx check_insn; + rtx p = frv_ifcvt.added_insns_list; + int i; + + /* Loop inserting the check insns. The last check insn is the first test, + and is the appropriate place to insert constants. */ + gcc_assert (p); + + do + { + rtx check_and_insert_insns = XEXP (p, 0); + rtx old_p = p; + + check_insn = XEXP (check_and_insert_insns, 0); + existing_insn = XEXP (check_and_insert_insns, 1); + p = XEXP (p, 1); + + /* The jump bit is used to say that the new insn is to be inserted BEFORE + the existing insn, otherwise it is to be inserted AFTER. */ + if (check_and_insert_insns->jump) + { + emit_insn_before (check_insn, existing_insn); + check_and_insert_insns->jump = 0; + } + else + emit_insn_after (check_insn, existing_insn); + + free_EXPR_LIST_node (check_and_insert_insns); + free_EXPR_LIST_node (old_p); + } + while (p != NULL_RTX); + + /* Load up any constants needed into temp gprs */ + for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++) + { + rtx insn = emit_insn_before (frv_ifcvt.scratch_regs[i], existing_insn); + if (! frv_ifcvt.scratch_insns_bitmap) + frv_ifcvt.scratch_insns_bitmap = BITMAP_ALLOC (NULL); + bitmap_set_bit (frv_ifcvt.scratch_insns_bitmap, INSN_UID (insn)); + frv_ifcvt.scratch_regs[i] = NULL_RTX; + } + + frv_ifcvt.added_insns_list = NULL_RTX; + frv_ifcvt.cur_scratch_regs = 0; +} + + +/* A C expression to cancel any machine dependent modifications in converting + code to conditional execution in the code described by the conditional if + information CE_INFO. */ + +void +frv_ifcvt_modify_cancel (ce_if_block_t *ce_info ATTRIBUTE_UNUSED) +{ + int i; + rtx p = frv_ifcvt.added_insns_list; + + /* Loop freeing up the EXPR_LIST's allocated. */ + while (p != NULL_RTX) + { + rtx check_and_jump = XEXP (p, 0); + rtx old_p = p; + + p = XEXP (p, 1); + free_EXPR_LIST_node (check_and_jump); + free_EXPR_LIST_node (old_p); + } + + /* Release any temporary gprs allocated. */ + for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++) + frv_ifcvt.scratch_regs[i] = NULL_RTX; + + frv_ifcvt.added_insns_list = NULL_RTX; + frv_ifcvt.cur_scratch_regs = 0; + return; +} + +/* A C expression for the size in bytes of the trampoline, as an integer. + The template is: + + setlo #0, <jmp_reg> + setlo #0, <static_chain> + sethi #0, <jmp_reg> + sethi #0, <static_chain> + jmpl @(gr0,<jmp_reg>) */ + +int +frv_trampoline_size (void) +{ + if (TARGET_FDPIC) + /* Allocate room for the function descriptor and the lddi + instruction. */ + return 8 + 6 * 4; + return 5 /* instructions */ * 4 /* instruction size. */; +} + + +/* A C statement to initialize the variable parts of a trampoline. ADDR is an + RTX for the address of the trampoline; FNADDR is an RTX for the address of + the nested function; STATIC_CHAIN is an RTX for the static chain value that + should be passed to the function when it is called. + + The template is: + + setlo #0, <jmp_reg> + setlo #0, <static_chain> + sethi #0, <jmp_reg> + sethi #0, <static_chain> + jmpl @(gr0,<jmp_reg>) */ + +static void +frv_trampoline_init (rtx m_tramp, tree fndecl, rtx static_chain) +{ + rtx addr = XEXP (m_tramp, 0); + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); + rtx sc_reg = force_reg (Pmode, static_chain); + + emit_library_call (gen_rtx_SYMBOL_REF (SImode, "__trampoline_setup"), + LCT_NORMAL, VOIDmode, 4, + addr, Pmode, + GEN_INT (frv_trampoline_size ()), SImode, + fnaddr, Pmode, + sc_reg, Pmode); +} + + +/* Many machines have some registers that cannot be copied directly to or from + memory or even from other types of registers. An example is the `MQ' + register, which on most machines, can only be copied to or from general + registers, but not memory. Some machines allow copying all registers to and + from memory, but require a scratch register for stores to some memory + locations (e.g., those with symbolic address on the RT, and those with + certain symbolic address on the SPARC when compiling PIC). In some cases, + both an intermediate and a scratch register are required. + + You should define these macros to indicate to the reload phase that it may + need to allocate at least one register for a reload in addition to the + register to contain the data. Specifically, if copying X to a register + RCLASS in MODE requires an intermediate register, you should define + `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of + whose registers can be used as intermediate registers or scratch registers. + + If copying a register RCLASS in MODE to X requires an intermediate or scratch + register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the + largest register class required. If the requirements for input and output + reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used + instead of defining both macros identically. + + The values returned by these macros are often `GENERAL_REGS'. Return + `NO_REGS' if no spare register is needed; i.e., if X can be directly copied + to or from a register of RCLASS in MODE without requiring a scratch register. + Do not define this macro if it would always return `NO_REGS'. + + If a scratch register is required (either with or without an intermediate + register), you should define patterns for `reload_inM' or `reload_outM', as + required.. These patterns, which will normally be implemented with a + `define_expand', should be similar to the `movM' patterns, except that + operand 2 is the scratch register. + + Define constraints for the reload register and scratch register that contain + a single register class. If the original reload register (whose class is + RCLASS) can meet the constraint given in the pattern, the value returned by + these macros is used for the class of the scratch register. Otherwise, two + additional reload registers are required. Their classes are obtained from + the constraints in the insn pattern. + + X might be a pseudo-register or a `subreg' of a pseudo-register, which could + either be in a hard register or in memory. Use `true_regnum' to find out; + it will return -1 if the pseudo is in memory and the hard register number if + it is in a register. + + These macros should not be used in the case where a particular class of + registers can only be copied to memory and not to another class of + registers. In that case, secondary reload registers are not needed and + would not be helpful. Instead, a stack location must be used to perform the + copy and the `movM' pattern should use memory as an intermediate storage. + This case often occurs between floating-point and general registers. */ + +enum reg_class +frv_secondary_reload_class (enum reg_class rclass, + enum machine_mode mode ATTRIBUTE_UNUSED, + rtx x) +{ + enum reg_class ret; + + switch (rclass) + { + default: + ret = NO_REGS; + break; + + /* Accumulators/Accumulator guard registers need to go through floating + point registers. */ + case QUAD_REGS: + case EVEN_REGS: + case GPR_REGS: + ret = NO_REGS; + if (x && GET_CODE (x) == REG) + { + int regno = REGNO (x); + + if (ACC_P (regno) || ACCG_P (regno)) + ret = FPR_REGS; + } + break; + + /* Nonzero constants should be loaded into an FPR through a GPR. */ + case QUAD_FPR_REGS: + case FEVEN_REGS: + case FPR_REGS: + if (x && CONSTANT_P (x) && !ZERO_P (x)) + ret = GPR_REGS; + else + ret = NO_REGS; + break; + + /* All of these types need gpr registers. */ + case ICC_REGS: + case FCC_REGS: + case CC_REGS: + case ICR_REGS: + case FCR_REGS: + case CR_REGS: + case LCR_REG: + case LR_REG: + ret = GPR_REGS; + break; + + /* The accumulators need fpr registers. */ + case ACC_REGS: + case EVEN_ACC_REGS: + case QUAD_ACC_REGS: + case ACCG_REGS: + ret = FPR_REGS; + break; + } + + return ret; +} + +/* This hook exists to catch the case where secondary_reload_class() is + called from init_reg_autoinc() in regclass.c - before the reload optabs + have been initialised. */ + +static reg_class_t +frv_secondary_reload (bool in_p, rtx x, reg_class_t reload_class_i, + enum machine_mode reload_mode, + secondary_reload_info * sri) +{ + enum reg_class rclass = NO_REGS; + enum reg_class reload_class = (enum reg_class) reload_class_i; + + if (sri->prev_sri && sri->prev_sri->t_icode != CODE_FOR_nothing) + { + sri->icode = sri->prev_sri->t_icode; + return NO_REGS; + } + + rclass = frv_secondary_reload_class (reload_class, reload_mode, x); + + if (rclass != NO_REGS) + { + enum insn_code icode + = direct_optab_handler (in_p ? reload_in_optab : reload_out_optab, + reload_mode); + if (icode == 0) + { + /* This happens when then the reload_[in|out]_optabs have + not been initialised. */ + sri->t_icode = CODE_FOR_nothing; + return rclass; + } + } + + /* Fall back to the default secondary reload handler. */ + return default_secondary_reload (in_p, x, reload_class, reload_mode, sri); + +} + +/* Worker function for TARGET_CLASS_LIKELY_SPILLED_P. */ + +static bool +frv_class_likely_spilled_p (reg_class_t rclass) +{ + switch (rclass) + { + default: + break; + + case GR8_REGS: + case GR9_REGS: + case GR89_REGS: + case FDPIC_FPTR_REGS: + case FDPIC_REGS: + case ICC_REGS: + case FCC_REGS: + case CC_REGS: + case ICR_REGS: + case FCR_REGS: + case CR_REGS: + case LCR_REG: + case LR_REG: + case SPR_REGS: + case QUAD_ACC_REGS: + case EVEN_ACC_REGS: + case ACC_REGS: + case ACCG_REGS: + return true; + } + + return false; +} + + +/* An expression for the alignment of a structure field FIELD if the + alignment computed in the usual way is COMPUTED. GCC uses this + value instead of the value in `BIGGEST_ALIGNMENT' or + `BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */ + +/* The definition type of the bit field data is either char, short, long or + long long. The maximum bit size is the number of bits of its own type. + + The bit field data is assigned to a storage unit that has an adequate size + for bit field data retention and is located at the smallest address. + + Consecutive bit field data are packed at consecutive bits having the same + storage unit, with regard to the type, beginning with the MSB and continuing + toward the LSB. + + If a field to be assigned lies over a bit field type boundary, its + assignment is completed by aligning it with a boundary suitable for the + type. + + When a bit field having a bit length of 0 is declared, it is forcibly + assigned to the next storage unit. + + e.g) + struct { + int a:2; + int b:6; + char c:4; + int d:10; + int :0; + int f:2; + } x; + + +0 +1 +2 +3 + &x 00000000 00000000 00000000 00000000 + MLM----L + a b + &x+4 00000000 00000000 00000000 00000000 + M--L + c + &x+8 00000000 00000000 00000000 00000000 + M----------L + d + &x+12 00000000 00000000 00000000 00000000 + ML + f +*/ + +int +frv_adjust_field_align (tree field, int computed) +{ + /* Make sure that the bitfield is not wider than the type. */ + if (DECL_BIT_FIELD (field) + && !DECL_ARTIFICIAL (field)) + { + tree parent = DECL_CONTEXT (field); + tree prev = NULL_TREE; + tree cur; + + for (cur = TYPE_FIELDS (parent); cur && cur != field; cur = DECL_CHAIN (cur)) + { + if (TREE_CODE (cur) != FIELD_DECL) + continue; + + prev = cur; + } + + gcc_assert (cur); + + /* If this isn't a :0 field and if the previous element is a bitfield + also, see if the type is different, if so, we will need to align the + bit-field to the next boundary. */ + if (prev + && ! DECL_PACKED (field) + && ! integer_zerop (DECL_SIZE (field)) + && DECL_BIT_FIELD_TYPE (field) != DECL_BIT_FIELD_TYPE (prev)) + { + int prev_align = TYPE_ALIGN (TREE_TYPE (prev)); + int cur_align = TYPE_ALIGN (TREE_TYPE (field)); + computed = (prev_align > cur_align) ? prev_align : cur_align; + } + } + + return computed; +} + + +/* A C expression that is nonzero if it is permissible to store a value of mode + MODE in hard register number REGNO (or in several registers starting with + that one). For a machine where all registers are equivalent, a suitable + definition is + + #define HARD_REGNO_MODE_OK(REGNO, MODE) 1 + + It is not necessary for this macro to check for the numbers of fixed + registers, because the allocation mechanism considers them to be always + occupied. + + On some machines, double-precision values must be kept in even/odd register + pairs. The way to implement that is to define this macro to reject odd + register numbers for such modes. + + The minimum requirement for a mode to be OK in a register is that the + `movMODE' instruction pattern support moves between the register and any + other hard register for which the mode is OK; and that moving a value into + the register and back out not alter it. + + Since the same instruction used to move `SImode' will work for all narrower + integer modes, it is not necessary on any machine for `HARD_REGNO_MODE_OK' + to distinguish between these modes, provided you define patterns `movhi', + etc., to take advantage of this. This is useful because of the interaction + between `HARD_REGNO_MODE_OK' and `MODES_TIEABLE_P'; it is very desirable for + all integer modes to be tieable. + + Many machines have special registers for floating point arithmetic. Often + people assume that floating point machine modes are allowed only in floating + point registers. This is not true. Any registers that can hold integers + can safely *hold* a floating point machine mode, whether or not floating + arithmetic can be done on it in those registers. Integer move instructions + can be used to move the values. + + On some machines, though, the converse is true: fixed-point machine modes + may not go in floating registers. This is true if the floating registers + normalize any value stored in them, because storing a non-floating value + there would garble it. In this case, `HARD_REGNO_MODE_OK' should reject + fixed-point machine modes in floating registers. But if the floating + registers do not automatically normalize, if you can store any bit pattern + in one and retrieve it unchanged without a trap, then any machine mode may + go in a floating register, so you can define this macro to say so. + + The primary significance of special floating registers is rather that they + are the registers acceptable in floating point arithmetic instructions. + However, this is of no concern to `HARD_REGNO_MODE_OK'. You handle it by + writing the proper constraints for those instructions. + + On some machines, the floating registers are especially slow to access, so + that it is better to store a value in a stack frame than in such a register + if floating point arithmetic is not being done. As long as the floating + registers are not in class `GENERAL_REGS', they will not be used unless some + pattern's constraint asks for one. */ + +int +frv_hard_regno_mode_ok (int regno, enum machine_mode mode) +{ + int base; + int mask; + + switch (mode) + { + case CCmode: + case CC_UNSmode: + case CC_NZmode: + return ICC_P (regno) || GPR_P (regno); + + case CC_CCRmode: + return CR_P (regno) || GPR_P (regno); + + case CC_FPmode: + return FCC_P (regno) || GPR_P (regno); + + default: + break; + } + + /* Set BASE to the first register in REGNO's class. Set MASK to the + bits that must be clear in (REGNO - BASE) for the register to be + well-aligned. */ + if (INTEGRAL_MODE_P (mode) || FLOAT_MODE_P (mode) || VECTOR_MODE_P (mode)) + { + if (ACCG_P (regno)) + { + /* ACCGs store one byte. Two-byte quantities must start in + even-numbered registers, four-byte ones in registers whose + numbers are divisible by four, and so on. */ + base = ACCG_FIRST; + mask = GET_MODE_SIZE (mode) - 1; + } + else + { + /* The other registers store one word. */ + if (GPR_P (regno) || regno == AP_FIRST) + base = GPR_FIRST; + + else if (FPR_P (regno)) + base = FPR_FIRST; + + else if (ACC_P (regno)) + base = ACC_FIRST; + + else if (SPR_P (regno)) + return mode == SImode; + + /* Fill in the table. */ + else + return 0; + + /* Anything smaller than an SI is OK in any word-sized register. */ + if (GET_MODE_SIZE (mode) < 4) + return 1; + + mask = (GET_MODE_SIZE (mode) / 4) - 1; + } + return (((regno - base) & mask) == 0); + } + + return 0; +} + + +/* A C expression for the number of consecutive hard registers, starting at + register number REGNO, required to hold a value of mode MODE. + + On a machine where all registers are exactly one word, a suitable definition + of this macro is + + #define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \ + / UNITS_PER_WORD)) */ + +/* On the FRV, make the CC_FP mode take 3 words in the integer registers, so + that we can build the appropriate instructions to properly reload the + values. Also, make the byte-sized accumulator guards use one guard + for each byte. */ + +int +frv_hard_regno_nregs (int regno, enum machine_mode mode) +{ + if (ACCG_P (regno)) + return GET_MODE_SIZE (mode); + else + return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; +} + + +/* A C expression for the maximum number of consecutive registers of + class RCLASS needed to hold a value of mode MODE. + + This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value + of the macro `CLASS_MAX_NREGS (RCLASS, MODE)' should be the maximum value of + `HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class RCLASS. + + This macro helps control the handling of multiple-word values in + the reload pass. + + This declaration is required. */ + +int +frv_class_max_nregs (enum reg_class rclass, enum machine_mode mode) +{ + if (rclass == ACCG_REGS) + /* An N-byte value requires N accumulator guards. */ + return GET_MODE_SIZE (mode); + else + return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; +} + + +/* A C expression that is nonzero if X is a legitimate constant for an + immediate operand on the target machine. You can assume that X satisfies + `CONSTANT_P', so you need not check this. In fact, `1' is a suitable + definition for this macro on machines where anything `CONSTANT_P' is valid. */ + +int +frv_legitimate_constant_p (rtx x) +{ + enum machine_mode mode = GET_MODE (x); + + /* frv_cannot_force_const_mem always returns true for FDPIC. This + means that the move expanders will be expected to deal with most + kinds of constant, regardless of what we return here. + + However, among its other duties, LEGITIMATE_CONSTANT_P decides whether + a constant can be entered into reg_equiv_constant[]. If we return true, + reload can create new instances of the constant whenever it likes. + + The idea is therefore to accept as many constants as possible (to give + reload more freedom) while rejecting constants that can only be created + at certain times. In particular, anything with a symbolic component will + require use of the pseudo FDPIC register, which is only available before + reload. */ + if (TARGET_FDPIC) + return LEGITIMATE_PIC_OPERAND_P (x); + + /* All of the integer constants are ok. */ + if (GET_CODE (x) != CONST_DOUBLE) + return TRUE; + + /* double integer constants are ok. */ + if (mode == VOIDmode || mode == DImode) + return TRUE; + + /* 0 is always ok. */ + if (x == CONST0_RTX (mode)) + return TRUE; + + /* If floating point is just emulated, allow any constant, since it will be + constructed in the GPRs. */ + if (!TARGET_HAS_FPRS) + return TRUE; + + if (mode == DFmode && !TARGET_DOUBLE) + return TRUE; + + /* Otherwise store the constant away and do a load. */ + return FALSE; +} + +/* Implement SELECT_CC_MODE. Choose CC_FP for floating-point comparisons, + CC_NZ for comparisons against zero in which a single Z or N flag test + is enough, CC_UNS for other unsigned comparisons, and CC for other + signed comparisons. */ + +enum machine_mode +frv_select_cc_mode (enum rtx_code code, rtx x, rtx y) +{ + if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + return CC_FPmode; + + switch (code) + { + case EQ: + case NE: + case LT: + case GE: + return y == const0_rtx ? CC_NZmode : CCmode; + + case GTU: + case GEU: + case LTU: + case LEU: + return y == const0_rtx ? CC_NZmode : CC_UNSmode; + + default: + return CCmode; + } +} + + +/* Worker function for TARGET_REGISTER_MOVE_COST. */ + +#define HIGH_COST 40 +#define MEDIUM_COST 3 +#define LOW_COST 1 + +static int +frv_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t from, reg_class_t to) +{ + switch (from) + { + default: + break; + + case QUAD_REGS: + case EVEN_REGS: + case GPR_REGS: + switch (to) + { + default: + break; + + case QUAD_REGS: + case EVEN_REGS: + case GPR_REGS: + return LOW_COST; + + case FEVEN_REGS: + case FPR_REGS: + return LOW_COST; + + case LCR_REG: + case LR_REG: + case SPR_REGS: + return LOW_COST; + } + + case FEVEN_REGS: + case FPR_REGS: + switch (to) + { + default: + break; + + case QUAD_REGS: + case EVEN_REGS: + case GPR_REGS: + case ACC_REGS: + case EVEN_ACC_REGS: + case QUAD_ACC_REGS: + case ACCG_REGS: + return MEDIUM_COST; + + case FEVEN_REGS: + case FPR_REGS: + return LOW_COST; + } + + case LCR_REG: + case LR_REG: + case SPR_REGS: + switch (to) + { + default: + break; + + case QUAD_REGS: + case EVEN_REGS: + case GPR_REGS: + return MEDIUM_COST; + } + + case ACC_REGS: + case EVEN_ACC_REGS: + case QUAD_ACC_REGS: + case ACCG_REGS: + switch (to) + { + default: + break; + + case FEVEN_REGS: + case FPR_REGS: + return MEDIUM_COST; + + } + } + + return HIGH_COST; +} + +/* Worker function for TARGET_MEMORY_MOVE_COST. */ + +static int +frv_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t rclass ATTRIBUTE_UNUSED, + bool in ATTRIBUTE_UNUSED) +{ + return 4; +} + + +/* Implementation of TARGET_ASM_INTEGER. In the FRV case we need to + use ".picptr" to generate safe relocations for PIC code. We also + need a fixup entry for aligned (non-debugging) code. */ + +static bool +frv_assemble_integer (rtx value, unsigned int size, int aligned_p) +{ + if ((flag_pic || TARGET_FDPIC) && size == UNITS_PER_WORD) + { + if (GET_CODE (value) == CONST + || GET_CODE (value) == SYMBOL_REF + || GET_CODE (value) == LABEL_REF) + { + if (TARGET_FDPIC && GET_CODE (value) == SYMBOL_REF + && SYMBOL_REF_FUNCTION_P (value)) + { + fputs ("\t.picptr\tfuncdesc(", asm_out_file); + output_addr_const (asm_out_file, value); + fputs (")\n", asm_out_file); + return true; + } + else if (TARGET_FDPIC && GET_CODE (value) == CONST + && frv_function_symbol_referenced_p (value)) + return false; + if (aligned_p && !TARGET_FDPIC) + { + static int label_num = 0; + char buf[256]; + const char *p; + + ASM_GENERATE_INTERNAL_LABEL (buf, "LCP", label_num++); + p = (* targetm.strip_name_encoding) (buf); + + fprintf (asm_out_file, "%s:\n", p); + fprintf (asm_out_file, "%s\n", FIXUP_SECTION_ASM_OP); + fprintf (asm_out_file, "\t.picptr\t%s\n", p); + fprintf (asm_out_file, "\t.previous\n"); + } + assemble_integer_with_op ("\t.picptr\t", value); + return true; + } + if (!aligned_p) + { + /* We've set the unaligned SI op to NULL, so we always have to + handle the unaligned case here. */ + assemble_integer_with_op ("\t.4byte\t", value); + return true; + } + } + return default_assemble_integer (value, size, aligned_p); +} + +/* Function to set up the backend function structure. */ + +static struct machine_function * +frv_init_machine_status (void) +{ + return ggc_alloc_cleared_machine_function (); +} + +/* Implement TARGET_SCHED_ISSUE_RATE. */ + +int +frv_issue_rate (void) +{ + if (!TARGET_PACK) + return 1; + + switch (frv_cpu_type) + { + default: + case FRV_CPU_FR300: + case FRV_CPU_SIMPLE: + return 1; + + case FRV_CPU_FR400: + case FRV_CPU_FR405: + case FRV_CPU_FR450: + return 2; + + case FRV_CPU_GENERIC: + case FRV_CPU_FR500: + case FRV_CPU_TOMCAT: + return 4; + + case FRV_CPU_FR550: + return 8; + } +} + +/* A for_each_rtx callback. If X refers to an accumulator, return + ACC_GROUP_ODD if the bit 2 of the register number is set and + ACC_GROUP_EVEN if it is clear. Return 0 (ACC_GROUP_NONE) + otherwise. */ + +static int +frv_acc_group_1 (rtx *x, void *data ATTRIBUTE_UNUSED) +{ + if (REG_P (*x)) + { + if (ACC_P (REGNO (*x))) + return (REGNO (*x) - ACC_FIRST) & 4 ? ACC_GROUP_ODD : ACC_GROUP_EVEN; + if (ACCG_P (REGNO (*x))) + return (REGNO (*x) - ACCG_FIRST) & 4 ? ACC_GROUP_ODD : ACC_GROUP_EVEN; + } + return 0; +} + +/* Return the value of INSN's acc_group attribute. */ + +int +frv_acc_group (rtx insn) +{ + /* This distinction only applies to the FR550 packing constraints. */ + if (frv_cpu_type != FRV_CPU_FR550) + return ACC_GROUP_NONE; + return for_each_rtx (&PATTERN (insn), frv_acc_group_1, 0); +} + +/* Return the index of the DFA unit in FRV_UNIT_NAMES[] that instruction + INSN will try to claim first. Since this value depends only on the + type attribute, we can cache the results in FRV_TYPE_TO_UNIT[]. */ + +static unsigned int +frv_insn_unit (rtx insn) +{ + enum attr_type type; + + type = get_attr_type (insn); + if (frv_type_to_unit[type] == ARRAY_SIZE (frv_unit_codes)) + { + /* We haven't seen this type of instruction before. */ + state_t state; + unsigned int unit; + + /* Issue the instruction on its own to see which unit it prefers. */ + state = alloca (state_size ()); + state_reset (state); + state_transition (state, insn); + + /* Find out which unit was taken. */ + for (unit = 0; unit < ARRAY_SIZE (frv_unit_codes); unit++) + if (cpu_unit_reservation_p (state, frv_unit_codes[unit])) + break; + + gcc_assert (unit != ARRAY_SIZE (frv_unit_codes)); + + frv_type_to_unit[type] = unit; + } + return frv_type_to_unit[type]; +} + +/* Return true if INSN issues to a branch unit. */ + +static bool +frv_issues_to_branch_unit_p (rtx insn) +{ + return frv_unit_groups[frv_insn_unit (insn)] == GROUP_B; +} + +/* The instructions in the packet, partitioned into groups. */ +struct frv_packet_group { + /* How many instructions in the packet belong to this group. */ + unsigned int num_insns; + + /* A list of the instructions that belong to this group, in the order + they appear in the rtl stream. */ + rtx insns[ARRAY_SIZE (frv_unit_codes)]; + + /* The contents of INSNS after they have been sorted into the correct + assembly-language order. Element X issues to unit X. The list may + contain extra nops. */ + rtx sorted[ARRAY_SIZE (frv_unit_codes)]; + + /* The member of frv_nops[] to use in sorted[]. */ + rtx nop; +}; + +/* The current state of the packing pass, implemented by frv_pack_insns. */ +static struct { + /* The state of the pipeline DFA. */ + state_t dfa_state; + + /* Which hardware registers are set within the current packet, + and the conditions under which they are set. */ + regstate_t regstate[FIRST_PSEUDO_REGISTER]; + + /* The memory locations that have been modified so far in this + packet. MEM is the memref and COND is the regstate_t condition + under which it is set. */ + struct { + rtx mem; + regstate_t cond; + } mems[2]; + + /* The number of valid entries in MEMS. The value is larger than + ARRAY_SIZE (mems) if there were too many mems to record. */ + unsigned int num_mems; + + /* The maximum number of instructions that can be packed together. */ + unsigned int issue_rate; + + /* The instructions in the packet, partitioned into groups. */ + struct frv_packet_group groups[NUM_GROUPS]; + + /* The instructions that make up the current packet. */ + rtx insns[ARRAY_SIZE (frv_unit_codes)]; + unsigned int num_insns; +} frv_packet; + +/* Return the regstate_t flags for the given COND_EXEC condition. + Abort if the condition isn't in the right form. */ + +static int +frv_cond_flags (rtx cond) +{ + gcc_assert ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE) + && GET_CODE (XEXP (cond, 0)) == REG + && CR_P (REGNO (XEXP (cond, 0))) + && XEXP (cond, 1) == const0_rtx); + return ((REGNO (XEXP (cond, 0)) - CR_FIRST) + | (GET_CODE (cond) == NE + ? REGSTATE_IF_TRUE + : REGSTATE_IF_FALSE)); +} + + +/* Return true if something accessed under condition COND2 can + conflict with something written under condition COND1. */ + +static bool +frv_regstate_conflict_p (regstate_t cond1, regstate_t cond2) +{ + /* If either reference was unconditional, we have a conflict. */ + if ((cond1 & REGSTATE_IF_EITHER) == 0 + || (cond2 & REGSTATE_IF_EITHER) == 0) + return true; + + /* The references might conflict if they were controlled by + different CRs. */ + if ((cond1 & REGSTATE_CC_MASK) != (cond2 & REGSTATE_CC_MASK)) + return true; + + /* They definitely conflict if they are controlled by the + same condition. */ + if ((cond1 & cond2 & REGSTATE_IF_EITHER) != 0) + return true; + + return false; +} + + +/* A for_each_rtx callback. Return 1 if *X depends on an instruction in + the current packet. DATA points to a regstate_t that describes the + condition under which *X might be set or used. */ + +static int +frv_registers_conflict_p_1 (rtx *x, void *data) +{ + unsigned int regno, i; + regstate_t cond; + + cond = *(regstate_t *) data; + + if (GET_CODE (*x) == REG) + FOR_EACH_REGNO (regno, *x) + if ((frv_packet.regstate[regno] & REGSTATE_MODIFIED) != 0) + if (frv_regstate_conflict_p (frv_packet.regstate[regno], cond)) + return 1; + + if (GET_CODE (*x) == MEM) + { + /* If we ran out of memory slots, assume a conflict. */ + if (frv_packet.num_mems > ARRAY_SIZE (frv_packet.mems)) + return 1; + + /* Check for output or true dependencies with earlier MEMs. */ + for (i = 0; i < frv_packet.num_mems; i++) + if (frv_regstate_conflict_p (frv_packet.mems[i].cond, cond)) + { + if (true_dependence (frv_packet.mems[i].mem, VOIDmode, + *x, rtx_varies_p)) + return 1; + + if (output_dependence (frv_packet.mems[i].mem, *x)) + return 1; + } + } + + /* The return values of calls aren't significant: they describe + the effect of the call as a whole, not of the insn itself. */ + if (GET_CODE (*x) == SET && GET_CODE (SET_SRC (*x)) == CALL) + { + if (for_each_rtx (&SET_SRC (*x), frv_registers_conflict_p_1, data)) + return 1; + return -1; + } + + /* Check subexpressions. */ + return 0; +} + + +/* Return true if something in X might depend on an instruction + in the current packet. */ + +static bool +frv_registers_conflict_p (rtx x) +{ + regstate_t flags; + + flags = 0; + if (GET_CODE (x) == COND_EXEC) + { + if (for_each_rtx (&XEXP (x, 0), frv_registers_conflict_p_1, &flags)) + return true; + + flags |= frv_cond_flags (XEXP (x, 0)); + x = XEXP (x, 1); + } + return for_each_rtx (&x, frv_registers_conflict_p_1, &flags); +} + + +/* A note_stores callback. DATA points to the regstate_t condition + under which X is modified. Update FRV_PACKET accordingly. */ + +static void +frv_registers_update_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data) +{ + unsigned int regno; + + if (GET_CODE (x) == REG) + FOR_EACH_REGNO (regno, x) + frv_packet.regstate[regno] |= *(regstate_t *) data; + + if (GET_CODE (x) == MEM) + { + if (frv_packet.num_mems < ARRAY_SIZE (frv_packet.mems)) + { + frv_packet.mems[frv_packet.num_mems].mem = x; + frv_packet.mems[frv_packet.num_mems].cond = *(regstate_t *) data; + } + frv_packet.num_mems++; + } +} + + +/* Update the register state information for an instruction whose + body is X. */ + +static void +frv_registers_update (rtx x) +{ + regstate_t flags; + + flags = REGSTATE_MODIFIED; + if (GET_CODE (x) == COND_EXEC) + { + flags |= frv_cond_flags (XEXP (x, 0)); + x = XEXP (x, 1); + } + note_stores (x, frv_registers_update_1, &flags); +} + + +/* Initialize frv_packet for the start of a new packet. */ + +static void +frv_start_packet (void) +{ + enum frv_insn_group group; + + memset (frv_packet.regstate, 0, sizeof (frv_packet.regstate)); + frv_packet.num_mems = 0; + frv_packet.num_insns = 0; + for (group = GROUP_I; group < NUM_GROUPS; + group = (enum frv_insn_group) (group + 1)) + frv_packet.groups[group].num_insns = 0; +} + + +/* Likewise for the start of a new basic block. */ + +static void +frv_start_packet_block (void) +{ + state_reset (frv_packet.dfa_state); + frv_start_packet (); +} + + +/* Finish the current packet, if any, and start a new one. Call + HANDLE_PACKET with FRV_PACKET describing the completed packet. */ + +static void +frv_finish_packet (void (*handle_packet) (void)) +{ + if (frv_packet.num_insns > 0) + { + handle_packet (); + state_transition (frv_packet.dfa_state, 0); + frv_start_packet (); + } +} + + +/* Return true if INSN can be added to the current packet. Update + the DFA state on success. */ + +static bool +frv_pack_insn_p (rtx insn) +{ + /* See if the packet is already as long as it can be. */ + if (frv_packet.num_insns == frv_packet.issue_rate) + return false; + + /* If the scheduler thought that an instruction should start a packet, + it's usually a good idea to believe it. It knows much more about + the latencies than we do. + + There are some exceptions though: + + - Conditional instructions are scheduled on the assumption that + they will be executed. This is usually a good thing, since it + tends to avoid unnecessary stalls in the conditional code. + But we want to pack conditional instructions as tightly as + possible, in order to optimize the case where they aren't + executed. + + - The scheduler will always put branches on their own, even + if there's no real dependency. + + - There's no point putting a call in its own packet unless + we have to. */ + if (frv_packet.num_insns > 0 + && GET_CODE (insn) == INSN + && GET_MODE (insn) == TImode + && GET_CODE (PATTERN (insn)) != COND_EXEC) + return false; + + /* Check for register conflicts. Don't do this for setlo since any + conflict will be with the partnering sethi, with which it can + be packed. */ + if (get_attr_type (insn) != TYPE_SETLO) + if (frv_registers_conflict_p (PATTERN (insn))) + return false; + + return state_transition (frv_packet.dfa_state, insn) < 0; +} + + +/* Add instruction INSN to the current packet. */ + +static void +frv_add_insn_to_packet (rtx insn) +{ + struct frv_packet_group *packet_group; + + packet_group = &frv_packet.groups[frv_unit_groups[frv_insn_unit (insn)]]; + packet_group->insns[packet_group->num_insns++] = insn; + frv_packet.insns[frv_packet.num_insns++] = insn; + + frv_registers_update (PATTERN (insn)); +} + + +/* Insert INSN (a member of frv_nops[]) into the current packet. If the + packet ends in a branch or call, insert the nop before it, otherwise + add to the end. */ + +static void +frv_insert_nop_in_packet (rtx insn) +{ + struct frv_packet_group *packet_group; + rtx last; + + packet_group = &frv_packet.groups[frv_unit_groups[frv_insn_unit (insn)]]; + last = frv_packet.insns[frv_packet.num_insns - 1]; + if (GET_CODE (last) != INSN) + { + insn = emit_insn_before (PATTERN (insn), last); + frv_packet.insns[frv_packet.num_insns - 1] = insn; + frv_packet.insns[frv_packet.num_insns++] = last; + } + else + { + insn = emit_insn_after (PATTERN (insn), last); + frv_packet.insns[frv_packet.num_insns++] = insn; + } + packet_group->insns[packet_group->num_insns++] = insn; +} + + +/* If packing is enabled, divide the instructions into packets and + return true. Call HANDLE_PACKET for each complete packet. */ + +static bool +frv_for_each_packet (void (*handle_packet) (void)) +{ + rtx insn, next_insn; + + frv_packet.issue_rate = frv_issue_rate (); + + /* Early exit if we don't want to pack insns. */ + if (!optimize + || !flag_schedule_insns_after_reload + || !TARGET_VLIW_BRANCH + || frv_packet.issue_rate == 1) + return false; + + /* Set up the initial packing state. */ + dfa_start (); + frv_packet.dfa_state = alloca (state_size ()); + + frv_start_packet_block (); + for (insn = get_insns (); insn != 0; insn = next_insn) + { + enum rtx_code code; + bool eh_insn_p; + + code = GET_CODE (insn); + next_insn = NEXT_INSN (insn); + + if (code == CODE_LABEL) + { + frv_finish_packet (handle_packet); + frv_start_packet_block (); + } + + if (INSN_P (insn)) + switch (GET_CODE (PATTERN (insn))) + { + case USE: + case CLOBBER: + case ADDR_VEC: + case ADDR_DIFF_VEC: + break; + + default: + /* Calls mustn't be packed on a TOMCAT. */ + if (GET_CODE (insn) == CALL_INSN && frv_cpu_type == FRV_CPU_TOMCAT) + frv_finish_packet (handle_packet); + + /* Since the last instruction in a packet determines the EH + region, any exception-throwing instruction must come at + the end of reordered packet. Insns that issue to a + branch unit are bound to come last; for others it's + too hard to predict. */ + eh_insn_p = (find_reg_note (insn, REG_EH_REGION, NULL) != NULL); + if (eh_insn_p && !frv_issues_to_branch_unit_p (insn)) + frv_finish_packet (handle_packet); + + /* Finish the current packet if we can't add INSN to it. + Simulate cycles until INSN is ready to issue. */ + if (!frv_pack_insn_p (insn)) + { + frv_finish_packet (handle_packet); + while (!frv_pack_insn_p (insn)) + state_transition (frv_packet.dfa_state, 0); + } + + /* Add the instruction to the packet. */ + frv_add_insn_to_packet (insn); + + /* Calls and jumps end a packet, as do insns that throw + an exception. */ + if (code == CALL_INSN || code == JUMP_INSN || eh_insn_p) + frv_finish_packet (handle_packet); + break; + } + } + frv_finish_packet (handle_packet); + dfa_finish (); + return true; +} + +/* Subroutine of frv_sort_insn_group. We are trying to sort + frv_packet.groups[GROUP].sorted[0...NUM_INSNS-1] into assembly + language order. We have already picked a new position for + frv_packet.groups[GROUP].sorted[X] if bit X of ISSUED is set. + These instructions will occupy elements [0, LOWER_SLOT) and + [UPPER_SLOT, NUM_INSNS) of the final (sorted) array. STATE is + the DFA state after issuing these instructions. + + Try filling elements [LOWER_SLOT, UPPER_SLOT) with every permutation + of the unused instructions. Return true if one such permutation gives + a valid ordering, leaving the successful permutation in sorted[]. + Do not modify sorted[] until a valid permutation is found. */ + +static bool +frv_sort_insn_group_1 (enum frv_insn_group group, + unsigned int lower_slot, unsigned int upper_slot, + unsigned int issued, unsigned int num_insns, + state_t state) +{ + struct frv_packet_group *packet_group; + unsigned int i; + state_t test_state; + size_t dfa_size; + rtx insn; + + /* Early success if we've filled all the slots. */ + if (lower_slot == upper_slot) + return true; + + packet_group = &frv_packet.groups[group]; + dfa_size = state_size (); + test_state = alloca (dfa_size); + + /* Try issuing each unused instruction. */ + for (i = num_insns - 1; i + 1 != 0; i--) + if (~issued & (1 << i)) + { + insn = packet_group->sorted[i]; + memcpy (test_state, state, dfa_size); + if (state_transition (test_state, insn) < 0 + && cpu_unit_reservation_p (test_state, + NTH_UNIT (group, upper_slot - 1)) + && frv_sort_insn_group_1 (group, lower_slot, upper_slot - 1, + issued | (1 << i), num_insns, + test_state)) + { + packet_group->sorted[upper_slot - 1] = insn; + return true; + } + } + + return false; +} + +/* Compare two instructions by their frv_insn_unit. */ + +static int +frv_compare_insns (const void *first, const void *second) +{ + const rtx *const insn1 = (rtx const *) first, + *const insn2 = (rtx const *) second; + return frv_insn_unit (*insn1) - frv_insn_unit (*insn2); +} + +/* Copy frv_packet.groups[GROUP].insns[] to frv_packet.groups[GROUP].sorted[] + and sort it into assembly language order. See frv.md for a description of + the algorithm. */ + +static void +frv_sort_insn_group (enum frv_insn_group group) +{ + struct frv_packet_group *packet_group; + unsigned int first, i, nop, max_unit, num_slots; + state_t state, test_state; + size_t dfa_size; + + packet_group = &frv_packet.groups[group]; + + /* Assume no nop is needed. */ + packet_group->nop = 0; + + if (packet_group->num_insns == 0) + return; + + /* Copy insns[] to sorted[]. */ + memcpy (packet_group->sorted, packet_group->insns, + sizeof (rtx) * packet_group->num_insns); + + /* Sort sorted[] by the unit that each insn tries to take first. */ + if (packet_group->num_insns > 1) + qsort (packet_group->sorted, packet_group->num_insns, + sizeof (rtx), frv_compare_insns); + + /* That's always enough for branch and control insns. */ + if (group == GROUP_B || group == GROUP_C) + return; + + dfa_size = state_size (); + state = alloca (dfa_size); + test_state = alloca (dfa_size); + + /* Find the highest FIRST such that sorted[0...FIRST-1] can issue + consecutively and such that the DFA takes unit X when sorted[X] + is added. Set STATE to the new DFA state. */ + state_reset (test_state); + for (first = 0; first < packet_group->num_insns; first++) + { + memcpy (state, test_state, dfa_size); + if (state_transition (test_state, packet_group->sorted[first]) >= 0 + || !cpu_unit_reservation_p (test_state, NTH_UNIT (group, first))) + break; + } + + /* If all the instructions issued in ascending order, we're done. */ + if (first == packet_group->num_insns) + return; + + /* Add nops to the end of sorted[] and try each permutation until + we find one that works. */ + for (nop = 0; nop < frv_num_nops; nop++) + { + max_unit = frv_insn_unit (frv_nops[nop]); + if (frv_unit_groups[max_unit] == group) + { + packet_group->nop = frv_nops[nop]; + num_slots = UNIT_NUMBER (max_unit) + 1; + for (i = packet_group->num_insns; i < num_slots; i++) + packet_group->sorted[i] = frv_nops[nop]; + if (frv_sort_insn_group_1 (group, first, num_slots, + (1 << first) - 1, num_slots, state)) + return; + } + } + gcc_unreachable (); +} + +/* Sort the current packet into assembly-language order. Set packing + flags as appropriate. */ + +static void +frv_reorder_packet (void) +{ + unsigned int cursor[NUM_GROUPS]; + rtx insns[ARRAY_SIZE (frv_unit_groups)]; + unsigned int unit, to, from; + enum frv_insn_group group; + struct frv_packet_group *packet_group; + + /* First sort each group individually. */ + for (group = GROUP_I; group < NUM_GROUPS; + group = (enum frv_insn_group) (group + 1)) + { + cursor[group] = 0; + frv_sort_insn_group (group); + } + + /* Go through the unit template and try add an instruction from + that unit's group. */ + to = 0; + for (unit = 0; unit < ARRAY_SIZE (frv_unit_groups); unit++) + { + group = frv_unit_groups[unit]; + packet_group = &frv_packet.groups[group]; + if (cursor[group] < packet_group->num_insns) + { + /* frv_reorg should have added nops for us. */ + gcc_assert (packet_group->sorted[cursor[group]] + != packet_group->nop); + insns[to++] = packet_group->sorted[cursor[group]++]; + } + } + + gcc_assert (to == frv_packet.num_insns); + + /* Clear the last instruction's packing flag, thus marking the end of + a packet. Reorder the other instructions relative to it. */ + CLEAR_PACKING_FLAG (insns[to - 1]); + for (from = 0; from < to - 1; from++) + { + remove_insn (insns[from]); + add_insn_before (insns[from], insns[to - 1], NULL); + SET_PACKING_FLAG (insns[from]); + } +} + + +/* Divide instructions into packets. Reorder the contents of each + packet so that they are in the correct assembly-language order. + + Since this pass can change the raw meaning of the rtl stream, it must + only be called at the last minute, just before the instructions are + written out. */ + +static void +frv_pack_insns (void) +{ + if (frv_for_each_packet (frv_reorder_packet)) + frv_insn_packing_flag = 0; + else + frv_insn_packing_flag = -1; +} + +/* See whether we need to add nops to group GROUP in order to + make a valid packet. */ + +static void +frv_fill_unused_units (enum frv_insn_group group) +{ + unsigned int non_nops, nops, i; + struct frv_packet_group *packet_group; + + packet_group = &frv_packet.groups[group]; + + /* Sort the instructions into assembly-language order. + Use nops to fill slots that are otherwise unused. */ + frv_sort_insn_group (group); + + /* See how many nops are needed before the final useful instruction. */ + i = nops = 0; + for (non_nops = 0; non_nops < packet_group->num_insns; non_nops++) + while (packet_group->sorted[i++] == packet_group->nop) + nops++; + + /* Insert that many nops into the instruction stream. */ + while (nops-- > 0) + frv_insert_nop_in_packet (packet_group->nop); +} + +/* Return true if accesses IO1 and IO2 refer to the same doubleword. */ + +static bool +frv_same_doubleword_p (const struct frv_io *io1, const struct frv_io *io2) +{ + if (io1->const_address != 0 && io2->const_address != 0) + return io1->const_address == io2->const_address; + + if (io1->var_address != 0 && io2->var_address != 0) + return rtx_equal_p (io1->var_address, io2->var_address); + + return false; +} + +/* Return true if operations IO1 and IO2 are guaranteed to complete + in order. */ + +static bool +frv_io_fixed_order_p (const struct frv_io *io1, const struct frv_io *io2) +{ + /* The order of writes is always preserved. */ + if (io1->type == FRV_IO_WRITE && io2->type == FRV_IO_WRITE) + return true; + + /* The order of reads isn't preserved. */ + if (io1->type != FRV_IO_WRITE && io2->type != FRV_IO_WRITE) + return false; + + /* One operation is a write and the other is (or could be) a read. + The order is only guaranteed if the accesses are to the same + doubleword. */ + return frv_same_doubleword_p (io1, io2); +} + +/* Generalize I/O operation X so that it covers both X and Y. */ + +static void +frv_io_union (struct frv_io *x, const struct frv_io *y) +{ + if (x->type != y->type) + x->type = FRV_IO_UNKNOWN; + if (!frv_same_doubleword_p (x, y)) + { + x->const_address = 0; + x->var_address = 0; + } +} + +/* Fill IO with information about the load or store associated with + membar instruction INSN. */ + +static void +frv_extract_membar (struct frv_io *io, rtx insn) +{ + extract_insn (insn); + io->type = (enum frv_io_type) INTVAL (recog_data.operand[2]); + io->const_address = INTVAL (recog_data.operand[1]); + io->var_address = XEXP (recog_data.operand[0], 0); +} + +/* A note_stores callback for which DATA points to an rtx. Nullify *DATA + if X is a register and *DATA depends on X. */ + +static void +frv_io_check_address (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data) +{ + rtx *other = (rtx *) data; + + if (REG_P (x) && *other != 0 && reg_overlap_mentioned_p (x, *other)) + *other = 0; +} + +/* A note_stores callback for which DATA points to a HARD_REG_SET. + Remove every modified register from the set. */ + +static void +frv_io_handle_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data) +{ + HARD_REG_SET *set = (HARD_REG_SET *) data; + unsigned int regno; + + if (REG_P (x)) + FOR_EACH_REGNO (regno, x) + CLEAR_HARD_REG_BIT (*set, regno); +} + +/* A for_each_rtx callback for which DATA points to a HARD_REG_SET. + Add every register in *X to the set. */ + +static int +frv_io_handle_use_1 (rtx *x, void *data) +{ + HARD_REG_SET *set = (HARD_REG_SET *) data; + unsigned int regno; + + if (REG_P (*x)) + FOR_EACH_REGNO (regno, *x) + SET_HARD_REG_BIT (*set, regno); + + return 0; +} + +/* A note_stores callback that applies frv_io_handle_use_1 to an + entire rhs value. */ + +static void +frv_io_handle_use (rtx *x, void *data) +{ + for_each_rtx (x, frv_io_handle_use_1, data); +} + +/* Go through block BB looking for membars to remove. There are two + cases where intra-block analysis is enough: + + - a membar is redundant if it occurs between two consecutive I/O + operations and if those operations are guaranteed to complete + in order. + + - a membar for a __builtin_read is redundant if the result is + used before the next I/O operation is issued. + + If the last membar in the block could not be removed, and there + are guaranteed to be no I/O operations between that membar and + the end of the block, store the membar in *LAST_MEMBAR, otherwise + store null. + + Describe the block's first I/O operation in *NEXT_IO. Describe + an unknown operation if the block doesn't do any I/O. */ + +static void +frv_optimize_membar_local (basic_block bb, struct frv_io *next_io, + rtx *last_membar) +{ + HARD_REG_SET used_regs; + rtx next_membar, set, insn; + bool next_is_end_p; + + /* NEXT_IO is the next I/O operation to be performed after the current + instruction. It starts off as being an unknown operation. */ + memset (next_io, 0, sizeof (*next_io)); + + /* NEXT_IS_END_P is true if NEXT_IO describes the end of the block. */ + next_is_end_p = true; + + /* If the current instruction is a __builtin_read or __builtin_write, + NEXT_MEMBAR is the membar instruction associated with it. NEXT_MEMBAR + is null if the membar has already been deleted. + + Note that the initialization here should only be needed to + suppress warnings. */ + next_membar = 0; + + /* USED_REGS is the set of registers that are used before the + next I/O instruction. */ + CLEAR_HARD_REG_SET (used_regs); + + for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN) + { + /* We can't predict what a call will do to volatile memory. */ + memset (next_io, 0, sizeof (struct frv_io)); + next_is_end_p = false; + CLEAR_HARD_REG_SET (used_regs); + } + else if (INSN_P (insn)) + switch (recog_memoized (insn)) + { + case CODE_FOR_optional_membar_qi: + case CODE_FOR_optional_membar_hi: + case CODE_FOR_optional_membar_si: + case CODE_FOR_optional_membar_di: + next_membar = insn; + if (next_is_end_p) + { + /* Local information isn't enough to decide whether this + membar is needed. Stash it away for later. */ + *last_membar = insn; + frv_extract_membar (next_io, insn); + next_is_end_p = false; + } + else + { + /* Check whether the I/O operation before INSN could be + reordered with one described by NEXT_IO. If it can't, + INSN will not be needed. */ + struct frv_io prev_io; + + frv_extract_membar (&prev_io, insn); + if (frv_io_fixed_order_p (&prev_io, next_io)) + { + if (dump_file) + fprintf (dump_file, + ";; [Local] Removing membar %d since order" + " of accesses is guaranteed\n", + INSN_UID (next_membar)); + + insn = NEXT_INSN (insn); + delete_insn (next_membar); + next_membar = 0; + } + *next_io = prev_io; + } + break; + + default: + /* Invalidate NEXT_IO's address if it depends on something that + is clobbered by INSN. */ + if (next_io->var_address) + note_stores (PATTERN (insn), frv_io_check_address, + &next_io->var_address); + + /* If the next membar is associated with a __builtin_read, + see if INSN reads from that address. If it does, and if + the destination register is used before the next I/O access, + there is no need for the membar. */ + set = PATTERN (insn); + if (next_io->type == FRV_IO_READ + && next_io->var_address != 0 + && next_membar != 0 + && GET_CODE (set) == SET + && GET_CODE (SET_DEST (set)) == REG + && TEST_HARD_REG_BIT (used_regs, REGNO (SET_DEST (set)))) + { + rtx src; + + src = SET_SRC (set); + if (GET_CODE (src) == ZERO_EXTEND) + src = XEXP (src, 0); + + if (GET_CODE (src) == MEM + && rtx_equal_p (XEXP (src, 0), next_io->var_address)) + { + if (dump_file) + fprintf (dump_file, + ";; [Local] Removing membar %d since the target" + " of %d is used before the I/O operation\n", + INSN_UID (next_membar), INSN_UID (insn)); + + if (next_membar == *last_membar) + *last_membar = 0; + + delete_insn (next_membar); + next_membar = 0; + } + } + + /* If INSN has volatile references, forget about any registers + that are used after it. Otherwise forget about uses that + are (or might be) defined by INSN. */ + if (volatile_refs_p (PATTERN (insn))) + CLEAR_HARD_REG_SET (used_regs); + else + note_stores (PATTERN (insn), frv_io_handle_set, &used_regs); + + note_uses (&PATTERN (insn), frv_io_handle_use, &used_regs); + break; + } +} + +/* See if MEMBAR, the last membar instruction in BB, can be removed. + FIRST_IO[X] describes the first operation performed by basic block X. */ + +static void +frv_optimize_membar_global (basic_block bb, struct frv_io *first_io, + rtx membar) +{ + struct frv_io this_io, next_io; + edge succ; + edge_iterator ei; + + /* We need to keep the membar if there is an edge to the exit block. */ + FOR_EACH_EDGE (succ, ei, bb->succs) + /* for (succ = bb->succ; succ != 0; succ = succ->succ_next) */ + if (succ->dest == EXIT_BLOCK_PTR) + return; + + /* Work out the union of all successor blocks. */ + ei = ei_start (bb->succs); + ei_cond (ei, &succ); + /* next_io = first_io[bb->succ->dest->index]; */ + next_io = first_io[succ->dest->index]; + ei = ei_start (bb->succs); + if (ei_cond (ei, &succ)) + { + for (ei_next (&ei); ei_cond (ei, &succ); ei_next (&ei)) + /*for (succ = bb->succ->succ_next; succ != 0; succ = succ->succ_next)*/ + frv_io_union (&next_io, &first_io[succ->dest->index]); + } + else + gcc_unreachable (); + + frv_extract_membar (&this_io, membar); + if (frv_io_fixed_order_p (&this_io, &next_io)) + { + if (dump_file) + fprintf (dump_file, + ";; [Global] Removing membar %d since order of accesses" + " is guaranteed\n", INSN_UID (membar)); + + delete_insn (membar); + } +} + +/* Remove redundant membars from the current function. */ + +static void +frv_optimize_membar (void) +{ + basic_block bb; + struct frv_io *first_io; + rtx *last_membar; + + compute_bb_for_insn (); + first_io = XCNEWVEC (struct frv_io, last_basic_block); + last_membar = XCNEWVEC (rtx, last_basic_block); + + FOR_EACH_BB (bb) + frv_optimize_membar_local (bb, &first_io[bb->index], + &last_membar[bb->index]); + + FOR_EACH_BB (bb) + if (last_membar[bb->index] != 0) + frv_optimize_membar_global (bb, first_io, last_membar[bb->index]); + + free (first_io); + free (last_membar); +} + +/* Used by frv_reorg to keep track of the current packet's address. */ +static unsigned int frv_packet_address; + +/* If the current packet falls through to a label, try to pad the packet + with nops in order to fit the label's alignment requirements. */ + +static void +frv_align_label (void) +{ + unsigned int alignment, target, nop; + rtx x, last, barrier, label; + + /* Walk forward to the start of the next packet. Set ALIGNMENT to the + maximum alignment of that packet, LABEL to the last label between + the packets, and BARRIER to the last barrier. */ + last = frv_packet.insns[frv_packet.num_insns - 1]; + label = barrier = 0; + alignment = 4; + for (x = NEXT_INSN (last); x != 0 && !INSN_P (x); x = NEXT_INSN (x)) + { + if (LABEL_P (x)) + { + unsigned int subalign = 1 << label_to_alignment (x); + alignment = MAX (alignment, subalign); + label = x; + } + if (BARRIER_P (x)) + barrier = x; + } + + /* If -malign-labels, and the packet falls through to an unaligned + label, try introducing a nop to align that label to 8 bytes. */ + if (TARGET_ALIGN_LABELS + && label != 0 + && barrier == 0 + && frv_packet.num_insns < frv_packet.issue_rate) + alignment = MAX (alignment, 8); + + /* Advance the address to the end of the current packet. */ + frv_packet_address += frv_packet.num_insns * 4; + + /* Work out the target address, after alignment. */ + target = (frv_packet_address + alignment - 1) & -alignment; + + /* If the packet falls through to the label, try to find an efficient + padding sequence. */ + if (barrier == 0) + { + /* First try adding nops to the current packet. */ + for (nop = 0; nop < frv_num_nops; nop++) + while (frv_packet_address < target && frv_pack_insn_p (frv_nops[nop])) + { + frv_insert_nop_in_packet (frv_nops[nop]); + frv_packet_address += 4; + } + + /* If we still haven't reached the target, add some new packets that + contain only nops. If there are two types of nop, insert an + alternating sequence of frv_nops[0] and frv_nops[1], which will + lead to packets like: + + nop.p + mnop.p/fnop.p + nop.p + mnop/fnop + + etc. Just emit frv_nops[0] if that's the only nop we have. */ + last = frv_packet.insns[frv_packet.num_insns - 1]; + nop = 0; + while (frv_packet_address < target) + { + last = emit_insn_after (PATTERN (frv_nops[nop]), last); + frv_packet_address += 4; + if (frv_num_nops > 1) + nop ^= 1; + } + } + + frv_packet_address = target; +} + +/* Subroutine of frv_reorg, called after each packet has been constructed + in frv_packet. */ + +static void +frv_reorg_packet (void) +{ + frv_fill_unused_units (GROUP_I); + frv_fill_unused_units (GROUP_FM); + frv_align_label (); +} + +/* Add an instruction with pattern NOP to frv_nops[]. */ + +static void +frv_register_nop (rtx nop) +{ + nop = make_insn_raw (nop); + NEXT_INSN (nop) = 0; + PREV_INSN (nop) = 0; + frv_nops[frv_num_nops++] = nop; +} + +/* Implement TARGET_MACHINE_DEPENDENT_REORG. Divide the instructions + into packets and check whether we need to insert nops in order to + fulfill the processor's issue requirements. Also, if the user has + requested a certain alignment for a label, try to meet that alignment + by inserting nops in the previous packet. */ + +static void +frv_reorg (void) +{ + if (optimize > 0 && TARGET_OPTIMIZE_MEMBAR && cfun->machine->has_membar_p) + frv_optimize_membar (); + + frv_num_nops = 0; + frv_register_nop (gen_nop ()); + if (TARGET_MEDIA) + frv_register_nop (gen_mnop ()); + if (TARGET_HARD_FLOAT) + frv_register_nop (gen_fnop ()); + + /* Estimate the length of each branch. Although this may change after + we've inserted nops, it will only do so in big functions. */ + shorten_branches (get_insns ()); + + frv_packet_address = 0; + frv_for_each_packet (frv_reorg_packet); +} + +#define def_builtin(name, type, code) \ + add_builtin_function ((name), (type), (code), BUILT_IN_MD, NULL, NULL) + +struct builtin_description +{ + enum insn_code icode; + const char *name; + enum frv_builtins code; + enum rtx_code comparison; + unsigned int flag; +}; + +/* Media intrinsics that take a single, constant argument. */ + +static struct builtin_description bdesc_set[] = +{ + { CODE_FOR_mhdsets, "__MHDSETS", FRV_BUILTIN_MHDSETS, UNKNOWN, 0 } +}; + +/* Media intrinsics that take just one argument. */ + +static struct builtin_description bdesc_1arg[] = +{ + { CODE_FOR_mnot, "__MNOT", FRV_BUILTIN_MNOT, UNKNOWN, 0 }, + { CODE_FOR_munpackh, "__MUNPACKH", FRV_BUILTIN_MUNPACKH, UNKNOWN, 0 }, + { CODE_FOR_mbtoh, "__MBTOH", FRV_BUILTIN_MBTOH, UNKNOWN, 0 }, + { CODE_FOR_mhtob, "__MHTOB", FRV_BUILTIN_MHTOB, UNKNOWN, 0}, + { CODE_FOR_mabshs, "__MABSHS", FRV_BUILTIN_MABSHS, UNKNOWN, 0 }, + { CODE_FOR_scutss, "__SCUTSS", FRV_BUILTIN_SCUTSS, UNKNOWN, 0 } +}; + +/* Media intrinsics that take two arguments. */ + +static struct builtin_description bdesc_2arg[] = +{ + { CODE_FOR_mand, "__MAND", FRV_BUILTIN_MAND, UNKNOWN, 0}, + { CODE_FOR_mor, "__MOR", FRV_BUILTIN_MOR, UNKNOWN, 0}, + { CODE_FOR_mxor, "__MXOR", FRV_BUILTIN_MXOR, UNKNOWN, 0}, + { CODE_FOR_maveh, "__MAVEH", FRV_BUILTIN_MAVEH, UNKNOWN, 0}, + { CODE_FOR_msaths, "__MSATHS", FRV_BUILTIN_MSATHS, UNKNOWN, 0}, + { CODE_FOR_msathu, "__MSATHU", FRV_BUILTIN_MSATHU, UNKNOWN, 0}, + { CODE_FOR_maddhss, "__MADDHSS", FRV_BUILTIN_MADDHSS, UNKNOWN, 0}, + { CODE_FOR_maddhus, "__MADDHUS", FRV_BUILTIN_MADDHUS, UNKNOWN, 0}, + { CODE_FOR_msubhss, "__MSUBHSS", FRV_BUILTIN_MSUBHSS, UNKNOWN, 0}, + { CODE_FOR_msubhus, "__MSUBHUS", FRV_BUILTIN_MSUBHUS, UNKNOWN, 0}, + { CODE_FOR_mqaddhss, "__MQADDHSS", FRV_BUILTIN_MQADDHSS, UNKNOWN, 0}, + { CODE_FOR_mqaddhus, "__MQADDHUS", FRV_BUILTIN_MQADDHUS, UNKNOWN, 0}, + { CODE_FOR_mqsubhss, "__MQSUBHSS", FRV_BUILTIN_MQSUBHSS, UNKNOWN, 0}, + { CODE_FOR_mqsubhus, "__MQSUBHUS", FRV_BUILTIN_MQSUBHUS, UNKNOWN, 0}, + { CODE_FOR_mpackh, "__MPACKH", FRV_BUILTIN_MPACKH, UNKNOWN, 0}, + { CODE_FOR_mcop1, "__Mcop1", FRV_BUILTIN_MCOP1, UNKNOWN, 0}, + { CODE_FOR_mcop2, "__Mcop2", FRV_BUILTIN_MCOP2, UNKNOWN, 0}, + { CODE_FOR_mwcut, "__MWCUT", FRV_BUILTIN_MWCUT, UNKNOWN, 0}, + { CODE_FOR_mqsaths, "__MQSATHS", FRV_BUILTIN_MQSATHS, UNKNOWN, 0}, + { CODE_FOR_mqlclrhs, "__MQLCLRHS", FRV_BUILTIN_MQLCLRHS, UNKNOWN, 0}, + { CODE_FOR_mqlmths, "__MQLMTHS", FRV_BUILTIN_MQLMTHS, UNKNOWN, 0}, + { CODE_FOR_smul, "__SMUL", FRV_BUILTIN_SMUL, UNKNOWN, 0}, + { CODE_FOR_umul, "__UMUL", FRV_BUILTIN_UMUL, UNKNOWN, 0}, + { CODE_FOR_addss, "__ADDSS", FRV_BUILTIN_ADDSS, UNKNOWN, 0}, + { CODE_FOR_subss, "__SUBSS", FRV_BUILTIN_SUBSS, UNKNOWN, 0}, + { CODE_FOR_slass, "__SLASS", FRV_BUILTIN_SLASS, UNKNOWN, 0}, + { CODE_FOR_scan, "__SCAN", FRV_BUILTIN_SCAN, UNKNOWN, 0} +}; + +/* Integer intrinsics that take two arguments and have no return value. */ + +static struct builtin_description bdesc_int_void2arg[] = +{ + { CODE_FOR_smass, "__SMASS", FRV_BUILTIN_SMASS, UNKNOWN, 0}, + { CODE_FOR_smsss, "__SMSSS", FRV_BUILTIN_SMSSS, UNKNOWN, 0}, + { CODE_FOR_smu, "__SMU", FRV_BUILTIN_SMU, UNKNOWN, 0} +}; + +static struct builtin_description bdesc_prefetches[] = +{ + { CODE_FOR_frv_prefetch0, "__data_prefetch0", FRV_BUILTIN_PREFETCH0, UNKNOWN, + 0}, + { CODE_FOR_frv_prefetch, "__data_prefetch", FRV_BUILTIN_PREFETCH, UNKNOWN, 0} +}; + +/* Media intrinsics that take two arguments, the first being an ACC number. */ + +static struct builtin_description bdesc_cut[] = +{ + { CODE_FOR_mcut, "__MCUT", FRV_BUILTIN_MCUT, UNKNOWN, 0}, + { CODE_FOR_mcutss, "__MCUTSS", FRV_BUILTIN_MCUTSS, UNKNOWN, 0}, + { CODE_FOR_mdcutssi, "__MDCUTSSI", FRV_BUILTIN_MDCUTSSI, UNKNOWN, 0} +}; + +/* Two-argument media intrinsics with an immediate second argument. */ + +static struct builtin_description bdesc_2argimm[] = +{ + { CODE_FOR_mrotli, "__MROTLI", FRV_BUILTIN_MROTLI, UNKNOWN, 0}, + { CODE_FOR_mrotri, "__MROTRI", FRV_BUILTIN_MROTRI, UNKNOWN, 0}, + { CODE_FOR_msllhi, "__MSLLHI", FRV_BUILTIN_MSLLHI, UNKNOWN, 0}, + { CODE_FOR_msrlhi, "__MSRLHI", FRV_BUILTIN_MSRLHI, UNKNOWN, 0}, + { CODE_FOR_msrahi, "__MSRAHI", FRV_BUILTIN_MSRAHI, UNKNOWN, 0}, + { CODE_FOR_mexpdhw, "__MEXPDHW", FRV_BUILTIN_MEXPDHW, UNKNOWN, 0}, + { CODE_FOR_mexpdhd, "__MEXPDHD", FRV_BUILTIN_MEXPDHD, UNKNOWN, 0}, + { CODE_FOR_mdrotli, "__MDROTLI", FRV_BUILTIN_MDROTLI, UNKNOWN, 0}, + { CODE_FOR_mcplhi, "__MCPLHI", FRV_BUILTIN_MCPLHI, UNKNOWN, 0}, + { CODE_FOR_mcpli, "__MCPLI", FRV_BUILTIN_MCPLI, UNKNOWN, 0}, + { CODE_FOR_mhsetlos, "__MHSETLOS", FRV_BUILTIN_MHSETLOS, UNKNOWN, 0}, + { CODE_FOR_mhsetloh, "__MHSETLOH", FRV_BUILTIN_MHSETLOH, UNKNOWN, 0}, + { CODE_FOR_mhsethis, "__MHSETHIS", FRV_BUILTIN_MHSETHIS, UNKNOWN, 0}, + { CODE_FOR_mhsethih, "__MHSETHIH", FRV_BUILTIN_MHSETHIH, UNKNOWN, 0}, + { CODE_FOR_mhdseth, "__MHDSETH", FRV_BUILTIN_MHDSETH, UNKNOWN, 0}, + { CODE_FOR_mqsllhi, "__MQSLLHI", FRV_BUILTIN_MQSLLHI, UNKNOWN, 0}, + { CODE_FOR_mqsrahi, "__MQSRAHI", FRV_BUILTIN_MQSRAHI, UNKNOWN, 0} +}; + +/* Media intrinsics that take two arguments and return void, the first argument + being a pointer to 4 words in memory. */ + +static struct builtin_description bdesc_void2arg[] = +{ + { CODE_FOR_mdunpackh, "__MDUNPACKH", FRV_BUILTIN_MDUNPACKH, UNKNOWN, 0}, + { CODE_FOR_mbtohe, "__MBTOHE", FRV_BUILTIN_MBTOHE, UNKNOWN, 0}, +}; + +/* Media intrinsics that take three arguments, the first being a const_int that + denotes an accumulator, and that return void. */ + +static struct builtin_description bdesc_void3arg[] = +{ + { CODE_FOR_mcpxrs, "__MCPXRS", FRV_BUILTIN_MCPXRS, UNKNOWN, 0}, + { CODE_FOR_mcpxru, "__MCPXRU", FRV_BUILTIN_MCPXRU, UNKNOWN, 0}, + { CODE_FOR_mcpxis, "__MCPXIS", FRV_BUILTIN_MCPXIS, UNKNOWN, 0}, + { CODE_FOR_mcpxiu, "__MCPXIU", FRV_BUILTIN_MCPXIU, UNKNOWN, 0}, + { CODE_FOR_mmulhs, "__MMULHS", FRV_BUILTIN_MMULHS, UNKNOWN, 0}, + { CODE_FOR_mmulhu, "__MMULHU", FRV_BUILTIN_MMULHU, UNKNOWN, 0}, + { CODE_FOR_mmulxhs, "__MMULXHS", FRV_BUILTIN_MMULXHS, UNKNOWN, 0}, + { CODE_FOR_mmulxhu, "__MMULXHU", FRV_BUILTIN_MMULXHU, UNKNOWN, 0}, + { CODE_FOR_mmachs, "__MMACHS", FRV_BUILTIN_MMACHS, UNKNOWN, 0}, + { CODE_FOR_mmachu, "__MMACHU", FRV_BUILTIN_MMACHU, UNKNOWN, 0}, + { CODE_FOR_mmrdhs, "__MMRDHS", FRV_BUILTIN_MMRDHS, UNKNOWN, 0}, + { CODE_FOR_mmrdhu, "__MMRDHU", FRV_BUILTIN_MMRDHU, UNKNOWN, 0}, + { CODE_FOR_mqcpxrs, "__MQCPXRS", FRV_BUILTIN_MQCPXRS, UNKNOWN, 0}, + { CODE_FOR_mqcpxru, "__MQCPXRU", FRV_BUILTIN_MQCPXRU, UNKNOWN, 0}, + { CODE_FOR_mqcpxis, "__MQCPXIS", FRV_BUILTIN_MQCPXIS, UNKNOWN, 0}, + { CODE_FOR_mqcpxiu, "__MQCPXIU", FRV_BUILTIN_MQCPXIU, UNKNOWN, 0}, + { CODE_FOR_mqmulhs, "__MQMULHS", FRV_BUILTIN_MQMULHS, UNKNOWN, 0}, + { CODE_FOR_mqmulhu, "__MQMULHU", FRV_BUILTIN_MQMULHU, UNKNOWN, 0}, + { CODE_FOR_mqmulxhs, "__MQMULXHS", FRV_BUILTIN_MQMULXHS, UNKNOWN, 0}, + { CODE_FOR_mqmulxhu, "__MQMULXHU", FRV_BUILTIN_MQMULXHU, UNKNOWN, 0}, + { CODE_FOR_mqmachs, "__MQMACHS", FRV_BUILTIN_MQMACHS, UNKNOWN, 0}, + { CODE_FOR_mqmachu, "__MQMACHU", FRV_BUILTIN_MQMACHU, UNKNOWN, 0}, + { CODE_FOR_mqxmachs, "__MQXMACHS", FRV_BUILTIN_MQXMACHS, UNKNOWN, 0}, + { CODE_FOR_mqxmacxhs, "__MQXMACXHS", FRV_BUILTIN_MQXMACXHS, UNKNOWN, 0}, + { CODE_FOR_mqmacxhs, "__MQMACXHS", FRV_BUILTIN_MQMACXHS, UNKNOWN, 0} +}; + +/* Media intrinsics that take two accumulator numbers as argument and + return void. */ + +static struct builtin_description bdesc_voidacc[] = +{ + { CODE_FOR_maddaccs, "__MADDACCS", FRV_BUILTIN_MADDACCS, UNKNOWN, 0}, + { CODE_FOR_msubaccs, "__MSUBACCS", FRV_BUILTIN_MSUBACCS, UNKNOWN, 0}, + { CODE_FOR_masaccs, "__MASACCS", FRV_BUILTIN_MASACCS, UNKNOWN, 0}, + { CODE_FOR_mdaddaccs, "__MDADDACCS", FRV_BUILTIN_MDADDACCS, UNKNOWN, 0}, + { CODE_FOR_mdsubaccs, "__MDSUBACCS", FRV_BUILTIN_MDSUBACCS, UNKNOWN, 0}, + { CODE_FOR_mdasaccs, "__MDASACCS", FRV_BUILTIN_MDASACCS, UNKNOWN, 0} +}; + +/* Intrinsics that load a value and then issue a MEMBAR. The load is + a normal move and the ICODE is for the membar. */ + +static struct builtin_description bdesc_loads[] = +{ + { CODE_FOR_optional_membar_qi, "__builtin_read8", + FRV_BUILTIN_READ8, UNKNOWN, 0}, + { CODE_FOR_optional_membar_hi, "__builtin_read16", + FRV_BUILTIN_READ16, UNKNOWN, 0}, + { CODE_FOR_optional_membar_si, "__builtin_read32", + FRV_BUILTIN_READ32, UNKNOWN, 0}, + { CODE_FOR_optional_membar_di, "__builtin_read64", + FRV_BUILTIN_READ64, UNKNOWN, 0} +}; + +/* Likewise stores. */ + +static struct builtin_description bdesc_stores[] = +{ + { CODE_FOR_optional_membar_qi, "__builtin_write8", + FRV_BUILTIN_WRITE8, UNKNOWN, 0}, + { CODE_FOR_optional_membar_hi, "__builtin_write16", + FRV_BUILTIN_WRITE16, UNKNOWN, 0}, + { CODE_FOR_optional_membar_si, "__builtin_write32", + FRV_BUILTIN_WRITE32, UNKNOWN, 0}, + { CODE_FOR_optional_membar_di, "__builtin_write64", + FRV_BUILTIN_WRITE64, UNKNOWN, 0}, +}; + +/* Initialize media builtins. */ + +static void +frv_init_builtins (void) +{ + tree endlink = void_list_node; + tree accumulator = integer_type_node; + tree integer = integer_type_node; + tree voidt = void_type_node; + tree uhalf = short_unsigned_type_node; + tree sword1 = long_integer_type_node; + tree uword1 = long_unsigned_type_node; + tree sword2 = long_long_integer_type_node; + tree uword2 = long_long_unsigned_type_node; + tree uword4 = build_pointer_type (uword1); + tree vptr = build_pointer_type (build_type_variant (void_type_node, 0, 1)); + tree ubyte = unsigned_char_type_node; + tree iacc = integer_type_node; + +#define UNARY(RET, T1) \ + build_function_type (RET, tree_cons (NULL_TREE, T1, endlink)) + +#define BINARY(RET, T1, T2) \ + build_function_type (RET, tree_cons (NULL_TREE, T1, \ + tree_cons (NULL_TREE, T2, endlink))) + +#define TRINARY(RET, T1, T2, T3) \ + build_function_type (RET, tree_cons (NULL_TREE, T1, \ + tree_cons (NULL_TREE, T2, \ + tree_cons (NULL_TREE, T3, endlink)))) + +#define QUAD(RET, T1, T2, T3, T4) \ + build_function_type (RET, tree_cons (NULL_TREE, T1, \ + tree_cons (NULL_TREE, T2, \ + tree_cons (NULL_TREE, T3, \ + tree_cons (NULL_TREE, T4, endlink))))) + + tree void_ftype_void = build_function_type (voidt, endlink); + + tree void_ftype_acc = UNARY (voidt, accumulator); + tree void_ftype_uw4_uw1 = BINARY (voidt, uword4, uword1); + tree void_ftype_uw4_uw2 = BINARY (voidt, uword4, uword2); + tree void_ftype_acc_uw1 = BINARY (voidt, accumulator, uword1); + tree void_ftype_acc_acc = BINARY (voidt, accumulator, accumulator); + tree void_ftype_acc_uw1_uw1 = TRINARY (voidt, accumulator, uword1, uword1); + tree void_ftype_acc_sw1_sw1 = TRINARY (voidt, accumulator, sword1, sword1); + tree void_ftype_acc_uw2_uw2 = TRINARY (voidt, accumulator, uword2, uword2); + tree void_ftype_acc_sw2_sw2 = TRINARY (voidt, accumulator, sword2, sword2); + + tree uw1_ftype_uw1 = UNARY (uword1, uword1); + tree uw1_ftype_sw1 = UNARY (uword1, sword1); + tree uw1_ftype_uw2 = UNARY (uword1, uword2); + tree uw1_ftype_acc = UNARY (uword1, accumulator); + tree uw1_ftype_uh_uh = BINARY (uword1, uhalf, uhalf); + tree uw1_ftype_uw1_uw1 = BINARY (uword1, uword1, uword1); + tree uw1_ftype_uw1_int = BINARY (uword1, uword1, integer); + tree uw1_ftype_acc_uw1 = BINARY (uword1, accumulator, uword1); + tree uw1_ftype_acc_sw1 = BINARY (uword1, accumulator, sword1); + tree uw1_ftype_uw2_uw1 = BINARY (uword1, uword2, uword1); + tree uw1_ftype_uw2_int = BINARY (uword1, uword2, integer); + + tree sw1_ftype_int = UNARY (sword1, integer); + tree sw1_ftype_sw1_sw1 = BINARY (sword1, sword1, sword1); + tree sw1_ftype_sw1_int = BINARY (sword1, sword1, integer); + + tree uw2_ftype_uw1 = UNARY (uword2, uword1); + tree uw2_ftype_uw1_int = BINARY (uword2, uword1, integer); + tree uw2_ftype_uw2_uw2 = BINARY (uword2, uword2, uword2); + tree uw2_ftype_uw2_int = BINARY (uword2, uword2, integer); + tree uw2_ftype_acc_int = BINARY (uword2, accumulator, integer); + tree uw2_ftype_uh_uh_uh_uh = QUAD (uword2, uhalf, uhalf, uhalf, uhalf); + + tree sw2_ftype_sw2_sw2 = BINARY (sword2, sword2, sword2); + tree sw2_ftype_sw2_int = BINARY (sword2, sword2, integer); + tree uw2_ftype_uw1_uw1 = BINARY (uword2, uword1, uword1); + tree sw2_ftype_sw1_sw1 = BINARY (sword2, sword1, sword1); + tree void_ftype_sw1_sw1 = BINARY (voidt, sword1, sword1); + tree void_ftype_iacc_sw2 = BINARY (voidt, iacc, sword2); + tree void_ftype_iacc_sw1 = BINARY (voidt, iacc, sword1); + tree sw1_ftype_sw1 = UNARY (sword1, sword1); + tree sw2_ftype_iacc = UNARY (sword2, iacc); + tree sw1_ftype_iacc = UNARY (sword1, iacc); + tree void_ftype_ptr = UNARY (voidt, const_ptr_type_node); + tree uw1_ftype_vptr = UNARY (uword1, vptr); + tree uw2_ftype_vptr = UNARY (uword2, vptr); + tree void_ftype_vptr_ub = BINARY (voidt, vptr, ubyte); + tree void_ftype_vptr_uh = BINARY (voidt, vptr, uhalf); + tree void_ftype_vptr_uw1 = BINARY (voidt, vptr, uword1); + tree void_ftype_vptr_uw2 = BINARY (voidt, vptr, uword2); + + def_builtin ("__MAND", uw1_ftype_uw1_uw1, FRV_BUILTIN_MAND); + def_builtin ("__MOR", uw1_ftype_uw1_uw1, FRV_BUILTIN_MOR); + def_builtin ("__MXOR", uw1_ftype_uw1_uw1, FRV_BUILTIN_MXOR); + def_builtin ("__MNOT", uw1_ftype_uw1, FRV_BUILTIN_MNOT); + def_builtin ("__MROTLI", uw1_ftype_uw1_int, FRV_BUILTIN_MROTLI); + def_builtin ("__MROTRI", uw1_ftype_uw1_int, FRV_BUILTIN_MROTRI); + def_builtin ("__MWCUT", uw1_ftype_uw2_uw1, FRV_BUILTIN_MWCUT); + def_builtin ("__MAVEH", uw1_ftype_uw1_uw1, FRV_BUILTIN_MAVEH); + def_builtin ("__MSLLHI", uw1_ftype_uw1_int, FRV_BUILTIN_MSLLHI); + def_builtin ("__MSRLHI", uw1_ftype_uw1_int, FRV_BUILTIN_MSRLHI); + def_builtin ("__MSRAHI", sw1_ftype_sw1_int, FRV_BUILTIN_MSRAHI); + def_builtin ("__MSATHS", sw1_ftype_sw1_sw1, FRV_BUILTIN_MSATHS); + def_builtin ("__MSATHU", uw1_ftype_uw1_uw1, FRV_BUILTIN_MSATHU); + def_builtin ("__MADDHSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_MADDHSS); + def_builtin ("__MADDHUS", uw1_ftype_uw1_uw1, FRV_BUILTIN_MADDHUS); + def_builtin ("__MSUBHSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_MSUBHSS); + def_builtin ("__MSUBHUS", uw1_ftype_uw1_uw1, FRV_BUILTIN_MSUBHUS); + def_builtin ("__MMULHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMULHS); + def_builtin ("__MMULHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMULHU); + def_builtin ("__MMULXHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMULXHS); + def_builtin ("__MMULXHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMULXHU); + def_builtin ("__MMACHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMACHS); + def_builtin ("__MMACHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMACHU); + def_builtin ("__MMRDHS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MMRDHS); + def_builtin ("__MMRDHU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MMRDHU); + def_builtin ("__MQADDHSS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQADDHSS); + def_builtin ("__MQADDHUS", uw2_ftype_uw2_uw2, FRV_BUILTIN_MQADDHUS); + def_builtin ("__MQSUBHSS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQSUBHSS); + def_builtin ("__MQSUBHUS", uw2_ftype_uw2_uw2, FRV_BUILTIN_MQSUBHUS); + def_builtin ("__MQMULHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMULHS); + def_builtin ("__MQMULHU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQMULHU); + def_builtin ("__MQMULXHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMULXHS); + def_builtin ("__MQMULXHU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQMULXHU); + def_builtin ("__MQMACHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMACHS); + def_builtin ("__MQMACHU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQMACHU); + def_builtin ("__MCPXRS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MCPXRS); + def_builtin ("__MCPXRU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MCPXRU); + def_builtin ("__MCPXIS", void_ftype_acc_sw1_sw1, FRV_BUILTIN_MCPXIS); + def_builtin ("__MCPXIU", void_ftype_acc_uw1_uw1, FRV_BUILTIN_MCPXIU); + def_builtin ("__MQCPXRS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQCPXRS); + def_builtin ("__MQCPXRU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQCPXRU); + def_builtin ("__MQCPXIS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQCPXIS); + def_builtin ("__MQCPXIU", void_ftype_acc_uw2_uw2, FRV_BUILTIN_MQCPXIU); + def_builtin ("__MCUT", uw1_ftype_acc_uw1, FRV_BUILTIN_MCUT); + def_builtin ("__MCUTSS", uw1_ftype_acc_sw1, FRV_BUILTIN_MCUTSS); + def_builtin ("__MEXPDHW", uw1_ftype_uw1_int, FRV_BUILTIN_MEXPDHW); + def_builtin ("__MEXPDHD", uw2_ftype_uw1_int, FRV_BUILTIN_MEXPDHD); + def_builtin ("__MPACKH", uw1_ftype_uh_uh, FRV_BUILTIN_MPACKH); + def_builtin ("__MUNPACKH", uw2_ftype_uw1, FRV_BUILTIN_MUNPACKH); + def_builtin ("__MDPACKH", uw2_ftype_uh_uh_uh_uh, FRV_BUILTIN_MDPACKH); + def_builtin ("__MDUNPACKH", void_ftype_uw4_uw2, FRV_BUILTIN_MDUNPACKH); + def_builtin ("__MBTOH", uw2_ftype_uw1, FRV_BUILTIN_MBTOH); + def_builtin ("__MHTOB", uw1_ftype_uw2, FRV_BUILTIN_MHTOB); + def_builtin ("__MBTOHE", void_ftype_uw4_uw1, FRV_BUILTIN_MBTOHE); + def_builtin ("__MCLRACC", void_ftype_acc, FRV_BUILTIN_MCLRACC); + def_builtin ("__MCLRACCA", void_ftype_void, FRV_BUILTIN_MCLRACCA); + def_builtin ("__MRDACC", uw1_ftype_acc, FRV_BUILTIN_MRDACC); + def_builtin ("__MRDACCG", uw1_ftype_acc, FRV_BUILTIN_MRDACCG); + def_builtin ("__MWTACC", void_ftype_acc_uw1, FRV_BUILTIN_MWTACC); + def_builtin ("__MWTACCG", void_ftype_acc_uw1, FRV_BUILTIN_MWTACCG); + def_builtin ("__Mcop1", uw1_ftype_uw1_uw1, FRV_BUILTIN_MCOP1); + def_builtin ("__Mcop2", uw1_ftype_uw1_uw1, FRV_BUILTIN_MCOP2); + def_builtin ("__MTRAP", void_ftype_void, FRV_BUILTIN_MTRAP); + def_builtin ("__MQXMACHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQXMACHS); + def_builtin ("__MQXMACXHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQXMACXHS); + def_builtin ("__MQMACXHS", void_ftype_acc_sw2_sw2, FRV_BUILTIN_MQMACXHS); + def_builtin ("__MADDACCS", void_ftype_acc_acc, FRV_BUILTIN_MADDACCS); + def_builtin ("__MSUBACCS", void_ftype_acc_acc, FRV_BUILTIN_MSUBACCS); + def_builtin ("__MASACCS", void_ftype_acc_acc, FRV_BUILTIN_MASACCS); + def_builtin ("__MDADDACCS", void_ftype_acc_acc, FRV_BUILTIN_MDADDACCS); + def_builtin ("__MDSUBACCS", void_ftype_acc_acc, FRV_BUILTIN_MDSUBACCS); + def_builtin ("__MDASACCS", void_ftype_acc_acc, FRV_BUILTIN_MDASACCS); + def_builtin ("__MABSHS", uw1_ftype_sw1, FRV_BUILTIN_MABSHS); + def_builtin ("__MDROTLI", uw2_ftype_uw2_int, FRV_BUILTIN_MDROTLI); + def_builtin ("__MCPLHI", uw1_ftype_uw2_int, FRV_BUILTIN_MCPLHI); + def_builtin ("__MCPLI", uw1_ftype_uw2_int, FRV_BUILTIN_MCPLI); + def_builtin ("__MDCUTSSI", uw2_ftype_acc_int, FRV_BUILTIN_MDCUTSSI); + def_builtin ("__MQSATHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQSATHS); + def_builtin ("__MHSETLOS", sw1_ftype_sw1_int, FRV_BUILTIN_MHSETLOS); + def_builtin ("__MHSETHIS", sw1_ftype_sw1_int, FRV_BUILTIN_MHSETHIS); + def_builtin ("__MHDSETS", sw1_ftype_int, FRV_BUILTIN_MHDSETS); + def_builtin ("__MHSETLOH", uw1_ftype_uw1_int, FRV_BUILTIN_MHSETLOH); + def_builtin ("__MHSETHIH", uw1_ftype_uw1_int, FRV_BUILTIN_MHSETHIH); + def_builtin ("__MHDSETH", uw1_ftype_uw1_int, FRV_BUILTIN_MHDSETH); + def_builtin ("__MQLCLRHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQLCLRHS); + def_builtin ("__MQLMTHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQLMTHS); + def_builtin ("__MQSLLHI", uw2_ftype_uw2_int, FRV_BUILTIN_MQSLLHI); + def_builtin ("__MQSRAHI", sw2_ftype_sw2_int, FRV_BUILTIN_MQSRAHI); + def_builtin ("__SMUL", sw2_ftype_sw1_sw1, FRV_BUILTIN_SMUL); + def_builtin ("__UMUL", uw2_ftype_uw1_uw1, FRV_BUILTIN_UMUL); + def_builtin ("__SMASS", void_ftype_sw1_sw1, FRV_BUILTIN_SMASS); + def_builtin ("__SMSSS", void_ftype_sw1_sw1, FRV_BUILTIN_SMSSS); + def_builtin ("__SMU", void_ftype_sw1_sw1, FRV_BUILTIN_SMU); + def_builtin ("__ADDSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_ADDSS); + def_builtin ("__SUBSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_SUBSS); + def_builtin ("__SLASS", sw1_ftype_sw1_sw1, FRV_BUILTIN_SLASS); + def_builtin ("__SCAN", sw1_ftype_sw1_sw1, FRV_BUILTIN_SCAN); + def_builtin ("__SCUTSS", sw1_ftype_sw1, FRV_BUILTIN_SCUTSS); + def_builtin ("__IACCreadll", sw2_ftype_iacc, FRV_BUILTIN_IACCreadll); + def_builtin ("__IACCreadl", sw1_ftype_iacc, FRV_BUILTIN_IACCreadl); + def_builtin ("__IACCsetll", void_ftype_iacc_sw2, FRV_BUILTIN_IACCsetll); + def_builtin ("__IACCsetl", void_ftype_iacc_sw1, FRV_BUILTIN_IACCsetl); + def_builtin ("__data_prefetch0", void_ftype_ptr, FRV_BUILTIN_PREFETCH0); + def_builtin ("__data_prefetch", void_ftype_ptr, FRV_BUILTIN_PREFETCH); + def_builtin ("__builtin_read8", uw1_ftype_vptr, FRV_BUILTIN_READ8); + def_builtin ("__builtin_read16", uw1_ftype_vptr, FRV_BUILTIN_READ16); + def_builtin ("__builtin_read32", uw1_ftype_vptr, FRV_BUILTIN_READ32); + def_builtin ("__builtin_read64", uw2_ftype_vptr, FRV_BUILTIN_READ64); + + def_builtin ("__builtin_write8", void_ftype_vptr_ub, FRV_BUILTIN_WRITE8); + def_builtin ("__builtin_write16", void_ftype_vptr_uh, FRV_BUILTIN_WRITE16); + def_builtin ("__builtin_write32", void_ftype_vptr_uw1, FRV_BUILTIN_WRITE32); + def_builtin ("__builtin_write64", void_ftype_vptr_uw2, FRV_BUILTIN_WRITE64); + +#undef UNARY +#undef BINARY +#undef TRINARY +#undef QUAD +} + +/* Set the names for various arithmetic operations according to the + FRV ABI. */ +static void +frv_init_libfuncs (void) +{ + set_optab_libfunc (smod_optab, SImode, "__modi"); + set_optab_libfunc (umod_optab, SImode, "__umodi"); + + set_optab_libfunc (add_optab, DImode, "__addll"); + set_optab_libfunc (sub_optab, DImode, "__subll"); + set_optab_libfunc (smul_optab, DImode, "__mulll"); + set_optab_libfunc (sdiv_optab, DImode, "__divll"); + set_optab_libfunc (smod_optab, DImode, "__modll"); + set_optab_libfunc (umod_optab, DImode, "__umodll"); + set_optab_libfunc (and_optab, DImode, "__andll"); + set_optab_libfunc (ior_optab, DImode, "__orll"); + set_optab_libfunc (xor_optab, DImode, "__xorll"); + set_optab_libfunc (one_cmpl_optab, DImode, "__notll"); + + set_optab_libfunc (add_optab, SFmode, "__addf"); + set_optab_libfunc (sub_optab, SFmode, "__subf"); + set_optab_libfunc (smul_optab, SFmode, "__mulf"); + set_optab_libfunc (sdiv_optab, SFmode, "__divf"); + + set_optab_libfunc (add_optab, DFmode, "__addd"); + set_optab_libfunc (sub_optab, DFmode, "__subd"); + set_optab_libfunc (smul_optab, DFmode, "__muld"); + set_optab_libfunc (sdiv_optab, DFmode, "__divd"); + + set_conv_libfunc (sext_optab, DFmode, SFmode, "__ftod"); + set_conv_libfunc (trunc_optab, SFmode, DFmode, "__dtof"); + + set_conv_libfunc (sfix_optab, SImode, SFmode, "__ftoi"); + set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftoll"); + set_conv_libfunc (sfix_optab, SImode, DFmode, "__dtoi"); + set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtoll"); + + set_conv_libfunc (ufix_optab, SImode, SFmode, "__ftoui"); + set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoull"); + set_conv_libfunc (ufix_optab, SImode, DFmode, "__dtoui"); + set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoull"); + + set_conv_libfunc (sfloat_optab, SFmode, SImode, "__itof"); + set_conv_libfunc (sfloat_optab, SFmode, DImode, "__lltof"); + set_conv_libfunc (sfloat_optab, DFmode, SImode, "__itod"); + set_conv_libfunc (sfloat_optab, DFmode, DImode, "__lltod"); +} + +/* Convert an integer constant to an accumulator register. ICODE is the + code of the target instruction, OPNUM is the number of the + accumulator operand and OPVAL is the constant integer. Try both + ACC and ACCG registers; only report an error if neither fit the + instruction. */ + +static rtx +frv_int_to_acc (enum insn_code icode, int opnum, rtx opval) +{ + rtx reg; + int i; + + /* ACCs and ACCGs are implicit global registers if media intrinsics + are being used. We set up this lazily to avoid creating lots of + unnecessary call_insn rtl in non-media code. */ + for (i = 0; i <= ACC_MASK; i++) + if ((i & ACC_MASK) == i) + global_regs[i + ACC_FIRST] = global_regs[i + ACCG_FIRST] = 1; + + if (GET_CODE (opval) != CONST_INT) + { + error ("accumulator is not a constant integer"); + return NULL_RTX; + } + if ((INTVAL (opval) & ~ACC_MASK) != 0) + { + error ("accumulator number is out of bounds"); + return NULL_RTX; + } + + reg = gen_rtx_REG (insn_data[icode].operand[opnum].mode, + ACC_FIRST + INTVAL (opval)); + if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode)) + SET_REGNO (reg, ACCG_FIRST + INTVAL (opval)); + + if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode)) + { + error ("inappropriate accumulator for %qs", insn_data[icode].name); + return NULL_RTX; + } + return reg; +} + +/* If an ACC rtx has mode MODE, return the mode that the matching ACCG + should have. */ + +static enum machine_mode +frv_matching_accg_mode (enum machine_mode mode) +{ + switch (mode) + { + case V4SImode: + return V4QImode; + + case DImode: + return HImode; + + case SImode: + return QImode; + + default: + gcc_unreachable (); + } +} + +/* Given that a __builtin_read or __builtin_write function is accessing + address ADDRESS, return the value that should be used as operand 1 + of the membar. */ + +static rtx +frv_io_address_cookie (rtx address) +{ + return (GET_CODE (address) == CONST_INT + ? GEN_INT (INTVAL (address) / 8 * 8) + : const0_rtx); +} + +/* Return the accumulator guard that should be paired with accumulator + register ACC. The mode of the returned register is in the same + class as ACC, but is four times smaller. */ + +rtx +frv_matching_accg_for_acc (rtx acc) +{ + return gen_rtx_REG (frv_matching_accg_mode (GET_MODE (acc)), + REGNO (acc) - ACC_FIRST + ACCG_FIRST); +} + +/* Read the requested argument from the call EXP given by INDEX. + Return the value as an rtx. */ + +static rtx +frv_read_argument (tree exp, unsigned int index) +{ + return expand_normal (CALL_EXPR_ARG (exp, index)); +} + +/* Like frv_read_argument, but interpret the argument as the number + of an IACC register and return a (reg:MODE ...) rtx for it. */ + +static rtx +frv_read_iacc_argument (enum machine_mode mode, tree call, + unsigned int index) +{ + int i, regno; + rtx op; + + op = frv_read_argument (call, index); + if (GET_CODE (op) != CONST_INT + || INTVAL (op) < 0 + || INTVAL (op) > IACC_LAST - IACC_FIRST + || ((INTVAL (op) * 4) & (GET_MODE_SIZE (mode) - 1)) != 0) + { + error ("invalid IACC argument"); + op = const0_rtx; + } + + /* IACCs are implicit global registers. We set up this lazily to + avoid creating lots of unnecessary call_insn rtl when IACCs aren't + being used. */ + regno = INTVAL (op) + IACC_FIRST; + for (i = 0; i < HARD_REGNO_NREGS (regno, mode); i++) + global_regs[regno + i] = 1; + + return gen_rtx_REG (mode, regno); +} + +/* Return true if OPVAL can be used for operand OPNUM of instruction ICODE. + The instruction should require a constant operand of some sort. The + function prints an error if OPVAL is not valid. */ + +static int +frv_check_constant_argument (enum insn_code icode, int opnum, rtx opval) +{ + if (GET_CODE (opval) != CONST_INT) + { + error ("%qs expects a constant argument", insn_data[icode].name); + return FALSE; + } + if (! (*insn_data[icode].operand[opnum].predicate) (opval, VOIDmode)) + { + error ("constant argument out of range for %qs", insn_data[icode].name); + return FALSE; + } + return TRUE; +} + +/* Return a legitimate rtx for instruction ICODE's return value. Use TARGET + if it's not null, has the right mode, and satisfies operand 0's + predicate. */ + +static rtx +frv_legitimize_target (enum insn_code icode, rtx target) +{ + enum machine_mode mode = insn_data[icode].operand[0].mode; + + if (! target + || GET_MODE (target) != mode + || ! (*insn_data[icode].operand[0].predicate) (target, mode)) + return gen_reg_rtx (mode); + else + return target; +} + +/* Given that ARG is being passed as operand OPNUM to instruction ICODE, + check whether ARG satisfies the operand's constraints. If it doesn't, + copy ARG to a temporary register and return that. Otherwise return ARG + itself. */ + +static rtx +frv_legitimize_argument (enum insn_code icode, int opnum, rtx arg) +{ + enum machine_mode mode = insn_data[icode].operand[opnum].mode; + + if ((*insn_data[icode].operand[opnum].predicate) (arg, mode)) + return arg; + else + return copy_to_mode_reg (mode, arg); +} + +/* Return a volatile memory reference of mode MODE whose address is ARG. */ + +static rtx +frv_volatile_memref (enum machine_mode mode, rtx arg) +{ + rtx mem; + + mem = gen_rtx_MEM (mode, memory_address (mode, arg)); + MEM_VOLATILE_P (mem) = 1; + return mem; +} + +/* Expand builtins that take a single, constant argument. At the moment, + only MHDSETS falls into this category. */ + +static rtx +frv_expand_set_builtin (enum insn_code icode, tree call, rtx target) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + + if (! frv_check_constant_argument (icode, 1, op0)) + return NULL_RTX; + + target = frv_legitimize_target (icode, target); + pat = GEN_FCN (icode) (target, op0); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand builtins that take one operand. */ + +static rtx +frv_expand_unop_builtin (enum insn_code icode, tree call, rtx target) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + + target = frv_legitimize_target (icode, target); + op0 = frv_legitimize_argument (icode, 1, op0); + pat = GEN_FCN (icode) (target, op0); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand builtins that take two operands. */ + +static rtx +frv_expand_binop_builtin (enum insn_code icode, tree call, rtx target) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + + target = frv_legitimize_target (icode, target); + op0 = frv_legitimize_argument (icode, 1, op0); + op1 = frv_legitimize_argument (icode, 2, op1); + pat = GEN_FCN (icode) (target, op0, op1); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand cut-style builtins, which take two operands and an implicit ACCG + one. */ + +static rtx +frv_expand_cut_builtin (enum insn_code icode, tree call, rtx target) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + rtx op2; + + target = frv_legitimize_target (icode, target); + op0 = frv_int_to_acc (icode, 1, op0); + if (! op0) + return NULL_RTX; + + if (icode == CODE_FOR_mdcutssi || GET_CODE (op1) == CONST_INT) + { + if (! frv_check_constant_argument (icode, 2, op1)) + return NULL_RTX; + } + else + op1 = frv_legitimize_argument (icode, 2, op1); + + op2 = frv_matching_accg_for_acc (op0); + pat = GEN_FCN (icode) (target, op0, op1, op2); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand builtins that take two operands and the second is immediate. */ + +static rtx +frv_expand_binopimm_builtin (enum insn_code icode, tree call, rtx target) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + + if (! frv_check_constant_argument (icode, 2, op1)) + return NULL_RTX; + + target = frv_legitimize_target (icode, target); + op0 = frv_legitimize_argument (icode, 1, op0); + pat = GEN_FCN (icode) (target, op0, op1); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand builtins that take two operands, the first operand being a pointer to + ints and return void. */ + +static rtx +frv_expand_voidbinop_builtin (enum insn_code icode, tree call) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + enum machine_mode mode0 = insn_data[icode].operand[0].mode; + rtx addr; + + if (GET_CODE (op0) != MEM) + { + rtx reg = op0; + + if (! offsettable_address_p (0, mode0, op0)) + { + reg = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, reg, op0)); + } + + op0 = gen_rtx_MEM (SImode, reg); + } + + addr = XEXP (op0, 0); + if (! offsettable_address_p (0, mode0, addr)) + addr = copy_to_mode_reg (Pmode, op0); + + op0 = change_address (op0, V4SImode, addr); + op1 = frv_legitimize_argument (icode, 1, op1); + pat = GEN_FCN (icode) (op0, op1); + if (! pat) + return 0; + + emit_insn (pat); + return 0; +} + +/* Expand builtins that take two long operands and return void. */ + +static rtx +frv_expand_int_void2arg (enum insn_code icode, tree call) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + + op0 = frv_legitimize_argument (icode, 1, op0); + op1 = frv_legitimize_argument (icode, 1, op1); + pat = GEN_FCN (icode) (op0, op1); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return NULL_RTX; +} + +/* Expand prefetch builtins. These take a single address as argument. */ + +static rtx +frv_expand_prefetches (enum insn_code icode, tree call) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + + pat = GEN_FCN (icode) (force_reg (Pmode, op0)); + if (! pat) + return 0; + + emit_insn (pat); + return 0; +} + +/* Expand builtins that take three operands and return void. The first + argument must be a constant that describes a pair or quad accumulators. A + fourth argument is created that is the accumulator guard register that + corresponds to the accumulator. */ + +static rtx +frv_expand_voidtriop_builtin (enum insn_code icode, tree call) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + rtx op2 = frv_read_argument (call, 2); + rtx op3; + + op0 = frv_int_to_acc (icode, 0, op0); + if (! op0) + return NULL_RTX; + + op1 = frv_legitimize_argument (icode, 1, op1); + op2 = frv_legitimize_argument (icode, 2, op2); + op3 = frv_matching_accg_for_acc (op0); + pat = GEN_FCN (icode) (op0, op1, op2, op3); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return NULL_RTX; +} + +/* Expand builtins that perform accumulator-to-accumulator operations. + These builtins take two accumulator numbers as argument and return + void. */ + +static rtx +frv_expand_voidaccop_builtin (enum insn_code icode, tree call) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + rtx op2; + rtx op3; + + op0 = frv_int_to_acc (icode, 0, op0); + if (! op0) + return NULL_RTX; + + op1 = frv_int_to_acc (icode, 1, op1); + if (! op1) + return NULL_RTX; + + op2 = frv_matching_accg_for_acc (op0); + op3 = frv_matching_accg_for_acc (op1); + pat = GEN_FCN (icode) (op0, op1, op2, op3); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return NULL_RTX; +} + +/* Expand a __builtin_read* function. ICODE is the instruction code for the + membar and TARGET_MODE is the mode that the loaded value should have. */ + +static rtx +frv_expand_load_builtin (enum insn_code icode, enum machine_mode target_mode, + tree call, rtx target) +{ + rtx op0 = frv_read_argument (call, 0); + rtx cookie = frv_io_address_cookie (op0); + + if (target == 0 || !REG_P (target)) + target = gen_reg_rtx (target_mode); + op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0); + convert_move (target, op0, 1); + emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_READ))); + cfun->machine->has_membar_p = 1; + return target; +} + +/* Likewise __builtin_write* functions. */ + +static rtx +frv_expand_store_builtin (enum insn_code icode, tree call) +{ + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + rtx cookie = frv_io_address_cookie (op0); + + op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0); + convert_move (op0, force_reg (insn_data[icode].operand[0].mode, op1), 1); + emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_WRITE))); + cfun->machine->has_membar_p = 1; + return NULL_RTX; +} + +/* Expand the MDPACKH builtin. It takes four unsigned short arguments and + each argument forms one word of the two double-word input registers. + CALL is the tree for the call and TARGET, if nonnull, suggests a good place + to put the return value. */ + +static rtx +frv_expand_mdpackh_builtin (tree call, rtx target) +{ + enum insn_code icode = CODE_FOR_mdpackh; + rtx pat, op0, op1; + rtx arg1 = frv_read_argument (call, 0); + rtx arg2 = frv_read_argument (call, 1); + rtx arg3 = frv_read_argument (call, 2); + rtx arg4 = frv_read_argument (call, 3); + + target = frv_legitimize_target (icode, target); + op0 = gen_reg_rtx (DImode); + op1 = gen_reg_rtx (DImode); + + /* The high half of each word is not explicitly initialized, so indicate + that the input operands are not live before this point. */ + emit_clobber (op0); + emit_clobber (op1); + + /* Move each argument into the low half of its associated input word. */ + emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 2), arg1); + emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 6), arg2); + emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 2), arg3); + emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 6), arg4); + + pat = GEN_FCN (icode) (target, op0, op1); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand the MCLRACC builtin. This builtin takes a single accumulator + number as argument. */ + +static rtx +frv_expand_mclracc_builtin (tree call) +{ + enum insn_code icode = CODE_FOR_mclracc; + rtx pat; + rtx op0 = frv_read_argument (call, 0); + + op0 = frv_int_to_acc (icode, 0, op0); + if (! op0) + return NULL_RTX; + + pat = GEN_FCN (icode) (op0); + if (pat) + emit_insn (pat); + + return NULL_RTX; +} + +/* Expand builtins that take no arguments. */ + +static rtx +frv_expand_noargs_builtin (enum insn_code icode) +{ + rtx pat = GEN_FCN (icode) (const0_rtx); + if (pat) + emit_insn (pat); + + return NULL_RTX; +} + +/* Expand MRDACC and MRDACCG. These builtins take a single accumulator + number or accumulator guard number as argument and return an SI integer. */ + +static rtx +frv_expand_mrdacc_builtin (enum insn_code icode, tree call) +{ + rtx pat; + rtx target = gen_reg_rtx (SImode); + rtx op0 = frv_read_argument (call, 0); + + op0 = frv_int_to_acc (icode, 1, op0); + if (! op0) + return NULL_RTX; + + pat = GEN_FCN (icode) (target, op0); + if (! pat) + return NULL_RTX; + + emit_insn (pat); + return target; +} + +/* Expand MWTACC and MWTACCG. These builtins take an accumulator or + accumulator guard as their first argument and an SImode value as their + second. */ + +static rtx +frv_expand_mwtacc_builtin (enum insn_code icode, tree call) +{ + rtx pat; + rtx op0 = frv_read_argument (call, 0); + rtx op1 = frv_read_argument (call, 1); + + op0 = frv_int_to_acc (icode, 0, op0); + if (! op0) + return NULL_RTX; + + op1 = frv_legitimize_argument (icode, 1, op1); + pat = GEN_FCN (icode) (op0, op1); + if (pat) + emit_insn (pat); + + return NULL_RTX; +} + +/* Emit a move from SRC to DEST in SImode chunks. This can be used + to move DImode values into and out of IACC0. */ + +static void +frv_split_iacc_move (rtx dest, rtx src) +{ + enum machine_mode inner; + int i; + + inner = GET_MODE (dest); + for (i = 0; i < GET_MODE_SIZE (inner); i += GET_MODE_SIZE (SImode)) + emit_move_insn (simplify_gen_subreg (SImode, dest, inner, i), + simplify_gen_subreg (SImode, src, inner, i)); +} + +/* Expand builtins. */ + +static rtx +frv_expand_builtin (tree exp, + rtx target, + rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + unsigned fcode = (unsigned)DECL_FUNCTION_CODE (fndecl); + unsigned i; + struct builtin_description *d; + + if (fcode < FRV_BUILTIN_FIRST_NONMEDIA && !TARGET_MEDIA) + { + error ("media functions are not available unless -mmedia is used"); + return NULL_RTX; + } + + switch (fcode) + { + case FRV_BUILTIN_MCOP1: + case FRV_BUILTIN_MCOP2: + case FRV_BUILTIN_MDUNPACKH: + case FRV_BUILTIN_MBTOHE: + if (! TARGET_MEDIA_REV1) + { + error ("this media function is only available on the fr500"); + return NULL_RTX; + } + break; + + case FRV_BUILTIN_MQXMACHS: + case FRV_BUILTIN_MQXMACXHS: + case FRV_BUILTIN_MQMACXHS: + case FRV_BUILTIN_MADDACCS: + case FRV_BUILTIN_MSUBACCS: + case FRV_BUILTIN_MASACCS: + case FRV_BUILTIN_MDADDACCS: + case FRV_BUILTIN_MDSUBACCS: + case FRV_BUILTIN_MDASACCS: + case FRV_BUILTIN_MABSHS: + case FRV_BUILTIN_MDROTLI: + case FRV_BUILTIN_MCPLHI: + case FRV_BUILTIN_MCPLI: + case FRV_BUILTIN_MDCUTSSI: + case FRV_BUILTIN_MQSATHS: + case FRV_BUILTIN_MHSETLOS: + case FRV_BUILTIN_MHSETLOH: + case FRV_BUILTIN_MHSETHIS: + case FRV_BUILTIN_MHSETHIH: + case FRV_BUILTIN_MHDSETS: + case FRV_BUILTIN_MHDSETH: + if (! TARGET_MEDIA_REV2) + { + error ("this media function is only available on the fr400" + " and fr550"); + return NULL_RTX; + } + break; + + case FRV_BUILTIN_SMASS: + case FRV_BUILTIN_SMSSS: + case FRV_BUILTIN_SMU: + case FRV_BUILTIN_ADDSS: + case FRV_BUILTIN_SUBSS: + case FRV_BUILTIN_SLASS: + case FRV_BUILTIN_SCUTSS: + case FRV_BUILTIN_IACCreadll: + case FRV_BUILTIN_IACCreadl: + case FRV_BUILTIN_IACCsetll: + case FRV_BUILTIN_IACCsetl: + if (!TARGET_FR405_BUILTINS) + { + error ("this builtin function is only available" + " on the fr405 and fr450"); + return NULL_RTX; + } + break; + + case FRV_BUILTIN_PREFETCH: + if (!TARGET_FR500_FR550_BUILTINS) + { + error ("this builtin function is only available on the fr500" + " and fr550"); + return NULL_RTX; + } + break; + + case FRV_BUILTIN_MQLCLRHS: + case FRV_BUILTIN_MQLMTHS: + case FRV_BUILTIN_MQSLLHI: + case FRV_BUILTIN_MQSRAHI: + if (!TARGET_MEDIA_FR450) + { + error ("this builtin function is only available on the fr450"); + return NULL_RTX; + } + break; + + default: + break; + } + + /* Expand unique builtins. */ + + switch (fcode) + { + case FRV_BUILTIN_MTRAP: + return frv_expand_noargs_builtin (CODE_FOR_mtrap); + + case FRV_BUILTIN_MCLRACC: + return frv_expand_mclracc_builtin (exp); + + case FRV_BUILTIN_MCLRACCA: + if (TARGET_ACC_8) + return frv_expand_noargs_builtin (CODE_FOR_mclracca8); + else + return frv_expand_noargs_builtin (CODE_FOR_mclracca4); + + case FRV_BUILTIN_MRDACC: + return frv_expand_mrdacc_builtin (CODE_FOR_mrdacc, exp); + + case FRV_BUILTIN_MRDACCG: + return frv_expand_mrdacc_builtin (CODE_FOR_mrdaccg, exp); + + case FRV_BUILTIN_MWTACC: + return frv_expand_mwtacc_builtin (CODE_FOR_mwtacc, exp); + + case FRV_BUILTIN_MWTACCG: + return frv_expand_mwtacc_builtin (CODE_FOR_mwtaccg, exp); + + case FRV_BUILTIN_MDPACKH: + return frv_expand_mdpackh_builtin (exp, target); + + case FRV_BUILTIN_IACCreadll: + { + rtx src = frv_read_iacc_argument (DImode, exp, 0); + if (target == 0 || !REG_P (target)) + target = gen_reg_rtx (DImode); + frv_split_iacc_move (target, src); + return target; + } + + case FRV_BUILTIN_IACCreadl: + return frv_read_iacc_argument (SImode, exp, 0); + + case FRV_BUILTIN_IACCsetll: + { + rtx dest = frv_read_iacc_argument (DImode, exp, 0); + rtx src = frv_read_argument (exp, 1); + frv_split_iacc_move (dest, force_reg (DImode, src)); + return 0; + } + + case FRV_BUILTIN_IACCsetl: + { + rtx dest = frv_read_iacc_argument (SImode, exp, 0); + rtx src = frv_read_argument (exp, 1); + emit_move_insn (dest, force_reg (SImode, src)); + return 0; + } + + default: + break; + } + + /* Expand groups of builtins. */ + + for (i = 0, d = bdesc_set; i < ARRAY_SIZE (bdesc_set); i++, d++) + if (d->code == fcode) + return frv_expand_set_builtin (d->icode, exp, target); + + for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) + if (d->code == fcode) + return frv_expand_unop_builtin (d->icode, exp, target); + + for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++) + if (d->code == fcode) + return frv_expand_binop_builtin (d->icode, exp, target); + + for (i = 0, d = bdesc_cut; i < ARRAY_SIZE (bdesc_cut); i++, d++) + if (d->code == fcode) + return frv_expand_cut_builtin (d->icode, exp, target); + + for (i = 0, d = bdesc_2argimm; i < ARRAY_SIZE (bdesc_2argimm); i++, d++) + if (d->code == fcode) + return frv_expand_binopimm_builtin (d->icode, exp, target); + + for (i = 0, d = bdesc_void2arg; i < ARRAY_SIZE (bdesc_void2arg); i++, d++) + if (d->code == fcode) + return frv_expand_voidbinop_builtin (d->icode, exp); + + for (i = 0, d = bdesc_void3arg; i < ARRAY_SIZE (bdesc_void3arg); i++, d++) + if (d->code == fcode) + return frv_expand_voidtriop_builtin (d->icode, exp); + + for (i = 0, d = bdesc_voidacc; i < ARRAY_SIZE (bdesc_voidacc); i++, d++) + if (d->code == fcode) + return frv_expand_voidaccop_builtin (d->icode, exp); + + for (i = 0, d = bdesc_int_void2arg; + i < ARRAY_SIZE (bdesc_int_void2arg); i++, d++) + if (d->code == fcode) + return frv_expand_int_void2arg (d->icode, exp); + + for (i = 0, d = bdesc_prefetches; + i < ARRAY_SIZE (bdesc_prefetches); i++, d++) + if (d->code == fcode) + return frv_expand_prefetches (d->icode, exp); + + for (i = 0, d = bdesc_loads; i < ARRAY_SIZE (bdesc_loads); i++, d++) + if (d->code == fcode) + return frv_expand_load_builtin (d->icode, TYPE_MODE (TREE_TYPE (exp)), + exp, target); + + for (i = 0, d = bdesc_stores; i < ARRAY_SIZE (bdesc_stores); i++, d++) + if (d->code == fcode) + return frv_expand_store_builtin (d->icode, exp); + + return 0; +} + +static bool +frv_in_small_data_p (const_tree decl) +{ + HOST_WIDE_INT size; + const_tree section_name; + + /* Don't apply the -G flag to internal compiler structures. We + should leave such structures in the main data section, partly + for efficiency and partly because the size of some of them + (such as C++ typeinfos) is not known until later. */ + if (TREE_CODE (decl) != VAR_DECL || DECL_ARTIFICIAL (decl)) + return false; + + /* If we already know which section the decl should be in, see if + it's a small data section. */ + section_name = DECL_SECTION_NAME (decl); + if (section_name) + { + gcc_assert (TREE_CODE (section_name) == STRING_CST); + if (frv_string_begins_with (section_name, ".sdata")) + return true; + if (frv_string_begins_with (section_name, ".sbss")) + return true; + return false; + } + + size = int_size_in_bytes (TREE_TYPE (decl)); + if (size > 0 && size <= g_switch_value) + return true; + + return false; +} + +static bool +frv_rtx_costs (rtx x, + int code ATTRIBUTE_UNUSED, + int outer_code ATTRIBUTE_UNUSED, + int *total, + bool speed ATTRIBUTE_UNUSED) +{ + if (outer_code == MEM) + { + /* Don't differentiate between memory addresses. All the ones + we accept have equal cost. */ + *total = COSTS_N_INSNS (0); + return true; + } + + switch (code) + { + case CONST_INT: + /* Make 12-bit integers really cheap. */ + if (IN_RANGE (INTVAL (x), -2048, 2047)) + { + *total = 0; + return true; + } + /* Fall through. */ + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + case CONST_DOUBLE: + *total = COSTS_N_INSNS (2); + return true; + + case PLUS: + case MINUS: + case AND: + case IOR: + case XOR: + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + case NOT: + case NEG: + case COMPARE: + if (GET_MODE (x) == SImode) + *total = COSTS_N_INSNS (1); + else if (GET_MODE (x) == DImode) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (3); + return true; + + case MULT: + if (GET_MODE (x) == SImode) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (6); /* guess */ + return true; + + case DIV: + case UDIV: + case MOD: + case UMOD: + *total = COSTS_N_INSNS (18); + return true; + + case MEM: + *total = COSTS_N_INSNS (3); + return true; + + default: + return false; + } +} + +static void +frv_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED) +{ + switch_to_section (ctors_section); + assemble_align (POINTER_SIZE); + if (TARGET_FDPIC) + { + int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1); + + gcc_assert (ok); + return; + } + assemble_integer_with_op ("\t.picptr\t", symbol); +} + +static void +frv_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) +{ + switch_to_section (dtors_section); + assemble_align (POINTER_SIZE); + if (TARGET_FDPIC) + { + int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1); + + gcc_assert (ok); + return; + } + assemble_integer_with_op ("\t.picptr\t", symbol); +} + +/* Worker function for TARGET_STRUCT_VALUE_RTX. */ + +static rtx +frv_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED, + int incoming ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (Pmode, FRV_STRUCT_VALUE_REGNUM); +} + +#define TLS_BIAS (2048 - 16) + +/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL. + We need to emit DTP-relative relocations. */ + +static void +frv_output_dwarf_dtprel (FILE *file, int size, rtx x) +{ + gcc_assert (size == 4); + fputs ("\t.picptr\ttlsmoff(", file); + /* We want the unbiased TLS offset, so add the bias to the + expression, such that the implicit biasing cancels out. */ + output_addr_const (file, plus_constant (x, TLS_BIAS)); + fputs (")", file); +} + +#include "gt-frv.h" diff --git a/gcc/config/frv/frv.h b/gcc/config/frv/frv.h new file mode 100644 index 000000000..299a85676 --- /dev/null +++ b/gcc/config/frv/frv.h @@ -0,0 +1,2188 @@ +/* Target macros for the FRV port of GCC. + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, + 2010, 2011 + Free Software Foundation, Inc. + Contributed by Red Hat Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +#ifndef __FRV_H__ +#define __FRV_H__ + +/* Frv general purpose macros. */ +/* Align an address. */ +#define ADDR_ALIGN(addr,align) (((addr) + (align) - 1) & ~((align) - 1)) + +/* Driver configuration. */ + +/* -fpic and -fPIC used to imply the -mlibrary-pic multilib, but with + FDPIC which multilib to use depends on whether FDPIC is in use or + not. The trick we use is to introduce -multilib-library-pic as a + pseudo-flag that selects the library-pic multilib, and map fpic + and fPIC to it only if fdpic is not selected. Also, if fdpic is + selected and no PIC/PIE options are present, we imply -fPIE. + Otherwise, if -fpic or -fPIC are enabled and we're optimizing for + speed, or if we have -On with n>=3, enable inlining of PLTs. As + for -mgprel-ro, we want to enable it by default, but not for -fpic or + -fpie. */ + +#define DRIVER_SELF_SPECS SUBTARGET_DRIVER_SELF_SPECS \ +"%{mno-pack:\ + %{!mhard-float:-msoft-float}\ + %{!mmedia:-mno-media}}\ + %{!mfdpic:%{fpic|fPIC: -multilib-library-pic}}\ + %{mfdpic:%{!fpic:%{!fpie:%{!fPIC:%{!fPIE:\ + %{!fno-pic:%{!fno-pie:%{!fno-PIC:%{!fno-PIE:-fPIE}}}}}}}} \ + %{!mno-inline-plt:%{O*:%{!O0:%{!Os:%{fpic|fPIC:-minline-plt} \ + %{!fpic:%{!fPIC:%{!O:%{!O1:%{!O2:-minline-plt}}}}}}}}} \ + %{!mno-gprel-ro:%{!fpic:%{!fpie:-mgprel-ro}}}} \ +" +#ifndef SUBTARGET_DRIVER_SELF_SPECS +# define SUBTARGET_DRIVER_SELF_SPECS +#endif + +#undef ASM_SPEC +#define ASM_SPEC "\ +%{G*} \ +%{mtomcat-stats} \ +%{!mno-eflags: \ + %{mcpu=*} \ + %{mgpr-*} %{mfpr-*} \ + %{msoft-float} %{mhard-float} \ + %{mdword} %{mno-dword} \ + %{mdouble} %{mno-double} \ + %{mmedia} %{mno-media} \ + %{mmuladd} %{mno-muladd} \ + %{mpack} %{mno-pack} \ + %{mno-fdpic:-mnopic} %{mfdpic} \ + %{fpic|fpie: -mpic} %{fPIC|fPIE: -mPIC} %{mlibrary-pic}}" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crt0%O%s frvbegin%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "frvend%O%s" + + +#define MASK_DEFAULT_FRV \ + (MASK_MEDIA \ + | MASK_DOUBLE \ + | MASK_MULADD \ + | MASK_DWORD \ + | MASK_PACK) + +#define MASK_DEFAULT_FR500 \ + (MASK_MEDIA | MASK_DWORD | MASK_PACK) + +#define MASK_DEFAULT_FR550 \ + (MASK_MEDIA | MASK_DWORD | MASK_PACK) + +#define MASK_DEFAULT_FR450 \ + (MASK_GPR_32 \ + | MASK_FPR_32 \ + | MASK_MEDIA \ + | MASK_SOFT_FLOAT \ + | MASK_DWORD \ + | MASK_PACK) + +#define MASK_DEFAULT_FR400 \ + (MASK_GPR_32 \ + | MASK_FPR_32 \ + | MASK_MEDIA \ + | MASK_ACC_4 \ + | MASK_SOFT_FLOAT \ + | MASK_DWORD \ + | MASK_PACK) + +#define MASK_DEFAULT_SIMPLE \ + (MASK_GPR_32 | MASK_SOFT_FLOAT) + +/* A C string constant that tells the GCC driver program options to pass to + `cc1'. It can also specify how to translate options you give to GCC into + options for GCC to pass to the `cc1'. + + Do not define this macro if it does not need to do anything. */ +/* For ABI compliance, we need to put bss data into the normal data section. */ +#define CC1_SPEC "%{G*}" + +#undef LINK_SPEC +#define LINK_SPEC "\ +%{h*} %{v:-V} \ +%{mfdpic:-melf32frvfd -z text} \ +%{static:-dn -Bstatic} \ +%{shared:-Bdynamic} \ +%{symbolic:-Bsymbolic} \ +%{G*}" + +#undef LIB_SPEC +#define LIB_SPEC "--start-group -lc -lsim --end-group" + +#ifndef CPU_TYPE +#define CPU_TYPE FRV_CPU_FR500 +#endif + +/* Run-time target specifications */ + +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + int issue_rate; \ + \ + builtin_define ("__frv__"); \ + builtin_assert ("machine=frv"); \ + \ + issue_rate = frv_issue_rate (); \ + if (issue_rate > 1) \ + builtin_define_with_int_value ("__FRV_VLIW__", issue_rate); \ + builtin_define_with_int_value ("__FRV_GPR__", NUM_GPRS); \ + builtin_define_with_int_value ("__FRV_FPR__", NUM_FPRS); \ + builtin_define_with_int_value ("__FRV_ACC__", NUM_ACCS); \ + \ + switch (frv_cpu_type) \ + { \ + case FRV_CPU_GENERIC: \ + builtin_define ("__CPU_GENERIC__"); \ + break; \ + case FRV_CPU_FR550: \ + builtin_define ("__CPU_FR550__"); \ + break; \ + case FRV_CPU_FR500: \ + case FRV_CPU_TOMCAT: \ + builtin_define ("__CPU_FR500__"); \ + break; \ + case FRV_CPU_FR450: \ + builtin_define ("__CPU_FR450__"); \ + break; \ + case FRV_CPU_FR405: \ + builtin_define ("__CPU_FR405__"); \ + break; \ + case FRV_CPU_FR400: \ + builtin_define ("__CPU_FR400__"); \ + break; \ + case FRV_CPU_FR300: \ + case FRV_CPU_SIMPLE: \ + builtin_define ("__CPU_FR300__"); \ + break; \ + } \ + \ + if (TARGET_HARD_FLOAT) \ + builtin_define ("__FRV_HARD_FLOAT__"); \ + if (TARGET_DWORD) \ + builtin_define ("__FRV_DWORD__"); \ + if (TARGET_FDPIC) \ + builtin_define ("__FRV_FDPIC__"); \ + if (flag_leading_underscore > 0) \ + builtin_define ("__FRV_UNDERSCORE__"); \ + } \ + while (0) + + +#define TARGET_HAS_FPRS (TARGET_HARD_FLOAT || TARGET_MEDIA) + +#define NUM_GPRS (TARGET_GPR_32? 32 : 64) +#define NUM_FPRS (!TARGET_HAS_FPRS? 0 : TARGET_FPR_32? 32 : 64) +#define NUM_ACCS (!TARGET_MEDIA? 0 : TARGET_ACC_4? 4 : 8) + +/* X is a valid accumulator number if (X & ACC_MASK) == X. */ +#define ACC_MASK \ + (!TARGET_MEDIA ? 0 \ + : TARGET_ACC_4 ? 3 \ + : frv_cpu_type == FRV_CPU_FR450 ? 11 \ + : 7) + +/* Macros to identify the blend of media instructions available. Revision 1 + is the one found on the FR500. Revision 2 includes the changes made for + the FR400. + + Treat the generic processor as a revision 1 machine for now, for + compatibility with earlier releases. */ + +#define TARGET_MEDIA_REV1 \ + (TARGET_MEDIA \ + && (frv_cpu_type == FRV_CPU_GENERIC \ + || frv_cpu_type == FRV_CPU_FR500)) + +#define TARGET_MEDIA_REV2 \ + (TARGET_MEDIA \ + && (frv_cpu_type == FRV_CPU_FR400 \ + || frv_cpu_type == FRV_CPU_FR405 \ + || frv_cpu_type == FRV_CPU_FR450 \ + || frv_cpu_type == FRV_CPU_FR550)) + +#define TARGET_MEDIA_FR450 \ + (frv_cpu_type == FRV_CPU_FR450) + +#define TARGET_FR500_FR550_BUILTINS \ + (frv_cpu_type == FRV_CPU_FR500 \ + || frv_cpu_type == FRV_CPU_FR550) + +#define TARGET_FR405_BUILTINS \ + (frv_cpu_type == FRV_CPU_FR405 \ + || frv_cpu_type == FRV_CPU_FR450) + +#ifndef HAVE_AS_TLS +#define HAVE_AS_TLS 0 +#endif + +/* This macro is a C statement to print on `stderr' a string describing the + particular machine description choice. Every machine description should + define `TARGET_VERSION'. For example: + + #ifdef MOTOROLA + #define TARGET_VERSION \ + fprintf (stderr, " (68k, Motorola syntax)"); + #else + #define TARGET_VERSION \ + fprintf (stderr, " (68k, MIT syntax)"); + #endif */ +#define TARGET_VERSION fprintf (stderr, _(" (frv)")) + +#define LABEL_ALIGN_AFTER_BARRIER(LABEL) (TARGET_ALIGN_LABELS ? 3 : 0) + +/* Small Data Area Support. */ +/* Maximum size of variables that go in .sdata/.sbss. + The -msdata=foo switch also controls how small variables are handled. */ +#ifndef SDATA_DEFAULT_SIZE +#define SDATA_DEFAULT_SIZE 8 +#endif + + +/* Storage Layout */ + +/* Define this macro to have the value 1 if the most significant bit in a byte + has the lowest number; otherwise define it to have the value zero. This + means that bit-field instructions count from the most significant bit. If + the machine has no bit-field instructions, then this must still be defined, + but it doesn't matter which value it is defined to. This macro need not be + a constant. + + This macro does not affect the way structure fields are packed into bytes or + words; that is controlled by `BYTES_BIG_ENDIAN'. */ +#define BITS_BIG_ENDIAN 1 + +/* Define this macro to have the value 1 if the most significant byte in a word + has the lowest number. This macro need not be a constant. */ +#define BYTES_BIG_ENDIAN 1 + +/* Define this macro to have the value 1 if, in a multiword object, the most + significant word has the lowest number. This applies to both memory + locations and registers; GCC fundamentally assumes that the order of + words in memory is the same as the order in registers. This macro need not + be a constant. */ +#define WORDS_BIG_ENDIAN 1 + +/* Number of storage units in a word; normally 4. */ +#define UNITS_PER_WORD 4 + +/* A macro to update MODE and UNSIGNEDP when an object whose type is TYPE and + which has the specified mode and signedness is to be stored in a register. + This macro is only called when TYPE is a scalar type. + + On most RISC machines, which only have operations that operate on a full + register, define this macro to set M to `word_mode' if M is an integer mode + narrower than `BITS_PER_WORD'. In most cases, only integer modes should be + widened because wider-precision floating-point operations are usually more + expensive than their narrower counterparts. + + For most machines, the macro definition does not change UNSIGNEDP. However, + some machines, have instructions that preferentially handle either signed or + unsigned quantities of certain modes. For example, on the DEC Alpha, 32-bit + loads from memory and 32-bit add instructions sign-extend the result to 64 + bits. On such machines, set UNSIGNEDP according to which kind of extension + is more efficient. + + Do not define this macro if it would never modify MODE. */ +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + do \ + { \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < 4) \ + (MODE) = SImode; \ + } \ + while (0) + +/* Normal alignment required for function parameters on the stack, in bits. + All stack parameters receive at least this much alignment regardless of data + type. On most machines, this is the same as the size of an integer. */ +#define PARM_BOUNDARY 32 + +/* Define this macro if you wish to preserve a certain alignment for the stack + pointer. The definition is a C expression for the desired alignment + (measured in bits). + + If `PUSH_ROUNDING' is not defined, the stack will always be aligned to the + specified boundary. If `PUSH_ROUNDING' is defined and specifies a less + strict alignment than `STACK_BOUNDARY', the stack may be momentarily + unaligned while pushing arguments. */ +#define STACK_BOUNDARY 64 + +/* Alignment required for a function entry point, in bits. */ +#define FUNCTION_BOUNDARY 128 + +/* Biggest alignment that any data type can require on this machine, + in bits. */ +#define BIGGEST_ALIGNMENT 64 + +/* @@@ A hack, needed because libobjc wants to use ADJUST_FIELD_ALIGN for + some reason. */ +#ifdef IN_TARGET_LIBS +#define BIGGEST_FIELD_ALIGNMENT 64 +#else +/* An expression for the alignment of a structure field FIELD if the + alignment computed in the usual way is COMPUTED. GCC uses this + value instead of the value in `BIGGEST_ALIGNMENT' or + `BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */ +#define ADJUST_FIELD_ALIGN(FIELD, COMPUTED) \ + frv_adjust_field_align (FIELD, COMPUTED) +#endif + +/* If defined, a C expression to compute the alignment for a static variable. + TYPE is the data type, and ALIGN is the alignment that the object + would ordinarily have. The value of this macro is used instead of that + alignment to align the object. + + If this macro is not defined, then ALIGN is used. + + One use of this macro is to increase alignment of medium-size data to make + it all fit in fewer cache lines. Another is to cause character arrays to be + word-aligned so that `strcpy' calls that copy constants to character arrays + can be done inline. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + (TREE_CODE (TYPE) == ARRAY_TYPE \ + && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \ + && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) + +/* If defined, a C expression to compute the alignment given to a constant that + is being placed in memory. CONSTANT is the constant and ALIGN is the + alignment that the object would ordinarily have. The value of this macro is + used instead of that alignment to align the object. + + If this macro is not defined, then ALIGN is used. + + The typical use of this macro is to increase alignment for string constants + to be word aligned so that `strcpy' calls that copy constants can be done + inline. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + (TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) + +/* Define this macro to be the value 1 if instructions will fail to work if + given data not on the nominal alignment. If instructions will merely go + slower in that case, define this macro as 0. */ +#define STRICT_ALIGNMENT 1 + +#define PCC_BITFIELD_TYPE_MATTERS 1 + + +/* Layout of Source Language Data Types. */ + +#define CHAR_TYPE_SIZE 8 +#define SHORT_TYPE_SIZE 16 +#define INT_TYPE_SIZE 32 +#define LONG_TYPE_SIZE 32 +#define LONG_LONG_TYPE_SIZE 64 +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 +#define LONG_DOUBLE_TYPE_SIZE 64 + +/* An expression whose value is 1 or 0, according to whether the type `char' + should be signed or unsigned by default. The user can always override this + default with the options `-fsigned-char' and `-funsigned-char'. */ +#define DEFAULT_SIGNED_CHAR 1 + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + + +/* General purpose registers. */ +#define GPR_FIRST 0 /* First gpr */ +#define GPR_LAST (GPR_FIRST + 63) /* Last gpr */ +#define GPR_R0 GPR_FIRST /* R0, constant 0 */ +#define GPR_FP (GPR_FIRST + 2) /* Frame pointer */ +#define GPR_SP (GPR_FIRST + 1) /* Stack pointer */ + /* small data register */ +#define SDA_BASE_REG ((unsigned)(TARGET_FDPIC ? -1 : flag_pic ? PIC_REGNO : (GPR_FIRST + 16))) +#define PIC_REGNO (GPR_FIRST + (TARGET_FDPIC?15:17)) /* PIC register. */ +#define FDPIC_FPTR_REGNO (GPR_FIRST + 14) /* uClinux PIC function pointer register. */ +#define FDPIC_REGNO (GPR_FIRST + 15) /* uClinux PIC register. */ + +#define HARD_REGNO_RENAME_OK(from,to) (TARGET_FDPIC ? ((to) != FDPIC_REG) : 1) + +#define OUR_FDPIC_REG get_hard_reg_initial_val (SImode, FDPIC_REGNO) + +#define FPR_FIRST 64 /* First FP reg */ +#define FPR_LAST 127 /* Last FP reg */ + +#define GPR_TEMP_NUM frv_condexec_temps /* # gprs to reserve for temps */ + +/* We reserve the last CR and CCR in each category to be used as a reload + register to reload the CR/CCR registers. This is a kludge. */ +#define CC_FIRST 128 /* First ICC/FCC reg */ +#define CC_LAST 135 /* Last ICC/FCC reg */ +#define ICC_FIRST (CC_FIRST + 4) /* First ICC reg */ +#define ICC_LAST (CC_FIRST + 7) /* Last ICC reg */ +#define ICC_TEMP (CC_FIRST + 7) /* Temporary ICC reg */ +#define FCC_FIRST (CC_FIRST) /* First FCC reg */ +#define FCC_LAST (CC_FIRST + 3) /* Last FCC reg */ + +/* Amount to shift a value to locate a ICC or FCC register in the CCR + register and shift it to the bottom 4 bits. */ +#define CC_SHIFT_RIGHT(REGNO) (((REGNO) - CC_FIRST) << 2) + +/* Mask to isolate a single ICC/FCC value. */ +#define CC_MASK 0xf + +/* Masks to isolate the various bits in an ICC field. */ +#define ICC_MASK_N 0x8 /* negative */ +#define ICC_MASK_Z 0x4 /* zero */ +#define ICC_MASK_V 0x2 /* overflow */ +#define ICC_MASK_C 0x1 /* carry */ + +/* Mask to isolate the N/Z flags in an ICC. */ +#define ICC_MASK_NZ (ICC_MASK_N | ICC_MASK_Z) + +/* Mask to isolate the Z/C flags in an ICC. */ +#define ICC_MASK_ZC (ICC_MASK_Z | ICC_MASK_C) + +/* Masks to isolate the various bits in a FCC field. */ +#define FCC_MASK_E 0x8 /* equal */ +#define FCC_MASK_L 0x4 /* less than */ +#define FCC_MASK_G 0x2 /* greater than */ +#define FCC_MASK_U 0x1 /* unordered */ + +/* For CCR registers, the machine wants CR4..CR7 to be used for integer + code and CR0..CR3 to be used for floating point. */ +#define CR_FIRST 136 /* First CCR */ +#define CR_LAST 143 /* Last CCR */ +#define CR_NUM (CR_LAST-CR_FIRST+1) /* # of CCRs (8) */ +#define ICR_FIRST (CR_FIRST + 4) /* First integer CCR */ +#define ICR_LAST (CR_FIRST + 7) /* Last integer CCR */ +#define ICR_TEMP ICR_LAST /* Temp integer CCR */ +#define FCR_FIRST (CR_FIRST + 0) /* First float CCR */ +#define FCR_LAST (CR_FIRST + 3) /* Last float CCR */ + +/* Amount to shift a value to locate a CR register in the CCCR special purpose + register and shift it to the bottom 2 bits. */ +#define CR_SHIFT_RIGHT(REGNO) (((REGNO) - CR_FIRST) << 1) + +/* Mask to isolate a single CR value. */ +#define CR_MASK 0x3 + +#define ACC_FIRST 144 /* First acc register */ +#define ACC_LAST 155 /* Last acc register */ + +#define ACCG_FIRST 156 /* First accg register */ +#define ACCG_LAST 167 /* Last accg register */ + +#define AP_FIRST 168 /* fake argument pointer */ + +#define SPR_FIRST 169 +#define SPR_LAST 172 +#define LR_REGNO (SPR_FIRST) +#define LCR_REGNO (SPR_FIRST + 1) +#define IACC_FIRST (SPR_FIRST + 2) +#define IACC_LAST (SPR_FIRST + 3) + +#define GPR_P(R) IN_RANGE (R, GPR_FIRST, GPR_LAST) +#define GPR_OR_AP_P(R) (GPR_P (R) || (R) == ARG_POINTER_REGNUM) +#define FPR_P(R) IN_RANGE (R, FPR_FIRST, FPR_LAST) +#define CC_P(R) IN_RANGE (R, CC_FIRST, CC_LAST) +#define ICC_P(R) IN_RANGE (R, ICC_FIRST, ICC_LAST) +#define FCC_P(R) IN_RANGE (R, FCC_FIRST, FCC_LAST) +#define CR_P(R) IN_RANGE (R, CR_FIRST, CR_LAST) +#define ICR_P(R) IN_RANGE (R, ICR_FIRST, ICR_LAST) +#define FCR_P(R) IN_RANGE (R, FCR_FIRST, FCR_LAST) +#define ACC_P(R) IN_RANGE (R, ACC_FIRST, ACC_LAST) +#define ACCG_P(R) IN_RANGE (R, ACCG_FIRST, ACCG_LAST) +#define SPR_P(R) IN_RANGE (R, SPR_FIRST, SPR_LAST) + +#define GPR_OR_PSEUDO_P(R) (GPR_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define FPR_OR_PSEUDO_P(R) (FPR_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define GPR_AP_OR_PSEUDO_P(R) (GPR_OR_AP_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define CC_OR_PSEUDO_P(R) (CC_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define ICC_OR_PSEUDO_P(R) (ICC_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define FCC_OR_PSEUDO_P(R) (FCC_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define CR_OR_PSEUDO_P(R) (CR_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define ICR_OR_PSEUDO_P(R) (ICR_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define FCR_OR_PSEUDO_P(R) (FCR_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define ACC_OR_PSEUDO_P(R) (ACC_P (R) || (R) >= FIRST_PSEUDO_REGISTER) +#define ACCG_OR_PSEUDO_P(R) (ACCG_P (R) || (R) >= FIRST_PSEUDO_REGISTER) + +#define MAX_STACK_IMMEDIATE_OFFSET 2047 + + +/* Register Basics. */ + +/* Number of hardware registers known to the compiler. They receive numbers 0 + through `FIRST_PSEUDO_REGISTER-1'; thus, the first pseudo register's number + really is assigned the number `FIRST_PSEUDO_REGISTER'. */ +#define FIRST_PSEUDO_REGISTER (SPR_LAST + 1) + +/* The first/last register that can contain the arguments to a function. */ +#define FIRST_ARG_REGNUM (GPR_FIRST + 8) +#define LAST_ARG_REGNUM (FIRST_ARG_REGNUM + FRV_NUM_ARG_REGS - 1) + +/* Registers used by the exception handling functions. These should be + registers that are not otherwise used by the calling sequence. */ +#define FIRST_EH_REGNUM 14 +#define LAST_EH_REGNUM 15 + +/* Scratch registers used in the prologue, epilogue and thunks. + OFFSET_REGNO is for loading constant addends that are too big for a + single instruction. TEMP_REGNO is used for transferring SPRs to and from + the stack, and various other activities. */ +#define OFFSET_REGNO 4 +#define TEMP_REGNO 5 + +/* Registers used in the prologue. OLD_SP_REGNO is the old stack pointer, + which is sometimes used to set up the frame pointer. */ +#define OLD_SP_REGNO 6 + +/* Registers used in the epilogue. STACKADJ_REGNO stores the exception + handler's stack adjustment. */ +#define STACKADJ_REGNO 6 + +/* Registers used in thunks. JMP_REGNO is used for loading the target + address. */ +#define JUMP_REGNO 6 + +#define EH_RETURN_DATA_REGNO(N) ((N) <= (LAST_EH_REGNUM - FIRST_EH_REGNUM)? \ + (N) + FIRST_EH_REGNUM : INVALID_REGNUM) +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (SImode, STACKADJ_REGNO) +#define EH_RETURN_HANDLER_RTX RETURN_ADDR_RTX (0, frame_pointer_rtx) + +#define EPILOGUE_USES(REGNO) ((REGNO) == LR_REGNO) + +/* An initializer that says which registers are used for fixed purposes all + throughout the compiled code and are therefore not available for general + allocation. These would include the stack pointer, the frame pointer + (except on machines where that can be used as a general register when no + frame pointer is needed), the program counter on machines where that is + considered one of the addressable registers, and any other numbered register + with a standard use. + + This information is expressed as a sequence of numbers, separated by commas + and surrounded by braces. The Nth number is 1 if register N is fixed, 0 + otherwise. + + The table initialized from this macro, and the table initialized by the + following one, may be overridden at run time either automatically, by the + actions of the macro `CONDITIONAL_REGISTER_USAGE', or by the user with the + command options `-ffixed-REG', `-fcall-used-REG' and `-fcall-saved-REG'. */ + +/* gr0 -- Hard Zero + gr1 -- Stack Pointer + gr2 -- Frame Pointer + gr3 -- Hidden Parameter + gr16 -- Small Data reserved + gr17 -- Pic reserved + gr28 -- OS reserved + gr29 -- OS reserved + gr30 -- OS reserved + gr31 -- OS reserved + cr3 -- reserved to reload FCC registers. + cr7 -- reserved to reload ICC registers. */ +#define FIXED_REGISTERS \ +{ /* Integer Registers */ \ + 1, 1, 1, 1, 0, 0, 0, 0, /* 000-007, gr0 - gr7 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 008-015, gr8 - gr15 */ \ + 1, 1, 0, 0, 0, 0, 0, 0, /* 016-023, gr16 - gr23 */ \ + 0, 0, 0, 0, 1, 1, 1, 1, /* 024-031, gr24 - gr31 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 032-039, gr32 - gr39 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 040-040, gr48 - gr47 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 048-055, gr48 - gr55 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 056-063, gr56 - gr63 */ \ + /* Float Registers */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 064-071, fr0 - fr7 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 072-079, fr8 - fr15 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 080-087, fr16 - fr23 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 088-095, fr24 - fr31 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 096-103, fr32 - fr39 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 104-111, fr48 - fr47 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 112-119, fr48 - fr55 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 120-127, fr56 - fr63 */ \ + /* Condition Code Registers */ \ + 0, 0, 0, 0, /* 128-131, fcc0 - fcc3 */ \ + 0, 0, 0, 1, /* 132-135, icc0 - icc3 */ \ + /* Conditional execution Registers (CCR) */ \ + 0, 0, 0, 0, 0, 0, 0, 1, /* 136-143, cr0 - cr7 */ \ + /* Accumulators */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 144-151, acc0 - acc7 */ \ + 1, 1, 1, 1, /* 152-155, acc8 - acc11 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 156-163, accg0 - accg7 */ \ + 1, 1, 1, 1, /* 164-167, accg8 - accg11 */ \ + /* Other registers */ \ + 1, /* 168, AP - fake arg ptr */ \ + 1, /* 169, LR - Link register*/ \ + 0, /* 170, LCR - Loop count reg*/ \ + 1, 1 /* 171-172, iacc0 */ \ +} + +/* Like `FIXED_REGISTERS' but has 1 for each register that is clobbered (in + general) by function calls as well as for fixed registers. This macro + therefore identifies the registers that are not available for general + allocation of values that must live across function calls. + + If a register has 0 in `CALL_USED_REGISTERS', the compiler automatically + saves it on function entry and restores it on function exit, if the register + is used within the function. */ +#define CALL_USED_REGISTERS \ +{ /* Integer Registers */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 000-007, gr0 - gr7 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 008-015, gr8 - gr15 */ \ + 1, 1, 0, 0, 0, 0, 0, 0, /* 016-023, gr16 - gr23 */ \ + 0, 0, 0, 0, 1, 1, 1, 1, /* 024-031, gr24 - gr31 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 032-039, gr32 - gr39 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 040-040, gr48 - gr47 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 048-055, gr48 - gr55 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 056-063, gr56 - gr63 */ \ + /* Float Registers */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 064-071, fr0 - fr7 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 072-079, fr8 - fr15 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 080-087, fr16 - fr23 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 088-095, fr24 - fr31 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 096-103, fr32 - fr39 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 104-111, fr48 - fr47 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 112-119, fr48 - fr55 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* 120-127, fr56 - fr63 */ \ + /* Condition Code Registers */ \ + 1, 1, 1, 1, /* 128-131, fcc0 - fcc3 */ \ + 1, 1, 1, 1, /* 132-135, icc0 - icc3 */ \ + /* Conditional execution Registers (CCR) */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 136-143, cr0 - cr7 */ \ + /* Accumulators */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 144-151, acc0 - acc7 */ \ + 1, 1, 1, 1, /* 152-155, acc8 - acc11 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, /* 156-163, accg0 - accg7 */ \ + 1, 1, 1, 1, /* 164-167, accg8 - accg11 */ \ + /* Other registers */ \ + 1, /* 168, AP - fake arg ptr */ \ + 1, /* 169, LR - Link register*/ \ + 1, /* 170, LCR - Loop count reg */ \ + 1, 1 /* 171-172, iacc0 */ \ +} + + +/* Order of allocation of registers. */ + +/* If defined, an initializer for a vector of integers, containing the numbers + of hard registers in the order in which GCC should prefer to use them + (from most preferred to least). + + If this macro is not defined, registers are used lowest numbered first (all + else being equal). + + One use of this macro is on machines where the highest numbered registers + must always be saved and the save-multiple-registers instruction supports + only sequences of consecutive registers. On such machines, define + `REG_ALLOC_ORDER' to be an initializer that lists the highest numbered + allocatable register first. */ + +/* On the FRV, allocate GR16 and GR17 after other saved registers so that we + have a better chance of allocating 2 registers at a time and can use the + double word load/store instructions in the prologue. */ +#define REG_ALLOC_ORDER \ +{ \ + /* volatile registers */ \ + GPR_FIRST + 4, GPR_FIRST + 5, GPR_FIRST + 6, GPR_FIRST + 7, \ + GPR_FIRST + 8, GPR_FIRST + 9, GPR_FIRST + 10, GPR_FIRST + 11, \ + GPR_FIRST + 12, GPR_FIRST + 13, GPR_FIRST + 14, GPR_FIRST + 15, \ + GPR_FIRST + 32, GPR_FIRST + 33, GPR_FIRST + 34, GPR_FIRST + 35, \ + GPR_FIRST + 36, GPR_FIRST + 37, GPR_FIRST + 38, GPR_FIRST + 39, \ + GPR_FIRST + 40, GPR_FIRST + 41, GPR_FIRST + 42, GPR_FIRST + 43, \ + GPR_FIRST + 44, GPR_FIRST + 45, GPR_FIRST + 46, GPR_FIRST + 47, \ + \ + FPR_FIRST + 0, FPR_FIRST + 1, FPR_FIRST + 2, FPR_FIRST + 3, \ + FPR_FIRST + 4, FPR_FIRST + 5, FPR_FIRST + 6, FPR_FIRST + 7, \ + FPR_FIRST + 8, FPR_FIRST + 9, FPR_FIRST + 10, FPR_FIRST + 11, \ + FPR_FIRST + 12, FPR_FIRST + 13, FPR_FIRST + 14, FPR_FIRST + 15, \ + FPR_FIRST + 32, FPR_FIRST + 33, FPR_FIRST + 34, FPR_FIRST + 35, \ + FPR_FIRST + 36, FPR_FIRST + 37, FPR_FIRST + 38, FPR_FIRST + 39, \ + FPR_FIRST + 40, FPR_FIRST + 41, FPR_FIRST + 42, FPR_FIRST + 43, \ + FPR_FIRST + 44, FPR_FIRST + 45, FPR_FIRST + 46, FPR_FIRST + 47, \ + \ + ICC_FIRST + 0, ICC_FIRST + 1, ICC_FIRST + 2, ICC_FIRST + 3, \ + FCC_FIRST + 0, FCC_FIRST + 1, FCC_FIRST + 2, FCC_FIRST + 3, \ + CR_FIRST + 0, CR_FIRST + 1, CR_FIRST + 2, CR_FIRST + 3, \ + CR_FIRST + 4, CR_FIRST + 5, CR_FIRST + 6, CR_FIRST + 7, \ + \ + /* saved registers */ \ + GPR_FIRST + 18, GPR_FIRST + 19, \ + GPR_FIRST + 20, GPR_FIRST + 21, GPR_FIRST + 22, GPR_FIRST + 23, \ + GPR_FIRST + 24, GPR_FIRST + 25, GPR_FIRST + 26, GPR_FIRST + 27, \ + GPR_FIRST + 48, GPR_FIRST + 49, GPR_FIRST + 50, GPR_FIRST + 51, \ + GPR_FIRST + 52, GPR_FIRST + 53, GPR_FIRST + 54, GPR_FIRST + 55, \ + GPR_FIRST + 56, GPR_FIRST + 57, GPR_FIRST + 58, GPR_FIRST + 59, \ + GPR_FIRST + 60, GPR_FIRST + 61, GPR_FIRST + 62, GPR_FIRST + 63, \ + GPR_FIRST + 16, GPR_FIRST + 17, \ + \ + FPR_FIRST + 16, FPR_FIRST + 17, FPR_FIRST + 18, FPR_FIRST + 19, \ + FPR_FIRST + 20, FPR_FIRST + 21, FPR_FIRST + 22, FPR_FIRST + 23, \ + FPR_FIRST + 24, FPR_FIRST + 25, FPR_FIRST + 26, FPR_FIRST + 27, \ + FPR_FIRST + 28, FPR_FIRST + 29, FPR_FIRST + 30, FPR_FIRST + 31, \ + FPR_FIRST + 48, FPR_FIRST + 49, FPR_FIRST + 50, FPR_FIRST + 51, \ + FPR_FIRST + 52, FPR_FIRST + 53, FPR_FIRST + 54, FPR_FIRST + 55, \ + FPR_FIRST + 56, FPR_FIRST + 57, FPR_FIRST + 58, FPR_FIRST + 59, \ + FPR_FIRST + 60, FPR_FIRST + 61, FPR_FIRST + 62, FPR_FIRST + 63, \ + \ + /* special or fixed registers */ \ + GPR_FIRST + 0, GPR_FIRST + 1, GPR_FIRST + 2, GPR_FIRST + 3, \ + GPR_FIRST + 28, GPR_FIRST + 29, GPR_FIRST + 30, GPR_FIRST + 31, \ + ACC_FIRST + 0, ACC_FIRST + 1, ACC_FIRST + 2, ACC_FIRST + 3, \ + ACC_FIRST + 4, ACC_FIRST + 5, ACC_FIRST + 6, ACC_FIRST + 7, \ + ACC_FIRST + 8, ACC_FIRST + 9, ACC_FIRST + 10, ACC_FIRST + 11, \ + ACCG_FIRST + 0, ACCG_FIRST + 1, ACCG_FIRST + 2, ACCG_FIRST + 3, \ + ACCG_FIRST + 4, ACCG_FIRST + 5, ACCG_FIRST + 6, ACCG_FIRST + 7, \ + ACCG_FIRST + 8, ACCG_FIRST + 9, ACCG_FIRST + 10, ACCG_FIRST + 11, \ + AP_FIRST, LR_REGNO, LCR_REGNO, \ + IACC_FIRST + 0, IACC_FIRST + 1 \ +} + + +/* How Values Fit in Registers. */ + +/* A C expression for the number of consecutive hard registers, starting at + register number REGNO, required to hold a value of mode MODE. + + On a machine where all registers are exactly one word, a suitable definition + of this macro is + + #define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \ + / UNITS_PER_WORD)) */ + +/* On the FRV, make the CC modes take 3 words in the integer registers, so that + we can build the appropriate instructions to properly reload the values. */ +#define HARD_REGNO_NREGS(REGNO, MODE) frv_hard_regno_nregs (REGNO, MODE) + +/* A C expression that is nonzero if it is permissible to store a value of mode + MODE in hard register number REGNO (or in several registers starting with + that one). For a machine where all registers are equivalent, a suitable + definition is + + #define HARD_REGNO_MODE_OK(REGNO, MODE) 1 + + It is not necessary for this macro to check for the numbers of fixed + registers, because the allocation mechanism considers them to be always + occupied. + + On some machines, double-precision values must be kept in even/odd register + pairs. The way to implement that is to define this macro to reject odd + register numbers for such modes. + + The minimum requirement for a mode to be OK in a register is that the + `movMODE' instruction pattern support moves between the register and any + other hard register for which the mode is OK; and that moving a value into + the register and back out not alter it. + + Since the same instruction used to move `SImode' will work for all narrower + integer modes, it is not necessary on any machine for `HARD_REGNO_MODE_OK' + to distinguish between these modes, provided you define patterns `movhi', + etc., to take advantage of this. This is useful because of the interaction + between `HARD_REGNO_MODE_OK' and `MODES_TIEABLE_P'; it is very desirable for + all integer modes to be tieable. + + Many machines have special registers for floating point arithmetic. Often + people assume that floating point machine modes are allowed only in floating + point registers. This is not true. Any registers that can hold integers + can safely *hold* a floating point machine mode, whether or not floating + arithmetic can be done on it in those registers. Integer move instructions + can be used to move the values. + + On some machines, though, the converse is true: fixed-point machine modes + may not go in floating registers. This is true if the floating registers + normalize any value stored in them, because storing a non-floating value + there would garble it. In this case, `HARD_REGNO_MODE_OK' should reject + fixed-point machine modes in floating registers. But if the floating + registers do not automatically normalize, if you can store any bit pattern + in one and retrieve it unchanged without a trap, then any machine mode may + go in a floating register, so you can define this macro to say so. + + The primary significance of special floating registers is rather that they + are the registers acceptable in floating point arithmetic instructions. + However, this is of no concern to `HARD_REGNO_MODE_OK'. You handle it by + writing the proper constraints for those instructions. + + On some machines, the floating registers are especially slow to access, so + that it is better to store a value in a stack frame than in such a register + if floating point arithmetic is not being done. As long as the floating + registers are not in class `GENERAL_REGS', they will not be used unless some + pattern's constraint asks for one. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) frv_hard_regno_mode_ok (REGNO, MODE) + +/* A C expression that is nonzero if it is desirable to choose register + allocation so as to avoid move instructions between a value of mode MODE1 + and a value of mode MODE2. + + If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R, MODE2)' are + ever different for any R, then `MODES_TIEABLE_P (MODE1, MODE2)' must be + zero. */ +#define MODES_TIEABLE_P(MODE1, MODE2) (MODE1 == MODE2) + +/* Define this macro if the compiler should avoid copies to/from CCmode + registers. You should only define this macro if support fo copying to/from + CCmode is incomplete. */ +#define AVOID_CCMODE_COPIES + + +/* Register Classes. */ + +/* An enumeral type that must be defined with all the register class names as + enumeral values. `NO_REGS' must be first. `ALL_REGS' must be the last + register class, followed by one more enumeral value, `LIM_REG_CLASSES', + which is not a register class but rather tells how many classes there are. + + Each register class has a number, which is the value of casting the class + name to type `int'. The number serves as an index in many of the tables + described below. */ +enum reg_class +{ + NO_REGS, + ICC_REGS, + FCC_REGS, + CC_REGS, + ICR_REGS, + FCR_REGS, + CR_REGS, + LCR_REG, + LR_REG, + GR8_REGS, + GR9_REGS, + GR89_REGS, + FDPIC_REGS, + FDPIC_FPTR_REGS, + FDPIC_CALL_REGS, + SPR_REGS, + QUAD_ACC_REGS, + EVEN_ACC_REGS, + ACC_REGS, + ACCG_REGS, + QUAD_FPR_REGS, + FEVEN_REGS, + FPR_REGS, + QUAD_REGS, + EVEN_REGS, + GPR_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define GENERAL_REGS GPR_REGS + +/* The number of distinct register classes, defined as follows: + + #define N_REG_CLASSES (int) LIM_REG_CLASSES */ +#define N_REG_CLASSES ((int) LIM_REG_CLASSES) + +/* An initializer containing the names of the register classes as C string + constants. These names are used in writing some of the debugging dumps. */ +#define REG_CLASS_NAMES { \ + "NO_REGS", \ + "ICC_REGS", \ + "FCC_REGS", \ + "CC_REGS", \ + "ICR_REGS", \ + "FCR_REGS", \ + "CR_REGS", \ + "LCR_REG", \ + "LR_REG", \ + "GR8_REGS", \ + "GR9_REGS", \ + "GR89_REGS", \ + "FDPIC_REGS", \ + "FDPIC_FPTR_REGS", \ + "FDPIC_CALL_REGS", \ + "SPR_REGS", \ + "QUAD_ACC_REGS", \ + "EVEN_ACC_REGS", \ + "ACC_REGS", \ + "ACCG_REGS", \ + "QUAD_FPR_REGS", \ + "FEVEN_REGS", \ + "FPR_REGS", \ + "QUAD_REGS", \ + "EVEN_REGS", \ + "GPR_REGS", \ + "ALL_REGS" \ +} + +/* An initializer containing the contents of the register classes, as integers + which are bit masks. The Nth integer specifies the contents of class N. + The way the integer MASK is interpreted is that register R is in the class + if `MASK & (1 << R)' is 1. + + When the machine has more than 32 registers, an integer does not suffice. + Then the integers are replaced by sub-initializers, braced groupings + containing several integers. Each sub-initializer must be suitable as an + initializer for the type `HARD_REG_SET' which is defined in + `hard-reg-set.h'. */ +#define REG_CLASS_CONTENTS \ +{ /* gr0-gr31 gr32-gr63 fr0-fr31 fr32-fr-63 cc/ccr/acc ap/spr */ \ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* NO_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x000000f0,0x0}, /* ICC_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x0000000f,0x0}, /* FCC_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x000000ff,0x0}, /* CC_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x0000f000,0x0}, /* ICR_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x00000f00,0x0}, /* FCR_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x0000ff00,0x0}, /* CR_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x400}, /* LCR_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x200}, /* LR_REGS */\ + { 0x00000100,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* GR8_REGS */\ + { 0x00000200,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* GR9_REGS */\ + { 0x00000300,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* GR89_REGS */\ + { 0x00008000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* FDPIC_REGS */\ + { 0x00004000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* FDPIC_FPTR_REGS */\ + { 0x0000c000,0x00000000,0x00000000,0x00000000,0x00000000,0x0}, /* FDPIC_CALL_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1e00}, /* SPR_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x0fff0000,0x0}, /* QUAD_ACC */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x0fff0000,0x0}, /* EVEN_ACC */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0x0fff0000,0x0}, /* ACC_REGS */\ + { 0x00000000,0x00000000,0x00000000,0x00000000,0xf0000000,0xff}, /* ACCG_REGS*/\ + { 0x00000000,0x00000000,0xffffffff,0xffffffff,0x00000000,0x0}, /* QUAD_FPR */\ + { 0x00000000,0x00000000,0xffffffff,0xffffffff,0x00000000,0x0}, /* FEVEN_REG*/\ + { 0x00000000,0x00000000,0xffffffff,0xffffffff,0x00000000,0x0}, /* FPR_REGS */\ + { 0x0ffffffc,0xffffffff,0x00000000,0x00000000,0x00000000,0x0}, /* QUAD_REGS*/\ + { 0xfffffffc,0xffffffff,0x00000000,0x00000000,0x00000000,0x0}, /* EVEN_REGS*/\ + { 0xffffffff,0xffffffff,0x00000000,0x00000000,0x00000000,0x100}, /* GPR_REGS */\ + { 0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0x1fff}, /* ALL_REGS */\ +} + +/* The following macro defines cover classes for Integrated Register + Allocator. Cover classes is a set of non-intersected register + classes covering all hard registers used for register allocation + purpose. Any move between two registers of a cover class should be + cheaper than load or store of the registers. The macro value is + array of register classes with LIM_REG_CLASSES used as the end + marker. */ + +#define IRA_COVER_CLASSES \ +{ \ + GPR_REGS, FPR_REGS, ACC_REGS, ICR_REGS, FCR_REGS, ICC_REGS, FCC_REGS, \ + ACCG_REGS, SPR_REGS, \ + LIM_REG_CLASSES \ +} + +/* A C expression whose value is a register class containing hard register + REGNO. In general there is more than one such class; choose a class which + is "minimal", meaning that no smaller class also contains the register. */ + +extern enum reg_class regno_reg_class[]; +#define REGNO_REG_CLASS(REGNO) regno_reg_class [REGNO] + +/* A macro whose definition is the name of the class to which a valid base + register must belong. A base register is one used in an address which is + the register value plus a displacement. */ +#define BASE_REG_CLASS GPR_REGS + +/* A macro whose definition is the name of the class to which a valid index + register must belong. An index register is one used in an address where its + value is either multiplied by a scale factor or added to another register + (as well as added to a displacement). */ +#define INDEX_REG_CLASS GPR_REGS + +/* A C expression which is nonzero if register number NUM is suitable for use + as a base register in operand addresses. It may be either a suitable hard + register or a pseudo register that has been allocated such a hard register. */ +#define REGNO_OK_FOR_BASE_P(NUM) \ + ((NUM) < FIRST_PSEUDO_REGISTER \ + ? GPR_P (NUM) \ + : (reg_renumber [NUM] >= 0 && GPR_P (reg_renumber [NUM]))) + +/* A C expression which is nonzero if register number NUM is suitable for use + as an index register in operand addresses. It may be either a suitable hard + register or a pseudo register that has been allocated such a hard register. + + The difference between an index register and a base register is that the + index register may be scaled. If an address involves the sum of two + registers, neither one of them scaled, then either one may be labeled the + "base" and the other the "index"; but whichever labeling is used must fit + the machine's constraints of which registers may serve in each capacity. + The compiler will try both labelings, looking for one that is valid, and + will reload one or both registers only if neither labeling works. */ +#define REGNO_OK_FOR_INDEX_P(NUM) \ + ((NUM) < FIRST_PSEUDO_REGISTER \ + ? GPR_P (NUM) \ + : (reg_renumber [NUM] >= 0 && GPR_P (reg_renumber [NUM]))) + +#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \ + frv_secondary_reload_class (CLASS, MODE, X) + +#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ + frv_secondary_reload_class (CLASS, MODE, X) + +/* A C expression for the maximum number of consecutive registers of + class CLASS needed to hold a value of mode MODE. + + This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value + of the macro `CLASS_MAX_NREGS (CLASS, MODE)' should be the maximum value of + `HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class CLASS. + + This macro helps control the handling of multiple-word values in + the reload pass. + + This declaration is required. */ +#define CLASS_MAX_NREGS(CLASS, MODE) frv_class_max_nregs (CLASS, MODE) + +#define ZERO_P(x) (x == CONST0_RTX (GET_MODE (x))) + + +/* Basic Stack Layout. */ + +/* Structure to describe information about a saved range of registers */ + +typedef struct frv_stack_regs { + const char * name; /* name of the register ranges */ + int first; /* first register in the range */ + int last; /* last register in the range */ + int size_1word; /* # of bytes to be stored via 1 word stores */ + int size_2words; /* # of bytes to be stored via 2 word stores */ + unsigned char field_p; /* true if the registers are a single SPR */ + unsigned char dword_p; /* true if we can do dword stores */ + unsigned char special_p; /* true if the regs have a fixed save loc. */ +} frv_stack_regs_t; + +/* Register ranges to look into saving. */ +#define STACK_REGS_GPR 0 /* Gprs (normally gr16..gr31, gr48..gr63) */ +#define STACK_REGS_FPR 1 /* Fprs (normally fr16..fr31, fr48..fr63) */ +#define STACK_REGS_LR 2 /* LR register */ +#define STACK_REGS_CC 3 /* CCrs (normally not saved) */ +#define STACK_REGS_LCR 5 /* lcr register */ +#define STACK_REGS_STDARG 6 /* stdarg registers */ +#define STACK_REGS_STRUCT 7 /* structure return (gr3) */ +#define STACK_REGS_FP 8 /* FP register */ +#define STACK_REGS_MAX 9 /* # of register ranges */ + +/* Values for save_p field. */ +#define REG_SAVE_NO_SAVE 0 /* register not saved */ +#define REG_SAVE_1WORD 1 /* save the register */ +#define REG_SAVE_2WORDS 2 /* save register and register+1 */ + +/* Structure used to define the frv stack. */ + +typedef struct frv_stack { + int total_size; /* total bytes allocated for stack */ + int vars_size; /* variable save area size */ + int parameter_size; /* outgoing parameter size */ + int stdarg_size; /* size of regs needed to be saved for stdarg */ + int regs_size; /* size of the saved registers */ + int regs_size_1word; /* # of bytes to be stored via 1 word stores */ + int regs_size_2words; /* # of bytes to be stored via 2 word stores */ + int header_size; /* size of the old FP, struct ret., LR save */ + int pretend_size; /* size of pretend args */ + int vars_offset; /* offset to save local variables from new SP*/ + int regs_offset; /* offset to save registers from new SP */ + /* register range information */ + frv_stack_regs_t regs[STACK_REGS_MAX]; + /* offset to store each register */ + int reg_offset[FIRST_PSEUDO_REGISTER]; + /* whether to save register (& reg+1) */ + unsigned char save_p[FIRST_PSEUDO_REGISTER]; +} frv_stack_t; + +/* Define this macro if pushing a word onto the stack moves the stack pointer + to a smaller address. */ +#define STACK_GROWS_DOWNWARD 1 + +/* Define this macro to nonzero if the addresses of local variable slots + are at negative offsets from the frame pointer. */ +#define FRAME_GROWS_DOWNWARD 1 + +/* Offset from the frame pointer to the first local variable slot to be + allocated. + + If `FRAME_GROWS_DOWNWARD', find the next slot's offset by subtracting the + first slot's length from `STARTING_FRAME_OFFSET'. Otherwise, it is found by + adding the length of the first slot to the value `STARTING_FRAME_OFFSET'. */ +#define STARTING_FRAME_OFFSET 0 + +/* Offset from the stack pointer register to the first location at which + outgoing arguments are placed. If not specified, the default value of zero + is used. This is the proper value for most machines. + + If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first + location at which outgoing arguments are placed. */ +#define STACK_POINTER_OFFSET 0 + +/* Offset from the argument pointer register to the first argument's address. + On some machines it may depend on the data type of the function. + + If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first + argument's address. */ +#define FIRST_PARM_OFFSET(FUNDECL) 0 + +/* A C expression whose value is RTL representing the address in a stack frame + where the pointer to the caller's frame is stored. Assume that FRAMEADDR is + an RTL expression for the address of the stack frame itself. + + If you don't define this macro, the default is to return the value of + FRAMEADDR--that is, the stack frame address is also the address of the stack + word that points to the previous frame. */ +#define DYNAMIC_CHAIN_ADDRESS(FRAMEADDR) frv_dynamic_chain_address (FRAMEADDR) + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame, after the + prologue. FRAMEADDR is the frame pointer of the COUNT frame, or the frame + pointer of the COUNT - 1 frame if `RETURN_ADDR_IN_PREVIOUS_FRAME' is + defined. + + The value of the expression must always be the correct address when COUNT is + zero, but may be `NULL_RTX' if there is not way to determine the return + address of other frames. */ +#define RETURN_ADDR_RTX(COUNT, FRAMEADDR) frv_return_addr_rtx (COUNT, FRAMEADDR) + +#define RETURN_POINTER_REGNUM LR_REGNO + +/* A C expression whose value is RTL representing the location of the incoming + return address at the beginning of any function, before the prologue. This + RTL is either a `REG', indicating that the return value is saved in `REG', + or a `MEM' representing a location in the stack. + + You only need to define this macro if you want to support call frame + debugging information like that provided by DWARF 2. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (SImode, RETURN_POINTER_REGNUM) + + +/* Register That Address the Stack Frame. */ + +/* The register number of the stack pointer register, which must also be a + fixed register according to `FIXED_REGISTERS'. On most machines, the + hardware determines which register this is. */ +#define STACK_POINTER_REGNUM (GPR_FIRST + 1) + +/* The register number of the frame pointer register, which is used to access + automatic variables in the stack frame. On some machines, the hardware + determines which register this is. On other machines, you can choose any + register you wish for this purpose. */ +#define FRAME_POINTER_REGNUM (GPR_FIRST + 2) + +/* The register number of the arg pointer register, which is used to access the + function's argument list. On some machines, this is the same as the frame + pointer register. On some machines, the hardware determines which register + this is. On other machines, you can choose any register you wish for this + purpose. If this is not the same register as the frame pointer register, + then you must mark it as a fixed register according to `FIXED_REGISTERS', or + arrange to be able to eliminate it. */ + +/* On frv this is a fake register that is eliminated in + terms of either the frame pointer or stack pointer. */ +#define ARG_POINTER_REGNUM AP_FIRST + +/* Register numbers used for passing a function's static chain pointer. If + register windows are used, the register number as seen by the called + function is `STATIC_CHAIN_INCOMING_REGNUM', while the register number as + seen by the calling function is `STATIC_CHAIN_REGNUM'. If these registers + are the same, `STATIC_CHAIN_INCOMING_REGNUM' need not be defined. + + The static chain register need not be a fixed register. + + If the static chain is passed in memory, these macros should not be defined; + instead, the next two macros should be defined. */ +#define STATIC_CHAIN_REGNUM (GPR_FIRST + 7) +#define STATIC_CHAIN_INCOMING_REGNUM (GPR_FIRST + 7) + + +/* Eliminating the Frame Pointer and the Arg Pointer. */ + +/* If defined, this macro specifies a table of register pairs used to eliminate + unneeded registers that point into the stack frame. If it is not defined, + the only elimination attempted by the compiler is to replace references to + the frame pointer with references to the stack pointer. + + The definition of this macro is a list of structure initializations, each of + which specifies an original and replacement register. + + On some machines, the position of the argument pointer is not known until + the compilation is completed. In such a case, a separate hard register must + be used for the argument pointer. This register can be eliminated by + replacing it with either the frame pointer or the argument pointer, + depending on whether or not the frame pointer has been eliminated. + + In this case, you might specify: + #define ELIMINABLE_REGS \ + {{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} + + Note that the elimination of the argument pointer with the stack pointer is + specified first since that is the preferred elimination. */ + +#define ELIMINABLE_REGS \ +{ \ + {ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \ +} + +/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It specifies the + initial difference between the specified pair of registers. This macro must + be defined if `ELIMINABLE_REGS' is defined. */ + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = frv_initial_elimination_offset (FROM, TO) + + +/* Passing Function Arguments on the Stack. */ + +/* If defined, the maximum amount of space required for outgoing arguments will + be computed and placed into the variable + `crtl->outgoing_args_size'. No space will be pushed onto the + stack for each call; instead, the function prologue should increase the + stack frame size by this amount. + + Defining both `PUSH_ROUNDING' and `ACCUMULATE_OUTGOING_ARGS' is not + proper. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + + +/* The number of register assigned to holding function arguments. */ + +#define FRV_NUM_ARG_REGS 6 + +/* A C type for declaring a variable that is used as the first argument of + `FUNCTION_ARG' and other related values. For some target machines, the type + `int' suffices and can hold the number of bytes of argument so far. + + There is no need to record in `CUMULATIVE_ARGS' anything about the arguments + that have been passed on the stack. The compiler has other variables to + keep track of that. For target machines on which all arguments are passed + on the stack, there is no need to store anything in `CUMULATIVE_ARGS'; + however, the data structure must exist and should not be empty, so use + `int'. */ +#define CUMULATIVE_ARGS int + +/* A C statement (sans semicolon) for initializing the variable CUM for the + state at the beginning of the argument list. The variable has type + `CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type + of the function which will receive the args, or 0 if the args are to a + compiler support library function. The value of INDIRECT is nonzero when + processing an indirect call, for example a call through a function pointer. + The value of INDIRECT is zero for a call to an explicitly named function, a + library function call, or when `INIT_CUMULATIVE_ARGS' is used to find + arguments for the function being compiled. + + When processing a call to a compiler support library function, LIBNAME + identifies which one. It is a `symbol_ref' rtx which contains the name of + the function, as a string. LIBNAME is 0 when an ordinary C function call is + being processed. Thus, each time this macro is called, either LIBNAME or + FNTYPE is nonzero, but never both of them at once. */ + +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \ + frv_init_cumulative_args (&CUM, FNTYPE, LIBNAME, FNDECL, FALSE) + +/* Like `INIT_CUMULATIVE_ARGS' but overrides it for the purposes of finding the + arguments for the function being compiled. If this macro is undefined, + `INIT_CUMULATIVE_ARGS' is used instead. + + The value passed for LIBNAME is always 0, since library routines with + special calling conventions are never compiled with GCC. The argument + LIBNAME exists for symmetry with `INIT_CUMULATIVE_ARGS'. */ + +#define INIT_CUMULATIVE_INCOMING_ARGS(CUM, FNTYPE, LIBNAME) \ + frv_init_cumulative_args (&CUM, FNTYPE, LIBNAME, NULL, TRUE) + +/* A C expression that is nonzero if REGNO is the number of a hard register in + which function arguments are sometimes passed. This does *not* include + implicit arguments such as the static chain and the structure-value address. + On many machines, no registers can be used for this purpose since all + function arguments are pushed on the stack. */ +#define FUNCTION_ARG_REGNO_P(REGNO) \ + ((REGNO) >= FIRST_ARG_REGNUM && ((REGNO) <= LAST_ARG_REGNUM)) + + +/* How Scalar Function Values are Returned. */ + +/* The number of the hard register that is used to return a scalar value from a + function call. */ +#define RETURN_VALUE_REGNUM (GPR_FIRST + 8) + +#define FUNCTION_VALUE_REGNO_P(REGNO) frv_function_value_regno_p (REGNO) + + +/* How Large Values are Returned. */ + +/* The number of the register that is used to pass the structure + value address. */ +#define FRV_STRUCT_VALUE_REGNUM (GPR_FIRST + 3) + + +/* Function Entry and Exit. */ + +/* Define this macro as a C expression that is nonzero if the return + instruction or the function epilogue ignores the value of the stack pointer; + in other words, if it is safe to delete an instruction to adjust the stack + pointer before a return from the function. + + Note that this macro's value is relevant only for functions for which frame + pointers are maintained. It is never safe to delete a final stack + adjustment in a function that has no frame pointer, and the compiler knows + this regardless of `EXIT_IGNORE_STACK'. */ +#define EXIT_IGNORE_STACK 1 + +/* Generating Code for Profiling. */ + +/* A C statement or compound statement to output to FILE some assembler code to + call the profiling subroutine `mcount'. Before calling, the assembler code + must load the address of a counter variable into a register where `mcount' + expects to find the address. The name of this variable is `LP' followed by + the number LABELNO, so you would generate the name using `LP%d' in a + `fprintf'. + + The details of how the address should be passed to `mcount' are determined + by your operating system environment, not by GCC. To figure them out, + compile a small program for profiling using the system's installed C + compiler and look at the assembler code that results. + + This declaration must be present, but it can be an abort if profiling is + not implemented. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) + +/* Trampolines for Nested Functions. */ + +/* A C expression for the size in bytes of the trampoline, as an integer. */ +#define TRAMPOLINE_SIZE frv_trampoline_size () + +/* Alignment required for trampolines, in bits. + + If you don't define this macro, the value of `BIGGEST_ALIGNMENT' is used for + aligning trampolines. */ +#define TRAMPOLINE_ALIGNMENT (TARGET_FDPIC ? 64 : 32) + +/* Define this macro if trampolines need a special subroutine to do their work. + The macro should expand to a series of `asm' statements which will be + compiled with GCC. They go in a library function named + `__transfer_from_trampoline'. + + If you need to avoid executing the ordinary prologue code of a compiled C + function when you jump to the subroutine, you can do so by placing a special + label of your own in the assembler code. Use one `asm' statement to + generate an assembler label, and another to make the label global. Then + trampolines can use that label to jump directly to your special assembler + code. */ + +#ifdef __FRV_UNDERSCORE__ +#define TRAMPOLINE_TEMPLATE_NAME "___trampoline_template" +#else +#define TRAMPOLINE_TEMPLATE_NAME "__trampoline_template" +#endif + +#define Twrite _write + +#if ! __FRV_FDPIC__ +#define TRANSFER_FROM_TRAMPOLINE \ +extern int Twrite (int, const void *, unsigned); \ + \ +void \ +__trampoline_setup (short * addr, int size, int fnaddr, int sc) \ +{ \ + extern short __trampoline_template[]; \ + short * to = addr; \ + short * from = &__trampoline_template[0]; \ + int i; \ + \ + if (size < 20) \ + { \ + Twrite (2, "__trampoline_setup bad size\n", \ + sizeof ("__trampoline_setup bad size\n") - 1); \ + exit (-1); \ + } \ + \ + to[0] = from[0]; \ + to[1] = (short)(fnaddr); \ + to[2] = from[2]; \ + to[3] = (short)(sc); \ + to[4] = from[4]; \ + to[5] = (short)(fnaddr >> 16); \ + to[6] = from[6]; \ + to[7] = (short)(sc >> 16); \ + to[8] = from[8]; \ + to[9] = from[9]; \ + \ + for (i = 0; i < 20; i++) \ + __asm__ volatile ("dcf @(%0,%1)\n\tici @(%0,%1)" :: "r" (to), "r" (i)); \ +} \ + \ +__asm__("\n" \ + "\t.globl " TRAMPOLINE_TEMPLATE_NAME "\n" \ + "\t.text\n" \ + TRAMPOLINE_TEMPLATE_NAME ":\n" \ + "\tsetlos #0, gr6\n" /* jump register */ \ + "\tsetlos #0, gr7\n" /* static chain */ \ + "\tsethi #0, gr6\n" \ + "\tsethi #0, gr7\n" \ + "\tjmpl @(gr0,gr6)\n"); +#else +#define TRANSFER_FROM_TRAMPOLINE \ +extern int Twrite (int, const void *, unsigned); \ + \ +void \ +__trampoline_setup (addr, size, fnaddr, sc) \ + short * addr; \ + int size; \ + int fnaddr; \ + int sc; \ +{ \ + extern short __trampoline_template[]; \ + short * from = &__trampoline_template[0]; \ + int i; \ + short **desc = (short **)addr; \ + short * to = addr + 4; \ + \ + if (size != 32) \ + { \ + Twrite (2, "__trampoline_setup bad size\n", \ + sizeof ("__trampoline_setup bad size\n") - 1); \ + exit (-1); \ + } \ + \ + /* Create a function descriptor with the address of the code below \ + and NULL as the FDPIC value. We don't need the real GOT value \ + here, since we don't use it, so we use NULL, that is just as \ + good. */ \ + desc[0] = to; \ + desc[1] = NULL; \ + size -= 8; \ + \ + to[0] = from[0]; \ + to[1] = (short)(fnaddr); \ + to[2] = from[2]; \ + to[3] = (short)(sc); \ + to[4] = from[4]; \ + to[5] = (short)(fnaddr >> 16); \ + to[6] = from[6]; \ + to[7] = (short)(sc >> 16); \ + to[8] = from[8]; \ + to[9] = from[9]; \ + to[10] = from[10]; \ + to[11] = from[11]; \ + \ + for (i = 0; i < size; i++) \ + __asm__ volatile ("dcf @(%0,%1)\n\tici @(%0,%1)" :: "r" (to), "r" (i)); \ +} \ + \ +__asm__("\n" \ + "\t.globl " TRAMPOLINE_TEMPLATE_NAME "\n" \ + "\t.text\n" \ + TRAMPOLINE_TEMPLATE_NAME ":\n" \ + "\tsetlos #0, gr6\n" /* Jump register. */ \ + "\tsetlos #0, gr7\n" /* Static chain. */ \ + "\tsethi #0, gr6\n" \ + "\tsethi #0, gr7\n" \ + "\tldd @(gr6,gr0),gr14\n" \ + "\tjmpl @(gr14,gr0)\n" \ + ); +#endif + + +/* Addressing Modes. */ + +/* A number, the maximum number of registers that can appear in a valid memory + address. Note that it is up to you to specify a value equal to the maximum + number that `TARGET_LEGITIMATE_ADDRESS_P' would ever accept. */ +#define MAX_REGS_PER_ADDRESS 2 + +/* A C expression that is nonzero if X (assumed to be a `reg' RTX) is valid for + use as a base register. For hard registers, it should always accept those + which the hardware permits and reject the others. Whether the macro accepts + or rejects pseudo registers must be controlled by `REG_OK_STRICT' as + described above. This usually requires two variant definitions, of which + `REG_OK_STRICT' controls the one actually used. */ +#ifdef REG_OK_STRICT +#define REG_OK_FOR_BASE_P(X) GPR_P (REGNO (X)) +#else +#define REG_OK_FOR_BASE_P(X) GPR_AP_OR_PSEUDO_P (REGNO (X)) +#endif + +/* A C expression that is nonzero if X (assumed to be a `reg' RTX) is valid for + use as an index register. + + The difference between an index register and a base register is that the + index register may be scaled. If an address involves the sum of two + registers, neither one of them scaled, then either one may be labeled the + "base" and the other the "index"; but whichever labeling is used must fit + the machine's constraints of which registers may serve in each capacity. + The compiler will try both labelings, looking for one that is valid, and + will reload one or both registers only if neither labeling works. */ +#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X) + +#define FIND_BASE_TERM frv_find_base_term + +/* A C expression that is nonzero if X is a legitimate constant for an + immediate operand on the target machine. You can assume that X satisfies + `CONSTANT_P', so you need not check this. In fact, `1' is a suitable + definition for this macro on machines where anything `CONSTANT_P' is valid. */ +#define LEGITIMATE_CONSTANT_P(X) frv_legitimate_constant_p (X) + +/* The load-and-update commands allow pre-modification in addresses. + The index has to be in a register. */ +#define HAVE_PRE_MODIFY_REG 1 + + +/* We define extra CC modes in frv-modes.def so we need a selector. */ + +#define SELECT_CC_MODE frv_select_cc_mode + +/* A C expression whose value is one if it is always safe to reverse a + comparison whose mode is MODE. If `SELECT_CC_MODE' can ever return MODE for + a floating-point inequality comparison, then `REVERSIBLE_CC_MODE (MODE)' + must be zero. + + You need not define this macro if it would always returns zero or if the + floating-point format is anything other than `IEEE_FLOAT_FORMAT'. For + example, here is the definition used on the SPARC, where floating-point + inequality comparisons are always given `CCFPEmode': + + #define REVERSIBLE_CC_MODE(MODE) ((MODE) != CCFPEmode) */ + +/* On frv, don't consider floating point comparisons to be reversible. In + theory, fp equality comparisons can be reversible. */ +#define REVERSIBLE_CC_MODE(MODE) \ + ((MODE) == CCmode || (MODE) == CC_UNSmode || (MODE) == CC_NZmode) + +/* Frv CCR_MODE's are not reversible. */ +#define REVERSE_CONDEXEC_PREDICATES_P(x,y) 0 + + +/* Describing Relative Costs of Operations. */ + +/* A C expression for the cost of a branch instruction. A value of 1 is the + default; other values are interpreted relative to that. */ +#define BRANCH_COST(speed_p, predictable_p) frv_branch_cost_int + +/* Define this macro as a C expression which is nonzero if accessing less than + a word of memory (i.e. a `char' or a `short') is no faster than accessing a + word of memory, i.e., if such access require more than one instruction or if + there is no difference in cost between byte and (aligned) word loads. + + When this macro is not defined, the compiler will access a field by finding + the smallest containing object; when it is defined, a fullword load will be + used if alignment permits. Unless bytes accesses are faster than word + accesses, using word accesses is preferable since it may eliminate + subsequent memory access if subsequent accesses occur to other fields in the + same word of the structure, but to different bytes. */ +#define SLOW_BYTE_ACCESS 1 + +/* Define this macro if it is as good or better to call a constant function + address than to call an address kept in a register. */ +#define NO_FUNCTION_CSE + + +/* Dividing the output into sections. */ + +/* A C expression whose value is a string containing the assembler operation + that should precede instructions and read-only data. Normally `".text"' is + right. */ +#define TEXT_SECTION_ASM_OP "\t.text" + +/* A C expression whose value is a string containing the assembler operation to + identify the following data as writable initialized data. Normally + `".data"' is right. */ +#define DATA_SECTION_ASM_OP "\t.data" + +/* If defined, a C expression whose value is a string containing the + assembler operation to identify the following data as + uninitialized global data. If not defined, and neither + `ASM_OUTPUT_BSS' nor `ASM_OUTPUT_ALIGNED_BSS' are defined, + uninitialized global data will be output in the data section if + `-fno-common' is passed, otherwise `ASM_OUTPUT_COMMON' will be + used. */ +#define BSS_SECTION_ASM_OP "\t.section .bss,\"aw\"" + +/* Short Data Support */ +#define SDATA_SECTION_ASM_OP "\t.section .sdata,\"aw\"" + +#undef INIT_SECTION_ASM_OP +#undef FINI_SECTION_ASM_OP +#define INIT_SECTION_ASM_OP "\t.section .init,\"ax\"" +#define FINI_SECTION_ASM_OP "\t.section .fini,\"ax\"" + +#undef CTORS_SECTION_ASM_OP +#undef DTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"a\"" +#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"a\"" + +/* A C expression whose value is a string containing the assembler operation to + switch to the fixup section that records all initialized pointers in a -fpic + program so they can be changed program startup time if the program is loaded + at a different address than linked for. */ +#define FIXUP_SECTION_ASM_OP "\t.section .rofixup,\"a\"" + +/* Position Independent Code. */ + +/* A C expression that is nonzero if X is a legitimate immediate operand on the + target machine when generating position independent code. You can assume + that X satisfies `CONSTANT_P', so you need not check this. You can also + assume FLAG_PIC is true, so you need not check it either. You need not + define this macro if all constants (including `SYMBOL_REF') can be immediate + operands when generating position independent code. */ +#define LEGITIMATE_PIC_OPERAND_P(X) \ + ( GET_CODE (X) == CONST_INT \ + || GET_CODE (X) == CONST_DOUBLE \ + || (GET_CODE (X) == HIGH && GET_CODE (XEXP (X, 0)) == CONST_INT) \ + || got12_operand (X, VOIDmode)) \ + + +/* The Overall Framework of an Assembler File. */ + +/* A C string constant describing how to begin a comment in the target + assembler language. The compiler assumes that the comment will end at the + end of the line. */ +#define ASM_COMMENT_START ";" + +/* A C string constant for text to be output before each `asm' statement or + group of consecutive ones. Normally this is `"#APP"', which is a comment + that has no effect on most assemblers but tells the GNU assembler that it + must check the lines that follow for all valid assembler constructs. */ +#define ASM_APP_ON "#APP\n" + +/* A C string constant for text to be output after each `asm' statement or + group of consecutive ones. Normally this is `"#NO_APP"', which tells the + GNU assembler to resume making the time-saving assumptions that are valid + for ordinary compiler output. */ +#define ASM_APP_OFF "#NO_APP\n" + + +/* Output of Data. */ + +/* This is how to output a label to dwarf/dwarf2. */ +#define ASM_OUTPUT_DWARF_ADDR(STREAM, LABEL) \ +do { \ + fprintf (STREAM, "\t.picptr\t"); \ + assemble_name (STREAM, LABEL); \ +} while (0) + +/* Whether to emit the gas specific dwarf2 line number support. */ +#define DWARF2_ASM_LINE_DEBUG_INFO (TARGET_DEBUG_LOC) + +/* Output of Uninitialized Variables. */ + +/* A C statement (sans semicolon) to output to the stdio stream STREAM the + assembler definition of a local-common-label named NAME whose size is SIZE + bytes. The variable ROUNDED is the size rounded up to whatever alignment + the caller wants. + + Use the expression `assemble_name (STREAM, NAME)' to output the name itself; + before and after that, output the additional assembler syntax for defining + the name, and a newline. + + This macro controls how the assembler definitions of uninitialized static + variables are output. */ +#undef ASM_OUTPUT_LOCAL + +#undef ASM_OUTPUT_ALIGNED_LOCAL + +/* This is for final.c, because it is used by ASM_DECLARE_OBJECT_NAME. */ +extern int size_directive_output; + +/* Like `ASM_OUTPUT_ALIGNED_LOCAL' except that it takes an additional + parameter - the DECL of variable to be output, if there is one. + This macro can be called with DECL == NULL_TREE. If you define + this macro, it is used in place of `ASM_OUTPUT_LOCAL' and + `ASM_OUTPUT_ALIGNED_LOCAL', and gives you more flexibility in + handling the destination of the variable. */ +#undef ASM_OUTPUT_ALIGNED_DECL_LOCAL +#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(STREAM, DECL, NAME, SIZE, ALIGN) \ +do { \ + if ((SIZE) > 0 && (SIZE) <= (unsigned HOST_WIDE_INT) g_switch_value) \ + switch_to_section (get_named_section (NULL, ".sbss", 0)); \ + else \ + switch_to_section (bss_section); \ + ASM_OUTPUT_ALIGN (STREAM, floor_log2 ((ALIGN) / BITS_PER_UNIT)); \ + ASM_DECLARE_OBJECT_NAME (STREAM, NAME, DECL); \ + ASM_OUTPUT_SKIP (STREAM, (SIZE) ? (SIZE) : 1); \ +} while (0) + + +/* Output and Generation of Labels. */ + +/* A C statement (sans semicolon) to output to the stdio stream STREAM the + assembler definition of a label named NAME. Use the expression + `assemble_name (STREAM, NAME)' to output the name itself; before and after + that, output the additional assembler syntax for defining the name, and a + newline. */ +#define ASM_OUTPUT_LABEL(STREAM, NAME) \ +do { \ + assemble_name (STREAM, NAME); \ + fputs (":\n", STREAM); \ +} while (0) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP "\t.globl " + +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \ +do { \ + sprintf (LABEL, "*.%s%ld", PREFIX, (long)NUM); \ +} while (0) + + +/* Macros Controlling Initialization Routines. */ + +#undef INIT_SECTION_ASM_OP + +/* If defined, `main' will call `__main' despite the presence of + `INIT_SECTION_ASM_OP'. This macro should be defined for systems where the + init section is not actually run automatically, but is still useful for + collecting the lists of constructors and destructors. */ +#define INVOKE__main + +/* Output of Assembler Instructions. */ + +/* A C initializer containing the assembler's names for the machine registers, + each one as a C string constant. This is what translates register numbers + in the compiler into assembler language. */ +#define REGISTER_NAMES \ +{ \ + "gr0", "sp", "fp", "gr3", "gr4", "gr5", "gr6", "gr7", \ + "gr8", "gr9", "gr10", "gr11", "gr12", "gr13", "gr14", "gr15", \ + "gr16", "gr17", "gr18", "gr19", "gr20", "gr21", "gr22", "gr23", \ + "gr24", "gr25", "gr26", "gr27", "gr28", "gr29", "gr30", "gr31", \ + "gr32", "gr33", "gr34", "gr35", "gr36", "gr37", "gr38", "gr39", \ + "gr40", "gr41", "gr42", "gr43", "gr44", "gr45", "gr46", "gr47", \ + "gr48", "gr49", "gr50", "gr51", "gr52", "gr53", "gr54", "gr55", \ + "gr56", "gr57", "gr58", "gr59", "gr60", "gr61", "gr62", "gr63", \ + \ + "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7", \ + "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", \ + "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", \ + "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31", \ + "fr32", "fr33", "fr34", "fr35", "fr36", "fr37", "fr38", "fr39", \ + "fr40", "fr41", "fr42", "fr43", "fr44", "fr45", "fr46", "fr47", \ + "fr48", "fr49", "fr50", "fr51", "fr52", "fr53", "fr54", "fr55", \ + "fr56", "fr57", "fr58", "fr59", "fr60", "fr61", "fr62", "fr63", \ + \ + "fcc0", "fcc1", "fcc2", "fcc3", "icc0", "icc1", "icc2", "icc3", \ + "cc0", "cc1", "cc2", "cc3", "cc4", "cc5", "cc6", "cc7", \ + "acc0", "acc1", "acc2", "acc3", "acc4", "acc5", "acc6", "acc7", \ + "acc8", "acc9", "acc10", "acc11", \ + "accg0","accg1","accg2","accg3","accg4","accg5","accg6","accg7", \ + "accg8", "accg9", "accg10", "accg11", \ + "ap", "lr", "lcr", "iacc0h", "iacc0l" \ +} + +/* Define this macro if you are using an unusual assembler that + requires different names for the machine instructions. + + The definition is a C statement or statements which output an + assembler instruction opcode to the stdio stream STREAM. The + macro-operand PTR is a variable of type `char *' which points to + the opcode name in its "internal" form--the form that is written + in the machine description. The definition should output the + opcode name to STREAM, performing any translation you desire, and + increment the variable PTR to point at the end of the opcode so + that it will not be output twice. + + In fact, your macro definition may process less than the entire + opcode name, or more than the opcode name; but if you want to + process text that includes `%'-sequences to substitute operands, + you must take care of the substitution yourself. Just be sure to + increment PTR over whatever text should not be output normally. + + If you need to look at the operand values, they can be found as the + elements of `recog_operand'. + + If the macro definition does nothing, the instruction is output in + the usual way. */ + +#define ASM_OUTPUT_OPCODE(STREAM, PTR)\ + (PTR) = frv_asm_output_opcode (STREAM, PTR) + +/* If defined, a C statement to be executed just prior to the output + of assembler code for INSN, to modify the extracted operands so + they will be output differently. + + Here the argument OPVEC is the vector containing the operands + extracted from INSN, and NOPERANDS is the number of elements of + the vector which contain meaningful data for this insn. The + contents of this vector are what will be used to convert the insn + template into assembler code, so you can change the assembler + output by changing the contents of the vector. + + This macro is useful when various assembler syntaxes share a single + file of instruction patterns; by defining this macro differently, + you can cause a large class of instructions to be output + differently (such as with rearranged operands). Naturally, + variations in assembler syntax affecting individual insn patterns + ought to be handled by writing conditional output routines in + those patterns. + + If this macro is not defined, it is equivalent to a null statement. */ + +#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS)\ + frv_final_prescan_insn (INSN, OPVEC, NOPERANDS) + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "" +#define REGISTER_PREFIX "" +#define LOCAL_LABEL_PREFIX "." +#define IMMEDIATE_PREFIX "#" + + +/* Output of dispatch tables. */ + +/* This macro should be provided on machines where the addresses in a dispatch + table are relative to the table's own address. + + The definition should be a C statement to output to the stdio stream STREAM + an assembler pseudo-instruction to generate a difference between two labels. + VALUE and REL are the numbers of two internal labels. The definitions of + these labels are output using `(*targetm.asm_out.internal_label)', and they must be + printed in the same way here. For example, + + fprintf (STREAM, "\t.word L%d-L%d\n", VALUE, REL) */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ +fprintf (STREAM, "\t.word .L%d-.L%d\n", VALUE, REL) + +/* This macro should be provided on machines where the addresses in a dispatch + table are absolute. + + The definition should be a C statement to output to the stdio stream STREAM + an assembler pseudo-instruction to generate a reference to a label. VALUE + is the number of an internal label whose definition is output using + `(*targetm.asm_out.internal_label)'. For example, + + fprintf (STREAM, "\t.word L%d\n", VALUE) */ +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ +fprintf (STREAM, "\t.word .L%d\n", VALUE) + +#define JUMP_TABLES_IN_TEXT_SECTION (flag_pic) + +/* Assembler Commands for Exception Regions. */ + +/* Define this macro to 0 if your target supports DWARF 2 frame unwind + information, but it does not yet work with exception handling. Otherwise, + if your target supports this information (if it defines + `INCOMING_RETURN_ADDR_RTX' and either `UNALIGNED_INT_ASM_OP' or + `OBJECT_FORMAT_ELF'), GCC will provide a default definition of 1. + + If this macro is defined to 1, the DWARF 2 unwinder will be the default + exception handling mechanism; otherwise, setjmp/longjmp will be used by + default. + + If this macro is defined to anything, the DWARF 2 unwinder will be used + instead of inline unwinders and __unwind_function in the non-setjmp case. */ +#define DWARF2_UNWIND_INFO 1 + +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (LR_REGNO) + +/* Assembler Commands for Alignment. */ + +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(STREAM, NBYTES) \ + fprintf (STREAM, "\t.zero\t%u\n", (int)(NBYTES)) + +/* A C statement to output to the stdio stream STREAM an assembler command to + advance the location counter to a multiple of 2 to the POWER bytes. POWER + will be a C expression of type `int'. */ +#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ + fprintf ((STREAM), "\t.p2align %d\n", (POWER)) + +/* Inside the text section, align with unpacked nops rather than zeros. */ +#define ASM_OUTPUT_ALIGN_WITH_NOP(STREAM, POWER) \ + fprintf ((STREAM), "\t.p2alignl %d,0x80880000\n", (POWER)) + +/* Macros Affecting all Debug Formats. */ + +/* A C expression that returns the DBX register number for the compiler + register number REGNO. In simple cases, the value of this expression may be + REGNO itself. But sometimes there are some registers that the compiler + knows about and DBX does not, or vice versa. In such cases, some register + may need to have one number in the compiler and another for DBX. + + If two registers have consecutive numbers inside GCC, and they can be + used as a pair to hold a multiword value, then they *must* have consecutive + numbers after renumbering with `DBX_REGISTER_NUMBER'. Otherwise, debuggers + will be unable to access such a pair, because they expect register pairs to + be consecutive in their own numbering scheme. + + If you find yourself defining `DBX_REGISTER_NUMBER' in way that does not + preserve register pairs, then what you must do instead is redefine the + actual register numbering scheme. + + This declaration is required. */ +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Miscellaneous Parameters. */ + +/* An alias for a machine mode name. This is the machine mode that elements of + a jump-table should have. */ +#define CASE_VECTOR_MODE SImode + +/* Define this macro if operations between registers with integral mode smaller + than a word are always performed on the entire register. Most RISC machines + have this property and most CISC machines do not. */ +#define WORD_REGISTER_OPERATIONS + +/* Define this macro to be a C expression indicating when insns that read + memory in MODE, an integral mode narrower than a word, set the bits outside + of MODE to be either the sign-extension or the zero-extension of the data + read. Return `SIGN_EXTEND' for values of MODE for which the insn + sign-extends, `ZERO_EXTEND' for which it zero-extends, and `UNKNOWN' for other + modes. + + This macro is not called with MODE non-integral or with a width greater than + or equal to `BITS_PER_WORD', so you may return any value in this case. Do + not define this macro if it would always return `UNKNOWN'. On machines where + this macro is defined, you will normally define it as the constant + `SIGN_EXTEND' or `ZERO_EXTEND'. */ +#define LOAD_EXTEND_OP(MODE) SIGN_EXTEND + +/* Define if loading short immediate values into registers sign extends. */ +#define SHORT_IMMEDIATES_SIGN_EXTEND + +/* The maximum number of bytes that a single instruction can move quickly from + memory to memory. */ +#define MOVE_MAX 8 + +/* A C expression which is nonzero if on this machine it is safe to "convert" + an integer of INPREC bits to one of OUTPREC bits (where OUTPREC is smaller + than INPREC) by merely operating on it as if it had only OUTPREC bits. + + On many machines, this expression can be 1. + + When `TRULY_NOOP_TRUNCATION' returns 1 for a pair of sizes for modes for + which `MODES_TIEABLE_P' is 0, suboptimal code can result. If this is the + case, making `TRULY_NOOP_TRUNCATION' return 0 in such cases may improve + things. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* An alias for the machine mode for pointers. On most machines, define this + to be the integer mode corresponding to the width of a hardware pointer; + `SImode' on 32-bit machine or `DImode' on 64-bit machines. On some machines + you must define this to be one of the partial integer modes, such as + `PSImode'. + + The width of `Pmode' must be at least as large as the value of + `POINTER_SIZE'. If it is not equal, you must define the macro + `POINTERS_EXTEND_UNSIGNED' to specify how pointers are extended to `Pmode'. */ +#define Pmode SImode + +/* An alias for the machine mode used for memory references to functions being + called, in `call' RTL expressions. On most machines this should be + `QImode'. */ +#define FUNCTION_MODE QImode + +/* A C expression for the maximum number of instructions to execute via + conditional execution instructions instead of a branch. A value of + BRANCH_COST+1 is the default if the machine does not use + cc0, and 1 if it does use cc0. */ +#define MAX_CONDITIONAL_EXECUTE frv_condexec_insns + +/* A C expression to modify the code described by the conditional if + information CE_INFO, possibly updating the tests in TRUE_EXPR, and + FALSE_EXPR for converting if-then and if-then-else code to conditional + instructions. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if the + tests cannot be converted. */ +#define IFCVT_MODIFY_TESTS(CE_INFO, TRUE_EXPR, FALSE_EXPR) \ +frv_ifcvt_modify_tests (CE_INFO, &TRUE_EXPR, &FALSE_EXPR) + +/* A C expression to modify the code described by the conditional if + information CE_INFO, for the basic block BB, possibly updating the tests in + TRUE_EXPR, and FALSE_EXPR for converting the && and || parts of if-then or + if-then-else code to conditional instructions. OLD_TRUE and OLD_FALSE are + the previous tests. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if + the tests cannot be converted. */ +#define IFCVT_MODIFY_MULTIPLE_TESTS(CE_INFO, BB, TRUE_EXPR, FALSE_EXPR) \ +frv_ifcvt_modify_multiple_tests (CE_INFO, BB, &TRUE_EXPR, &FALSE_EXPR) + +/* A C expression to modify the code described by the conditional if + information CE_INFO with the new PATTERN in INSN. If PATTERN is a null + pointer after the IFCVT_MODIFY_INSN macro executes, it is assumed that that + insn cannot be converted to be executed conditionally. */ +#define IFCVT_MODIFY_INSN(CE_INFO, PATTERN, INSN) \ +(PATTERN) = frv_ifcvt_modify_insn (CE_INFO, PATTERN, INSN) + +/* A C expression to perform any final machine dependent modifications in + converting code to conditional execution in the code described by the + conditional if information CE_INFO. */ +#define IFCVT_MODIFY_FINAL(CE_INFO) frv_ifcvt_modify_final (CE_INFO) + +/* A C expression to cancel any machine dependent modifications in converting + code to conditional execution in the code described by the conditional if + information CE_INFO. */ +#define IFCVT_MODIFY_CANCEL(CE_INFO) frv_ifcvt_modify_cancel (CE_INFO) + +/* Initialize the extra fields provided by IFCVT_EXTRA_FIELDS. */ +#define IFCVT_INIT_EXTRA_FIELDS(CE_INFO) frv_ifcvt_init_extra_fields (CE_INFO) + +/* The definition of the following macro results in that the 2nd jump + optimization (after the 2nd insn scheduling) is minimal. It is + necessary to define when start cycle marks of insns (TImode is used + for this) is used for VLIW insn packing. Some jump optimizations + make such marks invalid. These marks are corrected for some + (minimal) optimizations. ??? Probably the macro is temporary. + Final solution could making the 2nd jump optimizations before the + 2nd instruction scheduling or corrections of the marks for all jump + optimizations. Although some jump optimizations are actually + deoptimizations for VLIW (super-scalar) processors. */ + +#define MINIMAL_SECOND_JUMP_OPTIMIZATION + + +/* If the following macro is defined and nonzero and deterministic + finite state automata are used for pipeline hazard recognition, we + will try to exchange insns in queue ready to improve the schedule. + The more macro value, the more tries will be made. */ +#define FIRST_CYCLE_MULTIPASS_SCHEDULING 1 + +/* The following macro is used only when value of + FIRST_CYCLE_MULTIPASS_SCHEDULING is nonzero. The more macro value, + the more tries will be made to choose better schedule. If the + macro value is zero or negative there will be no multi-pass + scheduling. */ +#define FIRST_CYCLE_MULTIPASS_SCHEDULING_LOOKAHEAD frv_sched_lookahead + +enum frv_builtins +{ + FRV_BUILTIN_MAND, + FRV_BUILTIN_MOR, + FRV_BUILTIN_MXOR, + FRV_BUILTIN_MNOT, + FRV_BUILTIN_MAVEH, + FRV_BUILTIN_MSATHS, + FRV_BUILTIN_MSATHU, + FRV_BUILTIN_MADDHSS, + FRV_BUILTIN_MADDHUS, + FRV_BUILTIN_MSUBHSS, + FRV_BUILTIN_MSUBHUS, + FRV_BUILTIN_MPACKH, + FRV_BUILTIN_MQADDHSS, + FRV_BUILTIN_MQADDHUS, + FRV_BUILTIN_MQSUBHSS, + FRV_BUILTIN_MQSUBHUS, + FRV_BUILTIN_MUNPACKH, + FRV_BUILTIN_MDPACKH, + FRV_BUILTIN_MBTOH, + FRV_BUILTIN_MHTOB, + FRV_BUILTIN_MCOP1, + FRV_BUILTIN_MCOP2, + FRV_BUILTIN_MROTLI, + FRV_BUILTIN_MROTRI, + FRV_BUILTIN_MWCUT, + FRV_BUILTIN_MSLLHI, + FRV_BUILTIN_MSRLHI, + FRV_BUILTIN_MSRAHI, + FRV_BUILTIN_MEXPDHW, + FRV_BUILTIN_MEXPDHD, + FRV_BUILTIN_MMULHS, + FRV_BUILTIN_MMULHU, + FRV_BUILTIN_MMULXHS, + FRV_BUILTIN_MMULXHU, + FRV_BUILTIN_MMACHS, + FRV_BUILTIN_MMACHU, + FRV_BUILTIN_MMRDHS, + FRV_BUILTIN_MMRDHU, + FRV_BUILTIN_MQMULHS, + FRV_BUILTIN_MQMULHU, + FRV_BUILTIN_MQMULXHU, + FRV_BUILTIN_MQMULXHS, + FRV_BUILTIN_MQMACHS, + FRV_BUILTIN_MQMACHU, + FRV_BUILTIN_MCPXRS, + FRV_BUILTIN_MCPXRU, + FRV_BUILTIN_MCPXIS, + FRV_BUILTIN_MCPXIU, + FRV_BUILTIN_MQCPXRS, + FRV_BUILTIN_MQCPXRU, + FRV_BUILTIN_MQCPXIS, + FRV_BUILTIN_MQCPXIU, + FRV_BUILTIN_MCUT, + FRV_BUILTIN_MCUTSS, + FRV_BUILTIN_MWTACC, + FRV_BUILTIN_MWTACCG, + FRV_BUILTIN_MRDACC, + FRV_BUILTIN_MRDACCG, + FRV_BUILTIN_MTRAP, + FRV_BUILTIN_MCLRACC, + FRV_BUILTIN_MCLRACCA, + FRV_BUILTIN_MDUNPACKH, + FRV_BUILTIN_MBTOHE, + FRV_BUILTIN_MQXMACHS, + FRV_BUILTIN_MQXMACXHS, + FRV_BUILTIN_MQMACXHS, + FRV_BUILTIN_MADDACCS, + FRV_BUILTIN_MSUBACCS, + FRV_BUILTIN_MASACCS, + FRV_BUILTIN_MDADDACCS, + FRV_BUILTIN_MDSUBACCS, + FRV_BUILTIN_MDASACCS, + FRV_BUILTIN_MABSHS, + FRV_BUILTIN_MDROTLI, + FRV_BUILTIN_MCPLHI, + FRV_BUILTIN_MCPLI, + FRV_BUILTIN_MDCUTSSI, + FRV_BUILTIN_MQSATHS, + FRV_BUILTIN_MQLCLRHS, + FRV_BUILTIN_MQLMTHS, + FRV_BUILTIN_MQSLLHI, + FRV_BUILTIN_MQSRAHI, + FRV_BUILTIN_MHSETLOS, + FRV_BUILTIN_MHSETLOH, + FRV_BUILTIN_MHSETHIS, + FRV_BUILTIN_MHSETHIH, + FRV_BUILTIN_MHDSETS, + FRV_BUILTIN_MHDSETH, + FRV_BUILTIN_SMUL, + FRV_BUILTIN_UMUL, + FRV_BUILTIN_PREFETCH0, + FRV_BUILTIN_PREFETCH, + FRV_BUILTIN_SMASS, + FRV_BUILTIN_SMSSS, + FRV_BUILTIN_SMU, + FRV_BUILTIN_SCUTSS, + FRV_BUILTIN_ADDSS, + FRV_BUILTIN_SUBSS, + FRV_BUILTIN_SLASS, + FRV_BUILTIN_IACCreadll, + FRV_BUILTIN_IACCreadl, + FRV_BUILTIN_IACCsetll, + FRV_BUILTIN_IACCsetl, + FRV_BUILTIN_SCAN, + FRV_BUILTIN_READ8, + FRV_BUILTIN_READ16, + FRV_BUILTIN_READ32, + FRV_BUILTIN_READ64, + FRV_BUILTIN_WRITE8, + FRV_BUILTIN_WRITE16, + FRV_BUILTIN_WRITE32, + FRV_BUILTIN_WRITE64 +}; +#define FRV_BUILTIN_FIRST_NONMEDIA FRV_BUILTIN_SMUL + +/* Enable prototypes on the call rtl functions. */ +#define MD_CALL_PROTOTYPES 1 + +#define CPU_UNITS_QUERY 1 + +#ifdef __FRV_FDPIC__ +#define CRT_GET_RFIB_DATA(dbase) \ + ({ extern void *_GLOBAL_OFFSET_TABLE_; (dbase) = &_GLOBAL_OFFSET_TABLE_; }) +#endif + +#endif /* __FRV_H__ */ diff --git a/gcc/config/frv/frv.md b/gcc/config/frv/frv.md new file mode 100644 index 000000000..cc4a49141 --- /dev/null +++ b/gcc/config/frv/frv.md @@ -0,0 +1,8022 @@ +;; Frv Machine Description +;; Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2010 +;; Free Software Foundation, Inc. +;; Contributed by Red Hat, Inc. + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; GCC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. + + +;; :::::::::::::::::::: +;; :: +;; :: Unspec's used +;; :: +;; :::::::::::::::::::: + +;; GOT constants must go 12/HI/LO for the splitter to work + +(define_constants + [(UNSPEC_BLOCKAGE 0) + (UNSPEC_CC_TO_GPR 1) + (UNSPEC_GPR_TO_CC 2) + (UNSPEC_PIC_PROLOGUE 3) + (UNSPEC_CR_LOGIC 4) + (UNSPEC_STACK_ADJUST 5) + (UNSPEC_EH_RETURN_EPILOGUE 6) + (UNSPEC_GOT 7) + (UNSPEC_LDD 8) + (UNSPEC_OPTIONAL_MEMBAR 9) + + (UNSPEC_GETTLSOFF 200) + (UNSPEC_TLS_LOAD_GOTTLSOFF12 201) + (UNSPEC_TLS_INDIRECT_CALL 202) + (UNSPEC_TLS_TLSDESC_LDD 203) + (UNSPEC_TLS_TLSDESC_LDD_AUX 204) + (UNSPEC_TLS_TLSOFF_LD 205) + (UNSPEC_TLS_LDDI 206) + (UNSPEC_TLSOFF_HILO 207) + + (R_FRV_GOT12 11) + (R_FRV_GOTHI 12) + (R_FRV_GOTLO 13) + (R_FRV_FUNCDESC 14) + (R_FRV_FUNCDESC_GOT12 15) + (R_FRV_FUNCDESC_GOTHI 16) + (R_FRV_FUNCDESC_GOTLO 17) + (R_FRV_FUNCDESC_VALUE 18) + (R_FRV_FUNCDESC_GOTOFF12 19) + (R_FRV_FUNCDESC_GOTOFFHI 20) + (R_FRV_FUNCDESC_GOTOFFLO 21) + (R_FRV_GOTOFF12 22) + (R_FRV_GOTOFFHI 23) + (R_FRV_GOTOFFLO 24) + (R_FRV_GPREL12 25) + (R_FRV_GPRELHI 26) + (R_FRV_GPRELLO 27) + (R_FRV_GOTTLSOFF_HI 28) + (R_FRV_GOTTLSOFF_LO 29) + (R_FRV_TLSMOFFHI 30) + (R_FRV_TLSMOFFLO 31) + (R_FRV_TLSMOFF12 32) + (R_FRV_TLSDESCHI 33) + (R_FRV_TLSDESCLO 34) + (R_FRV_GOTTLSDESCHI 35) + (R_FRV_GOTTLSDESCLO 36) + + (GR8_REG 8) + (GR9_REG 9) + (GR14_REG 14) + ;; LR_REG conflicts with definition in frv.h + (LRREG 169) + (FDPIC_REG 15) + ]) + +(define_mode_iterator IMODE [QI HI SI DI]) +(define_mode_attr IMODEsuffix [(QI "b") (HI "h") (SI "") (DI "d")]) +(define_mode_attr BREADsuffix [(QI "ub") (HI "uh") (SI "") (DI "d")]) + +;; :::::::::::::::::::: +;; :: +;; :: Constraints +;; :: +;; :::::::::::::::::::: + +;; Standard Constraints +;; +;; `m' A memory operand is allowed, with any kind of address that the +;; machine supports in general. +;; +;; `o' A memory operand is allowed, but only if the address is +;; "offsettable". This means that adding a small integer (actually, the +;; width in bytes of the operand, as determined by its machine mode) may be +;; added to the address and the result is also a valid memory address. +;; +;; `V' A memory operand that is not offsettable. In other words, +;; anything that would fit the `m' constraint but not the `o' constraint. +;; +;; `<' A memory operand with autodecrement addressing (either +;; predecrement or postdecrement) is allowed. +;; +;; `>' A memory operand with autoincrement addressing (either +;; preincrement or postincrement) is allowed. +;; +;; `r' A register operand is allowed provided that it is in a general +;; register. +;; +;; `d', `a', `f', ... +;; Other letters can be defined in machine-dependent fashion to stand for +;; particular classes of registers. `d', `a' and `f' are defined on the +;; 68000/68020 to stand for data, address and floating point registers. +;; +;; `i' An immediate integer operand (one with constant value) is allowed. +;; This includes symbolic constants whose values will be known only at +;; assembly time. +;; +;; `n' An immediate integer operand with a known numeric value is allowed. +;; Many systems cannot support assembly-time constants for operands less +;; than a word wide. Constraints for these operands should use `n' rather +;; than `i'. +;; +;; 'I' First machine-dependent integer constant (6-bit signed ints). +;; 'J' Second machine-dependent integer constant (10-bit signed ints). +;; 'K' Third machine-dependent integer constant (-2048). +;; 'L' Fourth machine-dependent integer constant (16-bit signed ints). +;; 'M' Fifth machine-dependent integer constant (16-bit unsigned ints). +;; 'N' Sixth machine-dependent integer constant (-2047..-1). +;; 'O' Seventh machine-dependent integer constant (zero). +;; 'P' Eighth machine-dependent integer constant (1..2047). +;; +;; Other letters in the range `I' through `P' may be defined in a +;; machine-dependent fashion to permit immediate integer operands with +;; explicit integer values in specified ranges. For example, on the 68000, +;; `I' is defined to stand for the range of values 1 to 8. This is the +;; range permitted as a shift count in the shift instructions. +;; +;; `E' An immediate floating operand (expression code `const_double') is +;; allowed, but only if the target floating point format is the same as +;; that of the host machine (on which the compiler is running). +;; +;; `F' An immediate floating operand (expression code `const_double') is +;; allowed. +;; +;; 'G' First machine-dependent const_double. +;; 'H' Second machine-dependent const_double. +;; +;; `s' An immediate integer operand whose value is not an explicit +;; integer is allowed. +;; +;; This might appear strange; if an insn allows a constant operand with a +;; value not known at compile time, it certainly must allow any known +;; value. So why use `s' instead of `i'? Sometimes it allows better code +;; to be generated. +;; +;; For example, on the 68000 in a fullword instruction it is possible to +;; use an immediate operand; but if the immediate value is between -128 and +;; 127, better code results from loading the value into a register and +;; using the register. This is because the load into the register can be +;; done with a `moveq' instruction. We arrange for this to happen by +;; defining the letter `K' to mean "any integer outside the range -128 to +;; 127", and then specifying `Ks' in the operand constraints. +;; +;; `g' Any register, memory or immediate integer operand is allowed, +;; except for registers that are not general registers. +;; +;; `X' Any operand whatsoever is allowed, even if it does not satisfy +;; `general_operand'. This is normally used in the constraint of a +;; `match_scratch' when certain alternatives will not actually require a +;; scratch register. +;; +;; `0' Match operand 0. +;; `1' Match operand 1. +;; `2' Match operand 2. +;; `3' Match operand 3. +;; `4' Match operand 4. +;; `5' Match operand 5. +;; `6' Match operand 6. +;; `7' Match operand 7. +;; `8' Match operand 8. +;; `9' Match operand 9. +;; +;; An operand that matches the specified operand number is allowed. If a +;; digit is used together with letters within the same alternative, the +;; digit should come last. +;; +;; This is called a "matching constraint" and what it really means is that +;; the assembler has only a single operand that fills two roles considered +;; separate in the RTL insn. For example, an add insn has two input +;; operands and one output operand in the RTL, but on most CISC machines an +;; add instruction really has only two operands, one of them an +;; input-output operand: +;; +;; addl #35,r12 +;; +;; Matching constraints are used in these circumstances. More precisely, +;; the two operands that match must include one input-only operand and one +;; output-only operand. Moreover, the digit must be a smaller number than +;; the number of the operand that uses it in the constraint. +;; +;; For operands to match in a particular case usually means that they are +;; identical-looking RTL expressions. But in a few special cases specific +;; kinds of dissimilarity are allowed. For example, `*x' as an input +;; operand will match `*x++' as an output operand. For proper results in +;; such cases, the output template should always use the output-operand's +;; number when printing the operand. +;; +;; `p' An operand that is a valid memory address is allowed. This is for +;; "load address" and "push address" instructions. +;; +;; `p' in the constraint must be accompanied by `address_operand' as the +;; predicate in the `match_operand'. This predicate interprets the mode +;; specified in the `match_operand' as the mode of the memory reference for +;; which the address would be valid. +;; +;; `Q` First non constant, non register machine-dependent insns +;; `R` Second non constant, non register machine-dependent insns +;; `S` Third non constant, non register machine-dependent insns +;; `T` Fourth non constant, non register machine-dependent insns +;; `U` Fifth non constant, non register machine-dependent insns +;; +;; Letters in the range `Q' through `U' may be defined in a +;; machine-dependent fashion to stand for arbitrary operand types. The +;; machine description macro `EXTRA_CONSTRAINT' is passed the operand as +;; its first argument and the constraint letter as its second operand. +;; +;; A typical use for this would be to distinguish certain types of memory +;; references that affect other insn operands. +;; +;; Do not define these constraint letters to accept register references +;; (`reg'); the reload pass does not expect this and would not handle it +;; properly. + +;; Multiple Alternative Constraints +;; `?' Disparage slightly the alternative that the `?' appears in, as a +;; choice when no alternative applies exactly. The compiler regards this +;; alternative as one unit more costly for each `?' that appears in it. +;; +;; `!' Disparage severely the alternative that the `!' appears in. This +;; alternative can still be used if it fits without reloading, but if +;; reloading is needed, some other alternative will be used. + +;; Constraint modifiers +;; `=' Means that this operand is write-only for this instruction: the +;; previous value is discarded and replaced by output data. +;; +;; `+' Means that this operand is both read and written by the +;; instruction. +;; +;; When the compiler fixes up the operands to satisfy the constraints, it +;; needs to know which operands are inputs to the instruction and which are +;; outputs from it. `=' identifies an output; `+' identifies an operand +;; that is both input and output; all other operands are assumed to be +;; input only. +;; +;; `&' Means (in a particular alternative) that this operand is written +;; before the instruction is finished using the input operands. Therefore, +;; this operand may not lie in a register that is used as an input operand +;; or as part of any memory address. +;; +;; `&' applies only to the alternative in which it is written. In +;; constraints with multiple alternatives, sometimes one alternative +;; requires `&' while others do not. +;; +;; `&' does not obviate the need to write `='. +;; +;; `%' Declares the instruction to be commutative for this operand and the +;; following operand. This means that the compiler may interchange the two +;; operands if that is the cheapest way to make all operands fit the +;; constraints. This is often used in patterns for addition instructions +;; that really have only two operands: the result must go in one of the +;; arguments. +;; +;; `#' Says that all following characters, up to the next comma, are to be +;; ignored as a constraint. They are significant only for choosing +;; register preferences. +;; +;; `*' Says that the following character should be ignored when choosing +;; register preferences. `*' has no effect on the meaning of the +;; constraint as a constraint, and no effect on reloading. + + +;; :::::::::::::::::::: +;; :: +;; :: Attributes +;; :: +;; :::::::::::::::::::: + +;; The `define_attr' expression is used to define each attribute required by +;; the target machine. It looks like: +;; +;; (define_attr NAME LIST-OF-VALUES DEFAULT) + +;; NAME is a string specifying the name of the attribute being defined. + +;; LIST-OF-VALUES is either a string that specifies a comma-separated list of +;; values that can be assigned to the attribute, or a null string to indicate +;; that the attribute takes numeric values. + +;; DEFAULT is an attribute expression that gives the value of this attribute +;; for insns that match patterns whose definition does not include an explicit +;; value for this attribute. + +;; For each defined attribute, a number of definitions are written to the +;; `insn-attr.h' file. For cases where an explicit set of values is specified +;; for an attribute, the following are defined: + +;; * A `#define' is written for the symbol `HAVE_ATTR_NAME'. +;; +;; * An enumeral class is defined for `attr_NAME' with elements of the +;; form `UPPER-NAME_UPPER-VALUE' where the attribute name and value are first +;; converted to upper case. +;; +;; * A function `get_attr_NAME' is defined that is passed an insn and +;; returns the attribute value for that insn. + +;; For example, if the following is present in the `md' file: +;; +;; (define_attr "type" "branch,fp,load,store,arith" ...) +;; +;; the following lines will be written to the file `insn-attr.h'. +;; +;; #define HAVE_ATTR_type +;; enum attr_type {TYPE_BRANCH, TYPE_FP, TYPE_LOAD, TYPE_STORE, TYPE_ARITH}; +;; extern enum attr_type get_attr_type (); + +;; If the attribute takes numeric values, no `enum' type will be defined and +;; the function to obtain the attribute's value will return `int'. + +(define_attr "length" "" (const_int 4)) + +;; Processor type -- this attribute must exactly match the processor_type +;; enumeration in frv-protos.h. + +(define_attr "cpu" "generic,fr550,fr500,fr450,fr405,fr400,fr300,simple,tomcat" + (const (symbol_ref "(enum attr_cpu) frv_cpu_type"))) + +;; Attribute is "yes" for branches and jumps that span too great a distance +;; to be implemented in the most natural way. Such instructions will use +;; a call instruction in some way. + +(define_attr "far_jump" "yes,no" (const_string "no")) + +;; Instruction type +;; "unknown" must come last. +(define_attr "type" + "int,sethi,setlo,mul,div,gload,gstore,fload,fstore,movfg,movgf,macc,scan,cut,branch,jump,jumpl,call,spr,trap,fnop,fsconv,fsadd,fscmp,fsmul,fsmadd,fsdiv,sqrt_single,fdconv,fdadd,fdcmp,fdmul,fdmadd,fddiv,sqrt_double,mnop,mlogic,maveh,msath,maddh,mqaddh,mpackh,munpackh,mdpackh,mbhconv,mrot,mshift,mexpdhw,mexpdhd,mwcut,mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,mcpx,mqcpx,mcut,mclracc,mclracca,mdunpackh,mbhconve,mrdacc,mwtacc,maddacc,mdaddacc,mabsh,mdrot,mcpl,mdcut,mqsath,mqlimh,mqshift,mset,ccr,multi,load_or_call,unknown" + (const_string "unknown")) + +(define_attr "acc_group" "none,even,odd" + (symbol_ref "(enum attr_acc_group) frv_acc_group (insn)")) + +;; Scheduling and Packing Overview +;; ------------------------------- +;; +;; FR-V instructions are divided into five groups: integer, floating-point, +;; media, branch and control. Each group is associated with a separate set +;; of processing units, the number and behavior of which depend on the target +;; target processor. Integer units have names like I0 and I1, floating-point +;; units have names like F0 and F1, and so on. +;; +;; Each member of the FR-V family has its own restrictions on which +;; instructions can issue to which units. For example, some processors +;; allow loads to issue to I0 or I1 while others only allow them to issue +;; to I0. As well as these processor-specific restrictions, there is a +;; general rule that an instruction can only issue to unit X + 1 if an +;; instruction in the same packet issued to unit X. +;; +;; Sometimes the only way to honor these restrictions is by adding nops +;; to a packet. For example, on the fr550, media instructions that access +;; ACC4-7 can only issue to M1 or M3. It is therefore only possible to +;; execute these instructions by packing them with something that issues +;; to M0. When no useful M0 instruction exists, an "mnop" can be used +;; instead. +;; +;; Having decided which instructions should issue to which units, the packet +;; should be ordered according to the following template: +;; +;; I0 F0/M0 I1 F1/M1 .... B0 B1 ... +;; +;; Note that VLIW packets execute strictly in parallel. Every instruction +;; in the packet will stall until all input operands are ready. These +;; operands are then read simultaneously before any registers are modified. +;; This means that it's OK to have write-after-read hazards between +;; instructions in the same packet, even if the write is listed earlier +;; than the read. +;; +;; Three gcc passes are involved in generating VLIW packets: +;; +;; (1) The scheduler. This pass uses the standard scheduling code and +;; behaves in much the same way as it would for a superscalar RISC +;; architecture. +;; +;; (2) frv_reorg. This pass inserts nops into packets in order to meet +;; the processor's issue requirements. It also has code to optimize +;; the type of padding used to align labels. +;; +;; (3) frv_pack_insns. The final packing phase, which puts the +;; instructions into assembly language order according to the +;; "I0 F0/M0 ..." template above. +;; +;; In the ideal case, these three passes will agree on which instructions +;; should be packed together, but this won't always happen. In particular: +;; +;; (a) (2) might not pack predicated instructions in the same way as (1). +;; The scheduler tries to schedule predicated instructions for the +;; worst case, assuming the predicate is true. However, if we have +;; something like a predicated load, it isn't always possible to +;; fill the load delay with useful instructions. (2) should then +;; pack the user of the loaded value as aggressively as possible, +;; in order to optimize the case when the predicate is false. +;; See frv_pack_insn_p for more details. +;; +;; (b) The final shorten_branches pass runs between (2) and (3). +;; Since (2) inserts nops, it is possible that some branches +;; that were thought to be in range during (2) turned out to +;; out-of-range in (3). +;; +;; All three passes use DFAs to model issue restrictions. The main +;; question that the DFAs are supposed to answer is simply: can these +;; instructions be packed together? The DFAs are not responsible for +;; assigning instructions to execution units; that's the job of +;; frv_sort_insn_group, see below for details. +;; +;; To get the best results, the DFAs should try to allow packets to +;; be built in every possible order. This gives the scheduler more +;; flexibility, removing the need for things like multipass lookahead. +;; It also means we can take more advantage of inter-packet dependencies. +;; +;; For example, suppose we're compiling for the fr400 and we have: +;; +;; addi gr4,#1,gr5 +;; ldi @(gr6,gr0),gr4 +;; +;; We can pack these instructions together by assigning the load to I0 and +;; the addition to I1. However, because of the anti dependence between the +;; two instructions, the scheduler must schedule the addition first. +;; We should generally get better schedules if the DFA allows both +;; (ldi, addi) and (addi, ldi), leaving the final packing pass to +;; reorder the packet where appropriate. +;; +;; Almost all integer instructions can issue to any unit in the range I0 +;; to Ix, where the value of "x" depends on the type of instruction and +;; on the target processor. The rules for other instruction groups are +;; usually similar. +;; +;; When the restrictions are as regular as this, we can get the desired +;; behavior by claiming the DFA unit associated with the highest unused +;; execution unit. For example, if an instruction can issue to I0 or I1, +;; the DFA first tries to take the DFA unit associated with I1, and will +;; only take I0's unit if I1 isn't free. (Note that, as mentioned above, +;; the DFA does not assign instructions to units. An instruction that +;; claims DFA unit I1 will not necessarily issue to I1 in the final packet.) +;; +;; There are some cases, such as the fr550 media restriction mentioned +;; above, where the rule is not as simple as "any unit between 0 and X". +;; Even so, allocating higher units first brings us close to the ideal. +;; +;; Having divided instructions into packets, passes (2) and (3) must +;; assign instructions to specific execution units. They do this using +;; the following algorithm: +;; +;; 1. Partition the instructions into groups (integer, float/media, etc.) +;; +;; 2. For each group of instructions: +;; +;; (a) Issue each instruction in the reset DFA state and use the +;; DFA cpu_unit_query interface to find out which unit it picks +;; first. +;; +;; (b) Sort the instructions into ascending order of picked units. +;; Instructions that pick I1 first come after those that pick +;; I0 first, and so on. Let S be the sorted sequence and S[i] +;; be the ith element of it (counting from zero). +;; +;; (c) If this is the control or branch group, goto (i) +;; +;; (d) Find the largest L such that S[0]...S[L-1] can be issued +;; consecutively from the reset state and such that the DFA +;; claims unit X when S[X] is added. Let D be the DFA state +;; after instructions S[0]...S[L-1] have been issued. +;; +;; (e) If L is the length of S, goto (i) +;; +;; (f) Let U be the number of units belonging to this group and #S be +;; the length of S. Create a new sequence S' by concatenating +;; S[L]...S[#S-1] and (U - #S) nops. +;; +;; (g) For each permutation S'' of S', try issuing S'' from last to +;; first, starting with state D. See if the DFA claims unit +;; X + L when each S''[X] is added. If so, set S to the +;; concatenation of S[0]...S[L-1] and S'', then goto (i). +;; +;; (h) If (g) found no permutation, abort. +;; +;; (i) S is now the sorted sequence for this group, meaning that S[X] +;; issues to unit X. Trim any unwanted nops from the end of S. +;; +;; The sequence calculated by (b) is trivially correct for control +;; instructions since they can't be packed. It is also correct for branch +;; instructions due to their simple issue requirements. For integer and +;; floating-point/media instructions, the sequence calculated by (b) is +;; often the correct answer; the rest of the algorithm is optimized for +;; the case in which it is correct. +;; +;; If there were no irregularities in the issue restrictions then step +;; (d) would not be needed. It is mainly there to cope with the fr550 +;; integer restrictions, where a store can issue to I1, but only if a store +;; also issues to I0. (Note that if a packet has two stores, they will be +;; at the beginning of the sequence calculated by (b).) It also copes +;; with fr400 M-2 instructions, which must issue to M0, and which cannot +;; be issued together with an mnop in M1. +;; +;; Step (g) is the main one for integer and float/media instructions. +;; The first permutation it tries is S' itself (because, as noted above, +;; the sequence calculated by (b) is often correct). If S' doesn't work, +;; the implementation tries varying the beginning of the sequence first. +;; Thus the nops towards the end of the sequence will only move to lower +;; positions if absolutely necessary. +;; +;; The algorithm is theoretically exponential in the number of instructions +;; in a group, although it's only O(n log(n)) if the sequence calculated by +;; (b) is acceptable. In practice, the algorithm completes quickly even +;; in the rare cases where (g) needs to try other permutations. +(define_automaton "integer, float_media, branch, control, idiv, div") + +;; The main issue units. Note that not all units are available on +;; all processors. +(define_query_cpu_unit "i0,i1,i2,i3" "integer") +(define_query_cpu_unit "f0,f1,f2,f3" "float_media") +(define_query_cpu_unit "b0,b1" "branch") +(define_query_cpu_unit "c" "control") + +;; Division units. +(define_cpu_unit "idiv1,idiv2" "idiv") +(define_cpu_unit "div1,div2,root" "div") + +;; Control instructions cannot be packed with others. +(define_reservation "control" "i0+i1+i2+i3+f0+f1+f2+f3+b0+b1") + +;; Generic reservation for control insns +(define_insn_reservation "control" 1 + (eq_attr "type" "trap,spr,unknown,multi") + "c + control") + +;; Reservation for relaxable calls to gettlsoff. +(define_insn_reservation "load_or_call" 3 + (eq_attr "type" "load_or_call") + "c + control") + +;; :::::::::::::::::::: +;; :: +;; :: Generic/FR500 scheduler description +;; :: +;; :::::::::::::::::::: + +;; Integer insns +;; Synthetic units used to describe issue restrictions. +(define_automaton "fr500_integer") +(define_cpu_unit "fr500_load0,fr500_load1,fr500_store0" "fr500_integer") +(exclusion_set "fr500_load0,fr500_load1" "fr500_store0") + +(define_bypass 0 "fr500_i1_sethi" "fr500_i1_setlo") +(define_insn_reservation "fr500_i1_sethi" 1 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "sethi")) + "i1|i0") + +(define_insn_reservation "fr500_i1_setlo" 1 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "setlo")) + "i1|i0") + +(define_insn_reservation "fr500_i1_int" 1 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "int")) + "i1|i0") + +(define_insn_reservation "fr500_i1_mul" 3 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mul")) + "i1|i0") + +(define_insn_reservation "fr500_i1_div" 19 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "div")) + "(i1|i0),(idiv1*18|idiv2*18)") + +(define_insn_reservation "fr500_i2" 4 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "gload,fload")) + "(i1|i0) + (fr500_load0|fr500_load1)") + +(define_insn_reservation "fr500_i3" 0 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "gstore,fstore")) + "i0 + fr500_store0") + +(define_insn_reservation "fr500_i4" 3 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "movgf,movfg")) + "i0") + +(define_insn_reservation "fr500_i5" 0 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "jumpl")) + "i0") + +;; +;; Branch-instructions +;; +(define_insn_reservation "fr500_branch" 0 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "jump,branch,ccr")) + "b1|b0") + +(define_insn_reservation "fr500_call" 0 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "call")) + "b0") + +;; Floating point insns. The default latencies are for non-media +;; instructions; media instructions incur an extra cycle. + +(define_bypass 4 "fr500_farith" "fr500_m1,fr500_m2,fr500_m3, + fr500_m4,fr500_m5,fr500_m6") +(define_insn_reservation "fr500_farith" 3 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "fnop,fsconv,fsadd,fsmul,fsmadd,fdconv,fdadd,fdmul,fdmadd")) + "(f1|f0)") + +(define_insn_reservation "fr500_fcmp" 4 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "fscmp,fdcmp")) + "(f1|f0)") + +(define_bypass 11 "fr500_fdiv" "fr500_m1,fr500_m2,fr500_m3, + fr500_m4,fr500_m5,fr500_m6") +(define_insn_reservation "fr500_fdiv" 10 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "fsdiv,fddiv")) + "(f1|f0),(div1*9 | div2*9)") + +(define_bypass 16 "fr500_froot" "fr500_m1,fr500_m2,fr500_m3, + fr500_m4,fr500_m5,fr500_m6") +(define_insn_reservation "fr500_froot" 15 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "sqrt_single,sqrt_double")) + "(f1|f0) + root*15") + +;; Media insns. Conflict table is as follows: +;; +;; M1 M2 M3 M4 M5 M6 +;; M1 - - - - - - +;; M2 - - - - X X +;; M3 - - - - X X +;; M4 - - - - - X +;; M5 - X X - X X +;; M6 - X X X X X +;; +;; where X indicates an invalid combination. +;; +;; Target registers are as follows: +;; +;; M1 : FPRs +;; M2 : FPRs +;; M3 : ACCs +;; M4 : ACCs +;; M5 : FPRs +;; M6 : ACCs +;; +;; The default FPR latencies are for integer instructions. +;; Floating-point instructions need one cycle more and media +;; instructions need one cycle less. +(define_automaton "fr500_media") +(define_cpu_unit "fr500_m2_0,fr500_m2_1" "fr500_media") +(define_cpu_unit "fr500_m3_0,fr500_m3_1" "fr500_media") +(define_cpu_unit "fr500_m4_0,fr500_m4_1" "fr500_media") +(define_cpu_unit "fr500_m5" "fr500_media") +(define_cpu_unit "fr500_m6" "fr500_media") + +(exclusion_set "fr500_m5,fr500_m6" "fr500_m2_0,fr500_m2_1, + fr500_m3_0,fr500_m3_1") +(exclusion_set "fr500_m6" "fr500_m4_0,fr500_m4_1,fr500_m5") + +(define_bypass 2 "fr500_m1" "fr500_m1,fr500_m2,fr500_m3, + fr500_m4,fr500_m5,fr500_m6") +(define_bypass 4 "fr500_m1" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot") +(define_insn_reservation "fr500_m1" 3 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mnop,mlogic,maveh,msath,maddh,mqaddh")) + "(f1|f0)") + +(define_bypass 2 "fr500_m2" "fr500_m1,fr500_m2,fr500_m3, + fr500_m4,fr500_m5,fr500_m6") +(define_bypass 4 "fr500_m2" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot") +(define_insn_reservation "fr500_m2" 3 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mrdacc,mpackh,munpackh,mbhconv,mrot,mshift,mexpdhw,mexpdhd,mwcut,mcut,mdunpackh,mbhconve")) + "(f1|f0) + (fr500_m2_0|fr500_m2_1)") + +(define_bypass 1 "fr500_m3" "fr500_m4") +(define_insn_reservation "fr500_m3" 2 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mclracc,mwtacc")) + "(f1|f0) + (fr500_m3_0|fr500_m3_1)") + +(define_bypass 1 "fr500_m4" "fr500_m4") +(define_insn_reservation "fr500_m4" 2 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,mcpx,mqcpx")) + "(f1|f0) + (fr500_m4_0|fr500_m4_1)") + +(define_bypass 2 "fr500_m5" "fr500_m1,fr500_m2,fr500_m3, + fr500_m4,fr500_m5,fr500_m6") +(define_bypass 4 "fr500_m5" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot") +(define_insn_reservation "fr500_m5" 3 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mdpackh")) + "(f1|f0) + fr500_m5") + +(define_bypass 1 "fr500_m6" "fr500_m4") +(define_insn_reservation "fr500_m6" 2 + (and (eq_attr "cpu" "generic,fr500,tomcat") + (eq_attr "type" "mclracca")) + "(f1|f0) + fr500_m6") + +;; :::::::::::::::::::: +;; :: +;; :: FR400 scheduler description +;; :: +;; :::::::::::::::::::: + +;; Category 2 media instructions use both media units, but can be packed +;; with non-media instructions. Use fr400_m1unit to claim the M1 unit +;; without claiming a slot. + +;; Name Class Units Latency +;; ==== ===== ===== ======= +;; int I1 I0/I1 1 +;; sethi I1 I0/I1 0 -- does not interfere with setlo +;; setlo I1 I0/I1 1 +;; mul I1 I0 3 (*) +;; div I1 I0 20 (*) +;; gload I2 I0 4 (*) +;; fload I2 I0 4 -- only 3 if read by a media insn +;; gstore I3 I0 0 -- provides no result +;; fstore I3 I0 0 -- provides no result +;; movfg I4 I0 3 (*) +;; movgf I4 I0 3 (*) +;; jumpl I5 I0 0 -- provides no result +;; +;; (*) The results of these instructions can be read one cycle earlier +;; than indicated. The penalty given is for instructions with write-after- +;; write dependencies. + +;; The FR400 can only do loads and stores in I0, so we there's no danger +;; of memory unit collision in the same packet. There's only one divide +;; unit too. + +(define_automaton "fr400_integer") +(define_cpu_unit "fr400_mul" "fr400_integer") + +(define_insn_reservation "fr400_i1_int" 1 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "int")) + "i1|i0") + +(define_bypass 0 "fr400_i1_sethi" "fr400_i1_setlo") +(define_insn_reservation "fr400_i1_sethi" 1 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "sethi")) + "i1|i0") + +(define_insn_reservation "fr400_i1_setlo" 1 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "setlo")) + "i1|i0") + +;; 3 is the worst case (write-after-write hazard). +(define_insn_reservation "fr400_i1_mul" 3 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mul")) + "i0 + fr400_mul") + +(define_insn_reservation "fr450_i1_mul" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mul")) + "i0 + fr400_mul") + +(define_bypass 1 "fr400_i1_macc" "fr400_i1_macc") +(define_insn_reservation "fr400_i1_macc" 2 + (and (eq_attr "cpu" "fr405,fr450") + (eq_attr "type" "macc")) + "(i0|i1) + fr400_mul") + +(define_insn_reservation "fr400_i1_scan" 1 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "scan")) + "i0") + +(define_insn_reservation "fr400_i1_cut" 2 + (and (eq_attr "cpu" "fr405,fr450") + (eq_attr "type" "cut")) + "i0 + fr400_mul") + +;; 20 is for a write-after-write hazard. +(define_insn_reservation "fr400_i1_div" 20 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "div")) + "i0 + idiv1*19") + +(define_insn_reservation "fr450_i1_div" 19 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "div")) + "i0 + idiv1*19") + +;; 4 is for a write-after-write hazard. +(define_insn_reservation "fr400_i2" 4 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "gload,fload")) + "i0") + +(define_insn_reservation "fr450_i2_gload" 3 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "gload")) + "i0") + +;; 4 is for a write-after-write hazard. +(define_insn_reservation "fr450_i2_fload" 4 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "fload")) + "i0") + +(define_insn_reservation "fr400_i3" 0 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "gstore,fstore")) + "i0") + +;; 3 is for a write-after-write hazard. +(define_insn_reservation "fr400_i4" 3 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "movfg,movgf")) + "i0") + +(define_insn_reservation "fr450_i4_movfg" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "movfg")) + "i0") + +;; 3 is for a write-after-write hazard. +(define_insn_reservation "fr450_i4_movgf" 3 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "movgf")) + "i0") + +(define_insn_reservation "fr400_i5" 0 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "jumpl")) + "i0") + +;; The bypass between FPR loads and media instructions, described above. + +(define_bypass 3 + "fr400_i2" + "fr400_m1_1,fr400_m1_2,\ + fr400_m2_1,fr400_m2_2,\ + fr400_m3_1,fr400_m3_2,\ + fr400_m4_1,fr400_m4_2,\ + fr400_m5") + +;; The branch instructions all use the B unit and produce no result. + +(define_insn_reservation "fr400_b" 0 + (and (eq_attr "cpu" "fr400,fr405,fr450") + (eq_attr "type" "jump,branch,ccr,call")) + "b0") + +;; FP->FP moves are marked as "fsconv" instructions in the define_insns +;; below, but are implemented on the FR400 using "mlogic" instructions. +;; It's easier to class "fsconv" as a "m1:1" instruction than provide +;; separate define_insns for the FR400. + +;; M1 instructions store their results in FPRs. Any instruction can read +;; the result in the following cycle, so no penalty occurs. + +(define_automaton "fr400_media") +(define_cpu_unit "fr400_m1a,fr400_m1b,fr400_m2a" "fr400_media") +(exclusion_set "fr400_m1a,fr400_m1b" "fr400_m2a") + +(define_reservation "fr400_m1" "(f1|f0) + (fr400_m1a|fr400_m1b)") +(define_reservation "fr400_m2" "f0 + fr400_m2a") + +(define_insn_reservation "fr400_m1_1" 1 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "fsconv,mnop,mlogic,maveh,msath,maddh,mabsh,mset")) + "fr400_m1") + +(define_insn_reservation "fr400_m1_2" 1 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mqaddh,mqsath,mqlimh,mqshift")) + "fr400_m2") + +;; M2 instructions store their results in accumulators, which are read +;; by M2 or M4 media commands. M2 instructions can read the results in +;; the following cycle, but M4 instructions must wait a cycle more. + +(define_bypass 1 + "fr400_m2_1,fr400_m2_2" + "fr400_m2_1,fr400_m2_2") + +(define_insn_reservation "fr400_m2_1" 2 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mcpx,maddacc")) + "fr400_m1") + +(define_insn_reservation "fr400_m2_2" 2 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mqmulh,mqmulxh,mqmach,mqcpx,mdaddacc")) + "fr400_m2") + +;; For our purposes, there seems to be little real difference between +;; M1 and M3 instructions. Keep them separate anyway in case the distinction +;; is needed later. + +(define_insn_reservation "fr400_m3_1" 1 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mpackh,mrot,mshift,mexpdhw")) + "fr400_m1") + +(define_insn_reservation "fr400_m3_2" 1 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "munpackh,mdpackh,mbhconv,mexpdhd,mwcut,mdrot,mcpl")) + "fr400_m2") + +;; M4 instructions write to accumulators or FPRs. MOVFG and STF +;; instructions can read an FPR result in the following cycle, but +;; M-unit instructions must wait a cycle more for either kind of result. + +(define_bypass 1 "fr400_m4_1,fr400_m4_2" "fr400_i3,fr400_i4") + +(define_insn_reservation "fr400_m4_1" 2 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mrdacc,mcut,mclracc")) + "fr400_m1") + +(define_insn_reservation "fr400_m4_2" 2 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mclracca,mdcut")) + "fr400_m2") + +;; M5 instructions always incur a 1-cycle penalty. + +(define_insn_reservation "fr400_m5" 2 + (and (eq_attr "cpu" "fr400,fr405") + (eq_attr "type" "mwtacc")) + "fr400_m2") + +;; :::::::::::::::::::: +;; :: +;; :: FR450 media scheduler description +;; :: +;; :::::::::::::::::::: + +;; The FR451 media restrictions are similar to the FR400's, but not as +;; strict and not as regular. There are 6 categories with the following +;; restrictions: +;; +;; M1 +;; M-1 M-2 M-3 M-4 M-5 M-6 +;; M-1: x x x +;; M-2: x x x x x x +;; M0 M-3: x x x +;; M-4: x x x x +;; M-5: x x x +;; M-6: x x x x x x +;; +;; where "x" indicates a conflict. +;; +;; There is no difference between M-1 and M-3 as far as issue +;; restrictions are concerned, so they are combined as "m13". + +;; Units for odd-numbered categories. There can be two of these +;; in a packet. +(define_cpu_unit "fr450_m13a,fr450_m13b" "float_media") +(define_cpu_unit "fr450_m5a,fr450_m5b" "float_media") + +;; Units for even-numbered categories. There can only be one per packet. +(define_cpu_unit "fr450_m2a,fr450_m4a,fr450_m6a" "float_media") + +;; Enforce the restriction matrix above. +(exclusion_set "fr450_m2a,fr450_m4a,fr450_m6a" "fr450_m13a,fr450_m13b") +(exclusion_set "fr450_m2a,fr450_m6a" "fr450_m5a,fr450_m5b") +(exclusion_set "fr450_m4a,fr450_m6a" "fr450_m2a") + +(define_reservation "fr450_m13" "(f1|f0) + (fr450_m13a|fr450_m13b)") +(define_reservation "fr450_m2" "f0 + fr450_m2a") +(define_reservation "fr450_m4" "f0 + fr450_m4a") +(define_reservation "fr450_m5" "(f1|f0) + (fr450_m5a|fr450_m5b)") +(define_reservation "fr450_m6" "(f0|f1) + fr450_m6a") + +;; MD-1, MD-3 and MD-8 instructions, which are the same as far +;; as scheduling is concerned. The inputs and outputs are FPRs. +;; Instructions that have 32-bit inputs and outputs belong to M-1 while +;; the rest belong to M-2. +;; +;; ??? Arithmetic shifts (MD-6) have an extra cycle latency, but we don't +;; make the distinction between them and logical shifts. +(define_insn_reservation "fr450_md138_1" 1 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "fsconv,mnop,mlogic,maveh,msath,maddh,mabsh,mset, + mrot,mshift,mexpdhw,mpackh")) + "fr450_m13") + +(define_insn_reservation "fr450_md138_2" 1 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mqaddh,mqsath,mqlimh, + mdrot,mwcut,mqshift,mexpdhd, + munpackh,mdpackh,mbhconv,mcpl")) + "fr450_m2") + +;; MD-2 instructions. These take FPR or ACC inputs and produce an ACC output. +;; Instructions that write to double ACCs belong to M-3 while those that write +;; to quad ACCs belong to M-4. +(define_insn_reservation "fr450_md2_3" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mmulh,mmach,mcpx,mmulxh,mmrdh,maddacc")) + "fr450_m13") + +(define_insn_reservation "fr450_md2_4" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mqmulh,mqmach,mqcpx,mqmulxh,mdaddacc")) + "fr450_m4") + +;; Another MD-2 instruction can use the result on the following cycle. +(define_bypass 1 "fr450_md2_3,fr450_md2_4" "fr450_md2_3,fr450_md2_4") + +;; MD-4 instructions that write to ACCs. +(define_insn_reservation "fr450_md4_3" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mclracc")) + "fr450_m13") + +(define_insn_reservation "fr450_md4_4" 3 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mclracca")) + "fr450_m4") + +;; MD-4 instructions that write to FPRs. +(define_insn_reservation "fr450_md4_1" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mcut")) + "fr450_m13") + +(define_insn_reservation "fr450_md4_5" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mrdacc")) + "fr450_m5") + +(define_insn_reservation "fr450_md4_6" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mdcut")) + "fr450_m6") + +;; Integer instructions can read the FPR result of an MD-4 instruction on +;; the following cycle. +(define_bypass 1 "fr450_md4_1,fr450_md4_5,fr450_md4_6" + "fr400_i3,fr450_i4_movfg") + +;; MD-5 instructions, which belong to M-3. They take FPR inputs and +;; write to ACCs. +(define_insn_reservation "fr450_md5_3" 2 + (and (eq_attr "cpu" "fr450") + (eq_attr "type" "mwtacc")) + "fr450_m13") + +;; :::::::::::::::::::: +;; :: +;; :: FR550 scheduler description +;; :: +;; :::::::::::::::::::: + +;; Prevent loads and stores from being issued in the same packet. +;; These units must go into the generic "integer" reservation because +;; of the constraints on fr550_store0 and fr550_store1. +(define_cpu_unit "fr550_load0,fr550_load1" "integer") +(define_cpu_unit "fr550_store0,fr550_store1" "integer") +(exclusion_set "fr550_load0,fr550_load1" "fr550_store0,fr550_store1") + +;; A store can only issue to I1 if one has also been issued to I0. +(presence_set "fr550_store1" "fr550_store0") + +(define_bypass 0 "fr550_sethi" "fr550_setlo") +(define_insn_reservation "fr550_sethi" 1 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "sethi")) + "i3|i2|i1|i0") + +(define_insn_reservation "fr550_setlo" 1 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "setlo")) + "i3|i2|i1|i0") + +(define_insn_reservation "fr550_int" 1 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "int")) + "i3|i2|i1|i0") + +(define_insn_reservation "fr550_mul" 2 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "mul")) + "i1|i0") + +(define_insn_reservation "fr550_div" 19 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "div")) + "(i1|i0),(idiv1*18 | idiv2*18)") + +(define_insn_reservation "fr550_load" 3 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "gload,fload")) + "(i1|i0)+(fr550_load0|fr550_load1)") + +;; We can only issue a store to I1 if one was also issued to I0. +;; This means that, as far as frv_reorder_packet is concerned, +;; the instruction has the same priority as an I0-only instruction. +(define_insn_reservation "fr550_store" 1 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "gstore,fstore")) + "(i0+fr550_store0)|(i1+fr550_store1)") + +(define_insn_reservation "fr550_transfer" 2 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "movgf,movfg")) + "i0") + +(define_insn_reservation "fr550_jumpl" 0 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "jumpl")) + "i0") + +(define_cpu_unit "fr550_ccr0,fr550_ccr1" "float_media") + +(define_insn_reservation "fr550_branch" 0 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "jump,branch")) + "b1|b0") + +(define_insn_reservation "fr550_ccr" 0 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "ccr")) + "(b1|b0) + (fr550_ccr1|fr550_ccr0)") + +(define_insn_reservation "fr550_call" 0 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "call")) + "b0") + +(define_automaton "fr550_float_media") +(define_cpu_unit "fr550_add0,fr550_add1" "fr550_float_media") + +;; There are three possible combinations of floating-point/media instructions: +;; +;; - one media and one float +;; - up to four float, no media +;; - up to four media, no float +(define_cpu_unit "fr550_f0,fr550_f1,fr550_f2,fr550_f3" "fr550_float_media") +(define_cpu_unit "fr550_m0,fr550_m1,fr550_m2,fr550_m3" "fr550_float_media") +(exclusion_set "fr550_f1,fr550_f2,fr550_f3" "fr550_m1,fr550_m2,fr550_m3") +(exclusion_set "fr550_m0" "fr550_f1,fr550_f2,fr550_f3") +;; FIXME: This next exclusion set should be defined as well, so that we do +;; not get a packet containing multiple media instructions plus a single +;; floating point instruction. At the moment we can get away with not +;; defining it because gcc does not seem to generate such packets. +;; +;; If we do enable the exclusion however the insertion of fnop insns into +;; a packet containing media instructions will stop working, because the +;; fnop insn counts as a floating point instruction. The correct solution +;; is to fix the reservation for the fnop insn so that it does not have the +;; same restrictions as ordinary floating point insns. +;;(exclusion_set "fr550_f0" "fr550_m1,fr550_m2,fr550_m3") + +(define_reservation "fr550_float" "fr550_f0|fr550_f1|fr550_f2|fr550_f3") +(define_reservation "fr550_media" "fr550_m0|fr550_m1|fr550_m2|fr550_m3") + +(define_insn_reservation "fr550_f1" 0 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "fnop")) + "(f3|f2|f1|f0) + fr550_float") + +(define_insn_reservation "fr550_f2" 3 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "fsconv,fsadd,fscmp")) + "(f3|f2|f1|f0) + (fr550_add0|fr550_add1) + fr550_float") + +(define_insn_reservation "fr550_f3_mul" 3 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "fsmul")) + "(f1|f0) + fr550_float") + +(define_insn_reservation "fr550_f3_div" 10 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "fsdiv")) + "(f1|f0) + fr550_float") + +(define_insn_reservation "fr550_f3_sqrt" 15 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "sqrt_single")) + "(f1|f0) + fr550_float") + +;; Synthetic units for enforcing media issue restrictions. Certain types +;; of insn in M2 conflict with certain types in M0: +;; +;; M2 +;; MNOP MALU MSFT MMAC MSET +;; MNOP - - x - - +;; MALU - x x - - +;; M0 MSFT - - x - x +;; MMAC - - x x - +;; MSET - - x - - +;; +;; where "x" indicates a conflict. The same restrictions apply to +;; M3 and M1. +;; +;; In addition -- and this is the awkward bit! -- instructions that +;; access ACC0-3 can only issue to M0 or M2. Those that access ACC4-7 +;; can only issue to M1 or M3. We refer to such instructions as "even" +;; and "odd" respectively. +(define_cpu_unit "fr550_malu0,fr550_malu1" "float_media") +(define_cpu_unit "fr550_malu2,fr550_malu3" "float_media") +(define_cpu_unit "fr550_msft0,fr550_msft1" "float_media") +(define_cpu_unit "fr550_mmac0,fr550_mmac1" "float_media") +(define_cpu_unit "fr550_mmac2,fr550_mmac3" "float_media") +(define_cpu_unit "fr550_mset0,fr550_mset1" "float_media") +(define_cpu_unit "fr550_mset2,fr550_mset3" "float_media") + +(exclusion_set "fr550_malu0" "fr550_malu2") +(exclusion_set "fr550_malu1" "fr550_malu3") + +(exclusion_set "fr550_msft0" "fr550_mset2") +(exclusion_set "fr550_msft1" "fr550_mset3") + +(exclusion_set "fr550_mmac0" "fr550_mmac2") +(exclusion_set "fr550_mmac1" "fr550_mmac3") + +;; If an MSFT or MMAC instruction issues to a unit other than M0, we may +;; need to insert some nops. In the worst case, the packet will end up +;; having 4 integer instructions and 4 media instructions, leaving no +;; room for any branch instructions that the DFA might have accepted. +;; +;; This doesn't matter for JUMP_INSNs and CALL_INSNs because they are +;; always the last instructions to be passed to the DFA, and could be +;; pushed out to a separate packet once the nops have been added. +;; However, it does cause problems for ccr instructions since they +;; can occur anywhere in the unordered packet. +(exclusion_set "fr550_msft1,fr550_mmac1,fr550_mmac2,fr550_mmac3" + "fr550_ccr0,fr550_ccr1") + +(define_reservation "fr550_malu" + "(f3 + fr550_malu3) | (f2 + fr550_malu2) + | (f1 + fr550_malu1) | (f0 + fr550_malu0)") + +(define_reservation "fr550_msft_even" + "f0 + fr550_msft0") + +(define_reservation "fr550_msft_odd" + "f1 + fr550_msft1") + +(define_reservation "fr550_msft_either" + "(f1 + fr550_msft1) | (f0 + fr550_msft0)") + +(define_reservation "fr550_mmac_even" + "(f2 + fr550_mmac2) | (f0 + fr550_mmac0)") + +(define_reservation "fr550_mmac_odd" + "(f3 + fr550_mmac3) | (f1 + fr550_mmac1)") + +(define_reservation "fr550_mset" + "(f3 + fr550_mset3) | (f2 + fr550_mset2) + | (f1 + fr550_mset1) | (f0 + fr550_mset0)") + +(define_insn_reservation "fr550_mnop" 0 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "mnop")) + "fr550_media + (f3|f2|f1|f0)") + +(define_insn_reservation "fr550_malu" 2 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "mlogic,maveh,msath,mabsh,maddh,mqaddh,mqsath")) + "fr550_media + fr550_malu") + +;; These insns only operate on FPRs and so don't need to be classified +;; as even/odd. +(define_insn_reservation "fr550_msft_1_either" 2 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "mrot,mwcut,mshift,mexpdhw,mexpdhd,mpackh, + munpackh,mdpackh,mbhconv,mdrot,mcpl")) + "fr550_media + fr550_msft_either") + +;; These insns read from ACC0-3. +(define_insn_reservation "fr550_msft_1_even" 2 + (and (eq_attr "cpu" "fr550") + (and (eq_attr "type" "mcut,mrdacc,mdcut") + (eq_attr "acc_group" "even"))) + "fr550_media + fr550_msft_even") + +;; These insns read from ACC4-7. +(define_insn_reservation "fr550_msft_1_odd" 2 + (and (eq_attr "cpu" "fr550") + (and (eq_attr "type" "mcut,mrdacc,mdcut") + (eq_attr "acc_group" "odd"))) + "fr550_media + fr550_msft_odd") + +;; MCLRACC with A=1 can issue to either M0 or M1. +(define_insn_reservation "fr550_msft_2_either" 2 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "mclracca")) + "fr550_media + fr550_msft_either") + +;; These insns write to ACC0-3. +(define_insn_reservation "fr550_msft_2_even" 2 + (and (eq_attr "cpu" "fr550") + (and (eq_attr "type" "mclracc,mwtacc") + (eq_attr "acc_group" "even"))) + "fr550_media + fr550_msft_even") + +;; These insns write to ACC4-7. +(define_insn_reservation "fr550_msft_2_odd" 2 + (and (eq_attr "cpu" "fr550") + (and (eq_attr "type" "mclracc,mwtacc") + (eq_attr "acc_group" "odd"))) + "fr550_media + fr550_msft_odd") + +;; These insns read from and write to ACC0-3. +(define_insn_reservation "fr550_mmac_even" 2 + (and (eq_attr "cpu" "fr550") + (and (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach, + maddacc,mdaddacc,mcpx,mqcpx") + (eq_attr "acc_group" "even"))) + "fr550_media + fr550_mmac_even") + +;; These insns read from and write to ACC4-7. +(define_insn_reservation "fr550_mmac_odd" 2 + (and (eq_attr "cpu" "fr550") + (and (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach, + maddacc,mdaddacc,mcpx,mqcpx") + (eq_attr "acc_group" "odd"))) + "fr550_media + fr550_mmac_odd") + +(define_insn_reservation "fr550_mset" 1 + (and (eq_attr "cpu" "fr550") + (eq_attr "type" "mset")) + "fr550_media + fr550_mset") + +;; :::::::::::::::::::: +;; :: +;; :: Simple/FR300 scheduler description +;; :: +;; :::::::::::::::::::: + +;; Fr300 or simple processor. To describe it as 1 insn issue +;; processor, we use control unit. + +(define_insn_reservation "fr300_lat1" 1 + (and (eq_attr "cpu" "fr300,simple") + (eq_attr "type" "!gload,fload,movfg,movgf")) + "c + control") + +(define_insn_reservation "fr300_lat2" 2 + (and (eq_attr "cpu" "fr300,simple") + (eq_attr "type" "gload,fload,movfg,movgf")) + "c + control") + + +;; :::::::::::::::::::: +;; :: +;; :: Delay Slots +;; :: +;; :::::::::::::::::::: + +;; The insn attribute mechanism can be used to specify the requirements for +;; delay slots, if any, on a target machine. An instruction is said to require +;; a "delay slot" if some instructions that are physically after the +;; instruction are executed as if they were located before it. Classic +;; examples are branch and call instructions, which often execute the following +;; instruction before the branch or call is performed. + +;; On some machines, conditional branch instructions can optionally "annul" +;; instructions in the delay slot. This means that the instruction will not be +;; executed for certain branch outcomes. Both instructions that annul if the +;; branch is true and instructions that annul if the branch is false are +;; supported. + +;; Delay slot scheduling differs from instruction scheduling in that +;; determining whether an instruction needs a delay slot is dependent only +;; on the type of instruction being generated, not on data flow between the +;; instructions. See the next section for a discussion of data-dependent +;; instruction scheduling. + +;; The requirement of an insn needing one or more delay slots is indicated via +;; the `define_delay' expression. It has the following form: +;; +;; (define_delay TEST +;; [DELAY-1 ANNUL-TRUE-1 ANNUL-FALSE-1 +;; DELAY-2 ANNUL-TRUE-2 ANNUL-FALSE-2 +;; ...]) + +;; TEST is an attribute test that indicates whether this `define_delay' applies +;; to a particular insn. If so, the number of required delay slots is +;; determined by the length of the vector specified as the second argument. An +;; insn placed in delay slot N must satisfy attribute test DELAY-N. +;; ANNUL-TRUE-N is an attribute test that specifies which insns may be annulled +;; if the branch is true. Similarly, ANNUL-FALSE-N specifies which insns in +;; the delay slot may be annulled if the branch is false. If annulling is not +;; supported for that delay slot, `(nil)' should be coded. + +;; For example, in the common case where branch and call insns require a single +;; delay slot, which may contain any insn other than a branch or call, the +;; following would be placed in the `md' file: + +;; (define_delay (eq_attr "type" "branch,call") +;; [(eq_attr "type" "!branch,call") (nil) (nil)]) + +;; Multiple `define_delay' expressions may be specified. In this case, each +;; such expression specifies different delay slot requirements and there must +;; be no insn for which tests in two `define_delay' expressions are both true. + +;; For example, if we have a machine that requires one delay slot for branches +;; but two for calls, no delay slot can contain a branch or call insn, and any +;; valid insn in the delay slot for the branch can be annulled if the branch is +;; true, we might represent this as follows: + +;; (define_delay (eq_attr "type" "branch") +;; [(eq_attr "type" "!branch,call") +;; (eq_attr "type" "!branch,call") +;; (nil)]) +;; +;; (define_delay (eq_attr "type" "call") +;; [(eq_attr "type" "!branch,call") (nil) (nil) +;; (eq_attr "type" "!branch,call") (nil) (nil)]) + +;; Note - it is the backend's responsibility to fill any unfilled delay slots +;; at assembler generation time. This is usually done by adding a special print +;; operand to the delayed instruction, and then in the PRINT_OPERAND function +;; calling dbr_sequence_length() to determine how many delay slots were filled. +;; For example: +;; +;; --------------<machine>.md----------------- +;; (define_insn "call" +;; [(call (match_operand 0 "memory_operand" "m") +;; (match_operand 1 "" ""))] +;; "" +;; "call_delayed %0,%1,%2%#" +;; [(set_attr "length" "4") +;; (set_attr "type" "call")]) +;; +;; -------------<machine>.h------------------- +;; #define PRINT_OPERAND_PUNCT_VALID_P(CODE) (CODE == '#') +;; +;; ------------<machine>.c------------------ +;; void +;; machine_print_operand (file, x, code) +;; FILE * file; +;; rtx x; +;; int code; +;; { +;; switch (code) +;; { +;; case '#': +;; if (dbr_sequence_length () == 0) +;; fputs ("\n\tnop", file); +;; return; + +;; :::::::::::::::::::: +;; :: +;; :: Notes on Patterns +;; :: +;; :::::::::::::::::::: + +;; If you need to construct a sequence of assembler instructions in order +;; to implement a pattern be sure to escape any backslashes and double quotes +;; that you use, e.g.: +;; +;; (define_insn "an example" +;; [(some rtl)] +;; "" +;; "* +;; { static char buffer [100]; +;; sprintf (buffer, \"insn \\t %d\", REGNO (operands[1])); +;; return buffer; +;; }" +;; ) +;; +;; Also if there is more than one instruction, they can be separated by \\; +;; which is a space saving synonym for \\n\\t: +;; +;; (define_insn "another example" +;; [(some rtl)] +;; "" +;; "* +;; { static char buffer [100]; +;; sprintf (buffer, \"insn1 \\t %d\\;insn2 \\t %%1\", +;; REGNO (operands[1])); +;; return buffer; +;; }" +;; ) +;; + +(include "predicates.md") +(include "constraints.md") + +;; :::::::::::::::::::: +;; :: +;; :: Moves +;; :: +;; :::::::::::::::::::: + +;; Wrap moves in define_expand to prevent memory->memory moves from being +;; generated at the RTL level, which generates better code for most machines +;; which can't do mem->mem moves. + +;; If operand 0 is a `subreg' with mode M of a register whose own mode is wider +;; than M, the effect of this instruction is to store the specified value in +;; the part of the register that corresponds to mode M. The effect on the rest +;; of the register is undefined. + +;; This class of patterns is special in several ways. First of all, each of +;; these names *must* be defined, because there is no other way to copy a datum +;; from one place to another. + +;; Second, these patterns are not used solely in the RTL generation pass. Even +;; the reload pass can generate move insns to copy values from stack slots into +;; temporary registers. When it does so, one of the operands is a hard +;; register and the other is an operand that can need to be reloaded into a +;; register. + +;; Therefore, when given such a pair of operands, the pattern must +;; generate RTL which needs no reloading and needs no temporary +;; registers--no registers other than the operands. For example, if +;; you support the pattern with a `define_expand', then in such a +;; case the `define_expand' mustn't call `force_reg' or any other such +;; function which might generate new pseudo registers. + +;; This requirement exists even for subword modes on a RISC machine +;; where fetching those modes from memory normally requires several +;; insns and some temporary registers. Look in `spur.md' to see how +;; the requirement can be satisfied. + +;; During reload a memory reference with an invalid address may be passed as an +;; operand. Such an address will be replaced with a valid address later in the +;; reload pass. In this case, nothing may be done with the address except to +;; use it as it stands. If it is copied, it will not be replaced with a valid +;; address. No attempt should be made to make such an address into a valid +;; address and no routine (such as `change_address') that will do so may be +;; called. Note that `general_operand' will fail when applied to such an +;; address. +;; +;; The global variable `reload_in_progress' (which must be explicitly declared +;; if required) can be used to determine whether such special handling is +;; required. +;; +;; The variety of operands that have reloads depends on the rest of +;; the machine description, but typically on a RISC machine these can +;; only be pseudo registers that did not get hard registers, while on +;; other machines explicit memory references will get optional +;; reloads. +;; +;; If a scratch register is required to move an object to or from memory, it +;; can be allocated using `gen_reg_rtx' prior to reload. But this is +;; impossible during and after reload. If there are cases needing scratch +;; registers after reload, you must define `SECONDARY_INPUT_RELOAD_CLASS' and +;; perhaps also `SECONDARY_OUTPUT_RELOAD_CLASS' to detect them, and provide +;; patterns `reload_inM' or `reload_outM' to handle them. + +;; The constraints on a `moveM' must permit moving any hard register to any +;; other hard register provided that `HARD_REGNO_MODE_OK' permits mode M in +;; both registers and `REGISTER_MOVE_COST' applied to their classes returns a +;; value of 2. + +;; It is obligatory to support floating point `moveM' instructions +;; into and out of any registers that can hold fixed point values, +;; because unions and structures (which have modes `SImode' or +;; `DImode') can be in those registers and they may have floating +;; point members. + +;; There may also be a need to support fixed point `moveM' instructions in and +;; out of floating point registers. Unfortunately, I have forgotten why this +;; was so, and I don't know whether it is still true. If `HARD_REGNO_MODE_OK' +;; rejects fixed point values in floating point registers, then the constraints +;; of the fixed point `moveM' instructions must be designed to avoid ever +;; trying to reload into a floating point register. + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + "{ frv_emit_move (QImode, operands[0], operands[1]); DONE; }") + +(define_insn "*movqi_load" + [(set (match_operand:QI 0 "register_operand" "=d,f") + (match_operand:QI 1 "frv_load_operand" "m,m"))] + "" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "gload,fload")]) + +(define_insn "*movqi_internal" + [(set (match_operand:QI 0 "move_destination_operand" "=d,d,m,m,?f,?f,?d,?m,f,d,f") + (match_operand:QI 1 "move_source_operand" "L,d,d,O, d, f, f, f,GO,!m,!m"))] + "register_operand(operands[0], QImode) || reg_or_0_operand (operands[1], QImode)" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "int,int,gstore,gstore,movgf,fsconv,movfg,fstore,movgf,gload,fload")]) + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + "{ frv_emit_move (HImode, operands[0], operands[1]); DONE; }") + +(define_insn "*movhi_load" + [(set (match_operand:HI 0 "register_operand" "=d,f") + (match_operand:HI 1 "frv_load_operand" "m,m"))] + "" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "gload,fload")]) + +(define_insn "*movhi_internal" + [(set (match_operand:HI 0 "move_destination_operand" "=d,d,d,m,m,?f,?f,?d,?m,f,d,f") + (match_operand:HI 1 "move_source_operand" "L,n,d,d,O, d, f, f, f,GO,!m,!m"))] + "register_operand(operands[0], HImode) || reg_or_0_operand (operands[1], HImode)" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4,8,4,4,4,4,4,4,4,4,4,4") + (set_attr "type" "int,multi,int,gstore,gstore,movgf,fsconv,movfg,fstore,movgf,gload,fload")]) + +;; Split 2 word load of constants into sethi/setlo instructions +(define_split + [(set (match_operand:HI 0 "integer_register_operand" "") + (match_operand:HI 1 "int_2word_operand" ""))] + "reload_completed" + [(set (match_dup 0) + (high:HI (match_dup 1))) + (set (match_dup 0) + (lo_sum:HI (match_dup 0) + (match_dup 1)))] + "") + +(define_insn "movhi_high" + [(set (match_operand:HI 0 "integer_register_operand" "=d") + (high:HI (match_operand:HI 1 "int_2word_operand" "i")))] + "" + "sethi #hi(%1), %0" + [(set_attr "type" "sethi") + (set_attr "length" "4")]) + +(define_insn "movhi_lo_sum" + [(set (match_operand:HI 0 "integer_register_operand" "+d") + (lo_sum:HI (match_dup 0) + (match_operand:HI 1 "int_2word_operand" "i")))] + "" + "setlo #lo(%1), %0" + [(set_attr "type" "setlo") + (set_attr "length" "4")]) + +(define_expand "movsi" + [(set (match_operand:SI 0 "move_destination_operand" "") + (match_operand:SI 1 "move_source_operand" ""))] + "" + "{ frv_emit_move (SImode, operands[0], operands[1]); DONE; }") + +;; Note - it is best to only have one movsi pattern and to handle +;; all the various contingencies by the use of alternatives. This +;; allows reload the greatest amount of flexibility (since reload will +;; only choose amongst alternatives for a selected insn, it will not +;; replace the insn with another one). + +;; Unfortunately, we do have to separate out load-type moves from the rest, +;; and only allow memory source operands in the former. If we do memory and +;; constant loads in a single pattern, reload will be tempted to force +;; constants into memory when the destination is a floating-point register. +;; That may make a function use a PIC pointer when it didn't before, and we +;; cannot change PIC usage (and hence stack layout) so late in the game. +;; The resulting sequences for loading constants into FPRs are preferable +;; even when we're not generating PIC code. + +;; However, if we don't accept input from memory at all in the generic +;; movsi pattern, reloads for asm instructions that reference pseudos +;; that end up assigned to memory will fail to match, because we +;; recognize them right after they're emitted, and we don't +;; re-recognize them again after the substitution for memory. So keep +;; a memory constraint available, just make sure reload won't be +;; tempted to use it. +;; + + +(define_insn "*movsi_load" + [(set (match_operand:SI 0 "register_operand" "=d,f") + (match_operand:SI 1 "frv_load_operand" "m,m"))] + "" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "gload,fload")]) + +(define_insn "*movsi_got" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (match_operand:SI 1 "got12_operand" ""))] + "" + "addi gr0, %1, %0" + [(set_attr "type" "int") + (set_attr "length" "4")]) + +(define_insn "*movsi_high_got" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (high:SI (match_operand:SI 1 "const_unspec_operand" "")))] + "" + "sethi %1, %0" + [(set_attr "type" "sethi") + (set_attr "length" "4")]) + +(define_insn "*movsi_lo_sum_got" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (lo_sum:SI (match_operand:SI 1 "integer_register_operand" "0") + (match_operand:SI 2 "const_unspec_operand" "")))] + "" + "setlo %2, %0" + [(set_attr "type" "setlo") + (set_attr "length" "4")]) + +(define_insn "*movsi_internal" + [(set (match_operand:SI 0 "move_destination_operand" "=d,d,d,m,m,z,d,d,f,f,m,?f,?z,d,f") + (match_operand:SI 1 "move_source_operand" "L,n,d,d,O,d,z,f,d,f,f,GO,GO,!m,!m"))] + "register_operand (operands[0], SImode) || reg_or_0_operand (operands[1], SImode)" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4,8,4,4,4,4,4,4,4,4,4,4,4,4,4") + (set_attr "type" "int,multi,int,gstore,gstore,spr,spr,movfg,movgf,fsconv,fstore,movgf,spr,gload,fload")]) + +;; Split 2 word load of constants into sethi/setlo instructions +(define_insn_and_split "*movsi_2word" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (match_operand:SI 1 "int_2word_operand" "i"))] + "" + "#" + "reload_completed" + [(set (match_dup 0) + (high:SI (match_dup 1))) + (set (match_dup 0) + (lo_sum:SI (match_dup 0) + (match_dup 1)))] + "" + [(set_attr "length" "8") + (set_attr "type" "multi")]) + +(define_insn "movsi_high" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (high:SI (match_operand:SI 1 "int_2word_operand" "i")))] + "" + "sethi #hi(%1), %0" + [(set_attr "type" "sethi") + (set_attr "length" "4")]) + +(define_insn "movsi_lo_sum" + [(set (match_operand:SI 0 "integer_register_operand" "+d") + (lo_sum:SI (match_dup 0) + (match_operand:SI 1 "int_2word_operand" "i")))] + "" + "setlo #lo(%1), %0" + [(set_attr "type" "setlo") + (set_attr "length" "4")]) + +(define_expand "movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + "{ frv_emit_move (DImode, operands[0], operands[1]); DONE; }") + +(define_insn "*movdi_double" + [(set (match_operand:DI 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f") + (match_operand:DI 1 "move_source_operand" " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))] + "TARGET_DOUBLE + && (register_operand (operands[0], DImode) + || reg_or_0_operand (operands[1], DImode))" + "* return output_move_double (operands, insn);" + [(set_attr "length" "8,4,8,8,4,4,8,8,4,4,8,8,4,8,4,8,4,8,8,8,16,16,8,8") + (set_attr "type" "multi,fdconv,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")]) + +(define_insn "*movdi_nodouble" + [(set (match_operand:DI 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f") + (match_operand:DI 1 "move_source_operand" " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))] + "!TARGET_DOUBLE + && (register_operand (operands[0], DImode) + || reg_or_0_operand (operands[1], DImode))" + "* return output_move_double (operands, insn);" + [(set_attr "length" "8,8,8,8,4,4,8,8,4,4,8,8,8,8,8,8,4,8,8,8,16,16,8,8") + (set_attr "type" "multi,multi,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "dbl_memory_two_insn_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_load (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DI 0 "odd_reg_operand" "") + (match_operand:DI 1 "memory_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_load (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DI 0 "dbl_memory_two_insn_operand" "") + (match_operand:DI 1 "reg_or_0_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_store (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DI 0 "memory_operand" "") + (match_operand:DI 1 "odd_reg_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_store (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "reload_completed + && (odd_reg_operand (operands[0], DImode) + || odd_reg_operand (operands[1], DImode) + || (integer_register_operand (operands[0], DImode) + && integer_register_operand (operands[1], DImode)) + || (!TARGET_DOUBLE + && fpr_operand (operands[0], DImode) + && fpr_operand (operands[1], DImode)))" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + rtx op0 = operands[0]; + rtx op0_low = gen_lowpart (SImode, op0); + rtx op0_high = gen_highpart (SImode, op0); + rtx op1 = operands[1]; + rtx op1_low = gen_lowpart (SImode, op1); + rtx op1_high = gen_highpart (SImode, op1); + + /* We normally copy the low-numbered register first. However, if the first + register operand 0 is the same as the second register of operand 1, we + must copy in the opposite order. */ + + if (REGNO (op0_high) == REGNO (op1_low)) + { + operands[2] = op0_low; + operands[3] = op0_high; + operands[4] = op1_low; + operands[5] = op1_high; + } + else + { + operands[2] = op0_high; + operands[3] = op0_low; + operands[4] = op1_high; + operands[5] = op1_low; + } +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "const_int_operand" ""))] + "reload_completed" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + operands[2] = gen_highpart (SImode, op0); + operands[3] = gen_lowpart (SImode, op0); + if (HOST_BITS_PER_WIDE_INT <= 32) + { + operands[4] = GEN_INT ((INTVAL (op1) < 0) ? -1 : 0); + operands[5] = op1; + } + else + { + operands[4] = gen_int_mode ((INTVAL (op1) >> 16) >> 16, SImode); + operands[5] = gen_int_mode (INTVAL (op1), SImode); + } +}") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "const_double_operand" ""))] + "reload_completed" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + operands[2] = gen_highpart (SImode, op0); + operands[3] = gen_lowpart (SImode, op0); + operands[4] = GEN_INT (CONST_DOUBLE_HIGH (op1)); + operands[5] = GEN_INT (CONST_DOUBLE_LOW (op1)); +}") + +;; Floating Point Moves +;; +;; Note - Patterns for SF mode moves are compulsory, but +;; patterns for DF are optional, as GCC can synthesize them. + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + "{ frv_emit_move (SFmode, operands[0], operands[1]); DONE; }") + +(define_split + [(set (match_operand:SF 0 "integer_register_operand" "") + (match_operand:SF 1 "int_2word_operand" ""))] + "reload_completed" + [(set (match_dup 0) + (high:SF (match_dup 1))) + (set (match_dup 0) + (lo_sum:SF (match_dup 0) + (match_dup 1)))] + "") + +(define_insn "*movsf_load_has_fprs" + [(set (match_operand:SF 0 "register_operand" "=f,d") + (match_operand:SF 1 "frv_load_operand" "m,m"))] + "TARGET_HAS_FPRS" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "fload,gload")]) + +(define_insn "*movsf_internal_has_fprs" + [(set (match_operand:SF 0 "move_destination_operand" "=f,f,m,m,?f,?d,?d,m,?d") + (match_operand:SF 1 "move_source_operand" "f,OG,f,OG,d,f,d,d,F"))] + "TARGET_HAS_FPRS + && (register_operand (operands[0], SFmode) || reg_or_0_operand (operands[1], SFmode))" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4,4,4,4,4,4,4,4,8") + (set_attr "type" "fsconv,movgf,fstore,gstore,movgf,movfg,int,gstore,multi")]) + +;; If we don't support the double instructions, prefer gprs over fprs, since it +;; will all be emulated +(define_insn "*movsf_internal_no_fprs" + [(set (match_operand:SF 0 "move_destination_operand" "=d,d,m,d,d") + (match_operand:SF 1 "move_source_operand" " d,OG,dOG,m,F"))] + "!TARGET_HAS_FPRS + && (register_operand (operands[0], SFmode) || reg_or_0_operand (operands[1], SFmode))" + "* return output_move_single (operands, insn);" + [(set_attr "length" "4,4,4,4,8") + (set_attr "type" "int,int,gstore,gload,multi")]) + +(define_insn "movsf_high" + [(set (match_operand:SF 0 "integer_register_operand" "=d") + (high:SF (match_operand:SF 1 "int_2word_operand" "i")))] + "" + "sethi #hi(%1), %0" + [(set_attr "type" "sethi") + (set_attr "length" "4")]) + +(define_insn "movsf_lo_sum" + [(set (match_operand:SF 0 "integer_register_operand" "+d") + (lo_sum:SF (match_dup 0) + (match_operand:SF 1 "int_2word_operand" "i")))] + "" + "setlo #lo(%1), %0" + [(set_attr "type" "setlo") + (set_attr "length" "4")]) + +(define_expand "movdf" + [(set (match_operand:DF 0 "nonimmediate_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + "{ frv_emit_move (DFmode, operands[0], operands[1]); DONE; }") + +(define_insn "*movdf_double" + [(set (match_operand:DF 0 "move_destination_operand" "=h,?e,??f,??d,R,?R,??m,??m,h,?e,??f,??d,?h,??f,?e,??d,R,m,h,??f,e,??d,e,??d") + (match_operand:DF 1 "move_source_operand" " h,e,f,d,h,e,f,d,R,R,m,m,e,d,h,f,GO,GO,GO,GO,GO,GO,F,F"))] + "TARGET_DOUBLE + && (register_operand (operands[0], DFmode) + || reg_or_0_operand (operands[1], DFmode))" + "* return output_move_double (operands, insn);" + [(set_attr "length" "4,8,8,8,4,4,8,8,4,4,8,8,4,8,4,8,4,8,8,8,8,8,16,16") + (set_attr "type" "fdconv,multi,multi,multi,fstore,gstore,fstore,gstore,fload,gload,fload,gload,movgf,movgf,movfg,movfg,gstore,gstore,movgf,movgf,multi,multi,multi,multi")]) + +;; If we don't support the double instructions, prefer gprs over fprs, since it +;; will all be emulated +(define_insn "*movdf_nodouble" + [(set (match_operand:DF 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f") + (match_operand:DF 1 "move_source_operand" " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))] + "!TARGET_DOUBLE + && (register_operand (operands[0], DFmode) + || reg_or_0_operand (operands[1], DFmode))" + "* return output_move_double (operands, insn);" + [(set_attr "length" "8,8,8,8,4,4,8,8,4,4,8,8,8,8,8,8,4,8,8,8,16,16,8,8") + (set_attr "type" "multi,multi,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "dbl_memory_two_insn_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_load (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DF 0 "odd_reg_operand" "") + (match_operand:DF 1 "memory_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_load (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DF 0 "dbl_memory_two_insn_operand" "") + (match_operand:DF 1 "reg_or_0_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_store (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DF 0 "memory_operand" "") + (match_operand:DF 1 "odd_reg_operand" ""))] + "reload_completed" + [(const_int 0)] + "frv_split_double_store (operands[0], operands[1]);") + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "register_operand" ""))] + "reload_completed + && (odd_reg_operand (operands[0], DFmode) + || odd_reg_operand (operands[1], DFmode) + || (integer_register_operand (operands[0], DFmode) + && integer_register_operand (operands[1], DFmode)) + || (!TARGET_DOUBLE + && fpr_operand (operands[0], DFmode) + && fpr_operand (operands[1], DFmode)))" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + rtx op0 = operands[0]; + rtx op0_low = gen_lowpart (SImode, op0); + rtx op0_high = gen_highpart (SImode, op0); + rtx op1 = operands[1]; + rtx op1_low = gen_lowpart (SImode, op1); + rtx op1_high = gen_highpart (SImode, op1); + + /* We normally copy the low-numbered register first. However, if the first + register operand 0 is the same as the second register of operand 1, we + must copy in the opposite order. */ + + if (REGNO (op0_high) == REGNO (op1_low)) + { + operands[2] = op0_low; + operands[3] = op0_high; + operands[4] = op1_low; + operands[5] = op1_high; + } + else + { + operands[2] = op0_high; + operands[3] = op0_low; + operands[4] = op1_high; + operands[5] = op1_low; + } +}") + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "const_int_operand" ""))] + "reload_completed" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + operands[2] = gen_highpart (SImode, op0); + operands[3] = gen_lowpart (SImode, op0); + if (HOST_BITS_PER_WIDE_INT <= 32) + { + operands[4] = GEN_INT ((INTVAL (op1) < 0) ? -1 : 0); + operands[5] = op1; + } + else + { + operands[4] = GEN_INT (((((unsigned HOST_WIDE_INT)INTVAL (op1) >> 16) + >> 16) ^ ((unsigned HOST_WIDE_INT)1 << 31)) + - ((unsigned HOST_WIDE_INT)1 << 31)); + operands[5] = GEN_INT (trunc_int_for_mode (INTVAL (op1), SImode)); + } +}") + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "const_double_operand" ""))] + "reload_completed" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + REAL_VALUE_TYPE rv; + long l[2]; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, op1); + REAL_VALUE_TO_TARGET_DOUBLE (rv, l); + + operands[2] = gen_highpart (SImode, op0); + operands[3] = gen_lowpart (SImode, op0); + operands[4] = GEN_INT (l[0]); + operands[5] = GEN_INT (l[1]); +}") + +;; String/block move insn. +;; Argument 0 is the destination +;; Argument 1 is the source +;; Argument 2 is the length +;; Argument 3 is the alignment + +(define_expand "movmemsi" + [(parallel [(set (match_operand:BLK 0 "" "") + (match_operand:BLK 1 "" "")) + (use (match_operand:SI 2 "" "")) + (use (match_operand:SI 3 "" ""))])] + "" + " +{ + if (frv_expand_block_move (operands)) + DONE; + else + FAIL; +}") + +;; String/block set insn. +;; Argument 0 is the destination +;; Argument 1 is the length +;; Argument 2 is the byte value -- ignore any value but zero +;; Argument 3 is the alignment + +(define_expand "setmemsi" + [(parallel [(set (match_operand:BLK 0 "" "") + (match_operand 2 "" "")) + (use (match_operand:SI 1 "" "")) + (use (match_operand:SI 3 "" ""))])] + "" + " +{ + /* If value to set is not zero, use the library routine. */ + if (operands[2] != const0_rtx) + FAIL; + + if (frv_expand_block_clear (operands)) + DONE; + else + FAIL; +}") + + +;; The "membar" part of a __builtin_read* or __builtin_write* function. +;; Operand 0 is a volatile reference to the memory that the function reads +;; or writes. Operand 1 is the address being accessed, or zero if the +;; address isn't a known constant. Operand 2 describes the __builtin +;; function (either FRV_IO_READ or FRV_IO_WRITE). +(define_insn "optional_membar_<mode>" + [(set (match_operand:IMODE 0 "memory_operand" "=m") + (unspec:IMODE [(match_operand 1 "const_int_operand" "") + (match_operand 2 "const_int_operand" "")] + UNSPEC_OPTIONAL_MEMBAR))] + "" + "membar" + [(set_attr "length" "4")]) + +;; :::::::::::::::::::: +;; :: +;; :: Reload CC registers +;; :: +;; :::::::::::::::::::: + +;; Use as a define_expand so that cse/gcse/combine can't accidentally +;; create movcc insns. + +(define_expand "movcc" + [(parallel [(set (match_operand:CC 0 "move_destination_operand" "") + (match_operand:CC 1 "move_source_operand" "")) + (clobber (match_dup 2))])] + "" + " +{ + if (! reload_in_progress && ! reload_completed) + FAIL; + + operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP); +}") + +(define_insn "*internal_movcc" + [(set (match_operand:CC 0 "move_destination_operand" "=t,d,d,m,d") + (match_operand:CC 1 "move_source_operand" "d,d,m,d,t")) + (clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))] + "reload_in_progress || reload_completed" + "@ + cmpi %1, #0, %0 + mov %1, %0 + ld%I1%U1 %M1, %0 + st%I0%U0 %1, %M0 + #" + [(set_attr "length" "4,4,4,4,20") + (set_attr "type" "int,int,gload,gstore,multi")]) + +;; To move an ICC value to a GPR for a signed comparison, we create a value +;; that when compared to 0, sets the N and Z flags appropriately (we don't care +;; about the V and C flags, since these comparisons are signed). + +(define_split + [(set (match_operand:CC 0 "integer_register_operand" "") + (match_operand:CC 1 "icc_operand" "")) + (clobber (match_operand:CC_CCR 2 "icr_operand" ""))] + "reload_in_progress || reload_completed" + [(match_dup 3)] + " +{ + rtx dest = simplify_gen_subreg (SImode, operands[0], CCmode, 0); + rtx icc = operands[1]; + rtx icr = operands[2]; + + start_sequence (); + + emit_insn (gen_rtx_SET (VOIDmode, icr, + gen_rtx_LT (CC_CCRmode, icc, const0_rtx))); + + emit_insn (gen_movsi (dest, const1_rtx)); + + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (CC_CCRmode, icr, const0_rtx), + gen_rtx_SET (VOIDmode, dest, + gen_rtx_NEG (SImode, dest)))); + + emit_insn (gen_rtx_SET (VOIDmode, icr, + gen_rtx_EQ (CC_CCRmode, icc, const0_rtx))); + + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (CC_CCRmode, icr, const0_rtx), + gen_rtx_SET (VOIDmode, dest, const0_rtx))); + + operands[3] = get_insns (); + end_sequence (); +}") + +;; Reload CC_UNSmode for unsigned integer comparisons +;; Use define_expand so that cse/gcse/combine can't create movcc_uns insns + +(define_expand "movcc_uns" + [(parallel [(set (match_operand:CC_UNS 0 "move_destination_operand" "") + (match_operand:CC_UNS 1 "move_source_operand" "")) + (clobber (match_dup 2))])] + "" + " +{ + if (! reload_in_progress && ! reload_completed) + FAIL; + operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP); +}") + +(define_insn "*internal_movcc_uns" + [(set (match_operand:CC_UNS 0 "move_destination_operand" "=t,d,d,m,d") + (match_operand:CC_UNS 1 "move_source_operand" "d,d,m,d,t")) + (clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))] + "reload_in_progress || reload_completed" + "@ + cmpi %1, #1, %0 + mov %1, %0 + ld%I1%U1 %M1, %0 + st%I0%U0 %1, %M0 + #" + [(set_attr "length" "4,4,4,4,20") + (set_attr "type" "int,int,gload,gstore,multi")]) + +;; To move an ICC value to a GPR for an unsigned comparison, we create a value +;; that when compared to 1, sets the Z, V, and C flags appropriately (we don't +;; care about the N flag, since these comparisons are unsigned). + +(define_split + [(set (match_operand:CC_UNS 0 "integer_register_operand" "") + (match_operand:CC_UNS 1 "icc_operand" "")) + (clobber (match_operand:CC_CCR 2 "icr_operand" ""))] + "reload_in_progress || reload_completed" + [(match_dup 3)] + " +{ + rtx dest = simplify_gen_subreg (SImode, operands[0], CC_UNSmode, 0); + rtx icc = operands[1]; + rtx icr = operands[2]; + + start_sequence (); + + emit_insn (gen_rtx_SET (VOIDmode, icr, + gen_rtx_GTU (CC_CCRmode, icc, const0_rtx))); + + emit_insn (gen_movsi (dest, const1_rtx)); + + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (CC_CCRmode, icr, const0_rtx), + gen_addsi3 (dest, dest, dest))); + + emit_insn (gen_rtx_SET (VOIDmode, icr, + gen_rtx_LTU (CC_CCRmode, icc, const0_rtx))); + + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_NE (CC_CCRmode, icr, const0_rtx), + gen_rtx_SET (VOIDmode, dest, const0_rtx))); + + operands[3] = get_insns (); + end_sequence (); +}") + +;; Reload CC_NZmode. This is mostly the same as the CCmode and CC_UNSmode +;; handling, but it uses different sequences for moving between GPRs and ICCs. + +(define_expand "movcc_nz" + [(parallel [(set (match_operand:CC_NZ 0 "move_destination_operand" "") + (match_operand:CC_NZ 1 "move_source_operand" "")) + (clobber (match_dup 2))])] + "" + " +{ + if (!reload_in_progress && !reload_completed) + FAIL; + operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP); +}") + +(define_insn "*internal_movcc_nz" + [(set (match_operand:CC_NZ 0 "move_destination_operand" "=t,d,d,m,d") + (match_operand:CC_NZ 1 "move_source_operand" "d,d,m,d,t")) + (clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))] + "reload_in_progress || reload_completed" + "@ + cmpi %1, #0, %0 + mov %1, %0 + ld%I1%U1 %M1, %0 + st%I0%U0 %1, %M0 + #" + [(set_attr "length" "4,4,4,4,20") + (set_attr "type" "int,int,gload,gstore,multi")]) + +;; Set the destination to a value that, when compared with zero, will +;; restore the value of the Z and N flags. The values of the other +;; flags don't matter. The sequence is: +;; +;; setlos op0,#-1 +;; ckp op1,op2 +;; csub gr0,op0,op0,op2 +;; ckeq op1,op2 +;; cmov gr0,op0,op2 +(define_split + [(set (match_operand:CC_NZ 0 "integer_register_operand" "") + (match_operand:CC_NZ 1 "icc_operand" "")) + (clobber (match_operand:CC_CCR 2 "icr_operand" ""))] + "reload_in_progress || reload_completed" + [(set (match_dup 3) + (const_int -1)) + (set (match_dup 2) + (ge:CC_CCR (match_dup 1) + (const_int 0))) + (cond_exec (ne:CC_CCR (match_dup 2) + (const_int 0)) + (set (match_dup 3) + (neg:SI (match_dup 3)))) + (set (match_dup 2) + (eq:CC_CCR (match_dup 1) + (const_int 0))) + (cond_exec (ne:CC_CCR (match_dup 2) + (const_int 0)) + (set (match_dup 3) (const_int 0)))] + "operands[3] = simplify_gen_subreg (SImode, operands[0], CC_NZmode, 0);") + +;; Reload CC_FPmode for floating point comparisons +;; We use a define_expand here so that cse/gcse/combine can't accidentally +;; create movcc insns. If this was a named define_insn, we would not be able +;; to make it conditional on reload. + +(define_expand "movcc_fp" + [(set (match_operand:CC_FP 0 "movcc_fp_destination_operand" "") + (match_operand:CC_FP 1 "move_source_operand" ""))] + "TARGET_HAS_FPRS" + " +{ + if (! reload_in_progress && ! reload_completed) + FAIL; +}") + +(define_insn "*movcc_fp_internal" + [(set (match_operand:CC_FP 0 "movcc_fp_destination_operand" "=d,d,d,m") + (match_operand:CC_FP 1 "move_source_operand" "u,d,m,d"))] + "TARGET_HAS_FPRS && (reload_in_progress || reload_completed)" + "@ + # + mov %1, %0 + ld%I1%U1 %M1, %0 + st%I0%U0 %1, %M0" + [(set_attr "length" "12,4,4,4") + (set_attr "type" "multi,int,gload,gstore")]) + + +(define_expand "reload_incc_fp" + [(match_operand:CC_FP 0 "fcc_operand" "=u") + (match_operand:CC_FP 1 "gpr_or_memory_operand_with_scratch" "m") + (match_operand:TI 2 "integer_register_operand" "=&d")] + "TARGET_HAS_FPRS" + " +{ + rtx cc_op2 = simplify_gen_subreg (CC_FPmode, operands[2], TImode, 0); + rtx int_op2 = simplify_gen_subreg (SImode, operands[2], TImode, 0); + rtx temp1 = simplify_gen_subreg (SImode, operands[2], TImode, 4); + rtx temp2 = simplify_gen_subreg (SImode, operands[2], TImode, 8); + int shift = CC_SHIFT_RIGHT (REGNO (operands[0])); + HOST_WIDE_INT mask; + + if (!gpr_or_memory_operand (operands[1], CC_FPmode)) + { + rtx addr; + rtx temp3 = simplify_gen_subreg (SImode, operands[2], TImode, 12); + + gcc_assert (GET_CODE (operands[1]) == MEM); + + addr = XEXP (operands[1], 0); + + gcc_assert (GET_CODE (addr) == PLUS); + + emit_move_insn (temp3, XEXP (addr, 1)); + + operands[1] = replace_equiv_address (operands[1], + gen_rtx_PLUS (GET_MODE (addr), + XEXP (addr, 0), + temp3)); + } + + emit_insn (gen_movcc_fp (cc_op2, operands[1])); + if (shift) + emit_insn (gen_ashlsi3 (int_op2, int_op2, GEN_INT (shift))); + + mask = ~ ((HOST_WIDE_INT)CC_MASK << shift); + emit_insn (gen_movsi (temp1, GEN_INT (mask))); + emit_insn (gen_update_fcc (operands[0], int_op2, temp1, temp2)); + DONE; +}") + +(define_expand "reload_outcc_fp" + [(set (match_operand:CC_FP 2 "integer_register_operand" "=&d") + (match_operand:CC_FP 1 "fcc_operand" "u")) + (set (match_operand:CC_FP 0 "memory_operand" "=m") + (match_dup 2))] + "TARGET_HAS_FPRS" + "") + +;; Convert a FCC value to gpr +(define_insn "read_fcc" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec:SI [(match_operand:CC_FP 1 "fcc_operand" "u")] + UNSPEC_CC_TO_GPR))] + "TARGET_HAS_FPRS" + "movsg ccr, %0" + [(set_attr "type" "spr") + (set_attr "length" "4")]) + +(define_split + [(set (match_operand:CC_FP 0 "integer_register_operand" "") + (match_operand:CC_FP 1 "fcc_operand" ""))] + "reload_completed && TARGET_HAS_FPRS" + [(match_dup 2)] + " +{ + rtx int_op0 = simplify_gen_subreg (SImode, operands[0], CC_FPmode, 0); + int shift = CC_SHIFT_RIGHT (REGNO (operands[1])); + + start_sequence (); + + emit_insn (gen_read_fcc (int_op0, operands[1])); + if (shift) + emit_insn (gen_lshrsi3 (int_op0, int_op0, GEN_INT (shift))); + + emit_insn (gen_andsi3 (int_op0, int_op0, GEN_INT (CC_MASK))); + + operands[2] = get_insns (); + end_sequence (); +}") + +;; Move a gpr value to FCC. +;; Operand0 = FCC +;; Operand1 = reloaded value shifted appropriately +;; Operand2 = mask to eliminate current register +;; Operand3 = temporary to load/store ccr +(define_insn "update_fcc" + [(set (match_operand:CC_FP 0 "fcc_operand" "=u") + (unspec:CC_FP [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_GPR_TO_CC)) + (clobber (match_operand:SI 3 "integer_register_operand" "=&d"))] + "TARGET_HAS_FPRS" + "movsg ccr, %3\;and %2, %3, %3\;or %1, %3, %3\;movgs %3, ccr" + [(set_attr "type" "multi") + (set_attr "length" "16")]) + +;; Reload CC_CCRmode for conditional execution registers +(define_insn "movcc_ccr" + [(set (match_operand:CC_CCR 0 "move_destination_operand" "=d,d,d,m,v,?w,C,d") + (match_operand:CC_CCR 1 "move_source_operand" "C,d,m,d,n,n,C,L"))] + "" + "@ + # + mov %1, %0 + ld%I1%U1 %M1, %0 + st%I0%U0 %1, %M0 + # + # + orcr %1, %1, %0 + setlos #%1, %0" + [(set_attr "length" "8,4,4,4,8,12,4,4") + (set_attr "type" "multi,int,gload,gstore,multi,multi,ccr,int")]) + +(define_expand "reload_incc_ccr" + [(match_operand:CC_CCR 0 "cr_operand" "=C") + (match_operand:CC_CCR 1 "memory_operand" "m") + (match_operand:CC_CCR 2 "integer_register_operand" "=&d")] + "" + " +{ + rtx icc = gen_rtx_REG (CCmode, ICC_TEMP); + rtx int_op2 = simplify_gen_subreg (SImode, operands[2], CC_CCRmode, 0); + rtx icr = (ICR_P (REGNO (operands[0])) + ? operands[0] : gen_rtx_REG (CC_CCRmode, ICR_TEMP)); + + emit_insn (gen_movcc_ccr (operands[2], operands[1])); + emit_insn (gen_cmpsi_cc (icc, int_op2, const0_rtx)); + emit_insn (gen_movcc_ccr (icr, gen_rtx_NE (CC_CCRmode, icc, const0_rtx))); + + if (! ICR_P (REGNO (operands[0]))) + emit_insn (gen_movcc_ccr (operands[0], icr)); + + DONE; +}") + +(define_expand "reload_outcc_ccr" + [(set (match_operand:CC_CCR 2 "integer_register_operand" "=&d") + (match_operand:CC_CCR 1 "cr_operand" "C")) + (set (match_operand:CC_CCR 0 "memory_operand" "=m") + (match_dup 2))] + "" + "") + +(define_split + [(set (match_operand:CC_CCR 0 "integer_register_operand" "") + (match_operand:CC_CCR 1 "cr_operand" ""))] + "reload_completed" + [(match_dup 2)] + " +{ + rtx int_op0 = simplify_gen_subreg (SImode, operands[0], CC_CCRmode, 0); + + start_sequence (); + emit_move_insn (operands[0], const1_rtx); + emit_insn (gen_rtx_COND_EXEC (VOIDmode, + gen_rtx_EQ (CC_CCRmode, + operands[1], + const0_rtx), + gen_rtx_SET (VOIDmode, int_op0, + const0_rtx))); + + operands[2] = get_insns (); + end_sequence (); +}") + +(define_split + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (match_operand:CC_CCR 1 "const_int_operand" ""))] + "reload_completed" + [(match_dup 2)] + " +{ + rtx icc = gen_rtx_REG (CCmode, ICC_TEMP); + rtx r0 = gen_rtx_REG (SImode, GPR_FIRST); + rtx icr = (ICR_P (REGNO (operands[0])) + ? operands[0] : gen_rtx_REG (CC_CCRmode, ICR_TEMP)); + + start_sequence (); + + emit_insn (gen_cmpsi_cc (icc, r0, const0_rtx)); + + emit_insn (gen_movcc_ccr (icr, + gen_rtx_fmt_ee (((INTVAL (operands[1]) == 0) + ? EQ : NE), CC_CCRmode, + r0, const0_rtx))); + + if (! ICR_P (REGNO (operands[0]))) + emit_insn (gen_movcc_ccr (operands[0], icr)); + + operands[2] = get_insns (); + end_sequence (); +}") + + +;; :::::::::::::::::::: +;; :: +;; :: Conversions +;; :: +;; :::::::::::::::::::: + +;; Signed conversions from a smaller integer to a larger integer +;; +;; These operations are optional. If they are not +;; present GCC will synthesize them for itself +;; Even though frv does not provide these instructions, we define them +;; to allow load + sign extend to be collapsed together +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "integer_register_operand" "=d,d") + (sign_extend:HI (match_operand:QI 1 "gpr_or_memory_operand" "d,m")))] + "" + "@ + # + ldsb%I1%U1 %M1,%0" + [(set_attr "length" "8,4") + (set_attr "type" "multi,gload")]) + +(define_split + [(set (match_operand:HI 0 "integer_register_operand" "") + (sign_extend:HI (match_operand:QI 1 "integer_register_operand" "")))] + "reload_completed" + [(match_dup 2) + (match_dup 3)] + " +{ + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx shift = GEN_INT (24); + + operands[2] = gen_ashlsi3 (op0, op1, shift); + operands[3] = gen_ashrsi3 (op0, op0, shift); +}") + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (sign_extend:SI (match_operand:QI 1 "gpr_or_memory_operand" "d,m")))] + "" + "@ + # + ldsb%I1%U1 %M1,%0" + [(set_attr "length" "8,4") + (set_attr "type" "multi,gload")]) + +(define_split + [(set (match_operand:SI 0 "integer_register_operand" "") + (sign_extend:SI (match_operand:QI 1 "integer_register_operand" "")))] + "reload_completed" + [(match_dup 2) + (match_dup 3)] + " +{ + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx shift = GEN_INT (24); + + operands[2] = gen_ashlsi3 (op0, op1, shift); + operands[3] = gen_ashrsi3 (op0, op0, shift); +}") + +;;(define_insn "extendqidi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (sign_extend:DI (match_operand:QI 1 "general_operand" "g")))] +;; "" +;; "extendqihi2 %0,%1" +;; [(set_attr "length" "4")]) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (sign_extend:SI (match_operand:HI 1 "gpr_or_memory_operand" "d,m")))] + "" + "@ + # + ldsh%I1%U1 %M1,%0" + [(set_attr "length" "8,4") + (set_attr "type" "multi,gload")]) + +(define_split + [(set (match_operand:SI 0 "integer_register_operand" "") + (sign_extend:SI (match_operand:HI 1 "integer_register_operand" "")))] + "reload_completed" + [(match_dup 2) + (match_dup 3)] + " +{ + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx shift = GEN_INT (16); + + operands[2] = gen_ashlsi3 (op0, op1, shift); + operands[3] = gen_ashrsi3 (op0, op0, shift); +}") + +;;(define_insn "extendhidi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (sign_extend:DI (match_operand:HI 1 "general_operand" "g")))] +;; "" +;; "extendhihi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "extendsidi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (sign_extend:DI (match_operand:SI 1 "general_operand" "g")))] +;; "" +;; "extendsidi2 %0,%1" +;; [(set_attr "length" "4")]) + +;; Unsigned conversions from a smaller integer to a larger integer +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d") + (zero_extend:HI + (match_operand:QI 1 "gpr_or_memory_operand" "d,L,m")))] + "" + "@ + andi %1,#0xff,%0 + setlos %1,%0 + ldub%I1%U1 %M1,%0" + [(set_attr "length" "4") + (set_attr "type" "int,int,gload")]) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d") + (zero_extend:SI + (match_operand:QI 1 "gpr_or_memory_operand" "d,L,m")))] + "" + "@ + andi %1,#0xff,%0 + setlos %1,%0 + ldub%I1%U1 %M1,%0" + [(set_attr "length" "4") + (set_attr "type" "int,int,gload")]) + +;;(define_insn "zero_extendqidi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (zero_extend:DI (match_operand:QI 1 "general_operand" "g")))] +;; "" +;; "zero_extendqihi2 %0,%1" +;; [(set_attr "length" "4")]) + +;; Do not set the type for the sethi to "sethi", since the scheduler will think +;; the sethi takes 0 cycles as part of allowing sethi/setlo to be in the same +;; VLIW instruction. +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (zero_extend:SI (match_operand:HI 1 "gpr_or_memory_operand" "0,m")))] + "" + "@ + sethi #hi(#0),%0 + lduh%I1%U1 %M1,%0" + [(set_attr "length" "4") + (set_attr "type" "int,gload")]) + +;;(define_insn "zero_extendhidi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (zero_extend:DI (match_operand:HI 1 "general_operand" "g")))] +;; "" +;; "zero_extendhihi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "zero_extendsidi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (zero_extend:DI (match_operand:SI 1 "general_operand" "g")))] +;; "" +;; "zero_extendsidi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;;; Convert between floating point types of different sizes. +;; +;;(define_insn "extendsfdf2" +;; [(set (match_operand:DF 0 "register_operand" "=r") +;; (float_extend:DF (match_operand:SF 1 "register_operand" "r")))] +;; "" +;; "extendsfdf2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "truncdfsf2" +;; [(set (match_operand:SF 0 "register_operand" "=r") +;; (float_truncate:SF (match_operand:DF 1 "register_operand" "r")))] +;; "" +;; "truncdfsf2 %0,%1" +;; [(set_attr "length" "4")]) + +;;;; Convert between signed integer types and floating point. +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (float:SF (match_operand:SI 1 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fitos %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fsconv")]) + +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "fpr_operand" "=h") + (float:DF (match_operand:SI 1 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fitod %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fdconv")]) + +;;(define_insn "floatdisf2" +;; [(set (match_operand:SF 0 "register_operand" "=r") +;; (float:SF (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "floatdisf2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "floatdidf2" +;; [(set (match_operand:DF 0 "register_operand" "=r") +;; (float:DF (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "floatdidf2 %0,%1" +;; [(set_attr "length" "4")]) + +(define_insn "fix_truncsfsi2" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (fix:SI (match_operand:SF 1 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fstoi %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fsconv")]) + +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (fix:SI (match_operand:DF 1 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fdtoi %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fdconv")]) + +;;(define_insn "fix_truncsfdi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (fix:DI (match_operand:SF 1 "register_operand" "r")))] +;; "" +;; "fix_truncsfdi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "fix_truncdfdi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (fix:DI (match_operand:DF 1 "register_operand" "r")))] +;; "" +;; "fix_truncdfdi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;;; Convert between unsigned integer types and floating point. +;; +;;(define_insn "floatunssisf2" +;; [(set (match_operand:SF 0 "register_operand" "=r") +;; (unsigned_float:SF (match_operand:SI 1 "register_operand" "r")))] +;; "" +;; "floatunssisf2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "floatunssidf2" +;; [(set (match_operand:DF 0 "register_operand" "=r") +;; (unsigned_float:DF (match_operand:SI 1 "register_operand" "r")))] +;; "" +;; "floatunssidf2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "floatunsdisf2" +;; [(set (match_operand:SF 0 "register_operand" "=r") +;; (unsigned_float:SF (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "floatunsdisf2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "floatunsdidf2" +;; [(set (match_operand:DF 0 "register_operand" "=r") +;; (unsigned_float:DF (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "floatunsdidf2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "fixuns_truncsfsi2" +;; [(set (match_operand:SI 0 "register_operand" "=r") +;; (unsigned_fix:SI (match_operand:SF 1 "register_operand" "r")))] +;; "" +;; "fixuns_truncsfsi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "fixuns_truncdfsi2" +;; [(set (match_operand:SI 0 "register_operand" "=r") +;; (unsigned_fix:SI (match_operand:DF 1 "register_operand" "r")))] +;; "" +;; "fixuns_truncdfsi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "fixuns_truncsfdi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (unsigned_fix:DI (match_operand:SF 1 "register_operand" "r")))] +;; "" +;; "fixuns_truncsfdi2 %0,%1" +;; [(set_attr "length" "4")]) +;; +;;(define_insn "fixuns_truncdfdi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (unsigned_fix:DI (match_operand:DF 1 "register_operand" "r")))] +;; "" +;; "fixuns_truncdfdi2 %0,%1" +;; [(set_attr "length" "4")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 32-bit Integer arithmetic +;; :: +;; :::::::::::::::::::: + +;; Addition +(define_insn "addsi3" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (plus:SI (match_operand:SI 1 "integer_register_operand" "%d") + (match_operand:SI 2 "gpr_or_int12_operand" "dNOPQ")))] + "" + "add%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Subtraction. No need to worry about constants, since the compiler +;; canonicalizes them into addsi3's. We prevent SUBREG's here to work around a +;; combine bug, that combines the 32x32->upper 32 bit multiply that uses a +;; SUBREG with a minus that shows up in modulus by constants. +(define_insn "subsi3" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (minus:SI (match_operand:SI 1 "gpr_no_subreg_operand" "d") + (match_operand:SI 2 "gpr_no_subreg_operand" "d")))] + "" + "sub %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Signed multiplication producing 64-bit results from 32-bit inputs +;; Note, frv doesn't have a 32x32->32 bit multiply, but the compiler +;; will do the 32x32->64 bit multiply and use the bottom word. +(define_expand "mulsidi3" + [(set (match_operand:DI 0 "integer_register_operand" "") + (mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "")) + (sign_extend:DI (match_operand:SI 2 "gpr_or_int12_operand" ""))))] + "" + " +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + emit_insn (gen_mulsidi3_const (operands[0], operands[1], operands[2])); + DONE; + } +}") + +(define_insn "*mulsidi3_reg" + [(set (match_operand:DI 0 "even_gpr_operand" "=e") + (mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "%d")) + (sign_extend:DI (match_operand:SI 2 "integer_register_operand" "d"))))] + "" + "smul %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +(define_insn "mulsidi3_const" + [(set (match_operand:DI 0 "even_gpr_operand" "=e") + (mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "d")) + (match_operand:SI 2 "int12_operand" "NOP")))] + "" + "smuli %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +;; Unsigned multiplication producing 64-bit results from 32-bit inputs +(define_expand "umulsidi3" + [(set (match_operand:DI 0 "even_gpr_operand" "") + (mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "")) + (zero_extend:DI (match_operand:SI 2 "gpr_or_int12_operand" ""))))] + "" + " +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + emit_insn (gen_umulsidi3_const (operands[0], operands[1], operands[2])); + DONE; + } +}") + +(define_insn "*mulsidi3_reg" + [(set (match_operand:DI 0 "even_gpr_operand" "=e") + (mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "%d")) + (zero_extend:DI (match_operand:SI 2 "integer_register_operand" "d"))))] + "" + "umul %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +(define_insn "umulsidi3_const" + [(set (match_operand:DI 0 "even_gpr_operand" "=e") + (mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "d")) + (match_operand:SI 2 "int12_operand" "NOP")))] + "" + "umuli %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +;; Signed Division +(define_insn "divsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (div:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))] + "" + "sdiv%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "div")]) + +;; Unsigned Division +(define_insn "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (udiv:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))] + "" + "udiv%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "div")]) + +;; Negation +(define_insn "negsi2" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (neg:SI (match_operand:SI 1 "integer_register_operand" "d")))] + "" + "sub %.,%1,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Find first one bit +;; (define_insn "ffssi2" +;; [(set (match_operand:SI 0 "register_operand" "=r") +;; (ffs:SI (match_operand:SI 1 "register_operand" "r")))] +;; "" +;; "ffssi2 %0,%1" +;; [(set_attr "length" "4")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 64-bit Integer arithmetic +;; :: +;; :::::::::::::::::::: + +;; Addition +(define_insn_and_split "adddi3" + [(set (match_operand:DI 0 "integer_register_operand" "=&e,e") + (plus:DI (match_operand:DI 1 "integer_register_operand" "%e,0") + (match_operand:DI 2 "gpr_or_int10_operand" "eJ,eJ"))) + (clobber (match_scratch:CC 3 "=t,t"))] + "" + "#" + "reload_completed" + [(match_dup 4) + (match_dup 5)] + " +{ + rtx parts[3][2]; + int op, part; + + for (op = 0; op < 3; op++) + for (part = 0; part < 2; part++) + parts[op][part] = simplify_gen_subreg (SImode, operands[op], + DImode, part * UNITS_PER_WORD); + + operands[4] = gen_adddi3_lower (parts[0][1], parts[1][1], parts[2][1], + operands[3]); + operands[5] = gen_adddi3_upper (parts[0][0], parts[1][0], parts[2][0], + copy_rtx (operands[3])); +}" + [(set_attr "length" "8") + (set_attr "type" "multi")]) + +;; Subtraction No need to worry about constants, since the compiler +;; canonicalizes them into adddi3's. +(define_insn_and_split "subdi3" + [(set (match_operand:DI 0 "integer_register_operand" "=&e,e,e") + (minus:DI (match_operand:DI 1 "integer_register_operand" "e,0,e") + (match_operand:DI 2 "integer_register_operand" "e,e,0"))) + (clobber (match_scratch:CC 3 "=t,t,t"))] + "" + "#" + "reload_completed" + [(match_dup 4) + (match_dup 5)] + " +{ + rtx op0_high = gen_highpart (SImode, operands[0]); + rtx op1_high = gen_highpart (SImode, operands[1]); + rtx op2_high = gen_highpart (SImode, operands[2]); + rtx op0_low = gen_lowpart (SImode, operands[0]); + rtx op1_low = gen_lowpart (SImode, operands[1]); + rtx op2_low = gen_lowpart (SImode, operands[2]); + rtx op3 = operands[3]; + + operands[4] = gen_subdi3_lower (op0_low, op1_low, op2_low, op3); + operands[5] = gen_subdi3_upper (op0_high, op1_high, op2_high, op3); +}" + [(set_attr "length" "8") + (set_attr "type" "multi")]) + +;; Patterns for addsi3/subdi3 after splitting +(define_insn "adddi3_lower" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (plus:SI (match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "gpr_or_int10_operand" "dJ"))) + (set (match_operand:CC 3 "icc_operand" "=t") + (compare:CC (plus:SI (match_dup 1) + (match_dup 2)) + (const_int 0)))] + "" + "add%I2cc %1,%2,%0,%3" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "adddi3_upper" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (plus:SI (match_operand:SI 1 "integer_register_operand" "d") + (plus:SI (match_operand:SI 2 "gpr_or_int10_operand" "dJ") + (match_operand:CC 3 "icc_operand" "t"))))] + "" + "addx%I2 %1,%2,%0,%3" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "subdi3_lower" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (minus:SI (match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d"))) + (set (match_operand:CC 3 "icc_operand" "=t") + (compare:CC (plus:SI (match_dup 1) + (match_dup 2)) + (const_int 0)))] + "" + "subcc %1,%2,%0,%3" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "subdi3_upper" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (minus:SI (match_operand:SI 1 "integer_register_operand" "d") + (minus:SI (match_operand:SI 2 "integer_register_operand" "d") + (match_operand:CC 3 "icc_operand" "t"))))] + "" + "subx %1,%2,%0,%3" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn_and_split "negdi2" + [(set (match_operand:DI 0 "integer_register_operand" "=&e,e") + (neg:DI (match_operand:DI 1 "integer_register_operand" "e,0"))) + (clobber (match_scratch:CC 2 "=t,t"))] + "" + "#" + "reload_completed" + [(match_dup 3) + (match_dup 4)] + " +{ + rtx op0_high = gen_highpart (SImode, operands[0]); + rtx op1_high = gen_rtx_REG (SImode, GPR_FIRST); + rtx op2_high = gen_highpart (SImode, operands[1]); + rtx op0_low = gen_lowpart (SImode, operands[0]); + rtx op1_low = op1_high; + rtx op2_low = gen_lowpart (SImode, operands[1]); + rtx op3 = operands[2]; + + operands[3] = gen_subdi3_lower (op0_low, op1_low, op2_low, op3); + operands[4] = gen_subdi3_upper (op0_high, op1_high, op2_high, op3); +}" + [(set_attr "length" "8") + (set_attr "type" "multi")]) + +;; Multiplication (same size) +;; (define_insn "muldi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (mult:DI (match_operand:DI 1 "register_operand" "%r") +;; (match_operand:DI 2 "nonmemory_operand" "ri")))] +;; "" +;; "muldi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Signed Division +;; (define_insn "divdi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (div:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:DI 2 "nonmemory_operand" "ri")))] +;; "" +;; "divdi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Undsgned Division +;; (define_insn "udivdi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (udiv:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:DI 2 "nonmemory_operand" "ri")))] +;; "" +;; "udivdi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Negation +;; (define_insn "negdi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (neg:DI (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "negdi2 %0,%1" +;; [(set_attr "length" "4")]) + +;; Find first one bit +;; (define_insn "ffsdi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (ffs:DI (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "ffsdi2 %0,%1" +;; [(set_attr "length" "4")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 32-bit floating point arithmetic +;; :: +;; :::::::::::::::::::: + +;; Addition +(define_insn "addsf3" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (plus:SF (match_operand:SF 1 "fpr_operand" "%f") + (match_operand:SF 2 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fadds %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fsadd")]) + +;; Subtraction +(define_insn "subsf3" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (minus:SF (match_operand:SF 1 "fpr_operand" "f") + (match_operand:SF 2 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fsubs %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fsadd")]) + +;; Multiplication +(define_insn "mulsf3" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (mult:SF (match_operand:SF 1 "fpr_operand" "%f") + (match_operand:SF 2 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fmuls %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fsmul")]) + +;; Multiplication with addition/subtraction +(define_insn "fmasf4" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (fma:SF (match_operand:SF 1 "fpr_operand" "f") + (match_operand:SF 2 "fpr_operand" "f") + (match_operand:SF 3 "fpr_operand" "0")))] + "TARGET_HARD_FLOAT && TARGET_MULADD" + "fmadds %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fsmadd")]) + +(define_insn "fmssf4" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (fma:SF (match_operand:SF 1 "fpr_operand" "f") + (match_operand:SF 2 "fpr_operand" "f") + (neg:SF (match_operand:SF 3 "fpr_operand" "0"))))] + "TARGET_HARD_FLOAT && TARGET_MULADD" + "fmsubs %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fsmadd")]) + +;; Division +(define_insn "divsf3" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (div:SF (match_operand:SF 1 "fpr_operand" "f") + (match_operand:SF 2 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fdivs %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fsdiv")]) + +;; Negation +(define_insn "negsf2" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (neg:SF (match_operand:SF 1 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fnegs %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fsconv")]) + +;; Absolute value +(define_insn "abssf2" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (abs:SF (match_operand:SF 1 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fabss %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fsconv")]) + +;; Square root +(define_insn "sqrtsf2" + [(set (match_operand:SF 0 "fpr_operand" "=f") + (sqrt:SF (match_operand:SF 1 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fsqrts %1,%0" + [(set_attr "length" "4") + (set_attr "type" "sqrt_single")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 64-bit floating point arithmetic +;; :: +;; :::::::::::::::::::: + +;; Addition +(define_insn "adddf3" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (plus:DF (match_operand:DF 1 "fpr_operand" "%h") + (match_operand:DF 2 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "faddd %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fdadd")]) + +;; Subtraction +(define_insn "subdf3" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (minus:DF (match_operand:DF 1 "fpr_operand" "h") + (match_operand:DF 2 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fsubd %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fdadd")]) + +;; Multiplication +(define_insn "muldf3" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (mult:DF (match_operand:DF 1 "fpr_operand" "%h") + (match_operand:DF 2 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fmuld %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fdmul")]) + +;; Multiplication with addition/subtraction +(define_insn "*muladddf4" + [(set (match_operand:DF 0 "fpr_operand" "=f") + (plus:DF (mult:DF (match_operand:DF 1 "fpr_operand" "%f") + (match_operand:DF 2 "fpr_operand" "f")) + (match_operand:DF 3 "fpr_operand" "0")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE && TARGET_MULADD" + "fmaddd %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fdmadd")]) + +(define_insn "*mulsubdf4" + [(set (match_operand:DF 0 "fpr_operand" "=f") + (minus:DF (mult:DF (match_operand:DF 1 "fpr_operand" "%f") + (match_operand:DF 2 "fpr_operand" "f")) + (match_operand:DF 3 "fpr_operand" "0")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE && TARGET_MULADD" + "fmsubd %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fdmadd")]) + +;; Division +(define_insn "divdf3" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (div:DF (match_operand:DF 1 "fpr_operand" "h") + (match_operand:DF 2 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fdivd %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fddiv")]) + +;; Negation +(define_insn "negdf2" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (neg:DF (match_operand:DF 1 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fnegd %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fdconv")]) + +;; Absolute value +(define_insn "absdf2" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (abs:DF (match_operand:DF 1 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fabsd %1,%0" + [(set_attr "length" "4") + (set_attr "type" "fdconv")]) + +;; Square root +(define_insn "sqrtdf2" + [(set (match_operand:DF 0 "even_fpr_operand" "=h") + (sqrt:DF (match_operand:DF 1 "fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fsqrtd %1,%0" + [(set_attr "length" "4") + (set_attr "type" "sqrt_double")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 32-bit Integer Shifts and Rotates +;; :: +;; :::::::::::::::::::: + +;; Arithmetic Shift Left +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (ashift:SI (match_operand:SI 1 "integer_register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))] + "" + "sll%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Arithmetic Shift Right +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (ashiftrt:SI (match_operand:SI 1 "integer_register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))] + "" + "sra%I2 %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Logical Shift Right +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (lshiftrt:SI (match_operand:SI 1 "integer_register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))] + "" + "srl%I2 %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Rotate Left +;; (define_insn "rotlsi3" +;; [(set (match_operand:SI 0 "register_operand" "=r") +;; (rotate:SI (match_operand:SI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "rotlsi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Rotate Right +;; (define_insn "rotrsi3" +;; [(set (match_operand:SI 0 "register_operand" "=r") +;; (rotatert:SI (match_operand:SI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "rotrsi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 64-bit Integer Shifts and Rotates +;; :: +;; :::::::::::::::::::: + +;; Arithmetic Shift Left +;; (define_insn "ashldi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (ashift:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "ashldi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Arithmetic Shift Right +;; (define_insn "ashrdi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (ashiftrt:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "ashrdi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Logical Shift Right +;; (define_insn "lshrdi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (lshiftrt:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "lshrdi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Rotate Left +;; (define_insn "rotldi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (rotate:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "rotldi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Rotate Right +;; (define_insn "rotrdi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (rotatert:DI (match_operand:DI 1 "register_operand" "r") +;; (match_operand:SI 2 "nonmemory_operand" "ri")))] +;; "" +;; "rotrdi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + + +;; :::::::::::::::::::: +;; :: +;; :: 32-Bit Integer Logical operations +;; :: +;; :::::::::::::::::::: + +;; Logical AND, 32-bit integers +(define_insn "andsi3_media" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f") + (and:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f") + (match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))] + "TARGET_MEDIA" + "@ + and%I2 %1, %2, %0 + mand %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int,mlogic")]) + +(define_insn "andsi3_nomedia" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (and:SI (match_operand:SI 1 "integer_register_operand" "%d") + (match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))] + "!TARGET_MEDIA" + "and%I2 %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_expand "andsi3" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "") + (and:SI (match_operand:SI 1 "gpr_or_fpr_operand" "") + (match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))] + "" + "") + +;; Inclusive OR, 32-bit integers +(define_insn "iorsi3_media" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f") + (ior:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f") + (match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))] + "TARGET_MEDIA" + "@ + or%I2 %1, %2, %0 + mor %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int,mlogic")]) + +(define_insn "iorsi3_nomedia" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (ior:SI (match_operand:SI 1 "integer_register_operand" "%d") + (match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))] + "!TARGET_MEDIA" + "or%I2 %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_expand "iorsi3" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "") + (ior:SI (match_operand:SI 1 "gpr_or_fpr_operand" "") + (match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))] + "" + "") + +;; Exclusive OR, 32-bit integers +(define_insn "xorsi3_media" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f") + (xor:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f") + (match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))] + "TARGET_MEDIA" + "@ + xor%I2 %1, %2, %0 + mxor %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int,mlogic")]) + +(define_insn "xorsi3_nomedia" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (xor:SI (match_operand:SI 1 "integer_register_operand" "%d") + (match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))] + "!TARGET_MEDIA" + "xor%I2 %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_expand "xorsi3" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "") + (xor:SI (match_operand:SI 1 "gpr_or_fpr_operand" "") + (match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))] + "" + "") + +;; One's complement, 32-bit integers +(define_insn "one_cmplsi2_media" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f") + (not:SI (match_operand:SI 1 "gpr_or_fpr_operand" "d,f")))] + "TARGET_MEDIA" + "@ + not %1, %0 + mnot %1, %0" + [(set_attr "length" "4") + (set_attr "type" "int,mlogic")]) + +(define_insn "one_cmplsi2_nomedia" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (not:SI (match_operand:SI 1 "integer_register_operand" "d")))] + "!TARGET_MEDIA" + "not %1,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_expand "one_cmplsi2" + [(set (match_operand:SI 0 "gpr_or_fpr_operand" "") + (not:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")))] + "" + "") + + +;; :::::::::::::::::::: +;; :: +;; :: 64-Bit Integer Logical operations +;; :: +;; :::::::::::::::::::: + +;; Logical AND, 64-bit integers +;; (define_insn "anddi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (and:DI (match_operand:DI 1 "register_operand" "%r") +;; (match_operand:DI 2 "nonmemory_operand" "ri")))] +;; "" +;; "anddi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Inclusive OR, 64-bit integers +;; (define_insn "iordi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (ior:DI (match_operand:DI 1 "register_operand" "%r") +;; (match_operand:DI 2 "nonmemory_operand" "ri")))] +;; "" +;; "iordi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; Exclusive OR, 64-bit integers +;; (define_insn "xordi3" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (xor:DI (match_operand:DI 1 "register_operand" "%r") +;; (match_operand:DI 2 "nonmemory_operand" "ri")))] +;; "" +;; "xordi3 %0,%1,%2" +;; [(set_attr "length" "4")]) + +;; One's complement, 64-bit integers +;; (define_insn "one_cmpldi2" +;; [(set (match_operand:DI 0 "register_operand" "=r") +;; (not:DI (match_operand:DI 1 "register_operand" "r")))] +;; "" +;; "notdi3 %0,%1" +;; [(set_attr "length" "4")]) + + +;; :::::::::::::::::::: +;; :: +;; :: Combination of integer operation with comparison +;; :: +;; :::::::::::::::::::: + +(define_insn "*combo_intop_compare1" + [(set (match_operand:CC_NZ 0 "icc_operand" "=t") + (compare:CC_NZ + (match_operator:SI 1 "intop_compare_operator" + [(match_operand:SI 2 "integer_register_operand" "d") + (match_operand:SI 3 "gpr_or_int10_operand" "dJ")]) + (const_int 0)))] + "" + "%O1%I3cc %2, %3, %., %0" + [(set_attr "type" "int") + (set_attr "length" "4")]) + +(define_insn "*combo_intop_compare2" + [(set (match_operand:CC_NZ 0 "icc_operand" "=t") + (compare:CC_NZ + (match_operator:SI 1 "intop_compare_operator" + [(match_operand:SI 2 "integer_register_operand" "d") + (match_operand:SI 3 "gpr_or_int10_operand" "dJ")]) + (const_int 0))) + (set (match_operand:SI 4 "integer_register_operand" "=d") + (match_operator:SI 5 "intop_compare_operator" + [(match_dup 2) + (match_dup 3)]))] + "GET_CODE (operands[1]) == GET_CODE (operands[5])" + "%O1%I3cc %2, %3, %4, %0" + [(set_attr "type" "int") + (set_attr "length" "4")]) + +;; :::::::::::::::::::: +;; :: +;; :: Comparisons +;; :: +;; :::::::::::::::::::: + +;; The comparisons are generated by the branch and/or scc operations + +(define_insn "cmpsi_cc" + [(set (match_operand:CC 0 "icc_operand" "=t,t") + (compare:CC (match_operand:SI 1 "integer_register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int10_operand" "d,J")))] + "" + "cmp%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "*cmpsi_cc_uns" + [(set (match_operand:CC_UNS 0 "icc_operand" "=t,t") + (compare:CC_UNS (match_operand:SI 1 "integer_register_operand" "d,d") + (match_operand:SI 2 "gpr_or_int10_operand" "d,J")))] + "" + "cmp%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; The only requirement for a CC_NZmode GPR or memory value is that +;; comparing it against zero must set the Z and N flags appropriately. +;; The source operand is therefore a valid CC_NZmode value. +(define_insn "*cmpsi_cc_nz" + [(set (match_operand:CC_NZ 0 "nonimmediate_operand" "=t,d,m") + (compare:CC_NZ (match_operand:SI 1 "integer_register_operand" "d,d,d") + (const_int 0)))] + "" + "@ + cmpi %1, #0, %0 + mov %1, %0 + st%I0%U0 %1, %M0" + [(set_attr "length" "4,4,4") + (set_attr "type" "int,int,gstore")]) + +(define_insn "*cmpsf_cc_fp" + [(set (match_operand:CC_FP 0 "fcc_operand" "=u") + (compare:CC_FP (match_operand:SF 1 "fpr_operand" "f") + (match_operand:SF 2 "fpr_operand" "f")))] + "TARGET_HARD_FLOAT" + "fcmps %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fscmp")]) + +(define_insn "*cmpdf_cc_fp" + [(set (match_operand:CC_FP 0 "fcc_operand" "=u") + (compare:CC_FP (match_operand:DF 1 "even_fpr_operand" "h") + (match_operand:DF 2 "even_fpr_operand" "h")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + "fcmpd %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "fdcmp")]) + + +;; :::::::::::::::::::: +;; :: +;; :: Branches +;; :: +;; :::::::::::::::::::: + +;; Define_expands called by the machine independent part of the compiler +;; to allocate a new comparison register. + +(define_expand "cbranchdf4" + [(use (match_operator 0 "ordered_comparison_operator" + [(match_operand:DF 1 "fpr_operand" "") + (match_operand:DF 2 "fpr_operand" "")])) + (use (match_operand 3 ""))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + { if (frv_emit_cond_branch (operands)) DONE; gcc_unreachable (); }) + +(define_expand "cbranchsf4" + [(use (match_operator 0 "ordered_comparison_operator" + [(match_operand:SF 1 "fpr_operand" "") + (match_operand:SF 2 "fpr_operand" "")])) + (use (match_operand 3 ""))] + "TARGET_HARD_FLOAT" + { if (frv_emit_cond_branch (operands)) DONE; gcc_unreachable (); }) + +(define_expand "cbranchsi4" + [(use (match_operator 0 "ordered_comparison_operator" + [(match_operand:SI 1 "integer_register_operand" "") + (match_operand:SI 2 "gpr_or_int10_operand" "")])) + (use (match_operand 3 ""))] + "" + { if (frv_emit_cond_branch (operands)) DONE; gcc_unreachable (); }) + +;; Actual branches. We must allow for the (label_ref) and the (pc) to be +;; swapped. If they are swapped, it reverses the sense of the branch. +;; +;; Note - unlike the define expands above, these patterns can be amalgamated +;; into one pattern for branch-if-true and one for branch-if-false. This does +;; require an operand operator to select the correct branch mnemonic. +;; +;; If a fixed condition code register is being used, (as opposed to, say, +;; using cc0), then the expands could look like this: +;; +;; (define_insn "*branch_true" +;; [(set (pc) +;; (if_then_else (match_operator:CC 0 "comparison_operator" +;; [(reg:CC <number_of_CC_register>) +;; (const_int 0)]) +;; (label_ref (match_operand 1 "" "")) +;; (pc)))] +;; "" +;; "b%B0 %1" +;; [(set_attr "length" "4")] +;; ) +;; +;; In the above example the %B is a directive to frv_print_operand() +;; to decode and print the correct branch mnemonic. + +(define_insn "*branch_int_true" + [(set (pc) + (if_then_else (match_operator 0 "integer_relational_operator" + [(match_operand 1 "icc_operand" "t") + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + if (get_attr_length (insn) == 4) + return \"b%c0 %1,%#,%l2\"; + else + return \"b%C0 %1,%#,1f\;call %l2\\n1:\"; +}" + [(set (attr "length") + (if_then_else + (and (ge (minus (match_dup 2) (pc)) (const_int -32768)) + (le (minus (match_dup 2) (pc)) (const_int 32764))) + (const_int 4) + (const_int 8))) + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "4") + (const_string "no") + (const_string "yes"))) + (set (attr "type") + (if_then_else + (eq_attr "length" "4") + (const_string "branch") + (const_string "multi")))]) + +(define_insn "*branch_int_false" + [(set (pc) + (if_then_else (match_operator 0 "integer_relational_operator" + [(match_operand 1 "icc_operand" "t") + (const_int 0)]) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + if (get_attr_length (insn) == 4) + return \"b%C0 %1,%#,%l2\"; + else + return \"b%c0 %1,%#,1f\;call %l2\\n1:\"; +}" + [(set (attr "length") + (if_then_else + (and (ge (minus (match_dup 2) (pc)) (const_int -32768)) + (le (minus (match_dup 2) (pc)) (const_int 32764))) + (const_int 4) + (const_int 8))) + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "4") + (const_string "no") + (const_string "yes"))) + (set (attr "type") + (if_then_else + (eq_attr "length" "4") + (const_string "branch") + (const_string "multi")))]) + +(define_insn "*branch_fp_true" + [(set (pc) + (if_then_else (match_operator:CC_FP 0 "float_relational_operator" + [(match_operand 1 "fcc_operand" "u") + (const_int 0)]) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "* +{ + if (get_attr_length (insn) == 4) + return \"fb%f0 %1,%#,%l2\"; + else + return \"fb%F0 %1,%#,1f\;call %l2\\n1:\"; +}" + [(set (attr "length") + (if_then_else + (and (ge (minus (match_dup 2) (pc)) (const_int -32768)) + (le (minus (match_dup 2) (pc)) (const_int 32764))) + (const_int 4) + (const_int 8))) + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "4") + (const_string "no") + (const_string "yes"))) + (set (attr "type") + (if_then_else + (eq_attr "length" "4") + (const_string "branch") + (const_string "multi")))]) + +(define_insn "*branch_fp_false" + [(set (pc) + (if_then_else (match_operator:CC_FP 0 "float_relational_operator" + [(match_operand 1 "fcc_operand" "u") + (const_int 0)]) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "* +{ + if (get_attr_length (insn) == 4) + return \"fb%F0 %1,%#,%l2\"; + else + return \"fb%f0 %1,%#,1f\;call %l2\\n1:\"; +}" + [(set (attr "length") + (if_then_else + (and (ge (minus (match_dup 2) (pc)) (const_int -32768)) + (le (minus (match_dup 2) (pc)) (const_int 32764))) + (const_int 4) + (const_int 8))) + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "4") + (const_string "no") + (const_string "yes"))) + (set (attr "type") + (if_then_else + (eq_attr "length" "4") + (const_string "branch") + (const_string "multi")))]) + + +;; :::::::::::::::::::: +;; :: +;; :: Set flag operations +;; :: +;; :::::::::::::::::::: + +;; Define_expands called by the machine independent part of the compiler +;; to allocate a new comparison register + +(define_expand "cstoredf4" + [(use (match_operator:SI 1 "ordered_comparison_operator" + [(match_operand:DF 2 "fpr_operand") + (match_operand:DF 3 "fpr_operand")])) + (clobber (match_operand:SI 0 "register_operand"))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE" + { if (frv_emit_scc (operands)) DONE; else FAIL; }) + +(define_expand "cstoresf4" + [(use (match_operator:SI 1 "ordered_comparison_operator" + [(match_operand:SF 2 "fpr_operand") + (match_operand:SF 3 "fpr_operand")])) + (clobber (match_operand:SI 0 "register_operand"))] + "TARGET_HARD_FLOAT" + { if (frv_emit_scc (operands)) DONE; else FAIL; }) + +(define_expand "cstoresi4" + [(use (match_operator:SI 1 "ordered_comparison_operator" + [(match_operand:SI 2 "integer_register_operand") + (match_operand:SI 3 "gpr_or_int10_operand")])) + (clobber (match_operand:SI 0 "register_operand"))] + "" + { if (frv_emit_scc (operands)) DONE; else FAIL; }) + +(define_insn "*scc_int" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (match_operator:SI 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t") + (const_int 0)])) + (clobber (match_operand:CC_CCR 3 "icr_operand" "=v"))] + "" + "#" + [(set_attr "length" "12") + (set_attr "type" "multi")]) + +(define_insn "*scc_float" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (match_operator:SI 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u") + (const_int 0)])) + (clobber (match_operand:CC_CCR 3 "fcr_operand" "=w"))] + "" + "#" + [(set_attr "length" "12") + (set_attr "type" "multi")]) + +;; XXX -- add reload_completed to the splits, because register allocation +;; currently isn't ready to see cond_exec packets. +(define_split + [(set (match_operand:SI 0 "integer_register_operand" "") + (match_operator:SI 1 "relational_operator" + [(match_operand 2 "cc_operand" "") + (const_int 0)])) + (clobber (match_operand 3 "cr_operand" ""))] + "reload_completed" + [(match_dup 4)] + "operands[4] = frv_split_scc (operands[0], operands[1], operands[2], + operands[3], (HOST_WIDE_INT) 1);") + +(define_insn "*scc_neg1_int" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (neg:SI (match_operator:SI 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t") + (const_int 0)]))) + (clobber (match_operand:CC_CCR 3 "icr_operand" "=v"))] + "" + "#" + [(set_attr "length" "12") + (set_attr "type" "multi")]) + +(define_insn "*scc_neg1_float" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (neg:SI (match_operator:SI 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u") + (const_int 0)]))) + (clobber (match_operand:CC_CCR 3 "fcr_operand" "=w"))] + "" + "#" + [(set_attr "length" "12") + (set_attr "type" "multi")]) + +(define_split + [(set (match_operand:SI 0 "integer_register_operand" "") + (neg:SI (match_operator:SI 1 "relational_operator" + [(match_operand 2 "cc_operand" "") + (const_int 0)]))) + (clobber (match_operand 3 "cr_operand" ""))] + "reload_completed" + [(match_dup 4)] + "operands[4] = frv_split_scc (operands[0], operands[1], operands[2], + operands[3], (HOST_WIDE_INT) -1);") + + +;; :::::::::::::::::::: +;; :: +;; :: Conditionally executed instructions +;; :: +;; :::::::::::::::::::: + +;; Convert ICC/FCC comparison into CCR bits so we can do conditional execution +(define_insn "*ck_signed" + [(set (match_operand:CC_CCR 0 "icr_operand" "=v") + (match_operator:CC_CCR 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t") + (const_int 0)]))] + "" + "ck%c1 %2, %0" + [(set_attr "length" "4") + (set_attr "type" "ccr")]) + +(define_insn "*fck_float" + [(set (match_operand:CC_CCR 0 "fcr_operand" "=w") + (match_operator:CC_CCR 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u") + (const_int 0)]))] + "TARGET_HAS_FPRS" + "fck%c1 %2, %0" + [(set_attr "length" "4") + (set_attr "type" "ccr")]) + +;; Conditionally convert ICC/FCC comparison into CCR bits to provide && and || +;; tests in conditional execution +(define_insn "cond_exec_ck" + [(set (match_operand:CC_CCR 0 "cr_operand" "=v,w") + (if_then_else:CC_CCR (match_operator 1 "ccr_eqne_operator" + [(match_operand 2 "cr_operand" "C,C") + (const_int 0)]) + (match_operator 3 "relational_operator" + [(match_operand 4 "cc_operand" "t,u") + (const_int 0)]) + (const_int 0)))] + "" + "@ + cck%c3 %4, %0, %2, %e1 + cfck%f3 %4, %0, %2, %e1" + [(set_attr "length" "4") + (set_attr "type" "ccr")]) + +;; Conditionally set a register to either 0 or another register +(define_insn "*cond_exec_movqi" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C,C,C,C,C,C") + (const_int 0)]) + (set (match_operand:QI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d") + (match_operand:QI 3 "condexec_source_operand" "dO,U,dO,f,d,f")))] + "register_operand(operands[2], QImode) || reg_or_0_operand (operands[3], QImode)" + "* return output_condmove_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "int,gload,gstore,fsconv,movgf,movfg")]) + +(define_insn "*cond_exec_movhi" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C,C,C,C,C,C") + (const_int 0)]) + (set (match_operand:HI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d") + (match_operand:HI 3 "condexec_source_operand" "dO,U,dO,f,d,f")))] + "register_operand(operands[2], HImode) || reg_or_0_operand (operands[3], HImode)" + "* return output_condmove_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "int,gload,gstore,fsconv,movgf,movfg")]) + +(define_insn "*cond_exec_movsi" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C,C,C,C,C,C,C,C") + (const_int 0)]) + (set (match_operand:SI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d,?f,?m") + (match_operand:SI 3 "condexec_source_operand" "dO,U,dO,f,d,f,m,f")))] + "register_operand(operands[2], SImode) || reg_or_0_operand (operands[3], SImode)" + "* return output_condmove_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "int,gload,gstore,fsconv,movgf,movfg,fload,fstore")]) + + +(define_insn "*cond_exec_movsf_has_fprs" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C,C,C,C,C,C,C,C,C,C") + (const_int 0)]) + (set (match_operand:SF 2 "condexec_dest_operand" "=f,?d,?d,?f,f,f,?d,U,?U,U") + (match_operand:SF 3 "condexec_source_operand" "f,d,f,d,G,U,U,f,d,G")))] + "TARGET_HAS_FPRS" + "* return output_condmove_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "fsconv,int,movgf,movfg,movgf,fload,gload,fstore,gstore,gstore")]) + +(define_insn "*cond_exec_movsf_no_fprs" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C,C,C") + (const_int 0)]) + (set (match_operand:SF 2 "condexec_dest_operand" "=d,d,U") + (match_operand:SF 3 "condexec_source_operand" "d,U,dG")))] + "! TARGET_HAS_FPRS" + "* return output_condmove_single (operands, insn);" + [(set_attr "length" "4") + (set_attr "type" "int,gload,gstore")]) + +(define_insn "*cond_exec_si_binary1" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "integer_register_operand" "=d") + (match_operator:SI 3 "condexec_si_binary_operator" + [(match_operand:SI 4 "integer_register_operand" "d") + (match_operand:SI 5 "integer_register_operand" "d")])))] + "" + "* +{ + switch (GET_CODE (operands[3])) + { + case PLUS: return \"cadd %4, %z5, %2, %1, %e0\"; + case MINUS: return \"csub %4, %z5, %2, %1, %e0\"; + case AND: return \"cand %4, %z5, %2, %1, %e0\"; + case IOR: return \"cor %4, %z5, %2, %1, %e0\"; + case XOR: return \"cxor %4, %z5, %2, %1, %e0\"; + case ASHIFT: return \"csll %4, %z5, %2, %1, %e0\"; + case ASHIFTRT: return \"csra %4, %z5, %2, %1, %e0\"; + case LSHIFTRT: return \"csrl %4, %z5, %2, %1, %e0\"; + default: gcc_unreachable (); + } +}" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "*cond_exec_si_binary2" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (match_operator:SI 3 "condexec_si_media_operator" + [(match_operand:SI 4 "fpr_operand" "f") + (match_operand:SI 5 "fpr_operand" "f")])))] + "TARGET_MEDIA" + "* +{ + switch (GET_CODE (operands[3])) + { + case AND: return \"cmand %4, %5, %2, %1, %e0\"; + case IOR: return \"cmor %4, %5, %2, %1, %e0\"; + case XOR: return \"cmxor %4, %5, %2, %1, %e0\"; + default: gcc_unreachable (); + } +}" + [(set_attr "length" "4") + (set_attr "type" "mlogic")]) + +;; Note, flow does not (currently) know how to handle an operation that uses +;; only part of the hard registers allocated for a multiregister value, such as +;; DImode in this case if the user is only interested in the lower 32-bits. So +;; we emit a USE of the entire register after the csmul instruction so it won't +;; get confused. See frv_ifcvt_modify_insn for more details. + +(define_insn "*cond_exec_si_smul" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:DI 2 "even_gpr_operand" "=e") + (mult:DI (sign_extend:DI (match_operand:SI 3 "integer_register_operand" "%d")) + (sign_extend:DI (match_operand:SI 4 "integer_register_operand" "d")))))] + "" + "csmul %3, %4, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +(define_insn "*cond_exec_si_divide" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "integer_register_operand" "=d") + (match_operator:SI 3 "condexec_si_divide_operator" + [(match_operand:SI 4 "integer_register_operand" "d") + (match_operand:SI 5 "integer_register_operand" "d")])))] + "" + "* +{ + switch (GET_CODE (operands[3])) + { + case DIV: return \"csdiv %4, %z5, %2, %1, %e0\"; + case UDIV: return \"cudiv %4, %z5, %2, %1, %e0\"; + default: gcc_unreachable (); + } +}" + [(set_attr "length" "4") + (set_attr "type" "div")]) + +(define_insn "*cond_exec_si_unary1" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "integer_register_operand" "=d") + (match_operator:SI 3 "condexec_si_unary_operator" + [(match_operand:SI 4 "integer_register_operand" "d")])))] + "" + "* +{ + switch (GET_CODE (operands[3])) + { + case NOT: return \"cnot %4, %2, %1, %e0\"; + case NEG: return \"csub %., %4, %2, %1, %e0\"; + default: gcc_unreachable (); + } +}" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "*cond_exec_si_unary2" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (not:SI (match_operand:SI 3 "fpr_operand" "f"))))] + "TARGET_MEDIA" + "cmnot %3, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mlogic")]) + +(define_insn "*cond_exec_cmpsi_cc" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:CC 2 "icc_operand" "=t") + (compare:CC (match_operand:SI 3 "integer_register_operand" "d") + (match_operand:SI 4 "reg_or_0_operand" "dO"))))] + "reload_completed + && REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST" + "ccmp %3, %z4, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "*cond_exec_cmpsi_cc_uns" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:CC_UNS 2 "icc_operand" "=t") + (compare:CC_UNS (match_operand:SI 3 "integer_register_operand" "d") + (match_operand:SI 4 "reg_or_0_operand" "dO"))))] + "reload_completed + && REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST" + "ccmp %3, %z4, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "*cond_exec_cmpsi_cc_nz" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:CC_NZ 2 "icc_operand" "=t") + (compare:CC_NZ (match_operand:SI 3 "integer_register_operand" "d") + (const_int 0))))] + "reload_completed + && REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST" + "ccmp %3, %., %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "*cond_exec_sf_conv" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SF 2 "fpr_operand" "=f") + (match_operator:SF 3 "condexec_sf_conv_operator" + [(match_operand:SF 4 "fpr_operand" "f")])))] + "TARGET_HARD_FLOAT" + "* +{ + switch (GET_CODE (operands[3])) + { + case ABS: return \"cfabss %4, %2, %1, %e0\"; + case NEG: return \"cfnegs %4, %2, %1, %e0\"; + default: gcc_unreachable (); + } +}" + [(set_attr "length" "4") + (set_attr "type" "fsconv")]) + +(define_insn "*cond_exec_sf_add" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SF 2 "fpr_operand" "=f") + (match_operator:SF 3 "condexec_sf_add_operator" + [(match_operand:SF 4 "fpr_operand" "f") + (match_operand:SF 5 "fpr_operand" "f")])))] + "TARGET_HARD_FLOAT" + "* +{ + switch (GET_CODE (operands[3])) + { + case PLUS: return \"cfadds %4, %5, %2, %1, %e0\"; + case MINUS: return \"cfsubs %4, %5, %2, %1, %e0\"; + default: gcc_unreachable (); + } +}" + [(set_attr "length" "4") + (set_attr "type" "fsadd")]) + +(define_insn "*cond_exec_sf_mul" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SF 2 "fpr_operand" "=f") + (mult:SF (match_operand:SF 3 "fpr_operand" "f") + (match_operand:SF 4 "fpr_operand" "f"))))] + "TARGET_HARD_FLOAT" + "cfmuls %3, %4, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "fsmul")]) + +(define_insn "*cond_exec_sf_div" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SF 2 "fpr_operand" "=f") + (div:SF (match_operand:SF 3 "fpr_operand" "f") + (match_operand:SF 4 "fpr_operand" "f"))))] + "TARGET_HARD_FLOAT" + "cfdivs %3, %4, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "fsdiv")]) + +(define_insn "*cond_exec_sf_sqrt" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SF 2 "fpr_operand" "=f") + (sqrt:SF (match_operand:SF 3 "fpr_operand" "f"))))] + "TARGET_HARD_FLOAT" + "cfsqrts %3, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "fsdiv")]) + +(define_insn "*cond_exec_cmpsi_cc_fp" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:CC_FP 2 "fcc_operand" "=u") + (compare:CC_FP (match_operand:SF 3 "fpr_operand" "f") + (match_operand:SF 4 "fpr_operand" "f"))))] + "reload_completed && TARGET_HARD_FLOAT + && REGNO (operands[1]) == REGNO (operands[2]) - FCC_FIRST + FCR_FIRST" + "cfcmps %3, %4, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "fsconv")]) + + +;; :::::::::::::::::::: +;; :: +;; :: Logical operations on CR registers +;; :: +;; :::::::::::::::::::: + +;; We use UNSPEC to encode andcr/iorcr/etc. rather than the normal RTL +;; operations, since the RTL operations only have an idea of TRUE and FALSE, +;; while the CRs have TRUE, FALSE, and UNDEFINED. + +(define_expand "andcr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 0)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "orcr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 1)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "xorcr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 2)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "nandcr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 3)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "norcr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 4)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "andncr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 5)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "orncr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 6)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "nandncr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 7)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "norncr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_operand:CC_CCR 2 "cr_operand" "") + (const_int 8)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_expand "notcr" + [(set (match_operand:CC_CCR 0 "cr_operand" "") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "") + (match_dup 1) + (const_int 9)] UNSPEC_CR_LOGIC))] + "" + "") + +(define_insn "*logical_cr" + [(set (match_operand:CC_CCR 0 "cr_operand" "=C") + (unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "C") + (match_operand:CC_CCR 2 "cr_operand" "C") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_CR_LOGIC))] + "" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case 0: return \"andcr %1, %2, %0\"; + case 1: return \"orcr %1, %2, %0\"; + case 2: return \"xorcr %1, %2, %0\"; + case 3: return \"nandcr %1, %2, %0\"; + case 4: return \"norcr %1, %2, %0\"; + case 5: return \"andncr %1, %2, %0\"; + case 6: return \"orncr %1, %2, %0\"; + case 7: return \"nandncr %1, %2, %0\"; + case 8: return \"norncr %1, %2, %0\"; + case 9: return \"notcr %1, %0\"; + } + + fatal_insn (\"logical_cr\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "ccr")]) + + +;; :::::::::::::::::::: +;; :: +;; :: Conditional move instructions +;; :: +;; :::::::::::::::::::: + + +;; - conditional moves based on floating-point comparisons require +;; TARGET_HARD_FLOAT, because an FPU is required to do the comparison. + +;; - conditional moves between FPRs based on integer comparisons +;; require TARGET_HAS_FPRS. + +(define_expand "movqicc" + [(set (match_operand:QI 0 "integer_register_operand" "") + (if_then_else:QI (match_operand 1 "" "") + (match_operand:QI 2 "gpr_or_int_operand" "") + (match_operand:QI 3 "gpr_or_int_operand" "")))] + "TARGET_COND_MOVE" + " +{ + if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3])) + FAIL; + + DONE; +}") + +(define_insn "*movqicc_internal1_int" + [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d") + (if_then_else:QI (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t") + (const_int 0)]) + (match_operand:QI 3 "reg_or_0_operand" "0,dO,dO") + (match_operand:QI 4 "reg_or_0_operand" "dO,0,dO"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))] + "" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_insn "*movqicc_internal1_float" + [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d") + (if_then_else:QI (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u") + (const_int 0)]) + (match_operand:QI 3 "reg_or_0_operand" "0,dO,dO") + (match_operand:QI 4 "reg_or_0_operand" "dO,0,dO"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))] + "TARGET_HARD_FLOAT" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_insn "*movqicc_internal2_int" + [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d,d,d") + (if_then_else:QI (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t,t,t") + (const_int 0)]) + (match_operand:QI 3 "const_int_operand" "O,O,L,n,n") + (match_operand:QI 4 "const_int_operand" "L,n,O,O,n"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))] + "(INTVAL (operands[3]) == 0 + || INTVAL (operands[4]) == 0 + || (IN_RANGE (INTVAL (operands[3]), -2048, 2047) + && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))" + "#" + [(set_attr "length" "8,12,8,12,12") + (set_attr "type" "multi")]) + +(define_insn "*movqicc_internal2_float" + [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d,d,d") + (if_then_else:QI (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u") + (const_int 0)]) + (match_operand:QI 3 "const_int_operand" "O,O,L,n,n") + (match_operand:QI 4 "const_int_operand" "L,n,O,O,n"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))] + "TARGET_HARD_FLOAT + && (INTVAL (operands[3]) == 0 + || INTVAL (operands[4]) == 0 + || (IN_RANGE (INTVAL (operands[3]), -2048, 2047) + && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))" + "#" + [(set_attr "length" "8,12,8,12,12") + (set_attr "type" "multi")]) + +(define_split + [(set (match_operand:QI 0 "integer_register_operand" "") + (if_then_else:QI (match_operator 1 "relational_operator" + [(match_operand 2 "cc_operand" "") + (const_int 0)]) + (match_operand:QI 3 "gpr_or_int_operand" "") + (match_operand:QI 4 "gpr_or_int_operand" ""))) + (clobber (match_operand:CC_CCR 5 "cr_operand" ""))] + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_cond_move (operands);") + +(define_expand "movhicc" + [(set (match_operand:HI 0 "integer_register_operand" "") + (if_then_else:HI (match_operand 1 "" "") + (match_operand:HI 2 "gpr_or_int_operand" "") + (match_operand:HI 3 "gpr_or_int_operand" "")))] + "TARGET_COND_MOVE" + " +{ + if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3])) + FAIL; + + DONE; +}") + +(define_insn "*movhicc_internal1_int" + [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d") + (if_then_else:HI (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t") + (const_int 0)]) + (match_operand:HI 3 "reg_or_0_operand" "0,dO,dO") + (match_operand:HI 4 "reg_or_0_operand" "dO,0,dO"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))] + "" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_insn "*movhicc_internal1_float" + [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d") + (if_then_else:HI (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u") + (const_int 0)]) + (match_operand:HI 3 "reg_or_0_operand" "0,dO,dO") + (match_operand:HI 4 "reg_or_0_operand" "dO,0,dO"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))] + "TARGET_HARD_FLOAT" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_insn "*movhicc_internal2_int" + [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d,d,d") + (if_then_else:HI (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t,t,t") + (const_int 0)]) + (match_operand:HI 3 "const_int_operand" "O,O,L,n,n") + (match_operand:HI 4 "const_int_operand" "L,n,O,O,n"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))] + "(INTVAL (operands[3]) == 0 + || INTVAL (operands[4]) == 0 + || (IN_RANGE (INTVAL (operands[3]), -2048, 2047) + && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))" + "#" + [(set_attr "length" "8,12,8,12,12") + (set_attr "type" "multi")]) + +(define_insn "*movhicc_internal2_float" + [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d,d,d") + (if_then_else:HI (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u") + (const_int 0)]) + (match_operand:HI 3 "const_int_operand" "O,O,L,n,n") + (match_operand:HI 4 "const_int_operand" "L,n,O,O,n"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))] + "TARGET_HARD_FLOAT + && (INTVAL (operands[3]) == 0 + || INTVAL (operands[4]) == 0 + || (IN_RANGE (INTVAL (operands[3]), -2048, 2047) + && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))" + "#" + [(set_attr "length" "8,12,8,12,12") + (set_attr "type" "multi")]) + +(define_split + [(set (match_operand:HI 0 "integer_register_operand" "") + (if_then_else:HI (match_operator 1 "relational_operator" + [(match_operand 2 "cc_operand" "") + (const_int 0)]) + (match_operand:HI 3 "gpr_or_int_operand" "") + (match_operand:HI 4 "gpr_or_int_operand" ""))) + (clobber (match_operand:CC_CCR 5 "cr_operand" ""))] + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_cond_move (operands);") + +(define_expand "movsicc" + [(set (match_operand:SI 0 "integer_register_operand" "") + (if_then_else:SI (match_operand 1 "" "") + (match_operand:SI 2 "gpr_or_int_operand" "") + (match_operand:SI 3 "gpr_or_int_operand" "")))] + "TARGET_COND_MOVE" + " +{ + if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3])) + FAIL; + + DONE; +}") + +(define_insn "*movsicc_internal1_int" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d") + (if_then_else:SI (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t") + (const_int 0)]) + (match_operand:SI 3 "reg_or_0_operand" "0,dO,dO") + (match_operand:SI 4 "reg_or_0_operand" "dO,0,dO"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))] + "" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_insn "*movsicc_internal1_float" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d") + (if_then_else:SI (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u") + (const_int 0)]) + (match_operand:SI 3 "reg_or_0_operand" "0,dO,dO") + (match_operand:SI 4 "reg_or_0_operand" "dO,0,dO"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))] + "TARGET_HARD_FLOAT" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_insn "*movsicc_internal2_int" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d,d,d") + (if_then_else:SI (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t,t,t") + (const_int 0)]) + (match_operand:SI 3 "const_int_operand" "O,O,L,n,n") + (match_operand:SI 4 "const_int_operand" "L,n,O,O,n"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))] + "(INTVAL (operands[3]) == 0 + || INTVAL (operands[4]) == 0 + || (IN_RANGE (INTVAL (operands[3]), -2048, 2047) + && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))" + "#" + [(set_attr "length" "8,12,8,12,12") + (set_attr "type" "multi")]) + +(define_insn "*movsicc_internal2_float" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d,d,d") + (if_then_else:SI (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u") + (const_int 0)]) + (match_operand:SI 3 "const_int_operand" "O,O,L,n,n") + (match_operand:SI 4 "const_int_operand" "L,n,O,O,n"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))] + "TARGET_HARD_FLOAT + && (INTVAL (operands[3]) == 0 + || INTVAL (operands[4]) == 0 + || (IN_RANGE (INTVAL (operands[3]), -2048, 2047) + && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))" + "#" + [(set_attr "length" "8,12,8,12,12") + (set_attr "type" "multi")]) + +(define_split + [(set (match_operand:SI 0 "integer_register_operand" "") + (if_then_else:SI (match_operator 1 "relational_operator" + [(match_operand 2 "cc_operand" "") + (const_int 0)]) + (match_operand:SI 3 "gpr_or_int_operand" "") + (match_operand:SI 4 "gpr_or_int_operand" ""))) + (clobber (match_operand:CC_CCR 5 "cr_operand" ""))] + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_cond_move (operands);") + +(define_expand "movsfcc" + [(set (match_operand:SF 0 "register_operand" "") + (if_then_else:SF (match_operand 1 "" "") + (match_operand:SF 2 "register_operand" "") + (match_operand:SF 3 "register_operand" "")))] + "TARGET_COND_MOVE" + " +{ + if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3])) + FAIL; + + DONE; +}") + +(define_insn "*movsfcc_has_fprs_int" + [(set (match_operand:SF 0 "register_operand" "=f,f,f,?f,?f,?d") + (if_then_else:SF (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t,t,t,t") + (const_int 0)]) + (match_operand:SF 3 "register_operand" "0,f,f,f,d,fd") + (match_operand:SF 4 "register_operand" "f,0,f,d,fd,fd"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v,v"))] + "TARGET_HAS_FPRS" + "#" + [(set_attr "length" "8,8,12,12,12,12") + (set_attr "type" "multi")]) + +(define_insn "*movsfcc_hardfloat_float" + [(set (match_operand:SF 0 "register_operand" "=f,f,f,?f,?f,?d") + (if_then_else:SF (match_operator:CC_FP 1 "float_relational_operator" + [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u,u") + (const_int 0)]) + (match_operand:SF 3 "register_operand" "0,f,f,f,d,fd") + (match_operand:SF 4 "register_operand" "f,0,f,d,fd,fd"))) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w,w"))] + "TARGET_HARD_FLOAT" + "#" + [(set_attr "length" "8,8,12,12,12,12") + (set_attr "type" "multi")]) + +(define_insn "*movsfcc_no_fprs_int" + [(set (match_operand:SF 0 "integer_register_operand" "=d,d,d") + (if_then_else:SF (match_operator 1 "integer_relational_operator" + [(match_operand 2 "icc_operand" "t,t,t") + (const_int 0)]) + (match_operand:SF 3 "integer_register_operand" "0,d,d") + (match_operand:SF 4 "integer_register_operand" "d,0,d"))) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))] + "! TARGET_HAS_FPRS" + "#" + [(set_attr "length" "8,8,12") + (set_attr "type" "multi")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "") + (if_then_else:SF (match_operator 1 "relational_operator" + [(match_operand 2 "cc_operand" "") + (const_int 0)]) + (match_operand:SF 3 "register_operand" "") + (match_operand:SF 4 "register_operand" ""))) + (clobber (match_operand:CC_CCR 5 "cr_operand" ""))] + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_cond_move (operands);") + + +;; :::::::::::::::::::: +;; :: +;; :: Minimum, maximum, and integer absolute value +;; :: +;; :::::::::::::::::::: + +;; These 'instructions' are provided to give the compiler a slightly better +;; nudge at register allocation, then it would if it constructed the +;; instructions from basic building blocks (since it indicates it prefers one +;; of the operands to be the same as the destination. It also helps the +;; earlier passes of the compiler, by not breaking things into small basic +;; blocks. + +(define_expand "abssi2" + [(parallel [(set (match_operand:SI 0 "integer_register_operand" "") + (abs:SI (match_operand:SI 1 "integer_register_operand" ""))) + (clobber (match_dup 2)) + (clobber (match_dup 3))])] + "TARGET_COND_MOVE" + " +{ + operands[2] = gen_reg_rtx (CCmode); + operands[3] = gen_reg_rtx (CC_CCRmode); +}") + +(define_insn_and_split "*abssi2_internal" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d") + (abs:SI (match_operand:SI 1 "integer_register_operand" "0,d"))) + (clobber (match_operand:CC 2 "icc_operand" "=t,t")) + (clobber (match_operand:CC_CCR 3 "icr_operand" "=v,v"))] + "TARGET_COND_MOVE" + "#" + "reload_completed" + [(match_dup 4)] + "operands[4] = frv_split_abs (operands);" + [(set_attr "length" "12,16") + (set_attr "type" "multi")]) + +(define_expand "sminsi3" + [(parallel [(set (match_operand:SI 0 "integer_register_operand" "") + (smin:SI (match_operand:SI 1 "integer_register_operand" "") + (match_operand:SI 2 "gpr_or_int10_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE" + " +{ + operands[3] = gen_reg_rtx (CCmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_expand "smaxsi3" + [(parallel [(set (match_operand:SI 0 "integer_register_operand" "") + (smax:SI (match_operand:SI 1 "integer_register_operand" "") + (match_operand:SI 2 "gpr_or_int10_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE" + " +{ + operands[3] = gen_reg_rtx (CCmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_insn_and_split "*minmax_si_signed" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,&d") + (match_operator:SI 1 "minmax_operator" + [(match_operand:SI 2 "integer_register_operand" "%0,dO,d") + (match_operand:SI 3 "gpr_or_int10_operand" "dO,0,dJ")])) + (clobber (match_operand:CC 4 "icc_operand" "=t,t,t")) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))] + "TARGET_COND_MOVE" + "#" + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_minmax (operands);" + [(set_attr "length" "12,12,16") + (set_attr "type" "multi")]) + +(define_expand "uminsi3" + [(parallel [(set (match_operand:SI 0 "integer_register_operand" "") + (umin:SI (match_operand:SI 1 "integer_register_operand" "") + (match_operand:SI 2 "gpr_or_int10_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE" + " +{ + operands[3] = gen_reg_rtx (CC_UNSmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_expand "umaxsi3" + [(parallel [(set (match_operand:SI 0 "integer_register_operand" "") + (umax:SI (match_operand:SI 1 "integer_register_operand" "") + (match_operand:SI 2 "gpr_or_int10_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE" + " +{ + operands[3] = gen_reg_rtx (CC_UNSmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_insn_and_split "*minmax_si_unsigned" + [(set (match_operand:SI 0 "integer_register_operand" "=d,d,&d") + (match_operator:SI 1 "minmax_operator" + [(match_operand:SI 2 "integer_register_operand" "%0,dO,d") + (match_operand:SI 3 "gpr_or_int10_operand" "dO,0,dJ")])) + (clobber (match_operand:CC_UNS 4 "icc_operand" "=t,t,t")) + (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))] + "TARGET_COND_MOVE" + "#" + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_minmax (operands);" + [(set_attr "length" "12,12,16") + (set_attr "type" "multi")]) + +(define_expand "sminsf3" + [(parallel [(set (match_operand:SF 0 "fpr_operand" "") + (smin:SF (match_operand:SF 1 "fpr_operand" "") + (match_operand:SF 2 "fpr_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE && TARGET_HARD_FLOAT" + " +{ + operands[3] = gen_reg_rtx (CC_FPmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_expand "smaxsf3" + [(parallel [(set (match_operand:SF 0 "fpr_operand" "") + (smax:SF (match_operand:SF 1 "fpr_operand" "") + (match_operand:SF 2 "fpr_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE && TARGET_HARD_FLOAT" + " +{ + operands[3] = gen_reg_rtx (CC_FPmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_insn_and_split "*minmax_sf" + [(set (match_operand:SF 0 "fpr_operand" "=f,f,f") + (match_operator:SF 1 "minmax_operator" + [(match_operand:SF 2 "fpr_operand" "%0,f,f") + (match_operand:SF 3 "fpr_operand" "f,0,f")])) + (clobber (match_operand:CC_FP 4 "fcc_operand" "=u,u,u")) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))] + "TARGET_COND_MOVE && TARGET_HARD_FLOAT" + "#" + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_minmax (operands);" + [(set_attr "length" "12,12,16") + (set_attr "type" "multi")]) + +(define_expand "smindf3" + [(parallel [(set (match_operand:DF 0 "fpr_operand" "") + (smin:DF (match_operand:DF 1 "fpr_operand" "") + (match_operand:DF 2 "fpr_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE" + " +{ + operands[3] = gen_reg_rtx (CC_FPmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_expand "smaxdf3" + [(parallel [(set (match_operand:DF 0 "fpr_operand" "") + (smax:DF (match_operand:DF 1 "fpr_operand" "") + (match_operand:DF 2 "fpr_operand" ""))) + (clobber (match_dup 3)) + (clobber (match_dup 4))])] + "TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE" + " +{ + operands[3] = gen_reg_rtx (CC_FPmode); + operands[4] = gen_reg_rtx (CC_CCRmode); +}") + +(define_insn_and_split "*minmax_df" + [(set (match_operand:DF 0 "fpr_operand" "=f,f,f") + (match_operator:DF 1 "minmax_operator" + [(match_operand:DF 2 "fpr_operand" "%0,f,f") + (match_operand:DF 3 "fpr_operand" "f,0,f")])) + (clobber (match_operand:CC_FP 4 "fcc_operand" "=u,u,u")) + (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))] + "TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE" + "#" + "reload_completed" + [(match_dup 6)] + "operands[6] = frv_split_minmax (operands);" + [(set_attr "length" "12,12,16") + (set_attr "type" "multi")]) + + +;; :::::::::::::::::::: +;; :: +;; :: Call and branch instructions +;; :: +;; :::::::::::::::::::: + +;; Subroutine call instruction returning no value. Operand 0 is the function +;; to call; operand 1 is the number of bytes of arguments pushed (in mode +;; `SImode', except it is normally a `const_int'); operand 2 is the number of +;; registers used as operands. + +;; On most machines, operand 2 is not actually stored into the RTL pattern. It +;; is supplied for the sake of some RISC machines which need to put this +;; information into the assembler code; they can put it in the RTL instead of +;; operand 1. + +(define_expand "call" + [(use (match_operand:QI 0 "" "")) + (use (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" ""))] + "" + " +{ + rtx lr = gen_rtx_REG (Pmode, LR_REGNO); + rtx addr; + + gcc_assert (GET_CODE (operands[0]) == MEM); + + addr = XEXP (operands[0], 0); + if (! call_operand (addr, Pmode)) + addr = force_reg (Pmode, addr); + + if (! operands[2]) + operands[2] = const0_rtx; + + if (TARGET_FDPIC) + frv_expand_fdpic_call (operands, false, false); + else + emit_call_insn (gen_call_internal (addr, operands[1], operands[2], lr)); + + DONE; +}") + +(define_insn "call_internal" + [(call (mem:QI (match_operand:SI 0 "call_operand" "S,dNOP")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (match_operand:SI 3 "lr_operand" "=l,l"))] + "! TARGET_FDPIC" + "@ + call %0 + call%i0l %M0" + [(set_attr "length" "4") + (set_attr "type" "call,jumpl")]) + +;; The odd use of GR0 within the UNSPEC below prevents cseing or +;; hoisting function descriptor loads out of loops. This is almost +;; never desirable, since if we preserve the function descriptor in a +;; pair of registers, it takes two insns to move it to gr14/gr15, and +;; if it's in the stack, we just waste space with the store, since +;; we'll have to load back from memory anyway. And, in the worst +;; case, we may end up reusing a function descriptor still pointing at +;; a PLT entry, instead of to the resolved function, which means going +;; through the resolver for every call that uses the outdated value. +;; Bad! + +;; The explicit MEM inside the SPEC prevents the compiler from moving +;; the load before a branch after a NULL test, or before a store that +;; initializes a function descriptor. + +(define_insn "movdi_ldd" + [(set (match_operand:DI 0 "fdpic_fptr_operand" "=e") + (unspec:DI [(mem:DI (match_operand:SI 1 "ldd_address_operand" "p")) + (reg:SI 0)] UNSPEC_LDD))] + "" + "ldd%I1 %M1, %0" + [(set_attr "length" "4") + (set_attr "type" "gload")]) + +(define_insn "call_fdpicdi" + [(call (mem:QI (match_operand:DI 0 "fdpic_fptr_operand" "W")) + (match_operand 1 "" "")) + (clobber (match_operand:SI 2 "lr_operand" "=l"))] + "TARGET_FDPIC" + "call%i0l %M0" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +(define_insn "call_fdpicsi" + [(call (mem:QI (match_operand:SI 0 "call_operand" "S,dNOP")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand:SI 3 "fdpic_operand" "Z,Z")) + (clobber (match_operand:SI 4 "lr_operand" "=l,l"))] + "TARGET_FDPIC" + "@ + call %0 + call%i0l %M0" + [(set_attr "length" "4") + (set_attr "type" "call,jumpl")]) + +(define_expand "sibcall" + [(use (match_operand:QI 0 "" "")) + (use (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" ""))] + "" + " +{ + rtx addr; + + gcc_assert (GET_CODE (operands[0]) == MEM); + + addr = XEXP (operands[0], 0); + if (! sibcall_operand (addr, Pmode)) + addr = force_reg (Pmode, addr); + + if (! operands[2]) + operands[2] = const0_rtx; + + if (TARGET_FDPIC) + frv_expand_fdpic_call (operands, false, true); + else + emit_call_insn (gen_sibcall_internal (addr, operands[1], operands[2])); + + DONE; +}") + +;; It might seem that these sibcall patterns are missing references to +;; LR, but they're not necessary because sibcall_epilogue will make +;; sure LR is restored, and having LR here will set +;; regs_ever_used[REG_LR], forcing it to be saved on the stack, and +;; then restored in sibcalls and regular return code paths, even if +;; the function becomes a leaf function after tail-call elimination. + +;; We must not use a call-saved register here. `W' limits ourselves +;; to gr14 or gr15, but since we're almost running out of constraint +;; letters, and most other call-clobbered registers are often used for +;; argument-passing, this will do. +(define_insn "sibcall_internal" + [(call (mem:QI (match_operand:SI 0 "sibcall_operand" "WNOP")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (return)] + "! TARGET_FDPIC" + "jmp%i0l %M0" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +(define_insn "sibcall_fdpicdi" + [(call (mem:QI (match_operand:DI 0 "fdpic_fptr_operand" "W")) + (match_operand 1 "" "")) + (return)] + "TARGET_FDPIC" + "jmp%i0l %M0" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + + +;; Subroutine call instruction returning a value. Operand 0 is the hard +;; register in which the value is returned. There are three more operands, the +;; same as the three operands of the `call' instruction (but with numbers +;; increased by one). + +;; Subroutines that return `BLKmode' objects use the `call' insn. + +(define_expand "call_value" + [(use (match_operand 0 "" "")) + (use (match_operand:QI 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (use (match_operand 4 "" ""))] + "" + " +{ + rtx lr = gen_rtx_REG (Pmode, LR_REGNO); + rtx addr; + + gcc_assert (GET_CODE (operands[1]) == MEM); + + addr = XEXP (operands[1], 0); + if (! call_operand (addr, Pmode)) + addr = force_reg (Pmode, addr); + + if (! operands[3]) + operands[3] = const0_rtx; + + if (TARGET_FDPIC) + frv_expand_fdpic_call (operands, true, false); + else + emit_call_insn (gen_call_value_internal (operands[0], addr, operands[2], + operands[3], lr)); + + DONE; +}") + +(define_insn "call_value_internal" + [(set (match_operand 0 "register_operand" "=d,d") + (call (mem:QI (match_operand:SI 1 "call_operand" "S,dNOP")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (match_operand:SI 4 "lr_operand" "=l,l"))] + "! TARGET_FDPIC" + "@ + call %1 + call%i1l %M1" + [(set_attr "length" "4") + (set_attr "type" "call,jumpl")]) + +(define_insn "call_value_fdpicdi" + [(set (match_operand 0 "register_operand" "=d") + (call (mem:QI (match_operand:DI 1 "fdpic_fptr_operand" "W")) + (match_operand 2 "" ""))) + (clobber (match_operand:SI 3 "lr_operand" "=l"))] + "TARGET_FDPIC" + "call%i1l %M1" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +(define_insn "call_value_fdpicsi" + [(set (match_operand 0 "register_operand" "=d,d") + (call (mem:QI (match_operand:SI 1 "call_operand" "S,dNOP")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (use (match_operand:SI 4 "fdpic_operand" "Z,Z")) + (clobber (match_operand:SI 5 "lr_operand" "=l,l"))] + "TARGET_FDPIC" + "@ + call %1 + call%i1l %M1" + [(set_attr "length" "4") + (set_attr "type" "call,jumpl")]) + +(define_expand "sibcall_value" + [(use (match_operand 0 "" "")) + (use (match_operand:QI 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (use (match_operand 4 "" ""))] + "" + " +{ + rtx addr; + + gcc_assert (GET_CODE (operands[1]) == MEM); + + addr = XEXP (operands[1], 0); + if (! sibcall_operand (addr, Pmode)) + addr = force_reg (Pmode, addr); + + if (! operands[3]) + operands[3] = const0_rtx; + + if (TARGET_FDPIC) + frv_expand_fdpic_call (operands, true, true); + else + emit_call_insn (gen_sibcall_value_internal (operands[0], addr, operands[2], + operands[3])); + DONE; +}") + +(define_insn "sibcall_value_internal" + [(set (match_operand 0 "register_operand" "=d") + (call (mem:QI (match_operand:SI 1 "sibcall_operand" "WNOP")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (return)] + "! TARGET_FDPIC" + "jmp%i1l %M1" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +(define_insn "sibcall_value_fdpicdi" + [(set (match_operand 0 "register_operand" "=d") + (call (mem:QI (match_operand:DI 1 "fdpic_fptr_operand" "W")) + (match_operand 2 "" ""))) + (return)] + "TARGET_FDPIC" + "jmp%i1l %M1" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +;; return instruction generated instead of jmp to epilog +(define_expand "return" + [(parallel [(return) + (use (match_dup 0)) + (use (const_int 1))])] + "direct_return_p ()" + " +{ + operands[0] = gen_rtx_REG (Pmode, LR_REGNO); +}") + +;; return instruction generated by the epilogue +(define_expand "epilogue_return" + [(parallel [(return) + (use (match_operand:SI 0 "register_operand" "")) + (use (const_int 0))])] + "" + "") + +(define_insn "*return_internal" + [(return) + (use (match_operand:SI 0 "register_operand" "l,d")) + (use (match_operand:SI 1 "immediate_operand" "n,n"))] + "" + "@ + ret + jmpl @(%0,%.)" + [(set_attr "length" "4") + (set_attr "type" "jump,jumpl")]) + +(define_insn "*return_true" + [(set (pc) + (if_then_else (match_operator 0 "integer_relational_operator" + [(match_operand 1 "icc_operand" "t") + (const_int 0)]) + (return) + (pc)))] + "direct_return_p ()" + "b%c0lr %1,%#" + [(set_attr "length" "4") + (set_attr "type" "jump")]) + +(define_insn "*return_false" + [(set (pc) + (if_then_else (match_operator 0 "integer_relational_operator" + [(match_operand 1 "icc_operand" "t") + (const_int 0)]) + (pc) + (return)))] + "direct_return_p ()" + "b%C0lr %1,%#" + [(set_attr "length" "4") + (set_attr "type" "jump")]) + +;; A version of addsi3 for deallocating stack space at the end of the +;; epilogue. The addition is done in parallel with an (unspec_volatile), +;; which represents the clobbering of the deallocated space. +(define_insn "stack_adjust" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "general_operand" "dNOP"))) + (unspec_volatile [(const_int 0)] UNSPEC_STACK_ADJUST)] + "" + "add%I2 %1,%2,%0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +;; Normal unconditional jump + +;; Use the "call" instruction for long branches, but prefer to use "bra" for +;; short ones since it does not force us to save the link register. + +;; This define_insn uses the branch-shortening code to decide which +;; instruction it emits. Since the main branch-shortening interface is +;; through get_attr_length(), the two alternatives must be given different +;; lengths. Here we pretend that the far jump is 8 rather than 4 bytes +;; long, though both alternatives are really the same size. +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "* +{ + if (get_attr_length (insn) == 4) + return \"bra %l0\"; + else + return \"call %l0\"; +}" + [(set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -32768)) + (le (minus (match_dup 0) (pc)) (const_int 32764))) + (const_int 4) + (const_int 8))) + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "4") + (const_string "no") + (const_string "yes"))) + (set (attr "type") + (if_then_else + (eq_attr "length" "4") + (const_string "jump") + (const_string "call")))]) + +;; Indirect jump through a register +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "d,l"))] + "" + "@ + jmpl @(%0,%.) + bralr" + [(set_attr "length" "4") + (set_attr "type" "jumpl,branch")]) + +;; Instruction to jump to a variable address. This is a low-level capability +;; which can be used to implement a dispatch table when there is no `casesi' +;; pattern. Either the 'casesi' pattern or the 'tablejump' pattern, or both, +;; MUST be present in this file. + +;; This pattern requires two operands: the address or offset, and a label which +;; should immediately precede the jump table. If the macro +;; `CASE_VECTOR_PC_RELATIVE' is defined then the first operand is an offset +;; which counts from the address of the table; otherwise, it is an absolute +;; address to jump to. In either case, the first operand has mode `Pmode'. + +;; The `tablejump' insn is always the last insn before the jump table it uses. +;; Its assembler code normally has no need to use the second operand, but you +;; should incorporate it in the RTL pattern so that the jump optimizer will not +;; delete the table as unreachable code. + +(define_expand "tablejump" + [(parallel [(set (pc) (match_operand:SI 0 "address_operand" "p")) + (use (label_ref (match_operand 1 "" "")))])] + "!flag_pic" + "") + +(define_insn "tablejump_insn" + [(set (pc) (match_operand:SI 0 "address_operand" "p")) + (use (label_ref (match_operand 1 "" "")))] + "" + "jmp%I0l %M0" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +;; Implement switch statements when generating PIC code. Switches are +;; implemented by `tablejump' when not using -fpic. + +;; Emit code here to do the range checking and make the index zero based. +;; operand 0 is the index +;; operand 1 is the lower bound +;; operand 2 is the range of indices (highest - lowest + 1) +;; operand 3 is the label that precedes the table itself +;; operand 4 is the fall through label + +(define_expand "casesi" + [(use (match_operand:SI 0 "integer_register_operand" "")) + (use (match_operand:SI 1 "const_int_operand" "")) + (use (match_operand:SI 2 "const_int_operand" "")) + (use (match_operand 3 "" "")) + (use (match_operand 4 "" ""))] + "flag_pic" + " +{ + rtx indx; + rtx scale; + rtx low = operands[1]; + rtx range = operands[2]; + rtx table = operands[3]; + rtx treg; + rtx fail = operands[4]; + rtx mem; + rtx reg2; + rtx reg3; + + gcc_assert (GET_CODE (operands[1]) == CONST_INT); + + gcc_assert (GET_CODE (operands[2]) == CONST_INT); + + /* If we can't generate an immediate instruction, promote to register. */ + if (! IN_RANGE (INTVAL (range), -2048, 2047)) + range = force_reg (SImode, range); + + /* If low bound is 0, we don't have to subtract it. */ + if (INTVAL (operands[1]) == 0) + indx = operands[0]; + else + { + indx = gen_reg_rtx (SImode); + if (IN_RANGE (INTVAL (low), -2047, 2048)) + emit_insn (gen_addsi3 (indx, operands[0], GEN_INT (- INTVAL (low)))); + else + emit_insn (gen_subsi3 (indx, operands[0], force_reg (SImode, low))); + } + + /* Do an unsigned comparison (in the proper mode) between the index + expression and the value which represents the length of the range. + Since we just finished subtracting the lower bound of the range + from the index expression, this comparison allows us to simultaneously + check that the original index expression value is both greater than + or equal to the minimum value of the range and less than or equal to + the maximum value of the range. */ + + emit_cmp_and_jump_insns (indx, range, GTU, NULL_RTX, SImode, 1, fail); + + /* Move the table address to a register. */ + treg = gen_reg_rtx (Pmode); + emit_insn (gen_movsi (treg, gen_rtx_LABEL_REF (VOIDmode, table))); + + /* Scale index-low by wordsize. */ + scale = gen_reg_rtx (SImode); + emit_insn (gen_ashlsi3 (scale, indx, const2_rtx)); + + /* Load the address, add the start of the table back in, + and jump to it. */ + mem = gen_rtx_MEM (SImode, gen_rtx_PLUS (Pmode, scale, treg)); + reg2 = gen_reg_rtx (SImode); + reg3 = gen_reg_rtx (SImode); + emit_insn (gen_movsi (reg2, mem)); + emit_insn (gen_addsi3 (reg3, reg2, treg)); + emit_jump_insn (gen_tablejump_insn (reg3, table)); + DONE; +}") + + +;; :::::::::::::::::::: +;; :: +;; :: Prologue and Epilogue instructions +;; :: +;; :::::::::::::::::::: + +;; Called after register allocation to add any instructions needed for the +;; prologue. Using a prologue insn is favored compared to putting all of the +;; instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler +;; to intermix instructions with the saves of the caller saved registers. In +;; some cases, it might be necessary to emit a barrier instruction as the last +;; insn to prevent such scheduling. +(define_expand "prologue" + [(const_int 1)] + "" + " +{ + frv_expand_prologue (); + DONE; +}") + +;; Called after register allocation to add any instructions needed for the +;; epilogue. Using an epilogue insn is favored compared to putting all of the +;; instructions in the FUNCTION_EPILOGUE macro, since it allows the scheduler +;; to intermix instructions with the restores of the caller saved registers. +;; In some cases, it might be necessary to emit a barrier instruction as the +;; first insn to prevent such scheduling. +(define_expand "epilogue" + [(const_int 2)] + "" + " +{ + frv_expand_epilogue (true); + DONE; +}") + +;; This pattern, if defined, emits RTL for exit from a function without the final +;; branch back to the calling function. This pattern will be emitted before any +;; sibling call (aka tail call) sites. +;; +;; The sibcall_epilogue pattern must not clobber any arguments used for +;; parameter passing or any stack slots for arguments passed to the current +;; function. +(define_expand "sibcall_epilogue" + [(const_int 3)] + "" + " +{ + frv_expand_epilogue (false); + DONE; +}") + +;; Set up the pic register to hold the address of the pic table +(define_insn "pic_prologue" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec_volatile:SI [(const_int 0)] UNSPEC_PIC_PROLOGUE)) + (clobber (match_operand:SI 1 "lr_operand" "=l")) + (clobber (match_operand:SI 2 "integer_register_operand" "=d"))] + "" + "* +{ + static int frv_pic_labelno = 0; + + operands[3] = GEN_INT (frv_pic_labelno++); + return \"call %P3\\n%P3:\;movsg %1, %0\;sethi #gprelhi(%P3), %2\;setlo #gprello(%P3), %2\;sub %0,%2,%0\"; +}" + [(set_attr "length" "16") + (set_attr "type" "multi")]) + +;; :::::::::::::::::::: +;; :: +;; :: Miscellaneous instructions +;; :: +;; :::::::::::::::::::: + +;; No operation, needed in case the user uses -g but not -O. +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "fnop" + [(const_int 1)] + "" + "fnop" + [(set_attr "length" "4") + (set_attr "type" "fnop")]) + +(define_insn "mnop" + [(const_int 2)] + "" + "mnop" + [(set_attr "length" "4") + (set_attr "type" "mnop")]) + +;; Pseudo instruction that prevents the scheduler from moving code above this +;; point. Note, type unknown is used to make sure the VLIW instructions are +;; not continued past this point. +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)] + "" + "# blockage" + [(set_attr "length" "0") + (set_attr "type" "unknown")]) + +;; :::::::::::::::::::: +;; :: +;; :: Media instructions +;; :: +;; :::::::::::::::::::: + +;; Unimplemented instructions: +;; - MCMPSH, MCMPUH + +(define_constants + [(UNSPEC_MLOGIC 100) + (UNSPEC_MNOT 101) + (UNSPEC_MAVEH 102) + (UNSPEC_MSATH 103) + (UNSPEC_MADDH 104) + (UNSPEC_MQADDH 105) + (UNSPEC_MPACKH 106) + (UNSPEC_MUNPACKH 107) + (UNSPEC_MDPACKH 108) + (UNSPEC_MBTOH 109) + (UNSPEC_MHTOB 110) + (UNSPEC_MROT 111) + (UNSPEC_MSHIFT 112) + (UNSPEC_MEXPDHW 113) + (UNSPEC_MEXPDHD 114) + (UNSPEC_MWCUT 115) + (UNSPEC_MMULH 116) + (UNSPEC_MMULXH 117) + (UNSPEC_MMACH 118) + (UNSPEC_MMRDH 119) + (UNSPEC_MQMULH 120) + (UNSPEC_MQMULXH 121) + (UNSPEC_MQMACH 122) + (UNSPEC_MCPX 123) + (UNSPEC_MQCPX 124) + (UNSPEC_MCUT 125) + (UNSPEC_MRDACC 126) + (UNSPEC_MRDACCG 127) + (UNSPEC_MWTACC 128) + (UNSPEC_MWTACCG 129) + (UNSPEC_MTRAP 130) + (UNSPEC_MCLRACC 131) + (UNSPEC_MCLRACCA 132) + (UNSPEC_MCOP1 133) + (UNSPEC_MCOP2 134) + (UNSPEC_MDUNPACKH 135) + (UNSPEC_MDUNPACKH_INTERNAL 136) + (UNSPEC_MBTOHE 137) + (UNSPEC_MBTOHE_INTERNAL 138) + (UNSPEC_MBTOHE 137) + (UNSPEC_MBTOHE_INTERNAL 138) + (UNSPEC_MQMACH2 139) + (UNSPEC_MADDACC 140) + (UNSPEC_MDADDACC 141) + (UNSPEC_MABSHS 142) + (UNSPEC_MDROTLI 143) + (UNSPEC_MCPLHI 144) + (UNSPEC_MCPLI 145) + (UNSPEC_MDCUTSSI 146) + (UNSPEC_MQSATHS 147) + (UNSPEC_MHSETLOS 148) + (UNSPEC_MHSETLOH 149) + (UNSPEC_MHSETHIS 150) + (UNSPEC_MHSETHIH 151) + (UNSPEC_MHDSETS 152) + (UNSPEC_MHDSETH 153) + (UNSPEC_MQLCLRHS 154) + (UNSPEC_MQLMTHS 155) + (UNSPEC_MQSLLHI 156) + (UNSPEC_MQSRAHI 157) + (UNSPEC_MASACCS 158) + (UNSPEC_MDASACCS 159) +]) + +;; Logic operations: type "mlogic" + +(define_expand "mand" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "fpr_operand" "") + (match_dup 3)] + UNSPEC_MLOGIC))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MAND);") + +(define_expand "mor" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "fpr_operand" "") + (match_dup 3)] + UNSPEC_MLOGIC))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MOR);") + +(define_expand "mxor" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "fpr_operand" "") + (match_dup 3)] + UNSPEC_MLOGIC))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MXOR);") + +(define_insn "*mlogic" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MLOGIC))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MAND: return \"mand %1, %2, %0\"; + case FRV_BUILTIN_MOR: return \"mor %1, %2, %0\"; + case FRV_BUILTIN_MXOR: return \"mxor %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mlogic\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mlogic")]) + +(define_insn "*cond_exec_mlogic" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "fpr_operand" "f") + (match_operand:SI 5 "const_int_operand" "n")] + UNSPEC_MLOGIC)))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[5])) + { + default: break; + case FRV_BUILTIN_MAND: return \"cmand %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MOR: return \"cmor %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MXOR: return \"cmxor %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mlogic\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mlogic")]) + +;; Logical not: type "mlogic" + +(define_insn "mnot" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MNOT))] + "TARGET_MEDIA" + "mnot %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mlogic")]) + +(define_insn "*cond_exec_mnot" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 3 "fpr_operand" "f")] UNSPEC_MNOT)))] + "TARGET_MEDIA" + "cmnot %3, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mlogic")]) + +;; Dual average (halfword): type "maveh" + +(define_insn "maveh" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f")] + UNSPEC_MAVEH))] + "TARGET_MEDIA" + "maveh %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "maveh")]) + +;; Dual saturation (halfword): type "msath" + +(define_expand "msaths" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 3)] + UNSPEC_MSATH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSATHS);") + +(define_expand "msathu" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 3)] + UNSPEC_MSATH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSATHU);") + +(define_insn "*msath" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MSATH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MSATHS: return \"msaths %1, %2, %0\"; + case FRV_BUILTIN_MSATHU: return \"msathu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, msath\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "msath")]) + +;; Dual addition/subtraction with saturation (halfword): type "maddh" + +(define_expand "maddhss" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 3)] + UNSPEC_MADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MADDHSS);") + +(define_expand "maddhus" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 3)] + UNSPEC_MADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MADDHUS);") + +(define_expand "msubhss" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 3)] + UNSPEC_MADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSUBHSS);") + +(define_expand "msubhus" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 3)] + UNSPEC_MADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSUBHUS);") + +(define_insn "*maddh" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MADDH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MADDHSS: return \"maddhss %1, %2, %0\"; + case FRV_BUILTIN_MADDHUS: return \"maddhus %1, %2, %0\"; + case FRV_BUILTIN_MSUBHSS: return \"msubhss %1, %2, %0\"; + case FRV_BUILTIN_MSUBHUS: return \"msubhus %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, maddh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "maddh")]) + +(define_insn "*cond_exec_maddh" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "fpr_operand" "f") + (match_operand:SI 5 "const_int_operand" "n")] + UNSPEC_MADDH)))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[5])) + { + default: break; + case FRV_BUILTIN_MADDHSS: return \"cmaddhss %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MADDHUS: return \"cmaddhus %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MSUBHSS: return \"cmsubhss %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MSUBHUS: return \"cmsubhus %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_maddh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "maddh")]) + +;; Quad addition/subtraction with saturation (halfword): type "mqaddh" + +(define_expand "mqaddhss" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 3)] + UNSPEC_MQADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MQADDHSS);") + +(define_expand "mqaddhus" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 3)] + UNSPEC_MQADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MQADDHUS);") + +(define_expand "mqsubhss" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 3)] + UNSPEC_MQADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MQSUBHSS);") + +(define_expand "mqsubhus" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 3)] + UNSPEC_MQADDH))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MQSUBHUS);") + +(define_insn "*mqaddh" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MQADDH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MQADDHSS: return \"mqaddhss %1, %2, %0\"; + case FRV_BUILTIN_MQADDHUS: return \"mqaddhus %1, %2, %0\"; + case FRV_BUILTIN_MQSUBHSS: return \"mqsubhss %1, %2, %0\"; + case FRV_BUILTIN_MQSUBHUS: return \"mqsubhus %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mqaddh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqaddh")]) + +(define_insn "*cond_exec_mqaddh" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:DI 2 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 3 "even_fpr_operand" "h") + (match_operand:DI 4 "even_fpr_operand" "h") + (match_operand:SI 5 "const_int_operand" "n")] + UNSPEC_MQADDH)))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[5])) + { + default: break; + case FRV_BUILTIN_MQADDHSS: return \"cmqaddhss %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MQADDHUS: return \"cmqaddhus %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MQSUBHSS: return \"cmqsubhss %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MQSUBHUS: return \"cmqsubhus %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mqaddh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqaddh")]) + +;; Pack halfword: type "mpackh" + +(define_insn "mpackh" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:HI 1 "fpr_operand" "f") + (match_operand:HI 2 "fpr_operand" "f")] + UNSPEC_MPACKH))] + "TARGET_MEDIA" + "mpackh %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mpackh")]) + +;; Unpack halfword: type "mpackh" + +(define_insn "munpackh" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")] + UNSPEC_MUNPACKH))] + "TARGET_MEDIA" + "munpackh %1, %0" + [(set_attr "length" "4") + (set_attr "type" "munpackh")]) + +;; Dual pack halfword: type "mdpackh" + +(define_insn "mdpackh" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h")] + UNSPEC_MDPACKH))] + "TARGET_MEDIA" + "mdpackh %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mdpackh")]) + +;; Byte-halfword conversion: type "mbhconv" + +(define_insn "mbtoh" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")] + UNSPEC_MBTOH))] + "TARGET_MEDIA" + "mbtoh %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mbhconv")]) + +(define_insn "*cond_exec_mbtoh" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:DI 2 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:SI 3 "fpr_operand" "f")] + UNSPEC_MBTOH)))] + "TARGET_MEDIA" + "cmbtoh %3, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mbhconv")]) + +(define_insn "mhtob" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:DI 1 "even_fpr_operand" "h")] + UNSPEC_MHTOB))] + "TARGET_MEDIA" + "mhtob %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mbhconv")]) + +(define_insn "*cond_exec_mhtob" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (unspec:SI [(match_operand:DI 3 "even_fpr_operand" "h")] + UNSPEC_MHTOB)))] + "TARGET_MEDIA" + "cmhtob %3, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mbhconv")]) + +;; Rotate: type "mrot" + +(define_expand "mrotli" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "uint5_operand" "") + (match_dup 3)] + UNSPEC_MROT))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MROTLI);") + +(define_expand "mrotri" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "uint5_operand" "") + (match_dup 3)] + UNSPEC_MROT))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MROTRI);") + +(define_insn "*mrot" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "uint5_operand" "I") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MROT))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MROTLI: return \"mrotli %1, %2, %0\"; + case FRV_BUILTIN_MROTRI: return \"mrotri %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mrot\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mrot")]) + +;; Dual shift halfword: type "msh" + +(define_expand "msllhi" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "uint4_operand" "") + (match_dup 3)] + UNSPEC_MSHIFT))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSLLHI);") + +(define_expand "msrlhi" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "uint4_operand" "") + (match_dup 3)] + UNSPEC_MSHIFT))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSRLHI);") + +(define_expand "msrahi" + [(set (match_operand:SI 0 "fpr_operand" "") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "") + (match_operand:SI 2 "uint4_operand" "") + (match_dup 3)] + UNSPEC_MSHIFT))] + "TARGET_MEDIA" + "operands[3] = GEN_INT (FRV_BUILTIN_MSRAHI);") + +(define_insn "*mshift" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "uint4_operand" "I") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MSHIFT))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MSLLHI: return \"msllhi %1, %2, %0\"; + case FRV_BUILTIN_MSRLHI: return \"msrlhi %1, %2, %0\"; + case FRV_BUILTIN_MSRAHI: return \"msrahi %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mshift\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mshift")]) + +;; Expand halfword to word: type "mexpdhw" + +(define_insn "mexpdhw" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "uint1_operand" "I")] + UNSPEC_MEXPDHW))] + "TARGET_MEDIA" + "mexpdhw %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mexpdhw")]) + +(define_insn "*cond_exec_mexpdhw" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:SI 2 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "uint1_operand" "I")] + UNSPEC_MEXPDHW)))] + "TARGET_MEDIA" + "cmexpdhw %3, %4, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mexpdhw")]) + +;; Expand halfword to double: type "mexpdhd" + +(define_insn "mexpdhd" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "uint1_operand" "I")] + UNSPEC_MEXPDHD))] + "TARGET_MEDIA" + "mexpdhd %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mexpdhd")]) + +(define_insn "*cond_exec_mexpdhd" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (set (match_operand:DI 2 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "uint1_operand" "I")] + UNSPEC_MEXPDHD)))] + "TARGET_MEDIA" + "cmexpdhd %3, %4, %2, %1, %e0" + [(set_attr "length" "4") + (set_attr "type" "mexpdhd")]) + +;; FR cut: type "mwcut" + +(define_insn "mwcut" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:DI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_or_int6_operand" "fI")] + UNSPEC_MWCUT))] + "TARGET_MEDIA" + "mwcut%i2 %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mwcut")]) + +;; Dual multiplication (halfword): type "mmulh" + +(define_expand "mmulhs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MMULH)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMULHS);") + +(define_expand "mmulhu" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MMULH)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMULHU);") + +(define_insn "*mmulh" + [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MMULH)) + (set (match_operand:HI 4 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MMULHS: return \"mmulhs %1, %2, %0\"; + case FRV_BUILTIN_MMULHU: return \"mmulhu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mmulh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mmulh")]) + +(define_insn "*cond_exec_mmulh" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (parallel [(set (match_operand:DI 2 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "fpr_operand" "f") + (match_operand:SI 5 "const_int_operand" "n")] + UNSPEC_MMULH)) + (set (match_operand:HI 6 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULH))]))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[5])) + { + default: break; + case FRV_BUILTIN_MMULHS: return \"cmmulhs %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MMULHU: return \"cmmulhu %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mmulh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mmulh")]) + +;; Dual cross multiplication (halfword): type "mmulxh" + +(define_expand "mmulxhs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MMULXH)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULXH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMULXHS);") + +(define_expand "mmulxhu" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MMULXH)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULXH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMULXHU);") + +(define_insn "*mmulxh" + [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MMULXH)) + (set (match_operand:HI 4 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MMULXH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MMULXHS: return \"mmulxhs %1, %2, %0\"; + case FRV_BUILTIN_MMULXHU: return \"mmulxhu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mmulxh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mmulxh")]) + +;; Dual product-sum (halfword): type "mmach" + +(define_expand "mmachs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b") + (unspec:DI [(match_dup 0) + (match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:HI 3 "accg_operand" "+B") + (match_dup 4)] + UNSPEC_MMACH)) + (set (match_dup 3) + (unspec:HI [(const_int 0)] UNSPEC_MMACH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMACHS);") + +(define_expand "mmachu" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b") + (unspec:DI [(match_dup 0) + (match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:HI 3 "accg_operand" "+B") + (match_dup 4)] + UNSPEC_MMACH)) + (set (match_dup 3) + (unspec:HI [(const_int 0)] UNSPEC_MMACH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMACHU);") + +(define_insn "*mmach" + [(set (match_operand:DI 0 "even_acc_operand" "+b") + (unspec:DI [(match_dup 0) + (match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:HI 3 "accg_operand" "+B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MMACH)) + (set (match_dup 3) (unspec:HI [(const_int 0)] UNSPEC_MMACH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MMACHS: return \"mmachs %1, %2, %0\"; + case FRV_BUILTIN_MMACHU: return \"mmachu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mmach\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mmach")]) + +(define_insn "*cond_exec_mmach" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (parallel [(set (match_operand:DI 2 "even_acc_operand" "+b") + (unspec:DI [(match_dup 2) + (match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "fpr_operand" "f") + (match_operand:HI 5 "accg_operand" "+B") + (match_operand:SI 6 "const_int_operand" "n")] + UNSPEC_MMACH)) + (set (match_dup 5) + (unspec:HI [(const_int 0)] UNSPEC_MMACH))]))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[6])) + { + default: break; + case FRV_BUILTIN_MMACHS: return \"cmmachs %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MMACHU: return \"cmmachu %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mmach\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mmach")]) + +;; Dual product-difference: type "mmrdh" + +(define_expand "mmrdhs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b") + (unspec:DI [(match_dup 0) + (match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:HI 3 "accg_operand" "+B") + (match_dup 4)] + UNSPEC_MMRDH)) + (set (match_dup 3) + (unspec:HI [(const_int 0)] UNSPEC_MMRDH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMRDHS);") + +(define_expand "mmrdhu" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b") + (unspec:DI [(match_dup 0) + (match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:HI 3 "accg_operand" "+B") + (match_dup 4)] + UNSPEC_MMRDH)) + (set (match_dup 3) + (unspec:HI [(const_int 0)] UNSPEC_MMRDH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MMRDHU);") + +(define_insn "*mmrdh" + [(set (match_operand:DI 0 "even_acc_operand" "+b") + (unspec:DI [(match_dup 0) + (match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:HI 3 "accg_operand" "+B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MMRDH)) + (set (match_dup 3) + (unspec:HI [(const_int 0)] UNSPEC_MMRDH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MMRDHS: return \"mmrdhs %1, %2, %0\"; + case FRV_BUILTIN_MMRDHU: return \"mmrdhu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mrdh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mmrdh")]) + +;; Quad multiply (halfword): type "mqmulh" + +(define_expand "mqmulhs" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 4)] + UNSPEC_MQMULH)) + (set (match_operand:V4QI 3 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMULHS);") + +(define_expand "mqmulhu" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 4)] + UNSPEC_MQMULH)) + (set (match_operand:V4QI 3 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMULHU);") + +(define_insn "*mqmulh" + [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MQMULH)) + (set (match_operand:V4QI 4 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MQMULHS: return \"mqmulhs %1, %2, %0\"; + case FRV_BUILTIN_MQMULHU: return \"mqmulhu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mqmulh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqmulh")]) + +(define_insn "*cond_exec_mqmulh" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (parallel [(set (match_operand:V4SI 2 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 3 "even_fpr_operand" "h") + (match_operand:DI 4 "even_fpr_operand" "h") + (match_operand:SI 5 "const_int_operand" "n")] + UNSPEC_MQMULH)) + (set (match_operand:V4QI 6 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))]))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[5])) + { + default: break; + case FRV_BUILTIN_MQMULHS: return \"cmqmulhs %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MQMULHU: return \"cmqmulhu %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mqmulh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqmulh")]) + +;; Quad cross multiply (halfword): type "mqmulxh" + +(define_expand "mqmulxhs" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 4)] + UNSPEC_MQMULXH)) + (set (match_operand:V4QI 3 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMULXHS);") + +(define_expand "mqmulxhu" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_dup 4)] + UNSPEC_MQMULXH)) + (set (match_operand:V4QI 3 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMULXHU);") + +(define_insn "*mqmulxh" + [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MQMULXH)) + (set (match_operand:V4QI 4 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MQMULXHS: return \"mqmulxhs %1, %2, %0\"; + case FRV_BUILTIN_MQMULXHU: return \"mqmulxhu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mqmulxh\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqmulxh")]) + +;; Quad product-sum (halfword): type "mqmach" + +(define_expand "mqmachs" + [(parallel [(set (match_operand:V4SI 0 "even_acc_operand" "+A") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:V4QI 3 "accg_operand" "+B") + (match_dup 4)] + UNSPEC_MQMACH)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMACHS);") + +(define_expand "mqmachu" + [(parallel [(set (match_operand:V4SI 0 "even_acc_operand" "+A") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:V4QI 3 "accg_operand" "+B") + (match_dup 4)] + UNSPEC_MQMACH)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMACHU);") + +(define_insn "*mqmach" + [(set (match_operand:V4SI 0 "even_acc_operand" "+A") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:V4QI 3 "accg_operand" "+B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MQMACH)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MQMACHS: return \"mqmachs %1, %2, %0\"; + case FRV_BUILTIN_MQMACHU: return \"mqmachu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mqmach\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqmach")]) + +(define_insn "*cond_exec_mqmach" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (parallel [(set (match_operand:V4SI 2 "even_acc_operand" "+A") + (unspec:V4SI [(match_dup 2) + (match_operand:DI 3 "even_fpr_operand" "h") + (match_operand:DI 4 "even_fpr_operand" "h") + (match_operand:V4QI 5 "accg_operand" "+B") + (match_operand:SI 6 "const_int_operand" "n")] + UNSPEC_MQMACH)) + (set (match_dup 5) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))]))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[6])) + { + default: break; + case FRV_BUILTIN_MQMACHS: return \"cmqmachs %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MQMACHU: return \"cmqmachu %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mqmach\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqmach")]) + +;; Dual complex number product-sum (halfword) + +(define_expand "mcpxrs" + [(parallel [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MCPX)) + (set (match_operand:QI 3 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MCPXRS);") + +(define_expand "mcpxru" + [(parallel [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MCPX)) + (set (match_operand:QI 3 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MCPXRU);") + +(define_expand "mcpxis" + [(parallel [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MCPX)) + (set (match_operand:QI 3 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MCPXIS);") + +(define_expand "mcpxiu" + [(parallel [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MCPX)) + (set (match_operand:QI 3 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MCPXIU);") + +(define_insn "*mcpx" + [(parallel [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MCPX)) + (set (match_operand:QI 4 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCPX))])] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MCPXRS: return \"mcpxrs %1, %2, %0\"; + case FRV_BUILTIN_MCPXRU: return \"mcpxru %1, %2, %0\"; + case FRV_BUILTIN_MCPXIS: return \"mcpxis %1, %2, %0\"; + case FRV_BUILTIN_MCPXIU: return \"mcpxiu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mcpx\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mcpx")]) + +(define_insn "*cond_exec_mcpx" + [(cond_exec + (match_operator 0 "ccr_eqne_operator" + [(match_operand 1 "cr_operand" "C") + (const_int 0)]) + (parallel [(set (match_operand:SI 2 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 3 "fpr_operand" "f") + (match_operand:SI 4 "fpr_operand" "f") + (match_operand:SI 5 "const_int_operand" "n")] + UNSPEC_MCPX)) + (set (match_operand:QI 6 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCPX))]))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[5])) + { + default: break; + case FRV_BUILTIN_MCPXRS: return \"cmcpxrs %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MCPXRU: return \"cmcpxru %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MCPXIS: return \"cmcpxis %3, %4, %2, %1, %e0\"; + case FRV_BUILTIN_MCPXIU: return \"cmcpxiu %3, %4, %2, %1, %e0\"; + } + + fatal_insn (\"Bad media insn, cond_exec_mcpx\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mcpx")]) + +;; Quad complex number product-sum (halfword): type "mqcpx" + +(define_expand "mqcpxrs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:DI 1 "fpr_operand" "f") + (match_operand:DI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MQCPX)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXRS);") + +(define_expand "mqcpxru" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:DI 1 "fpr_operand" "f") + (match_operand:DI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MQCPX)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXRU);") + +(define_expand "mqcpxis" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:DI 1 "fpr_operand" "f") + (match_operand:DI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MQCPX)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXIS);") + +(define_expand "mqcpxiu" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:DI 1 "fpr_operand" "f") + (match_operand:DI 2 "fpr_operand" "f") + (match_dup 4)] + UNSPEC_MQCPX)) + (set (match_operand:HI 3 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXIU);") + +(define_insn "*mqcpx" + [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:DI 1 "fpr_operand" "f") + (match_operand:DI 2 "fpr_operand" "f") + (match_operand:SI 3 "const_int_operand" "n")] + UNSPEC_MQCPX)) + (set (match_operand:HI 4 "accg_operand" "=B") + (unspec:HI [(const_int 0)] UNSPEC_MQCPX))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[3])) + { + default: break; + case FRV_BUILTIN_MQCPXRS: return \"mqcpxrs %1, %2, %0\"; + case FRV_BUILTIN_MQCPXRU: return \"mqcpxru %1, %2, %0\"; + case FRV_BUILTIN_MQCPXIS: return \"mqcpxis %1, %2, %0\"; + case FRV_BUILTIN_MQCPXIU: return \"mqcpxiu %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mqcpx\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqcpx")]) + +;; Cut: type "mcut" + +(define_expand "mcut" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "acc_operand" "a") + (match_operand:SI 2 "fpr_or_int6_operand" "fI") + (match_operand:QI 3 "accg_operand" "B") + (match_dup 4)] + UNSPEC_MCUT))] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MCUT);") + +(define_expand "mcutss" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "acc_operand" "a") + (match_operand:SI 2 "fpr_or_int6_operand" "fI") + (match_operand:QI 3 "accg_operand" "B") + (match_dup 4)] + UNSPEC_MCUT))] + "TARGET_MEDIA" + "operands[4] = GEN_INT (FRV_BUILTIN_MCUTSS);") + +(define_insn "*mcut" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "acc_operand" "a") + (match_operand:SI 2 "fpr_or_int6_operand" "fI") + (match_operand:QI 3 "accg_operand" "B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MCUT))] + "TARGET_MEDIA" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MCUT: return \"mcut%i2 %1, %2, %0\"; + case FRV_BUILTIN_MCUTSS: return \"mcutss%i2 %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mcut\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mcut")]) + +;; Accumulator read: type "mrdacc" + +(define_insn "mrdacc" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "acc_operand" "a")] UNSPEC_MRDACC))] + "TARGET_MEDIA" + "mrdacc %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mrdacc")]) + +(define_insn "mrdaccg" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:QI 1 "accg_operand" "B")] UNSPEC_MRDACCG))] + "TARGET_MEDIA" + "mrdaccg %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mrdacc")]) + +;; Accumulator write: type "mwtacc" + +(define_insn "mwtacc" + [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MWTACC))] + "TARGET_MEDIA" + "mwtacc %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mwtacc")]) + +(define_insn "mwtaccg" + [(set (match_operand:QI 0 "accg_operand" "=B") + (unspec:QI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MWTACCG))] + "TARGET_MEDIA" + "mwtaccg %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mwtacc")]) + +;; Trap: This one executes on the control unit, not the media units. + +(define_insn "mtrap" + [(unspec_volatile [(const_int 0)] UNSPEC_MTRAP)] + "TARGET_MEDIA" + "mtrap" + [(set_attr "length" "4") + (set_attr "type" "trap")]) + +;; Clear single accumulator: type "mclracc" + +(define_insn "mclracc_internal" + [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(const_int 0)] UNSPEC_MCLRACC)) + (set (match_operand:QI 1 "accg_operand" "=B") + (unspec:QI [(const_int 0)] UNSPEC_MCLRACC))] + "TARGET_MEDIA" + "mclracc %0,#0" + [(set_attr "length" "4") + (set_attr "type" "mclracc")]) + +(define_expand "mclracc" + [(parallel [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(const_int 0)] UNSPEC_MCLRACC)) + (set (match_dup 1) + (unspec:QI [(const_int 0)] UNSPEC_MCLRACC))])] + "TARGET_MEDIA" + " +{ + if (GET_CODE (operands[0]) != REG || !ACC_P (REGNO (operands[0]))) + FAIL; + + operands[1] = frv_matching_accg_for_acc (operands[0]); +}") + +;; Clear all accumulators: type "mclracca" + +(define_insn "mclracca8_internal" + [(set (match_operand:V4SI 0 "quad_acc_operand" "=b") + (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_operand:V4SI 1 "quad_acc_operand" "=b") + (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_operand:V4QI 2 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_operand:V4QI 3 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))] + "TARGET_MEDIA && TARGET_ACC_8" + "mclracc acc0,#1" + [(set_attr "length" "4") + (set_attr "type" "mclracca")]) + +(define_insn "mclracca4_internal" + [(set (match_operand:V4SI 0 "quad_acc_operand" "=b") + (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_operand:V4QI 1 "accg_operand" "=B") + (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))] + "TARGET_MEDIA && TARGET_ACC_4" + "mclracc acc0,#1" + [(set_attr "length" "4") + (set_attr "type" "mclracca")]) + +(define_expand "mclracca8" + [(parallel [(set (match_dup 0) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_dup 1) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_dup 2) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_dup 3) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))])] + "TARGET_MEDIA && TARGET_ACC_8" + " +{ + operands[0] = gen_rtx_REG (V4SImode, ACC_FIRST); + operands[1] = gen_rtx_REG (V4SImode, ACC_FIRST + (~3 & ACC_MASK)); + operands[2] = gen_rtx_REG (V4QImode, ACCG_FIRST); + operands[3] = gen_rtx_REG (V4QImode, ACCG_FIRST + (~3 & ACC_MASK)); +}") + +(define_expand "mclracca4" + [(parallel [(set (match_dup 0) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA)) + (set (match_dup 1) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))])] + "TARGET_MEDIA && TARGET_ACC_4" + " +{ + operands[0] = gen_rtx_REG (V4SImode, ACC_FIRST); + operands[1] = gen_rtx_REG (V4QImode, ACCG_FIRST); +}") + +(define_insn "mcop1" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f")] UNSPEC_MCOP1))] + "TARGET_MEDIA_REV1" + "mcop1 %1, %2, %0" + [(set_attr "length" "4") +;; What is the class of the insn ??? + (set_attr "type" "multi")]) + +(define_insn "mcop2" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f") + (match_operand:SI 2 "fpr_operand" "f")] UNSPEC_MCOP2))] + "TARGET_MEDIA_REV1" + "mcop2 %1, %2, %0" + [(set_attr "length" "4") +;; What is the class of the insn ??? + (set_attr "type" "multi")]) + +(define_insn "*mdunpackh_internal" + [(set (match_operand:V4SI 0 "quad_fpr_operand" "=x") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")] + UNSPEC_MDUNPACKH_INTERNAL))] + "TARGET_MEDIA_REV1" + "mdunpackh %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mdunpackh")]) + +(define_insn_and_split "mdunpackh" + [(set (match_operand:V4SI 0 "memory_operand" "=o") + (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")] + UNSPEC_MDUNPACKH)) + (clobber (match_scratch:V4SI 2 "=x"))] + "TARGET_MEDIA_REV1" + "#" + "reload_completed" + [(set (match_dup 2) + (unspec:V4SI [(match_dup 1)] UNSPEC_MDUNPACKH_INTERNAL)) + (set (match_dup 3) + (match_dup 4)) + (set (match_dup 5) + (match_dup 6))] + " +{ + operands[3] = change_address (operands[0], DImode, NULL_RTX); + operands[4] = gen_rtx_REG (DImode, REGNO (operands[2])); + operands[5] = frv_index_memory (operands[0], DImode, 1); + operands[6] = gen_rtx_REG (DImode, REGNO (operands[2])+2); +}" + [(set_attr "length" "20") + (set_attr "type" "multi")]) + +(define_insn "*mbtohe_internal" + [(set (match_operand:V4SI 0 "quad_fpr_operand" "=x") + (unspec:V4SI [(match_operand:SI 1 "fpr_operand" "f")] + UNSPEC_MBTOHE_INTERNAL))] + "TARGET_MEDIA_REV1" + "mbtohe %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mbhconve")]) + +(define_insn_and_split "mbtohe" + [(set (match_operand:V4SI 0 "memory_operand" "=o") + (unspec:V4SI [(match_operand:SI 1 "fpr_operand" "f")] + UNSPEC_MBTOHE)) + (clobber (match_scratch:V4SI 2 "=x"))] + "TARGET_MEDIA_REV1" + "#" + "reload_completed" + [(set (match_dup 2) + (unspec:V4SI [(match_dup 1)] UNSPEC_MBTOHE_INTERNAL)) + (set (match_dup 3) + (match_dup 4)) + (set (match_dup 5) + (match_dup 6))] + " +{ + operands[3] = change_address (operands[0], DImode, NULL_RTX); + operands[4] = gen_rtx_REG (DImode, REGNO (operands[2])); + operands[5] = frv_index_memory (operands[0], DImode, 1); + operands[6] = gen_rtx_REG (DImode, REGNO (operands[2])+2); +}" + [(set_attr "length" "20") + (set_attr "type" "multi")]) + +;; Quad product-sum (halfword) instructions only found on the FR400. +;; type "mqmach" + +(define_expand "mqxmachs" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "") + (match_operand:DI 2 "even_fpr_operand" "") + (match_operand:V4QI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MQMACH2)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MQXMACHS);") + +(define_expand "mqxmacxhs" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "") + (match_operand:DI 2 "even_fpr_operand" "") + (match_operand:V4QI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MQMACH2)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MQXMACXHS);") + +(define_expand "mqmacxhs" + [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "") + (match_operand:DI 2 "even_fpr_operand" "") + (match_operand:V4QI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MQMACH2)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MQMACXHS);") + +(define_insn "*mqmach2" + [(set (match_operand:V4SI 0 "quad_acc_operand" "+A") + (unspec:V4SI [(match_dup 0) + (match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h") + (match_operand:V4QI 3 "accg_operand" "+B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MQMACH2)) + (set (match_dup 3) + (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))] + "TARGET_MEDIA_REV2" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MQXMACHS: return \"mqxmachs %1, %2, %0\"; + case FRV_BUILTIN_MQXMACXHS: return \"mqxmacxhs %1, %2, %0\"; + case FRV_BUILTIN_MQMACXHS: return \"mqmacxhs %1, %2, %0\"; + } + + fatal_insn (\"Bad media insn, mqmach2\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mqmach")]) + +;; Accumulator addition/subtraction: type "maddacc" + +(define_expand "maddaccs" + [(parallel [(set (match_operand:SI 0 "acc_operand" "") + (unspec:SI [(match_operand:DI 1 "even_acc_operand" "")] + UNSPEC_MADDACC)) + (set (match_operand:QI 2 "accg_operand" "") + (unspec:QI [(match_operand:HI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MADDACC))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MADDACCS);") + +(define_expand "msubaccs" + [(parallel [(set (match_operand:SI 0 "acc_operand" "") + (unspec:SI [(match_operand:DI 1 "even_acc_operand" "")] + UNSPEC_MADDACC)) + (set (match_operand:QI 2 "accg_operand" "") + (unspec:QI [(match_operand:HI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MADDACC))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MSUBACCS);") + +(define_insn "masaccs" + [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:DI 1 "even_acc_operand" "b")] + UNSPEC_MASACCS)) + (set (match_operand:HI 2 "accg_operand" "=B") + (unspec:HI [(match_operand:HI 3 "accg_operand" "B")] + UNSPEC_MASACCS))] + "TARGET_MEDIA_REV2" + "masaccs %1, %0" + [(set_attr "length" "4") + (set_attr "type" "maddacc")]) + +(define_insn "*maddacc" + [(set (match_operand:SI 0 "acc_operand" "=a") + (unspec:SI [(match_operand:DI 1 "even_acc_operand" "b")] + UNSPEC_MADDACC)) + (set (match_operand:QI 2 "accg_operand" "=B") + (unspec:QI [(match_operand:HI 3 "accg_operand" "B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MADDACC))] + "TARGET_MEDIA_REV2" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MADDACCS: return \"maddaccs %1, %0\"; + case FRV_BUILTIN_MSUBACCS: return \"msubaccs %1, %0\"; + } + + fatal_insn (\"Bad media insn, maddacc\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "maddacc")]) + +;; Dual accumulator addition/subtraction: type "mdaddacc" + +(define_expand "mdaddaccs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "") + (unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "")] + UNSPEC_MDADDACC)) + (set (match_operand:HI 2 "accg_operand" "") + (unspec:HI [(match_operand:V4QI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MDADDACC))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MDADDACCS);") + +(define_expand "mdsubaccs" + [(parallel [(set (match_operand:DI 0 "even_acc_operand" "") + (unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "")] + UNSPEC_MDADDACC)) + (set (match_operand:HI 2 "accg_operand" "") + (unspec:HI [(match_operand:V4QI 3 "accg_operand" "") + (match_dup 4)] + UNSPEC_MDADDACC))])] + "TARGET_MEDIA_REV2" + "operands[4] = GEN_INT (FRV_BUILTIN_MDSUBACCS);") + +(define_insn "mdasaccs" + [(set (match_operand:V4SI 0 "quad_acc_operand" "=A") + (unspec:V4SI [(match_operand:V4SI 1 "quad_acc_operand" "A")] + UNSPEC_MDASACCS)) + (set (match_operand:V4QI 2 "accg_operand" "=B") + (unspec:V4QI [(match_operand:V4QI 3 "accg_operand" "B")] + UNSPEC_MDASACCS))] + "TARGET_MEDIA_REV2" + "mdasaccs %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mdaddacc")]) + +(define_insn "*mdaddacc" + [(set (match_operand:DI 0 "even_acc_operand" "=b") + (unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "A")] + UNSPEC_MDADDACC)) + (set (match_operand:HI 2 "accg_operand" "=B") + (unspec:HI [(match_operand:V4QI 3 "accg_operand" "B") + (match_operand:SI 4 "const_int_operand" "n")] + UNSPEC_MDADDACC))] + "TARGET_MEDIA_REV2" + "* +{ + switch (INTVAL (operands[4])) + { + default: break; + case FRV_BUILTIN_MDADDACCS: return \"mdaddaccs %1, %0\"; + case FRV_BUILTIN_MDSUBACCS: return \"mdsubaccs %1, %0\"; + } + + fatal_insn (\"Bad media insn, mdaddacc\", insn); +}" + [(set_attr "length" "4") + (set_attr "type" "mdaddacc")]) + +;; Dual absolute (halfword): type "mabsh" + +(define_insn "mabshs" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MABSHS))] + "TARGET_MEDIA_REV2" + "mabshs %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mabsh")]) + +;; Dual rotate: type "mdrot" + +(define_insn "mdrotli" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:SI 2 "uint5_operand" "I")] + UNSPEC_MDROTLI))] + "TARGET_MEDIA_REV2" + "mdrotli %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mdrot")]) + +;; Dual coupling (concatenation): type "mcpl" + +(define_insn "mcplhi" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:DI 1 "fpr_operand" "h") + (match_operand:SI 2 "uint4_operand" "I")] + UNSPEC_MCPLHI))] + "TARGET_MEDIA_REV2" + "mcplhi %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mcpl")]) + +(define_insn "mcpli" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:DI 1 "fpr_operand" "h") + (match_operand:SI 2 "uint5_operand" "I")] + UNSPEC_MCPLI))] + "TARGET_MEDIA_REV2" + "mcpli %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mcpl")]) + +;; Dual cut: type "mdcut" + +(define_insn "mdcutssi" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_acc_operand" "b") + (match_operand:SI 2 "int6_operand" "I") + (match_operand:HI 3 "accg_operand" "B")] + UNSPEC_MDCUTSSI))] + "TARGET_MEDIA_REV2" + "mdcutssi %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mdcut")]) + +;; Quad saturate (halfword): type "mqsath" + +(define_insn "mqsaths" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h")] + UNSPEC_MQSATHS))] + "TARGET_MEDIA_REV2" + "mqsaths %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mqsath")]) + +;; Quad limit instructions: type "mqlimh" + +(define_insn "mqlclrhs" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h")] + UNSPEC_MQLCLRHS))] + "TARGET_MEDIA_FR450" + "mqlclrhs %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mqlimh")]) + +(define_insn "mqlmths" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:DI 2 "even_fpr_operand" "h")] + UNSPEC_MQLMTHS))] + "TARGET_MEDIA_FR450" + "mqlmths %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mqlimh")]) + +(define_insn "mqsllhi" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:SI 2 "int6_operand" "I")] + UNSPEC_MQSLLHI))] + "TARGET_MEDIA_FR450" + "mqsllhi %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mqshift")]) + +(define_insn "mqsrahi" + [(set (match_operand:DI 0 "even_fpr_operand" "=h") + (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h") + (match_operand:SI 2 "int6_operand" "I")] + UNSPEC_MQSRAHI))] + "TARGET_MEDIA_FR450" + "mqsrahi %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mqshift")]) + +;; Set hi/lo instructions: type "mset" + +(define_insn "mhsetlos" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "0") + (match_operand:SI 2 "int12_operand" "NOP")] + UNSPEC_MHSETLOS))] + "TARGET_MEDIA_REV2" + "mhsetlos %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mset")]) + +(define_insn "mhsetloh" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "0") + (match_operand:SI 2 "int5_operand" "I")] + UNSPEC_MHSETLOH))] + "TARGET_MEDIA_REV2" + "mhsetloh %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mset")]) + +(define_insn "mhsethis" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "0") + (match_operand:SI 2 "int12_operand" "NOP")] + UNSPEC_MHSETHIS))] + "TARGET_MEDIA_REV2" + "mhsethis %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mset")]) + +(define_insn "mhsethih" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "0") + (match_operand:SI 2 "int5_operand" "I")] + UNSPEC_MHSETHIH))] + "TARGET_MEDIA_REV2" + "mhsethih %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mset")]) + +(define_insn "mhdsets" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "int12_operand" "NOP")] + UNSPEC_MHDSETS))] + "TARGET_MEDIA_REV2" + "mhdsets %1, %0" + [(set_attr "length" "4") + (set_attr "type" "mset")]) + +(define_insn "mhdseth" + [(set (match_operand:SI 0 "fpr_operand" "=f") + (unspec:SI [(match_operand:SI 1 "fpr_operand" "0") + (match_operand:SI 2 "int5_operand" "I")] + UNSPEC_MHDSETH))] + "TARGET_MEDIA_REV2" + "mhdseth %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mset")]) + +;;----------------------------------------------------------------------------- + +(define_expand "symGOT2reg" + [(match_operand:SI 0 "" "") + (match_operand:SI 1 "" "") + (match_operand:SI 2 "" "") + (match_operand:SI 3 "" "")] + "" + " +{ + rtx insn; + + insn = emit_insn (gen_symGOT2reg_i (operands[0], operands[1], operands[2], operands[3])); + + MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1; + + set_unique_reg_note (insn, REG_EQUAL, operands[1]); + + DONE; +}") + +(define_expand "symGOT2reg_i" + [(set (match_operand:SI 0 "" "") + (mem:SI (plus:SI (match_operand:SI 2 "" "") + (const:SI (unspec:SI [(match_operand:SI 1 "" "") + (match_operand:SI 3 "" "")] + UNSPEC_GOT)))))] + "" + "") + +(define_expand "symGOT2reg_hilo" + [(set (match_dup 6) + (high:SI (const:SI (unspec:SI [(match_operand:SI 1 "" "") + (match_dup 4)] UNSPEC_GOT)))) + (set (match_dup 5) + (lo_sum:SI (match_dup 6) + (const:SI (unspec:SI [(match_dup 1) + (match_operand:SI 3 "" "")] + UNSPEC_GOT)))) + (set (match_operand:SI 0 "" "") + (mem:SI (plus:SI (match_dup 5) + (match_operand:SI 2 "" "")))) + ] + "" + " +{ + if (!can_create_pseudo_p ()) + operands[6] = operands[5] = operands[0]; + else + { + operands[6] = gen_reg_rtx (SImode); + operands[5] = gen_reg_rtx (SImode); + } + + operands[4] = GEN_INT (INTVAL (operands[3]) + 1); + operands[3] = GEN_INT (INTVAL (operands[3]) + 2); +}") + +(define_expand "symGOTOFF2reg_hilo" + [(set (match_dup 6) + (high:SI (const:SI (unspec:SI [(match_operand:SI 1 "" "") + (match_dup 4)] UNSPEC_GOT)))) + (set (match_dup 5) + (lo_sum:SI (match_dup 6) + (const:SI (unspec:SI [(match_dup 1) + (match_operand:SI 3 "" "")] + UNSPEC_GOT)))) + (set (match_operand:SI 0 "" "") + (plus:SI (match_dup 5) + (match_operand:SI 2 "" ""))) + ] + "" + " +{ + if (!can_create_pseudo_p ()) + operands[6] = operands[5] = operands[0]; + else + { + operands[6] = gen_reg_rtx (SImode); + operands[5] = gen_reg_rtx (SImode); + } + + operands[4] = GEN_INT (INTVAL (operands[3]) + 1); + operands[3] = GEN_INT (INTVAL (operands[3]) + 2); +}") + +(define_expand "symGOTOFF2reg" + [(match_operand:SI 0 "" "") + (match_operand:SI 1 "" "") + (match_operand:SI 2 "" "") + (match_operand:SI 3 "" "")] + "" + " +{ + rtx insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1], operands[2], operands[3])); + + set_unique_reg_note (insn, REG_EQUAL, operands[1]); + + DONE; +}") + +(define_expand "symGOTOFF2reg_i" + [(set (match_operand:SI 0 "" "") + (plus:SI (match_operand:SI 2 "" "") + (const:SI + (unspec:SI [(match_operand:SI 1 "" "") + (match_operand:SI 3 "" "")] + UNSPEC_GOT))))] + "" + "") + +(define_expand "symGPREL2reg" + [(match_operand:SI 0 "" "") + (match_operand:SI 1 "" "") + (match_operand:SI 2 "" "") + (match_operand:SI 3 "" "") + (match_dup 4)] + "" + " +{ + rtx insn; + + if (!can_create_pseudo_p ()) + operands[4] = operands[0]; + else + operands[4] = gen_reg_rtx (SImode); + + emit_insn (frv_gen_GPsym2reg (operands[4], operands[2])); + + insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1], + operands[4], operands[3])); + + set_unique_reg_note (insn, REG_EQUAL, operands[1]); + + DONE; +}") + +(define_expand "symGPREL2reg_hilo" + [(match_operand:SI 0 "" "") + (match_operand:SI 1 "" "") + (match_operand:SI 2 "" "") + (match_operand:SI 3 "" "") + (match_dup 4)] + "" + " +{ + rtx insn; + + if (!can_create_pseudo_p ()) + { + emit_insn (gen_symGOT2reg (operands[0], operands[1], operands[2], + GEN_INT (R_FRV_GOT12))); + DONE; + } + + operands[4] = gen_reg_rtx (SImode); + + emit_insn (frv_gen_GPsym2reg (operands[4], operands[2])); + + insn = emit_insn (gen_symGOTOFF2reg_hilo (operands[0], operands[1], + operands[4], operands[3])); + + set_unique_reg_note (insn, REG_EQUAL, operands[1]); + + DONE; +}") + +(define_constants + [ + (UNSPEC_SMUL 154) + (UNSPEC_UMUL 155) + (UNSPEC_SMU 156) + (UNSPEC_ADDSS 157) + (UNSPEC_SUBSS 158) + (UNSPEC_SLASS 159) + (UNSPEC_SCAN 160) + (UNSPEC_INTSS 161) + (UNSPEC_SCUTSS 162) + (UNSPEC_PREFETCH0 163) + (UNSPEC_PREFETCH 164) + (UNSPEC_IACCreadll 165) + (UNSPEC_IACCreadl 166) + (UNSPEC_IACCsetll 167) + (UNSPEC_IACCsetl 168) + (UNSPEC_SMASS 169) + (UNSPEC_SMSSS 170) + (UNSPEC_IMUL 171) + + (IACC0_REG 171) +]) + +(define_insn "smul" + [(set (match_operand:DI 0 "integer_register_operand" "=d") + (unspec:DI [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_SMUL))] + "" + "smul %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +(define_insn "umul" + [(set (match_operand:DI 0 "integer_register_operand" "=d") + (unspec:DI [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_UMUL))] + "" + "umul %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "mul")]) + +(define_insn "smass" + [(set (reg:DI IACC0_REG) + (unspec:DI [(match_operand:SI 0 "integer_register_operand" "d") + (match_operand:SI 1 "integer_register_operand" "d") + (reg:DI IACC0_REG)] + UNSPEC_SMASS))] + "TARGET_FR405_BUILTINS" + "smass %1, %0" + [(set_attr "length" "4") + (set_attr "type" "macc")]) + +(define_insn "smsss" + [(set (reg:DI IACC0_REG) + (unspec:DI [(match_operand:SI 0 "integer_register_operand" "d") + (match_operand:SI 1 "integer_register_operand" "d") + (reg:DI IACC0_REG)] + UNSPEC_SMSSS))] + "TARGET_FR405_BUILTINS" + "smsss %1, %0" + [(set_attr "length" "4") + (set_attr "type" "macc")]) + +(define_insn "smu" + [(set (reg:DI IACC0_REG) + (unspec:DI [(match_operand:SI 0 "integer_register_operand" "d") + (match_operand:SI 1 "integer_register_operand" "d")] + UNSPEC_SMU))] + "TARGET_FR405_BUILTINS" + "smu %1, %0" + [(set_attr "length" "4") + (set_attr "type" "macc")]) + +(define_insn "addss" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_ADDSS))] + "TARGET_FR405_BUILTINS" + "addss %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "subss" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_SUBSS))] + "TARGET_FR405_BUILTINS" + "subss %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "slass" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_SLASS))] + "TARGET_FR405_BUILTINS" + "slass %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "int")]) + +(define_insn "scan" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d") + (match_operand:SI 2 "integer_register_operand" "d")] + UNSPEC_SCAN))] + "" + "scan %1, %2, %0" + [(set_attr "length" "4") + (set_attr "type" "scan")]) + +(define_insn "scutss" + [(set (match_operand:SI 0 "integer_register_operand" "=d") + (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d") + (reg:DI IACC0_REG)] + UNSPEC_SCUTSS))] + "TARGET_FR405_BUILTINS" + "scutss %1,%0" + [(set_attr "length" "4") + (set_attr "type" "cut")]) + +(define_insn "frv_prefetch0" + [(prefetch (unspec:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_PREFETCH0) + (const_int 0) + (const_int 0))] + "" + "dcpl %0, gr0, #0" + [(set_attr "length" "4")]) + +(define_insn "frv_prefetch" + [(prefetch (unspec:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_PREFETCH) + (const_int 0) + (const_int 0))] + "TARGET_FR500_FR550_BUILTINS" + "nop.p\\n\\tnldub @(%0, gr0), gr0" + [(set_attr "length" "8")]) + +;; TLS patterns + +(define_insn "call_gettlsoff" + [(set (match_operand:SI 0 "register_operand" "=D09") + (unspec:SI + [(match_operand:SI 1 "symbolic_operand" "")] + UNSPEC_GETTLSOFF)) + (clobber (reg:SI GR8_REG)) + (clobber (reg:SI LRREG)) + (use (match_operand:SI 2 "register_operand" "D15"))] + "HAVE_AS_TLS" + "call #gettlsoff(%a1)" + [(set_attr "length" "4") + (set_attr "type" "load_or_call")]) + +;; We have to expand this like a libcall (it sort of actually is) +;; because otherwise sched may move, for example, an insn that sets up +;; GR8 for a subsequence call before the *tls_indirect_call insn, and +;; then reload won't be able to fix things up. +(define_expand "tls_indirect_call" + [(set (reg:DI GR8_REG) + (match_operand:DI 2 "register_operand" "")) + (parallel + [(set (reg:SI GR9_REG) + (unspec:SI + [(match_operand:SI 1 "symbolic_operand" "") + (reg:DI GR8_REG)] + UNSPEC_TLS_INDIRECT_CALL)) + (clobber (reg:SI GR8_REG)) + (clobber (reg:SI LRREG)) + (use (match_operand:SI 3 "register_operand" ""))]) + (set (match_operand:SI 0 "register_operand" "") + (reg:SI GR9_REG))] + "HAVE_AS_TLS") + +(define_insn "*tls_indirect_call" + [(set (reg:SI GR9_REG) + (unspec:SI + [(match_operand:SI 0 "symbolic_operand" "") + (reg:DI GR8_REG)] + UNSPEC_TLS_INDIRECT_CALL)) + (clobber (reg:SI GR8_REG)) + (clobber (reg:SI LRREG)) + ;; If there was a way to represent the fact that we don't need GR9 + ;; or GR15 to be set before this instruction (it could be in + ;; parallel), we could use it here. This change wouldn't apply to + ;; call_gettlsoff, thought, since the linker may turn the latter + ;; into ldi @(gr15,offset),gr9. + (use (match_operand:SI 1 "register_operand" "D15"))] + "HAVE_AS_TLS" + "calll #gettlsoff(%a0)@(gr8,gr0)" + [(set_attr "length" "4") + (set_attr "type" "jumpl")]) + +(define_insn "tls_load_gottlsoff12" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI + [(match_operand:SI 1 "symbolic_operand" "") + (match_operand:SI 2 "register_operand" "r")] + UNSPEC_TLS_LOAD_GOTTLSOFF12))] + "HAVE_AS_TLS" + "ldi @(%2, #gottlsoff12(%1)), %0" + [(set_attr "length" "4")]) + +(define_expand "tlsoff_hilo" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (const:SI (unspec:SI + [(match_operand:SI 1 "symbolic_operand" "") + (match_operand:SI 2 "immediate_operand" "n")] + UNSPEC_GOT)))) + (set (match_dup 0) + (lo_sum:SI (match_dup 0) + (const:SI (unspec:SI [(match_dup 1) + (match_dup 3)] UNSPEC_GOT))))] + "" + " +{ + operands[3] = GEN_INT (INTVAL (operands[2]) + 1); +}") + +;; Just like movdi_ldd, but with relaxation annotations. +(define_insn "tls_tlsdesc_ldd" + [(set (match_operand:DI 0 "register_operand" "=r") + (unspec:DI [(mem:DI (unspec:SI + [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "symbolic_operand" "")] + UNSPEC_TLS_TLSDESC_LDD_AUX))] + UNSPEC_TLS_TLSDESC_LDD))] + "" + "ldd #tlsdesc(%a3)@(%1,%2), %0" + [(set_attr "length" "4") + (set_attr "type" "gload")]) + +(define_insn "tls_tlsoff_ld" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (unspec:SI + [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "symbolic_operand" "")] + UNSPEC_TLS_TLSOFF_LD)))] + "" + "ld #tlsoff(%a3)@(%1,%2), %0" + [(set_attr "length" "4") + (set_attr "type" "gload")]) + +(define_insn "tls_lddi" + [(set (match_operand:DI 0 "register_operand" "=r") + (unspec:DI [(match_operand:SI 1 "symbolic_operand" "") + (match_operand:SI 2 "register_operand" "d")] + UNSPEC_TLS_LDDI))] + "" + "lddi @(%2, #gottlsdesc12(%a1)), %0" + [(set_attr "length" "4") + (set_attr "type" "gload")]) diff --git a/gcc/config/frv/frv.opt b/gcc/config/frv/frv.opt new file mode 100644 index 000000000..f44de1ff9 --- /dev/null +++ b/gcc/config/frv/frv.opt @@ -0,0 +1,199 @@ +; Options for the FR-V port of the compiler. + +; Copyright (C) 2005, 2007 Free Software Foundation, Inc. +; +; This file is part of GCC. +; +; GCC is free software; you can redistribute it and/or modify it under +; the terms of the GNU General Public License as published by the Free +; Software Foundation; either version 3, or (at your option) any later +; version. +; +; GCC is distributed in the hope that it will be useful, but WITHOUT ANY +; WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +; for more details. +; +; You should have received a copy of the GNU General Public License +; along with GCC; see the file COPYING3. If not see +; <http://www.gnu.org/licenses/>. + +macc-4 +Target Report RejectNegative Mask(ACC_4) +Use 4 media accumulators + +macc-8 +Target Report RejectNegative InverseMask(ACC_4, ACC_8) +Use 8 media accumulators + +malign-labels +Target Report Mask(ALIGN_LABELS) +Enable label alignment optimizations + +malloc-cc +Target Report RejectNegative Mask(ALLOC_CC) +Dynamically allocate cc registers + +; We used to default the branch cost to 2, but it was changed it to 1 to avoid +; generating SCC instructions and or/and-ing them together, and then doing the +; branch on the result, which collectively generate much worse code. +mbranch-cost= +Target RejectNegative Joined UInteger Var(frv_branch_cost_int) Init(1) +Set the cost of branches + +mcond-exec +Target Report Mask(COND_EXEC) +Enable conditional execution other than moves/scc + +mcond-exec-insns= +Target RejectNegative Joined UInteger Var(frv_condexec_insns) Init(8) +Change the maximum length of conditionally-executed sequences + +mcond-exec-temps= +Target RejectNegative Joined UInteger Var(frv_condexec_temps) Init(4) +Change the number of temporary registers that are available to conditionally-executed sequences + +mcond-move +Target Report Mask(COND_MOVE) +Enable conditional moves + +mcpu= +Target RejectNegative Joined +Set the target CPU type + +mdebug +Target Undocumented Var(TARGET_DEBUG) + +mdebug-arg +Target Undocumented Var(TARGET_DEBUG_ARG) + +mdebug-addr +Target Undocumented Var(TARGET_DEBUG_ADDR) + +mdebug-cond-exec +Target Undocumented Var(TARGET_DEBUG_COND_EXEC) + +mdebug-loc +Target Undocumented Var(TARGET_DEBUG_LOC) + +mdebug-stack +Target Undocumented Var(TARGET_DEBUG_STACK) + +mdouble +Target Report Mask(DOUBLE) +Use fp double instructions + +mdword +Target Report Mask(DWORD) +Change the ABI to allow double word insns + +mfdpic +Target Report Mask(FDPIC) +Enable Function Descriptor PIC mode + +mfixed-cc +Target Report RejectNegative InverseMask(ALLOC_CC, FIXED_CC) +Just use icc0/fcc0 + +mfpr-32 +Target Report RejectNegative Mask(FPR_32) +Only use 32 FPRs + +mfpr-64 +Target Report RejectNegative InverseMask(FPR_32, FPR_64) +Use 64 FPRs + +mgpr-32 +Target Report RejectNegative Mask(GPR_32) +Only use 32 GPRs + +mgpr-64 +Target Report RejectNegative InverseMask(GPR_32, GPR_64) +Use 64 GPRs + +mgprel-ro +Target Report Mask(GPREL_RO) +Enable use of GPREL for read-only data in FDPIC + +mhard-float +Target Report RejectNegative InverseMask(SOFT_FLOAT, HARD_FLOAT) +Use hardware floating point + +minline-plt +Target Report Mask(INLINE_PLT) +Enable inlining of PLT in function calls + +mlibrary-pic +Target Report Mask(LIBPIC) +Enable PIC support for building libraries + +mlinked-fp +Target Report Mask(LINKED_FP) +Follow the EABI linkage requirements + +mlong-calls +Target Report Mask(LONG_CALLS) +Disallow direct calls to global functions + +mmedia +Target Report Mask(MEDIA) +Use media instructions + +mmuladd +Target Report Mask(MULADD) +Use multiply add/subtract instructions + +mmulti-cond-exec +Target Report Mask(MULTI_CE) +Enable optimizing &&/|| in conditional execution + +mnested-cond-exec +Target Report Mask(NESTED_CE) +Enable nested conditional execution optimizations + +; Not used by the compiler proper. +mno-eflags +Target RejectNegative +Do not mark ABI switches in e_flags + +moptimize-membar +Target Report Mask(OPTIMIZE_MEMBAR) +Remove redundant membars + +mpack +Target Report Mask(PACK) +Pack VLIW instructions + +mscc +Target Report Mask(SCC) +Enable setting GPRs to the result of comparisons + +msched-lookahead= +Target RejectNegative Joined UInteger Var(frv_sched_lookahead) Init(4) +Change the amount of scheduler lookahead + +msoft-float +Target Report RejectNegative Mask(SOFT_FLOAT) +Use software floating point + +mTLS +Target Report RejectNegative Mask(BIG_TLS) +Assume a large TLS segment + +mtls +Target Report RejectNegative InverseMask(BIG_TLS) +Do not assume a large TLS segment + +; Not used by the compiler proper. +mtomcat-stats +Target +Cause gas to print tomcat statistics + +; Not used by the compiler proper. +multilib-library-pic +Target RejectNegative +Link with the library-pic libraries + +mvliw-branch +Target Report Mask(VLIW_BRANCH) +Allow branches to be packed with other instructions diff --git a/gcc/config/frv/frvbegin.c b/gcc/config/frv/frvbegin.c new file mode 100644 index 000000000..963ebd091 --- /dev/null +++ b/gcc/config/frv/frvbegin.c @@ -0,0 +1,157 @@ +/* Frv initialization file linked before all user modules + Copyright (C) 1999, 2000, 2003, 2004, 2009 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. + + This file was originally taken from the file crtstuff.c in the + main compiler directory, and simplified. */ + +#include "defaults.h" +#include <stddef.h> +#include "unwind-dw2-fde.h" +#include "gbl-ctors.h" + +/* Declare a pointer to void function type. */ +#define STATIC static + +#ifdef __FRV_UNDERSCORE__ +#define UNDERSCORE "_" +#else +#define UNDERSCORE "" +#endif + +#define INIT_SECTION_NEG_ONE(SECTION, FLAGS, NAME) \ +__asm__ (".section " SECTION "," FLAGS "\n\t" \ + ".globl " UNDERSCORE NAME "\n\t" \ + ".type " UNDERSCORE NAME ",@object\n\t" \ + ".p2align 2\n" \ + UNDERSCORE NAME ":\n\t" \ + ".word -1\n\t" \ + ".previous") + +#define INIT_SECTION(SECTION, FLAGS, NAME) \ +__asm__ (".section " SECTION "," FLAGS "\n\t" \ + ".globl " UNDERSCORE NAME "\n\t" \ + ".type " UNDERSCORE NAME ",@object\n\t" \ + ".p2align 2\n" \ + UNDERSCORE NAME ":\n\t" \ + ".previous") + +/* Beginning of .ctor/.dtor sections that provides a list of constructors and + destructors to run. */ + +INIT_SECTION_NEG_ONE (".ctors", "\"aw\"", "__CTOR_LIST__"); +INIT_SECTION_NEG_ONE (".dtors", "\"aw\"", "__DTOR_LIST__"); + +/* Beginning of .eh_frame section that provides all of the exception handling + tables. */ + +INIT_SECTION (".eh_frame", "\"aw\"", "__EH_FRAME_BEGIN__"); + +#if ! __FRV_FDPIC__ +/* In FDPIC, the linker itself generates this. */ +/* Beginning of .rofixup section that provides a list of pointers that we + need to adjust. */ + +INIT_SECTION (".rofixup", "\"a\"", "__ROFIXUP_LIST__"); +#endif /* __FRV_FDPIC__ */ + +extern void __frv_register_eh(void) __attribute__((__constructor__)); +extern void __frv_deregister_eh(void) __attribute__((__destructor__)); + +extern func_ptr __EH_FRAME_BEGIN__[]; + +/* Register the exception handling table as the first constructor. */ +void +__frv_register_eh (void) +{ + static struct object object; + if (__register_frame_info) + __register_frame_info (__EH_FRAME_BEGIN__, &object); +} + +/* Note, do not declare __{,de}register_frame_info weak as it seems + to interfere with the pic support. */ + +/* Unregister the exception handling table as a deconstructor. */ +void +__frv_deregister_eh (void) +{ + static int completed = 0; + + if (completed) + return; + + if (__deregister_frame_info) + __deregister_frame_info (__EH_FRAME_BEGIN__); + + completed = 1; +} + +/* Run the global destructors. */ +void +__do_global_dtors (void) +{ + static func_ptr *p = __DTOR_LIST__ + 1; + while (*p) + { + p++; + (*(p-1)) (); + } +} + +/* Run the global constructors. */ +void +__do_global_ctors (void) +{ + unsigned long nptrs = (unsigned long) __CTOR_LIST__[0]; + unsigned i; + + if (nptrs == (unsigned long)-1) + for (nptrs = 0; __CTOR_LIST__[nptrs + 1] != 0; nptrs++); + + for (i = nptrs; i >= 1; i--) + __CTOR_LIST__[i] (); + + atexit (__do_global_dtors); +} + +/* Subroutine called automatically by `main'. + Compiling a global function named `main' + produces an automatic call to this function at the beginning. + + For many systems, this routine calls __do_global_ctors. + For systems which support a .init section we use the .init section + to run __do_global_ctors, so we need not do anything here. */ + +void +__main (void) +{ + /* Support recursive calls to `main': run initializers just once. */ + static int initialized; + if (! initialized) + { + initialized = 1; + __do_global_ctors (); + } +} diff --git a/gcc/config/frv/frvend.c b/gcc/config/frv/frvend.c new file mode 100644 index 000000000..04b880df3 --- /dev/null +++ b/gcc/config/frv/frvend.c @@ -0,0 +1,70 @@ +/* Frv initialization file linked after all user modules + Copyright (C) 1999, 2000, 2003, 2004, 2009 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include "defaults.h" +#include <stddef.h> +#include "unwind-dw2-fde.h" + +#ifdef __FRV_UNDERSCORE__ +#define UNDERSCORE "_" +#else +#define UNDERSCORE "" +#endif + +#define FINI_SECTION_ZERO(SECTION, FLAGS, NAME) \ +__asm__ (".section " SECTION "," FLAGS "\n\t" \ + ".globl " UNDERSCORE NAME "\n\t" \ + ".type " UNDERSCORE NAME ",@object\n\t" \ + ".p2align 2\n" \ + UNDERSCORE NAME ":\n\t" \ + ".word 0\n\t" \ + ".previous") + +#define FINI_SECTION(SECTION, FLAGS, NAME) \ +__asm__ (".section " SECTION "," FLAGS "\n\t" \ + ".globl " UNDERSCORE NAME "\n\t" \ + ".type " UNDERSCORE NAME ",@object\n\t" \ + ".p2align 2\n" \ + UNDERSCORE NAME ":\n\t" \ + ".previous") + +/* End of .ctor/.dtor sections that provides a list of constructors and + destructors to run. */ + +FINI_SECTION_ZERO (".ctors", "\"aw\"", "__CTOR_END__"); +FINI_SECTION_ZERO (".dtors", "\"aw\"", "__DTOR_END__"); + +/* End of .eh_frame section that provides all of the exception handling + tables. */ + +FINI_SECTION_ZERO (".eh_frame", "\"aw\"", "__FRAME_END__"); + +#if ! __FRV_FDPIC__ +/* In FDPIC, the linker itself generates this. */ +/* End of .rofixup section that provides a list of pointers that we + need to adjust. */ + +FINI_SECTION (".rofixup", "\"a\"", "__ROFIXUP_END__"); +#endif /* __FRV_FDPIC__ */ diff --git a/gcc/config/frv/lib1funcs.asm b/gcc/config/frv/lib1funcs.asm new file mode 100644 index 000000000..d1ffcab61 --- /dev/null +++ b/gcc/config/frv/lib1funcs.asm @@ -0,0 +1,269 @@ +/* Library functions. + Copyright (C) 2000, 2003, 2008, 2009 Free Software Foundation, Inc. + Contributed by Red Hat, Inc. + + This file is part of GCC. + + GCC is free software ; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include <frv-asm.h> + + +#ifdef L_cmpll +/* icc0 = __cmpll (long long a, long long b) */ + + .globl EXT(__cmpll) + .type EXT(__cmpll),@function + .text + .p2align 4 +EXT(__cmpll): + cmp gr8, gr10, icc0 + ckeq icc0, cc4 + P(ccmp) gr9, gr11, cc4, 1 + ret +.Lend: + .size EXT(__cmpll),.Lend-EXT(__cmpll) +#endif /* L_cmpll */ + +#ifdef L_cmpf +/* icc0 = __cmpf (float a, float b) */ +/* Note, because this function returns the result in ICC0, it means it can't + handle NaNs. */ + + .globl EXT(__cmpf) + .type EXT(__cmpf),@function + .text + .p2align 4 +EXT(__cmpf): +#ifdef __FRV_HARD_FLOAT__ /* floating point instructions available */ + movgf gr8, fr0 + P(movgf) gr9, fr1 + setlos #1, gr8 + fcmps fr0, fr1, fcc0 + P(fcklt) fcc0, cc0 + fckeq fcc0, cc1 + csub gr0, gr8, gr8, cc0, 1 + cmov gr0, gr8, cc1, 1 + cmpi gr8, 0, icc0 + ret +#else /* no floating point instructions available */ + movsg lr, gr4 + addi sp, #-16, sp + sti gr4, @(sp, 8) + st fp, @(sp, gr0) + mov sp, fp + call EXT(__cmpsf2) + cmpi gr8, #0, icc0 + ldi @(sp, 8), gr4 + movgs gr4, lr + ld @(sp,gr0), fp + addi sp, #16, sp + ret +#endif +.Lend: + .size EXT(__cmpf),.Lend-EXT(__cmpf) +#endif + +#ifdef L_cmpd +/* icc0 = __cmpd (double a, double b) */ +/* Note, because this function returns the result in ICC0, it means it can't + handle NaNs. */ + + .globl EXT(__cmpd) + .type EXT(__cmpd),@function + .text + .p2align 4 +EXT(__cmpd): + movsg lr, gr4 + addi sp, #-16, sp + sti gr4, @(sp, 8) + st fp, @(sp, gr0) + mov sp, fp + call EXT(__cmpdf2) + cmpi gr8, #0, icc0 + ldi @(sp, 8), gr4 + movgs gr4, lr + ld @(sp,gr0), fp + addi sp, #16, sp + ret +.Lend: + .size EXT(__cmpd),.Lend-EXT(__cmpd) +#endif + +#ifdef L_addll +/* gr8,gr9 = __addll (long long a, long long b) */ +/* Note, gcc will never call this function, but it is present in case an + ABI program calls it. */ + + .globl EXT(__addll) + .type EXT(__addll),@function + .text + .p2align +EXT(__addll): + addcc gr9, gr11, gr9, icc0 + addx gr8, gr10, gr8, icc0 + ret +.Lend: + .size EXT(__addll),.Lend-EXT(__addll) +#endif + +#ifdef L_subll +/* gr8,gr9 = __subll (long long a, long long b) */ +/* Note, gcc will never call this function, but it is present in case an + ABI program calls it. */ + + .globl EXT(__subll) + .type EXT(__subll),@function + .text + .p2align 4 +EXT(__subll): + subcc gr9, gr11, gr9, icc0 + subx gr8, gr10, gr8, icc0 + ret +.Lend: + .size EXT(__subll),.Lend-EXT(__subll) +#endif + +#ifdef L_andll +/* gr8,gr9 = __andll (long long a, long long b) */ +/* Note, gcc will never call this function, but it is present in case an + ABI program calls it. */ + + .globl EXT(__andll) + .type EXT(__andll),@function + .text + .p2align 4 +EXT(__andll): + P(and) gr9, gr11, gr9 + P2(and) gr8, gr10, gr8 + ret +.Lend: + .size EXT(__andll),.Lend-EXT(__andll) +#endif + +#ifdef L_orll +/* gr8,gr9 = __orll (long long a, long long b) */ +/* Note, gcc will never call this function, but it is present in case an + ABI program calls it. */ + + .globl EXT(__orll) + .type EXT(__orll),@function + .text + .p2align 4 +EXT(__orll): + P(or) gr9, gr11, gr9 + P2(or) gr8, gr10, gr8 + ret +.Lend: + .size EXT(__orll),.Lend-EXT(__orll) +#endif + +#ifdef L_xorll +/* gr8,gr9 = __xorll (long long a, long long b) */ +/* Note, gcc will never call this function, but it is present in case an + ABI program calls it. */ + + .globl EXT(__xorll) + .type EXT(__xorll),@function + .text + .p2align 4 +EXT(__xorll): + P(xor) gr9, gr11, gr9 + P2(xor) gr8, gr10, gr8 + ret +.Lend: + .size EXT(__xorll),.Lend-EXT(__xorll) +#endif + +#ifdef L_notll +/* gr8,gr9 = __notll (long long a) */ +/* Note, gcc will never call this function, but it is present in case an + ABI program calls it. */ + + .globl EXT(__notll) + .type EXT(__notll),@function + .text + .p2align 4 +EXT(__notll): + P(not) gr9, gr9 + P2(not) gr8, gr8 + ret +.Lend: + .size EXT(__notll),.Lend-EXT(__notll) +#endif + +#ifdef L_cmov +/* (void) __cmov (char *dest, const char *src, size_t len) */ +/* + * void __cmov (char *dest, const char *src, size_t len) + * { + * size_t i; + * + * if (dest < src || dest > src+len) + * { + * for (i = 0; i < len; i++) + * dest[i] = src[i]; + * } + * else + * { + * while (len-- > 0) + * dest[len] = src[len]; + * } + * } + */ + + .globl EXT(__cmov) + .type EXT(__cmov),@function + .text + .p2align 4 +EXT(__cmov): + P(cmp) gr8, gr9, icc0 + add gr9, gr10, gr4 + P(cmp) gr8, gr4, icc1 + bc icc0, 0, .Lfwd + bls icc1, 0, .Lback +.Lfwd: + /* move bytes in a forward direction */ + P(setlos) #0, gr5 + cmp gr0, gr10, icc0 + P(subi) gr9, #1, gr9 + P2(subi) gr8, #1, gr8 + bnc icc0, 0, .Lret +.Lfloop: + /* forward byte move loop */ + addi gr5, #1, gr5 + P(ldsb) @(gr9, gr5), gr4 + cmp gr5, gr10, icc0 + P(stb) gr4, @(gr8, gr5) + bc icc0, 0, .Lfloop + ret +.Lbloop: + /* backward byte move loop body */ + ldsb @(gr9,gr10),gr4 + stb gr4,@(gr8,gr10) +.Lback: + P(cmpi) gr10, #0, icc0 + addi gr10, #-1, gr10 + bne icc0, 0, .Lbloop +.Lret: + ret +.Lend: + .size EXT(__cmov),.Lend-EXT(__cmov) +#endif diff --git a/gcc/config/frv/libgcc-frv.ver b/gcc/config/frv/libgcc-frv.ver new file mode 100644 index 000000000..6e27b4f9b --- /dev/null +++ b/gcc/config/frv/libgcc-frv.ver @@ -0,0 +1,73 @@ +# Copyright (C) 2004 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +GCC_3.4 { + # frv abi symbol names + __ftod + __ftoi + __ftoui + __dtoi + __ftoui + __dtoui + __ftoll + __dtoll + __ftoull + __dtoull + __itof + __lltof + __dtof + __itod + __lltof + __lltod + __addd + __subd + __muld + __divd + __addf + __subf + __mulf + __divf + __sllll + __srlll + __srall + __addll + __subll + __mulll + __umulll + __divll + __udivll + __modll + __umodll + __cmpll + __cmpf + __cmpd + __andll + __orll + __xorll + __notll + __cmov + __cmovd + __cmovh + __cmovw + __modi + __uitod + __uitof + __ulltod + __ulltof + __umodi +} diff --git a/gcc/config/frv/linux.h b/gcc/config/frv/linux.h new file mode 100644 index 000000000..4f11618b4 --- /dev/null +++ b/gcc/config/frv/linux.h @@ -0,0 +1,75 @@ +/* Target macros for the FRV Linux port of GCC. + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007, 2010 + Free Software Foundation, Inc. + Contributed by Red Hat Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +#ifndef __FRV_LINUX_H__ +#define __FRV_LINUX_H__ + +#undef SUBTARGET_DRIVER_SELF_SPECS +#define SUBTARGET_DRIVER_SELF_SPECS \ + "%{!mno-fdpic:-mfdpic}", + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: %{pg|p|profile:gcrt1.o%s;pie:Scrt1.o%s;:crt1.o%s}} \ + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" + +#define GLIBC_DYNAMIC_LINKER "/lib/ld.so.1" + +#undef LINK_SPEC +#define LINK_SPEC "\ + %{mfdpic: -m elf32frvfd -z text} %{shared} %{pie} \ + %{!shared: %{!static: \ + %{rdynamic:-export-dynamic} \ + -dynamic-linker " LINUX_DYNAMIC_LINKER "} \ + %{static}}" + +/* Support for compile-time default CPU. */ +#define OPTION_DEFAULT_SPECS \ + {"cpu", "%{!mcpu=*:-mcpu=%(VALUE)}" } + +/* Define OS-specific predefined preprocessor macros. */ +#define TARGET_OS_CPP_BUILTINS() \ + do { \ + builtin_define ("__gnu_linux__"); \ + builtin_define_std ("linux"); \ + builtin_define_std ("unix"); \ + builtin_assert ("system=linux"); \ + } while (0) + +#define HAS_INIT_SECTION 1 +#define INIT_SECTION_ASM_OP "\t.section .init,\"ax\"" +#define FINI_SECTION_ASM_OP "\t.section .fini,\"ax\"" + +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ +asm (SECTION_OP); \ +asm ("ldi.p @(fp,4), gr15 ! call " #FUNC); \ +asm (TEXT_SECTION_ASM_OP); + +#undef INVOKE__main + +#undef Twrite +#define Twrite __write + +#endif /* __FRV_LINUX_H__ */ diff --git a/gcc/config/frv/modi.c b/gcc/config/frv/modi.c new file mode 100644 index 000000000..d5a91fc0f --- /dev/null +++ b/gcc/config/frv/modi.c @@ -0,0 +1,4 @@ +int __modi (int a, int b) +{ + return a % b; +} diff --git a/gcc/config/frv/predicates.md b/gcc/config/frv/predicates.md new file mode 100644 index 000000000..aeef3d823 --- /dev/null +++ b/gcc/config/frv/predicates.md @@ -0,0 +1,1543 @@ +;; Predicate definitions for Frv. +;; Copyright (C) 2005, 2007, 2010 Free Software Foundation, Inc. +;; +;; This file is part of GCC. +;; +;; GCC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; GCC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;; Return true if operand is a GPR register. + +(define_predicate "integer_register_operand" + (match_code "reg,subreg") +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_AP_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return 1 is OP is a memory operand, or will be turned into one by +;; reload. + +(define_predicate "frv_load_operand" + (match_code "reg,subreg,mem") +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (reload_in_progress) + { + rtx tmp = op; + if (GET_CODE (tmp) == SUBREG) + tmp = SUBREG_REG (tmp); + if (GET_CODE (tmp) == REG + && REGNO (tmp) >= FIRST_PSEUDO_REGISTER) + op = reg_equiv_memory_loc[REGNO (tmp)]; + } + + return op && memory_operand (op, mode); +}) + +;; Return true if operand is a GPR register. Do not allow SUBREG's +;; here, in order to prevent a combine bug. + +(define_predicate "gpr_no_subreg_operand" + (match_code "reg") +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return 1 if operand is a GPR register or a FPR register. + +(define_predicate "gpr_or_fpr_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER) + return TRUE; + + return FALSE; +}) + +;; Return 1 if operand is a GPR register or 12-bit signed immediate. + +(define_predicate "gpr_or_int12_operand" + (match_code "reg,subreg,const_int,const") +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE (INTVAL (op), -2048, 2047); + + if (got12_operand (op, mode)) + return true; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return 1 if operand is a GPR register, or a FPR register, or a 12 +;; bit signed immediate. + +(define_predicate "gpr_fpr_or_int12_operand" + (match_code "reg,subreg,const_int") +{ + int regno; + + if (GET_CODE (op) == CONST_INT) + return IN_RANGE (INTVAL (op), -2048, 2047); + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER) + return TRUE; + + return FALSE; +}) + +;; Return 1 if operand is a register or 10-bit signed immediate. + +(define_predicate "gpr_or_int10_operand" + (match_code "reg,subreg,const_int") +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE (INTVAL (op), -512, 511); + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return 1 if operand is a register or an integer immediate. + +(define_predicate "gpr_or_int_operand" + (match_code "reg,subreg,const_int") +{ + if (GET_CODE (op) == CONST_INT) + return TRUE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return true if operand is something that can be an input for a move +;; operation. + +(define_predicate "move_source_operand" + (match_code "reg,subreg,const_int,mem,const_double,const,symbol_ref,label_ref") +{ + rtx subreg; + enum rtx_code code; + + switch (GET_CODE (op)) + { + default: + break; + + case CONST_INT: + case CONST_DOUBLE: + return immediate_operand (op, mode); + + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + subreg = SUBREG_REG (op); + code = GET_CODE (subreg); + if (code == MEM) + return frv_legitimate_address_p_1 (mode, XEXP (subreg, 0), + reload_completed, FALSE, FALSE); + + return (code == REG); + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return TRUE; + + case MEM: + return frv_legitimate_memory_operand (op, mode, FALSE); + } + + return FALSE; +}) + +;; Return true if operand is something that can be an output for a +;; move operation. + +(define_predicate "move_destination_operand" + (match_code "reg,subreg,mem") +{ + rtx subreg; + enum rtx_code code; + + switch (GET_CODE (op)) + { + default: + break; + + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + subreg = SUBREG_REG (op); + code = GET_CODE (subreg); + if (code == MEM) + return frv_legitimate_address_p_1 (mode, XEXP (subreg, 0), + reload_completed, FALSE, FALSE); + + return (code == REG); + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return TRUE; + + case MEM: + return frv_legitimate_memory_operand (op, mode, FALSE); + } + + return FALSE; +}) + +;; Return true if we the operand is a valid destination for a movcc_fp +;; instruction. This means rejecting fcc_operands, since we need +;; scratch registers to write to them. + +(define_predicate "movcc_fp_destination_operand" + (match_code "reg,subreg,mem") +{ + if (fcc_operand (op, mode)) + return FALSE; + + return move_destination_operand (op, mode); +}) + +;; Return true if operand is something that can be an input for a +;; conditional move operation. + +(define_predicate "condexec_source_operand" + (match_code "reg,subreg,const_int,mem,const_double") +{ + rtx subreg; + enum rtx_code code; + + switch (GET_CODE (op)) + { + default: + break; + + case CONST_INT: + case CONST_DOUBLE: + return ZERO_P (op); + + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + subreg = SUBREG_REG (op); + code = GET_CODE (subreg); + if (code == MEM) + return frv_legitimate_address_p_1 (mode, XEXP (subreg, 0), + reload_completed, TRUE, FALSE); + + return (code == REG); + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return TRUE; + + case MEM: + return frv_legitimate_memory_operand (op, mode, TRUE); + } + + return FALSE; +}) + +;; Return true if operand is something that can be an output for a +;; conditional move operation. + +(define_predicate "condexec_dest_operand" + (match_code "reg,subreg,mem") +{ + rtx subreg; + enum rtx_code code; + + switch (GET_CODE (op)) + { + default: + break; + + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + subreg = SUBREG_REG (op); + code = GET_CODE (subreg); + if (code == MEM) + return frv_legitimate_address_p_1 (mode, XEXP (subreg, 0), + reload_completed, TRUE, FALSE); + + return (code == REG); + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return TRUE; + + case MEM: + return frv_legitimate_memory_operand (op, mode, TRUE); + } + + return FALSE; +}) + +;; Return true if operand is a register of any flavor or a 0 of the +;; appropriate type. + +(define_predicate "reg_or_0_operand" + (match_code "reg,subreg,const_int,const_double") +{ + switch (GET_CODE (op)) + { + default: + break; + + case REG: + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return register_operand (op, mode); + + case CONST_INT: + case CONST_DOUBLE: + return ZERO_P (op); + } + + return FALSE; +}) + +;; Return true if operand is the link register. + +(define_predicate "lr_operand" + (match_code "reg") +{ + if (GET_CODE (op) != REG) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (REGNO (op) != LR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER) + return FALSE; + + return TRUE; +}) + +;; Return true if operand is a gpr register or a valid memory operand. + +(define_predicate "gpr_or_memory_operand" + (match_code "reg,subreg,mem") +{ + return (integer_register_operand (op, mode) + || frv_legitimate_memory_operand (op, mode, FALSE)); +}) + +;; Return true if operand is a gpr register, a valid memory operand, +;; or a memory operand that can be made valid using an additional gpr +;; register. + +(define_predicate "gpr_or_memory_operand_with_scratch" + (match_code "reg,subreg,mem") +{ + rtx addr; + + if (gpr_or_memory_operand (op, mode)) + return TRUE; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode) + return FALSE; + + addr = XEXP (op, 0); + + if (GET_CODE (addr) != PLUS) + return FALSE; + + if (!integer_register_operand (XEXP (addr, 0), Pmode)) + return FALSE; + + if (GET_CODE (XEXP (addr, 1)) != CONST_INT) + return FALSE; + + return TRUE; +}) + +;; Return true if operand is a fpr register or a valid memory +;; operation. + +(define_predicate "fpr_or_memory_operand" + (match_code "reg,subreg,mem") +{ + return (fpr_operand (op, mode) + || frv_legitimate_memory_operand (op, mode, FALSE)); +}) + +;; Return 1 if operand is a 12-bit signed immediate. + +(define_predicate "int12_operand" + (match_code "const_int") +{ + if (GET_CODE (op) != CONST_INT) + return FALSE; + + return IN_RANGE (INTVAL (op), -2048, 2047); +}) + +;; Return 1 if operand is an integer constant that takes 2 +;; instructions to load up and can be split into sethi/setlo +;; instructions.. + +(define_predicate "int_2word_operand" + (match_code "const_int,const_double,symbol_ref,label_ref,const") +{ + HOST_WIDE_INT value; + REAL_VALUE_TYPE rv; + long l; + + switch (GET_CODE (op)) + { + default: + break; + + case LABEL_REF: + if (TARGET_FDPIC) + return FALSE; + + return (flag_pic == 0); + + case CONST: + if (flag_pic || TARGET_FDPIC) + return FALSE; + + op = XEXP (op, 0); + if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT) + op = XEXP (op, 0); + return GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF; + + case SYMBOL_REF: + if (TARGET_FDPIC) + return FALSE; + + /* small data references are already 1 word */ + return (flag_pic == 0) && (! SYMBOL_REF_SMALL_P (op)); + + case CONST_INT: + return ! IN_RANGE (INTVAL (op), -32768, 32767); + + case CONST_DOUBLE: + if (GET_MODE (op) == SFmode) + { + REAL_VALUE_FROM_CONST_DOUBLE (rv, op); + REAL_VALUE_TO_TARGET_SINGLE (rv, l); + value = l; + return ! IN_RANGE (value, -32768, 32767); + } + else if (GET_MODE (op) == VOIDmode) + { + value = CONST_DOUBLE_LOW (op); + return ! IN_RANGE (value, -32768, 32767); + } + break; + } + + return FALSE; +}) + +;; Return true if operand is the uClinux PIC register. + +(define_predicate "fdpic_operand" + (match_code "reg") +{ + if (!TARGET_FDPIC) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (REGNO (op) != FDPIC_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER) + return FALSE; + + return TRUE; +}) + +;; TODO: Add a comment here. + +(define_predicate "fdpic_fptr_operand" + (match_code "reg") +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + if (GET_CODE (op) != REG) + return FALSE; + if (REGNO (op) != FDPIC_FPTR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER) + return FALSE; + return TRUE; +}) + +;; An address operand that may use a pair of registers, an addressing +;; mode that we reject in general. + +(define_predicate "ldd_address_operand" + (match_code "reg,subreg,plus") +{ + if (GET_MODE (op) != mode && GET_MODE (op) != VOIDmode) + return FALSE; + + return frv_legitimate_address_p_1 (DImode, op, reload_completed, FALSE, TRUE); +}) + +;; TODO: Add a comment here. + +(define_predicate "got12_operand" + (match_code "const") +{ + struct frv_unspec unspec; + + if (frv_const_unspec_p (op, &unspec)) + switch (unspec.reloc) + { + case R_FRV_GOT12: + case R_FRV_GOTOFF12: + case R_FRV_FUNCDESC_GOT12: + case R_FRV_FUNCDESC_GOTOFF12: + case R_FRV_GPREL12: + case R_FRV_TLSMOFF12: + return true; + } + return false; +}) + +;; Return true if OP is a valid const-unspec expression. + +(define_predicate "const_unspec_operand" + (match_code "const") +{ + struct frv_unspec unspec; + + return frv_const_unspec_p (op, &unspec); +}) + +;; Return true if operand is an icc register. + +(define_predicate "icc_operand" + (match_code "reg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + return ICC_OR_PSEUDO_P (regno); +}) + +;; Return true if operand is an fcc register. + +(define_predicate "fcc_operand" + (match_code "reg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + return FCC_OR_PSEUDO_P (regno); +}) + +;; Return true if operand is either an fcc or icc register. + +(define_predicate "cc_operand" + (match_code "reg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (CC_OR_PSEUDO_P (regno)) + return TRUE; + + return FALSE; +}) + +;; Return true if operand is an integer CCR register. + +(define_predicate "icr_operand" + (match_code "reg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + return ICR_OR_PSEUDO_P (regno); +}) + +;; Return true if operand is an fcc register. + +(define_predicate "fcr_operand" + (match_code "reg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + return FCR_OR_PSEUDO_P (regno); +}) + +;; Return true if operand is either an fcc or icc register. + +(define_predicate "cr_operand" + (match_code "reg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (CR_OR_PSEUDO_P (regno)) + return TRUE; + + return FALSE; +}) + +;; Return true if operand is a FPR register. + +(define_predicate "fpr_operand" + (match_code "reg,subreg") +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + return FPR_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return true if operand is an even GPR or FPR register. + +(define_predicate "even_reg_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (regno >= FIRST_PSEUDO_REGISTER) + return TRUE; + + if (GPR_P (regno)) + return (((regno - GPR_FIRST) & 1) == 0); + + if (FPR_P (regno)) + return (((regno - FPR_FIRST) & 1) == 0); + + return FALSE; +}) + +;; Return true if operand is an odd GPR register. + +(define_predicate "odd_reg_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + /* Assume that reload will give us an even register. */ + if (regno >= FIRST_PSEUDO_REGISTER) + return FALSE; + + if (GPR_P (regno)) + return (((regno - GPR_FIRST) & 1) != 0); + + if (FPR_P (regno)) + return (((regno - FPR_FIRST) & 1) != 0); + + return FALSE; +}) + +;; Return true if operand is an even GPR register. + +(define_predicate "even_gpr_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (regno >= FIRST_PSEUDO_REGISTER) + return TRUE; + + if (! GPR_P (regno)) + return FALSE; + + return (((regno - GPR_FIRST) & 1) == 0); +}) + +;; Return true if operand is an odd GPR register. + +(define_predicate "odd_gpr_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + /* Assume that reload will give us an even register. */ + if (regno >= FIRST_PSEUDO_REGISTER) + return FALSE; + + if (! GPR_P (regno)) + return FALSE; + + return (((regno - GPR_FIRST) & 1) != 0); +}) + +;; Return true if operand is a quad aligned FPR register. + +(define_predicate "quad_fpr_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (regno >= FIRST_PSEUDO_REGISTER) + return TRUE; + + if (! FPR_P (regno)) + return FALSE; + + return (((regno - FPR_FIRST) & 3) == 0); +}) + +;; Return true if operand is an even FPR register. + +(define_predicate "even_fpr_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + if (regno >= FIRST_PSEUDO_REGISTER) + return TRUE; + + if (! FPR_P (regno)) + return FALSE; + + return (((regno - FPR_FIRST) & 1) == 0); +}) + +;; Return true if operand is an odd FPR register. + +(define_predicate "odd_fpr_operand" + (match_code "reg,subreg") +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + regno = REGNO (op); + /* Assume that reload will give us an even register. */ + if (regno >= FIRST_PSEUDO_REGISTER) + return FALSE; + + if (! FPR_P (regno)) + return FALSE; + + return (((regno - FPR_FIRST) & 1) != 0); +}) + +;; Return true if operand is a 2 word memory address that can be +;; loaded in one instruction to load or store. We assume the stack +;; and frame pointers are suitably aligned, and variables in the small +;; data area. FIXME -- at some we should recognize other globals and +;; statics. We can't assume that any old pointer is aligned, given +;; that arguments could be passed on an odd word on the stack and the +;; address taken and passed through to another function. + +(define_predicate "dbl_memory_one_insn_operand" + (match_code "mem") +{ + rtx addr; + rtx addr_reg; + + if (! TARGET_DWORD) + return FALSE; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD) + return FALSE; + + addr = XEXP (op, 0); + if (GET_CODE (addr) == REG) + addr_reg = addr; + + else if (GET_CODE (addr) == PLUS) + { + rtx addr0 = XEXP (addr, 0); + rtx addr1 = XEXP (addr, 1); + + if (GET_CODE (addr0) != REG) + return FALSE; + + if (got12_operand (addr1, VOIDmode)) + return TRUE; + + if (GET_CODE (addr1) != CONST_INT) + return FALSE; + + if ((INTVAL (addr1) & 7) != 0) + return FALSE; + + addr_reg = addr0; + } + + else + return FALSE; + + if (addr_reg == frame_pointer_rtx || addr_reg == stack_pointer_rtx) + return TRUE; + + return FALSE; +}) + +;; Return true if operand is a 2 word memory address that needs to use +;; two instructions to load or store. + +(define_predicate "dbl_memory_two_insn_operand" + (match_code "mem") +{ + if (GET_CODE (op) != MEM) + return FALSE; + + if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD) + return FALSE; + + if (! TARGET_DWORD) + return TRUE; + + return ! dbl_memory_one_insn_operand (op, mode); +}) + +;; Return true if operand is a memory reference suitable for a call. + +(define_predicate "call_operand" + (match_code "reg,subreg,const_int,const,symbol_ref") +{ + if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT) + return FALSE; + + if (GET_CODE (op) == SYMBOL_REF) + return !TARGET_LONG_CALLS || SYMBOL_REF_LOCAL_P (op); + + /* Note this doesn't allow reg+reg or reg+imm12 addressing (which should + never occur anyway), but prevents reload from not handling the case + properly of a call through a pointer on a function that calls + vfork/setjmp, etc. due to the need to flush all of the registers to stack. */ + return gpr_or_int12_operand (op, mode); +}) + +;; Return true if operand is a memory reference suitable for a +;; sibcall. + +(define_predicate "sibcall_operand" + (match_code "reg,subreg,const_int,const") +{ + if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT) + return FALSE; + + /* Note this doesn't allow reg+reg or reg+imm12 addressing (which should + never occur anyway), but prevents reload from not handling the case + properly of a call through a pointer on a function that calls + vfork/setjmp, etc. due to the need to flush all of the registers to stack. */ + return gpr_or_int12_operand (op, mode); +}) + +;; Return 1 if operand is an integer constant with the bottom 16 bits +;; clear. + +(define_predicate "upper_int16_operand" + (match_code "const_int") +{ + if (GET_CODE (op) != CONST_INT) + return FALSE; + + return ((INTVAL (op) & 0xffff) == 0); +}) + +;; Return 1 if operand is a 16-bit unsigned immediate. + +(define_predicate "uint16_operand" + (match_code "const_int") +{ + if (GET_CODE (op) != CONST_INT) + return FALSE; + + return IN_RANGE (INTVAL (op), 0, 0xffff); +}) + +;; Returns 1 if OP is either a SYMBOL_REF or a constant. + +(define_predicate "symbolic_operand" + (match_code "symbol_ref,const,const_int") +{ + enum rtx_code c = GET_CODE (op); + + if (c == CONST) + { + /* Allow (const:SI (plus:SI (symbol_ref) (const_int))). */ + return GET_MODE (op) == SImode + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT; + } + + return c == SYMBOL_REF || c == CONST_INT; +}) + +;; Return true if operator is a kind of relational operator. + +(define_predicate "relational_operator" + (match_code "eq,ne,le,lt,ge,gt,leu,ltu,geu,gtu") +{ + return (integer_relational_operator (op, mode) + || float_relational_operator (op, mode)); +}) + +;; Return true if OP is a relational operator suitable for CCmode, +;; CC_UNSmode or CC_NZmode. + +(define_predicate "integer_relational_operator" + (match_code "eq,ne,le,lt,ge,gt,leu,ltu,geu,gtu") +{ + if (mode != VOIDmode && mode != GET_MODE (op)) + return FALSE; + + /* The allowable relations depend on the mode of the ICC register. */ + switch (GET_CODE (op)) + { + default: + return FALSE; + + case EQ: + case NE: + case LT: + case GE: + return (GET_MODE (XEXP (op, 0)) == CC_NZmode + || GET_MODE (XEXP (op, 0)) == CCmode); + + case LE: + case GT: + return GET_MODE (XEXP (op, 0)) == CCmode; + + case GTU: + case GEU: + case LTU: + case LEU: + return (GET_MODE (XEXP (op, 0)) == CC_NZmode + || GET_MODE (XEXP (op, 0)) == CC_UNSmode); + } +}) + +;; Return true if operator is a floating point relational operator. + +(define_predicate "float_relational_operator" + (match_code "eq,ne,le,lt,ge,gt") +{ + if (mode != VOIDmode && mode != GET_MODE (op)) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case EQ: case NE: + case LE: case LT: + case GE: case GT: +#if 0 + case UEQ: case UNE: + case ULE: case ULT: + case UGE: case UGT: + case ORDERED: + case UNORDERED: +#endif + return GET_MODE (XEXP (op, 0)) == CC_FPmode; + } +}) + +;; Return true if operator is EQ/NE of a conditional execution +;; register. + +(define_predicate "ccr_eqne_operator" + (match_code "eq,ne") +{ + enum machine_mode op_mode = GET_MODE (op); + rtx op0; + rtx op1; + int regno; + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case EQ: + case NE: + break; + } + + op1 = XEXP (op, 1); + if (op1 != const0_rtx) + return FALSE; + + op0 = XEXP (op, 0); + if (GET_CODE (op0) != REG) + return FALSE; + + regno = REGNO (op0); + if (op_mode == CC_CCRmode && CR_OR_PSEUDO_P (regno)) + return TRUE; + + return FALSE; +}) + +;; Return true if operator is a minimum or maximum operator (both +;; signed and unsigned). + +(define_predicate "minmax_operator" + (match_code "smin,smax,umin,umax") +{ + if (mode != VOIDmode && mode != GET_MODE (op)) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case SMIN: + case SMAX: + case UMIN: + case UMAX: + break; + } + + return TRUE; +}) + +;; Return true if operator is an integer binary operator that can +;; executed conditionally and takes 1 cycle. + +(define_predicate "condexec_si_binary_operator" + (match_code "plus,minus,and,ior,xor,ashift,ashiftrt,lshiftrt") +{ + enum machine_mode op_mode = GET_MODE (op); + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case PLUS: + case MINUS: + case AND: + case IOR: + case XOR: + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + return TRUE; + } +}) + +;; Return true if operator is an integer binary operator that can be +;; executed conditionally by a media instruction. + +(define_predicate "condexec_si_media_operator" + (match_code "and,ior,xor") +{ + enum machine_mode op_mode = GET_MODE (op); + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case AND: + case IOR: + case XOR: + return TRUE; + } +}) + +;; Return true if operator is an integer division operator that can +;; executed conditionally. + +(define_predicate "condexec_si_divide_operator" + (match_code "div,udiv") +{ + enum machine_mode op_mode = GET_MODE (op); + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case DIV: + case UDIV: + return TRUE; + } +}) + +;; Return true if operator is an integer unary operator that can +;; executed conditionally. + +(define_predicate "condexec_si_unary_operator" + (match_code "not,neg") +{ + enum machine_mode op_mode = GET_MODE (op); + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case NEG: + case NOT: + return TRUE; + } +}) + +;; Return true if operator is an addition or subtraction +;; expression. Such expressions can be evaluated conditionally by +;; floating-point instructions. + +(define_predicate "condexec_sf_add_operator" + (match_code "plus,minus") +{ + enum machine_mode op_mode = GET_MODE (op); + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case PLUS: + case MINUS: + return TRUE; + } +}) + +;; Return true if operator is a conversion-type expression that can be +;; evaluated conditionally by floating-point instructions. + +(define_predicate "condexec_sf_conv_operator" + (match_code "abs,neg") +{ + enum machine_mode op_mode = GET_MODE (op); + + if (mode != VOIDmode && op_mode != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case NEG: + case ABS: + return TRUE; + } +}) + +;; Return true if OP is an integer binary operator that can be +;; combined with a (set ... (compare:CC_NZ ...)) pattern. + +(define_predicate "intop_compare_operator" + (match_code "plus,minus,and,ior,xor,ashift,ashiftrt,lshiftrt") +{ + if (mode != VOIDmode && GET_MODE (op) != mode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case PLUS: + case MINUS: + case AND: + case IOR: + case XOR: + case ASHIFTRT: + case LSHIFTRT: + return GET_MODE (op) == SImode; + } +}) + +;; Return 1 if operand is a register or 6-bit signed immediate. + +(define_predicate "fpr_or_int6_operand" + (match_code "reg,subreg,const_int") +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE (INTVAL (op), -32, 31); + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + { + if (GET_CODE (SUBREG_REG (op)) != REG) + return register_operand (op, mode); + + op = SUBREG_REG (op); + } + + if (GET_CODE (op) != REG) + return FALSE; + + return FPR_OR_PSEUDO_P (REGNO (op)); +}) + +;; Return 1 if operand is a 6-bit signed immediate. + +(define_predicate "int6_operand" + (match_code "const_int") +{ + if (GET_CODE (op) != CONST_INT) + return FALSE; + + return IN_RANGE (INTVAL (op), -32, 31); +}) + +;; Return 1 if operand is a 5-bit signed immediate. + +(define_predicate "int5_operand" + (match_code "const_int") +{ + return GET_CODE (op) == CONST_INT && IN_RANGE (INTVAL (op), -16, 15); +}) + +;; Return 1 if operand is a 5-bit unsigned immediate. + +(define_predicate "uint5_operand" + (match_code "const_int") +{ + return GET_CODE (op) == CONST_INT && IN_RANGE (INTVAL (op), 0, 31); +}) + +;; Return 1 if operand is a 4-bit unsigned immediate. + +(define_predicate "uint4_operand" + (match_code "const_int") +{ + return GET_CODE (op) == CONST_INT && IN_RANGE (INTVAL (op), 0, 15); +}) + +;; Return 1 if operand is a 1-bit unsigned immediate (0 or 1). + +(define_predicate "uint1_operand" + (match_code "const_int") +{ + return GET_CODE (op) == CONST_INT && IN_RANGE (INTVAL (op), 0, 1); +}) + +;; Return 1 if operand is a valid ACC register number. + +(define_predicate "acc_operand" + (match_code "reg,subreg") +{ + return ((mode == VOIDmode || mode == GET_MODE (op)) + && REG_P (op) && ACC_P (REGNO (op)) + && ((REGNO (op) - ACC_FIRST) & ~ACC_MASK) == 0); +}) + +;; Return 1 if operand is a valid even ACC register number. + +(define_predicate "even_acc_operand" + (match_code "reg,subreg") +{ + return acc_operand (op, mode) && ((REGNO (op) - ACC_FIRST) & 1) == 0; +}) + +;; Return 1 if operand is zero or four. + +(define_predicate "quad_acc_operand" + (match_code "reg,subreg") +{ + return acc_operand (op, mode) && ((REGNO (op) - ACC_FIRST) & 3) == 0; +}) + +;; Return 1 if operand is a valid ACCG register number. + +(define_predicate "accg_operand" + (match_code "reg,subreg") +{ + return ((mode == VOIDmode || mode == GET_MODE (op)) + && REG_P (op) && ACCG_P (REGNO (op)) + && ((REGNO (op) - ACCG_FIRST) & ~ACC_MASK) == 0); +}) diff --git a/gcc/config/frv/t-frv b/gcc/config/frv/t-frv new file mode 100644 index 000000000..0c58bb167 --- /dev/null +++ b/gcc/config/frv/t-frv @@ -0,0 +1,113 @@ +# Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# Name of assembly file containing libgcc1 functions. +# This entry must be present, but it can be empty if the target does +# not need any assembler functions to support its code generation. +# +# Alternatively if assembler functions *are* needed then define the +# entries below: +CROSS_LIBGCC1 = libgcc1-asm.a +LIB1ASMSRC = frv/lib1funcs.asm +LIB1ASMFUNCS = _cmpll _cmpf _cmpd _addll _subll _andll _orll _xorll _notll _cmov +LIB2FUNCS_EXTRA = cmovh.c cmovw.c cmovd.c modi.c umodi.c uitof.c uitod.c ulltof.c ulltod.c + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +# If any special flags are necessary when building libgcc2 put them here. +TARGET_LIBGCC2_CFLAGS = + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#include "config/frv/frv-abi.h"' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#include "config/frv/frv-abi.h"' > dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +cmovh.c: $(srcdir)/config/frv/cmovh.c + $(LN_S) $(srcdir)/config/frv/cmovh.c . + +cmovw.c: $(srcdir)/config/frv/cmovw.c + $(LN_S) $(srcdir)/config/frv/cmovw.c . + +cmovd.c: $(srcdir)/config/frv/cmovd.c + $(LN_S) $(srcdir)/config/frv/cmovd.c . + +modi.c: $(srcdir)/config/frv/modi.c + $(LN_S) $(srcdir)/config/frv/modi.c . + +umodi.c: $(srcdir)/config/frv/umodi.c + $(LN_S) $(srcdir)/config/frv/umodi.c . + +uitof.c: $(srcdir)/config/frv/uitof.c + $(LN_S) $(srcdir)/config/frv/uitof.c . + +uitod.c: $(srcdir)/config/frv/uitod.c + $(LN_S) $(srcdir)/config/frv/uitod.c . + +ulltof.c: $(srcdir)/config/frv/ulltof.c + $(LN_S) $(srcdir)/config/frv/ulltof.c . + +ulltod.c: $(srcdir)/config/frv/ulltod.c + $(LN_S) $(srcdir)/config/frv/ulltod.c . + +# Build frvbegin.o and frvend.o +EXTRA_MULTILIB_PARTS=frvbegin.o frvend.o + +# Compile two additional files that are linked with every program +# linked using GCC on systems using COFF or ELF, for the sake of C++ +# constructors. + +FRVSTUFF_CFLAGS = $(TARGET_LIBGCC2_CFLAGS) + +$(T)frvbegin$(objext): $(srcdir)/config/frv/frvbegin.c $(GCC_PASSES) \ + $(CONFIG_H) defaults.h unwind-dw2-fde.h gbl-ctors.h + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) $(FRVSTUFF_CFLAGS) \ + -c $(srcdir)/config/frv/frvbegin.c -o $(T)frvbegin$(objext) + +$(T)frvend$(objext): $(srcdir)/config/frv/frvend.c $(GCC_PASSES) \ + $(CONFIG_H) defaults.h unwind-dw2-fde.h gbl-ctors.h + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) $(FRVSTUFF_CFLAGS) \ + -c $(srcdir)/config/frv/frvend.c -o $(T)frvend$(objext) + +# Enable the following if multilibs are needed. +# See gcc/genmultilib, gcc/gcc.texi and gcc/tm.texi for a +# description of the options and their values. +# +#MULTILIB_OPTIONS = mcpu=fr500/mcpu=tomcat/mcpu=simple/mcpu=frv msoft-float mdword/mno-dword +#MULTILIB_DIRNAMES = fr500 tomcat simple frv nof dw no-dw +#MULTILIB_MATCHES = mcpu?simple=mcpu?fr300 mno-double=mcpu?fr500 mcpu?frv=mdouble +#MULTILIB_EXCEPTIONS = *mcpu=simple/*msoft-float* *mcpu=frv/*msoft-float* +#MULTILIB_EXTRA_OPTS = mlibrary-pic + +MULTILIB_OPTIONS = mcpu=fr400/mcpu=fr550 mno-pack mlibrary-pic/mfdpic +MULTILIB_DIRNAMES = fr400 fr550 unpacked pic fdpic +MULTILIB_MATCHES = mcpu?simple=mcpu?fr300 \ + mlibrary-pic=multilib-library-pic \ + mcpu?fr400=mcpu?fr405 mcpu?fr400=mcpu?fr450 +MULTILIB_EXCEPTIONS = mcpu=frv/mno-pack* mcpu=simple/mno-pack* + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +EXTRA_HEADERS = $(srcdir)/config/frv/frv-asm.h diff --git a/gcc/config/frv/t-linux b/gcc/config/frv/t-linux new file mode 100644 index 000000000..13eb5369e --- /dev/null +++ b/gcc/config/frv/t-linux @@ -0,0 +1,33 @@ +# Copyright (C) 2004, 2007 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# We don't want multilibs. +MULTILIB_OPTIONS= +MULTILIB_DIRNAMES= +MULTILIB_MATCHES= +MULTILIB_EXCEPTIONS= +MULTILIB_EXTRA_OPTS= + +# We don't use frvbegin.o or frvend.o. +EXTRA_MULTILIB_PARTS = + +CRTSTUFF_T_CFLAGS = -fPIC +TARGET_LIBGCC2_CFLAGS = -fPIC + +SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver \ + $(srcdir)/config/frv/libgcc-frv.ver diff --git a/gcc/config/frv/uitod.c b/gcc/config/frv/uitod.c new file mode 100644 index 000000000..14290ab6b --- /dev/null +++ b/gcc/config/frv/uitod.c @@ -0,0 +1,4 @@ +double __uitod (unsigned int a) +{ + return a; +} diff --git a/gcc/config/frv/uitof.c b/gcc/config/frv/uitof.c new file mode 100644 index 000000000..059bc7c74 --- /dev/null +++ b/gcc/config/frv/uitof.c @@ -0,0 +1,4 @@ +float __uitof (unsigned int a) +{ + return a; +} diff --git a/gcc/config/frv/ulltod.c b/gcc/config/frv/ulltod.c new file mode 100644 index 000000000..e6bee1208 --- /dev/null +++ b/gcc/config/frv/ulltod.c @@ -0,0 +1,4 @@ +double __ulltod (unsigned long long a) +{ + return a; +} diff --git a/gcc/config/frv/ulltof.c b/gcc/config/frv/ulltof.c new file mode 100644 index 000000000..29cdfd4d2 --- /dev/null +++ b/gcc/config/frv/ulltof.c @@ -0,0 +1,4 @@ +float __ulltof (unsigned long long a) +{ + return a; +} diff --git a/gcc/config/frv/umodi.c b/gcc/config/frv/umodi.c new file mode 100644 index 000000000..4ffe5ad81 --- /dev/null +++ b/gcc/config/frv/umodi.c @@ -0,0 +1,4 @@ +unsigned int __umodi (unsigned int a, unsigned int b) +{ + return a % b; +} |