OSDN Git Service

* config/bfin/bfin-modes.def: New file.
authorbernds <bernds@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 5 Apr 2005 11:26:48 +0000 (11:26 +0000)
committerbernds <bernds@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 5 Apr 2005 11:26:48 +0000 (11:26 +0000)
* config/bfin/bfin-protos.h: New file.
* config/bfin/bfin.c: New file.
* config/bfin/bfin.h: New file.
* config/bfin/bfin.md: New file.
* config/bfin/bfin.opt: New file.
* config/bfin/crti.s: New file.
* config/bfin/crtn.s: New file.
* config/bfin/elf.h: New file.
* config/bfin/lib1funcs.asm: New file.
* config/bfin/predicates.md: New file.
* config/bfin/t-bfin: New file.
* config/bfin/t-bfin-elf: New file.
* doc/extend.texi (exception_handler, kspisusp, nesting, nmi_handler):
Document new attributes.
(interrupt, interrupt_handler, saveall): Update documentation for
these attributes.
* doc/install.texi (Specific): Add entry for the Blackfin.
* doc/invoke.texi (Blackfin Options): New section.
* doc/md.texi (Blackfin family): New section to document constraints.
* config.gcc: Add bfin*-* and bfin*-elf configurations.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@97622 138bc75d-0d04-0410-961f-82ee72b054a4

19 files changed:
gcc/ChangeLog
gcc/config.gcc
gcc/config/bfin/bfin-modes.def [new file with mode: 0644]
gcc/config/bfin/bfin-protos.h [new file with mode: 0644]
gcc/config/bfin/bfin.c [new file with mode: 0644]
gcc/config/bfin/bfin.h [new file with mode: 0644]
gcc/config/bfin/bfin.md [new file with mode: 0644]
gcc/config/bfin/bfin.opt [new file with mode: 0644]
gcc/config/bfin/crti.s [new file with mode: 0644]
gcc/config/bfin/crtn.s [new file with mode: 0644]
gcc/config/bfin/elf.h [new file with mode: 0644]
gcc/config/bfin/lib1funcs.asm [new file with mode: 0644]
gcc/config/bfin/predicates.md [new file with mode: 0644]
gcc/config/bfin/t-bfin [new file with mode: 0644]
gcc/config/bfin/t-bfin-elf [new file with mode: 0644]
gcc/doc/extend.texi
gcc/doc/install.texi
gcc/doc/invoke.texi
gcc/doc/md.texi

index 0fac52c..3296cd4 100644 (file)
@@ -1,3 +1,27 @@
+2005-04-05  Bernd Schmidt  <bernd.schmidt@analog.com>
+
+       * config/bfin/bfin-modes.def: New file.
+       * config/bfin/bfin-protos.h: New file.
+       * config/bfin/bfin.c: New file.
+       * config/bfin/bfin.h: New file.
+       * config/bfin/bfin.md: New file.
+       * config/bfin/bfin.opt: New file.
+       * config/bfin/crti.s: New file.
+       * config/bfin/crtn.s: New file.
+       * config/bfin/elf.h: New file.
+       * config/bfin/lib1funcs.asm: New file.
+       * config/bfin/predicates.md: New file.
+       * config/bfin/t-bfin: New file.
+       * config/bfin/t-bfin-elf: New file.
+       * doc/extend.texi (exception_handler, kspisusp, nesting, nmi_handler):
+       Document new attributes.
+       (interrupt, interrupt_handler, saveall): Update documentation for
+       these attributes.
+       * doc/install.texi (Specific): Add entry for the Blackfin.
+       * doc/invoke.texi (Blackfin Options): New section.
+       * doc/md.texi (Blackfin family): New section to document constraints.
+       * config.gcc: Add bfin*-* and bfin*-elf configurations.
+
 2005-04-05  Olivier Hainque  <hainque@adacore.com>
        
        * config/mips/iris6.h (DWARF_FRAME_RETURN_COLUMN): Redefine to
index d8c914f..4468992 100644 (file)
@@ -248,6 +248,9 @@ arm*-*-*)
        cpu_type=arm
        extra_headers="mmintrin.h"
        ;;
+bfin*-*)
+       cpu_type=bfin
+       ;;
 ep9312*-*-*)
        cpu_type=arm
        ;;
@@ -727,6 +730,16 @@ avr-*-*)
        tm_file="avr/avr.h dbxelf.h"
        use_fixproto=yes
        ;;
+bfin*-elf*)
+       tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h"
+        tmake_file=bfin/t-bfin-elf
+        use_collect2=no
+        ;;  
+bfin*-*)
+       tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h"
+        tmake_file=bfin/t-bfin
+        use_collect2=no
+        ;;  
 c4x-*-rtems* | tic4x-*-rtems*)
        tmake_file="c4x/t-c4x t-rtems c4x/t-rtems"
        tm_file="c4x/c4x.h c4x/rtems.h rtems.h"
