OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / config / bfin / bfin.c
index 8b685b6..bb679ea 100644 (file)
@@ -1,12 +1,12 @@
 /* The Blackfin code generation auxiliary output file.
-   Copyright (C) 2005, 2006  Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009 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
+   by the Free Software Foundation; either version 3, or (at your
    option) any later version.
 
    GCC is distributed in the hope that it will be useful, but WITHOUT
@@ -15,9 +15,8 @@
    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, 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
 #include "langhooks.h"
 #include "bfin-protos.h"
 #include "tm-preds.h"
+#include "tm-constrs.h"
 #include "gt-bfin.h"
 #include "basic-block.h"
+#include "cfglayout.h"
 #include "timevar.h"
+#include "df.h"
 
 /* A C structure for machine-specific, per-function data.
    This is added to the cfun structure.  */
-struct machine_function GTY(())
+struct GTY(()) machine_function
 {
+  /* Set if we are notified by the doloop pass that a hardware loop
+     was created.  */
   int has_hardware_loops;
-};
 
-/* 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;
+  /* Set if we create a memcpy pattern that uses loop registers.  */
+  int has_loopreg_clobber;
+};
 
 /* RTX for condition code flag register and RETS register */
 extern GTY(()) rtx bfin_cc_rtx;
@@ -79,6 +82,7 @@ const char *dregs_pair_names[] =  DREGS_PAIR_NAMES;
 const char *byte_reg_names[]   =  BYTE_REGISTER_NAMES;
 
 static int arg_regs[] = FUNCTION_ARG_REGISTERS;
+static int ret_regs[] = FUNCTION_RETURN_REGISTERS;
 
 /* Nonzero if -mshared-library-id was given.  */
 static int bfin_lib_id_given;
@@ -91,7 +95,250 @@ static int bfin_flag_schedule_insns2;
    reorganization.  */
 static int bfin_flag_var_tracking;
 
-int splitting_for_sched;
+/* -mcpu support */
+bfin_cpu_t bfin_cpu_type = BFIN_CPU_UNKNOWN;
+
+/* -msi-revision support. There are three special values:
+   -1      -msi-revision=none.
+   0xffff  -msi-revision=any.  */
+int bfin_si_revision;
+
+/* The workarounds enabled */
+unsigned int bfin_workarounds = 0;
+
+struct bfin_cpu
+{
+  const char *name;
+  bfin_cpu_t type;
+  int si_revision;
+  unsigned int workarounds;
+};
+
+struct bfin_cpu bfin_cpus[] =
+{
+  {"bf512", BFIN_CPU_BF512, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+
+  {"bf514", BFIN_CPU_BF514, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+
+  {"bf516", BFIN_CPU_BF516, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+
+  {"bf518", BFIN_CPU_BF518, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+
+  {"bf522", BFIN_CPU_BF522, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+  {"bf522", BFIN_CPU_BF522, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+  {"bf522", BFIN_CPU_BF522, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+
+  {"bf523", BFIN_CPU_BF523, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+  {"bf523", BFIN_CPU_BF523, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+  {"bf523", BFIN_CPU_BF523, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+
+  {"bf524", BFIN_CPU_BF524, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+  {"bf524", BFIN_CPU_BF524, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+  {"bf524", BFIN_CPU_BF524, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+
+  {"bf525", BFIN_CPU_BF525, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+  {"bf525", BFIN_CPU_BF525, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+  {"bf525", BFIN_CPU_BF525, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+
+  {"bf526", BFIN_CPU_BF526, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+  {"bf526", BFIN_CPU_BF526, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+  {"bf526", BFIN_CPU_BF526, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+
+  {"bf527", BFIN_CPU_BF527, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_05000074},
+  {"bf527", BFIN_CPU_BF527, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+  {"bf527", BFIN_CPU_BF527, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000074},
+
+  {"bf531", BFIN_CPU_BF531, 0x0006,
+   WA_SPECULATIVE_LOADS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf531", BFIN_CPU_BF531, 0x0005,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000283 | WA_05000315
+   | WA_LOAD_LCREGS | WA_05000074},
+  {"bf531", BFIN_CPU_BF531, 0x0004,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf531", BFIN_CPU_BF531, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf532", BFIN_CPU_BF532, 0x0006,
+   WA_SPECULATIVE_LOADS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf532", BFIN_CPU_BF532, 0x0005,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000283 | WA_05000315
+   | WA_LOAD_LCREGS | WA_05000074},
+  {"bf532", BFIN_CPU_BF532, 0x0004,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf532", BFIN_CPU_BF532, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf533", BFIN_CPU_BF533, 0x0006,
+   WA_SPECULATIVE_LOADS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf533", BFIN_CPU_BF533, 0x0005,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_05000283 | WA_05000315
+   | WA_LOAD_LCREGS | WA_05000074},
+  {"bf533", BFIN_CPU_BF533, 0x0004,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf533", BFIN_CPU_BF533, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf534", BFIN_CPU_BF534, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf534", BFIN_CPU_BF534, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf534", BFIN_CPU_BF534, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf536", BFIN_CPU_BF536, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf536", BFIN_CPU_BF536, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf536", BFIN_CPU_BF536, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf537", BFIN_CPU_BF537, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf537", BFIN_CPU_BF537, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf537", BFIN_CPU_BF537, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf538", BFIN_CPU_BF538, 0x0005,
+   WA_SPECULATIVE_LOADS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf538", BFIN_CPU_BF538, 0x0004,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf538", BFIN_CPU_BF538, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_RETS
+   | WA_05000283 | WA_05000315 | WA_LOAD_LCREGS | WA_05000074},
+  {"bf538", BFIN_CPU_BF538, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf539", BFIN_CPU_BF539, 0x0005,
+   WA_SPECULATIVE_LOADS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf539", BFIN_CPU_BF539, 0x0004,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_LOAD_LCREGS | WA_05000074},
+  {"bf539", BFIN_CPU_BF539, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_RETS
+   | WA_05000283 | WA_05000315 | WA_LOAD_LCREGS | WA_05000074},
+  {"bf539", BFIN_CPU_BF539, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf542m", BFIN_CPU_BF542M, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+
+  {"bf542", BFIN_CPU_BF542, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf542", BFIN_CPU_BF542, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf542", BFIN_CPU_BF542, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf544m", BFIN_CPU_BF544M, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+
+  {"bf544", BFIN_CPU_BF544, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf544", BFIN_CPU_BF544, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf544", BFIN_CPU_BF544, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf547m", BFIN_CPU_BF547M, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+
+  {"bf547", BFIN_CPU_BF547, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf547", BFIN_CPU_BF547, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf547", BFIN_CPU_BF547, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf548m", BFIN_CPU_BF548M, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+
+  {"bf548", BFIN_CPU_BF548, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf548", BFIN_CPU_BF548, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf548", BFIN_CPU_BF548, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf549m", BFIN_CPU_BF549M, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+
+  {"bf549", BFIN_CPU_BF549, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf549", BFIN_CPU_BF549, 0x0001,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_05000074},
+  {"bf549", BFIN_CPU_BF549, 0x0000,
+   WA_SPECULATIVE_LOADS | WA_RETS | WA_INDIRECT_CALLS | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {"bf561", BFIN_CPU_BF561, 0x0005, WA_RETS
+   | WA_05000283 | WA_05000315 | WA_LOAD_LCREGS | WA_05000074},
+  {"bf561", BFIN_CPU_BF561, 0x0003,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+  {"bf561", BFIN_CPU_BF561, 0x0002,
+   WA_SPECULATIVE_LOADS | WA_SPECULATIVE_SYNCS | WA_RETS
+   | WA_05000283 | WA_05000257 | WA_05000315 | WA_LOAD_LCREGS
+   | WA_05000074},
+
+  {NULL, 0, 0, 0}
+};
+
+int splitting_for_sched, splitting_loops;
 
 static void
 bfin_globalize_label (FILE *stream, const char *name)
@@ -110,7 +357,7 @@ output_file_start (void)
 
   /* Variable tracking should be run after all optimizations which change order
      of insns.  It also needs a valid CFG.  This can't be done in
-     ia64_override_options, because flag_var_tracking is finalized after
+     override_options, because flag_var_tracking is finalized after
      that.  */
   bfin_flag_var_tracking = flag_var_tracking;
   flag_var_tracking = 0;
@@ -136,7 +383,8 @@ conditional_register_usage (void)
 /* Examine machine-dependent attributes of function type FUNTYPE and return its
    type.  See the definition of E_FUNKIND.  */
 
-static e_funkind funkind (tree funtype)
+static e_funkind
+funkind (const_tree funtype)
 {
   tree attrs = TYPE_ATTRIBUTES (funtype);
   if (lookup_attribute ("interrupt_handler", attrs))
@@ -159,7 +407,7 @@ static rtx
 legitimize_pic_address (rtx orig, rtx reg, rtx picreg)
 {
   rtx addr = orig;
-  rtx new = orig;
+  rtx new_rtx = orig;
 
   if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
     {
@@ -176,16 +424,16 @@ legitimize_pic_address (rtx orig, rtx reg, rtx picreg)
 
       if (reg == 0)
        {
-         gcc_assert (!no_new_pseudos);
+         gcc_assert (can_create_pseudo_p ());
          reg = gen_reg_rtx (Pmode);
        }
 
       tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), unspec);
-      new = gen_const_mem (Pmode, gen_rtx_PLUS (Pmode, picreg, tmp));
+      new_rtx = gen_const_mem (Pmode, gen_rtx_PLUS (Pmode, picreg, tmp));
 
-      emit_move_insn (reg, new);
+      emit_move_insn (reg, new_rtx);
       if (picreg == pic_offset_table_rtx)
-       current_function_uses_pic_offset_table = 1;
+       crtl->uses_pic_offset_table = 1;
       return reg;
     }
 
@@ -204,7 +452,7 @@ legitimize_pic_address (rtx orig, rtx reg, rtx picreg)
 
       if (reg == 0)
        {
-         gcc_assert (!no_new_pseudos);
+         gcc_assert (can_create_pseudo_p ());
          reg = gen_reg_rtx (Pmode);
        }
 
@@ -228,28 +476,21 @@ legitimize_pic_address (rtx orig, rtx reg, rtx picreg)
       return gen_rtx_PLUS (Pmode, base, addr);
     }
 
-  return new;
+  return new_rtx;
 }
 \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.
-   If IS_INTHANDLER, then everything that is live must be saved, even
-   if normally call-clobbered.  */
-
-static int
-n_dregs_to_save (bool is_inthandler)
+/* For a given REGNO, determine whether it must be saved in the function
+   prologue.  IS_INTHANDLER specifies whether we're generating a normal
+   prologue or an interrupt/exception one.  */
+static bool
+must_save_p (bool is_inthandler, unsigned regno)
 {
-  unsigned i;
-
-  for (i = REG_R0; i <= REG_R7; i++)
+  if (D_REGNO_P (regno))
     {
-      if (regs_ever_live[i] && (is_inthandler || ! call_used_regs[i]))
-       return REG_R7 - i + 1;
-
-      if (current_function_calls_eh_return)
+      bool is_eh_return_reg = false;
+      if (crtl->calls_eh_return)
        {
          unsigned j;
          for (j = 0; ; j++)
@@ -257,30 +498,74 @@ n_dregs_to_save (bool is_inthandler)
              unsigned test = EH_RETURN_DATA_REGNO (j);
              if (test == INVALID_REGNUM)
                break;
-             if (test == i)
-               return REG_R7 - i + 1;
+             if (test == regno)
+               is_eh_return_reg = true;
            }
        }
 
+      return (is_eh_return_reg
+             || (df_regs_ever_live_p (regno)
+                 && !fixed_regs[regno]
+                 && (is_inthandler || !call_used_regs[regno])));
     }
-  return 0;
+  else if (P_REGNO_P (regno))
+    {
+      return ((df_regs_ever_live_p (regno)
+              && !fixed_regs[regno]
+              && (is_inthandler || !call_used_regs[regno]))
+             || (is_inthandler
+                 && (ENABLE_WA_05000283 || ENABLE_WA_05000315)
+                 && regno == REG_P5)
+             || (!TARGET_FDPIC
+                 && regno == PIC_OFFSET_TABLE_REGNUM
+                 && (crtl->uses_pic_offset_table
+                     || (TARGET_ID_SHARED_LIBRARY && !current_function_is_leaf))));
+    }
+  else
+    return ((is_inthandler || !call_used_regs[regno])
+           && (df_regs_ever_live_p (regno)
+               || (!leaf_function_p () && call_used_regs[regno])));
+
+}
+
+/* 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.
+   If IS_INTHANDLER, then everything that is live must be saved, even
+   if normally call-clobbered.
+   If CONSECUTIVE, return the number of registers we can save in one
+   instruction with a push/pop multiple instruction.  */
+
+static int
+n_dregs_to_save (bool is_inthandler, bool consecutive)
+{
+  int count = 0;
+  unsigned i;
+
+  for (i = REG_R7 + 1; i-- != REG_R0;)
+    {
+      if (must_save_p (is_inthandler, i))
+       count++;
+      else if (consecutive)
+       return count;
+    }
+  return count;
 }
 
 /* Like n_dregs_to_save, but compute number of PREGS to save.  */
 
 static int
-n_pregs_to_save (bool is_inthandler)
+n_pregs_to_save (bool is_inthandler, bool consecutive)
 {
+  int count = 0;
   unsigned i;
 
-  for (i = REG_P0; i <= REG_P5; i++)
-    if ((regs_ever_live[i] && (is_inthandler || ! call_used_regs[i]))
-       || (!TARGET_FDPIC
-           && 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;
+  for (i = REG_P5 + 1; i-- != REG_P0;)
+    if (must_save_p (is_inthandler, i))
+      count++;
+    else if (consecutive)
+      return count;
+  return count;
 }
 
 /* Determine if we are going to save the frame pointer in the prologue.  */
@@ -288,7 +573,14 @@ n_pregs_to_save (bool is_inthandler)
 static bool
 must_save_fp_p (void)
 {
-  return frame_pointer_needed || regs_ever_live[REG_FP];
+  return df_regs_ever_live_p (REG_FP);
+}
+
+/* Determine if we are going to save the RETS register.  */
+static bool
+must_save_rets_p (void)
+{
+  return df_regs_ever_live_p (REG_RETS);
 }
 
 static bool
@@ -296,7 +588,7 @@ 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)
+  if (crtl->calls_eh_return)
     return true;
   return frame_pointer_needed;
 }
@@ -309,48 +601,105 @@ stack_frame_needed_p (void)
 static void
 expand_prologue_reg_save (rtx spreg, int saveall, bool is_inthandler)
 {
-  int ndregs = saveall ? 8 : n_dregs_to_save (is_inthandler);
-  int npregs = saveall ? 6 : n_pregs_to_save (is_inthandler);
-  int dregno = REG_R7 + 1 - ndregs;
-  int pregno = REG_P5 + 1 - npregs;
-  int total = ndregs + npregs;
-  int i;
-  rtx pat, insn, val;
+  rtx predec1 = gen_rtx_PRE_DEC (SImode, spreg);
+  rtx predec = gen_rtx_MEM (SImode, predec1);
+  int ndregs = saveall ? 8 : n_dregs_to_save (is_inthandler, false);
+  int npregs = saveall ? 6 : n_pregs_to_save (is_inthandler, false);
+  int ndregs_consec = saveall ? 8 : n_dregs_to_save (is_inthandler, true);
+  int npregs_consec = saveall ? 6 : n_pregs_to_save (is_inthandler, true);
+  int dregno, pregno;
+  int total_consec = ndregs_consec + npregs_consec;
+  int i, d_to_save;
+
+  if (saveall || is_inthandler)
+    {
+      rtx insn = emit_move_insn (predec, gen_rtx_REG (SImode, REG_ASTAT));
 
-  if (total == 0)
-    return;
+      RTX_FRAME_RELATED_P (insn) = 1;
+      for (dregno = REG_LT0; dregno <= REG_LB1; dregno++)
+       if (! current_function_is_leaf
+           || cfun->machine->has_hardware_loops
+           || cfun->machine->has_loopreg_clobber
+           || (ENABLE_WA_05000257
+               && (dregno == REG_LC0 || dregno == REG_LC1)))
+         {
+           insn = emit_move_insn (predec, gen_rtx_REG (SImode, dregno));
+           RTX_FRAME_RELATED_P (insn) = 1;
+         }
+    }
+
+  if (total_consec != 0)
+    {
+      rtx insn;
+      rtx val = GEN_INT (-total_consec * 4);
+      rtx pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_consec + 2));
+
+      XVECEXP (pat, 0, 0) = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, val),
+                                           UNSPEC_PUSH_MULTIPLE);
+      XVECEXP (pat, 0, total_consec + 1) = gen_rtx_SET (VOIDmode, spreg,
+                                                       gen_rtx_PLUS (Pmode,
+                                                                     spreg,
+                                                                     val));
+      RTX_FRAME_RELATED_P (XVECEXP (pat, 0, total_consec + 1)) = 1;
+      d_to_save = ndregs_consec;
+      dregno = REG_R7 + 1 - ndregs_consec;
+      pregno = REG_P5 + 1 - npregs_consec;
+      for (i = 0; i < total_consec; i++)
+       {
+         rtx memref = gen_rtx_MEM (word_mode,
+                                   gen_rtx_PLUS (Pmode, spreg,
+                                                 GEN_INT (- i * 4 - 4)));
+         rtx subpat;
+         if (d_to_save > 0)
+           {
+             subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
+                                                                  dregno++));
+             d_to_save--;
+           }
+         else
+           {
+             subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
+                                                                  pregno++));
+           }
+         XVECEXP (pat, 0, i + 1) = subpat;
+         RTX_FRAME_RELATED_P (subpat) = 1;
+       }
+      insn = emit_insn (pat);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
 
-  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)
+  for (dregno = REG_R0; ndregs != ndregs_consec; dregno++)
+    {
+      if (must_save_p (is_inthandler, dregno))
        {
-         subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
-                                                              dregno++));
+         rtx insn = emit_move_insn (predec, gen_rtx_REG (word_mode, dregno));
+         RTX_FRAME_RELATED_P (insn) = 1;
          ndregs--;
        }
-      else
+    }
+  for (pregno = REG_P0; npregs != npregs_consec; pregno++)
+    {
+      if (must_save_p (is_inthandler, pregno))
        {
-         subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
-                                                              pregno++));
-         npregs++;
+         rtx insn = emit_move_insn (predec, gen_rtx_REG (word_mode, pregno));
+         RTX_FRAME_RELATED_P (insn) = 1;
+         npregs--;
        }
-      XVECEXP (pat, 0, i + 1) = subpat;
-      RTX_FRAME_RELATED_P (subpat) = 1;
     }
-  insn = emit_insn (pat);
-  RTX_FRAME_RELATED_P (insn) = 1;
+  for (i = REG_P7 + 1; i < REG_CC; i++)
+    if (saveall 
+       || (is_inthandler
+           && (df_regs_ever_live_p (i)
+               || (!leaf_function_p () && call_used_regs[i]))))
+      {
+       rtx insn;
+       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;
+      }
 }
 
 /* Emit code to restore registers in the epilogue.  SAVEALL is nonzero if we
@@ -361,45 +710,101 @@ expand_prologue_reg_save (rtx spreg, int saveall, bool is_inthandler)
 static void
 expand_epilogue_reg_restore (rtx spreg, bool saveall, bool is_inthandler)
 {
-  int ndregs = saveall ? 8 : n_dregs_to_save (is_inthandler);
-  int npregs = saveall ? 6 : n_pregs_to_save (is_inthandler);
-  int total = ndregs + npregs;
-  int i, regno;
-  rtx pat, insn;
+  rtx postinc1 = gen_rtx_POST_INC (SImode, spreg);
+  rtx postinc = gen_rtx_MEM (SImode, postinc1);
 
-  if (total == 0)
-    return;
+  int ndregs = saveall ? 8 : n_dregs_to_save (is_inthandler, false);
+  int npregs = saveall ? 6 : n_pregs_to_save (is_inthandler, false);
+  int ndregs_consec = saveall ? 8 : n_dregs_to_save (is_inthandler, true);
+  int npregs_consec = saveall ? 6 : n_pregs_to_save (is_inthandler, true);
+  int total_consec = ndregs_consec + npregs_consec;
+  int i, regno;
+  rtx insn;
 
-  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)));
+  /* A slightly crude technique to stop flow from trying to delete "dead"
+     insns.  */
+  MEM_VOLATILE_P (postinc) = 1;
 
-  if (npregs > 0)
-    regno = REG_P5 + 1;
-  else
-    regno = REG_R7 + 1;
+  for (i = REG_CC - 1; i > REG_P7; i--)
+    if (saveall
+       || (is_inthandler
+           && (df_regs_ever_live_p (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);
+      }
 
-  for (i = 0; i < total; i++)
+  regno = REG_P5 - npregs_consec;
+  for (; npregs != npregs_consec; regno--)
     {
-      rtx addr = (i > 0
-                 ? gen_rtx_PLUS (Pmode, spreg, GEN_INT (i * 4))
-                 : spreg);
-      rtx memref = gen_rtx_MEM (word_mode, addr);
+      if (must_save_p (is_inthandler, regno))
+       {
+         emit_move_insn (gen_rtx_REG (word_mode, regno), postinc);
+         npregs--;
+       }
+    }
+  regno = REG_R7 - ndregs_consec;
+  for (; ndregs != ndregs_consec; regno--)
+    {
+      if (must_save_p (is_inthandler, regno))
+       {
+         emit_move_insn (gen_rtx_REG (word_mode, regno), postinc);
+         ndregs--;
+       }
+    }
 
-      regno--;
-      XVECEXP (pat, 0, i + 1)
-       = gen_rtx_SET (VOIDmode, gen_rtx_REG (word_mode, regno), memref);
+  if (total_consec != 0)
+    {
+      rtx pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_consec + 1));
+      XVECEXP (pat, 0, 0)
+       = gen_rtx_SET (VOIDmode, spreg,
+                      gen_rtx_PLUS (Pmode, spreg,
+                                    GEN_INT (total_consec * 4)));
+
+      if (npregs_consec > 0)
+       regno = REG_P5 + 1;
+      else
+       regno = REG_R7 + 1;
 
-      if (npregs > 0)
+      for (i = 0; i < total_consec; i++)
        {
-         if (--npregs == 0)
-           regno = REG_R7 + 1;
+         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_consec > 0)
+           {
+             if (--npregs_consec == 0)
+               regno = REG_R7 + 1;
+           }
        }
-    }
 
-  insn = emit_insn (pat);
-  RTX_FRAME_RELATED_P (insn) = 1;
+      insn = emit_insn (pat);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+  if (saveall || is_inthandler)
+    {
+      for (regno = REG_LB1; regno >= REG_LT0; regno--)
+       if (! current_function_is_leaf
+           || cfun->machine->has_hardware_loops
+           || cfun->machine->has_loopreg_clobber
+           || (ENABLE_WA_05000257 && (regno == REG_LC0 || regno == REG_LC1)))
+         emit_move_insn (gen_rtx_REG (SImode, regno), postinc);
+
+      emit_move_insn (gen_rtx_REG (SImode, REG_ASTAT), postinc);
+    }
 }
 
 /* Perform any needed actions needed for a function that is receiving a
@@ -455,20 +860,20 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum,
    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
+static bool
 bfin_frame_pointer_required (void) 
 {
   e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
 
   if (fkind != SUBROUTINE)
-    return 1;
+    return true;
 
   /* We turn 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 true;
 
-  return 0;
+  return false;
 }
 
 /* Return the number of registers pushed during the prologue.  */
@@ -481,41 +886,61 @@ n_regs_saved_by_prologue (void)
   tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
   bool all = (lookup_attribute ("saveall", attrs) != NULL_TREE
              || (is_inthandler && !current_function_is_leaf));
-  int ndregs = all ? 8 : n_dregs_to_save (is_inthandler);
-  int npregs = all ? 6 : n_pregs_to_save (is_inthandler);  
+  int ndregs = all ? 8 : n_dregs_to_save (is_inthandler, false);
+  int npregs = all ? 6 : n_pregs_to_save (is_inthandler, false);
   int n = ndregs + npregs;
+  int i;
 
   if (all || 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)
+      if (must_save_rets_p ())
        n++;
     }
 
-  if (fkind != SUBROUTINE)
+  if (fkind != SUBROUTINE || all)
     {
-      int i;
-
       /* Increment once for ASTAT.  */
       n++;
+      if (! current_function_is_leaf
+         || cfun->machine->has_hardware_loops
+         || cfun->machine->has_loopreg_clobber)
+       {
+         n += 6;
+       }
+    }
 
+  if (fkind != SUBROUTINE)
+    {
       /* 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;
     }
+
+  for (i = REG_P7 + 1; i < REG_CC; i++)
+    if (all
+       || (fkind != SUBROUTINE
+           && (df_regs_ever_live_p (i)
+               || (!leaf_function_p () && call_used_regs[i]))))
+      n += i == REG_A0 || i == REG_A1 ? 2 : 1;
+
   return n;
 }
 
+/* Given FROM and TO register numbers, say whether this elimination is
+   allowed.  Frame pointer elimination is automatically handled.
+
+   All other eliminations are valid.  */
+
+static bool
+bfin_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
+{
+  return (to == STACK_POINTER_REGNUM ? ! frame_pointer_needed : true);
+}
+
 /* Return the offset between two registers, one to be eliminated, and the other
    its replacement, at the start of a routine.  */
 
@@ -529,9 +954,9 @@ bfin_initial_elimination_offset (int from, int to)
 
   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)
+      if (crtl->outgoing_args_size >= FIXED_STACK_AREA)
+       offset += crtl->outgoing_args_size;
+      else if (crtl->outgoing_args_size)
        offset += FIXED_STACK_AREA;
 
       offset += get_frame_size ();
@@ -565,24 +990,58 @@ frame_related_constant_load (rtx reg, HOST_WIDE_INT constant, bool related)
     RTX_FRAME_RELATED_P (insn) = 1;
 }
 
-/* Generate efficient code to add a value to a P register.  We can use
-   P1 as a scratch register.  Set RTX_FRAME_RELATED_P on the generated
-   insns if FRAME is nonzero.  */
+/* Generate efficient code to add a value to a P register.
+   Set RTX_FRAME_RELATED_P on the generated insns if FRAME is nonzero.
+   EPILOGUE_P is zero if this function is called for prologue,
+   otherwise it's nonzero. And it's less than zero if this is for
+   sibcall epilogue.  */
 
 static void
-add_to_reg (rtx reg, HOST_WIDE_INT value, int frame)
+add_to_reg (rtx reg, HOST_WIDE_INT value, int frame, int epilogue_p)
 {
   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
+     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 tmpreg;
+      rtx tmpreg2;
       rtx insn;
 
+      tmpreg2 = NULL_RTX;
+
+      /* For prologue or normal epilogue, P1 can be safely used
+        as the temporary register. For sibcall epilogue, we try to find
+        a call used P register, which will be restored in epilogue.
+        If we cannot find such a P register, we have to use one I register
+        to help us.  */
+
+      if (epilogue_p >= 0)
+       tmpreg = gen_rtx_REG (SImode, REG_P1);
+      else
+       {
+         int i;
+         for (i = REG_P0; i <= REG_P5; i++)
+           if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
+               || (!TARGET_FDPIC
+                   && i == PIC_OFFSET_TABLE_REGNUM
+                   && (crtl->uses_pic_offset_table
+                       || (TARGET_ID_SHARED_LIBRARY
+                           && ! current_function_is_leaf))))
+             break;
+         if (i <= REG_P5)
+           tmpreg = gen_rtx_REG (SImode, i);
+         else
+           {
+             tmpreg = gen_rtx_REG (SImode, REG_P1);
+             tmpreg2 = gen_rtx_REG (SImode, REG_I0);
+             emit_move_insn (tmpreg2, tmpreg);
+           }
+       }
+
       if (frame)
        frame_related_constant_load (tmpreg, value, TRUE);
       else
@@ -591,6 +1050,9 @@ add_to_reg (rtx reg, HOST_WIDE_INT value, int frame)
       insn = emit_insn (gen_addsi3 (reg, reg, tmpreg));
       if (frame)
        RTX_FRAME_RELATED_P (insn) = 1;
+
+      if (tmpreg2 != NULL_RTX)
+       emit_move_insn (tmpreg, tmpreg2);
     }
   else
     do
@@ -658,10 +1120,10 @@ emit_link_insn (rtx spreg, HOST_WIDE_INT frame_size)
 static HOST_WIDE_INT
 arg_area_size (void)
 {
-  if (current_function_outgoing_args_size)
+  if (crtl->outgoing_args_size)
     {
-      if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
-       return current_function_outgoing_args_size;
+      if (crtl->outgoing_args_size >= FIXED_STACK_AREA)
+       return crtl->outgoing_args_size;
       else
        return FIXED_STACK_AREA;
     }
@@ -677,12 +1139,13 @@ do_link (rtx spreg, HOST_WIDE_INT frame_size, bool all)
 {
   frame_size += arg_area_size ();
 
-  if (all || stack_frame_needed_p ()
-      || (must_save_fp_p () && ! current_function_is_leaf))
+  if (all
+      || stack_frame_needed_p ()
+      || (must_save_rets_p () && must_save_fp_p ()))
     emit_link_insn (spreg, frame_size);
   else
     {
-      if (! current_function_is_leaf)
+      if (must_save_rets_p ())
        {
          rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
                                            gen_rtx_PRE_DEC (Pmode, spreg)),
@@ -698,34 +1161,37 @@ do_link (rtx spreg, HOST_WIDE_INT frame_size, bool all)
          rtx insn = emit_insn (pat);
          RTX_FRAME_RELATED_P (insn) = 1;
        }
-      add_to_reg (spreg, -frame_size, 1);
+      add_to_reg (spreg, -frame_size, 1, 0);
     }
 }
 
-/* Like do_link, but used for epilogues to deallocate the stack frame.  */
+/* Like do_link, but used for epilogues to deallocate the stack frame.
+   EPILOGUE_P is zero if this function is called for prologue,
+   otherwise it's nonzero. And it's less than zero if this is for
+   sibcall epilogue.  */
 
 static void
-do_unlink (rtx spreg, HOST_WIDE_INT frame_size, bool all)
+do_unlink (rtx spreg, HOST_WIDE_INT frame_size, bool all, int epilogue_p)
 {
   frame_size += arg_area_size ();
 
-  if (all || stack_frame_needed_p ())
+  if (stack_frame_needed_p ())
     emit_insn (gen_unlink ());
   else 
     {
       rtx postinc = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, spreg));
 
-      add_to_reg (spreg, frame_size, 0);
-      if (must_save_fp_p ())
+      add_to_reg (spreg, frame_size, 0, epilogue_p);
+      if (all || must_save_fp_p ())
        {
          rtx fpreg = gen_rtx_REG (Pmode, REG_FP);
          emit_move_insn (fpreg, postinc);
-         emit_insn (gen_rtx_USE (VOIDmode, fpreg));
+         emit_use (fpreg);
        }
-      if (! current_function_is_leaf)
+      if (all || must_save_rets_p ())
        {
          emit_move_insn (bfin_rets_rtx, postinc);
-         emit_insn (gen_rtx_USE (VOIDmode, bfin_rets_rtx));
+         emit_use (bfin_rets_rtx);
        }
     }
 }
@@ -735,15 +1201,13 @@ do_unlink (rtx spreg, HOST_WIDE_INT frame_size, bool all)
    SPREG contains (reg:SI REG_SP).  */
 
 static void
-expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
+expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind, bool all)
 {
-  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));
-  bool all = lookup_attribute ("saveall", attrs) != NULL_TREE;
   tree kspisusp = lookup_attribute ("kspisusp", attrs);
 
   if (kspisusp)
@@ -760,33 +1224,25 @@ expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
       RTX_FRAME_RELATED_P (insn) = 1;
     }
 
-  insn = emit_move_insn (predec, gen_rtx_REG (SImode, REG_ASTAT));
-  RTX_FRAME_RELATED_P (insn) = 1;
-
   /* If we're calling other functions, they won't save their call-clobbered
      registers, so we must save everything here.  */
   if (!current_function_is_leaf)
     all = true;
   expand_prologue_reg_save (spreg, all, true);
 
-  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 (ENABLE_WA_05000283 || ENABLE_WA_05000315)
+    {
+      rtx chipid = GEN_INT (trunc_int_for_mode (0xFFC00014, SImode));
+      rtx p5reg = gen_rtx_REG (Pmode, REG_P5);
+      emit_insn (gen_movbi (bfin_cc_rtx, const1_rtx));
+      emit_insn (gen_movsi_high (p5reg, chipid));
+      emit_insn (gen_movsi_low (p5reg, p5reg, chipid));
+      emit_insn (gen_dummy_load (p5reg, bfin_cc_rtx));
+    }
+  
   if (lookup_attribute ("nesting", attrs))
     {
-      rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
-                                       : fkind == NMI_HANDLER ? REG_RETN
-                                       : REG_RETI));
+      rtx srcreg = gen_rtx_REG (Pmode, ret_regs[fkind]);
       insn = emit_move_insn (predec, srcreg);
       RTX_FRAME_RELATED_P (insn) = 1;
     }
@@ -801,23 +1257,11 @@ expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
       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);
     }
 }
 
