summaryrefslogtreecommitdiff
path: root/gcc/config/frv
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/frv')
-rw-r--r--gcc/config/frv/cmovd.c51
-rw-r--r--gcc/config/frv/cmovh.c47
-rw-r--r--gcc/config/frv/cmovw.c51
-rw-r--r--gcc/config/frv/constraints.md174
-rw-r--r--gcc/config/frv/frv-asm.h48
-rw-r--r--gcc/config/frv/frv-modes.def34
-rw-r--r--gcc/config/frv/frv-protos.h195
-rw-r--r--gcc/config/frv/frv.c9680
-rw-r--r--gcc/config/frv/frv.h2188
-rw-r--r--gcc/config/frv/frv.md8022
-rw-r--r--gcc/config/frv/frv.opt199
-rw-r--r--gcc/config/frv/frvbegin.c157
-rw-r--r--gcc/config/frv/frvend.c70
-rw-r--r--gcc/config/frv/lib1funcs.asm269
-rw-r--r--gcc/config/frv/libgcc-frv.ver73
-rw-r--r--gcc/config/frv/linux.h75
-rw-r--r--gcc/config/frv/modi.c4
-rw-r--r--gcc/config/frv/predicates.md1543
-rw-r--r--gcc/config/frv/t-frv113
-rw-r--r--gcc/config/frv/t-linux33
-rw-r--r--gcc/config/frv/uitod.c4
-rw-r--r--gcc/config/frv/uitof.c4
-rw-r--r--gcc/config/frv/ulltod.c4
-rw-r--r--gcc/config/frv/ulltof.c4
-rw-r--r--gcc/config/frv/umodi.c4
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 = &reg_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;
+}