diff --git a/gcc/config/bfin/bfin-modes.def b/gcc/config/bfin/bfin-modes.def
new file mode 100644 (file)
index 0000000..18ed315
--- /dev/null
@@ -0,0 +1,25 @@
+/* Definitions of target machine for GNU compiler, for Blackfin.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   Contributed by Analog Devices.
+
+   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 2, 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 COPYING.  If not, write to the
+   Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA.  */
+
+/* PDImode for the 40 bit accumulators.  */
+PARTIAL_INT_MODE (DI);
+
+VECTOR_MODE (INT, HI, 2); /* V2HI */
diff --git a/gcc/config/bfin/bfin-protos.h b/gcc/config/bfin/bfin-protos.h
new file mode 100644 (file)
index 0000000..e75046a
--- /dev/null
@@ -0,0 +1,88 @@
+/* Prototypes for Blackfin functions used in the md file & elsewhere.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+   This file is part of GNU CC.
+
+   GNU CC 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 2, or (at your option)
+   any later version.
+
+   GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+   the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* Function prototypes that cannot exist in bfin.h due to dependency
+   complications.  */
+#ifndef GCC_BFIN_PROTOS_H
+#define GCC_BFIN_PROTOS_H
+
+#define Mmode enum machine_mode
+
+extern rtx function_arg (CUMULATIVE_ARGS *, Mmode, tree, int);
+extern void function_arg_advance (CUMULATIVE_ARGS *, Mmode, tree, int);
+extern bool function_arg_regno_p (int);
+
+extern const char *output_load_immediate (rtx *);
+extern const char *output_casesi_internal (rtx *);
+extern char *bfin_asm_long (void);
+extern char *bfin_asm_short (void);
+extern int log2constp (unsigned HOST_WIDE_INT);
+
+extern rtx legitimize_address (rtx, rtx, Mmode);
+extern int hard_regno_mode_ok (int, Mmode);
+extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx);         
+extern int bfin_frame_pointer_required (void);
+extern HOST_WIDE_INT bfin_initial_elimination_offset (int, int);
+
+extern int effective_address_32bit_p (rtx, Mmode);
+extern int symbolic_reference_mentioned_p (rtx);
+extern rtx bfin_gen_compare (rtx, Mmode);
+extern void expand_move (rtx *, Mmode);
+extern void bfin_expand_call (rtx, rtx, rtx, int);
+extern bool bfin_expand_strmov (rtx, rtx, rtx, rtx);
+
+extern void conditional_register_usage (void);
+extern int bfin_register_move_cost (enum machine_mode, enum reg_class,
+                                   enum reg_class);
+extern int bfin_memory_move_cost (enum machine_mode, enum reg_class, int in);
+extern enum reg_class secondary_input_reload_class (enum reg_class, Mmode,
+                                                   rtx);
+extern enum reg_class secondary_output_reload_class (enum reg_class, Mmode,
+                                                    rtx);
+extern char *section_asm_op_1 (SECT_ENUM_T);
+extern char *section_asm_op (SECT_ENUM_T);
+extern void override_options (void);
+extern void print_operand (FILE *,  rtx, char);
+extern void print_address_operand (FILE *, rtx);
+extern void split_di (rtx [], int, rtx [], rtx []);
+extern int split_load_immediate (rtx []);
+extern rtx legitimize_pic_address (rtx, rtx);
+extern void emit_pic_move (rtx *, Mmode);
+extern void override_options (void);
+extern void asm_conditional_branch (rtx, rtx *, int, int);
+extern rtx bfin_gen_compare (rtx, Mmode);
+
+extern int bfin_return_in_memory (tree);
+extern void initialize_trampoline (rtx, rtx, rtx);
+extern bool bfin_legitimate_address_p (Mmode, rtx, int);
+extern rtx bfin_va_arg (tree, tree);
+
+extern void bfin_expand_prologue (void);
+extern void bfin_expand_epilogue (int, int);
+extern int push_multiple_operation (rtx, Mmode);
+extern int pop_multiple_operation (rtx, Mmode);
+extern void output_push_multiple (rtx, rtx *);
+extern void output_pop_multiple (rtx, rtx *);
+extern int bfin_hard_regno_rename_ok (unsigned int, unsigned int);
+extern rtx bfin_return_addr_rtx (int);
+#undef  Mmode 
+
+#endif
+
diff --git a/gcc/config/bfin/bfin.c b/gcc/config/bfin/bfin.c
new file mode 100644 (file)
index 0000000..be3beb3
--- /dev/null
@@ -0,0 +1,2728 @@
+/* The Blackfin code generation auxilary output file.
+   Copyright (C) 2005  Free Software Foundation, Inc.
+   Contributed by Analog Devices.
+
+   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 2, 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 COPYING.  If not, write to
+   the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "tree.h"
+#include "flags.h"
+#include "except.h"
+#include "function.h"
+#include "input.h"
+#include "target.h"
+#include "target-def.h"
+#include "expr.h"
+#include "toplev.h"
+#include "recog.h"
+#include "ggc.h"
+#include "integrate.h"
+#include "bfin-protos.h"
+#include "tm-preds.h"
+#include "gt-bfin.h"
+
+/* Test and compare insns in bfin.md store the information needed to
+   generate branch and scc insns here.  */
+rtx bfin_compare_op0, bfin_compare_op1;
+
+/* RTX for condition code flag register and RETS register */
+extern GTY(()) rtx bfin_cc_rtx;
+extern GTY(()) rtx bfin_rets_rtx;
+rtx bfin_cc_rtx, bfin_rets_rtx;
+
+int max_arg_registers = 0;
+
+/* Arrays used when emitting register names.  */
+const char *short_reg_names[]  =  SHORT_REGISTER_NAMES;
+const char *high_reg_names[]   =  HIGH_REGISTER_NAMES;
+const char *dregs_pair_names[] =  DREGS_PAIR_NAMES;
+const char *byte_reg_names[]   =  BYTE_REGISTER_NAMES;
+
+static int arg_regs[] = FUNCTION_ARG_REGISTERS;
+
+const char *bfin_library_id_string;
+
+static void
+bfin_globalize_label (FILE *stream, const char *name)
+{
+  fputs (".global ", stream);
+  assemble_name (stream, name);
+  fputc (';',stream);
+  fputc ('\n',stream);
+}
+
+static void 
+output_file_start (void) 
+{
+  FILE *file = asm_out_file;
+  int i;
+
+  fprintf (file, ".file \"%s\";\n", input_filename);
+  
+  for (i = 0; arg_regs[i] >= 0; i++)
+    ;
+  max_arg_registers = i;       /* how many arg reg used  */
+}
+
+/* Called early in the compilation to conditionally modify
+   fixed_regs/call_used_regs.  */
+
+void 
+conditional_register_usage (void)
+{
+  /* initialize condition code flag register rtx */
+  bfin_cc_rtx = gen_rtx_REG (BImode, REG_CC);
+  bfin_rets_rtx = gen_rtx_REG (Pmode, REG_RETS);
+}
+
+/* Examine machine-dependent attributes of function type FUNTYPE and return its
+   type.  See the definition of E_FUNKIND.  */
+
+static e_funkind funkind (tree funtype)
+{
+  tree attrs = TYPE_ATTRIBUTES (funtype);
+  if (lookup_attribute ("interrupt_handler", attrs))
+    return INTERRUPT_HANDLER;
+  else if (lookup_attribute ("exception_handler", attrs))
+    return EXCPT_HANDLER;
+  else if (lookup_attribute ("nmi_handler", attrs))
+    return NMI_HANDLER;
+  else
+    return SUBROUTINE;
+}
+\f
+/* Stack frame layout. */
+
+/* Compute the number of DREGS to save with a push_multiple operation.
+   This could include registers that aren't modified in the function,
+   since push_multiple only takes a range of registers.  */
+
+static int
+n_dregs_to_save (void)
+{
+  unsigned i;
+
+  for (i = REG_R0; i <= REG_R7; i++)
+    {
+      if (regs_ever_live[i] && ! call_used_regs[i])
+       return REG_R7 - i + 1;
+
+      if (current_function_calls_eh_return)
+       {
+         unsigned j;
+         for (j = 0; ; j++)
+           {
+             unsigned test = EH_RETURN_DATA_REGNO (j);
+             if (test == INVALID_REGNUM)
+               break;
+             if (test == i)
+               return REG_R7 - i + 1;
+           }
+       }
+
+    }
+  return 0;
+}
+
+/* Like n_dregs_to_save, but compute number of PREGS to save.  */
+
+static int
+n_pregs_to_save (void)
+{
+  unsigned i;
+
+  for (i = REG_P0; i <= REG_P5; i++)
+    if ((regs_ever_live[i] && ! call_used_regs[i])
+       || (i == PIC_OFFSET_TABLE_REGNUM
+           && (current_function_uses_pic_offset_table
+               || (TARGET_ID_SHARED_LIBRARY && ! current_function_is_leaf))))
+      return REG_P5 - i + 1;
+  return 0;
+}
+
+/* Determine if we are going to save the frame pointer in the prologue.  */
+
+static bool
+must_save_fp_p (void)
+{
+  return (frame_pointer_needed || regs_ever_live[REG_FP]);
+}
+
+static bool
+stack_frame_needed_p (void)
+{
+  /* EH return puts a new return address into the frame using an
+     address relative to the frame pointer.  */
+  if (current_function_calls_eh_return)
+    return true;
+  return frame_pointer_needed;
+}
+
+/* Emit code to save registers in the prologue.  SAVEALL is nonzero if we
+   must save all registers; this is used for interrupt handlers.
+   SPREG contains (reg:SI REG_SP).  */
+
+static void
+expand_prologue_reg_save (rtx spreg, int saveall)
+{
+  int ndregs = saveall ? 8 : n_dregs_to_save ();
+  int npregs = saveall ? 6 : n_pregs_to_save ();
+  int dregno = REG_R7 + 1 - ndregs;
+  int pregno = REG_P5 + 1 - npregs;
+  int total = ndregs + npregs;
+  int i;
+  rtx pat, insn, val;
+
+  if (total == 0)
+    return;
+
+  val = GEN_INT (-total * 4);
+  pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total + 2));
+  XVECEXP (pat, 0, 0) = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, val),
+                                       UNSPEC_PUSH_MULTIPLE);
+  XVECEXP (pat, 0, total + 1) = gen_rtx_SET (VOIDmode, spreg,
+                                            gen_rtx_PLUS (Pmode, spreg,
+                                                          val));
+  RTX_FRAME_RELATED_P (XVECEXP (pat, 0, total + 1)) = 1;
+  for (i = 0; i < total; i++)
+    {
+      rtx memref = gen_rtx_MEM (word_mode,
+                               gen_rtx_PLUS (Pmode, spreg,
+                                             GEN_INT (- i * 4 - 4)));
+      rtx subpat;
+      if (ndregs > 0)
+       {
+         subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
+                                                              dregno++));
+         ndregs--;
+       }
+      else
+       {
+         subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
+                                                              pregno++));
+         npregs++;
+       }
+      XVECEXP (pat, 0, i + 1) = subpat;
+      RTX_FRAME_RELATED_P (subpat) = 1;
+    }
+  insn = emit_insn (pat);
+  RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Emit code to restore registers in the epilogue.  SAVEALL is nonzero if we
+   must save all registers; this is used for interrupt handlers.
+   SPREG contains (reg:SI REG_SP).  */
+
+static void
+expand_epilogue_reg_restore (rtx spreg, int saveall)
+{
+  int ndregs = saveall ? 8 : n_dregs_to_save ();
+  int npregs = saveall ? 6 : n_pregs_to_save ();
+  int total = ndregs + npregs;
+  int i, regno;
+  rtx pat, insn;
+
+  if (total == 0)
+    return;
+
+  pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total + 1));
+  XVECEXP (pat, 0, 0) = gen_rtx_SET (VOIDmode, spreg,
+                                    gen_rtx_PLUS (Pmode, spreg,
+                                                  GEN_INT (total * 4)));
+
+  if (npregs > 0)
+    regno = REG_P5 + 1;
+  else
+    regno = REG_R7 + 1;
+
+  for (i = 0; i < total; i++)
+    {
+      rtx addr = (i > 0
+                 ? gen_rtx_PLUS (Pmode, spreg, GEN_INT (i * 4))
+                 : spreg);
+      rtx memref = gen_rtx_MEM (word_mode, addr);
+
+      regno--;
+      XVECEXP (pat, 0, i + 1)
+       = gen_rtx_SET (VOIDmode, gen_rtx_REG (word_mode, regno), memref);
+
+      if (npregs > 0)
+       {
+         if (--npregs == 0)
+           regno = REG_R7 + 1;
+       }
+    }
+
+  insn = emit_insn (pat);
+  RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Perform any needed actions needed for a function that is receiving a
+   variable number of arguments.
+
+   CUM is as above.
+
+   MODE and TYPE are the mode and type of the current parameter.
+
+   PRETEND_SIZE is a variable that should be set to the amount of stack
+   that must be pushed by the prolog to pretend that our caller pushed
+   it.
+
+   Normally, this macro will push all remaining incoming registers on the
+   stack and set PRETEND_SIZE to the length of the registers pushed.  
+
+   Blackfin specific :
+   - VDSP C compiler manual (our ABI) says that a variable args function
+     should save the R0, R1 and R2 registers in the stack.
+   - The caller will always leave space on the stack for the
+     arguments that are passed in registers, so we dont have
+     to leave any extra space.
+   - now, the vastart pointer can access all arguments from the stack.  */
+
+static void
+setup_incoming_varargs (CUMULATIVE_ARGS *cum,
+                       enum machine_mode mode ATTRIBUTE_UNUSED,
+                       tree type ATTRIBUTE_UNUSED, int *pretend_size,
+                       int no_rtl)
+{
+  rtx mem;
+  int i;
+
+  if (no_rtl)
+    return;
+
+  /* The move for named arguments will be generated automatically by the
+     compiler.  We need to generate the move rtx for the unnamed arguments
+     if they are in the first 3 words.  We assume atleast 1 named argument
+     exists, so we never generate [ARGP] = R0 here.  */
+
+  for (i = cum->words + 1; i < max_arg_registers; i++)
+    {
+      mem = gen_rtx_MEM (Pmode,
+                        plus_constant (arg_pointer_rtx, (i * UNITS_PER_WORD)));
+      emit_move_insn (mem, gen_rtx_REG (Pmode, i));
+    }
+
+  *pretend_size = 0;
+}
+
+/* Value should be nonzero if functions must have frame pointers.
+   Zero means the frame pointer need not be set up (and parms may
+   be accessed via the stack pointer) in functions that seem suitable.  */
+
+int
+bfin_frame_pointer_required (void) 
+{
+  e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+
+  if (fkind != SUBROUTINE)
+    return 1;
+
+  /* We turn on on -fomit-frame-pointer if -momit-leaf-frame-pointer is used,
+     so we have to override it for non-leaf functions.  */
+  if (TARGET_OMIT_LEAF_FRAME_POINTER && ! current_function_is_leaf)
+    return 1;
+
+  return 0;
+}
+
+/* Return the number of registers pushed during the prologue.  */
+
+static int
+n_regs_saved_by_prologue (void)
+{
+  e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+  int n = n_dregs_to_save () + n_pregs_to_save ();
+
+  if (stack_frame_needed_p ())
+    /* We use a LINK instruction in this case.  */
+    n += 2;
+  else
+    {
+      if (must_save_fp_p ())
+       n++;
+      if (! current_function_is_leaf)
+       n++;
+    }
+
+  if (fkind != SUBROUTINE)
+    {
+      tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+      tree all = lookup_attribute ("saveall", attrs);
+      int i;
+
+      /* Increment once for ASTAT.  */
+      n++;
+
+      /* RETE/X/N.  */
+      if (lookup_attribute ("nesting", attrs))
+       n++;
+
+      for (i = REG_P7 + 1; i < REG_CC; i++)
+       if (all 
+           || regs_ever_live[i]
+           || (!leaf_function_p () && call_used_regs[i]))
+         n += i == REG_A0 || i == REG_A1 ? 2 : 1;
+    }
+  return n;
+}
+
+/* Return the offset between two registers, one to be eliminated, and the other
+   its replacement, at the start of a routine.  */
+
+HOST_WIDE_INT
+bfin_initial_elimination_offset (int from, int to)
+{
+  HOST_WIDE_INT offset = 0;
+
+  if (from == ARG_POINTER_REGNUM)
+    offset = n_regs_saved_by_prologue () * 4;
+
+  if (to == STACK_POINTER_REGNUM)
+    {
+      if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
+       offset += current_function_outgoing_args_size;
+      else if (current_function_outgoing_args_size)
+       offset += FIXED_STACK_AREA;
+
+      offset += get_frame_size ();
+    }
+
+  return offset;
+}
+
+/* Emit code to load a constant CONSTANT into register REG; setting
+   RTX_FRAME_RELATED_P on all insns we generate.  Make sure that the insns
+   we generate need not be split.  */
+
+static void
+frame_related_constant_load (rtx reg, HOST_WIDE_INT constant)
+{
+  rtx insn;
+  rtx cst = GEN_INT (constant);
+
+  if (constant >= -32768 && constant < 65536)
+    insn = emit_move_insn (reg, cst);
+  else
+    {
+      /* We don't call split_load_immediate here, since dwarf2out.c can get
+        confused about some of the more clever sequences it can generate.  */
+      insn = emit_insn (gen_movsi_high (reg, cst));
+      RTX_FRAME_RELATED_P (insn) = 1;
+      insn = emit_insn (gen_movsi_low (reg, reg, cst));
+    }
+  RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Generate efficient code to add a value to the frame pointer.  We
+   can use P1 as a scratch register.  Set RTX_FRAME_RELATED_P on the
+   generated insns if FRAME is nonzero.  */
+
+static void
+add_to_sp (rtx spreg, HOST_WIDE_INT value, int frame)
+{
+  if (value == 0)
+    return;
+
+  /* Choose whether to use a sequence using a temporary register, or
+     a sequence with multiple adds.  We can add a signed 7 bit value
+     in one instruction.  */
+  if (value > 120 || value < -120)
+    {
+      rtx tmpreg = gen_rtx_REG (SImode, REG_P1);
+      rtx insn;
+
+      if (frame)
+       frame_related_constant_load (tmpreg, value);
+      else
+       {
+         insn = emit_move_insn (tmpreg, GEN_INT (value));
+         if (frame)
+           RTX_FRAME_RELATED_P (insn) = 1;
+       }
+
+      insn = emit_insn (gen_addsi3 (spreg, spreg, tmpreg));
+      if (frame)
+       RTX_FRAME_RELATED_P (insn) = 1;
+    }
+  else
+    do
+      {
+       int size = value;
+       rtx insn;
+
+       if (size > 60)
+         size = 60;
+       else if (size < -60)
+         /* We could use -62, but that would leave the stack unaligned, so
+            it's no good.  */
+         size = -60;
+
+       insn = emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (size)));
+       if (frame)
+         RTX_FRAME_RELATED_P (insn) = 1;
+       value -= size;
+      }
+    while (value != 0);
+}
+
+/* Generate a LINK insn for a frame sized FRAME_SIZE.  If this constant
+   is too large, generate a sequence of insns that has the same effect.
+   SPREG contains (reg:SI REG_SP).  */
+
+static void
+emit_link_insn (rtx spreg, HOST_WIDE_INT frame_size)
+{
+  HOST_WIDE_INT link_size = frame_size;
+  rtx insn;
+  int i;
+
+  if (link_size > 262140)
+    link_size = 262140;
+
+  /* Use a LINK insn with as big a constant as possible, then subtract
+     any remaining size from the SP.  */
+  insn = emit_insn (gen_link (GEN_INT (-8 - link_size)));
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+    {
+      rtx set = XVECEXP (PATTERN (insn), 0, i);
+      if (GET_CODE (set) != SET)
+       abort ();
+      RTX_FRAME_RELATED_P (set) = 1;
+    }
+
+  frame_size -= link_size;
+
+  if (frame_size > 0)
+    {
+      /* Must use a call-clobbered PREG that isn't the static chain.  */
+      rtx tmpreg = gen_rtx_REG (Pmode, REG_P1);
+
+      frame_related_constant_load (tmpreg, -frame_size);
+      insn = emit_insn (gen_addsi3 (spreg, spreg, tmpreg));
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+}
+
+/* Return the number of bytes we must reserve for outgoing arguments
+   in the current function's stack frame.  */
+
+static HOST_WIDE_INT
+arg_area_size (void)
+{
+  if (current_function_outgoing_args_size)
+    {
+      if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
+       return current_function_outgoing_args_size;
+      else
+       return FIXED_STACK_AREA;
+    }
+  return 0;
+}
+
+/* Save RETS and FP, and allocate a stack frame.  */
+
+static void
+do_link (rtx spreg, HOST_WIDE_INT frame_size)
+{
+  frame_size += arg_area_size ();
+
+  if (stack_frame_needed_p ()
+      || (must_save_fp_p () && ! current_function_is_leaf))
+    emit_link_insn (spreg, frame_size);
+  else
+    {
+      if (! current_function_is_leaf)
+       {
+         rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
+                                           gen_rtx_PRE_DEC (Pmode, spreg)),
+                              bfin_rets_rtx);
+         rtx insn = emit_insn (pat);
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
+      if (must_save_fp_p ())
+       {
+         rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
+                                           gen_rtx_PRE_DEC (Pmode, spreg)),
+                              gen_rtx_REG (Pmode, REG_FP));
+         rtx insn = emit_insn (pat);
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
+      add_to_sp (spreg, -frame_size, 1);
+    }
+}
+
+/* Like do_link, but used for epilogues to deallocate the stack frame.  */
+
+static void
+do_unlink (rtx spreg, HOST_WIDE_INT frame_size)
+{
+  frame_size += arg_area_size ();
+
+  if (stack_frame_needed_p ())
+    emit_insn (gen_unlink ());
+  else 
+    {
+      rtx postinc = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, spreg));
+
+      add_to_sp (spreg, frame_size, 0);
+      if (must_save_fp_p ())
+       {
+         rtx fpreg = gen_rtx_REG (Pmode, REG_FP);
+         emit_move_insn (fpreg, postinc);
+         emit_insn (gen_rtx_USE (VOIDmode, fpreg));
+       }
+      if (! current_function_is_leaf)
+       {
+         emit_move_insn (bfin_rets_rtx, postinc);
+         emit_insn (gen_rtx_USE (VOIDmode, bfin_rets_rtx));
+       }
+    }
+}
+
+/* Generate a prologue suitable for a function of kind FKIND.  This is
+   called for interrupt and exception handler prologues.
+   SPREG contains (reg:SI REG_SP).  */
+
+static void
+expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
+{
+  int i;
+  HOST_WIDE_INT frame_size = get_frame_size ();
+  rtx predec1 = gen_rtx_PRE_DEC (SImode, spreg);
+  rtx predec = gen_rtx_MEM (SImode, predec1);
+  rtx insn;
+  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+  tree all = lookup_attribute ("saveall", attrs);
+  tree kspisusp = lookup_attribute ("kspisusp", attrs);
+
+  if (kspisusp)
+    {
+      insn = emit_move_insn (spreg, gen_rtx_REG (Pmode, REG_USP));
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+
+  /* We need space on the stack in case we need to save the argument
+     registers.  */
+  if (fkind == EXCPT_HANDLER)
+    {
+      insn = emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (-12)));
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+
+  insn = emit_move_insn (predec, gen_rtx_REG (SImode, REG_ASTAT));
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  expand_prologue_reg_save (spreg, all != NULL_TREE);
+
+  for (i = REG_P7 + 1; i < REG_CC; i++)
+    if (all 
+       || regs_ever_live[i]
+       || (!leaf_function_p () && call_used_regs[i]))
+      {
+       if (i == REG_A0 || i == REG_A1)
+         insn = emit_move_insn (gen_rtx_MEM (PDImode, predec1),
+                                gen_rtx_REG (PDImode, i));
+       else
+         insn = emit_move_insn (predec, gen_rtx_REG (SImode, i));
+       RTX_FRAME_RELATED_P (insn) = 1;
+      }
+
+  if (lookup_attribute ("nesting", attrs))
+    {
+      rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
+                                       : fkind == NMI_HANDLER ? REG_RETN
+                                       : REG_RETI));
+      insn = emit_move_insn (predec, srcreg);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+
+  do_link (spreg, frame_size);
+
+  if (fkind == EXCPT_HANDLER)
+    {
+      rtx r0reg = gen_rtx_REG (SImode, REG_R0);
+      rtx r1reg = gen_rtx_REG (SImode, REG_R1);
+      rtx r2reg = gen_rtx_REG (SImode, REG_R2);
+      rtx insn;
+
+      insn = emit_move_insn (r0reg, gen_rtx_REG (SImode, REG_SEQSTAT));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+                                           NULL_RTX);
+      insn = emit_insn (gen_ashrsi3 (r0reg, r0reg, GEN_INT (26)));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+                                           NULL_RTX);
+      insn = emit_insn (gen_ashlsi3 (r0reg, r0reg, GEN_INT (26)));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+                                           NULL_RTX);
+      insn = emit_move_insn (r1reg, spreg);
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+                                           NULL_RTX);
+      insn = emit_move_insn (r2reg, gen_rtx_REG (Pmode, REG_FP));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+                                           NULL_RTX);
+      insn = emit_insn (gen_addsi3 (r2reg, r2reg, GEN_INT (8)));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+                                           NULL_RTX);
+    }
+}
+
+/* Generate an epilogue suitable for a function of kind FKIND.  This is
+   called for interrupt and exception handler epilogues.
+   SPREG contains (reg:SI REG_SP).  */
+
+static void
+expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind) 
+{
+  int i;
+  rtx postinc1 = gen_rtx_POST_INC (SImode, spreg);
+  rtx postinc = gen_rtx_MEM (SImode, postinc1);
+  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+  tree all = lookup_attribute ("saveall", attrs);
+
+  /* A slightly crude technique to stop flow from trying to delete "dead"
+     insns.  */
+  MEM_VOLATILE_P (postinc) = 1;
+
+  do_unlink (spreg, get_frame_size ());
+
+  if (lookup_attribute ("nesting", attrs))
+    {
+      rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
+                                       : fkind == NMI_HANDLER ? REG_RETN
+                                       : REG_RETI));
+      emit_move_insn (srcreg, postinc);
+    }
+
+  for (i = REG_CC - 1; i > REG_P7; i--)
+    if (all
+       || regs_ever_live[i] 
+       || (!leaf_function_p () && call_used_regs[i]))
+      {
+       if (i == REG_A0 || i == REG_A1)
+         {
+           rtx mem = gen_rtx_MEM (PDImode, postinc1);
+           MEM_VOLATILE_P (mem) = 1;
+           emit_move_insn (gen_rtx_REG (PDImode, i), mem);
+         }
+       else
+         emit_move_insn (gen_rtx_REG (SImode, i), postinc);
+      }
+
+  expand_epilogue_reg_restore (spreg, all != NULL_TREE);
+
+  emit_move_insn (gen_rtx_REG (SImode, REG_ASTAT), postinc);
+
+  /* Deallocate any space we left on the stack in case we needed to save the
+     argument registers.  */
+  if (fkind == EXCPT_HANDLER)
+    emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (12)));
+
+  emit_jump_insn (gen_return_internal (GEN_INT (fkind)));
+}
+
+/* Generate RTL for the prologue of the current function.  */
+
+void
+bfin_expand_prologue (void)
+{
+  rtx insn;
+  HOST_WIDE_INT frame_size = get_frame_size ();
+  rtx spreg = gen_rtx_REG (Pmode, REG_SP);
+  e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+
+  if (fkind != SUBROUTINE)
+    {
+      expand_interrupt_handler_prologue (spreg, fkind);
+      return;
+    }
+
+  expand_prologue_reg_save (spreg, 0);
+
+  do_link (spreg, frame_size);
+
+  if (TARGET_ID_SHARED_LIBRARY
+      && (current_function_uses_pic_offset_table
+         || !current_function_is_leaf))
+    {
+      rtx addr;
+      
+      if (bfin_library_id_string)
+       addr = plus_constant (pic_offset_table_rtx, atoi (bfin_library_id_string));
+      else
+       addr = gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+                            gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+                                            UNSPEC_LIBRARY_OFFSET));
+      insn = emit_insn (gen_movsi (pic_offset_table_rtx,
+                                  gen_rtx_MEM (Pmode, addr)));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL);
+    }
+}
+
+/* Generate RTL for the epilogue of the current function.  NEED_RETURN is zero
+   if this is for a sibcall.  EH_RETURN is nonzero if we're expanding an
+   eh_return pattern.  */
+
+void
+bfin_expand_epilogue (int need_return, int eh_return)
+{
+  rtx spreg = gen_rtx_REG (Pmode, REG_SP);
+  e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+
+  if (fkind != SUBROUTINE)
+    {
+      expand_interrupt_handler_epilogue (spreg, fkind);
+      return;
+    }
+
+  do_unlink (spreg, get_frame_size ());
+
+  expand_epilogue_reg_restore (spreg, 0);
+
+  /* Omit the return insn if this is for a sibcall.  */
+  if (! need_return)
+    return;
+
+  if (eh_return)
+    emit_insn (gen_addsi3 (spreg, spreg, gen_rtx_REG (Pmode, REG_P2)));
+
+  emit_jump_insn (gen_return_internal (GEN_INT (SUBROUTINE)));
+}
+\f
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
+
+int
+bfin_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+                          unsigned int new_reg)
+{
+  /* Interrupt functions can only use registers that have already been
+     saved by the prologue, even if they would normally be
+     call-clobbered.  */
+
+  if (funkind (TREE_TYPE (current_function_decl)) != SUBROUTINE
+      && !regs_ever_live[new_reg])
+    return 0;
+
+  return 1;
+}
+
+/* Return the value of the return address for the frame COUNT steps up
+   from the current frame, after the prologue.
+   We punt for everything but the current frame by returning const0_rtx.  */
+
+rtx
+bfin_return_addr_rtx (int count)
+{
+  if (count != 0)
+    return const0_rtx;
+
+  return get_hard_reg_initial_val (Pmode, REG_RETS);
+}
+
+/* Try machine-dependent ways of modifying an illegitimate address X
+   to be legitimate.  If we find one, return the new, valid address,
+   otherwise return NULL_RTX.
+
+   OLDX is the address as it was before break_out_memory_refs was called.
+   In some cases it is useful to look at this to decide what needs to be done.
+
+   MODE is the mode of the memory reference.  */
+
+rtx
+legitimize_address (rtx x ATTRIBUTE_UNUSED, rtx oldx ATTRIBUTE_UNUSED,
+                   enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return NULL_RTX;
+}
+
+/* This predicate is used to compute the length of a load/store insn.
+   OP is a MEM rtx, we return nonzero if its addressing mode requires a
+   32 bit instruction.  */
+
+int
+effective_address_32bit_p (rtx op, enum machine_mode mode) 
+{
+  HOST_WIDE_INT offset;
+
+  mode = GET_MODE (op);
+  op = XEXP (op, 0);
+
+  if (REG_P (op) || GET_CODE (op) == POST_INC
+      || GET_CODE (op) == PRE_DEC || GET_CODE (op) == POST_DEC)
+    return 0;
+  if (GET_CODE (op) != PLUS)
+    abort ();
+
+  offset = INTVAL (XEXP (op, 1));
+
+  /* All byte loads use a 16 bit offset.  */
+  if (GET_MODE_SIZE (mode) == 1)
+    return 1;
+
+  if (GET_MODE_SIZE (mode) == 4)
+    {
+      /* Frame pointer relative loads can use a negative offset, all others
+        are restricted to a small positive one.  */
+      if (XEXP (op, 0) == frame_pointer_rtx)
+       return offset < -128 || offset > 60;
+      return offset < 0 || offset > 60;
+    }
+
+  /* Must be HImode now.  */
+  return offset < 0 || offset > 30;
+}
+
+/* Return cost of the memory address ADDR.
+   All addressing modes are equally cheap on the Blackfin.  */
+
+static int
+bfin_address_cost (rtx addr ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
+
+/* Subroutine of print_operand; used to print a memory reference X to FILE.  */
+
+void
+print_address_operand (FILE *file, rtx x)
+{
+  if (GET_CODE (x) == MEM) 
+    abort ();
+
+  switch (GET_CODE (x))
+    {
+    case PLUS:
+      output_address (XEXP (x, 0));
+      fprintf (file, "+");
+      output_address (XEXP (x, 1));
+      break;
+
+    case PRE_DEC:
+      fprintf (file, "--");
+      output_address (XEXP (x, 0));    
+      break;
+    case POST_INC:
+      output_address (XEXP (x, 0));
+      fprintf (file, "++");
+      break;
+    case POST_DEC:
+      output_address (XEXP (x, 0));
+      fprintf (file, "--");
+      break;
+
+    default:
+      print_operand (file, x, 0);
+    }
+}
+
+/* Adding intp DImode support by Tony
+ * -- Q: (low  word)
+ * -- R: (high word)
+ */
+
+void
+print_operand (FILE *file, rtx x, char code)
+{
+  enum machine_mode mode = GET_MODE (x);
+
+  switch (code)
+    {
+    case 'j':
+      switch (GET_CODE (x))
+       {
+       case EQ:
+         fprintf (file, "e");
+         break;
+       case NE:
+         fprintf (file, "ne");
+         break;
+       case GT:
+         fprintf (file, "g");
+         break;
+       case LT:
+         fprintf (file, "l");
+         break;
+       case GE:
+         fprintf (file, "ge");
+         break;
+       case LE:
+         fprintf (file, "le");
+         break;
+       case GTU:
+         fprintf (file, "g");
+         break;
+       case LTU:
+         fprintf (file, "l");
+         break;
+       case GEU:
+         fprintf (file, "ge");
+         break;
+       case LEU:
+         fprintf (file, "le");
+         break;
+       default:
+         output_operand_lossage ("invalid %%j value");
+       }
+      break;
+    
+    case 'J':                                   /* reverse logic */
+      switch (GET_CODE(x))
+       {
+       case EQ:
+         fprintf (file, "ne");
+         break;
+       case NE:
+         fprintf (file, "e");
+         break;
+       case GT:
+         fprintf (file, "le");
+         break;
+       case LT:
+         fprintf (file, "ge");
+         break;
+       case GE:
+         fprintf (file, "l");
+         break;
+       case LE:
+         fprintf (file, "g");
+         break;
+       case GTU:
+         fprintf (file, "le");
+         break;
+       case LTU:
+         fprintf (file, "ge");
+         break;
+       case GEU:
+         fprintf (file, "l");
+         break;
+       case LEU:
+         fprintf (file, "g");
+         break;
+       default:
+         output_operand_lossage ("invalid %%J value");
+       }
+      break;
+
+    default:
+      switch (GET_CODE (x))
+       {
+       case REG:
+         if (code == 'h')
+           {
+             gcc_assert (REGNO (x) < 32);
+             fprintf (file, "%s", short_reg_names[REGNO (x)]);
+             /*fprintf (file, "\n%d\n ", REGNO (x));*/
+             break;
+           }
+         else if (code == 'd')
+           {
+             gcc_assert (REGNO (x) < 32);
+             fprintf (file, "%s", high_reg_names[REGNO (x)]);
+             break;
+           }
+         else if (code == 'w')
+           {
+             gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
+             fprintf (file, "%s.w", reg_names[REGNO (x)]);
+           }
+         else if (code == 'x')
+           {
+             gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
+             fprintf (file, "%s.x", reg_names[REGNO (x)]);
+           }
+         else if (code == 'D')
+           {
+             fprintf (file, "%s", dregs_pair_names[REGNO (x)]);
+           }
+         else if (code == 'H')
+           {
+             gcc_assert (mode == DImode || mode == DFmode);
+             gcc_assert (REG_P (x));
+             fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+           }
+         else if (code == 'T')
+           {
+             if (REGNO (x) > 7)
+               abort ();
+             fprintf (file, "%s", byte_reg_names[REGNO (x)]);
+           }
+         else 
+           fprintf (file, "%s", reg_names[REGNO (x)]);
+         break;
+
+       case MEM:
+         fputc ('[', file);
+         x = XEXP (x,0);
+         print_address_operand (file, x);
+         fputc (']', file);
+         break;
+
+       case CONST_INT:
+         /* Moves to half registers with d or h modifiers always use unsigned
+            constants.  */
+         if (code == 'd')
+           x = GEN_INT ((INTVAL (x) >> 16) & 0xffff);
+         else if (code == 'h')
+           x = GEN_INT (INTVAL (x) & 0xffff);
+         else if (code == 'X')
+           x = GEN_INT (exact_log2 (0xffffffff & INTVAL (x)));
+         else if (code == 'Y')
+           x = GEN_INT (exact_log2 (0xffffffff & ~INTVAL (x)));
+         else if (code == 'Z')
+           /* Used for LINK insns.  */
+           x = GEN_INT (-8 - INTVAL (x));
+
+         /* fall through */
+
+       case SYMBOL_REF:
+         output_addr_const (file, x);
+         if (code == 'G' && flag_pic)
+           fprintf (file, "@GOT");
+         break;
+
+       case CONST_DOUBLE:
+         output_operand_lossage ("invalid const_double operand");
+         break;
+
+       case UNSPEC:
+         if (XINT (x, 1) == UNSPEC_MOVE_PIC)
+           {
+             output_addr_const (file, XVECEXP (x, 0, 0));
+             fprintf (file, "@GOT");
+           }
+         else if (XINT (x, 1) == UNSPEC_LIBRARY_OFFSET)
+           fprintf (file, "_current_shared_library_p5_offset_");
+         else
+           abort ();
+         break;
+
+       default:
+         output_addr_const (file, x);
+       }
+    }
+}
+\f
+/* Argument support functions.  */
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  
+   VDSP C Compiler manual, our ABI says that
+   first 3 words of arguments will use R0, R1 and R2.
+*/
+
+void
+init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype ATTRIBUTE_UNUSED,
+                     rtx libname ATTRIBUTE_UNUSED)
+{
+  static CUMULATIVE_ARGS zero_cum;
+
+  *cum = zero_cum;
+
+  /* Set up the number of registers to use for passing arguments.  */
+
+  cum->nregs = max_arg_registers;
+  cum->arg_regs = arg_regs;
+
+  return;
+}
+
+/* Update the data in CUM to advance over an argument
+   of mode MODE and data type TYPE.
+   (TYPE is null for libcalls where that information may not be available.)  */
+
+void
+function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+                     int named ATTRIBUTE_UNUSED)
+{
+  int count, bytes, words;
+
+  bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+  words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+  cum->words += words;
+  cum->nregs -= words;
+
+  if (cum->nregs <= 0)
+    {
+      cum->nregs = 0;
+      cum->arg_regs = NULL;
+    }
+  else
+    {
+      for (count = 1; count <= words; count++)
+        cum->arg_regs++;
+    }
+
+  return;
+}
+
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).  */
+
+struct rtx_def *
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+             int named ATTRIBUTE_UNUSED)
+{
+  int bytes
+    = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+
+  if (bytes == -1)
+    return NULL_RTX;
+
+  if (cum->nregs)
+    return gen_rtx_REG (mode, *(cum->arg_regs));
+
+  return NULL_RTX;
+}
+
+/* For an arg passed partly in registers and partly in memory,
+   this is the number of bytes passed in registers.
+   For args passed entirely in registers or entirely in memory, zero.
+
+   Refer VDSP C Compiler manual, our ABI.
+   First 3 words are in registers. So, if a an argument is larger
+   than the registers available, it will span the register and
+   stack.   */
+
+static int
+bfin_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                       tree type ATTRIBUTE_UNUSED,
+                       bool named ATTRIBUTE_UNUSED)
+{
+  int bytes
+    = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+  int bytes_left = cum->nregs * UNITS_PER_WORD;
+  
+  if (bytes == -1)
+    return 0;
+
+  if (bytes_left == 0)
+    return 0;
+  if (bytes > bytes_left)
+    return bytes_left;
+  return 0;
+}
+
+/* Variable sized types are passed by reference.  */
+
+static bool
+bfin_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+                       enum machine_mode mode ATTRIBUTE_UNUSED,
+                       tree type, bool named ATTRIBUTE_UNUSED)
+{
+  return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
+}
+
+/* Decide whether a type should be returned in memory (true)
+   or in a register (false).  This is called by the macro
+   RETURN_IN_MEMORY.  */
+
+int
+bfin_return_in_memory (tree type)
+{
+  int size;
+  enum machine_mode mode = TYPE_MODE (type);
+
+  if (mode == BLKmode)
+    return 1;
+  size = int_size_in_bytes (type);     
+  if (VECTOR_MODE_P (mode) || mode == TImode)
+    {
+      /* User-created vectors small enough to fit in REG.  */
+      if (size < 8)
+        return 0;
+      if (size == 8 || size == 16)
+       return 1;
+    }
+
+  if (size > 12)
+    return 1;
+  return 0;
+}
+
+/* Register in which address to store a structure value
+   is passed to a function.  */
+static rtx
+bfin_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+                     int incoming ATTRIBUTE_UNUSED)
+{
+  return gen_rtx_REG (Pmode, REG_P0);
+}
+
+/* Return true when register may be used to pass function parameters.  */
+
+bool 
+function_arg_regno_p (int n)
+{
+  int i;
+  for (i = 0; arg_regs[i] != -1; i++)
+    if (n == arg_regs[i])
+      return true;
+  return false;
+}
+
+/* Returns 1 if OP contains a symbol reference */
+
+int
+symbolic_reference_mentioned_p (rtx op)
+{
+  register const char *fmt;
+  register int i;
+
+  if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
+    return 1;
+
+  fmt = GET_RTX_FORMAT (GET_CODE (op));
+  for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+       {
+         register int j;
+
+         for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+           if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+             return 1;
+       }
+
+      else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
+       return 1;
+    }
+
+  return 0;
+}
+
+/* Decide whether we can make a sibling call to a function.  DECL is the
+   declaration of the function being targeted by the call and EXP is the
+   CALL_EXPR representing the call.  */
+
+static bool
+bfin_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
+                             tree exp ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+\f
+/* Emit RTL insns to initialize the variable parts of a trampoline at
+   TRAMP. FNADDR is an RTX for the address of the function's pure
+   code.  CXT is an RTX for the static chain value for the function.  */
+
+void
+initialize_trampoline (tramp, fnaddr, cxt)
+     rtx tramp, fnaddr, cxt;
+{
+  rtx t1 = copy_to_reg (fnaddr);
+  rtx t2 = copy_to_reg (cxt);
+  rtx addr;
+
+  addr = memory_address (Pmode, plus_constant (tramp, 2));
+  emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t1));
+  emit_insn (gen_ashrsi3 (t1, t1, GEN_INT (16)));
+  addr = memory_address (Pmode, plus_constant (tramp, 6));
+  emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t1));
+
+  addr = memory_address (Pmode, plus_constant (tramp, 10));
+  emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t2));
+  emit_insn (gen_ashrsi3 (t2, t2, GEN_INT (16)));
+  addr = memory_address (Pmode, plus_constant (tramp, 14));
+  emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t2));
+}
+
+/* Legitimize PIC addresses.  If the address is already position-independent,
+   we return ORIG.  Newly generated position-independent addresses go into a
+   reg.  This is REG if nonzero, otherwise we allocate register(s) as
+   necessary.  */
+
+rtx
+legitimize_pic_address (rtx orig, rtx reg)
+{
+  rtx addr = orig;
+  rtx new = orig;
+
+  if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+    {
+      if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
+       reg = new = orig;
+      else
+       {
+         if (reg == 0)
+           {
+             if (no_new_pseudos)
+               abort ();
+             reg = gen_reg_rtx (Pmode);
+           }
+
+         if (flag_pic == 2)
+           {
+             emit_insn (gen_movsi_high_pic (reg, addr));
+             emit_insn (gen_movsi_low_pic (reg, reg, addr));
+             emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx));
+             new = gen_rtx_MEM (Pmode, reg);
+           }
+         else
+           {
+             rtx tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+                                       UNSPEC_MOVE_PIC);
+             new = gen_rtx_MEM (Pmode,
+                                gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+                                              tmp));
+           }
+         emit_move_insn (reg, new);
+       }
+      current_function_uses_pic_offset_table = 1;
+      return reg;
+    }
+
+  else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
+    {
+      rtx base;
+
+      if (GET_CODE (addr) == CONST)
+       {
+         addr = XEXP (addr, 0);
+         if (GET_CODE (addr) != PLUS)
+           abort ();
+       }
+
+      if (XEXP (addr, 0) == pic_offset_table_rtx)
+       return orig;
+
+      if (reg == 0)
+       {
+         if (no_new_pseudos)
+           abort ();
+         reg = gen_reg_rtx (Pmode);
+       }
+
+      base = legitimize_pic_address (XEXP (addr, 0), reg);
+      addr = legitimize_pic_address (XEXP (addr, 1),
+                                    base == reg ? NULL_RTX : reg);
+
+      if (GET_CODE (addr) == CONST_INT)
+       {
+         if (! reload_in_progress && ! reload_completed)
+           addr = force_reg (Pmode, addr);
+         else
+           /* If we reach here, then something is seriously wrong.  */
+           abort ();
+       }
+
+      if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
+       {
+         base = gen_rtx_PLUS (Pmode, base, XEXP (addr, 0));
+         addr = XEXP (addr, 1);
+       }
+
+      return gen_rtx_PLUS (Pmode, base, addr);
+    }
+
+  return new;
+}
+
+/* Emit insns to move operands[1] into operands[0].  */
+
+void
+emit_pic_move (rtx *operands, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
+
+  if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
+    operands[1] = force_reg (SImode, operands[1]);
+  else
+    operands[1] = legitimize_pic_address (operands[1], temp);
+}
+
+/* Expand a move operation in mode MODE.  The operands are in OPERANDS.  */
+
+void
+expand_move (rtx *operands, enum machine_mode mode)
+{
+  if (flag_pic && SYMBOLIC_CONST (operands[1]))
+    emit_pic_move (operands, mode);
+
+  /* Don't generate memory->memory or constant->memory moves, go through a
+     register */
+  else if ((reload_in_progress | reload_completed) == 0
+          && GET_CODE (operands[0]) == MEM
+          && GET_CODE (operands[1]) != REG)
+    operands[1] = force_reg (mode, operands[1]);
+}
+\f
+/* Split one or more DImode RTL references into pairs of SImode
+   references.  The RTL can be REG, offsettable MEM, integer constant, or
+   CONST_DOUBLE.  "operands" is a pointer to an array of DImode RTL to
+   split and "num" is its length.  lo_half and hi_half are output arrays
+   that parallel "operands".  */
+
+void
+split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[])
+{
+  while (num--)
+    {
+      rtx op = operands[num];
+
+      /* simplify_subreg refuse to split volatile memory addresses,
+         but we still have to handle it.  */
+      if (GET_CODE (op) == MEM)
+       {
+         lo_half[num] = adjust_address (op, SImode, 0);
+         hi_half[num] = adjust_address (op, SImode, 4);
+       }
+      else
+       {
+         lo_half[num] = simplify_gen_subreg (SImode, op,
+                                             GET_MODE (op) == VOIDmode
+                                             ? DImode : GET_MODE (op), 0);
+         hi_half[num] = simplify_gen_subreg (SImode, op,
+                                             GET_MODE (op) == VOIDmode
+                                             ? DImode : GET_MODE (op), 4);
+       }
+    }
+}
+\f
+/* Expand a call instruction.  FNADDR is the call target, RETVAL the return value.
+   SIBCALL is nonzero if this is a sibling call.  */
+
+void
+bfin_expand_call (rtx retval, rtx fnaddr, rtx callarg1, int sibcall)
+{
+  rtx use = NULL, call;
+
+  /* Static functions and indirect calls don't need the pic register.  */
+  if (flag_pic
+      && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
+      && ! SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0)))
+    use_reg (&use, pic_offset_table_rtx);
+
+  if (! call_insn_operand (XEXP (fnaddr, 0), Pmode))
+    {
+      fnaddr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0));
+      fnaddr = gen_rtx_MEM (Pmode, fnaddr);
+    }
+  call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1);
+
+  if (retval)
+    call = gen_rtx_SET (VOIDmode, retval, call);
+  if (sibcall)
+    {
+      rtx pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
+      XVECEXP (pat, 0, 0) = call;
+      XVECEXP (pat, 0, 1) = gen_rtx_RETURN (VOIDmode);
+      call = pat;
+    }
+  call = emit_call_insn (call);
+  if (use)
+    CALL_INSN_FUNCTION_USAGE (call) = use;
+}
+\f
+/* Return 1 if hard register REGNO can hold a value of machine-mode MODE.  */
+
+int
+hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+  /* Allow only dregs to store value of mode HI or QI */
+  enum reg_class class = REGNO_REG_CLASS (regno);
+
+  if (mode == CCmode)
+    return 0;
+
+  if (mode == V2HImode)
+    return D_REGNO_P (regno);
+  if (class == CCREGS)
+    return mode == BImode;
+  if (mode == PDImode)
+    return regno == REG_A0 || regno == REG_A1;
+  if (mode == SImode
+      && TEST_HARD_REG_BIT (reg_class_contents[PROLOGUE_REGS], regno))
+    return 1;
+      
+  return TEST_HARD_REG_BIT (reg_class_contents[MOST_REGS], regno);
+}
+
+/* Implements target hook vector_mode_supported_p.  */
+
+static bool
+bfin_vector_mode_supported_p (enum machine_mode mode)
+{
+  return mode == V2HImode;
+}
+
+/* Return the cost of moving data from a register in class CLASS1 to
+   one in class CLASS2.  A cost of 2 is the default.  */
+
+int
+bfin_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                        enum reg_class class1, enum reg_class class2)
+{
+  /* If optimizing for size, always prefer reg-reg over reg-memory moves.  */
+  if (optimize_size)
+    return 2;
+
+  /* There are some stalls involved when moving from a DREG to a different
+     class reg, and using the value in one of the following instructions.
+     Attempt to model this by slightly discouraging such moves.  */
+  if (class1 == DREGS && class2 != DREGS)
+    return 2 * 2;
+
+  return 2;
+}
+
+/* Return the cost of moving data of mode M between a
+   register and memory.  A value of 2 is the default; this cost is
+   relative to those in `REGISTER_MOVE_COST'.
+
+   ??? In theory L1 memory has single-cycle latency.  We should add a switch
+   that tells the compiler whether we expect to use only L1 memory for the
+   program; it'll make the costs more accurate.  */
+
+int
+bfin_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                      enum reg_class class,
+                      int in ATTRIBUTE_UNUSED)
+{
+  /* Make memory accesses slightly more expensive than any register-register
+     move.  Also, penalize non-DP registers, since they need secondary
+     reloads to load and store.  */
+  if (! reg_class_subset_p (class, DPREGS))
+    return 10;
+
+  return 8;
+}
+
+/* Inform reload about cases where moving X with a mode MODE to a register in
+   CLASS requires an extra scratch register.  Return the class needed for the
+   scratch register.  */
+
+enum reg_class
+secondary_input_reload_class (enum reg_class class, enum machine_mode mode,
+                             rtx x)
+{
+  /* If we have HImode or QImode, we can only use DREGS as secondary registers;
+     in most other cases we can also use PREGS.  */
+  enum reg_class default_class = GET_MODE_SIZE (mode) >= 4 ? DPREGS : DREGS;
+  enum reg_class x_class = NO_REGS;
+  enum rtx_code code = GET_CODE (x);
+
+  if (code == SUBREG)
+    x = SUBREG_REG (x), code = GET_CODE (x);
+  if (REG_P (x))
+    {
+      int regno = REGNO (x);
+      if (regno >= FIRST_PSEUDO_REGISTER)
+       regno = reg_renumber[regno];
+
+      if (regno == -1)
+       code = MEM;
+      else
+       x_class = REGNO_REG_CLASS (regno);
+    }
+
+  /* We can be asked to reload (plus (FP) (large_constant)) into a DREG.
+     This happens as a side effect of register elimination, and we need
+     a scratch register to do it.  */
+  if (fp_plus_const_operand (x, mode))
+    {
+      rtx op2 = XEXP (x, 1);
+      int large_constant_p = ! CONST_7BIT_IMM_P (INTVAL (op2));
+
+      if (class == PREGS || class == PREGS_CLOBBERED)
+       return NO_REGS;
+      /* If destination is a DREG, we can do this without a scratch register
+        if the constant is valid for an add instruction.  */
+      if (class == DREGS || class == DPREGS)
+       return large_constant_p ? PREGS : NO_REGS;
+      /* Reloading to anything other than a DREG?  Use a PREG scratch
+        register.  */
+      return PREGS;
+    }
+
+  /* Data can usually be moved freely between registers of most classes.
+     AREGS are an exception; they can only move to or from another register
+     in AREGS or one in DREGS.  They can also be assigned the constant 0.  */
+  if (x_class == AREGS)
+    return class == DREGS || class == AREGS ? NO_REGS : DREGS;
+
+  if (class == AREGS)
+    {
+      if (x != const0_rtx && x_class != DREGS)
+       return DREGS;
+      else
+       return NO_REGS;
+    }
+
+  /* CCREGS can only be moved from/to DREGS.  */
+  if (class == CCREGS && x_class != DREGS)
+    return DREGS;
+  if (x_class == CCREGS && class != DREGS)
+    return DREGS;
+  /* All registers other than AREGS can load arbitrary constants.  The only
+     case that remains is MEM.  */
+  if (code == MEM)
+    if (! reg_class_subset_p (class, default_class))
+      return default_class;
+  return NO_REGS;
+}
+
+/* Like secondary_input_reload_class; and all we do is call that function.  */
+
+enum reg_class
+secondary_output_reload_class (enum reg_class class, enum machine_mode mode,
+                              rtx x)
+{
+  return secondary_input_reload_class (class, mode, x);
+}
+\f
+/* Implement the macro OVERRIDE_OPTIONS.  */
+
+void
+override_options (void)
+{
+  if (TARGET_OMIT_LEAF_FRAME_POINTER)
+    flag_omit_frame_pointer = 1;
+
+  /* Library identification */
+  if (bfin_library_id_string)
+    {
+      int id;
+
+      if (! TARGET_ID_SHARED_LIBRARY)
+       error ("-mshared-library-id= specified without -mid-shared-library");
+      id = atoi (bfin_library_id_string);
+      if (id < 0 || id > MAX_LIBRARY_ID)
+       error ("-mshared-library-id=%d is not between 0 and %d", id, MAX_LIBRARY_ID);
+
+      /* From now on, bfin_library_id_string will contain the library offset.  */
+      asprintf ((char **)&bfin_library_id_string, "%d", (id * -4) - 4);
+    }
+
+  if (TARGET_ID_SHARED_LIBRARY)
+    /* ??? Provide a way to use a bigger GOT.  */
+    flag_pic = 1;
+
+  flag_schedule_insns = 0;
+}
+
+/* Return the destination address of BRANCH.  */
+
+static int
+branch_dest (rtx branch)
+{
+  rtx dest;
+  int dest_uid;
+  rtx pat = PATTERN (branch);
+  if (GET_CODE (pat) == PARALLEL)
+    pat = XVECEXP (pat, 0, 0);
+  dest = SET_SRC (pat);
+  if (GET_CODE (dest) == IF_THEN_ELSE)
+    dest = XEXP (dest, 1);
+  dest = XEXP (dest, 0);
+  dest_uid = INSN_UID (dest);
+  return INSN_ADDRESSES (dest_uid);
+}
+
+/* Return nonzero if INSN is annotated with a REG_BR_PROB note that indicates
+   it's a branch that's predicted taken.  */
+
+static int
+cbranch_predicted_taken_p (rtx insn)
+{
+  rtx x = find_reg_note (insn, REG_BR_PROB, 0);
+
+  if (x)
+    {
+      int pred_val = INTVAL (XEXP (x, 0));
+
+      return pred_val >= REG_BR_PROB_BASE / 2;
+    }
+
+  return 0;
+}
+
+/* Templates for use by asm_conditional_branch.  */
+
+static const char *ccbranch_templates[][3] = {
+  { "if !cc jump %3;",  "if cc jump 4 (bp); jump.s %3;",  "if cc jump 6 (bp); jump.l %3;" },
+  { "if cc jump %3;",   "if !cc jump 4 (bp); jump.s %3;", "if !cc jump 6 (bp); jump.l %3;" },
+  { "if !cc jump %3 (bp);",  "if cc jump 4; jump.s %3;",  "if cc jump 6; jump.l %3;" },
+  { "if cc jump %3 (bp);",  "if !cc jump 4; jump.s %3;",  "if !cc jump 6; jump.l %3;" },
+};
+
+/* Output INSN, which is a conditional branch instruction with operands
+   OPERANDS.
+
+   We deal with the various forms of conditional branches that can be generated
+   by bfin_reorg to prevent the hardware from doing speculative loads, by
+   - emitting a sufficient number of nops, if N_NOPS is nonzero, or
+   - always emitting the branch as predicted taken, if PREDICT_TAKEN is true.
+   Either of these is only necessary if the branch is short, otherwise the
+   template we use ends in an unconditional jump which flushes the pipeline
+   anyway.  */
+
+void
+asm_conditional_branch (rtx insn, rtx *operands, int n_nops, int predict_taken)
+{
+  int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
+  /* Note : offset for instructions like if cc jmp; jump.[sl] offset
+            is to be taken from start of if cc rather than jump.
+            Range for jump.s is (-4094, 4096) instead of (-4096, 4094)
+  */
+  int len = (offset >= -1024 && offset <= 1022 ? 0
+            : offset >= -4094 && offset <= 4096 ? 1
+            : 2);
+  int bp = predict_taken && len == 0 ? 1 : cbranch_predicted_taken_p (insn);
+  int idx = (bp << 1) | (GET_CODE (operands[0]) == EQ ? BRF : BRT);
+  output_asm_insn (ccbranch_templates[idx][len], operands);
+  if (n_nops > 0 && bp)
+    abort ();
+  if (len == 0)
+    while (n_nops-- > 0)
+      output_asm_insn ("nop;", NULL);
+}
+
+/* Emit rtl for a comparison operation CMP in mode MODE.  Operands have been
+   stored in bfin_compare_op0 and bfin_compare_op1 already.  */
+
+rtx
+bfin_gen_compare (rtx cmp, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  enum rtx_code code1, code2;
+  rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
+  rtx tem = bfin_cc_rtx;
+  enum rtx_code code = GET_CODE (cmp);
+
+  /* If we have a BImode input, then we already have a compare result, and
+     do not need to emit another comparison.  */
+  if (GET_MODE (op0) == BImode)
+    {
+      if ((code == NE || code == EQ) && op1 == const0_rtx)
+       tem = op0, code2 = code;
+      else
+       abort ();
+    }
+  else
+    {
+      switch (code) {
+       /* bfin has these conditions */
+      case EQ:
+      case LT:
+      case LE:
+      case LEU:
+      case LTU:
+       code1 = code;
+       code2 = NE;
+       break;
+      default:
+       code1 = reverse_condition (code);
+       code2 = EQ;
+       break;
+      }
+      emit_insn (gen_rtx_SET (BImode, tem,
+                             gen_rtx_fmt_ee (code1, BImode, op0, op1)));
+    }
+
+  return gen_rtx_fmt_ee (code2, BImode, tem, CONST0_RTX (BImode));
+}
+\f
+/* Return nonzero iff C has exactly one bit set if it is interpreted
+   as a 32 bit constant.  */
+
+int
+log2constp (unsigned HOST_WIDE_INT c)
+{
+  c &= 0xFFFFFFFF;
+  return c != 0 && (c & (c-1)) == 0;
+}
+
+/* Returns the number of consecutive least significant zeros in the binary
+   representation of *V.
+   We modify *V to contain the original value arithmetically shifted right by
+   the number of zeroes.  */
+
+static int
+shiftr_zero (HOST_WIDE_INT *v)
+{
+  unsigned HOST_WIDE_INT tmp = *v;
+  unsigned HOST_WIDE_INT sgn;
+  int n = 0;
+
+  if (tmp == 0)
+    return 0;
+
+  sgn = tmp & ((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1));
+  while ((tmp & 0x1) == 0 && n <= 32)
+    {
+      tmp = (tmp >> 1) | sgn;
+      n++;
+    }
+  *v = tmp;
+  return n;
+}
+
+/* After reload, split the load of an immediate constant.  OPERANDS are the
+   operands of the movsi_insn pattern which we are splitting.  We return
+   nonzero if we emitted a sequence to load the constant, zero if we emitted
+   nothing because we want to use the splitter's default sequence.  */
+
+int
+split_load_immediate (rtx operands[])
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+  HOST_WIDE_INT tmp;
+  HOST_WIDE_INT shifted = val;
+  HOST_WIDE_INT shifted_compl = ~val;
+  int num_zero = shiftr_zero (&shifted);
+  int num_compl_zero = shiftr_zero (&shifted_compl);
+  unsigned int regno = REGNO (operands[0]);
+  enum reg_class class1 = REGNO_REG_CLASS (regno);
+
+  /* This case takes care of single-bit set/clear constants, which we could
+     also implement with BITSET/BITCLR.  */
+  if (num_zero
+      && shifted >= -32768 && shifted < 65536
+      && (D_REGNO_P (regno)
+         || (regno >= REG_P0 && regno <= REG_P7 && num_zero <= 2)))
+    {
+      emit_insn (gen_movsi (operands[0], GEN_INT (shifted)));
+      emit_insn (gen_ashlsi3 (operands[0], operands[0], GEN_INT (num_zero)));
+      return 1;
+    }
+
+  tmp = val & 0xFFFF;
+  tmp |= -(tmp & 0x8000);
+
+  /* If high word has one bit set or clear, try to use a bit operation.  */
+  if (D_REGNO_P (regno))
+    {
+      if (log2constp (val & 0xFFFF0000))
+       {
+         emit_insn (gen_movsi (operands[0], GEN_INT (val & 0xFFFF)));
+         emit_insn (gen_iorsi3 (operands[0], operands[0], GEN_INT (val & 0xFFFF0000)));
+         return 1;
+       }
+      else if (log2constp (val | 0xFFFF) && (val & 0x8000) != 0)
+       {
+         emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
+         emit_insn (gen_andsi3 (operands[0], operands[0], GEN_INT (val | 0xFFFF)));
+       }
+    }
+
+  if (D_REGNO_P (regno))
+    {
+      if (CONST_7BIT_IMM_P (tmp))
+       {
+         emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
+         emit_insn (gen_movstricthi_high (operands[0], GEN_INT (val & -65536)));
+         return 1;
+       }
+
+      if ((val & 0xFFFF0000) == 0)
+       {
+         emit_insn (gen_movsi (operands[0], const0_rtx));
+         emit_insn (gen_movsi_low (operands[0], operands[0], operands[1]));
+         return 1;
+       }
+
+      if ((val & 0xFFFF0000) == 0xFFFF0000)
+       {
+         emit_insn (gen_movsi (operands[0], constm1_rtx));
+         emit_insn (gen_movsi_low (operands[0], operands[0], operands[1]));
+         return 1;
+       }
+    }
+
+  /* Need DREGs for the remaining case.  */
+  if (regno > REG_R7)
+    return 0;
+
+  if (optimize_size
+      && num_compl_zero && CONST_7BIT_IMM_P (shifted_compl))
+    {
+      /* If optimizing for size, generate a sequence that has more instructions
+        but is shorter.  */
+      emit_insn (gen_movsi (operands[0], GEN_INT (shifted_compl)));
+      emit_insn (gen_ashlsi3 (operands[0], operands[0],
+                             GEN_INT (num_compl_zero)));
+      emit_insn (gen_one_cmplsi2 (operands[0], operands[0]));
+      return 1;
+    }
+  return 0;
+}
+\f
+/* Return true if the legitimate memory address for a memory operand of mode
+   MODE.  Return false if not.  */
+
+static bool
+bfin_valid_add (enum machine_mode mode, HOST_WIDE_INT value)
+{
+  unsigned HOST_WIDE_INT v = value > 0 ? value : -value;
+  int sz = GET_MODE_SIZE (mode);
+  int shift = sz == 1 ? 0 : sz == 2 ? 1 : 2;
+  /* The usual offsettable_memref machinery doesn't work so well for this
+     port, so we deal with the problem here.  */
+  unsigned HOST_WIDE_INT mask = sz == 8 ? 0x7ffe : 0x7fff;
+  return (v & ~(mask << shift)) == 0;
+}
+
+static bool
+bfin_valid_reg_p (unsigned int regno, int strict)
+{
+  return ((strict && REGNO_OK_FOR_BASE_STRICT_P (regno))
+         || (!strict && REGNO_OK_FOR_BASE_NONSTRICT_P (regno)));
+}
+
+bool
+bfin_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+{
+  switch (GET_CODE (x)) {
+  case REG:
+    if (bfin_valid_reg_p (REGNO (x), strict))
+      return true;
+    break;
+  case PLUS:
+    if (REG_P (XEXP (x, 0))
+       && bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict)
+       && (GET_CODE (XEXP (x, 1)) == UNSPEC
+           || (GET_CODE (XEXP (x, 1)) == CONST_INT
+               && bfin_valid_add (mode, INTVAL (XEXP (x, 1))))))
+      return true;
+    break;
+  case POST_INC:
+  case POST_DEC:
+    if (LEGITIMATE_MODE_FOR_AUTOINC_P (mode)
+       && REG_P (XEXP (x, 0))
+       && bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict))
+      return true;
+  case PRE_DEC:
+    if (LEGITIMATE_MODE_FOR_AUTOINC_P (mode)
+       && XEXP (x, 0) == stack_pointer_rtx
+       && REG_P (XEXP (x, 0))
+       && bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict))
+      return true;
+    break;
+  default:
+    break;
+  }
+  return false;
+}
+
+static bool
+bfin_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+  int cost2 = COSTS_N_INSNS (1);
+
+  switch (code)
+    {
+    case CONST_INT:
+      if (outer_code == SET || outer_code == PLUS)
+        *total = CONST_7BIT_IMM_P (INTVAL (x)) ? 0 : cost2;
+      else if (outer_code == AND)
+        *total = log2constp (~INTVAL (x)) ? 0 : cost2;
+      else if (outer_code == LE || outer_code == LT || outer_code == EQ)
+        *total = (INTVAL (x) >= -4 && INTVAL (x) <= 3) ? 0 : cost2;
+      else if (outer_code == LEU || outer_code == LTU)
+        *total = (INTVAL (x) >= 0 && INTVAL (x) <= 7) ? 0 : cost2;
+      else if (outer_code == MULT)
+        *total = (INTVAL (x) == 2 || INTVAL (x) == 4) ? 0 : cost2;
+      else if (outer_code == ASHIFT && (INTVAL (x) == 1 || INTVAL (x) == 2))
+        *total = 0;
+      else if (outer_code == ASHIFT || outer_code == ASHIFTRT
+              || outer_code == LSHIFTRT)
+        *total = (INTVAL (x) >= 0 && INTVAL (x) <= 31) ? 0 : cost2;
+      else if (outer_code == IOR || outer_code == XOR)
+        *total = (INTVAL (x) & (INTVAL (x) - 1)) == 0 ? 0 : cost2;
+      else
+       *total = cost2;
+      return true;
+
+    case CONST:
+    case LABEL_REF:
+    case SYMBOL_REF:
+    case CONST_DOUBLE:
+      *total = COSTS_N_INSNS (2);
+      return true;
+
+    case PLUS:
+      if (GET_MODE (x) == Pmode)
+       {
+         if (GET_CODE (XEXP (x, 0)) == MULT
+             && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+           {
+             HOST_WIDE_INT val = INTVAL (XEXP (XEXP (x, 0), 1));
+             if (val == 2 || val == 4)
+               {
+                 *total = cost2;
+                 *total += rtx_cost (XEXP (XEXP (x, 0), 0), outer_code);
+                 *total += rtx_cost (XEXP (x, 1), outer_code);
+                 return true;
+               }
+           }
+       }
+
+      /* fall through */
+
+    case MINUS:
+    case ASHIFT: 
+    case ASHIFTRT:
+    case LSHIFTRT:
+      if (GET_MODE (x) == DImode)
+       *total = 6 * cost2;
+      return false;
+         
+    case AND:
+    case IOR:
+    case XOR:
+      if (GET_MODE (x) == DImode)
+       *total = 2 * cost2;
+      return false;
+
+    case MULT:
+      if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD)
+       *total = COSTS_N_INSNS (3);
+      return false;
+
+    default:
+      return false;
+    }
+}
+
+static void
+bfin_internal_label (FILE *stream, const char *prefix, unsigned long num)
+{
+  fprintf (stream, "%s%s$%ld:\n", LOCAL_LABEL_PREFIX, prefix, num);
+}
+\f
+/* Used for communication between {push,pop}_multiple_operation (which
+   we use not only as a predicate) and the corresponding output functions.  */
+static int first_preg_to_save, first_dreg_to_save;
+
+int
+push_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  int lastdreg = 8, lastpreg = 6;
+  int i, group;
+
+  first_preg_to_save = lastpreg;
+  first_dreg_to_save = lastdreg;
+  for (i = 1, group = 0; i < XVECLEN (op, 0) - 1; i++)
+    {
+      rtx t = XVECEXP (op, 0, i);
+      rtx src, dest;
+      int regno;
+
+      if (GET_CODE (t) != SET)
+       return 0;
+
+      src = SET_SRC (t);
+      dest = SET_DEST (t);
+      if (GET_CODE (dest) != MEM || ! REG_P (src))
+       return 0;
+      dest = XEXP (dest, 0);
+      if (GET_CODE (dest) != PLUS
+         || ! REG_P (XEXP (dest, 0))
+         || REGNO (XEXP (dest, 0)) != REG_SP
+         || GET_CODE (XEXP (dest, 1)) != CONST_INT
+         || INTVAL (XEXP (dest, 1)) != -i * 4)
+       return 0;
+
+      regno = REGNO (src);
+      if (group == 0)
+       {
+         if (D_REGNO_P (regno))
+           {
+             group = 1;
+             first_dreg_to_save = lastdreg = regno - REG_R0;
+           }
+         else if (regno >= REG_P0 && regno <= REG_P7)
+           {
+             group = 2;
+             first_preg_to_save = lastpreg = regno - REG_P0;
+           }
+         else
+           return 0;
+
+         continue;
+       }
+
+      if (group == 1)
+       {
+         if (regno >= REG_P0 && regno <= REG_P7)
+           {
+             group = 2;
+             first_preg_to_save = lastpreg = regno - REG_P0;
+           }
+         else if (regno != REG_R0 + lastdreg + 1)
+           return 0;
+         else
+           lastdreg++;
+       }
+      else if (group == 2)
+       {
+         if (regno != REG_P0 + lastpreg + 1)
+           return 0;
+         lastpreg++;
+       }
+    }
+  return 1;
+}
+
+int
+pop_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  int lastdreg = 8, lastpreg = 6;
+  int i, group;
+
+  for (i = 1, group = 0; i < XVECLEN (op, 0); i++)
+    {
+      rtx t = XVECEXP (op, 0, i);
+      rtx src, dest;
+      int regno;
+
+      if (GET_CODE (t) != SET)
+       return 0;
+
+      src = SET_SRC (t);
+      dest = SET_DEST (t);
+      if (GET_CODE (src) != MEM || ! REG_P (dest))
+       return 0;
+      src = XEXP (src, 0);
+
+      if (i == 1)
+       {
+         if (! REG_P (src) || REGNO (src) != REG_SP)
+           return 0;
+       }
+      else if (GET_CODE (src) != PLUS
+              || ! REG_P (XEXP (src, 0))
+              || REGNO (XEXP (src, 0)) != REG_SP
+              || GET_CODE (XEXP (src, 1)) != CONST_INT
+              || INTVAL (XEXP (src, 1)) != (i - 1) * 4)
+       return 0;
+
+      regno = REGNO (dest);
+      if (group == 0)
+       {
+         if (regno == REG_R7)
+           {
+             group = 1;
+             lastdreg = 7;
+           }
+         else if (regno != REG_P0 + lastpreg - 1)
+           return 0;
+         else
+           lastpreg--;
+       }
+      else if (group == 1)
+       {
+         if (regno != REG_R0 + lastdreg - 1)
+           return 0;
+         else
+           lastdreg--;
+       }
+    }
+  first_dreg_to_save = lastdreg;
+  first_preg_to_save = lastpreg;
+  return 1;
+}
+
+/* Emit assembly code for one multi-register push described by INSN, with
+   operands in OPERANDS.  */
+
+void
+output_push_multiple (rtx insn, rtx *operands)
+{
+  char buf[80];
+  /* Validate the insn again, and compute first_[dp]reg_to_save. */
+  if (! push_multiple_operation (PATTERN (insn), VOIDmode))
+    abort ();
+  if (first_dreg_to_save == 8)
+    sprintf (buf, "[--sp] = ( p5:%d );\n", first_preg_to_save);
+  else if (first_preg_to_save == 6)
+    sprintf (buf, "[--sp] = ( r7:%d );\n", first_dreg_to_save);
+  else
+    sprintf (buf, "[--sp] = ( r7:%d, p5:%d );\n", first_dreg_to_save, first_preg_to_save);
+
+  output_asm_insn (buf, operands);
+}
+
+/* Emit assembly code for one multi-register pop described by INSN, with
+   operands in OPERANDS.  */
+
+void
+output_pop_multiple (rtx insn, rtx *operands)
+{
+  char buf[80];
+  /* Validate the insn again, and compute first_[dp]reg_to_save. */
+  if (! pop_multiple_operation (PATTERN (insn), VOIDmode))
+    abort ();
+
+  if (first_dreg_to_save == 8)
+    sprintf (buf, "( p5:%d ) = [sp++];\n", first_preg_to_save);
+  else if (first_preg_to_save == 6)
+    sprintf (buf, "( r7:%d ) = [sp++];\n", first_dreg_to_save);
+  else
+    sprintf (buf, "( r7:%d, p5:%d ) = [sp++];\n", first_dreg_to_save, first_preg_to_save);
+
+  output_asm_insn (buf, operands);
+}
+
+/* Adjust DST and SRC by OFFSET bytes, and generate one move in mode MODE.  */
+
+static void
+single_move_for_strmov (rtx dst, rtx src, enum machine_mode mode, HOST_WIDE_INT offset)
+{
+  rtx scratch = gen_reg_rtx (mode);
+  rtx srcmem, dstmem;
+
+  srcmem = adjust_address_nv (src, mode, offset);
+  dstmem = adjust_address_nv (dst, mode, offset);
+  emit_move_insn (scratch, srcmem);
+  emit_move_insn (dstmem, scratch);
+}
+
+/* Expand a string move operation of COUNT_EXP bytes from SRC to DST, with
+   alignment ALIGN_EXP.  Return true if successful, false if we should fall
+   back on a different method.  */
+
+bool
+bfin_expand_strmov (rtx dst, rtx src, rtx count_exp, rtx align_exp)
+{
+  rtx srcreg, destreg, countreg;
+  HOST_WIDE_INT align = 0;
+  unsigned HOST_WIDE_INT count = 0;
+
+  if (GET_CODE (align_exp) == CONST_INT)
+    align = INTVAL (align_exp);
+  if (GET_CODE (count_exp) == CONST_INT)
+    {
+      count = INTVAL (count_exp);
+#if 0
+      if (!TARGET_INLINE_ALL_STRINGOPS && count > 64)
+       return false;
+#endif
+    }
+
+  /* If optimizing for size, only do single copies inline.  */
+  if (optimize_size)
+    {
+      if (count == 2 && align < 2)
+       return false;
+      if (count == 4 && align < 4)
+       return false;
+      if (count != 1 && count != 2 && count != 4)
+       return false;
+    }
+  if (align < 2 && count != 1)
+    return false;
+
+  destreg = copy_to_mode_reg (Pmode, XEXP (dst, 0));
+  if (destreg != XEXP (dst, 0))
+    dst = replace_equiv_address_nv (dst, destreg);
+  srcreg = copy_to_mode_reg (Pmode, XEXP (src, 0));
+  if (srcreg != XEXP (src, 0))
+    src = replace_equiv_address_nv (src, srcreg);
+
+  if (count != 0 && align >= 2)
+    {
+      unsigned HOST_WIDE_INT offset = 0;
+
+      if (align >= 4)
+       {
+         if ((count & ~3) == 4)
+           {
+             single_move_for_strmov (dst, src, SImode, offset);
+             offset = 4;
+           }
+         else if (count & ~3)
+           {
+             HOST_WIDE_INT new_count = ((count >> 2) & 0x3fffffff) - 1;
+             countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
+
+             emit_insn (gen_rep_movsi (destreg, srcreg, countreg, destreg, srcreg));
+           }
+       }
+      else
+       {
+         if ((count & ~1) == 2)
+           {
+             single_move_for_strmov (dst, src, HImode, offset);
+             offset = 2;
+           }
+         else if (count & ~1)
+           {
+             HOST_WIDE_INT new_count = ((count >> 1) & 0x7fffffff) - 1;
+             countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
+
+             emit_insn (gen_rep_movhi (destreg, srcreg, countreg, destreg, srcreg));
+           }
+       }
+      if (count & 2)
+       {
+         single_move_for_strmov (dst, src, HImode, offset);
+         offset += 2;
+       }
+      if (count & 1)
+       {
+         single_move_for_strmov (dst, src, QImode, offset);
+       }
+      return true;
+    }
+  return false;
+}
+
+\f
+static int
+bfin_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
+{
+  enum attr_type insn_type, dep_insn_type;
+  int dep_insn_code_number;
+
+  /* Anti and output dependencies have zero cost.  */
+  if (REG_NOTE_KIND (link) != 0)
+    return 0;
+
+  dep_insn_code_number = recog_memoized (dep_insn);
+
+  /* If we can't recognize the insns, we can't really do anything.  */
+  if (dep_insn_code_number < 0 || recog_memoized (insn) < 0)
+    return cost;
+
+  insn_type = get_attr_type (insn);
+  dep_insn_type = get_attr_type (dep_insn);
+
+  if (dep_insn_type == TYPE_MOVE || dep_insn_type == TYPE_MCLD)
+    {
+      rtx pat = PATTERN (dep_insn);
+      rtx dest = SET_DEST (pat);
+      rtx src = SET_SRC (pat);
+      if (! ADDRESS_REGNO_P (REGNO (dest)) || ! D_REGNO_P (REGNO (src)))
+       return cost;
+      return cost + (dep_insn_type == TYPE_MOVE ? 4 : 3);
+    }
+
+  return cost;
+}
+\f
+/* We use the machine specific reorg pass for emitting CSYNC instructions
+   after conditional branches as needed.
+
+   The Blackfin is unusual in that a code sequence like
+     if cc jump label
+     r0 = (p0)
+   may speculatively perform the load even if the condition isn't true.  This
+   happens for a branch that is predicted not taken, because the pipeline
+   isn't flushed or stalled, so the early stages of the following instructions,
+   which perform the memory reference, are allowed to execute before the
+   jump condition is evaluated.
+   Therefore, we must insert additional instructions in all places where this
+   could lead to incorrect behaviour.  The manual recommends CSYNC, while
+   VDSP seems to use NOPs (even though its corresponding compiler option is
+   named CSYNC).
+
+   When optimizing for speed, we emit NOPs, which seems faster than a CSYNC.
+   When optimizing for size, we turn the branch into a predicted taken one.
+   This may be slower due to mispredicts, but saves code size.  */
+
+static void
+bfin_reorg (void)
+{
+  rtx insn, last_condjump = NULL_RTX;
+  int cycles_since_jump = INT_MAX;
+
+  if (! TARGET_CSYNC)
+    return;
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      rtx pat;
+
+      if (NOTE_P (insn) || BARRIER_P (insn) || LABEL_P (insn))
+       continue;
+
+      pat = PATTERN (insn);
+      if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER
+         || GET_CODE (pat) == ASM_INPUT || GET_CODE (pat) == ADDR_VEC
+         || GET_CODE (pat) == ADDR_DIFF_VEC || asm_noperands (pat) >= 0)
+       continue;
+
+      if (JUMP_P (insn))
+       {
+         if (any_condjump_p (insn)
+             && ! cbranch_predicted_taken_p (insn))
+           {
+             last_condjump = insn;
+             cycles_since_jump = 0;
+           }
+         else
+           cycles_since_jump = INT_MAX;
+       }
+      else if (INSN_P (insn))
+       {
+         enum attr_type type = get_attr_type (insn);
+         if (cycles_since_jump < INT_MAX)
+           cycles_since_jump++;
+
+         if (type == TYPE_MCLD && cycles_since_jump < 3)
+           {
+             rtx pat;
+
+             pat = single_set (insn);
+             if (may_trap_p (SET_SRC (pat)))
+               {
+                 int num_clobbers;
+                 rtx *op = recog_data.operand;
+
+                 extract_insn (last_condjump);
+                 if (optimize_size)
+                   pat = gen_cbranch_predicted_taken (op[0], op[1], op[2],
+                                                      op[3]);
+                 else
+                   pat = gen_cbranch_with_nops (op[0], op[1], op[2], op[3],
+                                                GEN_INT (3 - cycles_since_jump));
+                 PATTERN (last_condjump) = pat;
+                 INSN_CODE (last_condjump) = recog (pat, insn, &num_clobbers);
+                 cycles_since_jump = INT_MAX;
+               }
+           }
+       }
+    }
+}
+\f
+/* Handle interrupt_handler, exception_handler and nmi_handler function
+   attributes; arguments as in struct attribute_spec.handler.  */
+
+static tree
+handle_int_attribute (tree *node, tree name,
+                     tree args ATTRIBUTE_UNUSED,
+                     int flags ATTRIBUTE_UNUSED,
+                     bool *no_add_attrs)
+{
+  tree x = *node;
+  if (TREE_CODE (x) == FUNCTION_DECL)
+    x = TREE_TYPE (x);
+
+  if (TREE_CODE (x) != FUNCTION_TYPE)
+    {
+      warning ("%qs attribute only applies to functions",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+  else if (funkind (x) != SUBROUTINE)
+    error ("multiple function type attributes specified");
+
+  return NULL_TREE;
+}
+
+/* Return 0 if the attributes for two types are incompatible, 1 if they
+   are compatible, and 2 if they are nearly compatible (which causes a
+   warning to be generated).  */
+
+static int
+bfin_comp_type_attributes (tree type1, tree type2)
+{
+  e_funkind kind1, kind2;
+
+  if (TREE_CODE (type1) != FUNCTION_TYPE)
+    return 1;
+
+  kind1 = funkind (type1);
+  kind2 = funkind (type2);
+
+  if (kind1 != kind2)
+    return 0;
+  
+  /*  Check for mismatched modifiers */
+  if (!lookup_attribute ("nesting", TYPE_ATTRIBUTES (type1))
+      != !lookup_attribute ("nesting", TYPE_ATTRIBUTES (type2)))
+    return 0;
+
+  if (!lookup_attribute ("saveall", TYPE_ATTRIBUTES (type1))
+      != !lookup_attribute ("saveall", TYPE_ATTRIBUTES (type2)))
+    return 0;
+
+  if (!lookup_attribute ("kspisusp", TYPE_ATTRIBUTES (type1))
+      != !lookup_attribute ("kspisusp", TYPE_ATTRIBUTES (type2)))
+    return 0;
+
+  return 1;
+}
+
+/* Table of valid machine attributes.  */
+const struct attribute_spec bfin_attribute_table[] =
+{
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  { "interrupt_handler", 0, 0, false, true,  true, handle_int_attribute },
+  { "exception_handler", 0, 0, false, true,  true, handle_int_attribute },
+  { "nmi_handler", 0, 0, false, true,  true, handle_int_attribute },
+  { "nesting", 0, 0, false, true,  true, NULL },
+  { "kspisusp", 0, 0, false, true,  true, NULL },
+  { "saveall", 0, 0, false, true,  true, NULL },
+  { NULL, 0, 0, false, false, false, NULL }
+};
+\f
+/* Output the assembler code for a thunk function.  THUNK_DECL is the
+   declaration for the thunk function itself, FUNCTION is the decl for
+   the target function.  DELTA is an immediate constant offset to be
+   added to THIS.  If VCALL_OFFSET is nonzero, the word at
+   *(*this + vcall_offset) should be added to THIS.  */
+
+static void
+bfin_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
+                     tree thunk ATTRIBUTE_UNUSED, HOST_WIDE_INT delta,
+                     HOST_WIDE_INT vcall_offset, tree function)
+{
+  rtx xops[3];
+  /* The this parameter is passed as the first argument.  */
+  rtx this = gen_rtx_REG (Pmode, REG_R0);
+
+  /* Adjust the this parameter by a fixed constant.  */
+  if (delta)
+    {
+      xops[1] = this;
+      if (delta >= -64 && delta <= 63)
+       {
+         xops[0] = GEN_INT (delta);
+         output_asm_insn ("%1 += %0;", xops);
+       }
+      else if (delta >= -128 && delta < -64)
+       {
+         xops[0] = GEN_INT (delta + 64);
+         output_asm_insn ("%1 += -64; %1 += %0;", xops);
+       }
+      else if (delta > 63 && delta <= 126)
+       {
+         xops[0] = GEN_INT (delta - 63);
+         output_asm_insn ("%1 += 63; %1 += %0;", xops);
+       }
+      else
+       {
+         xops[0] = GEN_INT (delta);
+         output_asm_insn ("r3.l = %h0; r3.h = %d0; %1 = %1 + r3;", xops);
+       }
+    }
+
+  /* Adjust the this parameter by a value stored in the vtable.  */
+  if (vcall_offset)
+    {
+      rtx p2tmp = gen_rtx_REG (Pmode, REG_P2);
+      rtx tmp = gen_rtx_REG (Pmode, REG_R2);
+
+      xops[1] = tmp;
+      xops[2] = p2tmp;
+      output_asm_insn ("%2 = r0; %2 = [%2];", xops);
+
+      /* Adjust the this parameter.  */
+      xops[0] = gen_rtx_MEM (Pmode, plus_constant (p2tmp, vcall_offset));
+      if (!memory_operand (xops[0], Pmode))
+       {
+         rtx tmp2 = gen_rtx_REG (Pmode, REG_P1);
+         xops[0] = GEN_INT (vcall_offset);
+         xops[1] = tmp2;
+         output_asm_insn ("%h1 = %h0; %d1 = %d0; %2 = %2 + %1", xops);
+         xops[0] = gen_rtx_MEM (Pmode, p2tmp);
+       }
+      xops[2] = this;
+      output_asm_insn ("%1 = %0; %2 = %2 + %1;", xops);
+    }
+
+  xops[0] = XEXP (DECL_RTL (function), 0);
+  if (1 || !flag_pic || (*targetm.binds_local_p) (function))
+    output_asm_insn ("jump.l\t%P0", xops);
+}
+\f
+#undef TARGET_ASM_GLOBALIZE_LABEL
+#define TARGET_ASM_GLOBALIZE_LABEL bfin_globalize_label 
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START output_file_start
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE bfin_attribute_table
+
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES bfin_comp_type_attributes
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS bfin_rtx_costs
+
+#undef  TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST bfin_address_cost
+
+#undef TARGET_ASM_INTERNAL_LABEL
+#define TARGET_ASM_INTERNAL_LABEL bfin_internal_label
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG bfin_reorg
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL bfin_function_ok_for_sibcall
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK bfin_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
+
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST bfin_adjust_cost
+
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES bfin_arg_partial_bytes
+
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE bfin_pass_by_reference
+
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS setup_incoming_varargs
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX bfin_struct_value_rtx
+
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P bfin_vector_mode_supported_p
+
+struct gcc_target targetm = TARGET_INITIALIZER;
diff --git a/gcc/config/bfin/bfin.h b/gcc/config/bfin/bfin.h
new file mode 100644 (file)
index 0000000..d52d407
--- /dev/null
@@ -0,0 +1,1168 @@
+/* Definitions for the Blackfin port.
+   Copyright (C) 2005  Free Software Foundation, Inc.
+   Contributed by Analog Devices.
+
+   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 2, 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 COPYING.  If not, write to
+   the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _BFIN_CONFIG
+#define _BFIN_CONFIG
+
+#define OBJECT_FORMAT_ELF
+
+#define BRT 1
+#define BRF 0
+
+/* Print subsidiary information on the compiler version in use.  */
+#define TARGET_VERSION fprintf (stderr, " (BlackFin bfin)")
+
+/* Run-time compilation parameters selecting different hardware subsets.  */
+
+extern int target_flags;
+
+/* Predefinition in the preprocessor for this target machine */
+#ifndef TARGET_CPU_CPP_BUILTINS
+#define TARGET_CPU_CPP_BUILTINS()               \
+  do                                            \
+    {                                           \
+      builtin_define ("bfin");                  \
+      builtin_define ("BFIN");                  \
+    }                                           \
+  while (0)
+#endif
+
+/* Generate DSP instructions, like DSP halfword loads */
+#define TARGET_DSP                     (1)
+
+#define TARGET_DEFAULT MASK_CSYNC
+
+/* This macro is similar to `TARGET_SWITCHES' but defines names of
+   command options that have values.  Its definition is an
+   initializer with a subgrouping for each command option.
+
+   Each subgrouping contains a string constant, that defines the
+   fixed part of the option name, and the address of a variable.  The
+   variable, type `char *', is set to the variable part of the given
+   option if the fixed part matches.  The actual option name is made
+   by appending `-m' to the specified name.  */
+#define TARGET_OPTIONS                                                 \
+{ { "shared-library-id=",      &bfin_library_id_string,                \
+    "ID of shared library to build", 0}                                        \
+}
+
+/* Maximum number of library ids we permit */
+#define MAX_LIBRARY_ID 255
+
+extern const char *bfin_library_id_string;
+
+/* Sometimes certain combinations of command options do not make
+   sense on a particular target machine.  You can define a macro
+   `OVERRIDE_OPTIONS' to take account of this.  This macro, if
+   defined, is executed once just after all the command options have
+   been parsed.
+   Don't use this macro to turn on various extra optimizations for
+   `-O'.  That is what `OPTIMIZATION_OPTIONS' is for.  */
+#define OVERRIDE_OPTIONS override_options ()
+
+#define FUNCTION_MODE    SImode
+#define Pmode            SImode
+
+/* store-condition-codes instructions store 0 for false
+   This is the value stored for true.  */
+#define STORE_FLAG_VALUE 1
+
+/* Define this if pushing a word on the stack
+   makes the stack pointer a smaller address.  */
+#define STACK_GROWS_DOWNWARD
+
+#define STACK_PUSH_CODE PRE_DEC
+
+/* Define this if the nominal address of the stack frame
+   is at the high-address end of the local variables;
+   that is, each additional local variable allocated
+   goes at a more negative offset in the frame.  */
+#define FRAME_GROWS_DOWNWARD
+
+/* We define a dummy ARGP register; the parameters start at offset 0 from
+   it. */
+#define FIRST_PARM_OFFSET(DECL) 0
+
+/* Offset within stack frame to start allocating local variables at.
+   If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
+   first local allocated.  Otherwise, it is the offset to the BEGINNING
+   of the first local allocated.  */
+#define STARTING_FRAME_OFFSET 0
+
+/* Register to use for pushing function arguments.  */
+#define STACK_POINTER_REGNUM REG_P6
+
+/* Base register for access to local variables of the function.  */
+#define FRAME_POINTER_REGNUM REG_P7
+
+/* A dummy register that will be eliminated to either FP or SP.  */
+#define ARG_POINTER_REGNUM REG_ARGP
+
+/* `PIC_OFFSET_TABLE_REGNUM'
+     The register number of the register used to address a table of
+     static data addresses in memory.  In some cases this register is
+     defined by a processor's "application binary interface" (ABI).
+     When this macro is defined, RTL is generated for this register
+     once, as with the stack pointer and frame pointer registers.  If
+     this macro is not defined, it is up to the machine-dependent files
+     to allocate such a register (if necessary). */
+#define PIC_OFFSET_TABLE_REGNUM (REG_P5)
+
+/* A static chain register for nested functions.  We need to use a
+   call-clobbered register for this.  */
+#define STATIC_CHAIN_REGNUM REG_P2
+
+/* Define this if functions should assume that stack space has been
+   allocated for arguments even when their values are passed in
+   registers.
+
+   The value of this macro is the size, in bytes, of the area reserved for
+   arguments passed in registers.
+
+   This space can either be allocated by the caller or be a part of the
+   machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE'
+   says which.  */
+#define FIXED_STACK_AREA 12
+#define REG_PARM_STACK_SPACE(FNDECL) FIXED_STACK_AREA
+
+/* Define this if the above stack space is to be considered part of the
+ * space allocated by the caller.  */
+#define OUTGOING_REG_PARM_STACK_SPACE
+         
+/* Define this if the maximum size of all the outgoing args is to be
+   accumulated and pushed during the prologue.  The amount can be
+   found in the variable current_function_outgoing_args_size. */ 
+#define ACCUMULATE_OUTGOING_ARGS 1
+
+/* Value should be nonzero if functions must have frame pointers.
+   Zero means the frame pointer need not be set up (and parms
+   may be accessed via the stack pointer) in functions that seem suitable.
+   This is computed in `reload', in reload1.c.  
+*/
+#define FRAME_POINTER_REQUIRED (bfin_frame_pointer_required ())
+
+#define PARM_BOUNDRY            32
+
+#define STACK_BOUNDRY           32
+
+/*#define DATA_ALIGNMENT(TYPE, BASIC-ALIGN) for arrays.. */
+
+/* Make strings word-aligned so strcpy from constants will be faster.  */
+#define CONSTANT_ALIGNMENT(EXP, ALIGN)  \
+  (TREE_CODE (EXP) == STRING_CST        \
+   && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))    
+
+#define TRAMPOLINE_SIZE 18
+#define TRAMPOLINE_TEMPLATE(FILE)                                       \
+  fprintf(FILE, "\t.dd\t0x0000e109\n"); /* p1.l = fn low */            \
+  fprintf(FILE, "\t.dd\t0x0000e149\n"); /* p1.h = fn high */;          \
+  fprintf(FILE, "\t.dd\t0x0000e10a\n"); /* p2.l = sc low */;           \
+  fprintf(FILE, "\t.dd\t0x0000e14a\n"); /* p2.h = sc high */;          \
+  fprintf(FILE, "\t.dw\t0x0051\n"); /* jump (p1)*/
+
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
+  initialize_trampoline (TRAMP, FNADDR, CXT)
+\f
+/* Definitions for register eliminations.
+
+   This is an array of structures.  Each structure initializes one pair
+   of eliminable registers.  The "from" register number is given first,
+   followed by "to".  Eliminations of the same "from" register are listed
+   in order of preference.
+
+   There are two registers that can always be eliminated on the i386.
+   The frame pointer and the arg pointer can be replaced by either the
+   hard frame pointer or to the stack pointer, depending upon the
+   circumstances.  The hard frame pointer is not used before reload and
+   so it is not eligible for elimination.  */
+
+#define ELIMINABLE_REGS                                \
+{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM},  \
+ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM},  \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}        \
+
+/* Given FROM and TO register numbers, say whether this elimination is
+   allowed.  Frame pointer elimination is automatically handled.
+
+   All other eliminations are valid.  */
+
+#define CAN_ELIMINATE(FROM, TO) \
+  ((TO) == STACK_POINTER_REGNUM ? ! frame_pointer_needed : 1)
+
+/* Define the offset between two registers, one to be eliminated, and the other
+   its replacement, at the start of a routine.  */
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+  ((OFFSET) = bfin_initial_elimination_offset ((FROM), (TO)))
+\f
+/* This processor has
+   8 data register for doing arithmetic
+   8  pointer register for doing addressing, including
+      1  stack pointer P6
+      1  frame pointer P7
+   4 sets of indexing registers (I0-3, B0-3, L0-3, M0-3)
+   1  condition code flag register CC
+   5  return address registers RETS/I/X/N/E
+   1  arithmetic status register (ASTAT).  */
+
+#define FIRST_PSEUDO_REGISTER 44
+
+#define PREG_P(X) (REG_P (X) && REGNO (X) >= REG_P0 && REGNO (X) <= REG_P7)
+#define ADDRESS_REGNO_P(X) ((X) >= REG_P0 && (X) <= REG_M3)
+#define D_REGNO_P(X) ((X) <= REG_R7)
+
+#define REGISTER_NAMES { \
+  "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", \
+  "P0", "P1", "P2", "P3", "P4", "P5", "SP", "FP", \
+  "I0", "B0", "L0", "I1", "B1", "L1", "I2", "B2", \
+  "L2", "I3", "B3", "L3", "M0", "M1", "M2", "M3", \
+  "A0", "A1", \
+  "CC", \
+  "RETS", "RETI", "RETX", "RETN", "RETE", "ASTAT", "SEQSTAT", "USP", \
+  "ARGP" \
+}
+
+#define SHORT_REGISTER_NAMES { \
+       "R0.L", "R1.L", "R2.L", "R3.L", "R4.L", "R5.L", "R6.L", "R7.L", \
+       "P0.L", "P1.L", "P2.L", "P3.L", "P4.L", "P5.L", "SP.L", "FP.L", \
+       "I0.L", "B0.L", "L0.L", "I1.L", "B1.L", "L1.L", "I2.L", "B2.L", \
+       "L2.L", "I3.L", "B3.L", "L3.L", "M0.L", "M1.L", "M2.L", "M3.L", }
+
+#define HIGH_REGISTER_NAMES { \
+       "R0.H", "R1.H", "R2.H", "R3.H", "R4.H", "R5.H", "R6.H", "R7.H", \
+       "P0.H", "P1.H", "P2.H", "P3.H", "P4.H", "P5.H", "SP.H", "FP.H", \
+       "I0.H", "B0.H", "L0.H", "I1.H", "B1.H", "L1.H", "I2.H", "B2.H", \
+       "L2.H", "I3.H", "B3.H", "L3.H", "M0.H", "M1.H", "M2.H", "M3.H", }
+
+#define DREGS_PAIR_NAMES { \
+  "R1:0.p", 0, "R3:2.p", 0, "R5:4.p", 0, "R7:6.p", 0,  }
+
+#define BYTE_REGISTER_NAMES { \
+  "R0.B", "R1.B", "R2.B", "R3.B", "R4.B", "R5.B", "R6.B", "R7.B",  }
+
+
+/* 1 for registers that have pervasive standard uses
+   and are not available for the register allocator.  */
+
+#define FIXED_REGISTERS \
+/*r0 r1 r2 r3 r4 r5 r6 r7   p0 p1 p2 p3 p4 p5 p6 p7 */ \
+{ 0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 1, 0,    \
+/*i0 b0 l0 i1 b1 l1 i2 b2   l2 i3 b3 l3 m0 m1 m2 m3 */ \
+  0, 0, 1, 0, 0, 1, 0, 0,   1, 0, 0, 1, 0, 0, 0, 0,    \
+/*a0 a1 cc rets/i/x/n/e     astat seqstat usp argp */ \
+  0, 0, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1  \
+}
+
+/* 1 for registers not available across function calls.
+   These must include the FIXED_REGISTERS and also any
+   registers that can be used without being saved.
+   The latter must include the registers where values are returned
+   and the register where structure-value addresses are passed.
+   Aside from that, you can include as many other registers as you like.  */
+
+#define CALL_USED_REGISTERS \
+/*r0 r1 r2 r3 r4 r5 r6 r7   p0 p1 p2 p3 p4 p5 p6 p7 */ \
+{ 1, 1, 1, 1, 0, 0, 0, 0,   1, 1, 1, 0, 0, 0, 1, 0, \
+/*i0 b0 l0 i1 b1 l1 i2 b2   l2 i3 b3 l3 m0 m1 m2 m3 */ \
+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,   \
+/*a0 a1 cc rets/i/x/n/e     astat seqstat usp argp */ \
+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1  \
+}
+
+/* Order in which to allocate registers.  Each register must be
+   listed once, even those in FIXED_REGISTERS.  List frame pointer
+   late and fixed registers last.  Note that, in general, we prefer
+   registers listed in CALL_USED_REGISTERS, keeping the others
+   available for storage of persistent values. */
+
+#define REG_ALLOC_ORDER \
+{ REG_R0, REG_R1, REG_R2, REG_R3, REG_R7, REG_R6, REG_R5, REG_R4, \
+  REG_P2, REG_P1, REG_P0, REG_P5, REG_P4, REG_P3, REG_P6, REG_P7, \
+  REG_A0, REG_A1, \
+  REG_I0, REG_B0, REG_L0, REG_I1, REG_B1, REG_L1, REG_I2, REG_B2, \
+  REG_L2, REG_I3, REG_B3, REG_L3, REG_M0, REG_M1, REG_M2, REG_M3, \
+  REG_RETS, REG_RETI, REG_RETX, REG_RETN, REG_RETE,              \
+  REG_ASTAT, REG_SEQSTAT, REG_USP,                               \
+  REG_CC, REG_ARGP                                               \
+}
+
+/* Macro to conditionally modify fixed_regs/call_used_regs.  */
+#define CONDITIONAL_REGISTER_USAGE                     \
+  {                                                    \
+    conditional_register_usage();                       \
+    if (flag_pic)                                      \
+      {                                                        \
+       fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;        \
+       call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;    \
+      }                                                        \
+  }
+
+/* Define the classes of registers for register constraints in the
+   machine description.  Also define ranges of constants.
+
+   One of the classes must always be named ALL_REGS and include all hard regs.
+   If there is more than one class, another class must be named NO_REGS
+   and contain no registers.
+
+   The name GENERAL_REGS must be the name of a class (or an alias for
+   another name such as ALL_REGS).  This is the class of registers
+   that is allowed by "g" or "r" in a register constraint.
+   Also, registers outside this class are allocated only when
+   instructions express preferences for them.
+
+   The classes must be numbered in nondecreasing order; that is,
+   a larger-numbered class must never be contained completely
+   in a smaller-numbered class.
+
+   For any two classes, it is very desirable that there be another
+   class that represents their union. */
+
+
+enum reg_class
+{
+  NO_REGS,
+  IREGS,
+  BREGS,
+  LREGS,
+  MREGS,
+  CIRCREGS, /* Circular buffering registers, Ix, Bx, Lx together form. See Automatic Circlur Buffering */
+  DAGREGS,
+  EVEN_AREGS,
+  ODD_AREGS,
+  AREGS,
+  CCREGS,
+  EVEN_DREGS,
+  ODD_DREGS,
+  DREGS,
+  PREGS_CLOBBERED,
+  PREGS,
+  DPREGS,
+  MOST_REGS,
+  PROLOGUE_REGS,
+  NON_A_CC_REGS,
+  ALL_REGS, LIM_REG_CLASSES
+};
+
+#define N_REG_CLASSES ((int)LIM_REG_CLASSES)
+
+#define GENERAL_REGS DPREGS
+
+/* Give names of register classes as strings for dump file.   */
+
+#define REG_CLASS_NAMES \
+{  "NO_REGS",          \
+   "IREGS",            \
+   "BREGS",            \
+   "LREGS",            \
+   "MREGS",            \
+   "CIRCREGS",         \
+   "DAGREGS",          \
+   "EVEN_AREGS",       \
+   "ODD_AREGS",                \
+   "AREGS",            \
+   "CCREGS",           \
+   "EVEN_DREGS",       \
+   "ODD_DREGS",                \
+   "DREGS",            \
+   "PREGS_CLOBBERED",  \
+   "PREGS",            \
+   "DPREGS",           \
+   "MOST_REGS",                \
+   "PROLOGUE_REGS",    \
+   "NON_A_CC_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'.  */
+
+/* NOTE: DSP registers, IREGS - AREGS, are not GENERAL_REGS.  We use
+   MOST_REGS as the union of DPREGS and DAGREGS.  */
+
+#define REG_CLASS_CONTENTS \
+    /* 31 - 0       63-32   */ \
+{   { 0x00000000,    0 },              /* NO_REGS */   \
+    { 0x02490000,    0 },              /* IREGS */     \
+    { 0x04920000,    0 },              /* BREGS */             \
+    { 0x09240000,    0 },              /* LREGS */     \
+    { 0xf0000000,    0 },              /* MREGS */   \
+    { 0x0fff0000,    0 },              /* CIRCREGS */   \
+    { 0xffff0000,    0 },              /* DAGREGS */   \
+    { 0x00000000,    0x1 },            /* EVEN_AREGS */   \
+    { 0x00000000,    0x2 },            /* ODD_AREGS */   \
+    { 0x00000000,    0x3 },            /* AREGS */   \
+    { 0x00000000,    0x4 },            /* CCREGS */  \
+    { 0x00000055,    0 },              /* EVEN_DREGS */   \
+    { 0x000000aa,    0 },              /* ODD_DREGS */   \
+    { 0x000000ff,    0 },              /* DREGS */   \
+    { 0x00004700,    0x800 },          /* PREGS_CLOBBERED */   \
+    { 0x0000ff00,    0x800 },          /* PREGS */   \
+    { 0x0000ffff,    0x800 },          /* DPREGS */   \
+    { 0xffffffff,    0x800 },          /* MOST_REGS */\
+    { 0x00000000,    0x7f8 },          /* PROLOGUE_REGS */\
+    { 0xffffffff,    0xff8 },          /* NON_A_CC_REGS */\
+    { 0xffffffff,    0xfff }}          /* ALL_REGS */
+
+#define BASE_REG_CLASS          PREGS
+#define INDEX_REG_CLASS         PREGS
+
+#define REGNO_OK_FOR_BASE_STRICT_P(X) (REGNO_REG_CLASS (X) == BASE_REG_CLASS)
+#define REGNO_OK_FOR_BASE_NONSTRICT_P(X)  \
+ (((X) >= FIRST_PSEUDO_REGISTER) || REGNO_REG_CLASS (X) == BASE_REG_CLASS)
+
+#ifdef REG_OK_STRICT
+#define REGNO_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_STRICT_P (X)
+#else
+#define REGNO_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_NONSTRICT_P (X)
+#endif
+
+#define REG_OK_FOR_BASE_P(X)    (REG_P (X) && REGNO_OK_FOR_BASE_P (REGNO (X)))
+#define REG_OK_FOR_INDEX_P(X)   0
+#define REGNO_OK_FOR_INDEX_P(X)   0
+
+/* Get reg_class from a letter such as appears in the machine description.  */
+
+#define REG_CLASS_FROM_LETTER(LETTER)  \
+  ((LETTER) == 'a' ? PREGS :            \
+   (LETTER) == 'd' ? DREGS :           \
+   (LETTER) == 'z' ? PREGS_CLOBBERED : \
+   (LETTER) == 'D' ? EVEN_DREGS :      \
+   (LETTER) == 'W' ? ODD_DREGS :       \
+   (LETTER) == 'e' ? AREGS :           \
+   (LETTER) == 'A' ? EVEN_AREGS :      \
+   (LETTER) == 'B' ? ODD_AREGS :       \
+   (LETTER) == 'b' ? IREGS :            \
+   (LETTER) == 'B' ? BREGS :            \
+   (LETTER) == 'f' ? MREGS :           \
+   (LETTER) == 'c' ? CIRCREGS :         \
+   (LETTER) == 'C' ? CCREGS :          \
+   (LETTER) == 'x' ? MOST_REGS :       \
+   (LETTER) == 'y' ? PROLOGUE_REGS :   \
+   (LETTER) == 'w' ? NON_A_CC_REGS :   \
+   NO_REGS)
+
+/* The same information, inverted:
+   Return the class number of the smallest class containing
+   reg number REGNO.  This could be a conditional expression
+   or could index an array.  */
+
+#define REGNO_REG_CLASS(REGNO) \
+ ((REGNO) < REG_P0 ? DREGS                             \
+ : (REGNO) < REG_I0 ? PREGS                            \
+ : (REGNO) == REG_ARGP ? BASE_REG_CLASS                        \
+ : (REGNO) >= REG_I0 && (REGNO) <= REG_I3 ? IREGS      \
+ : (REGNO) >= REG_L0 && (REGNO) <= REG_L3 ? LREGS      \
+ : (REGNO) >= REG_B0 && (REGNO) <= REG_B3 ? BREGS      \
+ : (REGNO) >= REG_M0 && (REGNO) <= REG_M3 ? MREGS      \
+ : (REGNO) == REG_A0 || (REGNO) == REG_A1 ? AREGS      \
+ : (REGNO) == REG_CC ? CCREGS                          \
+ : (REGNO) >= REG_RETS ? PROLOGUE_REGS                 \
+ : NO_REGS)
+
+/* When defined, the compiler allows registers explicitly used in the
+   rtl to be used as spill registers but prevents the compiler from
+   extending the lifetime of these registers. */
+#define SMALL_REGISTER_CLASSES 1
+
+#define CLASS_LIKELY_SPILLED_P(CLASS) \
+    ((CLASS) == PREGS_CLOBBERED \
+     || (CLASS) == PROLOGUE_REGS \
+     || (CLASS) == CCREGS)
+
+/* Do not allow to store a value in REG_CC for any mode */
+/* Do not allow to store value in pregs if mode is not SI*/
+#define HARD_REGNO_MODE_OK(REGNO, MODE) hard_regno_mode_ok((REGNO), (MODE))
+
+/* Return the maximum number of consecutive registers
+   needed to represent mode MODE in a register of class CLASS.  */
+#define CLASS_MAX_NREGS(CLASS, MODE)   \
+  ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+((MODE) == PDImode && ((REGNO) == REG_A0 || (REGNO) == REG_A1) \
+ ? 1 : CLASS_MAX_NREGS (GENERAL_REGS, MODE))
+
+/* A C expression that is nonzero if hard register TO can be
+   considered for use as a rename register for FROM register */
+#define HARD_REGNO_RENAME_OK(FROM, TO) bfin_hard_regno_rename_ok (FROM, TO)
+
+/* 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))
+
+/* `PREFERRED_RELOAD_CLASS (X, CLASS)'
+   A C expression that places additional restrictions on the register
+   class to use when it is necessary to copy value X into a register
+   in class CLASS.  The value is a register class; perhaps CLASS, or
+   perhaps another, smaller class.  */
+#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
+
+#define  SECONDARY_OUTPUT_RELOAD_CLASS(class,mode,x) \
+    secondary_output_reload_class(class,mode,x)
+#define  SECONDARY_INPUT_RELOAD_CLASS(class,mode,x)  \
+    secondary_input_reload_class(class,mode,x)
+
+/* Function Calling Conventions. */
+
+/* The type of the current function; normal functions are of type
+   SUBROUTINE.  */
+typedef enum {
+  SUBROUTINE, INTERRUPT_HANDLER, EXCPT_HANDLER, NMI_HANDLER
+} e_funkind;
+
+#define FUNCTION_ARG_REGISTERS { REG_R0, REG_R1, REG_R2, -1 }
+
+typedef struct {
+  int words;                   /* # words passed so far */
+  int nregs;                   /* # registers available for passing */
+  int *arg_regs;               /* array of register -1 terminated */
+} CUMULATIVE_ARGS;
+
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).  */
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+  (function_arg (&CUM, MODE, TYPE, NAMED))
+
+#define FUNCTION_ARG_REGNO_P(REGNO) function_arg_regno_p (REGNO)
+
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  */
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT, N_NAMED_ARGS)        \
+  (init_cumulative_args (&CUM, FNTYPE, LIBNAME))
+
+/* Update the data in CUM to advance over an argument
+   of mode MODE and data type TYPE.
+   (TYPE is null for libcalls where that information may not be available.)  */
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)   \
+  (function_arg_advance (&CUM, MODE, TYPE, NAMED))
+
+#define RETURN_POPS_ARGS(FDECL, FUNTYPE, STKSIZE) 0
+
+/* Define how to find the value returned by a function.
+   VALTYPE is the data type of the value (as a tree).
+   If the precise function being called is known, FUNC is its FUNCTION_DECL;
+   otherwise, FUNC is 0.
+*/
+
+#define VALUE_REGNO(MODE) (REG_R0)
+
+#define FUNCTION_VALUE(VALTYPE, FUNC)          \
+  gen_rtx_REG (TYPE_MODE (VALTYPE),            \
+              VALUE_REGNO(TYPE_MODE(VALTYPE)))
+
+/* Define how to find the value returned by a library function
+   assuming the value has mode MODE.  */
+
+#define LIBCALL_VALUE(MODE)  gen_rtx_REG (MODE, VALUE_REGNO(MODE))
+
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == REG_R0)
+
+#define DEFAULT_PCC_STRUCT_RETURN 0
+#define RETURN_IN_MEMORY(TYPE) bfin_return_in_memory(TYPE)
+
+/* Before the prologue, the return address is in the RETS register.  */
+#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, REG_RETS)
+
+#define RETURN_ADDR_RTX(COUNT, FRAME) bfin_return_addr_rtx (COUNT)
+
+#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (REG_RETS)
+
+/* Call instructions don't modify the stack pointer on the Blackfin.  */
+#define INCOMING_FRAME_SP_OFFSET 0
+
+/* Describe how we implement __builtin_eh_return.  */
+#define EH_RETURN_DATA_REGNO(N)        ((N) < 2 ? (N) : INVALID_REGNUM)
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, REG_P2)
+#define EH_RETURN_HANDLER_RTX \
+    gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx, UNITS_PER_WORD))
+
+/* Addressing Modes */
+
+/* Recognize any constant value that is a valid address.  */
+#define CONSTANT_ADDRESS_P(X)  (CONSTANT_P (X))
+
+/* Nonzero if the constant value X is a legitimate general operand.
+   symbol_ref are not legitimate and will be put into constant pool.
+   See force_const_mem().
+   If -mno-pool, all constants are legitimate.
+ */
+#define LEGITIMATE_CONSTANT_P(x) 1
+
+/*   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 `GO_IF_LEGITIMATE_ADDRESS'
+     would ever accept. */
+#define MAX_REGS_PER_ADDRESS 1
+
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
+   that is a valid memory address for an instruction.
+   The MODE argument is the machine mode for the MEM expression
+   that wants to use this address. 
+
+   Blackfin addressing modes are as follows:
+
+      [preg]
+      [preg + imm16]
+
+      B [ Preg + uimm15 ]
+      W [ Preg + uimm16m2 ]
+      [ Preg + uimm17m4 ] 
+
+      [preg++]
+      [preg--]
+      [--sp]
+*/
+
+#define LEGITIMATE_MODE_FOR_AUTOINC_P(MODE) \
+      (GET_MODE_SIZE (MODE) <= 4 || (MODE) == PDImode)
+
+#ifdef REG_OK_STRICT
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN)         \
+  do {                                                 \
+    if (bfin_legitimate_address_p (MODE, X, 1))                \
+      goto WIN;                                                \
+  } while (0);
+#else
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN)         \
+  do {                                                 \
+    if (bfin_legitimate_address_p (MODE, X, 0))                \
+      goto WIN;                                                \
+  } while (0);
+#endif
+
+/* Try machine-dependent ways of modifying an illegitimate address
+   to be legitimate.  If we find one, return the new, valid address.
+   This macro is used in only one place: `memory_address' in explow.c.
+
+   OLDX is the address as it was before break_out_memory_refs was called.
+   In some cases it is useful to look at this to decide what needs to be done.
+
+   MODE and WIN are passed so that this macro can use
+   GO_IF_LEGITIMATE_ADDRESS.
+
+   It is always safe for this macro to do nothing.  It exists to recognize
+   opportunities to optimize the output.
+ */
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN)    \
+do {                                          \
+   rtx _q = legitimize_address(X, OLDX, MODE); \
+   if (_q) { X = _q; goto WIN; }              \
+} while (0)
+
+#define HAVE_POST_INCREMENT 1
+#define HAVE_POST_DECREMENT 1
+#define HAVE_PRE_DECREMENT  1
+
+/* `LEGITIMATE_PIC_OPERAND_P (X)'
+     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) ! SYMBOLIC_CONST (X)
+
+#define SYMBOLIC_CONST(X)      \
+(GET_CODE (X) == SYMBOL_REF                                            \
+ || GET_CODE (X) == LABEL_REF                                          \
+ || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X)))
+
+/*
+     A C statement or compound statement with a conditional `goto
+     LABEL;' executed if memory address X (an RTX) can have different
+     meanings depending on the machine mode of the memory reference it
+     is used for or if the address is valid for some modes but not
+     others.
+
+     Autoincrement and autodecrement addresses typically have
+     mode-dependent effects because the amount of the increment or
+     decrement is the size of the operand being addressed.  Some
+     machines have other mode-dependent addresses.  Many RISC machines
+     have no mode-dependent addresses.
+
+     You may assume that ADDR is a valid address for the machine.
+*/
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)  \
+do {                                              \
+ if (GET_CODE (ADDR) == POST_INC                  \
+     || GET_CODE (ADDR) == POST_DEC               \
+     || GET_CODE (ADDR) == PRE_DEC)               \
+   goto LABEL;                                   \
+} while (0)
+
+#define NOTICE_UPDATE_CC(EXPR, INSN) 0
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+   is done just by pretending it is already truncated.  */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+/* Max number of bytes we can move from memory to memory
+   in one reasonably fast instruction.  */
+#define MOVE_MAX UNITS_PER_WORD
+
+
+/* STORAGE LAYOUT: target machine storage layout
+   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  0
+#define SLOW_SHORT_ACCESS 0
+
+/* Define this if most significant bit is lowest numbered
+   in instructions that operate on numbered bit-fields. */
+#define BITS_BIG_ENDIAN  0
+
+/* Define this if most significant byte of a word is the lowest numbered.
+   We can't access bytes but if we could we would in the Big Endian order. */
+#define BYTES_BIG_ENDIAN 0
+
+/* Define this if most significant word of a multiword number is numbered. */
+#define WORDS_BIG_ENDIAN 0
+
+/* number of bits in an addressable storage unit */
+#define BITS_PER_UNIT 8
+
+/* Width in bits of a "word", which is the contents of a machine register.
+   Note that this is not necessarily the width of data type `int';
+   if using 16-bit ints on a 68000, this would still be 32.
+   But on a machine with 16-bit registers, this would be 16.  */
+#define BITS_PER_WORD 32
+
+/* Width of a word, in units (bytes).  */
+#define UNITS_PER_WORD 4
+
+/* Size of a vector for autovectorization.  */
+#define UNITS_PER_SIMD_WORD 4
+
+/* Width in bits of a pointer.
+   See also the macro `Pmode1' defined below.  */
+#define POINTER_SIZE 32
+
+/* Allocation boundary (in *bits*) for storing pointers in memory.  */
+#define POINTER_BOUNDARY 32
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list.  */
+#define PARM_BOUNDARY 32
+
+/* Boundary (in *bits*) on which stack pointer should be aligned.  */
+#define STACK_BOUNDARY 32
+
+/* Allocation boundary (in *bits*) for the code of a function.  */
+#define FUNCTION_BOUNDARY 32
+
+/* Alignment of field after `int : 0' in a structure.  */
+#define EMPTY_FIELD_BOUNDARY BITS_PER_WORD
+
+/* No data type wants to be aligned rounder than this.  */
+#define BIGGEST_ALIGNMENT 32
+
+/* Define this if move instructions will actually fail to work
+   when given unaligned data.  */
+#define STRICT_ALIGNMENT 1
+
+/* (shell-command "rm c-decl.o stor-layout.o")
+ *  never define PCC_BITFIELD_TYPE_MATTERS
+ *  really cause some alignment problem
+ */
+
+#define UNITS_PER_FLOAT  ((FLOAT_TYPE_SIZE  + BITS_PER_UNIT - 1) / \
+                          BITS_PER_UNIT)
+
+#define UNITS_PER_DOUBLE ((DOUBLE_TYPE_SIZE + BITS_PER_UNIT - 1) / \
+                          BITS_PER_UNIT)
+
+
+/* what is the 'type' of size_t */
+#define SIZE_TYPE "long unsigned int"
+
+/* Define this as 1 if `char' should by default be signed; else as 0.  */
+#define DEFAULT_SIGNED_CHAR 1
+#define FLOAT_TYPE_SIZE BITS_PER_WORD
+#define SHORT_TYPE_SIZE 16 
+#define CHAR_TYPE_SIZE 8
+#define INT_TYPE_SIZE  32
+#define LONG_TYPE_SIZE 32
+#define LONG_LONG_TYPE_SIZE 64 
+
+/* Note: Fix this to depend on target switch. -- lev */
+
+/* Note: Try to implement double and force long double. -- tonyko
+ * #define __DOUBLES_ARE_FLOATS__
+ * #define DOUBLE_TYPE_SIZE FLOAT_TYPE_SIZE
+ * #define LONG_DOUBLE_TYPE_SIZE DOUBLE_TYPE_SIZE
+ * #define DOUBLES_ARE_FLOATS 1
+ */
+
+#define DOUBLE_TYPE_SIZE       64
+#define LONG_DOUBLE_TYPE_SIZE  64
+
+/* `PROMOTE_MODE (M, UNSIGNEDP, TYPE)'
+     A macro to update M 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 M.*/
+
+#define BFIN_PROMOTE_MODE_P(MODE) \
+    (!TARGET_DSP && GET_MODE_CLASS (MODE) == MODE_INT  \
+      && GET_MODE_SIZE (MODE) < UNITS_PER_WORD)
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE)     \
+  if (BFIN_PROMOTE_MODE_P(MODE))               \
+    {                                           \
+      if (MODE == QImode)                       \
+        UNSIGNEDP = 1;                          \
+      else if (MODE == HImode)                  \
+        UNSIGNEDP = 0;                         \
+      (MODE) = SImode;                          \
+    }
+
+/* Describing Relative Costs of Operations */
+
+/* Do not put function addr into constant pool */
+#define NO_FUNCTION_CSE 1
+
+/* A C expression for the cost of moving data from a register in class FROM to
+   one in class TO.  The classes are expressed using the enumeration values
+   such as `GENERAL_REGS'.  A value of 2 is the default; other values are
+   interpreted relative to that.
+
+   It is not required that the cost always equal 2 when FROM is the same as TO;
+   on some machines it is expensive to move between registers if they are not
+   general registers.  */
+
+#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
+   bfin_register_move_cost ((MODE), (CLASS1), (CLASS2))
+
+/* A C expression for the cost of moving data of mode M between a
+   register and memory.  A value of 2 is the default; this cost is
+   relative to those in `REGISTER_MOVE_COST'.
+
+   If moving between registers and memory is more expensive than
+   between two registers, you should define this macro to express the
+   relative cost.  */
+
+#define MEMORY_MOVE_COST(MODE, CLASS, IN)      \
+  bfin_memory_move_cost ((MODE), (CLASS), (IN))
+
+/* Specify the machine mode that this machine uses
+   for the index in the tablejump instruction.  */
+#define CASE_VECTOR_MODE SImode
+
+#define JUMP_TABLES_IN_TEXT_SECTION flag_pic
+
+/* Define if operations between registers always perform the operation
+   on the full register even if a narrower mode is specified. 
+#define WORD_REGISTER_OPERATIONS
+*/
+
+#define CONST_18UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 262140)
+#define CONST_16BIT_IMM_P(VALUE) ((VALUE) >= -32768 && (VALUE) <= 32767)
+#define CONST_16UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 65535)
+#define CONST_7BIT_IMM_P(VALUE) ((VALUE) >= -64 && (VALUE) <= 63)
+#define CONST_7NBIT_IMM_P(VALUE) ((VALUE) >= -64 && (VALUE) <= 0)
+#define CONST_5UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 31)
+#define CONST_4BIT_IMM_P(VALUE) ((VALUE) >= -8 && (VALUE) <= 7)
+#define CONST_4UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 15)
+#define CONST_3BIT_IMM_P(VALUE) ((VALUE) >= -4 && (VALUE) <= 3)
+#define CONST_3UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 7)
+
+#define CONSTRAINT_LEN(C, STR)                 \
+    ((C) == 'P' || (C) == 'M' || (C) == 'N' ? 2        \
+     : (C) == 'K' ? 3                          \
+     : DEFAULT_CONSTRAINT_LEN ((C), (STR)))
+
+#define CONST_OK_FOR_P(VALUE, STR)    \
+    ((STR)[1] == '0' ? (VALUE) == 0   \
+     : (STR)[1] == '1' ? (VALUE) == 1 \
+     : (STR)[1] == '2' ? (VALUE) == 2 \
+     : (STR)[1] == '3' ? (VALUE) == 3 \
+     : (STR)[1] == '4' ? (VALUE) == 4 \
+     : 0)
+
+#define CONST_OK_FOR_K(VALUE, STR)                     \
+    ((STR)[1] == 'u'                                   \
+     ? ((STR)[2] == '3' ? CONST_3UBIT_IMM_P (VALUE)    \
+       : (STR)[2] == '4' ? CONST_4UBIT_IMM_P (VALUE)   \
+       : (STR)[2] == '5' ? CONST_5UBIT_IMM_P (VALUE)   \
+       : (STR)[2] == 'h' ? CONST_16UBIT_IMM_P (VALUE)  \
+       : 0)                                            \
+     : (STR)[1] == 's'                                 \
+     ? ((STR)[2] == '3' ? CONST_3BIT_IMM_P (VALUE)     \
+       : (STR)[2] == '4' ? CONST_4BIT_IMM_P (VALUE)    \
+       : (STR)[2] == '7' ? CONST_7BIT_IMM_P (VALUE)    \
+       : (STR)[2] == 'h' ? CONST_16BIT_IMM_P (VALUE)   \
+       : 0)                                            \
+     : (STR)[1] == 'n'                                 \
+     ? ((STR)[2] == '7' ? CONST_7NBIT_IMM_P (VALUE)    \
+       : 0)                                            \
+     : 0)
+
+#define CONST_OK_FOR_M(VALUE, STR)                     \
+    ((STR)[1] == '1' ? (VALUE) == 255                  \
+     : (STR)[1] == '2' ? (VALUE) == 65535              \
+     : 0)
+
+/* The letters I, J, K, L and M in a register constraint string
+   can be used to stand for particular ranges of immediate operands.
+   This macro defines what the ranges are.
+   C is the letter, and VALUE is a constant value.
+   Return 1 if VALUE is in the range specified by C. 
+   
+   bfin constant operands are as follows
+   
+     J   2**N       5bit imm scaled
+     Ks7 -64 .. 63  signed 7bit imm
+     Ku5 0..31      unsigned 5bit imm
+     Ks4 -8 .. 7    signed 4bit imm
+     Ks3 -4 .. 3    signed 3bit imm
+     Ku3 0 .. 7     unsigned 3bit imm
+     Pn  0, 1, 2    constants 0, 1 or 2, corresponding to n
+*/
+#define CONST_OK_FOR_CONSTRAINT_P(VALUE, C, STR)               \
+  ((C) == 'J' ? (log2constp (VALUE))                           \
+   : (C) == 'K' ? CONST_OK_FOR_K (VALUE, STR)                  \
+   : (C) == 'L' ? log2constp (~(VALUE))                                \
+   : (C) == 'M' ? CONST_OK_FOR_M (VALUE, STR)                  \
+   : (C) == 'P' ? CONST_OK_FOR_P (VALUE, STR)                  \
+   : 0)
+
+     /*Constant Output Formats */
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
+  ((C) == 'H' ? 1 : 0)
+
+#define EXTRA_CONSTRAINT(VALUE, D) \
+    ((D) == 'Q' ? GET_CODE (VALUE) == SYMBOL_REF : 0)
+
+/* `FINALIZE_PIC'
+     By generating position-independent code, when two different
+     programs (A and B) share a common library (libC.a), the text of
+     the library can be shared whether or not the library is linked at
+     the same address for both programs.  In some of these
+     environments, position-independent code requires not only the use
+     of different addressing modes, but also special code to enable the
+     use of these addressing modes.
+
+     The `FINALIZE_PIC' macro serves as a hook to emit these special
+     codes once the function is being compiled into assembly code, but
+     not before.  (It is not done before, because in the case of
+     compiling an inline function, it would lead to multiple PIC
+     prologues being included in functions which used inline functions
+     and were compiled to assembly language.) */
+#define FINALIZE_PIC  do {} while (0)
+
+/* Switch into a generic section.  */
+#define TARGET_ASM_NAMED_SECTION  default_elf_asm_named_section
+
+#define PRINT_OPERAND(FILE, RTX, CODE)  print_operand (FILE, RTX, CODE)
+#define PRINT_OPERAND_ADDRESS(FILE, RTX) print_address_operand (FILE, RTX)
+
+typedef enum sections {
+    CODE_DIR,
+    DATA_DIR,
+    LAST_SECT_NM
+} SECT_ENUM_T;
+
+typedef enum directives {
+    LONG_CONST_DIR,
+    SHORT_CONST_DIR,
+    BYTE_CONST_DIR,
+    SPACE_DIR,
+    INIT_DIR,
+    LAST_DIR_NM
+} DIR_ENUM_T;
+
+#define TEXT_SECTION_ASM_OP ".text;"
+#define DATA_SECTION_ASM_OP ".data;"
+
+#define ASM_APP_ON  ""
+#define ASM_APP_OFF ""
+
+#define ASM_GLOBALIZE_LABEL1(FILE, NAME) \
+  do {  fputs (".global ", FILE);              \
+        assemble_name (FILE, NAME);            \
+        fputc (';',FILE);                      \
+        fputc ('\n',FILE);                     \
+      } while (0)
+
+#define ASM_DECLARE_FUNCTION_NAME(FILE,NAME,DECL) \
+  do {                                 \
+    fputs (".type ", FILE);            \
+    assemble_name (FILE, NAME);         \
+    fputs (", STT_FUNC", FILE);         \
+    fputc (';',FILE);                   \
+    fputc ('\n',FILE);                 \
+    ASM_OUTPUT_LABEL(FILE, NAME);      \
+  } while (0)
+
+#define ASM_OUTPUT_LABEL(FILE, NAME)    \
+  do {  assemble_name (FILE, NAME);            \
+        fputs (":\n",FILE);                    \
+      } while (0)
+
+#define ASM_OUTPUT_LABELREF(FILE,NAME)         \
+    do {  fprintf (FILE, "_%s", NAME); \
+        } while (0)
+
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO)                 \
+  do {                                                                 \
+    int len = strlen (NAME);                                           \
+    char *temp = (char *) alloca (len + 4);                            \
+    temp[0] = 'L';                                                     \
+    temp[1] = '_';                                                     \
+    strcpy (&temp[2], (NAME));                                         \
+    temp[len + 2] = '_';                                               \
+    temp[len + 3] = 0;                                                 \
+    (OUTPUT) = (char *) alloca (strlen (NAME) + 13);                   \
+    sprintf (OUTPUT, "_%s$%d", temp, LABELNO);                         \
+  } while (0)
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE)           \
+do { char __buf[256];                                  \
+     fprintf (FILE, "\t.dd\t");                                \
+     ASM_GENERATE_INTERNAL_LABEL (__buf, "L", VALUE);  \
+     assemble_name (FILE, __buf);                      \
+     fputc (';', FILE);                                        \
+     fputc ('\n', FILE);                               \
+   } while (0)
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+    MY_ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL)
+
+#define MY_ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL)          \
+    do {                                                       \
+       char __buf[256];                                        \
+       fprintf (FILE, "\t.dd\t");                              \
+       ASM_GENERATE_INTERNAL_LABEL (__buf, "L", VALUE);        \
+       assemble_name (FILE, __buf);                            \
+       fputs (" - ", FILE);                                    \
+       ASM_GENERATE_INTERNAL_LABEL (__buf, "L", REL);          \
+       assemble_name (FILE, __buf);                            \
+       fputc (';', FILE);                                      \
+       fputc ('\n', FILE);                                     \
+    } while (0)
+
+#define ASM_OUTPUT_ALIGN(FILE,LOG)                             \
+    do {                                                       \
+       fprintf (FILE, ".align %d\n", LOG);                     \
+    } while (0)
+
+#define ASM_OUTPUT_SKIP(FILE,SIZE)             \
+    do {                                       \
+       asm_output_skip (FILE, SIZE);           \
+    } while (0)
+
+#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED)    \
+do {                                           \
+    data_section();                            \
+    if ((SIZE) >= (unsigned int) 4 ) ASM_OUTPUT_ALIGN(FILE,2); \
+    ASM_OUTPUT_SIZE_DIRECTIVE (FILE, NAME, SIZE);              \
+    ASM_OUTPUT_LABEL (FILE, NAME);                             \
+    fprintf (FILE, "%s %ld;\n", ASM_SPACE,                     \
+            (ROUNDED) > (unsigned int) 1 ? (ROUNDED) : 1);     \
+} while (0)
+
+#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED)   \
+     do {                                              \
+       ASM_GLOBALIZE_LABEL1(FILE,NAME);                \
+        ASM_OUTPUT_LOCAL (FILE, NAME, SIZE, ROUNDED); } while(0)
+
+#define ASM_COMMENT_START "//"
+
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+  do {\
+    fprintf (FILE, "\tP1.l =LP$%d; P1.h =LP$%d; call mcount;\n", \
+       LABELNO, LABELNO);\
+  } while(0)
+
+#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) fprintf (FILE, "[SP--] = %s;\n", reg_names[REGNO])
+#define ASM_OUTPUT_REG_POP(FILE, REGNO)  fprintf (FILE, "%s = [SP++];\n", reg_names[REGNO])
+
+extern struct rtx_def *bfin_compare_op0, *bfin_compare_op1;
+extern struct rtx_def *bfin_cc_rtx, *bfin_rets_rtx;
+
+/* This works for GAS and some other assemblers.  */
+#define SET_ASM_OP              ".set "
+
+/* Don't know how to order these.  UNALIGNED_WORD_ASM_OP is in
+   dwarf2.out. */
+#define UNALIGNED_WORD_ASM_OP ".4byte"
+
+/* DBX register number for a given compiler register number */
+#define DBX_REGISTER_NUMBER(REGNO)  (REGNO) 
+
+#define SIZE_ASM_OP     "\t.size\t"
+
+#endif /*  _BFIN_CONFIG */
diff --git a/gcc/config/bfin/bfin.md b/gcc/config/bfin/bfin.md
new file mode 100644 (file)
index 0000000..348bce9
--- /dev/null
@@ -0,0 +1,1902 @@
+;;- Machine description for Blackfin for GNU compiler
+;;  Copyright 2005  Free Software Foundation, Inc.
+;;  Contributed by Analog Devices.
+
+;; 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 2, 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 COPYING.  If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+; operand punctuation marks:
+;
+;     X -- integer value printed as log2
+;     Y -- integer value printed as log2(~value) - for bitclear
+;     h -- print half word register, low part
+;     d -- print half word register, high part
+;     D -- print operand as dregs pairs
+;     w -- print operand as accumulator register word (a0w, a1w)
+;     H -- high part of double mode operand
+;     T -- byte register representation Oct. 02 2001
+
+; constant operand classes
+;
+;     J   2**N       5bit imm scaled
+;     Ks7 -64 .. 63  signed 7bit imm
+;     Ku5 0..31      unsigned 5bit imm
+;     Ks4 -8 .. 7    signed 4bit imm
+;     Ks3 -4 .. 3    signed 3bit imm
+;     Ku3 0 .. 7     unsigned 3bit imm
+;     Pn  0, 1, 2    constants 0, 1 or 2, corresponding to n
+;
+; register operands
+;     d  (r0..r7)
+;     a  (p0..p5,fp,sp)
+;     e  (a0, a1)
+;     b  (i0..i3)
+;     f  (m0..m3)
+;     B
+;     c (i0..i3,m0..m3) CIRCREGS
+;     C (CC)            CCREGS
+;
+
+;; Define constants for hard registers.
+
+(define_constants
+  [(REG_R0 0)
+   (REG_R1 1)
+   (REG_R2 2)
+   (REG_R3 3)
+   (REG_R4 4)
+   (REG_R5 5)
+   (REG_R6 6)
+   (REG_R7 7)
+
+   (REG_P0 8)
+   (REG_P1 9)
+   (REG_P2 10)
+   (REG_P3 11)
+   (REG_P4 12)
+   (REG_P5 13)
+   (REG_P6 14)
+   (REG_P7 15)
+
+   (REG_SP 14)
+   (REG_FP 15)
+
+   (REG_I0 16)
+   (REG_B0 17)
+   (REG_L0 18)
+   (REG_I1 19)
+   (REG_B1 20)
+   (REG_L1 21)
+   (REG_I2 22)
+   (REG_B2 23)
+   (REG_L2 24)
+   (REG_I3 25)
+   (REG_B3 26)
+   (REG_L3 27)
+
+   (REG_M0 28)
+   (REG_M1 29)
+   (REG_M2 30)
+   (REG_M3 31)
+
+   (REG_A0 32)
+   (REG_A1 33)
+
+   (REG_CC 34)
+   (REG_RETS 35)
+   (REG_RETI 36)
+   (REG_RETX 37)
+   (REG_RETN 38)
+   (REG_RETE 39)
+
+   (REG_ASTAT 40)
+   (REG_SEQSTAT 41)
+   (REG_USP 42)
+
+   (REG_ARGP 43)])
+
+;; Constants used in UNSPECs and UNSPEC_VOLATILEs.
+
+(define_constants
+  [(UNSPEC_CBRANCH_TAKEN 0)
+   (UNSPEC_CBRANCH_NOPS 1)
+   (UNSPEC_RETURN 2)
+   (UNSPEC_MOVE_PIC 3)
+   (UNSPEC_LIBRARY_OFFSET 4)
+   (UNSPEC_PUSH_MULTIPLE 5)])
+
+(define_constants
+  [(UNSPEC_VOLATILE_EH_RETURN 0)])
+
+(define_attr "type"
+  "move,mvi,mcld,mcst,dsp32,mult,alu0,shft,brcc,br,call,misc,compare,dummy"
+  (const_string "misc"))
+
+;; Scheduling definitions
+
+(define_automaton "bfin")
+
+(define_cpu_unit "core" "bfin")
+
+(define_insn_reservation "alu" 1
+  (eq_attr "type" "move,mvi,mcst,dsp32,alu0,shft,brcc,br,call,misc,compare")
+  "core")
+
+(define_insn_reservation "imul" 3
+  (eq_attr "type" "mult")
+  "core*3")
+
+(define_insn_reservation "load" 1
+  (eq_attr "type" "mcld")
+  "core")
+
+;; Make sure genautomata knows about the maximum latency that can be produced
+;; by the adjust_cost function.
+(define_insn_reservation "dummy" 5
+  (eq_attr "type" "mcld")
+  "core")
+\f
+;; Operand and operator predicates
+
+(include "predicates.md")
+
+\f
+;;; FRIO branches have been optimized for code density
+;;; this comes at a slight cost of complexity when
+;;; a compiler needs to generate branches in the general
+;;; case.  In order to generate the correct branching
+;;; mechanisms the compiler needs keep track of instruction
+;;; lengths.  The follow table describes how to count instructions
+;;; for the FRIO architecture.
+;;;
+;;; unconditional br are 12-bit imm pcrelative branches *2
+;;; conditional   br are 10-bit imm pcrelative branches *2
+;;; brcc 10-bit:
+;;;   1024 10-bit imm *2 is 2048 (-1024..1022)
+;;; br 12-bit  :
+;;;   4096 12-bit imm *2 is 8192 (-4096..4094)
+;;; NOTE : For brcc we generate instructions such as
+;;;   if cc jmp; jump.[sl] offset
+;;;   offset of jump.[sl] is from the jump instruction but
+;;;     gcc calculates length from the if cc jmp instruction
+;;;     hence our range is (-4094, 4096) instead of (-4096, 4094) for a br
+;;;
+;;; The way the (pc) rtx works in these calculations is somewhat odd;
+;;; for backward branches it's the address of the current instruction,
+;;; for forward branches it's the previously known address of the following
+;;; instruction - we have to take this into account by reducing the range
+;;; for a forward branch.
+
+;; Lengths for type "mvi" insns are always defined by the instructions
+;; themselves.
+(define_attr "length" ""
+  (cond [(eq_attr "type" "mcld")
+         (if_then_else (match_operand 1 "effective_address_32bit_p" "")
+                       (const_int 4) (const_int 2))
+
+        (eq_attr "type" "mcst")
+        (if_then_else (match_operand 0 "effective_address_32bit_p" "")
+                      (const_int 4) (const_int 2))
+
+        (eq_attr "type" "move") (const_int 2)
+
+        (eq_attr "type" "dsp32") (const_int 4)
+        (eq_attr "type" "call")  (const_int 4)
+
+         (eq_attr "type" "br")
+        (if_then_else (and
+                         (le (minus (match_dup 0) (pc)) (const_int 4092))
+                         (ge (minus (match_dup 0) (pc)) (const_int -4096)))
+                 (const_int 2)
+                  (const_int 4))
+
+         (eq_attr "type" "brcc")
+        (cond [(and
+                   (le (minus (match_dup 3) (pc)) (const_int 1020))
+                   (ge (minus (match_dup 3) (pc)) (const_int -1024)))
+                 (const_int 2)
+               (and
+                   (le (minus (match_dup 3) (pc)) (const_int 4096))
+                   (ge (minus (match_dup 3) (pc)) (const_int -4094)))
+                 (const_int 4)]
+              (const_int 6))
+        ]
+
+       (const_int 2)))
+
+;; Conditional moves
+
+(define_expand "movsicc"
+  [(set (match_operand:SI 0 "register_operand" "")
+        (if_then_else:SI (match_operand 1 "comparison_operator" "")
+                         (match_operand:SI 2 "register_operand" "")
+                         (match_operand:SI 3 "register_operand" "")))]
+  ""
+{
+  operands[1] = bfin_gen_compare (operands[1], SImode);
+})
+
+(define_insn "*movsicc_insn1"
+  [(set (match_operand:SI 0 "register_operand" "=da,da,da")
+        (if_then_else:SI
+           (eq:BI (match_operand:BI 3 "cc_operand" "C,C,C")
+               (const_int 0))
+           (match_operand:SI 1 "register_operand" "da,0,da")
+           (match_operand:SI 2 "register_operand" "0,da,da")))]
+  ""
+  "@
+    if !cc %0 =%1; /* movsicc-1a */
+    if cc %0 =%2; /* movsicc-1b */
+    if !cc %0 =%1; if cc %0=%2; /* movsicc-1 */"
+  [(set_attr "length" "2,2,4")
+   (set_attr "type" "move")])
+
+(define_insn "*movsicc_insn2"
+  [(set (match_operand:SI 0 "register_operand" "=da,da,da")
+        (if_then_else:SI
+           (ne:BI (match_operand:BI 3 "cc_operand" "C,C,C")
+               (const_int 0))
+           (match_operand:SI 1 "register_operand" "0,da,da")
+           (match_operand:SI 2 "register_operand" "da,0,da")))]
+  ""
+  "@
+   if !cc %0 =%2; /* movsicc-2b */
+   if cc %0 =%1; /* movsicc-2a */
+   if cc %0 =%1; if !cc %0=%2; /* movsicc-1 */"
+  [(set_attr "length" "2,2,4")
+   (set_attr "type" "move")])
+
+;; Insns to load HIGH and LO_SUM
+
+(define_insn "movsi_high"
+  [(set (match_operand:SI 0 "register_operand" "=x")
+       (high:SI (match_operand:SI 1 "immediate_operand" "i")))]
+  "reload_completed"
+  "%d0 = %d1;"
+  [(set_attr "type" "mvi")
+   (set_attr "length" "4")])
+
+(define_insn "movstricthi_high"
+  [(set (match_operand:SI 0 "register_operand" "+x")
+       (ior:SI (and:SI (match_dup 0) (const_int 65535))
+               (match_operand:SI 1 "immediate_operand" "i")))]
+  "reload_completed"
+  "%d0 = %d1;"
+  [(set_attr "type" "mvi")
+   (set_attr "length" "4")])
+
+(define_insn "movsi_low"
+  [(set (match_operand:SI 0 "register_operand" "=x")
+       (lo_sum:SI (match_operand:SI 1 "register_operand" "0")
+                  (match_operand:SI 2 "immediate_operand" "i")))]
+  "reload_completed"
+  "%h0 = %h2;"
+  [(set_attr "type" "mvi")
+   (set_attr "length" "4")])
+
+(define_insn "movsi_high_pic"
+  [(set (match_operand:SI 0 "register_operand" "=x")
+       (high:SI (unspec:SI [(match_operand:SI 1 "" "")]
+                           UNSPEC_MOVE_PIC)))]
+  ""
+  "%d0 = %1@GOT_LOW;"
+  [(set_attr "type" "mvi")
+   (set_attr "length" "4")])
+
+(define_insn "movsi_low_pic"
+  [(set (match_operand:SI 0 "register_operand" "=x")
+       (lo_sum:SI (match_operand:SI 1 "register_operand" "0")
+                  (unspec:SI [(match_operand:SI 2 "" "")]
+                             UNSPEC_MOVE_PIC)))]
+  ""
+  "%h0 = %h2@GOT_HIGH;"
+  [(set_attr "type" "mvi")
+   (set_attr "length" "4")])
+
+;;; Move instructions
+
+(define_insn_and_split "movdi_insn"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=x,mx,r")
+       (match_operand:DI 1 "general_operand" "iFx,r,mx"))]
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "#"
+  "reload_completed"
+  [(set (match_dup 2) (match_dup 3))
+   (set (match_dup 4) (match_dup 5))]
+{
+  rtx lo_half[2], hi_half[2];
+  split_di (operands, 2, lo_half, hi_half);
+
+  if (reg_overlap_mentioned_p (lo_half[0], hi_half[1]))
+    {
+      operands[2] = hi_half[0];
+      operands[3] = hi_half[1];
+      operands[4] = lo_half[0];
+      operands[5] = lo_half[1];
+    }
+  else
+    {
+      operands[2] = lo_half[0];
+      operands[3] = lo_half[1];
+      operands[4] = hi_half[0];
+      operands[5] = hi_half[1];
+    }
+})
+
+(define_insn "movbi"
+  [(set (match_operand:BI 0 "nonimmediate_operand" "=x,x,d,mr,C,d")
+        (match_operand:BI 1 "general_operand" "x,xKs3,mr,d,d,C"))]
+
+  ""
+  "@
+   %0 = %1;
+   %0 = %1 (X);
+   %0 = %1;
+   %0 = %1;
+   CC = %1;
+   %0 = CC;"
+  [(set_attr "type" "move,mvi,mcld,mcst,compare,compare")
+   (set_attr "length" "2,2,*,*,2,2")])
+
+(define_insn "movpdi"
+  [(set (match_operand:PDI 0 "nonimmediate_operand" "=e,<,e")
+        (match_operand:PDI 1 "general_operand" " e,e,>"))]
+  ""
+  "@
+   %0 = %1;
+   %0 = %x1; %0 = %w1;
+   %w0 = %1; %x0 = %1;"
+  [(set_attr "type" "move,mcst,mcld")])
+
+(define_insn "*pushsi_insn"
+  [(set (mem:SI (pre_dec:SI (reg:SI REG_SP)))
+        (match_operand:SI 0 "register_operand" "xy"))]
+  ""
+  "[--SP] = %0;"
+  [(set_attr "type" "mcst")
+   (set_attr "length" "2")])
+
+(define_insn "*popsi_insn"
+  [(set (match_operand:SI 0 "register_operand" "=xy")
+        (mem:SI (post_inc:SI (reg:SI REG_SP))))]
+  ""
+  "%0 = [SP++];"
+  [(set_attr "type" "mcld")
+   (set_attr "length" "2")])
+
+;; The first alternative is used to make reload choose a limited register
+;; class when faced with a movsi_insn that had its input operand replaced
+;; with a PLUS.  We generally require fewer secondary reloads this way.
+(define_insn "*movsi_insn"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=da,x*y,da,x,x,x,da,mr")
+        (match_operand:SI 1 "general_operand" "da,x*y,xKs7,xKsh,xKuh,ix,mr,da"))]
+
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "@
+   %0 = %1;
+   %0 = %1;
+   %0 = %1 (X);
+   %0 = %1 (X);
+   %0 = %1 (Z);
+   #
+   %0 = %1;
+   %0 = %1;"
+  [(set_attr "type" "move,move,mvi,mvi,mvi,*,mcld,mcst")
+   (set_attr "length" "2,2,2,4,4,*,*,*")])
+
+(define_insn "*movv2hi_insn"
+  [(set (match_operand:V2HI 0 "nonimmediate_operand" "=da,d,m")
+        (match_operand:V2HI 1 "general_operand" "d,m,d"))]
+
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "%0 = %1;"
+  [(set_attr "type" "move,mcld,mcst")
+   (set_attr "length" "2,*,*")])
+
+(define_insn "*movhi_insn"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=x,da,x,d,mr")
+        (match_operand:HI 1 "general_operand" "x,xKs7,xKsh,mr,d"))]
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "@
+   %0 = %1;
+   %0 = %1 (X);
+   %0 = %1 (X);
+   %0 = W %1 (X);
+   W %0 = %1;"
+  [(set_attr "type" "move,mvi,mvi,mcld,mcst")
+   (set_attr "length" "2,2,4,*,*")])
+
+(define_insn "*movqi_insn"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=x,da,x,d,mr")
+        (match_operand:QI 1 "general_operand" "x,xKs7,xKsh,mr,d"))]
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "@
+   %0 = %1;
+   %0 = %1 (X);
+   %0 = %1 (X);
+   %0 = B %1 (X);
+   B %0 = %1;"
+  [(set_attr "type" "move,mvi,mvi,mcld,mcst")
+   (set_attr "length" "2,2,4,*,*")])
+
+(define_insn "*movsf_insn"
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=x,x,da,mr")
+        (match_operand:SF 1 "general_operand" "x,Fx,mr,da"))]
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "@
+   %0 = %1;
+   #
+   %0 = %1;
+   %0 = %1;"
+  [(set_attr "type" "move,*,mcld,mcst")])
+
+(define_insn_and_split "movdf_insn"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=x,mx,r")
+       (match_operand:DF 1 "general_operand" "iFx,r,mx"))]
+  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+  "#"
+  "reload_completed"
+  [(set (match_dup 2) (match_dup 3))
+   (set (match_dup 4) (match_dup 5))]
+{
+  rtx lo_half[2], hi_half[2];
+  split_di (operands, 2, lo_half, hi_half);
+
+  if (reg_overlap_mentioned_p (lo_half[0], hi_half[1]))
+    {
+      operands[2] = hi_half[0];
+      operands[3] = hi_half[1];
+      operands[4] = lo_half[0];
+      operands[5] = lo_half[1];
+    }
+  else
+    {
+      operands[2] = lo_half[0];
+      operands[3] = lo_half[1];
+      operands[4] = hi_half[0];
+      operands[5] = hi_half[1];
+    }
+})
+
+;; This is the main "hook" for PIC code.  When generating
+;; PIC, movsi is responsible for determining when the source address
+;; needs PIC relocation and appropriately calling legitimize_pic_address
+;; to perform the actual relocation.
+
+(define_expand "movsi"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "")
+       (match_operand:SI 1 "general_operand" ""))]
+  ""
+  "expand_move (operands, SImode);")
+
+(define_expand "movv2hi"
+  [(set (match_operand:V2HI 0 "nonimmediate_operand" "")
+       (match_operand:V2HI 1 "general_operand" ""))]
+  ""
+  "expand_move (operands, V2HImode);")
+
+(define_expand "movdi"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "")
+       (match_operand:DI 1 "general_operand" ""))]
+  ""
+  "expand_move (operands, DImode);")
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "")
+       (match_operand:SF 1 "general_operand" ""))]
+  ""
+  "expand_move (operands, SFmode);")
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "")
+       (match_operand:DF 1 "general_operand" ""))]
+  ""
+  "expand_move (operands, DFmode);")
+
+(define_expand "movhi"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "")
+       (match_operand:HI 1 "general_operand" ""))]
+  ""
+  "expand_move (operands, HImode);")
+
+(define_expand "movqi"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "")
+       (match_operand:QI 1 "general_operand" ""))]
+  ""
+  " expand_move (operands, QImode); ")
+
+;; Some define_splits to break up SI/SFmode loads of immediate constants.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (match_operand:SI 1 "symbolic_or_const_operand" ""))]
+  "reload_completed
+   /* Always split symbolic operands; split integer constants that are
+      too large for a single instruction.  */
+   && (GET_CODE (operands[1]) != CONST_INT
+       || (INTVAL (operands[1]) < -32768
+          || INTVAL (operands[1]) >= 65536
+          || (INTVAL (operands[1]) >= 32768 && PREG_P (operands[0]))))"
+  [(set (match_dup 0) (high:SI (match_dup 1)))
+   (set (match_dup 0) (lo_sum:SI (match_dup 0) (match_dup 1)))]
+{
+  if (GET_CODE (operands[1]) == CONST_INT
+      && split_load_immediate (operands))
+    DONE;
+  /* ??? Do something about TARGET_LOW_64K.  */
+})
+
+(define_split
+  [(set (match_operand:SF 0 "register_operand" "")
+       (match_operand:SF 1 "immediate_operand" ""))]
+  "reload_completed"
+  [(set (match_dup 2) (high:SI (match_dup 3)))
+   (set (match_dup 2) (lo_sum:SI (match_dup 2) (match_dup 3)))]
+{
+  long values;
+  REAL_VALUE_TYPE value;
+
+  if (GET_CODE (operands[1]) != CONST_DOUBLE)
+    abort ();
+
+  REAL_VALUE_FROM_CONST_DOUBLE (value, operands[1]);
+  REAL_VALUE_TO_TARGET_SINGLE (value, values);
+
+  operands[2] = gen_rtx_REG (SImode, true_regnum (operands[0]));
+  operands[3] = GEN_INT (trunc_int_for_mode (values, SImode));
+  if (values >= -32768 && values < 65536)
+    {
+      emit_move_insn (operands[2], operands[3]);
+      DONE;
+    }
+  if (split_load_immediate (operands + 2))
+    DONE;
+})
+
+;; Sadly, this can't be a proper named movstrict pattern, since the compiler
+;; expects to be able to use registers for operand 1.
+;; Note that the asm instruction is defined by the manual to take an unsigned
+;; constant, but it doesn't matter to the assembler, and the compiler only
+;; deals with sign-extended constants.  Hence "Ksh".
+(define_insn "*movstricthi"
+  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+x"))
+       (match_operand:HI 1 "immediate_operand" "Ksh"))]
+  ""
+  "%h0 = %1;"
+  [(set_attr "type" "mvi")
+   (set_attr "length" "4")])
+
+;; Sign and zero extensions
+
+(define_insn "extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "=d, d")
+       (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d, m")))]
+  ""
+  "@
+   %0 = %h1 (X);
+   %0 = W %h1 (X);"
+  [(set_attr "type" "alu0,mcld")])
+
+(define_insn "zero_extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "=d, d")
+       (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d, m")))]
+  ""
+  "@
+   %0 = %h1 (Z);
+   %0 = W%h1 (Z);"
+  [(set_attr "type" "alu0,mcld")])
+
+(define_insn "zero_extendbisi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (zero_extend:SI (match_operand:BI 1 "nonimmediate_operand" "C")))]
+  ""
+  "%0 = %1;"
+  [(set_attr "type" "compare")])
+
+(define_insn "extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "=d, d")
+       (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+  ""
+  "@
+   %0 = B %1 (X);
+   %0 = %T1 (X);"
+  [(set_attr "type" "mcld,alu0")])
+
+(define_insn "extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "=d, d")
+       (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+  ""
+  "@
+   %0 = B %1 (X);
+   %0 = %T1 (X);"
+  [(set_attr "type" "mcld,alu0")])
+
+
+(define_insn "zero_extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "=d, d")
+       (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+  ""
+  "@
+   %0 = B %1 (Z);
+   %0 = %T1 (Z);"
+  [(set_attr "type" "mcld,alu0")])
+
+
+(define_insn "zero_extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "=d, d")
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+  ""
+  "@
+   %0 = B %1 (Z);
+   %0 = %T1 (Z);"
+  [(set_attr "type" "mcld,alu0")])
+
+;; DImode logical operations
+
+(define_code_macro any_logical [and ior xor])
+(define_code_attr optab [(and "and")
+                        (ior "ior")
+                        (xor "xor")])
+(define_code_attr op [(and "&")
+                     (ior "|")
+                     (xor "^")])
+(define_code_attr high_result [(and "0")
+                              (ior "%H1")
+                              (xor "%H1")])
+
+(define_insn "<optab>di3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (any_logical:DI (match_operand:DI 1 "register_operand" "0")
+                       (match_operand:DI 2 "register_operand" "d")))]
+  ""
+  "%0 = %1 <op> %2;\\n\\t%H0 = %H1 <op> %H2;"
+  [(set_attr "length" "4")])
+
+(define_insn "*<optab>di_zesidi_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (any_logical:DI (zero_extend:DI
+                        (match_operand:SI 2 "register_operand" "d"))
+                       (match_operand:DI 1 "register_operand" "d")))]
+  ""
+  "%0 = %1 <op>  %2;\\n\\t%H0 = <high_result>;"
+  [(set_attr "length" "4")])
+
+(define_insn "*<optab>di_sesdi_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (any_logical:DI (sign_extend:DI
+                        (match_operand:SI 2 "register_operand" "d"))
+                       (match_operand:DI 1 "register_operand" "0")))
+   (clobber (match_scratch:SI 3 "=&d"))]
+  ""
+  "%0 = %1 <op> %2;\\n\\t%3 = %2;\\n\\t%3 >>>= 31;\\n\\t%H0 = %H1 <op> %3;"
+  [(set_attr "length" "8")])
+
+(define_insn "negdi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (neg:DI (match_operand:DI 1 "register_operand" "d")))
+   (clobber (match_scratch:SI 2 "=&d"))
+   (clobber (reg:CC REG_CC))]
+  ""
+  "%2 = 0; %2 = %2 - %1; cc = ac0; cc = !cc; %2 = cc;\\n\\t%0 = -%1; %H0 = -%H1; %H0 = %H0 - %2;"
+  [(set_attr "length" "16")])
+
+(define_insn "one_cmpldi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (not:DI (match_operand:DI 1 "register_operand" "d")))]
+  ""
+  "%0 = ~%1;\\n\\t%H0 = ~%H1;"
+  [(set_attr "length" "4")])
+
+;; DImode zero and sign extend patterns
+
+(define_insn_and_split "zero_extendsidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (zero_extend:DI (match_operand:SI 1 "register_operand" "d")))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 3) (const_int 0))]
+{
+  split_di (operands, 1, operands + 2, operands + 3);
+  if (REGNO (operands[0]) != REGNO (operands[1]))
+    emit_move_insn (operands[2], operands[1]);
+})
+
+(define_insn "zero_extendqidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (zero_extend:DI (match_operand:QI 1 "register_operand" "d")))]
+  ""
+  "%0 = %T1 (Z);\\n\\t%H0 = 0;"
+  [(set_attr "length" "4")])
+
+(define_insn "zero_extendhidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (zero_extend:DI (match_operand:HI 1 "register_operand" "d")))]
+  ""
+  "%0 = %h1 (Z);\\n\\t%H0 = 0;"
+  [(set_attr "length" "4")])
+
+(define_insn_and_split "extendsidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (sign_extend:DI (match_operand:SI 1 "register_operand" "d")))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 3) (match_dup 1))
+   (set (match_dup 3) (ashiftrt:SI (match_dup 3) (const_int 31)))]
+{
+  split_di (operands, 1, operands + 2, operands + 3);
+  if (REGNO (operands[0]) != REGNO (operands[1]))
+    emit_move_insn (operands[2], operands[1]);
+})
+
+(define_insn_and_split "extendqidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (sign_extend:DI (match_operand:QI 1 "register_operand" "d")))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 2) (sign_extend:SI (match_dup 1)))
+   (set (match_dup 3) (sign_extend:SI (match_dup 1)))
+   (set (match_dup 3) (ashiftrt:SI (match_dup 3) (const_int 31)))]
+{
+  split_di (operands, 1, operands + 2, operands + 3);
+})
+
+(define_insn_and_split "extendhidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (sign_extend:DI (match_operand:HI 1 "register_operand" "d")))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 2) (sign_extend:SI (match_dup 1)))
+   (set (match_dup 3) (sign_extend:SI (match_dup 1)))
+   (set (match_dup 3) (ashiftrt:SI (match_dup 3) (const_int 31)))]
+{
+  split_di (operands, 1, operands + 2, operands + 3);
+})
+
+;; DImode arithmetic operations
+
+(define_insn "adddi3"
+  [(set (match_operand:DI 0 "register_operand" "=&d,&d,&d")
+        (plus:DI (match_operand:DI 1 "register_operand" "%0,0,0")
+                 (match_operand:DI 2 "nonmemory_operand" "Kn7,Ks7,d")))
+   (clobber (match_scratch:SI 3 "=&d,&d,&d"))
+   (clobber (reg:CC 34))]
+  ""
+  "@
+   %0 += %2; cc = ac0; %3 = cc; %H0 += -1; %H0 = %H0 + %3;
+   %0 += %2; cc = ac0; %3 = cc; %H0 = %H0 + %3;
+   %0 = %0 + %2; cc = ac0; %3 = cc; %H0 = %H0 + %H2; %H0 = %H0 + %3;"
+  [(set_attr "type" "alu0")
+   (set_attr "length" "10,8,10")])
+
+(define_insn "subdi3"
+  [(set (match_operand:DI 0 "register_operand" "=&d")
+        (minus:DI (match_operand:DI 1 "register_operand" "0")
+                  (match_operand:DI 2 "register_operand" "d")))
+   (clobber (reg:CC 34))]
+  ""
+  "%0 = %1-%2;\\n\\tcc = ac0;\\n\\t%H0 = %H1-%H2;\\n\\tif cc jump 1f;\\n\\t%H0 += -1;\\n\\t1:"
+  [(set_attr "length" "10")])
+
+(define_insn "*subdi_di_zesidi"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (minus:DI (match_operand:DI 1 "register_operand" "0")
+                  (zero_extend:DI
+                  (match_operand:SI 2 "register_operand" "d"))))
+   (clobber (match_scratch:SI 3 "=&d"))
+   (clobber (reg:CC 34))]
+  ""
+  "%0 = %1 - %2;\\n\\tcc = ac0;\\n\\tcc = ! cc;\\n\\t%3 = cc;\\n\\t%H0 = %H1 - %3;"
+  [(set_attr "length" "10")])
+
+(define_insn "*subdi_zesidi_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (minus:DI (zero_extend:DI
+                  (match_operand:SI 2 "register_operand" "d"))
+                  (match_operand:DI 1 "register_operand" "0")))
+   (clobber (match_scratch:SI 3 "=&d"))
+   (clobber (reg:CC 34))]
+  ""
+  "%0 = %2 - %1;\\n\\tcc = ac0;\\n\\tcc = ! cc;\\n\\t%3 = cc;\\n\\t%3 = -%3;\\n\\t%H0 = %3 - %H1"
+  [(set_attr "length" "12")])
+
+(define_insn "*subdi_di_sesidi"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (minus:DI (match_operand:DI 1 "register_operand" "0")
+                  (sign_extend:DI
+                  (match_operand:SI 2 "register_operand" "d"))))
+   (clobber (match_scratch:SI 3 "=&d"))
+   (clobber (reg:CC 34))]
+  ""
+  "%0 = %1 - %2;\\n\\tcc = ac0;\\n\\t%3 = %2;\\n\\t%3 >>>= 31;\\n\\t%H0 = %H1 - %3;\\n\\tif cc jump 1f;\\n\\t%H0 += -1;\\n\\t1:"
+  [(set_attr "length" "14")])
+
+(define_insn "*subdi_sesidi_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+        (minus:DI (sign_extend:DI
+                  (match_operand:SI 2 "register_operand" "d"))
+                  (match_operand:DI 1 "register_operand" "0")))
+   (clobber (match_scratch:SI 3 "=&d"))
+   (clobber (reg:CC 34))]
+  ""
+  "%0 = %2 - %1;\\n\\tcc = ac0;\\n\\t%3 = %2;\\n\\t%3 >>>= 31;\\n\\t%H0 = %3 - %H1;\\n\\tif cc jump 1f;\\n\\t%H0 += -1;\\n\\t1:"
+  [(set_attr "length" "14")])
+
+;; Combined shift/add instructions
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=a,d")
+       (ashift:SI (plus:SI (match_operand:SI 1 "register_operand" "%0,0")
+                           (match_operand:SI 2 "register_operand" "a,d"))
+                  (match_operand:SI 3 "pos_scale_operand" "P1P2,P1P2")))]
+  ""
+  "%0 = (%0 + %2) << %3;" /* "shadd %0,%2,%3;" */
+  [(set_attr "type" "alu0")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=a")
+       (plus:SI (match_operand:SI 1 "register_operand" "a")
+                (mult:SI (match_operand:SI 2 "register_operand" "a")
+                         (match_operand:SI 3 "scale_by_operand" "i"))))]
+  ""
+  "%0 = %1 + (%2 << %X3);"
+  [(set_attr "type" "alu0")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=a")
+       (plus:SI (match_operand:SI 1 "register_operand" "a")
+                (ashift:SI (match_operand:SI 2 "register_operand" "a")
+                           (match_operand:SI 3 "pos_scale_operand" "i"))))]
+  ""
+  "%0 = %1 + (%2 << %3);"
+  [(set_attr "type" "alu0")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=a")
+       (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "a")
+                         (match_operand:SI 2 "scale_by_operand" "i"))
+                (match_operand:SI 3 "register_operand" "a")))]
+  ""
+  "%0 = %3 + (%1 << %X2);"
+  [(set_attr "type" "alu0")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=a")
+       (plus:SI (ashift:SI (match_operand:SI 1 "register_operand" "a")
+                           (match_operand:SI 2 "pos_scale_operand" "i"))
+                (match_operand:SI 3 "register_operand" "a")))]
+  ""
+  "%0 = %3 + (%1 << %2);"
+  [(set_attr "type" "alu0")])
+
+(define_insn "mulhisi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%d"))
+                (sign_extend:SI (match_operand:HI 2 "register_operand" "d"))))]
+  ""
+  "%0 = %h1 * %h2 (IS);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "umulhisi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%d"))
+                (zero_extend:SI (match_operand:HI 2 "register_operand" "d"))))]
+  ""
+  "%0 = %h1 * %h2 (FU);"
+  [(set_attr "type" "dsp32")])
+
+;; The processor also supports ireg += mreg or ireg -= mreg, but these
+;; are unusable if we don't ensure that the corresponding lreg is zero.
+;; The same applies to the add/subtract constant versions involving
+;; iregs
+
+(define_insn "addsi3"
+  [(set (match_operand:SI 0 "register_operand" "=ad,a,d")
+       (plus:SI (match_operand:SI 1 "register_operand" "%0, a,d")
+                (match_operand:SI 2 "reg_or_7bit_operand" "Ks7, a,d")))]
+  ""
+  "@
+   %0 += %2;
+   %0 = %1 + %2;
+   %0 = %1 + %2;"
+  [(set_attr "type" "alu0")
+   (set_attr "length" "2,2,2")])
+
+(define_expand "subsi3"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (minus:SI (match_operand:SI 1 "register_operand" "")
+                 (match_operand:SI 2 "reg_or_7bit_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=da,d,a")
+       (minus:SI (match_operand:SI 1 "register_operand" "0,d,0")
+                 (match_operand:SI 2 "reg_or_7bit_operand" "Ks7,d,a")))]
+  "GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -64"
+{
+  static const char *const strings_subsi3[] = {
+    "%0 += -%2;",
+    "%0 = %1 - %2;",
+    "%0 -= %2;",
+  };
+
+  if (CONSTANT_P (operands[2]) && INTVAL (operands[2]) < 0) {
+     rtx tmp_op = operands[2];
+     operands[2] = GEN_INT (-INTVAL (operands[2]));
+     output_asm_insn ("%0 += %2;", operands);
+     operands[2] = tmp_op;
+     return "";
+  }
+
+  return strings_subsi3[which_alternative];
+}
+  [(set_attr "type" "alu0")])
+
+;; Bit test instructions
+
+(define_insn "*not_bittst"
+ [(set (match_operand:BI 0 "cc_operand" "=C")
+       (eq:BI (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
+                              (const_int 1)
+                              (match_operand:SI 2 "immediate_operand" "Ku5"))
+             (const_int 0)))]
+ ""
+ "cc = !BITTST (%1,%2);"
+  [(set_attr "type" "alu0")])
+
+(define_insn "*bittst"
+ [(set (match_operand:BI 0 "cc_operand" "=C")
+       (ne:BI (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
+                              (const_int 1)
+                              (match_operand:SI 2 "immediate_operand" "Ku5"))
+               (const_int 0)))]
+ ""
+ "cc = BITTST (%1,%2);"
+  [(set_attr "type" "alu0")])
+
+(define_insn_and_split "*bit_extract"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
+                        (const_int 1)
+                        (match_operand:SI 2 "immediate_operand" "Ku5")))
+   (clobber (reg:BI REG_CC))]
+  ""
+  "#"
+  ""
+  [(set (reg:BI REG_CC)
+       (ne:BI (zero_extract:SI (match_dup 1) (const_int 1) (match_dup 2))
+              (const_int 0)))
+   (set (match_dup 0)
+       (ne:SI (reg:BI REG_CC) (const_int 0)))])
+
+(define_insn_and_split "*not_bit_extract"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (zero_extract:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
+                        (const_int 1)
+                        (match_operand:SI 2 "immediate_operand" "Ku5")))
+   (clobber (reg:BI REG_CC))]
+  ""
+  "#"
+  ""
+  [(set (reg:BI REG_CC)
+       (eq:BI (zero_extract:SI (match_dup 1) (const_int 1) (match_dup 2))
+              (const_int 0)))
+   (set (match_dup 0)
+       (ne:SI (reg:BI REG_CC) (const_int 0)))])
+
+(define_insn "*andsi_insn"
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d,d")
+       (and:SI (match_operand:SI 1 "register_operand" "%0,d,d,d")
+               (match_operand:SI 2 "rhs_andsi3_operand" "L,M1,M2,d")))]
+  ""
+  "@
+   BITCLR (%0,%Y2);
+   %0 = %T1 (Z);
+   %0 = %h1 (Z);
+   %0 = %1 & %2;"
+  [(set_attr "type" "alu0")])
+
+(define_expand "andsi3"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (and:SI (match_operand:SI 1 "register_operand" "")
+               (match_operand:SI 2 "general_operand" "")))]
+  ""
+{
+  if (highbits_operand (operands[2], SImode))
+    {
+      operands[2] = GEN_INT (exact_log2 (-INTVAL (operands[2])));
+      emit_insn (gen_ashrsi3 (operands[0], operands[1], operands[2]));
+      emit_insn (gen_ashlsi3 (operands[0], operands[0], operands[2]));
+      DONE;
+    }
+  if (! rhs_andsi3_operand (operands[2], SImode))
+    operands[2] = force_reg (SImode, operands[2]);
+})
+
+(define_insn "iorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (ior:SI (match_operand:SI 1 "register_operand" "%0,d")
+               (match_operand:SI 2 "regorlog2_operand" "J,d")))]
+  ""
+  "@
+   BITSET (%0, %X2);
+   %0 = %1 | %2;"
+  [(set_attr "type" "alu0")])
+
+(define_insn "xorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (xor:SI (match_operand:SI 1 "register_operand" "%0,d")
+                 (match_operand:SI 2 "regorlog2_operand" "J,d")))]
+  ""
+  "@
+   BITTGL (%0, %X2);
+   %0 = %1 ^ %2;"
+  [(set_attr "type" "alu0")])
+
+(define_insn "smaxsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (smax:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "register_operand" "d")))]
+  ""
+  "%0 =max(%1,%2);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "sminsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (smin:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "register_operand" "d")))]
+  ""
+  "%0 =min(%1,%2);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "abssi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (abs:SI (match_operand:SI 1 "register_operand" " d")))]
+  ""
+  "%0 =abs %1;"
+  [(set_attr "type" "dsp32")])
+
+
+(define_insn "negsi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (neg:SI (match_operand:SI 1 "register_operand" " d")))]
+  ""
+  "%0 =-%1;"
+  [(set_attr "type" "alu0")])
+
+(define_insn "one_cmplsi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (not:SI (match_operand:SI 1 "register_operand" " d")))]
+  ""
+  "%0 =~%1;"
+  [(set_attr "type" "alu0")])
+
+(define_insn "mulsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (mult:SI (match_operand:SI 1 "register_operand" "%0")
+                (match_operand:SI 2 "register_operand" "d")))]
+  ""
+  "%0 *=%2;"
+  [(set_attr "type" "mult")])
+
+(define_expand "ashlsi3"
+  [(set (match_operand:SI 0 "register_operand" "")
+        (ashift:SI (match_operand:SI 1 "register_operand" "")
+                   (match_operand:SI 2 "nonmemory_operand" "")))]
+  ""
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+     && ((unsigned HOST_WIDE_INT) INTVAL (operands[2])) > 31)
+   {
+     emit_insn (gen_movsi (operands[0], const0_rtx));
+     DONE;
+   }
+})
+
+(define_insn_and_split "*ashlsi3_insn"
+  [(set (match_operand:SI 0 "register_operand" "=d,a,a,a")
+       (ashift:SI (match_operand:SI 1 "register_operand" "0,a,a,a")
+                  (match_operand:SI 2 "nonmemory_operand" "dKu5,P1,P2,?P3P4")))]
+  ""
+  "@
+   %0 <<= %2;
+   %0 = %1 + %1;
+   %0 = %1 << %2;
+   #"
+  "PREG_P (operands[0]) && INTVAL (operands[2]) > 2"
+  [(set (match_dup 0) (ashift:SI (match_dup 1) (const_int 2)))
+   (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 3)))]
+  "operands[3] = GEN_INT (INTVAL (operands[2]) - 2);"
+  [(set_attr "type" "shft")])
+
+(define_insn "ashrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
+                    (match_operand:SI 2 "nonmemory_operand" "dKu5")))]
+  ""
+  "%0 >>>= %2;"
+  [(set_attr "type" "shft")])
+
+(define_insn "lshrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d,a")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" " 0,a")
+                    (match_operand:SI 2 "nonmemory_operand" "dKu5,P1P2")))]
+  ""
+  "@
+   %0 >>= %2;
+   %0 = %1 >> %2;"
+  [(set_attr "type" "shft")])
+
+;; A pattern to reload the equivalent of
+;;   (set (Dreg) (plus (FP) (large_constant)))
+;; or
+;;   (set (dagreg) (plus (FP) (arbitrary_constant))) 
+;; using a scratch register
+(define_expand "reload_insi"
+  [(parallel [(set (match_operand:SI 0 "register_operand" "=w")
+                   (match_operand:SI 1 "fp_plus_const_operand" ""))
+              (clobber (match_operand:SI 2 "register_operand" "=&a"))])]
+  ""
+{
+  rtx fp_op = XEXP (operands[1], 0);
+  rtx const_op = XEXP (operands[1], 1);
+  rtx primary = operands[0];
+  rtx scratch = operands[2];
+
+  emit_move_insn (scratch, const_op);
+  emit_insn (gen_addsi3 (scratch, scratch, fp_op));
+  emit_move_insn (primary, scratch);
+  DONE;
+})
+
+;; Jump instructions
+
+(define_insn "jump"
+  [(set (pc)
+       (label_ref (match_operand 0 "" "")))]
+  ""
+{
+  if (get_attr_length (insn) == 2)
+    return "jump.s %0;";
+  else
+    return "jump.l %0;";
+}
+  [(set_attr "type" "br")])
+
+(define_insn "indirect_jump"
+  [(set (pc)
+       (match_operand:SI 0 "register_operand" "a"))]
+  ""
+  "jump (%0);"
+  [(set_attr "type" "misc")])
+
+(define_expand "tablejump"
+  [(parallel [(set (pc) (match_operand:SI 0 "register_operand" "a"))
+              (use (label_ref (match_operand 1 "" "")))])]
+  ""
+{
+  /* In PIC mode, the table entries are stored PC relative.
+     Convert the relative address to an absolute address.  */
+  if (flag_pic)
+    {
+      rtx op1 = gen_rtx_LABEL_REF (Pmode, operands[1]);
+
+      operands[0] = expand_simple_binop (Pmode, PLUS, operands[0],
+                                        op1, NULL_RTX, 0, OPTAB_DIRECT);
+    }
+})
+
+(define_insn "*tablejump_internal"
+  [(set (pc) (match_operand:SI 0 "register_operand" "a"))
+   (use (label_ref (match_operand 1 "" "")))]
+  ""
+  "jump (%0);"
+  [(set_attr "type" "misc")])
+
+;;  Call instructions..
+
+(define_expand "call"
+  [(call (match_operand:SI 0 "" "")
+        (match_operand 1 "" ""))]
+  ""
+  "bfin_expand_call (NULL_RTX, operands[0], operands[1], 0); DONE;")
+
+(define_expand "sibcall"
+  [(parallel [(call (match_operand:SI 0 "" "")
+                   (match_operand 1 "" ""))
+             (return)])]
+  ""
+  "bfin_expand_call (NULL_RTX, operands[0], operands[1], 1); DONE;")
+
+(define_expand "call_value"
+  [(set (match_operand 0 "register_operand" "")
+         (call (match_operand:SI 1 "" "")
+              (match_operand 2 "" "")))]
+  ""
+  "bfin_expand_call (operands[0], operands[1], operands[2], 0); DONE;")
+
+(define_expand "sibcall_value"
+  [(parallel [(set (match_operand 0 "register_operand" "")
+                  (call (match_operand:SI 1 "" "")
+                        (match_operand 2 "" "")))
+             (return)])]
+  ""
+  "bfin_expand_call (operands[0], operands[1], operands[2], 1); DONE;")
+
+(define_insn "*call_insn"
+  [(call (mem:SI (match_operand:SI 0 "call_insn_operand" "a,Q"))
+        (match_operand 1 "general_operand" "g,g"))]
+  "! SIBLING_CALL_P (insn)
+   && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+  "@
+  call (%0);
+  call %G0;"
+  [(set_attr "type" "call")
+   (set_attr "length" "2,4")])
+
+(define_insn "*sibcall_insn"
+  [(call (mem:SI (match_operand:SI 0 "call_insn_operand" "z,Q"))
+        (match_operand 1 "general_operand" "g,g"))
+   (return)]
+  "SIBLING_CALL_P (insn)
+   && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+  "@
+  jump (%0);
+  jump.l %G0;"
+  [(set_attr "type" "br")
+   (set_attr "length" "2,4")])
+
+(define_insn "*call_value_insn"
+  [(set (match_operand 0 "register_operand" "=d,d")
+        (call (mem:SI (match_operand:SI 1 "call_insn_operand" "a,Q"))
+             (match_operand 2 "general_operand" "g,g")))]
+  "! SIBLING_CALL_P (insn)
+   && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+  "@
+  call (%1);
+  call %G1;"
+  [(set_attr "type" "call")
+   (set_attr "length" "2,4")])
+
+(define_insn "*sibcall_value_insn"
+  [(set (match_operand 0 "register_operand" "=d,d")
+         (call (mem:SI (match_operand:SI 1 "call_insn_operand" "z,Q"))
+              (match_operand 2 "general_operand" "g,g")))
+   (return)]
+  "SIBLING_CALL_P (insn)
+   && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+  "@
+  jump (%1);
+  jump.l %G1;"
+  [(set_attr "type" "br")
+   (set_attr "length" "2,4")])
+
+;; Block move patterns
+
+;; We cheat.  This copies one more word than operand 2 indicates.
+
+(define_insn "rep_movsi"
+  [(set (match_operand:SI 0 "register_operand" "=&a")
+        (plus:SI (plus:SI (match_operand:SI 3 "register_operand" "0")
+                         (ashift:SI (match_operand:SI 2 "register_operand" "a")
+                                    (const_int 2)))
+                (const_int 4)))
+   (set (match_operand:SI 1 "register_operand" "=&b")
+        (plus:SI (plus:SI (match_operand:SI 4 "register_operand" "1")
+                         (ashift:SI (match_dup 2) (const_int 2)))
+                (const_int 4)))
+   (set (mem:BLK (match_dup 3))
+       (mem:BLK (match_dup 4)))
+   (use (match_dup 2))
+   (clobber (match_scratch:HI 5 "=&d"))]
+  ""
+  "lsetup (1f, 1f) LC1 = %2; %5 = [%4++]; 1: MNOP || [%3++] = %5 || %5 = [%4++]; [%3++] = %5;"
+  [(set_attr "type" "misc")
+   (set_attr "length" "16")])
+
+(define_insn "rep_movhi"
+  [(set (match_operand:SI 0 "register_operand" "=&a")
+        (plus:SI (plus:SI (match_operand:SI 3 "register_operand" "0")
+                         (ashift:SI (match_operand:SI 2 "register_operand" "a")
+                                    (const_int 1)))
+                (const_int 2)))
+   (set (match_operand:SI 1 "register_operand" "=&b")
+        (plus:SI (plus:SI (match_operand:SI 4 "register_operand" "1")
+                         (ashift:SI (match_dup 2) (const_int 1)))
+                (const_int 2)))
+   (set (mem:BLK (match_dup 3))
+       (mem:BLK (match_dup 4)))
+   (use (match_dup 2))
+   (clobber (match_scratch:HI 5 "=&d"))]
+  ""
+  "lsetup (1f, 1f) LC1 = %2; %h5 = W[%4++]; 1: MNOP || W [%3++] = %5 || %h5 = W [%4++]; W [%3++] = %5;"
+  [(set_attr "type" "misc")
+   (set_attr "length" "16")])
+
+(define_expand "movstrsi"
+  [(match_operand:BLK 0 "general_operand" "")
+   (match_operand:BLK 1 "general_operand" "")
+   (match_operand:SI 2 "const_int_operand" "")
+   (match_operand:SI 3 "const_int_operand" "")]
+  ""
+{
+  if (bfin_expand_strmov (operands[0], operands[1], operands[2], operands[3]))
+    DONE;
+  FAIL;
+})
+
+;; Conditional branch patterns
+;; The Blackfin has only few condition codes: eq, lt, lte, ltu, leu
+
+;; The only outcome of this pattern is that global variables
+;; bfin_compare_op[01] are set for use in bcond patterns.
+
+(define_expand "cmpbi"
+ [(set (cc0) (compare (match_operand:BI 0 "register_operand" "")
+                      (match_operand:BI 1 "immediate_operand" "")))]
+ ""
+{
+  bfin_compare_op0 = operands[0];
+  bfin_compare_op1 = operands[1];
+  DONE;
+})
+
+(define_expand "cmpsi"
+ [(set (cc0) (compare (match_operand:SI 0 "register_operand" "")
+                      (match_operand:SI 1 "nonmemory_operand" "")))]
+ ""
+{
+  bfin_compare_op0 = operands[0];
+  bfin_compare_op1 = operands[1];
+  DONE;
+})
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C,C")
+        (eq:BI (match_operand:SI 1 "register_operand" "d,a")
+               (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+  ""
+  "cc =%1==%2;"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C,C")
+        (ne:BI (match_operand:SI 1 "register_operand" "d,a")
+               (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+  "0"
+  "cc =%1!=%2;"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C,C")
+        (lt:BI (match_operand:SI 1 "register_operand" "d,a")
+               (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+  ""
+  "cc =%1<%2;"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C,C")
+        (le:BI (match_operand:SI 1 "register_operand" "d,a")
+               (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+  ""
+  "cc =%1<=%2;"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C,C")
+        (leu:BI (match_operand:SI 1 "register_operand" "d,a")
+                (match_operand:SI 2 "nonmemory_operand" "dKu3,aKu3")))]
+  ""
+  "cc =%1<=%2 (iu);"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C,C")
+        (ltu:BI (match_operand:SI 1 "register_operand" "d,a")
+                (match_operand:SI 2 "nonmemory_operand" "dKu3,aKu3")))]
+  ""
+  "cc =%1<%2 (iu);"
+  [(set_attr "type" "compare")])
+
+(define_expand "beq"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                  (label_ref (match_operand 0 "" ""))
+                  (pc)))]
+  ""
+{
+  rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
+  operands[1] = bfin_cc_rtx;   /* hard register: CC */
+  operands[2] = gen_rtx_EQ (BImode, op0, op1);
+  /* If we have a BImode input, then we already have a compare result, and
+     do not need to emit another comparison.  */
+  if (GET_MODE (bfin_compare_op0) == BImode)
+    {
+      if (bfin_compare_op1 == const0_rtx)
+       {
+         emit_insn (gen_cbranchbi4 (operands[2], op0, op1,
+                                    operands[0]));
+         DONE;
+       }
+      else
+       abort ();
+    }
+
+  operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bne"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                   (pc)))]
+  ""
+{
+  rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
+  /* If we have a BImode input, then we already have a compare result, and
+     do not need to emit another comparison.  */
+  if (GET_MODE (bfin_compare_op0) == BImode)
+    {
+      if (bfin_compare_op1 == const0_rtx)
+       {
+         rtx cmp = gen_rtx_NE (BImode, op0, op1);
+         emit_insn (gen_cbranchbi4 (cmp, op0, op1, operands[0]));
+         DONE;
+       }
+      else
+       abort ();
+    }
+
+  operands[1] = bfin_cc_rtx;   /* hard register: CC */
+  operands[2] = gen_rtx_EQ (BImode, op0, op1);
+  operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bgt"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                   (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LE (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bgtu"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                   (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LEU (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "blt"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                   (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LT (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bltu"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LTU (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+
+(define_expand "bge"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LT (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bgeu"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LTU (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "ble"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LE (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bleu"
+  [(set (match_dup 1) (match_dup 2))
+   (set (pc)
+       (if_then_else (match_dup 3)
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))
+  ]
+  ""
+{
+  operands[1] = bfin_cc_rtx;
+  operands[2] = gen_rtx_LEU (BImode, bfin_compare_op0, bfin_compare_op1);
+  operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_insn "cbranchbi4"
+  [(set (pc)
+       (if_then_else
+        (match_operator 0 "bfin_cbranch_operator"
+                        [(match_operand:BI 1 "cc_operand" "C")
+                         (match_operand:BI 2 "immediate_operand" "P0")])
+        (label_ref (match_operand 3 "" ""))
+        (pc)))]
+  ""
+{
+  asm_conditional_branch (insn, operands, 0, 0);
+  return "";
+}
+  [(set_attr "type" "brcc")])
+
+;; Special cbranch patterns to deal with the speculative load problem - see
+;; bfin_reorg for details.
+
+(define_insn "cbranch_predicted_taken"
+  [(set (pc)
+       (if_then_else
+        (match_operator 0 "bfin_cbranch_operator"
+                        [(match_operand:BI 1 "cc_operand" "C")
+                         (match_operand:BI 2 "immediate_operand" "P0")])
+        (label_ref (match_operand 3 "" ""))
+        (pc)))
+   (unspec [(const_int 0)] UNSPEC_CBRANCH_TAKEN)]
+  ""
+{
+  asm_conditional_branch (insn, operands, 0, 1);
+  return "";
+}
+  [(set_attr "type" "brcc")])
+
+(define_insn "cbranch_with_nops"
+  [(set (pc)
+       (if_then_else
+        (match_operator 0 "bfin_cbranch_operator"
+                        [(match_operand:BI 1 "cc_operand" "C")
+                         (match_operand:BI 2 "immediate_operand" "P0")])
+        (label_ref (match_operand 3 "" ""))
+        (pc)))
+   (unspec [(match_operand 4 "immediate_operand" "")] UNSPEC_CBRANCH_NOPS)]
+  "reload_completed"
+{
+  asm_conditional_branch (insn, operands, INTVAL (operands[4]), 0);
+  return "";
+}
+  [(set_attr "type" "brcc")
+   (set_attr "length" "6")])
+
+;; setcc insns.  */
+(define_expand "seq"
+  [(set (match_dup 1) (eq:BI (match_dup 2) (match_dup 3)))
+   (set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_dup 1) (const_int 0)))]
+  ""
+{
+  operands[2] = bfin_compare_op0;
+  operands[3] = bfin_compare_op1;
+  operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "slt"
+  [(set (match_dup 1) (lt:BI (match_dup 2) (match_dup 3)))
+   (set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_dup 1) (const_int 0)))]
+  ""
+{
+   operands[2] = bfin_compare_op0;
+   operands[3] = bfin_compare_op1;
+   operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "sle"
+  [(set (match_dup 1) (le:BI (match_dup 2) (match_dup 3)))
+   (set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_dup 1) (const_int 0)))]
+  ""
+{
+   operands[2] = bfin_compare_op0;
+   operands[3] = bfin_compare_op1;
+   operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "sltu"
+  [(set (match_dup 1) (ltu:BI (match_dup 2) (match_dup 3)))
+   (set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_dup 1) (const_int 0)))]
+  ""
+{
+   operands[2] = bfin_compare_op0;
+   operands[3] = bfin_compare_op1;
+   operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "sleu"
+  [(set (match_dup 1) (leu:BI (match_dup 2) (match_dup 3)))
+   (set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_dup 1) (const_int 0)))]
+  ""
+{
+   operands[2] = bfin_compare_op0;
+   operands[3] = bfin_compare_op1;
+   operands[1] = bfin_cc_rtx;
+})
+
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop;")
+
+;;;;;;;;;;;;;;;;;;;;   CC2dreg   ;;;;;;;;;;;;;;;;;;;;;;;;;
+(define_insn "movsibi"
+  [(set (match_operand:BI 0 "cc_operand" "=C")
+       (ne:BI (match_operand:SI 1 "register_operand" "d")
+              (const_int 0)))]
+  ""
+  "CC = %1;"
+  [(set_attr "length" "2")])
+
+(define_insn "movbisi"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ne:SI (match_operand:BI 1 "cc_operand" "C")
+              (const_int 0)))]
+  ""
+  "%0 = CC;"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:BI 0 "cc_operand" "=C")
+       (eq:BI (match_operand:BI 1 "cc_operand" " 0")
+              (const_int 0)))]
+  ""
+  "%0 = ! %0;"    /*  NOT CC;"  */
+  [(set_attr "type" "compare")])
+
+;; Vector and DSP insns
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "d")
+                          (const_int 24))
+               (lshiftrt:SI (match_operand:SI 2 "register_operand" "d")
+                            (const_int 8))))]
+  ""
+  "%0 = ALIGN8(%1, %2);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "d")
+                          (const_int 16))
+               (lshiftrt:SI (match_operand:SI 2 "register_operand" "d")
+                            (const_int 16))))]
+  ""
+  "%0 = ALIGN16(%1, %2);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "d")
+                          (const_int 8))
+               (lshiftrt:SI (match_operand:SI 2 "register_operand" "d")
+                            (const_int 24))))]
+  ""
+  "%0 = ALIGN24(%1, %2);"
+  [(set_attr "type" "dsp32")])
+
+;; Prologue and epilogue.
+
+(define_expand "prologue"
+  [(const_int 1)]
+  ""
+  "bfin_expand_prologue (); DONE;")
+
+(define_expand "epilogue"
+  [(const_int 1)]
+  ""
+  "bfin_expand_epilogue (1, 0); DONE;")
+
+(define_expand "sibcall_epilogue"
+  [(const_int 1)]
+  ""
+  "bfin_expand_epilogue (0, 0); DONE;")
+
+(define_expand "eh_return"
+  [(unspec_volatile [(match_operand:SI 0 "register_operand" "")]
+                   UNSPEC_VOLATILE_EH_RETURN)]
+  ""
+{
+  emit_move_insn (EH_RETURN_HANDLER_RTX, operands[0]);
+  emit_insn (gen_eh_return_internal ());
+  emit_barrier ();
+})
+
+(define_insn_and_split "eh_return_internal"
+  [(unspec_volatile [(reg:SI REG_P2)] UNSPEC_VOLATILE_EH_RETURN)]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 1)]
+  "bfin_expand_epilogue (1, 1); DONE;")
+
+(define_insn "link"
+  [(set (mem:SI (plus:SI (reg:SI REG_SP) (const_int -4))) (reg:SI REG_RETS))
+   (set (mem:SI (plus:SI (reg:SI REG_SP) (const_int -8))) (reg:SI REG_FP))
+   (set (reg:SI REG_FP)
+       (plus:SI (reg:SI REG_SP) (const_int -8)))
+   (set (reg:SI REG_SP)
+       (plus:SI (reg:SI REG_SP) (match_operand:SI 0 "immediate_operand" "i")))]
+  ""
+  "LINK %Z0;"
+  [(set_attr "length" "4")])
+
+(define_insn "unlink"
+  [(set (reg:SI REG_FP) (mem:SI (reg:SI REG_FP)))
+   (set (reg:SI REG_RETS) (mem:SI (plus:SI (reg:SI REG_FP) (const_int 4))))
+   (set (reg:SI REG_SP) (plus:SI (reg:SI REG_FP) (const_int 8)))]
+  ""
+  "UNLINK;"
+  [(set_attr "length" "4")])
+
+;; This pattern is slightly clumsy.  The stack adjust must be the final SET in
+;; the pattern, otherwise dwarf2out becomes very confused about which reg goes
+;; where on the stack, since it goes through all elements of the parallel in
+;; sequence.
+(define_insn "push_multiple"
+  [(match_parallel 0 "push_multiple_operation"
+    [(unspec [(match_operand:SI 1 "immediate_operand" "i")] UNSPEC_PUSH_MULTIPLE)])]
+  ""
+{
+  output_push_multiple (insn, operands);
+  return "";
+})
+
+(define_insn "pop_multiple"
+  [(match_parallel 0 "pop_multiple_operation"
+    [(set (reg:SI REG_SP)
+         (plus:SI (reg:SI REG_SP) (match_operand:SI 1 "immediate_operand" "i")))])]
+  ""
+{
+  output_pop_multiple (insn, operands);
+  return "";
+})
+
+(define_insn "return_internal"
+  [(return)
+   (unspec [(match_operand 0 "immediate_operand" "i")] UNSPEC_RETURN)]
+  "reload_completed"
+{
+  switch (INTVAL (operands[0]))
+    {
+    case EXCPT_HANDLER:
+      return "rtx;";
+    case NMI_HANDLER:
+      return "rtn;";
+    case INTERRUPT_HANDLER:
+      return "rti;";
+    case SUBROUTINE:
+      return "rts;";
+    }
+  gcc_unreachable ();
+})
+
+;;; Vector instructions
+
+(define_insn "addv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (plus:V2HI (match_operand:V2HI 1 "register_operand" "d")
+                  (match_operand:V2HI 2 "register_operand" "d")))]
+  ""
+  "%0 = %1 +|+ %2;"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "subv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (minus:V2HI (match_operand:V2HI 1 "register_operand" "d")
+                  (match_operand:V2HI 2 "register_operand" "d")))]
+  ""
+  "%0 = %1 -|- %2;"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "sminv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (smin:V2HI (match_operand:V2HI 1 "register_operand" "d")
+                  (match_operand:V2HI 2 "register_operand" "d")))]
+  ""
+  "%0 = MIN (%1, %2) (V);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "smaxv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (smax:V2HI (match_operand:V2HI 1 "register_operand" "d")
+                  (match_operand:V2HI 2 "register_operand" "d")))]
+  ""
+  "%0 = MAX (%1, %2) (V);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "mulv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (mult:V2HI (match_operand:V2HI 1 "register_operand" "d")
+                  (match_operand:V2HI 2 "register_operand" "d")))]
+  ""
+  "%h0 = %h1 * %h2, %d0 = %d1 * %d2 (IS);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "negv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (neg:V2HI (match_operand:V2HI 1 "register_operand" "d")))]
+  ""
+  "%0 = - %1 (V);"
+  [(set_attr "type" "dsp32")])
+
+(define_insn "absv2hi"
+  [(set (match_operand:V2HI 0 "register_operand" "=d")
+       (abs:V2HI (match_operand:V2HI 1 "register_operand" "d")))]
+  ""
+  "%0 = ABS %1 (V);"
+  [(set_attr "type" "dsp32")])
+
diff --git a/gcc/config/bfin/bfin.opt b/gcc/config/bfin/bfin.opt
new file mode 100644 (file)
index 0000000..b09d4dd
--- /dev/null
@@ -0,0 +1,36 @@
+; Options for the Blackfin port of the compiler
+;
+; Copyright (C) 2005 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 2, 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 COPYING.  If not, write to the Free
+; Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+; 02111-1307, USA.
+
+momit-leaf-frame-pointer
+Target Report Mask(OMIT_LEAF_FRAME_POINTER)
+Omit frame pointer for leaf functions
+
+mlow64k
+Target Report Mask(LOW_64K)
+Program is entirely located in low 64k of memory.
+
+mcsync
+Target Report Mask(CSYNC)
+Avoid speculative loads by inserting CSYNC or equivalent
+
+mid-shared-library
+Target Report Mask(ID_SHARED_LIBRARY)
+Enabled ID based shared library
diff --git a/gcc/config/bfin/crti.s b/gcc/config/bfin/crti.s
new file mode 100644 (file)
index 0000000..69e6700
--- /dev/null
@@ -0,0 +1,47 @@
+/* Specialized code needed to support construction and destruction of
+   file-scope objects in C++ and Java code, and to support exception handling.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   Contributed by Analog Devices.
+
+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 2, 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 COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+/*
+ * This file just supplies function prologues for the .init and .fini
+ * sections.  It is linked in before crtbegin.o.
+ */
+
+       .file   "crti.o"
+       .ident  "GNU C crti.o"
+
+       .section .init
+       .globl  _init
+       .type   _init,@function
+_init:
+       LINK 0;
+
+       .section .fini
+       .globl  _fini
+       .type   _fini,@function
+_fini:
+       LINK 0; 
diff --git a/gcc/config/bfin/crtn.s b/gcc/config/bfin/crtn.s
new file mode 100644 (file)
index 0000000..fb25b77
--- /dev/null
@@ -0,0 +1,43 @@
+/* Specialized code needed to support construction and destruction of
+   file-scope objects in C++ and Java code, and to support exception handling.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   Contributed by Analog Devices.
+
+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 2, 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 COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+/*
+ * This file supplies function epilogues for the .init and .fini sections.
+ * It is linked in after all other files.
+ */
+
+       .file   "crtn.o"
+       .ident  "GNU C crtn.o"
+
+       .section .init
+       unlink; 
+       rts;
+
+       .section .fini
+       unlink;
+       rts;
diff --git a/gcc/config/bfin/elf.h b/gcc/config/bfin/elf.h
new file mode 100644 (file)
index 0000000..643d519
--- /dev/null
@@ -0,0 +1,16 @@
+#define OBJECT_FORMAT_ELF
+
+#define LOCAL_LABEL_PREFIX "L$"
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM)                \
+     sprintf (LABEL, "*%s%s$%d", LOCAL_LABEL_PREFIX, PREFIX, (int) NUM)
+
+#undef  STARTFILE_SPEC
+#define STARTFILE_SPEC "crt0%O%s crti%O%s crtbegin%O%s"
+
+#undef  ENDFILE_SPEC
+#define ENDFILE_SPEC   "crtend%O%s crtn%O%s"
+
+#undef USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX "_"
diff --git a/gcc/config/bfin/lib1funcs.asm b/gcc/config/bfin/lib1funcs.asm
new file mode 100644 (file)
index 0000000..778d056
--- /dev/null
@@ -0,0 +1,120 @@
+/* libgcc functions for Blackfin.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   Contributed by Analog Devices.
+
+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 2, 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 COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+
+#ifdef L_divsi3
+.text
+.align 2
+.global ___divsi3;
+.type ___divsi3, STT_FUNC;
+
+___divsi3:
+        [--SP]= RETS;
+       [--SP] = R7;
+
+       R2 = -R0;
+        CC = R0 < 0;
+       IF CC R0 = R2;
+       R7 = CC;
+
+       R2 = -R1;
+        CC = R1 < 0;
+       IF CC R1 = R2;
+       R2 = CC;
+       R7 = R7 ^ R2;
+
+        CALL ___udivsi3;
+
+       CC = R7;
+       R1 = -R0;
+       IF CC R0 = R1;
+
+       R7 = [SP++];
+        RETS = [SP++];
+        RTS;
+#endif
+
+#ifdef L_modsi3        
+.align 2
+.global ___modsi3;
+.type ___modsi3, STT_FUNC;
+
+___modsi3:
+        [--SP] = RETS;
+       /* P1 and P2 are preserved by divsi3 and udivsi3.  */
+       P1 = R0;
+       P2 = R1;
+        CALL ___divsi3;
+       R1 = P1;
+       R2 = P2;
+       R2 *= R0;
+       R0 = R1 - R2;
+       RETS = [SP++];
+        RTS; 
+#endif
+
+#ifdef L_udivsi3
+.align 2
+.global ___udivsi3;
+.type ___udivsi3, STT_FUNC;
+
+___udivsi3:
+        P0 = 32;
+        LSETUP (0f, 1f) LC0 = P0;
+       /* upper half of dividend */
+        R3 = 0;
+0:
+       /* The first time round in the loop we shift in garbage, but since we
+          perform 33 shifts, it doesn't matter.  */
+       R0 = ROT R0 BY 1;
+       R3 = ROT R3 BY 1;
+       R2 = R3 - R1;
+        CC = R3 < R1 (IU);
+1:
+       /* Last instruction of the loop.  */
+       IF ! CC R3 = R2;
+
+       /* Shift in the last bit.  */
+       R0 = ROT R0 BY 1;
+       /* R0 is the result, R3 contains the remainder.  */
+       R0 = ~ R0;
+        RTS;
+#endif
+
+#ifdef L_umodsi3
+.align 2
+.global ___umodsi3;
+.type ___umodsi3, STT_FUNC;
+
+___umodsi3:
+        P1 = RETS;
+        CALL ___udivsi3;
+       R0 = R3;
+       RETS = P1; 
+        RTS;
+#endif
+
diff --git a/gcc/config/bfin/predicates.md b/gcc/config/bfin/predicates.md
new file mode 100644 (file)
index 0000000..5344884
--- /dev/null
@@ -0,0 +1,127 @@
+;; Predicate definitions for the Blackfin.
+;;
+;; 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 2, 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 COPYING.  If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; Return nonzero iff OP is one of the integer constants 1 or 2.
+(define_predicate "pos_scale_operand"
+  (and (match_code "const_int")
+       (match_test "INTVAL (op) == 1 || INTVAL (op) == 2")))
+
+;; Return nonzero iff OP is one of the integer constants 2 or 4.
+(define_predicate "scale_by_operand"
+  (and (match_code "const_int")
+       (match_test "INTVAL (op) == 2 || INTVAL (op) == 4")))
+
+;; Return nonzero if OP is a constant that consists of two parts; lower
+;; bits all zero and upper bits all ones.  In this case, we can perform
+;; an AND operation with a sequence of two shifts.  Don't return nonzero
+;; if the constant would be cheap to load.
+(define_predicate "highbits_operand"
+  (and (match_code "const_int")
+       (match_test "log2constp (-INTVAL (op)) && !CONST_7BIT_IMM_P (INTVAL (op))")))
+
+;; Return nonzero if OP is suitable as a right-hand side operand for an
+;; andsi3 operation.
+(define_predicate "rhs_andsi3_operand"
+  (ior (match_operand 0 "register_operand")
+       (and (match_code "const_int")
+           (match_test "log2constp (~INTVAL (op)) || INTVAL (op) == 255 || INTVAL (op) == 65535"))))
+
+;; Return nonzero if OP is a register or a constant with exactly one bit
+;; set.
+(define_predicate "regorlog2_operand"
+  (ior (match_operand 0 "register_operand")
+       (and (match_code "const_int")
+           (match_test "log2constp (INTVAL (op))"))))
+
+;; Like register_operand, but make sure that hard regs have a valid mode.
+(define_predicate "valid_reg_operand"
+  (match_operand 0 "register_operand")
+{
+  if (GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
+  if (REGNO (op) < FIRST_PSEUDO_REGISTER)
+    return HARD_REGNO_MODE_OK (REGNO (op), mode);
+  return 1;
+})
+
+;; Return nonzero if OP is the CC register.
+(define_predicate "cc_operand"
+  (and (match_code "reg")
+       (match_test "REGNO (op) == REG_CC && GET_MODE (op) == BImode")))
+
+;; Return nonzero if OP is a register or a 7 bit signed constant.
+(define_predicate "reg_or_7bit_operand"
+  (ior (match_operand 0 "register_operand")
+       (and (match_code "const_int")
+           (match_test "CONST_7BIT_IMM_P (INTVAL (op))"))))
+
+;; Used for secondary reloads, this function returns 1 if OP is of the
+;; form (plus (fp) (const_int)).
+(define_predicate "fp_plus_const_operand"
+  (match_code "plus")
+{
+  rtx op1, op2;
+
+  op1 = XEXP (op, 0);
+  op2 = XEXP (op, 1);
+  return (REG_P (op1)
+         && (REGNO (op1) == FRAME_POINTER_REGNUM
+             || REGNO (op1) == STACK_POINTER_REGNUM)
+         && GET_CODE (op2) == CONST_INT);
+})
+
+;; Returns 1 if OP is a symbolic operand, i.e. a symbol_ref or a label_ref,
+;; possibly with an offset.
+(define_predicate "symbolic_operand"
+  (ior (match_code "symbol_ref,label_ref")
+       (and (match_code "const")
+           (match_test "GET_CODE (XEXP (op,0)) == PLUS
+                        && (GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
+                            || GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF)
+                        && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT"))))
+
+;; Returns 1 if OP is a plain constant or matched by symbolic_operand.
+(define_predicate "symbolic_or_const_operand"
+  (ior (match_code "const_int,const_double")
+       (match_operand 0 "symbolic_operand")))
+
+;; True for any non-virtual or eliminable register.  Used in places where
+;; instantiation of such a register may cause the pattern to not be recognized.
+(define_predicate "register_no_elim_operand"
+  (match_operand 0 "register_operand")
+{
+  if (GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
+  return !(op == arg_pointer_rtx
+          || op == frame_pointer_rtx
+          || (REGNO (op) >= FIRST_PSEUDO_REGISTER
+              && REGNO (op) <= LAST_VIRTUAL_REGISTER));
+})
+
+;; Test for a valid operand for a call instruction.  Don't allow the
+;; arg pointer register or virtual regs since they may decay into
+;; reg + const, which the patterns can't handle.
+;; We only allow SYMBOL_REF if !flag_pic.
+(define_predicate "call_insn_operand"
+  (ior (and (match_test "!flag_pic") (match_code "symbol_ref"))
+       (match_operand 0 "register_no_elim_operand")))
+
+;; Test for an operator valid in a conditional branch
+(define_predicate "bfin_cbranch_operator"
+  (match_code "eq,ne"))
diff --git a/gcc/config/bfin/t-bfin b/gcc/config/bfin/t-bfin
new file mode 100644 (file)
index 0000000..662dc4c
--- /dev/null
@@ -0,0 +1,29 @@
+## Target part of the Makefile
+
+LIB1ASMSRC = bfin/lib1funcs.asm
+LIB1ASMFUNCS = _divsi3 _udivsi3 _umodsi3 _modsi3
+
+EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+       cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+       echo '#define FLOAT' > fp-bit.c
+       cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+# This shouldn't be needed here.  I added it to the specs file for now, until
+# it is fixed in binutils (if it is necessary).
+GCC_CFLAGS += -N
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/bfin/crti.s $(GCC_PASSES)
+       $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+       -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/bfin/crti.s
+
+$(T)crtn.o: $(srcdir)/config/bfin/crtn.s $(GCC_PASSES)
+       $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+       -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/bfin/crtn.s
diff --git a/gcc/config/bfin/t-bfin-elf b/gcc/config/bfin/t-bfin-elf
new file mode 100644 (file)
index 0000000..662dc4c
--- /dev/null
@@ -0,0 +1,29 @@
+## Target part of the Makefile
+
+LIB1ASMSRC = bfin/lib1funcs.asm
+LIB1ASMFUNCS = _divsi3 _udivsi3 _umodsi3 _modsi3
+
+EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+       cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+       echo '#define FLOAT' > fp-bit.c
+       cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+# This shouldn't be needed here.  I added it to the specs file for now, until
+# it is fixed in binutils (if it is necessary).
+GCC_CFLAGS += -N
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/bfin/crti.s $(GCC_PASSES)
+       $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+       -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/bfin/crti.s
+
+$(T)crtn.o: $(srcdir)/config/bfin/crtn.s $(GCC_PASSES)
+       $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+       -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/bfin/crtn.s
index 06d700e..23b046d 100644 (file)
@@ -1724,6 +1724,13 @@ on data in the eight bit data area.  Note the eight bit data area is limited to
 You must use GAS and GLD from GNU binutils version 2.7 or later for
 this attribute to work correctly.
 
+@item exception_handler
+@cindex exception handler functions on the Blackfin processor
+Use this attribute on the Blackfin to indicate that the specified function
+is an exception handler.  The compiler will generate function entry and
+exit sequences suitable for use in an exception handler when this
+attribute is present.
+
 @item far
 @cindex functions which handle memory bank switching
 On 68HC11 and 68HC12 the @code{far} attribute causes the compiler to
@@ -1872,8 +1879,8 @@ that the specified function is an interrupt handler.  The compiler will
 generate function entry and exit sequences suitable for use in an
 interrupt handler when this attribute is present.
 
-Note, interrupt handlers for the m68k, H8/300, H8/300H, H8S, and SH processors
-can be specified via the @code{interrupt_handler} attribute.
+Note, interrupt handlers for the Blackfin, m68k, H8/300, H8/300H, H8S, and
+SH processors can be specified via the @code{interrupt_handler} attribute.
 
 Note, on the AVR, interrupts will be enabled inside the function.
 
@@ -1887,11 +1894,17 @@ void f () __attribute__ ((interrupt ("IRQ")));
 Permissible values for this parameter are: IRQ, FIQ, SWI, ABORT and UNDEF@.
 
 @item interrupt_handler
-@cindex interrupt handler functions on the m68k, H8/300 and SH processors
-Use this attribute on the m68k, H8/300, H8/300H, H8S, and SH to indicate that
-the specified function is an interrupt handler.  The compiler will generate
-function entry and exit sequences suitable for use in an interrupt
-handler when this attribute is present.
+@cindex interrupt handler functions on the Blackfin, m68k, H8/300 and SH processors
+Use this attribute on the Blackfin, m68k, H8/300, H8/300H, H8S, and SH to
+indicate that the specified function is an interrupt handler.  The compiler
+will generate function entry and exit sequences suitable for use in an
+interrupt handler when this attribute is present.
+
+@item kspisusp
+@cindex User stack pointer in interrupts on the Blackfin
+When used together with @code{interrupt_handler}, @code{exception_handler}
+or @code{nmi_handler}, code will be generated to load the stack pointer
+from the USP register in the function prologue.
 
 @item long_call/short_call
 @cindex indirect calls on ARM
@@ -1971,6 +1984,19 @@ use the normal calling convention based on @code{jsr} and @code{rts}.
 This attribute can be used to cancel the effect of the @option{-mlong-calls}
 option.
 
+@item nesting
+@cindex Allow nesting in an interrupt handler on the Blackfin processor.
+Use this attribute together with @code{interrupt_handler},
+@code{exception_handler} or @code{nmi_handler} to indicate that the function
+entry code should enable nested interrupts or exceptions.
+
+@item nmi_handler
+@cindex NMI handler functions on the Blackfin processor
+Use this attribute on the Blackfin to indicate that the specified function
+is an NMI handler.  The compiler will generate function entry and
+exit sequences suitable for use in an NMI handler when this
+attribute is present.
+
 @item no_instrument_function
 @cindex @code{no_instrument_function} function attribute
 @opindex finstrument-functions
@@ -2125,8 +2151,8 @@ The @code{longjmp}-like counterpart of such function, if any, might need
 to be marked with the @code{noreturn} attribute.
 
 @item saveall
-@cindex save all registers on the H8/300, H8/300H, and H8S
-Use this attribute on the H8/300, H8/300H, and H8S to indicate that
+@cindex save all registers on the Blackfin, H8/300, H8/300H, and H8S
+Use this attribute on the Blackfin, H8/300, H8/300H, and H8S to indicate that
 all registers except the stack pointer should be saved in the prologue
 regardless of whether they are used or not.
 
index 975f983..09bc9fc 100644 (file)
@@ -2176,6 +2176,8 @@ GNU Compiler Collection on your machine.
 @item
 @uref{#avr,,avr}
 @item
+@uref{#bfin,,Blackfin}
+@item
 @uref{#c4x,,c4x}
 @item
 @uref{#dos,,DOS}
@@ -2490,6 +2492,23 @@ indicates that you should upgrade to a newer version of the binutils.
 @html
 <hr />
 @end html
+@heading @anchor{bfin}Blackfin
+
+The Blackfin processor, an Analog Devices DSP.
+@ifnothtml
+@xref{Blackfin Options,, Blackfin Options, gcc, Using and Porting the GNU
+Compiler Collection (GCC)},
+@end ifnothtml
+@ifhtml
+See ``Blackfin Options'' in the main manual
+@end ifhtml
+
+More information, and a version of binutils with support for this processor,
+is available at @uref{http://blackfin.uclinux.org}
+
+@html
+<hr />
+@end html
 @heading @anchor{c4x}c4x
 
 Texas Instruments TMS320C3x and TMS320C4x Floating Point Digital Signal
index d132655..2fe6bd9 100644 (file)
@@ -401,6 +401,11 @@ Objective-C and Objective-C++ Dialects}.
 @gccoptlist{-mmcu=@var{mcu}  -msize  -minit-stack=@var{n}  -mno-interrupts @gol
 -mcall-prologues  -mno-tablejump  -mtiny-stack  -mint8}
 
+@emph{Blackfin Options}
+@gccoptlist{-momit-leaf-frame-pointer -mno-omit-leaf-frame-pointer -mcsync @gol
+-mno-csync -mlow-64k -mno-low64k -mid-shared-library @gol
+-mno-id-shared-library -mshared-library-id=@var{n} @gol}
+
 @emph{CRIS Options}
 @gccoptlist{-mcpu=@var{cpu}  -march=@var{cpu}  -mtune=@var{cpu} @gol
 -mmax-stack-frame=@var{n}  -melinux-stacksize=@var{n} @gol
@@ -6730,6 +6735,7 @@ that macro, which enables you to change the defaults.
 * ARC Options::
 * ARM Options::
 * AVR Options::
+* Blackfin Options::
 * CRIS Options::
 * Darwin Options::
 * DEC Alpha Options::
@@ -7181,6 +7187,57 @@ comply to the C standards, but it will provide you with smaller code
 size.
 @end table
 
+@node Blackfin Options
+@subsection Blackfin Options
+@cindex Blackfin Options
+
+@table @gcctabopt
+@item -momit-leaf-frame-pointer
+@opindex momit-leaf-frame-pointer
+Don't keep the frame pointer in a register for leaf functions.  This
+avoids the instructions to save, set up and restore frame pointers and
+makes an extra register available in leaf functions.  The option
+@option{-fomit-frame-pointer} removes the frame pointer for all functions
+which might make debugging harder.
+
+@item -mcsync
+@opindex mcsync
+When enabled, the compiler will ensure that the generated code does not
+contain speculative loads after jump instructions.  This option is enabled
+by default.
+
+@item -mno-csync
+@opindex mno-csync
+Don't generate extra code to prevent speculative loads from occurring.
+
+@item -mlow-64k
+@opindex
+When enabled, the compiler is free to take advantage of the knowledge that
+the entire program fits into the low 64k of memory.
+
+@item -mno-low-64k
+@opindex mno-low-64k
+Assume that the program is arbitrarily large.  This is the default.
+
+@item -mid-shared-library
+@opindex mid-shared-library
+Generate code that supports shared libraries via the library ID method.
+This allows for execute in place and shared libraries in an environment
+without virtual memory management.  This option implies @option{-fPIC}.
+
+@item -mno-id-shared-library
+@opindex mno-id-shared-library
+Generate code that doesn't assume ID based shared libraries are being used.
+This is the default.
+
+@item -mshared-library-id=n
+@opindex mshared-library-id
+Specified the identification number of the ID based shared library being
+compiled.  Specifying a value of 0 will generate more compact code, specifying
+other values will force the allocation of that number to the current
+library but is no more space or time efficient than omitting this option.
+@end table
+
 @node CRIS Options
 @subsection CRIS Options
 @cindex CRIS Options
index 608b0a8..4600230 100644 (file)
@@ -2069,6 +2069,102 @@ range of 1 to 2047.
 
 @end table
 
+@item Blackfin family---@file{bfin.h}
+@table @code
+@item a
+P register
+
+@item d
+D register
+
+@item z
+A call clobbered P register.
+
+@item D
+Even-numbered D register
+
+@item W
+Odd-numbered D register
+
+@item e
+Accumulator register.
+
+@item A
+Even-numbered accumulator register.
+
+@item B
+Odd-numbered accumulator register.
+
+@item b
+I register
+
+@item B
+B register
+
+@item f
+M register
+
+@item c
+Registers used for circular buffering, i.e. I, B, or L registers.
+
+@item C
+The CC register.
+
+@item x
+Any D, P, B, M, I or L register.
+
+@item y
+Additional registers typically used only in prologues and epilogues: RETS,
+RETN, RETI, RETX, RETE, ASTAT, SEQSTAT and USP.
+
+@item w
+Any register except accumulators or CC.
+
+@item Ksh
+Signed 16 bit integer (in the range -32768 to 32767)
+
+@item Kuh
+Unsigned 16 bit integer (in the range 0 to 65535)
+
+@item Ks7
+Signed 7 bit integer (in the range -64 to 63)
+
+@item Ku7
+Unsigned 7 bit integer (in the range 0 to 127)
+
+@item Ku5
+Unsigned 5 bit integer (in the range 0 to 31)
+
+@item Ks4
+Signed 4 bit integer (in the range -8 to 7)
+
+@item Ks3
+Signed 3 bit integer (in the range -3 to 4)
+
+@item Ku3
+Unsigned 3 bit integer (in the range 0 to 7)
+
+@item P@var{n}
+Constant @var{n}, where @var{n} is a single-digit constant in the range 0 to 4.
+
+@item M1
+Constant 255.
+
+@item M2
+Constant 65535.
+
+@item J
+An integer constant with exactly a single bit set.
+
+@item L
+An integer constant with all bits set except exactly one.
+
+@item H
+
+@item Q
+Any SYMBOL_REF.
+@end table
+
 @item IP2K---@file{ip2k.h}
 @table @code
 @item a