@@ -826,25 +1270,21 @@ expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
    SPREG contains (reg:SI REG_SP).  */
 
 static void
-expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind)
+expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind, bool all)
 {
-  int i;
+  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
   rtx postinc1 = gen_rtx_POST_INC (SImode, spreg);
   rtx postinc = gen_rtx_MEM (SImode, postinc1);
-  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
-  bool all = lookup_attribute ("saveall", attrs) != NULL_TREE;
 
   /* A slightly crude technique to stop flow from trying to delete "dead"
      insns.  */
   MEM_VOLATILE_P (postinc) = 1;
 
-  do_unlink (spreg, get_frame_size (), all);
+  do_unlink (spreg, get_frame_size (), all, 1);
 
   if (lookup_attribute ("nesting", attrs))
     {
-      rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
-                                       : fkind == NMI_HANDLER ? REG_RETN
-                                       : REG_RETI));
+      rtx srcreg = gen_rtx_REG (Pmode, ret_regs[fkind]);
       emit_move_insn (srcreg, postinc);
     }
 
@@ -853,31 +1293,14 @@ expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind)
   if (!current_function_is_leaf)
     all = true;
 
-  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, true);
 
-  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)));
+  emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, ret_regs[fkind])));
 }
 
 /* Used while emitting the prologue to generate code to load the correct value
@@ -889,8 +1312,7 @@ bfin_load_pic_reg (rtx dest)
   struct cgraph_local_info *i = NULL;
   rtx addr, insn;
  
-  if (flag_unit_at_a_time)
-    i = cgraph_local_info (current_function_decl);
+  i = cgraph_local_info (current_function_decl);
  
   /* Functions local to the translation unit don't need to reload the
      pic reg, since the caller always passes a usable one.  */
@@ -904,7 +1326,6 @@ bfin_load_pic_reg (rtx dest)
                         gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
                                         UNSPEC_LIBRARY_OFFSET));
   insn = emit_insn (gen_movsi (dest, gen_rtx_MEM (Pmode, addr)));
-  REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL);
   return dest;
 }
 
@@ -913,30 +1334,31 @@ bfin_load_pic_reg (rtx dest)
 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));
   rtx pic_reg_loaded = NULL_RTX;
+  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+  bool all = lookup_attribute ("saveall", attrs) != NULL_TREE;
 
   if (fkind != SUBROUTINE)
     {
-      expand_interrupt_handler_prologue (spreg, fkind);
+      expand_interrupt_handler_prologue (spreg, fkind, all);
       return;
     }
 
-  if (current_function_limit_stack
-      || TARGET_STACK_CHECK_L1)
+  if (crtl->limit_stack
+      || (TARGET_STACK_CHECK_L1
+         && !DECL_NO_LIMIT_STACK (current_function_decl)))
     {
       HOST_WIDE_INT offset
        = bfin_initial_elimination_offset (ARG_POINTER_REGNUM,
                                           STACK_POINTER_REGNUM);
-      rtx lim = current_function_limit_stack ? stack_limit_rtx : NULL_RTX;
+      rtx lim = crtl->limit_stack ? stack_limit_rtx : NULL_RTX;
       rtx p2reg = gen_rtx_REG (Pmode, REG_P2);
 
       if (!lim)
        {
-         rtx p1reg = gen_rtx_REG (Pmode, REG_P1);
          emit_move_insn (p2reg, gen_int_mode (0xFFB00000, SImode));
          emit_move_insn (p2reg, gen_rtx_MEM (Pmode, p2reg));
          lim = p2reg;
@@ -966,42 +1388,46 @@ bfin_expand_prologue (void)
        {
          if (lim != p2reg)
            emit_move_insn (p2reg, lim);
-         add_to_reg (p2reg, offset, 0);
+         add_to_reg (p2reg, offset, 0, 0);
          lim = p2reg;
        }
       emit_insn (gen_compare_lt (bfin_cc_rtx, spreg, lim));
       emit_insn (gen_trapifcc ());
     }
-  expand_prologue_reg_save (spreg, 0, false);
+  expand_prologue_reg_save (spreg, all, false);
 
-  do_link (spreg, frame_size, false);
+  do_link (spreg, frame_size, all);
 
   if (TARGET_ID_SHARED_LIBRARY
       && !TARGET_SEP_DATA
-      && (current_function_uses_pic_offset_table
+      && (crtl->uses_pic_offset_table
          || !current_function_is_leaf))
     bfin_load_pic_reg (pic_offset_table_rtx);
 }
 
 /* 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.  */
+   eh_return pattern. SIBCALL_P is true if this is a sibcall epilogue,
+   false otherwise.  */
 
 void
-bfin_expand_epilogue (int need_return, int eh_return)
+bfin_expand_epilogue (int need_return, int eh_return, bool sibcall_p)
 {
   rtx spreg = gen_rtx_REG (Pmode, REG_SP);
   e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+  int e = sibcall_p ? -1 : 1;
+  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+  bool all = lookup_attribute ("saveall", attrs) != NULL_TREE;
 
   if (fkind != SUBROUTINE)
     {
-      expand_interrupt_handler_epilogue (spreg, fkind);
+      expand_interrupt_handler_epilogue (spreg, fkind, all);
       return;
     }
 
-  do_unlink (spreg, get_frame_size (), false);
+  do_unlink (spreg, get_frame_size (), all, e);
 
-  expand_epilogue_reg_restore (spreg, false, false);
+  expand_epilogue_reg_restore (spreg, all, false);
 
   /* Omit the return insn if this is for a sibcall.  */
   if (! need_return)
@@ -1010,7 +1436,7 @@ bfin_expand_epilogue (int need_return, int eh_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)));
+  emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, REG_RETS)));
 }
 \f
 /* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
@@ -1024,7 +1450,7 @@ bfin_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
      call-clobbered.  */
 
   if (funkind (TREE_TYPE (current_function_decl)) != SUBROUTINE
-      && !regs_ever_live[new_reg])
+      && !df_regs_ever_live_p (new_reg))
     return 0;
 
   return 1;
@@ -1043,26 +1469,10 @@ bfin_return_addr_rtx (int count)
   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;
-}
-
 static rtx
 bfin_delegitimize_address (rtx orig_x)
 {
-  rtx x = orig_x, y;
+  rtx x = orig_x;
 
   if (GET_CODE (x) != MEM)
     return orig_x;
@@ -1080,7 +1490,7 @@ bfin_delegitimize_address (rtx orig_x)
 
 /* 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.  */
+   32-bit instruction.  */
 
 int
 effective_address_32bit_p (rtx op, enum machine_mode mode) 
@@ -1097,9 +1507,12 @@ effective_address_32bit_p (rtx op, enum machine_mode mode)
       return 0;
     }
 
+  if (GET_CODE (XEXP (op, 1)) == UNSPEC)
+    return 1;
+
   offset = INTVAL (XEXP (op, 1));
 
-  /* All byte loads use a 16 bit offset.  */
+  /* All byte loads use a 16-bit offset.  */
   if (GET_MODE_SIZE (mode) == 1)
     return 1;
 
@@ -1133,7 +1546,7 @@ bfin_dsp_memref_p (rtx x)
    All addressing modes are equally cheap on the Blackfin.  */
 
 static int
-bfin_address_cost (rtx addr ATTRIBUTE_UNUSED)
+bfin_address_cost (rtx addr ATTRIBUTE_UNUSED, bool speed ATTRIBUTE_UNUSED)
 {
   return 1;
 }
@@ -1276,41 +1689,61 @@ print_operand (FILE *file, rtx x, char code)
        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;
+             if (REGNO (x) < 32)
+               fprintf (file, "%s", short_reg_names[REGNO (x)]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else if (code == 'd')
            {
-             gcc_assert (REGNO (x) < 32);
-             fprintf (file, "%s", high_reg_names[REGNO (x)]);
-             break;
+             if (REGNO (x) < 32)
+               fprintf (file, "%s", high_reg_names[REGNO (x)]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else if (code == 'w')
            {
-             gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
-             fprintf (file, "%s.w", reg_names[REGNO (x)]);
+             if (REGNO (x) == REG_A0 || REGNO (x) == REG_A1)
+               fprintf (file, "%s.w", reg_names[REGNO (x)]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else if (code == 'x')
            {
-             gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
-             fprintf (file, "%s.x", reg_names[REGNO (x)]);
+             if (REGNO (x) == REG_A0 || REGNO (x) == REG_A1)
+               fprintf (file, "%s.x", reg_names[REGNO (x)]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
+           }
+         else if (code == 'v')
+           {
+             if (REGNO (x) == REG_A0)
+               fprintf (file, "AV0");
+             else if (REGNO (x) == REG_A1)
+               fprintf (file, "AV1");
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else if (code == 'D')
            {
-             fprintf (file, "%s", dregs_pair_names[REGNO (x)]);
+             if (D_REGNO_P (REGNO (x)))
+               fprintf (file, "%s", dregs_pair_names[REGNO (x)]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else if (code == 'H')
            {
-             gcc_assert (mode == DImode || mode == DFmode);
-             gcc_assert (REG_P (x));
-             fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+             if ((mode == DImode || mode == DFmode) && REG_P (x))
+               fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else if (code == 'T')
            {
-             gcc_assert (D_REGNO_P (REGNO (x)));
-             fprintf (file, "%s", byte_reg_names[REGNO (x)]);
+             if (D_REGNO_P (REGNO (x)))
+               fprintf (file, "%s", byte_reg_names[REGNO (x)]);
+             else
+               output_operand_lossage ("invalid operand for code '%c'", code);
            }
          else 
            fprintf (file, "%s", reg_names[REGNO (x)]);
@@ -1354,6 +1787,9 @@ print_operand (FILE *file, rtx x, char code)
                case MACFLAG_M:
                  fputs ("(M)", file);
                  break;
+               case MACFLAG_IS_M:
+                 fputs ("(IS,M)", file);
+                 break;
                case MACFLAG_ISS2:
                  fputs ("(ISS2)", file);
                  break;
@@ -1381,6 +1817,8 @@ print_operand (FILE *file, rtx x, char code)
            x = GEN_INT ((INTVAL (x) >> 16) & 0xffff);
          else if (code == 'h')
            x = GEN_INT (INTVAL (x) & 0xffff);
+         else if (code == 'N')
+           x = GEN_INT (-INTVAL (x));
          else if (code == 'X')
            x = GEN_INT (exact_log2 (0xffffffff & INTVAL (x)));
          else if (code == 'Y')
@@ -1532,7 +1970,7 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
    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
+   First 3 words are in registers. So, if an argument is larger
    than the registers available, it will span the register and
    stack.   */
 
@@ -1560,17 +1998,17 @@ bfin_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 static bool
 bfin_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
                        enum machine_mode mode ATTRIBUTE_UNUSED,
-                       tree type, bool named ATTRIBUTE_UNUSED)
+                       const_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.  */
+   TARGET_RETURN_IN_MEMORY.  */
 
-int
-bfin_return_in_memory (tree type)
+static bool
+bfin_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
 {
   int size = int_size_in_bytes (type);
   return size > 2 * UNITS_PER_WORD || size == -1;
@@ -1635,6 +2073,7 @@ static bool
 bfin_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
                              tree exp ATTRIBUTE_UNUSED)
 {
+  struct cgraph_local_info *this_func, *called_func;
   e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
   if (fkind != SUBROUTINE)
     return false;
@@ -1646,18 +2085,13 @@ bfin_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
      not need to reload P5 in the prologue, but the sibcall wil pop P5 in the
      sibcall epilogue, and we end up with the wrong value in P5.  */
 
-  if (!flag_unit_at_a_time || decl == NULL)
+  if (!decl)
     /* Not enough information.  */
     return false;
-
-  {
-    struct cgraph_local_info *this_func, *called_func;
-    rtx addr, insn;
  
-    this_func = cgraph_local_info (current_function_decl);
-    called_func = cgraph_local_info (decl);
-    return !called_func->local || this_func->local;
-  }
+  this_func = cgraph_local_info (current_function_decl);
+  called_func = cgraph_local_info (decl);
+  return !called_func->local || this_func->local;
 }
 \f
 /* Emit RTL insns to initialize the variable parts of a trampoline at
@@ -1665,8 +2099,7 @@ bfin_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
    code.  CXT is an RTX for the static chain value for the function.  */
 
 void
-initialize_trampoline (tramp, fnaddr, cxt)
-     rtx tramp, fnaddr, cxt;
+initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
 {
   rtx t1 = copy_to_reg (fnaddr);
   rtx t2 = copy_to_reg (cxt);
@@ -1786,6 +2219,8 @@ bool
 bfin_longcall_p (rtx op, int call_cookie)
 {
   gcc_assert (GET_CODE (op) == SYMBOL_REF);
+  if (SYMBOL_REF_WEAK (op))
+    return 1;
   if (call_cookie & CALL_SHORT)
     return 0;
   if (call_cookie & CALL_LONG)
@@ -1804,9 +2239,10 @@ bfin_expand_call (rtx retval, rtx fnaddr, rtx callarg1, rtx cookie, int sibcall)
 {
   rtx use = NULL, call;
   rtx callee = XEXP (fnaddr, 0);
-  int nelts = 2 + !!sibcall;
+  int nelts = 3;
   rtx pat;
   rtx picreg = get_hard_reg_initial_val (SImode, FDPIC_REGNO);
+  rtx retsreg = gen_rtx_REG (Pmode, REG_RETS);
   int n;
 
   /* In an untyped call, we can get NULL for operand 2.  */
@@ -1821,8 +2257,40 @@ bfin_expand_call (rtx retval, rtx fnaddr, rtx callarg1, rtx cookie, int sibcall)
 
   if (TARGET_FDPIC)
     {
+      int caller_in_sram, callee_in_sram;
+
+      /* 0 is not in sram, 1 is in L1 sram, 2 is in L2 sram.  */
+      caller_in_sram = callee_in_sram = 0;
+
+      if (lookup_attribute ("l1_text",
+                           DECL_ATTRIBUTES (cfun->decl)) != NULL_TREE)
+       caller_in_sram = 1;
+      else if (lookup_attribute ("l2",
+                                DECL_ATTRIBUTES (cfun->decl)) != NULL_TREE)
+       caller_in_sram = 2;
+
+      if (GET_CODE (callee) == SYMBOL_REF
+         && SYMBOL_REF_DECL (callee) && DECL_P (SYMBOL_REF_DECL (callee)))
+       {
+         if (lookup_attribute
+             ("l1_text",
+              DECL_ATTRIBUTES (SYMBOL_REF_DECL (callee))) != NULL_TREE)
+           callee_in_sram = 1;
+         else if (lookup_attribute
+                  ("l2",
+                   DECL_ATTRIBUTES (SYMBOL_REF_DECL (callee))) != NULL_TREE)
+           callee_in_sram = 2;
+       }
+
       if (GET_CODE (callee) != SYMBOL_REF
-         || bfin_longcall_p (callee, INTVAL (cookie)))
+         || bfin_longcall_p (callee, INTVAL (cookie))
+         || (GET_CODE (callee) == SYMBOL_REF
+             && !SYMBOL_REF_LOCAL_P (callee)
+             && TARGET_INLINE_PLT)
+         || caller_in_sram != callee_in_sram
+         || (caller_in_sram && callee_in_sram
+             && (GET_CODE (callee) != SYMBOL_REF
+                 || !SYMBOL_REF_LOCAL_P (callee))))
        {
          rtx addr = callee;
          if (! address_operand (addr, Pmode))
@@ -1861,6 +2329,8 @@ bfin_expand_call (rtx retval, rtx fnaddr, rtx callarg1, rtx cookie, int sibcall)
   XVECEXP (pat, 0, n++) = gen_rtx_USE (VOIDmode, cookie);
   if (sibcall)
     XVECEXP (pat, 0, n++) = gen_rtx_RETURN (VOIDmode);
+  else
+    XVECEXP (pat, 0, n++) = gen_rtx_CLOBBER (VOIDmode, retsreg);
   call = emit_call_insn (pat);
   if (use)
     CALL_INSN_FUNCTION_USAGE (call) = use;
@@ -1872,19 +2342,19 @@ 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);
+  enum reg_class rclass = REGNO_REG_CLASS (regno);
 
   if (mode == CCmode)
     return 0;
 
   if (mode == V2HImode)
     return D_REGNO_P (regno);
-  if (class == CCREGS)
+  if (rclass == CCREGS)
     return mode == BImode;
   if (mode == PDImode || mode == V2PDImode)
     return regno == REG_A0 || regno == REG_A1;
 
-  /* Allow all normal 32 bit regs, except REG_M3, in case regclass ever comes
+  /* Allow all normal 32-bit regs, except REG_M3, in case regclass ever comes
      up with a bad register class (such as ALL_REGS) for DImode.  */
   if (mode == DImode)
     return regno < REG_M3;
@@ -1912,20 +2382,14 @@ bfin_register_move_cost (enum machine_mode mode,
                         enum reg_class class1, enum reg_class class2)
 {
   /* These need secondary reloads, so they're more expensive.  */
-  if ((class1 == CCREGS && class2 != DREGS)
-      || (class1 != DREGS && class2 == CCREGS))
+  if ((class1 == CCREGS && !reg_class_subset_p (class2, DREGS))
+      || (class2 == CCREGS && !reg_class_subset_p (class1, DREGS)))
     return 4;
 
   /* 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;
-
   if (GET_MODE_CLASS (mode) == MODE_INT)
     {
       /* Discourage trying to use the accumulators.  */
@@ -1948,25 +2412,25 @@ bfin_register_move_cost (enum machine_mode mode,
 
 int
 bfin_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
-                      enum reg_class class,
+                      enum reg_class rclass,
                       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))
+  if (! reg_class_subset_p (rclass, 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
+   RCLASS requires an extra scratch register.  Return the class needed for the
    scratch register.  */
 
 static enum reg_class
-bfin_secondary_reload (bool in_p, rtx x, enum reg_class class,
-                    enum machine_mode mode, secondary_reload_info *sri)
+bfin_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
+                      enum machine_mode mode, secondary_reload_info *sri)
 {
   /* If we have HImode or QImode, we can only use DREGS as secondary registers;
      in most other cases we can also use PREGS.  */
@@ -1994,13 +2458,13 @@ bfin_secondary_reload (bool in_p, rtx x, enum reg_class class,
   if (fp_plus_const_operand (x, mode))
     {
       rtx op2 = XEXP (x, 1);
-      int large_constant_p = ! CONST_7BIT_IMM_P (INTVAL (op2));
+      int large_constant_p = ! satisfies_constraint_Ks7 (op2);
 
-      if (class == PREGS || class == PREGS_CLOBBERED)
+      if (rclass == PREGS || rclass == 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)
+      if ((rclass == DREGS || rclass == DPREGS)
          && ! large_constant_p)
        return NO_REGS;
       /* Reloading to anything other than a DREG?  Use a PREG scratch
@@ -2012,28 +2476,39 @@ bfin_secondary_reload (bool in_p, rtx x, enum reg_class class,
   /* 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 (x_class == AREGS || x_class == EVEN_AREGS || x_class == ODD_AREGS)
+    return (rclass == DREGS || rclass == AREGS || rclass == EVEN_AREGS
+           || rclass == ODD_AREGS
+           ? NO_REGS : DREGS);
 
-  if (class == AREGS)
+  if (rclass == AREGS || rclass == EVEN_AREGS || rclass == ODD_AREGS)
     {
+      if (code == MEM)
+       {
+         sri->icode = in_p ? CODE_FOR_reload_inpdi : CODE_FOR_reload_outpdi;
+         return NO_REGS;
+       }
+
       if (x != const0_rtx && x_class != DREGS)
-       return DREGS;
+       {
+         return DREGS;
+       }
       else
        return NO_REGS;
     }
 
   /* CCREGS can only be moved from/to DREGS.  */
-  if (class == CCREGS && x_class != DREGS)
+  if (rclass == CCREGS && x_class != DREGS)
     return DREGS;
-  if (x_class == CCREGS && class != DREGS)
+  if (x_class == CCREGS && rclass != 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))
+    if (! reg_class_subset_p (rclass, default_class))
       return default_class;
+
   return NO_REGS;
 }
 \f
@@ -2051,17 +2526,87 @@ bfin_handle_option (size_t code, const char *arg, int value)
       bfin_lib_id_given = 1;
       return true;
 
-    default:
-      return true;
-    }
-}
+    case OPT_mcpu_:
+      {
+       const char *p, *q;
+       int i;
 
-static struct machine_function *
-bfin_init_machine_status (void)
-{
+       i = 0;
+       while ((p = bfin_cpus[i].name) != NULL)
+         {
+           if (strncmp (arg, p, strlen (p)) == 0)
+             break;
+           i++;
+         }
+
+       if (p == NULL)
+         {
+           error ("-mcpu=%s is not valid", arg);
+           return false;
+         }
+
+       bfin_cpu_type = bfin_cpus[i].type;
+
+       q = arg + strlen (p);
+
+       if (*q == '\0')
+         {
+           bfin_si_revision = bfin_cpus[i].si_revision;
+           bfin_workarounds |= bfin_cpus[i].workarounds;
+         }
+       else if (strcmp (q, "-none") == 0)
+         bfin_si_revision = -1;
+       else if (strcmp (q, "-any") == 0)
+         {
+           bfin_si_revision = 0xffff;
+           while (bfin_cpus[i].type == bfin_cpu_type)
+             {
+               bfin_workarounds |= bfin_cpus[i].workarounds;
+               i++;
+             }
+         }
+       else
+         {
+           unsigned int si_major, si_minor;
+           int rev_len, n;
+
+           rev_len = strlen (q);
+
+           if (sscanf (q, "-%u.%u%n", &si_major, &si_minor, &n) != 2
+               || n != rev_len
+               || si_major > 0xff || si_minor > 0xff)
+             {
+             invalid_silicon_revision:
+               error ("-mcpu=%s has invalid silicon revision", arg);
+               return false;
+             }
+
+           bfin_si_revision = (si_major << 8) | si_minor;
+
+           while (bfin_cpus[i].type == bfin_cpu_type
+                  && bfin_cpus[i].si_revision != bfin_si_revision)
+             i++;
+
+           if (bfin_cpus[i].type != bfin_cpu_type)
+             goto invalid_silicon_revision;
+
+           bfin_workarounds |= bfin_cpus[i].workarounds;
+         }
+
+       return true;
+      }
+
+    default:
+      return true;
+    }
+}
+
+static struct machine_function *
+bfin_init_machine_status (void)
+{
   struct machine_function *f;
 
-  f = ggc_alloc_cleared (sizeof (struct machine_function));
+  f = GGC_CNEW (struct machine_function);
 
   return f;
 }
@@ -2071,6 +2616,27 @@ bfin_init_machine_status (void)
 void
 override_options (void)
 {
+  /* If processor type is not specified, enable all workarounds.  */
+  if (bfin_cpu_type == BFIN_CPU_UNKNOWN)
+    {
+      int i;
+
+      for (i = 0; bfin_cpus[i].name != NULL; i++)
+       bfin_workarounds |= bfin_cpus[i].workarounds;
+
+      bfin_si_revision = 0xffff;
+    }
+
+  if (bfin_csync_anomaly == 1)
+    bfin_workarounds |= WA_SPECULATIVE_SYNCS;
+  else if (bfin_csync_anomaly == 0)
+    bfin_workarounds &= ~WA_SPECULATIVE_SYNCS;
+
+  if (bfin_specld_anomaly == 1)
+    bfin_workarounds |= WA_SPECULATIVE_LOADS;
+  else if (bfin_specld_anomaly == 0)
+    bfin_workarounds &= ~WA_SPECULATIVE_LOADS;
+
   if (TARGET_OMIT_LEAF_FRAME_POINTER)
     flag_omit_frame_pointer = 1;
 
@@ -2078,9 +2644,6 @@ override_options (void)
   if (bfin_lib_id_given && ! TARGET_ID_SHARED_LIBRARY)
     error ("-mshared-library-id= specified without -mid-shared-library");
 
-  if (TARGET_ID_SHARED_LIBRARY && flag_pic == 0)
-    flag_pic = 1;
-
   if (stack_limit_rtx && TARGET_STACK_CHECK_L1)
     error ("Can't use multiple stack checking methods together.");
 
@@ -2095,6 +2658,9 @@ override_options (void)
   if (TARGET_SEP_DATA)
     target_flags |= MASK_ID_SHARED_LIBRARY | MASK_LEAF_ID_SHARED_LIBRARY;
 
+  if (TARGET_ID_SHARED_LIBRARY && flag_pic == 0)
+    flag_pic = 1;
+
   /* There is no single unaligned SI op for PIC code.  Sometimes we
      need to use ".4byte" and sometimes we need to use ".picptr".
      See bfin_assemble_integer for details.  */
@@ -2106,6 +2672,18 @@ override_options (void)
   if (flag_pic && !TARGET_FDPIC && !TARGET_ID_SHARED_LIBRARY)
     flag_pic = 0;
 
+  if (TARGET_MULTICORE && bfin_cpu_type != BFIN_CPU_BF561)
+    error ("-mmulticore can only be used with BF561");
+
+  if (TARGET_COREA && !TARGET_MULTICORE)
+    error ("-mcorea should be used with -mmulticore");
+
+  if (TARGET_COREB && !TARGET_MULTICORE)
+    error ("-mcoreb should be used with -mmulticore");
+
+  if (TARGET_COREA && TARGET_COREB)
+    error ("-mcorea and -mcoreb can't be used together");
+
   flag_schedule_insns = 0;
 
   /* Passes after sched2 can break the helpful TImode annotations that
@@ -2202,7 +2780,7 @@ 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 op0 = XEXP (cmp, 0), op1 = XEXP (cmp, 1);
   rtx tem = bfin_cc_rtx;
   enum rtx_code code = GET_CODE (cmp);
 
@@ -2230,7 +2808,7 @@ bfin_gen_compare (rtx cmp, enum machine_mode mode ATTRIBUTE_UNUSED)
        code2 = EQ;
        break;
       }
-      emit_insn (gen_rtx_SET (BImode, tem,
+      emit_insn (gen_rtx_SET (VOIDmode, tem,
                              gen_rtx_fmt_ee (code1, BImode, op0, op1)));
     }
 
@@ -2238,7 +2816,7 @@ bfin_gen_compare (rtx cmp, enum machine_mode mode ATTRIBUTE_UNUSED)
 }
 \f
 /* Return nonzero iff C has exactly one bit set if it is interpreted
-   as a 32 bit constant.  */
+   as a 32-bit constant.  */
 
 int
 log2constp (unsigned HOST_WIDE_INT c)
@@ -2287,7 +2865,6 @@ split_load_immediate (rtx operands[])
   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.  */
@@ -2322,7 +2899,7 @@ split_load_immediate (rtx operands[])
 
   if (D_REGNO_P (regno))
     {
-      if (CONST_7BIT_IMM_P (tmp))
+      if (tmp >= -64 && tmp <= 63)
        {
          emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
          emit_insn (gen_movstricthi_high (operands[0], GEN_INT (val & -65536)));
@@ -2349,7 +2926,7 @@ split_load_immediate (rtx operands[])
     return 0;
 
   if (optimize_size
-      && num_compl_zero && CONST_7BIT_IMM_P (shifted_compl))
+      && num_compl_zero && shifted_compl >= -64 && shifted_compl <= 63)
     {
       /* If optimizing for size, generate a sequence that has more instructions
         but is shorter.  */
@@ -2388,8 +2965,26 @@ bfin_valid_reg_p (unsigned int regno, int strict, enum machine_mode mode,
     return REGNO_OK_FOR_BASE_NONSTRICT_P (regno, mode, outer_code, SCRATCH);
 }
 
-bool
-bfin_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+/* Recognize 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]
+*/
+
+static bool
+bfin_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
 {
   switch (GET_CODE (x)) {
   case REG:
@@ -2471,15 +3066,16 @@ bfin_legitimate_constant_p (rtx x)
 }
 
 static bool
-bfin_rtx_costs (rtx x, int code, int outer_code, int *total)
+bfin_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
 {
   int cost2 = COSTS_N_INSNS (1);
+  rtx op0, op1;
 
   switch (code)
     {
     case CONST_INT:
       if (outer_code == SET || outer_code == PLUS)
-        *total = CONST_7BIT_IMM_P (INTVAL (x)) ? 0 : cost2;
+        *total = satisfies_constraint_Ks7 (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)
@@ -2507,43 +3103,153 @@ bfin_rtx_costs (rtx x, int code, int outer_code, int *total)
       return true;
 
     case PLUS:
-      if (GET_MODE (x) == Pmode)
+      op0 = XEXP (x, 0);
+      op1 = XEXP (x, 1);
+      if (GET_MODE (x) == SImode)
        {
-         if (GET_CODE (XEXP (x, 0)) == MULT
-             && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+         if (GET_CODE (op0) == MULT
+             && GET_CODE (XEXP (op0, 1)) == CONST_INT)
            {
-             HOST_WIDE_INT val = INTVAL (XEXP (XEXP (x, 0), 1));
+             HOST_WIDE_INT val = INTVAL (XEXP (op0, 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);
+                 *total += rtx_cost (XEXP (op0, 0), outer_code, speed);
+                 *total += rtx_cost (op1, outer_code, speed);
                  return true;
                }
            }
+         *total = cost2;
+         if (GET_CODE (op0) != REG
+             && (GET_CODE (op0) != SUBREG || GET_CODE (SUBREG_REG (op0)) != REG))
+           *total += rtx_cost (op0, SET, speed);
+#if 0 /* We'd like to do this for accuracy, but it biases the loop optimizer
+        towards creating too many induction variables.  */
+         if (!reg_or_7bit_operand (op1, SImode))
+           *total += rtx_cost (op1, SET, speed);
+#endif
        }
-
-      /* fall through */
+      else if (GET_MODE (x) == DImode)
+       {
+         *total = 6 * cost2;
+         if (GET_CODE (op1) != CONST_INT
+             || !satisfies_constraint_Ks7 (op1))
+           *total += rtx_cost (op1, PLUS, speed);
+         if (GET_CODE (op0) != REG
+             && (GET_CODE (op0) != SUBREG || GET_CODE (SUBREG_REG (op0)) != REG))
+           *total += rtx_cost (op0, PLUS, speed);
+       }
+      return true;
 
     case MINUS:
+      if (GET_MODE (x) == DImode)
+       *total = 6 * cost2;
+      else
+       *total = cost2;
+      return true;
+      
     case ASHIFT: 
     case ASHIFTRT:
     case LSHIFTRT:
       if (GET_MODE (x) == DImode)
        *total = 6 * cost2;
-      return false;
+      else
+       *total = cost2;
+
+      op0 = XEXP (x, 0);
+      op1 = XEXP (x, 1);
+      if (GET_CODE (op0) != REG
+         && (GET_CODE (op0) != SUBREG || GET_CODE (SUBREG_REG (op0)) != REG))
+       *total += rtx_cost (op0, code, speed);
+
+      return true;
          
-    case AND:
     case IOR:
+    case AND:
     case XOR:
+      op0 = XEXP (x, 0);
+      op1 = XEXP (x, 1);
+
+      /* Handle special cases of IOR: rotates, ALIGN insns, movstricthi_high.  */
+      if (code == IOR)
+       {
+         if ((GET_CODE (op0) == LSHIFTRT && GET_CODE (op1) == ASHIFT)
+             || (GET_CODE (op0) == ASHIFT && GET_CODE (op1) == ZERO_EXTEND)
+             || (GET_CODE (op0) == ASHIFT && GET_CODE (op1) == LSHIFTRT)
+             || (GET_CODE (op0) == AND && GET_CODE (op1) == CONST_INT))
+           {
+             *total = cost2;
+             return true;
+           }
+       }
+
+      if (GET_CODE (op0) != REG
+         && (GET_CODE (op0) != SUBREG || GET_CODE (SUBREG_REG (op0)) != REG))
+       *total += rtx_cost (op0, code, speed);
+
       if (GET_MODE (x) == DImode)
-       *total = 2 * cost2;
-      return false;
+       {
+         *total = 2 * cost2;
+         return true;
+       }
+      *total = cost2;
+      if (GET_MODE (x) != SImode)
+       return true;
+
+      if (code == AND)
+       {
+         if (! rhs_andsi3_operand (XEXP (x, 1), SImode))
+           *total += rtx_cost (XEXP (x, 1), code, speed);
+       }
+      else
+       {
+         if (! regorlog2_operand (XEXP (x, 1), SImode))
+           *total += rtx_cost (XEXP (x, 1), code, speed);
+       }
+
+      return true;
+
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+      if (outer_code == SET
+         && XEXP (x, 1) == const1_rtx
+         && GET_CODE (XEXP (x, 2)) == CONST_INT)
+       {
+         *total = 2 * cost2;
+         return true;
+       }
+      /* fall through */
+
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      *total = cost2;
+      return true;
 
     case MULT:
-      if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD)
-       *total = COSTS_N_INSNS (3);
-      return false;
+       {
+         op0 = XEXP (x, 0);
+         op1 = XEXP (x, 1);
+         if (GET_CODE (op0) == GET_CODE (op1)
+             && (GET_CODE (op0) == ZERO_EXTEND
+                 || GET_CODE (op0) == SIGN_EXTEND))
+           {
+             *total = COSTS_N_INSNS (1);
+             op0 = XEXP (op0, 0);
+             op1 = XEXP (op1, 0);
+           }
+         else if (!speed)
+           *total = COSTS_N_INSNS (1);
+         else
+           *total = COSTS_N_INSNS (3);
+
+         if (GET_CODE (op0) != REG
+             && (GET_CODE (op0) != SUBREG || GET_CODE (SUBREG_REG (op0)) != REG))
+           *total += rtx_cost (op0, MULT, speed);
+         if (GET_CODE (op1) != REG
+             && (GET_CODE (op1) != SUBREG || GET_CODE (SUBREG_REG (op1)) != REG))
+           *total += rtx_cost (op1, MULT, speed);
+       }
+      return true;
 
     case UDIV:
     case UMOD:
@@ -2560,16 +3266,11 @@ bfin_rtx_costs (rtx x, int code, int outer_code, int *total)
       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;
+static int n_regs_to_save;
 
 int
 push_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
@@ -2638,6 +3339,7 @@ push_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
          lastpreg++;
        }
     }
+  n_regs_to_save = 8 - first_dreg_to_save + 6 - first_preg_to_save;
   return 1;
 }
 
@@ -2697,6 +3399,7 @@ pop_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
     }
   first_dreg_to_save = lastdreg;
   first_preg_to_save = lastpreg;
+  n_regs_to_save = 8 - first_dreg_to_save + 6 - first_preg_to_save;
   return 1;
 }
 
@@ -2821,6 +3524,7 @@ bfin_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp)
              countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
 
              emit_insn (gen_rep_movsi (destreg, srcreg, countreg, destreg, srcreg));
+             cfun->machine->has_loopreg_clobber = true;
            }
          if (count & 2)
            {
@@ -2841,6 +3545,7 @@ bfin_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp)
              countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
 
              emit_insn (gen_rep_movhi (destreg, srcreg, countreg, destreg, srcreg));
+             cfun->machine->has_loopreg_clobber = true;
            }
        }
       if (count & 1)
@@ -2852,6 +3557,24 @@ bfin_expand_movmem (rtx dst, rtx src, rtx count_exp, rtx align_exp)
   return false;
 }
 \f
+/* Compute the alignment for a local variable.
+   TYPE is the data type, and ALIGN is the alignment that
+   the object would ordinarily have.  The value of this macro is used
+   instead of that alignment to align the object.  */
+
+int
+bfin_local_alignment (tree type, int align)
+{
+  /* Increasing alignment for (relatively) big types allows the builtin
+     memcpy can use 32 bit loads/stores.  */
+  if (TYPE_SIZE (type)
+      && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+      && (TREE_INT_CST_LOW (TYPE_SIZE (type)) > 8
+         || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 32)
+    return 32;
+  return align;
+}
+\f
 /* Implement TARGET_SCHED_ISSUE_RATE.  */
 
 static int
@@ -2882,16 +3605,48 @@ bfin_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
   if (dep_insn_type == TYPE_MOVE || dep_insn_type == TYPE_MCLD)
     {
       rtx pat = PATTERN (dep_insn);
+      if (GET_CODE (pat) == PARALLEL)
+       pat = XVECEXP (pat, 0, 0);
       rtx dest = SET_DEST (pat);
       rtx src = SET_SRC (pat);
-      if (! ADDRESS_REGNO_P (REGNO (dest)) || ! D_REGNO_P (REGNO (src)))
+      if (! ADDRESS_REGNO_P (REGNO (dest))
+         || ! (MEM_P (src) || D_REGNO_P (REGNO (src))))
        return cost;
       return cost + (dep_insn_type == TYPE_MOVE ? 4 : 3);
     }
 
   return cost;
 }
+\f
+/* This function acts like NEXT_INSN, but is aware of three-insn bundles and
+   skips all subsequent parallel instructions if INSN is the start of such
+   a group.  */
+static rtx
+find_next_insn_start (rtx insn)
+{
+  if (GET_MODE (insn) == SImode)
+    {
+      while (GET_MODE (insn) != QImode)
+       insn = NEXT_INSN (insn);
+    }
+  return NEXT_INSN (insn);
+}
 
+/* This function acts like PREV_INSN, but is aware of three-insn bundles and
+   skips all subsequent parallel instructions if INSN is the start of such
+   a group.  */
+static rtx
+find_prev_insn_start (rtx insn)
+{
+  insn = PREV_INSN (insn);
+  gcc_assert (GET_MODE (insn) != SImode);
+  if (GET_MODE (insn) == QImode)
+    {
+      while (GET_MODE (PREV_INSN (insn)) == SImode)
+       insn = PREV_INSN (insn);
+    }
+  return insn;
+}
 \f
 /* Increment the counter for the number of loop instructions in the
    current function.  */
@@ -2908,6 +3663,9 @@ bfin_hardware_loop (void)
 /* Maximum size of a loop.  */
 #define MAX_LOOP_LENGTH 2042
 
+/* Maximum distance of the LSETUP instruction from the loop start.  */
+#define MAX_LSETUP_DISTANCE 30
+
 /* We need to keep a vector of loops */
 typedef struct loop_info *loop_info;
 DEF_VEC_P (loop_info);
@@ -2915,14 +3673,21 @@ DEF_VEC_ALLOC_P (loop_info,heap);
 
 /* Information about a loop we have found (or are in the process of
    finding).  */
-struct loop_info GTY (())
+struct GTY (()) loop_info
 {
   /* loop number, for dumps */
   int loop_no;
 
-  /* Predecessor block of the loop.   This is the one that falls into
-     the loop and contains the initialization instruction.  */
-  basic_block predecessor;
+  /* All edges that jump into and out of the loop.  */
+  VEC(edge,gc) *incoming;
+
+  /* We can handle two cases: all incoming edges have the same destination
+     block, or all incoming edges have the same source block.  These two
+     members are set to the common source or destination we found, or NULL
+     if different blocks were found.  If both are NULL the loop can't be
+     optimized.  */
+  basic_block incoming_src;
+  basic_block incoming_dest;
 
   /* First block in the loop.  This is the one branched to by the loop_end
      insn.  */
@@ -2944,12 +3709,6 @@ struct loop_info GTY (())
   /* The iteration register.  */
   rtx iter_reg;
 
-  /* The new initialization insn.  */
-  rtx init;
-
-  /* The new initialization instruction.  */
-  rtx loop_init;
-
   /* The new label placed at the beginning of the loop. */
   rtx start_label;
 
@@ -3058,6 +3817,31 @@ bfin_scan_loop (loop_info loop, rtx reg, rtx loop_end)
   return false;
 }
 
+/* Estimate the length of INSN conservatively.  */
+
+static int
+length_for_loop (rtx insn)
+{
+  int length = 0;
+  if (JUMP_P (insn) && any_condjump_p (insn) && !optimize_size)
+    {
+      if (ENABLE_WA_SPECULATIVE_SYNCS)
+       length = 8;
+      else if (ENABLE_WA_SPECULATIVE_LOADS)
+       length = 6;
+    }
+  else if (LABEL_P (insn))
+    {
+      if (ENABLE_WA_SPECULATIVE_SYNCS)
+       length = 4;
+    }
+
+  if (INSN_P (insn))
+    length += get_attr_length (insn);
+
+  return length;
+}
+
 /* Optimize LOOP.  */
 
 static void
@@ -3065,12 +3849,12 @@ bfin_optimize_loop (loop_info loop)
 {
   basic_block bb;
   loop_info inner;
-  rtx insn, init_insn, last_insn, nop_insn;
+  rtx insn, last_insn;
   rtx loop_init, start_label, end_label;
   rtx reg_lc0, reg_lc1, reg_lt0, reg_lt1, reg_lb0, reg_lb1;
-  rtx iter_reg;
+  rtx iter_reg, scratchreg, scratch_init, scratch_init_insn;
   rtx lc_reg, lt_reg, lb_reg;
-  rtx seq;
+  rtx seq, seq_end;
   int length;
   unsigned ix;
   int inner_depth = 0;
@@ -3114,39 +3898,98 @@ bfin_optimize_loop (loop_info loop)
   /* Get the loop iteration register.  */
   iter_reg = loop->iter_reg;
 
-  if (!DPREG_P (iter_reg))
+  if (!REG_P (iter_reg))
     {
       if (dump_file)
-       fprintf (dump_file, ";; loop %d iteration count NOT in PREG or DREG\n",
+       fprintf (dump_file, ";; loop %d iteration count not in a register\n",
                 loop->loop_no);
       goto bad_loop;
     }
+  scratchreg = NULL_RTX;
+  scratch_init = iter_reg;
+  scratch_init_insn = NULL_RTX;
+  if (!PREG_P (iter_reg) && loop->incoming_src)
+    {
+      basic_block bb_in = loop->incoming_src;
+      int i;
+      for (i = REG_P0; i <= REG_P5; i++)
+       if ((df_regs_ever_live_p (i)
+            || (funkind (TREE_TYPE (current_function_decl)) == SUBROUTINE
+                && call_used_regs[i]))
+           && !REGNO_REG_SET_P (df_get_live_out (bb_in), i))
+         {
+           scratchreg = gen_rtx_REG (SImode, i);
+           break;
+         }
+      for (insn = BB_END (bb_in); insn != BB_HEAD (bb_in);
+          insn = PREV_INSN (insn))
+       {
+         rtx set;
+         if (NOTE_P (insn) || BARRIER_P (insn))
+           continue;
+         set = single_set (insn);
+         if (set && rtx_equal_p (SET_DEST (set), iter_reg))
+           {
+             if (CONSTANT_P (SET_SRC (set)))
+               {
+                 scratch_init = SET_SRC (set);
+                 scratch_init_insn = insn;
+               }
+             break;
+           }
+         else if (reg_mentioned_p (iter_reg, PATTERN (insn)))
+           break;
+       }
+    }
 
-  /* Check if start_label appears before loop_end and calculate the
-     offset between them.  We calculate the length of instructions
-     conservatively.  */
-  length = 0;
-  for (insn = loop->start_label;
-       insn && insn != loop->loop_end;
-       insn = NEXT_INSN (insn))
+  if (loop->incoming_src)
     {
-      if (JUMP_P (insn) && any_condjump_p (insn) && !optimize_size)
+      /* Make sure the predecessor is before the loop start label, as required by
+        the LSETUP instruction.  */
+      length = 0;
+      insn = BB_END (loop->incoming_src);
+      /* If we have to insert the LSETUP before a jump, count that jump in the
+        length.  */
+      if (VEC_length (edge, loop->incoming) > 1
+         || !(VEC_last (edge, loop->incoming)->flags & EDGE_FALLTHRU))
        {
-         if (TARGET_CSYNC_ANOMALY)
-           length += 8;
-         else if (TARGET_SPECLD_ANOMALY)
-           length += 6;
+         gcc_assert (JUMP_P (insn));
+         insn = PREV_INSN (insn);
        }
-      else if (LABEL_P (insn))
+
+      for (; insn && insn != loop->start_label; insn = NEXT_INSN (insn))
+       length += length_for_loop (insn);
+
+      if (!insn)
        {
-         if (TARGET_CSYNC_ANOMALY)
-           length += 4;
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d lsetup not before loop_start\n",
+                    loop->loop_no);
+         goto bad_loop;
        }
 
-      if (INSN_P (insn))
-       length += get_attr_length (insn);
+      /* Account for the pop of a scratch register where necessary.  */
+      if (!PREG_P (iter_reg) && scratchreg == NULL_RTX
+         && ENABLE_WA_LOAD_LCREGS)
+       length += 2;
+
+      if (length > MAX_LSETUP_DISTANCE)
+       {
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d lsetup too far away\n", loop->loop_no);
+         goto bad_loop;
+       }
     }
 
+  /* Check if start_label appears before loop_end and calculate the
+     offset between them.  We calculate the length of instructions
+     conservatively.  */
+  length = 0;
+  for (insn = loop->start_label;
+       insn && insn != loop->loop_end;
+       insn = NEXT_INSN (insn))
+    length += length_for_loop (insn);
+
   if (!insn)
     {
       if (dump_file)
@@ -3224,19 +4067,20 @@ bfin_optimize_loop (loop_info loop)
      - Returns (RTS, RTN, etc.)  */
 
   bb = loop->tail;
-  last_insn = PREV_INSN (loop->loop_end);
+  last_insn = find_prev_insn_start (loop->loop_end);
 
   while (1)
     {
-      for (; last_insn != PREV_INSN (BB_HEAD (bb));
-          last_insn = PREV_INSN (last_insn))
+      for (; last_insn != BB_HEAD (bb);
+          last_insn = find_prev_insn_start (last_insn))
        if (INSN_P (last_insn))
          break;
 
-      if (last_insn != PREV_INSN (BB_HEAD (bb)))
+      if (last_insn != BB_HEAD (bb))
        break;
 
       if (single_pred_p (bb)
+         && single_pred_edge (bb)->flags & EDGE_FALLTHRU
          && single_pred (bb) != ENTRY_BLOCK_PTR)
        {
          bb = single_pred (bb);
@@ -3258,42 +4102,34 @@ bfin_optimize_loop (loop_info loop)
       goto bad_loop;
     }
 
-  if (JUMP_P (last_insn))
+  if (JUMP_P (last_insn) && !any_condjump_p (last_insn))
     {
-      loop_info inner = bb->aux;
-      if (inner
-         && inner->outer == loop
-         && inner->loop_end == last_insn
-         && inner->depth == 1)
-       /* This jump_insn is the exact loop_end of an inner loop
-          and to be optimized away. So use the inner's last_insn.  */
-       last_insn = inner->last_insn;
-      else
+      if (dump_file)
+       fprintf (dump_file, ";; loop %d has bad last instruction\n",
+                loop->loop_no);
+      goto bad_loop;
+    }
+  /* In all other cases, try to replace a bad last insn with a nop.  */
+  else if (JUMP_P (last_insn)
+          || CALL_P (last_insn)
+          || get_attr_type (last_insn) == TYPE_SYNC
+          || get_attr_type (last_insn) == TYPE_CALL
+          || get_attr_seq_insns (last_insn) == SEQ_INSNS_MULTI
+          || recog_memoized (last_insn) == CODE_FOR_return_internal
+          || GET_CODE (PATTERN (last_insn)) == ASM_INPUT
+          || asm_noperands (PATTERN (last_insn)) >= 0)
+    {
+      if (loop->length + 2 > MAX_LOOP_LENGTH)
        {
          if (dump_file)
-           fprintf (dump_file, ";; loop %d has bad last instruction\n",
-                    loop->loop_no);
+           fprintf (dump_file, ";; loop %d too long\n", loop->loop_no);
          goto bad_loop;
        }
-    }
-  else if (CALL_P (last_insn)
-          || (GET_CODE (PATTERN (last_insn)) != SEQUENCE
-              && get_attr_type (last_insn) == TYPE_SYNC)
-          || recog_memoized (last_insn) == CODE_FOR_return_internal)
-    {
       if (dump_file)
-       fprintf (dump_file, ";; loop %d has bad last instruction\n",
+       fprintf (dump_file, ";; loop %d has bad last insn; replace with nop\n",
                 loop->loop_no);
-      goto bad_loop;
-    }
 
-  if (GET_CODE (PATTERN (last_insn)) == ASM_INPUT
-      || asm_noperands (PATTERN (last_insn)) >= 0
-      || (GET_CODE (PATTERN (last_insn)) != SEQUENCE
-         && get_attr_seq_insns (last_insn) == SEQ_INSNS_MULTI))
-    {
-      nop_insn = emit_insn_after (gen_nop (), last_insn);
-      last_insn = nop_insn;
+      last_insn = emit_insn_after (gen_forced_nop (), last_insn);
     }
 
   loop->last_insn = last_insn;
@@ -3318,58 +4154,149 @@ bfin_optimize_loop (loop_info loop)
       loop->clobber_loop0 = 1;
     }
 
-  /* If iter_reg is a DREG, we need generate an instruction to load
-     the loop count into LC register. */
-  if (D_REGNO_P (REGNO (iter_reg)))
+  loop->end_label = end_label;
+
+  /* Create a sequence containing the loop setup.  */
+  start_sequence ();
+
+  /* LSETUP only accepts P registers.  If we have one, we can use it,
+     otherwise there are several ways of working around the problem.
+     If we're not affected by anomaly 312, we can load the LC register
+     from any iteration register, and use LSETUP without initialization.
+     If we've found a P scratch register that's not live here, we can
+     instead copy the iter_reg into that and use an initializing LSETUP.
+     If all else fails, push and pop P0 and use it as a scratch.  */
+  if (P_REGNO_P (REGNO (iter_reg)))
+    {
+      loop_init = gen_lsetup_with_autoinit (lt_reg, start_label,
+                                           lb_reg, end_label,
+                                           lc_reg, iter_reg);
+      seq_end = emit_insn (loop_init);
+    }
+  else if (!ENABLE_WA_LOAD_LCREGS && DPREG_P (iter_reg))
     {
-      init_insn = gen_movsi (lc_reg, iter_reg);
+      emit_insn (gen_movsi (lc_reg, iter_reg));
       loop_init = gen_lsetup_without_autoinit (lt_reg, start_label,
                                               lb_reg, end_label,
                                               lc_reg);
+      seq_end = emit_insn (loop_init);
     }
-  else if (P_REGNO_P (REGNO (iter_reg)))
+  else if (scratchreg != NULL_RTX)
     {
-      init_insn = NULL_RTX;
+      emit_insn (gen_movsi (scratchreg, scratch_init));
       loop_init = gen_lsetup_with_autoinit (lt_reg, start_label,
                                            lb_reg, end_label,
-                                           lc_reg, iter_reg);
+                                           lc_reg, scratchreg);
+      seq_end = emit_insn (loop_init);
+      if (scratch_init_insn != NULL_RTX)
+       delete_insn (scratch_init_insn);
     }
   else
-    gcc_unreachable ();
-
-  loop->init = init_insn;
-  loop->end_label = end_label;
-  loop->loop_init = loop_init;
+    {
+      rtx p0reg = gen_rtx_REG (SImode, REG_P0);
+      rtx push = gen_frame_mem (SImode,
+                               gen_rtx_PRE_DEC (SImode, stack_pointer_rtx));
+      rtx pop = gen_frame_mem (SImode,
+                              gen_rtx_POST_INC (SImode, stack_pointer_rtx));
+      emit_insn (gen_movsi (push, p0reg));
+      emit_insn (gen_movsi (p0reg, scratch_init));
+      loop_init = gen_lsetup_with_autoinit (lt_reg, start_label,
+                                           lb_reg, end_label,
+                                           lc_reg, p0reg);
+      emit_insn (loop_init);
+      seq_end = emit_insn (gen_movsi (p0reg, pop));
+      if (scratch_init_insn != NULL_RTX)
+       delete_insn (scratch_init_insn);
+    }
 
   if (dump_file)
     {
       fprintf (dump_file, ";; replacing loop %d initializer with\n",
               loop->loop_no);
-      print_rtl_single (dump_file, loop->loop_init);
+      print_rtl_single (dump_file, loop_init);
       fprintf (dump_file, ";; replacing loop %d terminator with\n",
               loop->loop_no);
       print_rtl_single (dump_file, loop->loop_end);
     }
 
-  start_sequence ();
-
-  if (loop->init != NULL_RTX)
-    emit_insn (loop->init);
-  emit_insn(loop->loop_init);
-  emit_label (loop->start_label);
+  /* If the loop isn't entered at the top, also create a jump to the entry
+     point.  */
+  if (!loop->incoming_src && loop->head != loop->incoming_dest)
+    {
+      rtx label = BB_HEAD (loop->incoming_dest);
+      /* If we're jumping to the final basic block in the loop, and there's
+        only one cheap instruction before the end (typically an increment of
+        an induction variable), we can just emit a copy here instead of a
+        jump.  */
+      if (loop->incoming_dest == loop->tail
+         && next_real_insn (label) == last_insn
+         && asm_noperands (last_insn) < 0
+         && GET_CODE (PATTERN (last_insn)) == SET)
+       {
+         seq_end = emit_insn (copy_rtx (PATTERN (last_insn)));
+       }
+      else
+       {
+         emit_jump_insn (gen_jump (label));
+         seq_end = emit_barrier ();
+       }
+    }
 
   seq = get_insns ();
   end_sequence ();
 
-  emit_insn_after (seq, BB_END (loop->predecessor));
-  delete_insn (loop->loop_end);
+  if (loop->incoming_src)
+    {
+      rtx prev = BB_END (loop->incoming_src);
+      if (VEC_length (edge, loop->incoming) > 1
+         || !(VEC_last (edge, loop->incoming)->flags & EDGE_FALLTHRU))
+       {
+         gcc_assert (JUMP_P (prev));
+         prev = PREV_INSN (prev);
+       }
+      emit_insn_after (seq, prev);
+    }
+  else
+    {
+      basic_block new_bb;
+      edge e;
+      edge_iterator ei;
+
+#ifdef ENABLE_CHECKING
+      if (loop->head != loop->incoming_dest)
+       {
+         /* We aren't entering the loop at the top.  Since we've established
+            that the loop is entered only at one point, this means there
+            can't be fallthru edges into the head.  Any such fallthru edges
+            would become invalid when we insert the new block, so verify
+            that this does not in fact happen.  */
+         FOR_EACH_EDGE (e, ei, loop->head->preds)
+           gcc_assert (!(e->flags & EDGE_FALLTHRU));
+       }
+#endif
+
+      emit_insn_before (seq, BB_HEAD (loop->head));
+      seq = emit_label_before (gen_label_rtx (), seq);
+
+      new_bb = create_basic_block (seq, seq_end, loop->head->prev_bb);
+      FOR_EACH_EDGE (e, ei, loop->incoming)
+       {
+         if (!(e->flags & EDGE_FALLTHRU)
+             || e->dest != loop->head)
+           redirect_edge_and_branch_force (e, new_bb);
+         else
+           redirect_edge_succ (e, new_bb);
+       }
+      e = make_edge (new_bb, loop->head, 0);
+    }
 
+  delete_insn (loop->loop_end);
   /* Insert the loop end label before the last instruction of the loop.  */
   emit_label_before (loop->end_label, loop->last_insn);
 
   return;
 
-bad_loop:
+ bad_loop:
 
   if (dump_file)
     fprintf (dump_file, ";; loop %d is bad\n", loop->loop_no);
@@ -3380,17 +4307,17 @@ bad_loop:
     {
       /* If loop->iter_reg is a DREG or PREG, we can split it here
         without scratch register.  */
-      rtx insn;
+      rtx insn, test;
 
       emit_insn_before (gen_addsi3 (loop->iter_reg,
                                    loop->iter_reg,
                                    constm1_rtx),
                        loop->loop_end);
 
-      emit_insn_before (gen_cmpsi (loop->iter_reg, const0_rtx),
-                       loop->loop_end);
-
-      insn = emit_jump_insn_before (gen_bne (loop->start_label),
+      test = gen_rtx_NE (VOIDmode, loop->iter_reg, const0_rtx);
+      insn = emit_jump_insn_before (gen_cbranchsi4 (test,
+                                                   loop->iter_reg, const0_rtx,
+                                                   loop->start_label),
                                    loop->loop_end);
 
       JUMP_LABEL (insn) = loop->start_label;
@@ -3414,7 +4341,6 @@ bfin_discover_loop (loop_info loop, basic_block tail_bb, rtx tail_insn)
   loop->tail = tail_bb;
   loop->head = BRANCH_EDGE (tail_bb)->dest;
   loop->successor = FALLTHRU_EDGE (tail_bb)->dest;
-  loop->predecessor = NULL;
   loop->loop_end = tail_insn;
   loop->last_insn = NULL_RTX;
   loop->iter_reg = SET_DEST (XVECEXP (PATTERN (tail_insn), 0, 1));
@@ -3423,8 +4349,7 @@ bfin_discover_loop (loop_info loop, basic_block tail_bb, rtx tail_insn)
   loop->clobber_loop0 = loop->clobber_loop1 = 0;
   loop->outer = NULL;
   loop->loops = NULL;
-
-  loop->init = loop->loop_init = NULL_RTX;
+  loop->incoming = VEC_alloc (edge, gc, 2);
   loop->start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (tail_insn), 0, 0)), 1), 0);
   loop->end_label = NULL_RTX;
   loop->bad = 0;
@@ -3459,7 +4384,7 @@ bfin_discover_loop (loop_info loop, basic_block tail_bb, rtx tail_insn)
          FOR_EACH_EDGE (e, ei, bb->succs)
            {
              basic_block succ = EDGE_SUCC (bb, ei.index)->dest;
-             if (!REGNO_REG_SET_P (succ->il.rtl->global_live_at_start,
+             if (!REGNO_REG_SET_P (df_get_live_in (succ),
                                    REGNO (loop->iter_reg)))
                continue;
              if (!VEC_space (basic_block, works, 1))
@@ -3477,69 +4402,119 @@ bfin_discover_loop (loop_info loop, basic_block tail_bb, rtx tail_insn)
        }
     }
 
+  /* Find the predecessor, and make sure nothing else jumps into this loop.  */
   if (!loop->bad)
     {
-      /* Make sure we only have one entry point.  */
-      if (EDGE_COUNT (loop->head->preds) == 2)
+      int pass, retry;
+      for (dwork = 0; VEC_iterate (basic_block, loop->blocks, dwork, bb); dwork++)
        {
-         loop->predecessor = EDGE_PRED (loop->head, 0)->src;
-         if (loop->predecessor == loop->tail)
-           /* We wanted the other predecessor.  */
-           loop->predecessor = EDGE_PRED (loop->head, 1)->src;
-
-         /* We can only place a loop insn on a fall through edge of a
-            single exit block.  */
-         if (EDGE_COUNT (loop->predecessor->succs) != 1
-             || !(EDGE_SUCC (loop->predecessor, 0)->flags & EDGE_FALLTHRU)
-             /* If loop->predecessor is in loop, loop->head is not really
-                the head of the loop.  */
-             || bfin_bb_in_loop (loop, loop->predecessor))
-           loop->predecessor = NULL;
-       }
+         edge e;
+         edge_iterator ei;
+         FOR_EACH_EDGE (e, ei, bb->preds)
+           {
+             basic_block pred = e->src;
 
-      if (loop->predecessor == NULL)
+             if (!bfin_bb_in_loop (loop, pred))
+               {
+                 if (dump_file)
+                   fprintf (dump_file, ";; Loop %d: incoming edge %d -> %d\n",
+                            loop->loop_no, pred->index,
+                            e->dest->index);
+                 VEC_safe_push (edge, gc, loop->incoming, e);
+               }
+           }
+       }
+
+      for (pass = 0, retry = 1; retry && pass < 2; pass++)
        {
-         if (dump_file)
-           fprintf (dump_file, ";; loop has bad predecessor\n");
-         loop->bad = 1;
+         edge e;
+         edge_iterator ei;
+         bool first = true;
+         retry = 0;
+
+         FOR_EACH_EDGE (e, ei, loop->incoming)
+           {
+             if (first)
+               {
+                 loop->incoming_src = e->src;
+                 loop->incoming_dest = e->dest;
+                 first = false;
+               }
+             else
+               {
+                 if (e->dest != loop->incoming_dest)
+                   loop->incoming_dest = NULL;
+                 if (e->src != loop->incoming_src)
+                   loop->incoming_src = NULL;
+               }
+             if (loop->incoming_src == NULL && loop->incoming_dest == NULL)
+               {
+                 if (pass == 0)
+                   {
+                     if (dump_file)
+                       fprintf (dump_file,
+                                ";; retrying loop %d with forwarder blocks\n",
+                                loop->loop_no);
+                     retry = 1;
+                     break;
+                   }
+                 loop->bad = 1;
+                 if (dump_file)
+                   fprintf (dump_file,
+                            ";; can't find suitable entry for loop %d\n",
+                            loop->loop_no);
+                 goto out;
+               }
+           }
+         if (retry)
+           {
+             retry = 0;
+             FOR_EACH_EDGE (e, ei, loop->incoming)
+               {
+                 if (forwarder_block_p (e->src))
+                   {
+                     edge e2;
+                     edge_iterator ei2;
+
+                     if (dump_file)
+                       fprintf (dump_file,
+                                ";; Adding forwarder block %d to loop %d and retrying\n",
+                                e->src->index, loop->loop_no);
+                     VEC_safe_push (basic_block, heap, loop->blocks, e->src);
+                     bitmap_set_bit (loop->block_bitmap, e->src->index);
+                     FOR_EACH_EDGE (e2, ei2, e->src->preds)
+                       VEC_safe_push (edge, gc, loop->incoming, e2);
+                     VEC_unordered_remove (edge, loop->incoming, ei.index);
+                     retry = 1;
+                     break;
+                   }
+               }
+             if (!retry)
+               {
+                 if (dump_file)
+                   fprintf (dump_file, ";; No forwarder blocks found\n");
+                 loop->bad = 1;
+               }
+           }
        }
     }
 
-#ifdef ENABLE_CHECKING
-  /* Make sure nothing jumps into this loop.  This shouldn't happen as we
-     wouldn't have generated the counted loop patterns in such a case.
-     However, this test must be done after the test above to detect loops
-     with invalid headers.  */
-  if (!loop->bad)
-    for (dwork = 0; VEC_iterate (basic_block, loop->blocks, dwork, bb); dwork++)
-      {
-       edge e;
-       edge_iterator ei;
-       if (bb == loop->head)
-         continue;
-       FOR_EACH_EDGE (e, ei, bb->preds)
-         {
-           basic_block pred = EDGE_PRED (bb, ei.index)->src;
-           if (!bfin_bb_in_loop (loop, pred))
-             abort ();
-         }
-      }
-#endif
+ out:
   VEC_free (basic_block, heap, works);
 }
 
-static void
-bfin_reorg_loops (FILE *dump_file)
+/* Analyze the structure of the loops in the current function.  Use STACK
+   for bitmap allocations.  Returns all the valid candidates for hardware
+   loops found in this function.  */
+static loop_info
+bfin_discover_loops (bitmap_obstack *stack, FILE *dump_file)
 {
-  bitmap_obstack stack;
-  bitmap tmp_bitmap;
-  basic_block bb;
   loop_info loops = NULL;
   loop_info loop;
+  basic_block bb;
+  bitmap tmp_bitmap;
   int nloops = 0;
 
-  bitmap_obstack_initialize (&stack);
-
   /* Find all the possible loop tails.  This means searching for every
      loop_end instruction.  For each one found, create a loop_info
      structure and add the head block to the work list. */
@@ -3554,14 +4529,29 @@ bfin_reorg_loops (FILE *dump_file)
 
       if (INSN_P (tail) && recog_memoized (tail) == CODE_FOR_loop_end)
        {
+         rtx insn;
          /* A possible loop end */
 
+         /* There's a degenerate case we can handle - an empty loop consisting
+            of only a back branch.  Handle that by deleting the branch.  */
+         insn = BB_HEAD (BRANCH_EDGE (bb)->dest);
+         if (next_real_insn (insn) == tail)
+           {
+             if (dump_file)
+               {
+                 fprintf (dump_file, ";; degenerate loop ending at\n");
+                 print_rtl_single (dump_file, tail);
+               }
+             delete_insn_and_edges (tail);
+             continue;
+           }
+
          loop = XNEW (struct loop_info);
          loop->next = loops;
          loops = loop;
          loop->loop_no = nloops++;
          loop->blocks = VEC_alloc (basic_block, heap, 20);
-         loop->block_bitmap = BITMAP_ALLOC (&stack);
+         loop->block_bitmap = BITMAP_ALLOC (stack);
          bb->aux = loop;
 
          if (dump_file)
@@ -3575,7 +4565,7 @@ bfin_reorg_loops (FILE *dump_file)
        }
     }
 
-  tmp_bitmap = BITMAP_ALLOC (&stack);
+  tmp_bitmap = BITMAP_ALLOC (stack);
   /* Compute loop nestings.  */
   for (loop = loops; loop; loop = loop->next)
     {
@@ -3603,18 +4593,137 @@ bfin_reorg_loops (FILE *dump_file)
            }
          else
            {
+             if (dump_file)
+               fprintf (dump_file,
+                        ";; can't find suitable nesting for loops %d and %d\n",
+                        loop->loop_no, other->loop_no);
              loop->bad = other->bad = 1;
            }
        }
     }
   BITMAP_FREE (tmp_bitmap);
 
+  return loops;
+}
+
+/* Free up the loop structures in LOOPS.  */
+static void
+free_loops (loop_info loops)
+{
+  while (loops)
+    {
+      loop_info loop = loops;
+      loops = loop->next;
+      VEC_free (loop_info, heap, loop->loops);
+      VEC_free (basic_block, heap, loop->blocks);
+      BITMAP_FREE (loop->block_bitmap);
+      XDELETE (loop);
+    }
+}
+
+#define BB_AUX_INDEX(BB) ((unsigned)(BB)->aux)
+
+/* The taken-branch edge from the loop end can actually go forward.  Since the
+   Blackfin's LSETUP instruction requires that the loop end be after the loop
+   start, try to reorder a loop's basic blocks when we find such a case.  */
+static void
+bfin_reorder_loops (loop_info loops, FILE *dump_file)
+{
+  basic_block bb;
+  loop_info loop;
+
+  FOR_EACH_BB (bb)
+    bb->aux = NULL;
+  cfg_layout_initialize (0);
+
+  for (loop = loops; loop; loop = loop->next)
+    {
+      unsigned index;
+      basic_block bb;
+      edge e;
+      edge_iterator ei;
+
+      if (loop->bad)
+       continue;
+
+      /* Recreate an index for basic blocks that represents their order.  */
+      for (bb = ENTRY_BLOCK_PTR->next_bb, index = 0;
+          bb != EXIT_BLOCK_PTR;
+          bb = bb->next_bb, index++)
+       bb->aux = (PTR) index;
+
+      if (BB_AUX_INDEX (loop->head) < BB_AUX_INDEX (loop->tail))
+       continue;
+
+      FOR_EACH_EDGE (e, ei, loop->head->succs)
+       {
+         if (bitmap_bit_p (loop->block_bitmap, e->dest->index)
+             && BB_AUX_INDEX (e->dest) < BB_AUX_INDEX (loop->tail))
+           {
+             basic_block start_bb = e->dest;
+             basic_block start_prev_bb = start_bb->prev_bb;
+
+             if (dump_file)
+               fprintf (dump_file, ";; Moving block %d before block %d\n",
+                        loop->head->index, start_bb->index);
+             loop->head->prev_bb->next_bb = loop->head->next_bb;
+             loop->head->next_bb->prev_bb = loop->head->prev_bb;
+
+             loop->head->prev_bb = start_prev_bb;
+             loop->head->next_bb = start_bb;
+             start_prev_bb->next_bb = start_bb->prev_bb = loop->head;
+             break;
+           }
+       }
+      loops = loops->next;
+    }
+  
+  FOR_EACH_BB (bb)
+    {
+      if (bb->next_bb != EXIT_BLOCK_PTR)
+       bb->aux = bb->next_bb;
+      else
+       bb->aux = NULL;
+    }
+  cfg_layout_finalize ();
+  df_analyze ();
+}
+
+/* Run from machine_dependent_reorg, this pass looks for doloop_end insns
+   and tries to rewrite the RTL of these loops so that proper Blackfin
+   hardware loops are generated.  */
+
+static void
+bfin_reorg_loops (FILE *dump_file)
+{
+  loop_info loops = NULL;
+  loop_info loop;
+  basic_block bb;
+  bitmap_obstack stack;
+
+  bitmap_obstack_initialize (&stack);
+
+  if (dump_file)
+    fprintf (dump_file, ";; Find loops, first pass\n\n");
+
+  loops = bfin_discover_loops (&stack, dump_file);
+
+  if (dump_file)
+    bfin_dump_loops (loops);
+
+  bfin_reorder_loops (loops, dump_file);
+  free_loops (loops);
+
+  if (dump_file)
+    fprintf (dump_file, ";; Find loops, second pass\n\n");
+
+  loops = bfin_discover_loops (&stack, dump_file);
   if (dump_file)
     {
       fprintf (dump_file, ";; All loops found:\n\n");
       bfin_dump_loops (loops);
     }
-  
+
   /* Now apply the optimizations.  */
   for (loop = loops; loop; loop = loop->next)
     bfin_optimize_loop (loop);
@@ -3625,22 +4734,24 @@ bfin_reorg_loops (FILE *dump_file)
       bfin_dump_loops (loops);
     }
 
-  /* Free up the loop structures */
-  while (loops)
-    {
-      loop = loops;
-      loops = loop->next;
-      VEC_free (loop_info, heap, loop->loops);
-      VEC_free (basic_block, heap, loop->blocks);
-      BITMAP_FREE (loop->block_bitmap);
-      XDELETE (loop);
-    }
+  free_loops (loops);
 
   if (dump_file)
     print_rtl (dump_file, get_insns ());
 
   FOR_EACH_BB (bb)
     bb->aux = NULL;
+
+  splitting_loops = 1;
+  FOR_EACH_BB (bb)
+    {
+      rtx insn = BB_END (bb);
+      if (!JUMP_P (insn))
+       continue;
+
+      try_split (PATTERN (insn), insn, 1);
+    }
+  splitting_loops = 0;
 }
 \f
 /* Possibly generate a SEQUENCE out of three insns found in SLOT.
@@ -3648,10 +4759,13 @@ bfin_reorg_loops (FILE *dump_file)
 static bool
 gen_one_bundle (rtx slot[3])
 {
-  rtx bundle;
-
   gcc_assert (slot[1] != NULL_RTX);
 
+  /* Don't add extra NOPs if optimizing for size.  */
+  if (optimize_size
+      && (slot[0] == NULL_RTX || slot[2] == NULL_RTX))
+    return false;
+
   /* Verify that we really can do the multi-issue.  */
   if (slot[0])
     {
@@ -3659,7 +4773,7 @@ gen_one_bundle (rtx slot[3])
       while (t != slot[1])
        {
          if (GET_CODE (t) != NOTE
-             || NOTE_LINE_NUMBER (t) != NOTE_INSN_DELETED)
+             || NOTE_KIND (t) != NOTE_INSN_DELETED)
            return false;
          t = NEXT_INSN (t);
        }
@@ -3670,16 +4784,22 @@ gen_one_bundle (rtx slot[3])
       while (t != slot[2])
        {
          if (GET_CODE (t) != NOTE
-             || NOTE_LINE_NUMBER (t) != NOTE_INSN_DELETED)
+             || NOTE_KIND (t) != NOTE_INSN_DELETED)
            return false;
          t = NEXT_INSN (t);
        }
     }
 
   if (slot[0] == NULL_RTX)
-    slot[0] = emit_insn_before (gen_mnop (), slot[1]);
+    {
+      slot[0] = emit_insn_before (gen_mnop (), slot[1]);
+      df_insn_rescan (slot[0]);
+    }
   if (slot[2] == NULL_RTX)
-    slot[2] = emit_insn_after (gen_nop (), slot[1]);
+    {
+      slot[2] = emit_insn_after (gen_forced_nop (), slot[1]);
+      df_insn_rescan (slot[2]);
+    }
 
   /* Avoid line number information being printed inside one bundle.  */
   if (INSN_LOCATOR (slot[1])
@@ -3692,17 +4812,8 @@ gen_one_bundle (rtx slot[3])
   /* Terminate them with "|| " instead of ";" in the output.  */
   PUT_MODE (slot[0], SImode);
   PUT_MODE (slot[1], SImode);
-
-  /* This is a cheat to avoid emit_insn's special handling of SEQUENCEs.
-     Generating a PARALLEL first and changing its code later is the
-     easiest way to emit a SEQUENCE insn.  */
-  bundle = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (3, slot[0], slot[1], slot[2]));
-  emit_insn_before (bundle, slot[0]);
-  remove_insn (slot[0]);
-  remove_insn (slot[1]);
-  remove_insn (slot[2]);
-  PUT_CODE (bundle, SEQUENCE);
-  
+  /* Terminate the bundle, for the benefit of reorder_var_tracking_notes.  */
+  PUT_MODE (slot[2], QImode);
   return true;
 }
 
@@ -3724,15 +4835,27 @@ bfin_gen_bundles (void)
       for (insn = BB_HEAD (bb);; insn = next)
        {
          int at_end;
+         rtx delete_this = NULL_RTX;
+
          if (INSN_P (insn))
            {
-             if (get_attr_type (insn) == TYPE_DSP32)
-               slot[0] = insn;
-             else if (slot[1] == NULL_RTX)
-               slot[1] = insn;
+             enum attr_type type = get_attr_type (insn);
+
+             if (type == TYPE_STALL)
+               {
+                 gcc_assert (n_filled == 0);
+                 delete_this = insn;
+               }
              else
-               slot[2] = insn;
-             n_filled++;
+               {
+                 if (type == TYPE_DSP32 || type == TYPE_DSP32SHIFTIMM)
+                   slot[0] = insn;
+                 else if (slot[1] == NULL_RTX)
+                   slot[1] = insn;
+                 else
+                   slot[2] = insn;
+                 n_filled++;
+               }
            }
 
          next = NEXT_INSN (insn);
@@ -3747,7 +4870,7 @@ bfin_gen_bundles (void)
 
          /* BB_END can change due to emitting extra NOPs, so check here.  */
          at_end = insn == BB_END (bb);
-         if (at_end || GET_MODE (next) == TImode)
+         if (delete_this == NULL_RTX && (at_end || GET_MODE (next) == TImode))
            {
              if ((n_filled < 2
                   || !gen_one_bundle (slot))
@@ -3760,17 +4883,151 @@ bfin_gen_bundles (void)
                    {
                      SET_SRC (pat) = XVECEXP (SET_SRC (pat), 0, 0);
                      INSN_CODE (slot[0]) = -1;
+                     df_insn_rescan (slot[0]);
                    }
                }
              n_filled = 0;
              slot[0] = slot[1] = slot[2] = NULL_RTX;
            }
+         if (delete_this != NULL_RTX)
+           delete_insn (delete_this);
          if (at_end)
            break;
        }
     }
 }
+
+/* Ensure that no var tracking notes are emitted in the middle of a
+   three-instruction bundle.  */
+
+static void
+reorder_var_tracking_notes (void)
+{
+  basic_block bb;
+  FOR_EACH_BB (bb)
+    {
+      rtx insn, next;
+      rtx queue = NULL_RTX;
+      bool in_bundle = false;
+
+      for (insn = BB_HEAD (bb); insn != BB_END (bb); insn = next)
+       {
+         next = NEXT_INSN (insn);
+
+         if (INSN_P (insn))
+           {
+             /* Emit queued up notes at the last instruction of a bundle.  */
+             if (GET_MODE (insn) == QImode)
+               {
+                 while (queue)
+                   {
+                     rtx next_queue = PREV_INSN (queue);
+                     PREV_INSN (NEXT_INSN (insn)) = queue;
+                     NEXT_INSN (queue) = NEXT_INSN (insn);
+                     NEXT_INSN (insn) = queue;
+                     PREV_INSN (queue) = insn;
+                     queue = next_queue;
+                   }
+                 in_bundle = false;
+               }
+             else if (GET_MODE (insn) == SImode)
+               in_bundle = true;
+           }
+         else if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_VAR_LOCATION)
+           {
+             if (in_bundle)
+               {
+                 rtx prev = PREV_INSN (insn);
+                 PREV_INSN (next) = prev;
+                 NEXT_INSN (prev) = next;
+
+                 PREV_INSN (insn) = queue;
+                 queue = insn;
+               }
+           }
+       }
+    }
+}
 \f
+/* On some silicon revisions, functions shorter than a certain number of cycles
+   can cause unpredictable behaviour.  Work around this by adding NOPs as
+   needed.  */
+static void
+workaround_rts_anomaly (void)
+{
+  rtx insn, first_insn = NULL_RTX;
+  int cycles = 4;
+
+  if (! ENABLE_WA_RETS)
+    return;
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      rtx pat;
+
+      if (BARRIER_P (insn))
+       return;
+      
+      if (NOTE_P (insn) || LABEL_P (insn))
+       continue;
+
+      if (first_insn == NULL_RTX)
+       first_insn = insn;
+      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 (CALL_P (insn))
+       return;
+
+      if (JUMP_P (insn))
+       {
+         if (recog_memoized (insn) == CODE_FOR_return_internal)
+           break;
+
+         /* Nothing to worry about for direct jumps.  */
+         if (!any_condjump_p (insn))
+           return;
+         if (cycles <= 1)
+           return;
+         cycles--;
+       }
+      else if (INSN_P (insn))
+       {
+         rtx pat = PATTERN (insn);
+         int this_cycles = 1;
+
+         if (GET_CODE (pat) == PARALLEL)
+           {
+             if (push_multiple_operation (pat, VOIDmode)
+                 || pop_multiple_operation (pat, VOIDmode))
+               this_cycles = n_regs_to_save;
+           }
+         else
+           {
+             enum insn_code icode = recog_memoized (insn);
+             if (icode == CODE_FOR_link)
+               this_cycles = 4;
+             else if (icode == CODE_FOR_unlink)
+               this_cycles = 3;
+             else if (icode == CODE_FOR_mulsi3)
+               this_cycles = 5;
+           }
+         if (this_cycles >= cycles)
+           return;
+
+         cycles -= this_cycles;
+       }
+    }
+  while (cycles > 0)
+    {
+      emit_insn_before (gen_nop (), first_insn);
+      cycles--;
+    }
+}
+
 /* Return an insn type for INSN that can be used by the caller for anomaly
    workarounds.  This differs from plain get_attr_type in that it handles
    SEQUENCEs.  */
@@ -3794,156 +5051,254 @@ type_for_anomaly (rtx insn)
     return get_attr_type (insn);
 }
 
-/* Return nonzero if INSN contains any loads that may trap.  It handles
-   SEQUENCEs correctly.  */
-
+/* Return true iff the address found in MEM is based on the register
+   NP_REG and optionally has a positive offset.  */
 static bool
-trapping_loads_p (rtx insn)
+harmless_null_pointer_p (rtx mem, int np_reg)
 {
-  rtx pat = PATTERN (insn);
-  if (GET_CODE (pat) == SEQUENCE)
+  mem = XEXP (mem, 0);
+  if (GET_CODE (mem) == POST_INC || GET_CODE (mem) == POST_DEC)
+    mem = XEXP (mem, 0);
+  if (REG_P (mem) && REGNO (mem) == np_reg)
+    return true;
+  if (GET_CODE (mem) == PLUS
+      && REG_P (XEXP (mem, 0)) && REGNO (XEXP (mem, 0)) == np_reg)
     {
-      enum attr_type t;
-      t = get_attr_type (XVECEXP (pat, 0, 1));
-      if (t == TYPE_MCLD && may_trap_p (SET_SRC (XVECEXP (pat, 0, 1))))
-       return true;
-      t = get_attr_type (XVECEXP (pat, 0, 2));
-      if (t == TYPE_MCLD && may_trap_p (SET_SRC (XVECEXP (pat, 0, 2))))
+      mem = XEXP (mem, 1);
+      if (GET_CODE (mem) == CONST_INT && INTVAL (mem) > 0)
        return true;
-      return false;
     }
-  else
-    return may_trap_p (SET_SRC (single_set (insn)));
+  return false;
 }
 
-/* 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 behavior.  The manual recommends CSYNC, while
-   VDSP seems to use NOPs (even though its corresponding compiler option is
-   named CSYNC).
+/* Return nonzero if INSN contains any loads that may trap.  */
 
-   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)
+static bool
+trapping_loads_p (rtx insn, int np_reg, bool after_np_branch)
 {
-  rtx insn, last_condjump = NULL_RTX;
-  int cycles_since_jump = INT_MAX;
-
-  /* We are freeing block_for_insn in the toplev to keep compatibility
-     with old MDEP_REORGS that are not CFG based.  Recompute it now.  */
-  compute_bb_for_insn ();
-
-  if (bfin_flag_schedule_insns2)
-    {
-      splitting_for_sched = 1;
-      split_all_insns (0);
-      splitting_for_sched = 0;
+  rtx pat = PATTERN (insn);
+  rtx mem = SET_SRC (single_set (insn));
 
-      update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES, PROP_DEATH_NOTES);
+  if (!after_np_branch)
+    np_reg = -1;
+  return ((np_reg == -1 || !harmless_null_pointer_p (mem, np_reg))
+         && may_trap_p (mem));
+}
 
-      timevar_push (TV_SCHED2);
-      schedule_insns ();
-      timevar_pop (TV_SCHED2);
+/* Return INSN if it is of TYPE_MCLD.  Alternatively, if INSN is the start of
+   a three-insn bundle, see if one of them is a load and return that if so.
+   Return NULL_RTX if the insn does not contain loads.  */
+static rtx
+find_load (rtx insn)
+{
+  if (get_attr_type (insn) == TYPE_MCLD)
+    return insn;
+  if (GET_MODE (insn) != SImode)
+    return NULL_RTX;
+  do {
+    insn = NEXT_INSN (insn);
+    if ((GET_MODE (insn) == SImode || GET_MODE (insn) == QImode)
+       && get_attr_type (insn) == TYPE_MCLD)
+      return insn;
+  } while (GET_MODE (insn) != QImode);
+  return NULL_RTX;
+}
 
-      /* Examine the schedule and insert nops as necessary for 64 bit parallel
-        instructions.  */
-      bfin_gen_bundles ();
-    }
+/* Determine whether PAT is an indirect call pattern.  */
+static bool
+indirect_call_p (rtx pat)
+{
+  if (GET_CODE (pat) == PARALLEL)
+    pat = XVECEXP (pat, 0, 0);
+  if (GET_CODE (pat) == SET)
+    pat = SET_SRC (pat);
+  gcc_assert (GET_CODE (pat) == CALL);
+  pat = XEXP (pat, 0);
+  gcc_assert (GET_CODE (pat) == MEM);
+  pat = XEXP (pat, 0);
+  
+  return REG_P (pat);
+}
 
-  /* Doloop optimization */
-  if (cfun->machine->has_hardware_loops)
-    bfin_reorg_loops (dump_file);
+/* During workaround_speculation, track whether we're in the shadow of a
+   conditional branch that tests a P register for NULL.  If so, we can omit
+   emitting NOPs if we see a load from that P register, since a speculative
+   access at address 0 isn't a problem, and the load is executed in all other
+   cases anyway.
+   Global for communication with note_np_check_stores through note_stores.
+   */
+int np_check_regno = -1;
+bool np_after_branch = false;
+
+/* Subroutine of workaround_speculation, called through note_stores.  */
+static void
+note_np_check_stores (rtx x, const_rtx pat, void *data ATTRIBUTE_UNUSED)
+{
+  if (REG_P (x) && (REGNO (x) == REG_CC || REGNO (x) == np_check_regno))
+    np_check_regno = -1;
+}
 
-  if (! TARGET_SPECLD_ANOMALY && ! TARGET_CSYNC_ANOMALY)
+static void
+workaround_speculation (void)
+{
+  rtx insn, next;
+  rtx last_condjump = NULL_RTX;
+  int cycles_since_jump = INT_MAX;
+  int delay_added = 0;
+
+  if (! ENABLE_WA_SPECULATIVE_LOADS && ! ENABLE_WA_SPECULATIVE_SYNCS
+      && ! ENABLE_WA_INDIRECT_CALLS)
     return;
 
   /* First pass: find predicted-false branches; if something after them
      needs nops, insert them or change the branch to predict true.  */
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  for (insn = get_insns (); insn; insn = next)
     {
       rtx pat;
+      int delay_needed = 0;
 
-      if (NOTE_P (insn) || BARRIER_P (insn) || LABEL_P (insn))
+      next = find_next_insn_start (insn);
+      
+      if (NOTE_P (insn) || BARRIER_P (insn))
        continue;
 
+      if (LABEL_P (insn))
+       {
+         np_check_regno = -1;
+         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)
+         || GET_CODE (pat) == ADDR_VEC || GET_CODE (pat) == ADDR_DIFF_VEC)
        continue;
+      
+      if (GET_CODE (pat) == ASM_INPUT || asm_noperands (pat) >= 0)
+       {
+         np_check_regno = -1;
+         continue;
+       }
 
       if (JUMP_P (insn))
        {
+         /* Is this a condjump based on a null pointer comparison we saw
+            earlier?  */
+         if (np_check_regno != -1
+             && recog_memoized (insn) == CODE_FOR_cbranchbi4)
+           {
+             rtx op = XEXP (SET_SRC (PATTERN (insn)), 0);
+             gcc_assert (GET_CODE (op) == EQ || GET_CODE (op) == NE);
+             if (GET_CODE (op) == NE)
+               np_after_branch = true;
+           }
          if (any_condjump_p (insn)
              && ! cbranch_predicted_taken_p (insn))
            {
              last_condjump = insn;
+             delay_added = 0;
              cycles_since_jump = 0;
            }
          else
            cycles_since_jump = INT_MAX;
        }
+      else if (CALL_P (insn))
+       {
+         np_check_regno = -1;
+         if (cycles_since_jump < INT_MAX)
+           cycles_since_jump++;
+         if (indirect_call_p (pat) && ENABLE_WA_INDIRECT_CALLS)
+           {
+             delay_needed = 3;
+           }
+       }
       else if (INSN_P (insn))
        {
+         rtx load_insn = find_load (insn);
          enum attr_type type = type_for_anomaly (insn);
-         int delay_needed = 0;
+
          if (cycles_since_jump < INT_MAX)
            cycles_since_jump++;
 
-         if (type == TYPE_MCLD && TARGET_SPECLD_ANOMALY)
+         /* Detect a comparison of a P register with zero.  If we later
+            see a condjump based on it, we have found a null pointer
+            check.  */
+         if (recog_memoized (insn) == CODE_FOR_compare_eq)
            {
-             if (trapping_loads_p (insn))
-               delay_needed = 3;
+             rtx src = SET_SRC (PATTERN (insn));
+             if (REG_P (XEXP (src, 0))
+                 && P_REGNO_P (REGNO (XEXP (src, 0)))
+                 && XEXP (src, 1) == const0_rtx)
+               {
+                 np_check_regno = REGNO (XEXP (src, 0));
+                 np_after_branch = false;
+               }
+             else
+               np_check_regno = -1;
            }
-         else if (type == TYPE_SYNC && TARGET_CSYNC_ANOMALY)
-           delay_needed = 4;
 
-         if (delay_needed > cycles_since_jump)
+         if (load_insn && ENABLE_WA_SPECULATIVE_LOADS)
            {
-             rtx pat;
-             int num_clobbers;
-             rtx *op = recog_data.operand;
-
-             delay_needed -= cycles_since_jump;
+             if (trapping_loads_p (load_insn, np_check_regno,
+                                   np_after_branch))
+               delay_needed = 4;
+           }
+         else if (type == TYPE_SYNC && ENABLE_WA_SPECULATIVE_SYNCS)
+           delay_needed = 3;
 
-             extract_insn (last_condjump);
-             if (optimize_size)
+         /* See if we need to forget about a null pointer comparison
+            we found earlier.  */
+         if (recog_memoized (insn) != CODE_FOR_compare_eq)
+           {
+             note_stores (PATTERN (insn), note_np_check_stores, NULL);
+             if (np_check_regno != -1)
                {
-                 pat = gen_cbranch_predicted_taken (op[0], op[1], op[2],
-                                                    op[3]);
-                 cycles_since_jump = INT_MAX;
+                 if (find_regno_note (insn, REG_INC, np_check_regno))
+                   np_check_regno = -1;
                }
-             else
-               /* Do not adjust cycles_since_jump in this case, so that
-                  we'll increase the number of NOPs for a subsequent insn
-                  if necessary.  */
-               pat = gen_cbranch_with_nops (op[0], op[1], op[2], op[3],
-                                            GEN_INT (delay_needed));
-             PATTERN (last_condjump) = pat;
-             INSN_CODE (last_condjump) = recog (pat, insn, &num_clobbers);
            }
+
+       }
+
+      if (delay_needed > cycles_since_jump
+         && (delay_needed - cycles_since_jump) > delay_added)
+       {
+         rtx pat1;
+         int num_clobbers;
+         rtx *op = recog_data.operand;
+
+         delay_needed -= cycles_since_jump;
+
+         extract_insn (last_condjump);
+         if (optimize_size)
+           {
+             pat1 = gen_cbranch_predicted_taken (op[0], op[1], op[2],
+                                                op[3]);
+             cycles_since_jump = INT_MAX;
+           }
+         else
+           {
+             /* Do not adjust cycles_since_jump in this case, so that
+                we'll increase the number of NOPs for a subsequent insn
+                if necessary.  */
+             pat1 = gen_cbranch_with_nops (op[0], op[1], op[2], op[3],
+                                           GEN_INT (delay_needed));
+             delay_added = delay_needed;
+           }
+         PATTERN (last_condjump) = pat1;
+         INSN_CODE (last_condjump) = recog (pat1, insn, &num_clobbers);
+       }
+      if (CALL_P (insn))
+       {
+         cycles_since_jump = INT_MAX;
+         delay_added = 0;
        }
     }
+
   /* Second pass: for predicted-true branches, see if anything at the
      branch destination needs extra nops.  */
-  if (! TARGET_CSYNC_ANOMALY)
-    return;
-
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
+      int cycles_since_jump;
       if (JUMP_P (insn)
          && any_condjump_p (insn)
          && (INSN_CODE (insn) == CODE_FOR_cbranch_predicted_taken
@@ -3951,11 +5306,15 @@ bfin_reorg (void)
        {
          rtx target = JUMP_LABEL (insn);
          rtx label = target;
+         rtx next_tgt;
+
          cycles_since_jump = 0;
-         for (; target && cycles_since_jump < 3; target = NEXT_INSN (target))
+         for (; target && cycles_since_jump < 3; target = next_tgt)
            {
              rtx pat;
 
+             next_tgt = find_next_insn_start (target);
+
              if (NOTE_P (target) || BARRIER_P (target) || LABEL_P (target))
                continue;
 
@@ -3967,12 +5326,18 @@ bfin_reorg (void)
 
              if (INSN_P (target))
                {
+                 rtx load_insn = find_load (target);
                  enum attr_type type = type_for_anomaly (target);
                  int delay_needed = 0;
                  if (cycles_since_jump < INT_MAX)
                    cycles_since_jump++;
 
-                 if (type == TYPE_SYNC && TARGET_CSYNC_ANOMALY)
+                 if (load_insn && ENABLE_WA_SPECULATIVE_LOADS)
+                   {
+                     if (trapping_loads_p (load_insn, -1, false))
+                       delay_needed = 2;
+                   }
+                 else if (type == TYPE_SYNC && ENABLE_WA_SPECULATIVE_SYNCS)
                    delay_needed = 2;
 
                  if (delay_needed > cycles_since_jump)
@@ -4005,13 +5370,130 @@ bfin_reorg (void)
            }
        }
     }
+}
+
+/* Called just before the final scheduling pass.  If we need to insert NOPs
+   later on to work around speculative loads, insert special placeholder
+   insns that cause loads to be delayed for as many cycles as necessary
+   (and possible).  This reduces the number of NOPs we need to add.
+   The dummy insns we generate are later removed by bfin_gen_bundles.  */
+static void
+add_sched_insns_for_speculation (void)
+{
+  rtx insn;
+
+  if (! ENABLE_WA_SPECULATIVE_LOADS && ! ENABLE_WA_SPECULATIVE_SYNCS
+      && ! ENABLE_WA_INDIRECT_CALLS)
+    return;
+
+  /* First pass: find predicted-false branches; if something after them
+     needs nops, insert them or change the branch to predict true.  */
+  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))
+           {
+             rtx n = next_real_insn (insn);
+             emit_insn_before (gen_stall (GEN_INT (3)), n);
+           }
+       }
+    }
+
+  /* Second pass: for predicted-true branches, see if anything at the
+     branch destination needs extra nops.  */
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (JUMP_P (insn)
+         && any_condjump_p (insn)
+         && (cbranch_predicted_taken_p (insn)))
+       {
+         rtx target = JUMP_LABEL (insn);
+         rtx next = next_real_insn (target);
+
+         if (GET_CODE (PATTERN (next)) == UNSPEC_VOLATILE
+             && get_attr_type (next) == TYPE_STALL)
+           continue;
+         emit_insn_before (gen_stall (GEN_INT (1)), next);       
+       }
+    }
+}
+
+/* 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 behavior.  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)
+{
+  /* We are freeing block_for_insn in the toplev to keep compatibility
+     with old MDEP_REORGS that are not CFG based.  Recompute it now.  */
+  compute_bb_for_insn ();
+
+  if (bfin_flag_schedule_insns2)
+    {
+      splitting_for_sched = 1;
+      split_all_insns ();
+      splitting_for_sched = 0;
+
+      add_sched_insns_for_speculation ();
+
+      timevar_push (TV_SCHED2);
+      schedule_insns ();
+      timevar_pop (TV_SCHED2);
+
+      /* Examine the schedule and insert nops as necessary for 64-bit parallel
+        instructions.  */
+      bfin_gen_bundles ();
+    }
+
+  df_analyze ();
+
+  /* Doloop optimization */
+  if (cfun->machine->has_hardware_loops)
+    bfin_reorg_loops (dump_file);
+
+  workaround_speculation ();
 
   if (bfin_flag_var_tracking)
     {
       timevar_push (TV_VAR_TRACKING);
       variable_tracking_main ();
+      reorder_var_tracking_notes ();
       timevar_pop (TV_VAR_TRACKING);
     }
+
+  df_finish_pass (false);
+
+  workaround_rts_anomaly ();
 }
 \f
 /* Handle interrupt_handler, exception_handler and nmi_handler function
@@ -4029,8 +5511,8 @@ handle_int_attribute (tree *node, tree name,
 
   if (TREE_CODE (x) != FUNCTION_TYPE)
     {
-      warning (OPT_Wattributes, "%qs attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
       *no_add_attrs = true;
     }
   else if (funkind (x) != SUBROUTINE)
@@ -4044,7 +5526,7 @@ handle_int_attribute (tree *node, tree name,
    warning to be generated).  */
 
 static int
-bfin_comp_type_attributes (tree type1, tree type2)
+bfin_comp_type_attributes (const_tree type1, const_tree type2)
 {
   e_funkind kind1, kind2;
 
@@ -4090,8 +5572,8 @@ bfin_handle_longcall_attribute (tree *node, tree name,
       && TREE_CODE (*node) != FIELD_DECL
       && TREE_CODE (*node) != TYPE_DECL)
     {
-      warning (OPT_Wattributes, "`%s' attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
       *no_add_attrs = true;
     }
 
@@ -4108,8 +5590,132 @@ bfin_handle_longcall_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "l1_text" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+bfin_handle_l1_text_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+                              int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    {
+      error ("%qE attribute only applies to functions",
+            name);
+      *no_add_attrs = true;
+    }
+
+  /* The decl may have already been given a section attribute
+     from a previous declaration. Ensure they match.  */
+  else if (DECL_SECTION_NAME (decl) != NULL_TREE
+          && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+                     ".l1.text") != 0)
+    {
+      error ("section of %q+D conflicts with previous declaration",
+            decl);
+      *no_add_attrs = true;
+    }
+  else
+    DECL_SECTION_NAME (decl) = build_string (9, ".l1.text");
+
+  return NULL_TREE;
+}
+
+/* Handle a "l1_data", "l1_data_A" or "l1_data_B" attribute;
+   arguments as in struct attribute_spec.handler.  */
+
+static tree
+bfin_handle_l1_data_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+                              int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != VAR_DECL)
+    {
+      error ("%qE attribute only applies to variables",
+            name);
+      *no_add_attrs = true;
+    }
+  else if (current_function_decl != NULL_TREE
+          && !TREE_STATIC (decl))
+    {
+      error ("%qE attribute cannot be specified for local variables",
+            name);
+      *no_add_attrs = true;
+    }
+  else
+    {
+      const char *section_name;
+
+      if (strcmp (IDENTIFIER_POINTER (name), "l1_data") == 0)
+       section_name = ".l1.data";
+      else if (strcmp (IDENTIFIER_POINTER (name), "l1_data_A") == 0)
+       section_name = ".l1.data.A";
+      else if (strcmp (IDENTIFIER_POINTER (name), "l1_data_B") == 0)
+       section_name = ".l1.data.B";
+      else
+       gcc_unreachable ();
+
+      /* The decl may have already been given a section attribute
+        from a previous declaration. Ensure they match.  */
+      if (DECL_SECTION_NAME (decl) != NULL_TREE
+         && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+                    section_name) != 0)
+       {
+         error ("section of %q+D conflicts with previous declaration",
+                decl);
+         *no_add_attrs = true;
+       }
+      else
+       DECL_SECTION_NAME (decl)
+         = build_string (strlen (section_name) + 1, section_name);
+    }
+
+ return NULL_TREE;
+}
+
+/* Handle a "l2" attribute; arguments as in struct attribute_spec.handler.  */
+
+static tree
+bfin_handle_l2_attribute (tree *node, tree ARG_UNUSED (name),
+                         tree ARG_UNUSED (args), int ARG_UNUSED (flags),
+                         bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    {
+      if (DECL_SECTION_NAME (decl) != NULL_TREE
+         && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+                    ".l2.text") != 0)
+       {
+         error ("section of %q+D conflicts with previous declaration",
+                decl);
+         *no_add_attrs = true;
+       }
+      else
+       DECL_SECTION_NAME (decl) = build_string (9, ".l2.text");
+    }
+  else if (TREE_CODE (decl) == VAR_DECL)
+    {
+      if (DECL_SECTION_NAME (decl) != NULL_TREE
+         && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+                    ".l2.data") != 0)
+       {
+         error ("section of %q+D conflicts with previous declaration",
+                decl);
+         *no_add_attrs = true;
+       }
+      else
+       DECL_SECTION_NAME (decl) = build_string (9, ".l2.data");
+    }
+
+  return NULL_TREE;
+}
+
 /* Table of valid machine attributes.  */
-const struct attribute_spec bfin_attribute_table[] =
+static 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 },
@@ -4120,6 +5726,11 @@ const struct attribute_spec bfin_attribute_table[] =
   { "saveall", 0, 0, false, true,  true, NULL },
   { "longcall",  0, 0, false, true,  true,  bfin_handle_longcall_attribute },
   { "shortcall", 0, 0, false, true,  true,  bfin_handle_longcall_attribute },
+  { "l1_text", 0, 0, true, false, false,  bfin_handle_l1_text_attribute },
+  { "l1_data", 0, 0, true, false, false,  bfin_handle_l1_data_attribute },
+  { "l1_data_A", 0, 0, true, false, false, bfin_handle_l1_data_attribute },
+  { "l1_data_B", 0, 0, true, false, false,  bfin_handle_l1_data_attribute },
+  { "l2", 0, 0, true, false, false,  bfin_handle_l2_attribute },
   { NULL, 0, 0, false, false, false, NULL }
 };
 \f
@@ -4164,12 +5775,12 @@ bfin_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
 {
   rtx xops[3];
   /* The this parameter is passed as the first argument.  */
-  rtx this = gen_rtx_REG (Pmode, REG_R0);
+  rtx this_rtx = gen_rtx_REG (Pmode, REG_R0);
 
   /* Adjust the this parameter by a fixed constant.  */
   if (delta)
     {
-      xops[1] = this;
+      xops[1] = this_rtx;
       if (delta >= -64 && delta <= 63)
        {
          xops[0] = GEN_INT (delta);
@@ -4196,7 +5807,7 @@ bfin_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
   if (vcall_offset)
     {
       rtx p2tmp = gen_rtx_REG (Pmode, REG_P2);
-      rtx tmp = gen_rtx_REG (Pmode, REG_R2);
+      rtx tmp = gen_rtx_REG (Pmode, REG_R3);
 
       xops[1] = tmp;
       xops[2] = p2tmp;
@@ -4212,7 +5823,7 @@ bfin_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
          output_asm_insn ("%h1 = %h0; %d1 = %d0; %2 = %2 + %1", xops);
          xops[0] = gen_rtx_MEM (Pmode, p2tmp);
        }
-      xops[2] = this;
+      xops[2] = this_rtx;
       output_asm_insn ("%1 = %0; %2 = %2 + %1;", xops);
     }
 
@@ -4226,6 +5837,7 @@ enum bfin_builtins
 {
   BFIN_BUILTIN_CSYNC,
   BFIN_BUILTIN_SSYNC,
+  BFIN_BUILTIN_ONES,
   BFIN_BUILTIN_COMPOSE_2X16,
   BFIN_BUILTIN_EXTRACTLO,
   BFIN_BUILTIN_EXTRACTHI,
@@ -4251,16 +5863,21 @@ enum bfin_builtins
   BFIN_BUILTIN_MIN_1X16,
   BFIN_BUILTIN_MAX_1X16,
 
+  BFIN_BUILTIN_SUM_2X16,
   BFIN_BUILTIN_DIFFHL_2X16,
   BFIN_BUILTIN_DIFFLH_2X16,
 
   BFIN_BUILTIN_SSADD_1X32,
   BFIN_BUILTIN_SSSUB_1X32,
   BFIN_BUILTIN_NORM_1X32,
+  BFIN_BUILTIN_ROUND_1X32,
   BFIN_BUILTIN_NEG_1X32,
+  BFIN_BUILTIN_ABS_1X32,
   BFIN_BUILTIN_MIN_1X32,
   BFIN_BUILTIN_MAX_1X32,
   BFIN_BUILTIN_MULT_1X32,
+  BFIN_BUILTIN_MULT_1X32X32,
+  BFIN_BUILTIN_MULT_1X32X32NS,
 
   BFIN_BUILTIN_MULHISILL,
   BFIN_BUILTIN_MULHISILH,
@@ -4271,11 +5888,20 @@ enum bfin_builtins
   BFIN_BUILTIN_LSHIFT_2X16,
   BFIN_BUILTIN_SSASHIFT_1X16,
   BFIN_BUILTIN_SSASHIFT_2X16,
+  BFIN_BUILTIN_SSASHIFT_1X32,
 
   BFIN_BUILTIN_CPLX_MUL_16,
   BFIN_BUILTIN_CPLX_MAC_16,
   BFIN_BUILTIN_CPLX_MSU_16,
 
+  BFIN_BUILTIN_CPLX_MUL_16_S40,
+  BFIN_BUILTIN_CPLX_MAC_16_S40,
+  BFIN_BUILTIN_CPLX_MSU_16_S40,
+
+  BFIN_BUILTIN_CPLX_SQU,
+
+  BFIN_BUILTIN_LOADBYTES,
+
   BFIN_BUILTIN_MAX
 };
 
@@ -4330,11 +5956,17 @@ bfin_init_builtins (void)
   tree short_ftype_v2hi
     = build_function_type_list (short_integer_type_node, V2HI_type_node,
                                NULL_TREE);
-
+  tree int_ftype_pint
+    = build_function_type_list (integer_type_node,
+                               build_pointer_type (integer_type_node),
+                               NULL_TREE);
+  
   /* Add the remaining MMX insns with somewhat more complicated types.  */
   def_builtin ("__builtin_bfin_csync", void_ftype_void, BFIN_BUILTIN_CSYNC);
   def_builtin ("__builtin_bfin_ssync", void_ftype_void, BFIN_BUILTIN_SSYNC);
 
+  def_builtin ("__builtin_bfin_ones", short_ftype_int, BFIN_BUILTIN_ONES);
+
   def_builtin ("__builtin_bfin_compose_2x16", v2hi_ftype_int_int,
               BFIN_BUILTIN_COMPOSE_2X16);
   def_builtin ("__builtin_bfin_extract_hi", short_ftype_v2hi,
@@ -4364,6 +5996,11 @@ bfin_init_builtins (void)
   def_builtin ("__builtin_bfin_abs_fr2x16", v2hi_ftype_v2hi,
               BFIN_BUILTIN_ABS_2X16);
 
+  def_builtin ("__builtin_bfin_min_fr1x16", short_ftype_int_int,
+              BFIN_BUILTIN_MIN_1X16);
+  def_builtin ("__builtin_bfin_max_fr1x16", short_ftype_int_int,
+              BFIN_BUILTIN_MAX_1X16);
+
   def_builtin ("__builtin_bfin_add_fr1x16", short_ftype_int_int,
               BFIN_BUILTIN_SSADD_1X16);
   def_builtin ("__builtin_bfin_sub_fr1x16", short_ftype_int_int,
@@ -4379,6 +6016,8 @@ bfin_init_builtins (void)
   def_builtin ("__builtin_bfin_norm_fr1x16", short_ftype_int,
               BFIN_BUILTIN_NORM_1X16);
 
+  def_builtin ("__builtin_bfin_sum_fr2x16", short_ftype_v2hi,
+              BFIN_BUILTIN_SUM_2X16);
   def_builtin ("__builtin_bfin_diff_hl_fr2x16", short_ftype_v2hi,
               BFIN_BUILTIN_DIFFHL_2X16);
   def_builtin ("__builtin_bfin_diff_lh_fr2x16", short_ftype_v2hi,
@@ -4393,16 +6032,29 @@ bfin_init_builtins (void)
   def_builtin ("__builtin_bfin_mulhisihh", int_ftype_v2hi_v2hi,
               BFIN_BUILTIN_MULHISIHH);
 
+  def_builtin ("__builtin_bfin_min_fr1x32", int_ftype_int_int,
+              BFIN_BUILTIN_MIN_1X32);
+  def_builtin ("__builtin_bfin_max_fr1x32", int_ftype_int_int,
+              BFIN_BUILTIN_MAX_1X32);
+
   def_builtin ("__builtin_bfin_add_fr1x32", int_ftype_int_int,
               BFIN_BUILTIN_SSADD_1X32);
   def_builtin ("__builtin_bfin_sub_fr1x32", int_ftype_int_int,
               BFIN_BUILTIN_SSSUB_1X32);
   def_builtin ("__builtin_bfin_negate_fr1x32", int_ftype_int,
               BFIN_BUILTIN_NEG_1X32);
+  def_builtin ("__builtin_bfin_abs_fr1x32", int_ftype_int,
+              BFIN_BUILTIN_ABS_1X32);
   def_builtin ("__builtin_bfin_norm_fr1x32", short_ftype_int,
               BFIN_BUILTIN_NORM_1X32);
+  def_builtin ("__builtin_bfin_round_fr1x32", short_ftype_int,
+              BFIN_BUILTIN_ROUND_1X32);
   def_builtin ("__builtin_bfin_mult_fr1x32", int_ftype_short_short,
               BFIN_BUILTIN_MULT_1X32);
+  def_builtin ("__builtin_bfin_mult_fr1x32x32", int_ftype_int_int,
+              BFIN_BUILTIN_MULT_1X32X32);
+  def_builtin ("__builtin_bfin_mult_fr1x32x32NS", int_ftype_int_int,
+              BFIN_BUILTIN_MULT_1X32X32NS);
 
   /* Shifts.  */
   def_builtin ("__builtin_bfin_shl_fr1x16", short_ftype_int_int,
@@ -4413,14 +6065,33 @@ bfin_init_builtins (void)
               BFIN_BUILTIN_LSHIFT_1X16);
   def_builtin ("__builtin_bfin_lshl_fr2x16", v2hi_ftype_v2hi_int,
               BFIN_BUILTIN_LSHIFT_2X16);
+  def_builtin ("__builtin_bfin_shl_fr1x32", int_ftype_int_int,
+              BFIN_BUILTIN_SSASHIFT_1X32);
 
   /* Complex numbers.  */
+  def_builtin ("__builtin_bfin_cmplx_add", v2hi_ftype_v2hi_v2hi,
+              BFIN_BUILTIN_SSADD_2X16);
+  def_builtin ("__builtin_bfin_cmplx_sub", v2hi_ftype_v2hi_v2hi,
+              BFIN_BUILTIN_SSSUB_2X16);
   def_builtin ("__builtin_bfin_cmplx_mul", v2hi_ftype_v2hi_v2hi,
               BFIN_BUILTIN_CPLX_MUL_16);
   def_builtin ("__builtin_bfin_cmplx_mac", v2hi_ftype_v2hi_v2hi_v2hi,
               BFIN_BUILTIN_CPLX_MAC_16);
   def_builtin ("__builtin_bfin_cmplx_msu", v2hi_ftype_v2hi_v2hi_v2hi,
               BFIN_BUILTIN_CPLX_MSU_16);
+  def_builtin ("__builtin_bfin_cmplx_mul_s40", v2hi_ftype_v2hi_v2hi,
+              BFIN_BUILTIN_CPLX_MUL_16_S40);
+  def_builtin ("__builtin_bfin_cmplx_mac_s40", v2hi_ftype_v2hi_v2hi_v2hi,
+              BFIN_BUILTIN_CPLX_MAC_16_S40);
+  def_builtin ("__builtin_bfin_cmplx_msu_s40", v2hi_ftype_v2hi_v2hi_v2hi,
+              BFIN_BUILTIN_CPLX_MSU_16_S40);
+  def_builtin ("__builtin_bfin_csqu_fr16", v2hi_ftype_v2hi,
+              BFIN_BUILTIN_CPLX_SQU);
+
+  /* "Unaligned" load.  */
+  def_builtin ("__builtin_bfin_loadbytes", int_ftype_pint,
+              BFIN_BUILTIN_LOADBYTES);
+
 }
 
 
@@ -4440,6 +6111,7 @@ static const struct builtin_description bdesc_2arg[] =
   { CODE_FOR_ssashifthi3, "__builtin_bfin_shl_fr1x16", BFIN_BUILTIN_SSASHIFT_1X16, -1 },
   { CODE_FOR_lshiftv2hi3, "__builtin_bfin_lshl_fr2x16", BFIN_BUILTIN_LSHIFT_2X16, -1 },
   { CODE_FOR_lshifthi3, "__builtin_bfin_lshl_fr1x16", BFIN_BUILTIN_LSHIFT_1X16, -1 },
+  { CODE_FOR_ssashiftsi3, "__builtin_bfin_shl_fr1x32", BFIN_BUILTIN_SSASHIFT_1X32, -1 },
 
   { CODE_FOR_sminhi3, "__builtin_bfin_min_fr1x16", BFIN_BUILTIN_MIN_1X16, -1 },
   { CODE_FOR_smaxhi3, "__builtin_bfin_max_fr1x16", BFIN_BUILTIN_MAX_1X16, -1 },
@@ -4462,22 +6134,34 @@ static const struct builtin_description bdesc_2arg[] =
   { CODE_FOR_flag_mulhi, "__builtin_bfin_mult_fr1x16", BFIN_BUILTIN_MULT_1X16, MACFLAG_T },
   { CODE_FOR_flag_mulhi, "__builtin_bfin_multr_fr1x16", BFIN_BUILTIN_MULTR_1X16, MACFLAG_NONE },
   { CODE_FOR_flag_mulv2hi, "__builtin_bfin_mult_fr2x16", BFIN_BUILTIN_MULT_2X16, MACFLAG_T },
-  { CODE_FOR_flag_mulv2hi, "__builtin_bfin_multr_fr2x16", BFIN_BUILTIN_MULTR_2X16, MACFLAG_NONE }
+  { CODE_FOR_flag_mulv2hi, "__builtin_bfin_multr_fr2x16", BFIN_BUILTIN_MULTR_2X16, MACFLAG_NONE },
+
+  { CODE_FOR_mulhisi_ll, "__builtin_bfin_mulhisill", BFIN_BUILTIN_MULHISILL, -1 },
+  { CODE_FOR_mulhisi_lh, "__builtin_bfin_mulhisilh", BFIN_BUILTIN_MULHISILH, -1 },
+  { CODE_FOR_mulhisi_hl, "__builtin_bfin_mulhisihl", BFIN_BUILTIN_MULHISIHL, -1 },
+  { CODE_FOR_mulhisi_hh, "__builtin_bfin_mulhisihh", BFIN_BUILTIN_MULHISIHH, -1 }
+
 };
 
 static const struct builtin_description bdesc_1arg[] =
 {
+  { CODE_FOR_loadbytes, "__builtin_bfin_loadbytes", BFIN_BUILTIN_LOADBYTES, 0 },
+
+  { CODE_FOR_ones, "__builtin_bfin_ones", BFIN_BUILTIN_ONES, 0 },
+
   { CODE_FOR_signbitshi2, "__builtin_bfin_norm_fr1x16", BFIN_BUILTIN_NORM_1X16, 0 },
   { CODE_FOR_ssneghi2, "__builtin_bfin_negate_fr1x16", BFIN_BUILTIN_NEG_1X16, 0 },
   { CODE_FOR_abshi2, "__builtin_bfin_abs_fr1x16", BFIN_BUILTIN_ABS_1X16, 0 },
 
   { CODE_FOR_signbitssi2, "__builtin_bfin_norm_fr1x32", BFIN_BUILTIN_NORM_1X32, 0 },
+  { CODE_FOR_ssroundsi2, "__builtin_bfin_round_fr1x32", BFIN_BUILTIN_ROUND_1X32, 0 },
   { CODE_FOR_ssnegsi2, "__builtin_bfin_negate_fr1x32", BFIN_BUILTIN_NEG_1X32, 0 },
+  { CODE_FOR_ssabssi2, "__builtin_bfin_abs_fr1x32", BFIN_BUILTIN_ABS_1X32, 0 },
 
   { CODE_FOR_movv2hi_hi_low, "__builtin_bfin_extract_lo", BFIN_BUILTIN_EXTRACTLO, 0 },
   { CODE_FOR_movv2hi_hi_high, "__builtin_bfin_extract_hi", BFIN_BUILTIN_EXTRACTHI, 0 },
   { CODE_FOR_ssnegv2hi2, "__builtin_bfin_negate_fr2x16", BFIN_BUILTIN_NEG_2X16, 0 },
-  { CODE_FOR_absv2hi2, "__builtin_bfin_abs_fr2x16", BFIN_BUILTIN_ABS_2X16, 0 }
+  { CODE_FOR_ssabsv2hi2, "__builtin_bfin_abs_fr2x16", BFIN_BUILTIN_ABS_2X16, 0 }
 };
 
 /* Errors in the source file can cause expand_expr to return const0_rtx
@@ -4498,12 +6182,12 @@ safe_vector_operand (rtx x, enum machine_mode mode)
    if this is a normal binary op, or one of the MACFLAG_xxx constants.  */
 
 static rtx
-bfin_expand_binop_builtin (enum insn_code icode, tree arglist, rtx target,
+bfin_expand_binop_builtin (enum insn_code icode, tree exp, rtx target,
                           int macflag)
 {
   rtx pat;
-  tree arg0 = TREE_VALUE (arglist);
-  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
   rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
   rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
   enum machine_mode op0mode = GET_MODE (op0);
@@ -4556,11 +6240,11 @@ bfin_expand_binop_builtin (enum insn_code icode, tree arglist, rtx target,
 /* Subroutine of bfin_expand_builtin to take care of unop insns.  */
 
 static rtx
-bfin_expand_unop_builtin (enum insn_code icode, tree arglist,
+bfin_expand_unop_builtin (enum insn_code icode, tree exp,
                          rtx target)
 {
   rtx pat;
-  tree arg0 = TREE_VALUE (arglist);
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
   rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
   enum machine_mode op0mode = GET_MODE (op0);
   enum machine_mode tmode = insn_data[icode].operand[0].mode;
@@ -4606,11 +6290,10 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
   size_t i;
   enum insn_code icode;
   const struct builtin_description *d;
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
-  tree arglist = TREE_OPERAND (exp, 1);
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
   unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
   tree arg0, arg1, arg2;
-  rtx op0, op1, op2, accvec, pat, tmp1, tmp2;
+  rtx op0, op1, op2, accvec, pat, tmp1, tmp2, a0reg, a1reg;
   enum machine_mode tmode, mode0;
 
   switch (fcode)
@@ -4624,10 +6307,12 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
 
     case BFIN_BUILTIN_DIFFHL_2X16:
     case BFIN_BUILTIN_DIFFLH_2X16:
-      arg0 = TREE_VALUE (arglist);
+    case BFIN_BUILTIN_SUM_2X16:
+      arg0 = CALL_EXPR_ARG (exp, 0);
       op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
-      icode = (fcode == BFIN_BUILTIN_DIFFHL_2X16
-              ? CODE_FOR_subhilov2hi3 : CODE_FOR_sublohiv2hi3);
+      icode = (fcode == BFIN_BUILTIN_DIFFHL_2X16 ? CODE_FOR_subhilov2hi3
+              : fcode == BFIN_BUILTIN_DIFFLH_2X16 ? CODE_FOR_sublohiv2hi3
+              : CODE_FOR_ssaddhilov2hi3);
       tmode = insn_data[icode].operand[0].mode;
       mode0 = insn_data[icode].operand[1].mode;
 
@@ -4648,12 +6333,73 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
       emit_insn (pat);
       return target;
 
+    case BFIN_BUILTIN_MULT_1X32X32:
+    case BFIN_BUILTIN_MULT_1X32X32NS:
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+      op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
+      if (! target
+         || !register_operand (target, SImode))
+       target = gen_reg_rtx (SImode);
+      if (! register_operand (op0, SImode))
+       op0 = copy_to_mode_reg (SImode, op0);
+      if (! register_operand (op1, SImode))
+       op1 = copy_to_mode_reg (SImode, op1);
+
+      a1reg = gen_rtx_REG (PDImode, REG_A1);
+      a0reg = gen_rtx_REG (PDImode, REG_A0);
+      tmp1 = gen_lowpart (V2HImode, op0);
+      tmp2 = gen_lowpart (V2HImode, op1);
+      emit_insn (gen_flag_macinit1hi (a1reg,
+                                     gen_lowpart (HImode, op0),
+                                     gen_lowpart (HImode, op1),
+                                     GEN_INT (MACFLAG_FU)));
+      emit_insn (gen_lshrpdi3 (a1reg, a1reg, GEN_INT (16)));
+
+      if (fcode == BFIN_BUILTIN_MULT_1X32X32)
+       emit_insn (gen_flag_mul_macv2hi_parts_acconly (a0reg, a1reg, tmp1, tmp2,
+                                                      const1_rtx, const1_rtx,
+                                                      const1_rtx, const0_rtx, a1reg,
+                                                      const0_rtx, GEN_INT (MACFLAG_NONE),
+                                                      GEN_INT (MACFLAG_M)));
+      else
+       {
+         /* For saturating multiplication, there's exactly one special case
+            to be handled: multiplying the smallest negative value with
+            itself.  Due to shift correction in fractional multiplies, this
+            can overflow.  Iff this happens, OP2 will contain 1, which, when
+            added in 32 bits to the smallest negative, wraps to the largest
+            positive, which is the result we want.  */
+         op2 = gen_reg_rtx (V2HImode);
+         emit_insn (gen_packv2hi (op2, tmp1, tmp2, const0_rtx, const0_rtx));
+         emit_insn (gen_movsibi (gen_rtx_REG (BImode, REG_CC),
+                                 gen_lowpart (SImode, op2)));
+         emit_insn (gen_flag_mul_macv2hi_parts_acconly_andcc0 (a0reg, a1reg, tmp1, tmp2,
+                                                               const1_rtx, const1_rtx,
+                                                               const1_rtx, const0_rtx, a1reg,
+                                                               const0_rtx, GEN_INT (MACFLAG_NONE),
+                                                               GEN_INT (MACFLAG_M)));
+         op2 = gen_reg_rtx (SImode);
+         emit_insn (gen_movbisi (op2, gen_rtx_REG (BImode, REG_CC)));
+       }
+      emit_insn (gen_flag_machi_parts_acconly (a1reg, tmp2, tmp1,
+                                              const1_rtx, const0_rtx,
+                                              a1reg, const0_rtx, GEN_INT (MACFLAG_M)));
+      emit_insn (gen_ashrpdi3 (a1reg, a1reg, GEN_INT (15)));
+      emit_insn (gen_sum_of_accumulators (target, a0reg, a0reg, a1reg));
+      if (fcode == BFIN_BUILTIN_MULT_1X32X32NS)
+       emit_insn (gen_addsi3 (target, target, op2));
+      return target;
+
     case BFIN_BUILTIN_CPLX_MUL_16:
-      arg0 = TREE_VALUE (arglist);
-      arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+    case BFIN_BUILTIN_CPLX_MUL_16_S40:
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
       op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
       op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
       accvec = gen_reg_rtx (V2PDImode);
+      icode = CODE_FOR_flag_macv2hi_parts;
 
       if (! target
          || GET_MODE (target) != V2HImode
@@ -4664,9 +6410,14 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
       if (! register_operand (op1, GET_MODE (op1)))
        op1 = copy_to_mode_reg (GET_MODE (op1), op1);
 
-      emit_insn (gen_flag_macinit1v2hi_parts (accvec, op0, op1, const0_rtx,
-                                             const0_rtx, const0_rtx,
-                                             const1_rtx, GEN_INT (MACFLAG_NONE)));
+      if (fcode == BFIN_BUILTIN_CPLX_MUL_16)
+       emit_insn (gen_flag_macinit1v2hi_parts (accvec, op0, op1, const0_rtx,
+                                               const0_rtx, const0_rtx,
+                                               const1_rtx, GEN_INT (MACFLAG_W32)));
+      else
+       emit_insn (gen_flag_macinit1v2hi_parts (accvec, op0, op1, const0_rtx,
+                                               const0_rtx, const0_rtx,
+                                               const1_rtx, GEN_INT (MACFLAG_NONE)));
       emit_insn (gen_flag_macv2hi_parts (target, op0, op1, const1_rtx,
                                         const1_rtx, const1_rtx,
                                         const0_rtx, accvec, const1_rtx, const0_rtx,
@@ -4676,55 +6427,103 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
 
     case BFIN_BUILTIN_CPLX_MAC_16:
     case BFIN_BUILTIN_CPLX_MSU_16:
-      arg0 = TREE_VALUE (arglist);
-      arg1 = TREE_VALUE (TREE_CHAIN (arglist));
-      arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+    case BFIN_BUILTIN_CPLX_MAC_16_S40:
+    case BFIN_BUILTIN_CPLX_MSU_16_S40:
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      arg2 = CALL_EXPR_ARG (exp, 2);
       op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
       op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
       op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0);
       accvec = gen_reg_rtx (V2PDImode);
+      icode = CODE_FOR_flag_macv2hi_parts;
 
       if (! target
          || GET_MODE (target) != V2HImode
          || ! (*insn_data[icode].operand[0].predicate) (target, V2HImode))
        target = gen_reg_rtx (tmode);
-      if (! register_operand (op0, GET_MODE (op0)))
-       op0 = copy_to_mode_reg (GET_MODE (op0), op0);
       if (! register_operand (op1, GET_MODE (op1)))
        op1 = copy_to_mode_reg (GET_MODE (op1), op1);
+      if (! register_operand (op2, GET_MODE (op2)))
+       op2 = copy_to_mode_reg (GET_MODE (op2), op2);
 
       tmp1 = gen_reg_rtx (SImode);
       tmp2 = gen_reg_rtx (SImode);
-      emit_insn (gen_ashlsi3 (tmp1, gen_lowpart (SImode, op2), GEN_INT (16)));
-      emit_move_insn (tmp2, gen_lowpart (SImode, op2));
+      emit_insn (gen_ashlsi3 (tmp1, gen_lowpart (SImode, op0), GEN_INT (16)));
+      emit_move_insn (tmp2, gen_lowpart (SImode, op0));
       emit_insn (gen_movstricthi_1 (gen_lowpart (HImode, tmp2), const0_rtx));
       emit_insn (gen_load_accumulator_pair (accvec, tmp1, tmp2));
-      emit_insn (gen_flag_macv2hi_parts_acconly (accvec, op0, op1, const0_rtx,
-                                                const0_rtx, const0_rtx,
-                                                const1_rtx, accvec, const0_rtx,
-                                                const0_rtx,
-                                                GEN_INT (MACFLAG_W32)));
-      tmp1 = (fcode == BFIN_BUILTIN_CPLX_MAC_16 ? const1_rtx : const0_rtx);
-      tmp2 = (fcode == BFIN_BUILTIN_CPLX_MAC_16 ? const0_rtx : const1_rtx);
-      emit_insn (gen_flag_macv2hi_parts (target, op0, op1, const1_rtx,
+      if (fcode == BFIN_BUILTIN_CPLX_MAC_16
+         || fcode == BFIN_BUILTIN_CPLX_MSU_16)
+       emit_insn (gen_flag_macv2hi_parts_acconly (accvec, op1, op2, const0_rtx,
+                                                  const0_rtx, const0_rtx,
+                                                  const1_rtx, accvec, const0_rtx,
+                                                  const0_rtx,
+                                                  GEN_INT (MACFLAG_W32)));
+      else
+       emit_insn (gen_flag_macv2hi_parts_acconly (accvec, op1, op2, const0_rtx,
+                                                  const0_rtx, const0_rtx,
+                                                  const1_rtx, accvec, const0_rtx,
+                                                  const0_rtx,
+                                                  GEN_INT (MACFLAG_NONE)));
+      if (fcode == BFIN_BUILTIN_CPLX_MAC_16
+         || fcode == BFIN_BUILTIN_CPLX_MAC_16_S40)
+       {
+         tmp1 = const1_rtx;
+         tmp2 = const0_rtx;
+       }
+      else
+       {
+         tmp1 = const0_rtx;
+         tmp2 = const1_rtx;
+       }
+      emit_insn (gen_flag_macv2hi_parts (target, op1, op2, const1_rtx,
                                         const1_rtx, const1_rtx,
                                         const0_rtx, accvec, tmp1, tmp2,
                                         GEN_INT (MACFLAG_NONE), accvec));
 
       return target;
 
+    case BFIN_BUILTIN_CPLX_SQU:
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+      accvec = gen_reg_rtx (V2PDImode);
+      icode = CODE_FOR_flag_mulv2hi;
+      tmp1 = gen_reg_rtx (V2HImode);
+      tmp2 = gen_reg_rtx (V2HImode);
+
+      if (! target
+         || GET_MODE (target) != V2HImode
+         || ! (*insn_data[icode].operand[0].predicate) (target, V2HImode))
+       target = gen_reg_rtx (V2HImode);
+      if (! register_operand (op0, GET_MODE (op0)))
+       op0 = copy_to_mode_reg (GET_MODE (op0), op0);
+
+      emit_insn (gen_flag_mulv2hi (tmp1, op0, op0, GEN_INT (MACFLAG_NONE)));
+
+      emit_insn (gen_flag_mulhi_parts (gen_lowpart (HImode, tmp2), op0, op0,
+                                      const0_rtx, const1_rtx,
+                                      GEN_INT (MACFLAG_NONE)));
+
+      emit_insn (gen_ssaddhi3_high_parts (target, tmp2, tmp2, tmp2, const0_rtx,
+                                         const0_rtx));
+      emit_insn (gen_sssubhi3_low_parts (target, target, tmp1, tmp1,
+                                        const0_rtx, const1_rtx));
+
+      return target;
+
     default:
       break;
     }
 
   for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
     if (d->code == fcode)
-      return bfin_expand_binop_builtin (d->icode, arglist, target,
+      return bfin_expand_binop_builtin (d->icode, exp, target,
                                        d->macflag);
 
   for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
     if (d->code == fcode)
-      return bfin_expand_unop_builtin (d->icode, arglist, target);
+      return bfin_expand_unop_builtin (d->icode, exp, target);
 
   gcc_unreachable ();
 }
@@ -4753,9 +6552,6 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
 #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_ASM_INTEGER
 #define TARGET_ASM_INTEGER bfin_assemble_integer
 
@@ -4768,7 +6564,7 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
 #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
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
 #undef TARGET_SCHED_ADJUST_COST
 #define TARGET_SCHED_ADJUST_COST bfin_adjust_cost
@@ -4776,12 +6572,8 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
 #undef TARGET_SCHED_ISSUE_RATE
 #define TARGET_SCHED_ISSUE_RATE bfin_issue_rate
 
-#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_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
 
 #undef TARGET_ARG_PARTIAL_BYTES
 #define TARGET_ARG_PARTIAL_BYTES bfin_arg_partial_bytes
@@ -4813,4 +6605,16 @@ bfin_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
 #undef TARGET_CANNOT_FORCE_CONST_MEM
 #define TARGET_CANNOT_FORCE_CONST_MEM bfin_cannot_force_const_mem
 
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY bfin_return_in_memory
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P    bfin_legitimate_address_p
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED bfin_frame_pointer_required
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE bfin_can_eliminate
+
 struct gcc_target targetm = TARGET_INITIALIZER;