]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/config/cris/cris.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / config / cris / cris.c
index b4d433bece923ae3dbe02e72e0f1d0ca4fa81eb2..9bae9cdb44ed42e025c6073d0791ea996ac44bf1 100644 (file)
@@ -1,12 +1,13 @@
 /* Definitions for GCC.  Part of the machine description for CRIS.
-   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+   2008  Free Software Foundation, Inc.
    Contributed by Axis Communications.  Written by Hans-Peter Nilsson.
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+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,
@@ -15,12 +16,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
@@ -35,26 +37,21 @@ Boston, MA 02111-1307, USA.  */
 #include "function.h"
 #include "toplev.h"
 #include "recog.h"
+#include "reload.h"
 #include "tm_p.h"
 #include "debug.h"
 #include "output.h"
 #include "target.h"
 #include "target-def.h"
+#include "ggc.h"
+#include "optabs.h"
+#include "df.h"
 
 /* Usable when we have an amount to add or subtract, and want the
    optimal size of the insn.  */
 #define ADDITIVE_SIZE_MODIFIER(size) \
  ((size) <= 63 ? "q" : (size) <= 255 ? "u.b" : (size) <= 65535 ? "u.w" : ".d")
 
-#define ASSERT_PLT_UNSPEC(x)                                   \
-  do                                                           \
-    {                                                          \
-      if (XEXP (x, 1) != NULL_RTX                              \
-         || (GET_CODE (XVECEXP (x, 0, 0)) != SYMBOL_REF        \
-             && GET_CODE (XVECEXP (x, 0, 0)) != LABEL_REF))    \
-       abort ();                                               \
-    } while (0)
-
 #define LOSE_AND_RETURN(msgid, x)                      \
   do                                           \
     {                                          \
@@ -62,59 +59,68 @@ Boston, MA 02111-1307, USA.  */
       return;                                  \
     } while (0)
 
+enum cris_retinsn_type
+ { CRIS_RETINSN_UNKNOWN = 0, CRIS_RETINSN_RET, CRIS_RETINSN_JUMP };
+
 /* Per-function machine data.  */
-struct machine_function
+struct machine_function GTY(())
  {
    int needs_return_address_on_stack;
+
+   /* This is the number of registers we save in the prologue due to
+      stdarg.  */
+   int stdarg_regs;
+
+   enum cris_retinsn_type return_type;
  };
 
 /* This little fix suppresses the 'u' or 's' when '%e' in assembly
    pattern.  */
 static char cris_output_insn_is_bound = 0;
 
-/* This one suppresses printing out the "rPIC+" in
-   "rPIC+sym:GOTOFF+offset" when doing PIC.  For a PLT symbol, it
-   suppresses outputting it as [rPIC+sym:GOTPLT] and outputs similarly
-   just the "sym:GOTOFF" part.  */
-static int cris_pic_sympart_only = 0;
+/* In code for output macros, this is how we know whether e.g. constant
+   goes in code or in a static initializer.  */
+static int in_code = 0;
 
 /* Fix for reg_overlap_mentioned_p.  */
-static int cris_reg_overlap_mentioned_p PARAMS ((rtx, rtx));
+static int cris_reg_overlap_mentioned_p (rtx, rtx);
+
+static void cris_print_base (rtx, FILE *);
 
-static void cris_print_base PARAMS ((rtx, FILE *));
+static void cris_print_index (rtx, FILE *);
 
-static void cris_print_index PARAMS ((rtx, FILE *));
+static void cris_output_addr_const (FILE *, rtx);
 
-static void cris_init_machine_status PARAMS ((struct function *));
+static struct machine_function * cris_init_machine_status (void);
 
-static int cris_initial_frame_pointer_offset PARAMS ((void));
+static rtx cris_struct_value_rtx (tree, int);
 
-static int saved_regs_mentioned PARAMS ((rtx));
+static void cris_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
+                                        tree type, int *, int);
 
-static void cris_target_asm_function_prologue
-  PARAMS ((FILE *, HOST_WIDE_INT));
+static int cris_initial_frame_pointer_offset (void);
 
-static void cris_target_asm_function_epilogue
-  PARAMS ((FILE *, HOST_WIDE_INT));
+static int saved_regs_mentioned (rtx);
 
-static void cris_operand_lossage PARAMS ((const char *, rtx));
+static void cris_operand_lossage (const char *, rtx);
 
-/* The function cris_target_asm_function_epilogue puts the last insn to
-   output here.  It always fits; there won't be a symbol operand.  Used in
-   delay_slots_for_epilogue and function_epilogue.  */
-static char save_last[80];
+static int cris_reg_saved_in_regsave_area  (unsigned int, bool);
 
-/* This is the argument from the "-max-stack-stackframe=" option.  */
-const char *cris_max_stackframe_str;
+static void cris_asm_output_mi_thunk
+  (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
 
-/* This is the argument from the "-march=" option.  */
-const char *cris_cpu_str;
+static void cris_file_start (void);
+static void cris_init_libfuncs (void);
 
-/* This is the argument from the "-mtune=" option.  */
-const char *cris_tune_str;
+static bool cris_rtx_costs (rtx, int, int, int *, bool);
+static int cris_address_cost (rtx, bool);
+static bool cris_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
+                                   const_tree, bool);
+static int cris_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+                                  tree, bool);
+static tree cris_md_asm_clobbers (tree, tree, tree);
 
-/* This is the argument from the "-melinux-stacksize=" option.  */
-const char *cris_elinux_stacksize_str;
+static bool cris_handle_option (size_t, const char *, int);
 
 /* This is the parsed result of the "-max-stack-stackframe=" option.  If
    it (still) is zero, then there was no such option given.  */
@@ -142,295 +148,277 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION;
 #undef TARGET_ASM_UNALIGNED_DI_OP
 #define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP
 
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE cris_target_asm_function_prologue
-
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE cris_target_asm_function_epilogue
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK cris_asm_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START cris_file_start
+
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS cris_init_libfuncs
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS cris_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST cris_address_cost
+
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX cris_struct_value_rtx
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS cris_setup_incoming_varargs
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE cris_pass_by_reference
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES cris_arg_partial_bytes
+#undef TARGET_MD_ASM_CLOBBERS
+#define TARGET_MD_ASM_CLOBBERS cris_md_asm_clobbers
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | CRIS_SUBTARGET_DEFAULT)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION cris_handle_option
 
 struct gcc_target targetm = TARGET_INITIALIZER;
 
-/* Predicate functions.  */
-
-/* This checks a part of an address, the one that is not a plain register
-   for an addressing mode using BDAP.
-   Allowed operands is either:
-   a) a register
-   b) a CONST operand (but not a symbol when generating PIC)
-   c) a [r] or [r+] in SImode, or sign-extend from HI or QI.  */
+/* Helper for cris_load_multiple_op and cris_ret_movem_op.  */
 
-int
-cris_bdap_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+bool
+cris_movem_load_rest_p (rtx op, int offs)
 {
-  register enum rtx_code code = GET_CODE (op);
-
-  if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode))
-    return 0;
-
-  /* Just return whether this is a simple register or constant.  */
-  if (register_operand (op, mode)
-      || (CONSTANT_P (op) && !(flag_pic && cris_symbol (op))))
-    return 1;
-
-  /* Is it a [r] or possibly a [r+]?  */
-  if (code == MEM)
+  unsigned int reg_count = XVECLEN (op, 0) - offs;
+  rtx src_addr;
+  int i;
+  rtx elt;
+  int setno;
+  int regno_dir = 1;
+  unsigned int regno = 0;
+
+  /* Perform a quick check so we don't blow up below.  FIXME: Adjust for
+     other than (MEM reg).  */
+  if (reg_count <= 1
+      || GET_CODE (XVECEXP (op, 0, offs)) != SET
+      || !REG_P (SET_DEST (XVECEXP (op, 0, offs)))
+      || !MEM_P (SET_SRC (XVECEXP (op, 0, offs))))
+    return false;
+
+  /* Check a possible post-inc indicator.  */
+  if (GET_CODE (SET_SRC (XVECEXP (op, 0, offs + 1))) == PLUS)
     {
-      rtx tem = XEXP (op, 0);
-
-      if (mode == SImode
-         && (register_operand (tem, SImode)
-             || (GET_CODE (tem) == POST_INC
-                 && register_operand (XEXP (tem, 0), SImode))))
-       return 1;
-      else
-       return 0;
+      rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 0);
+      rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 1);
+
+      reg_count--;
+
+      if (reg_count == 1
+         || !REG_P (reg)
+         || !REG_P (SET_DEST (XVECEXP (op, 0, offs + 1)))
+         || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, offs + 1)))
+         || !CONST_INT_P (inc)
+         || INTVAL (inc) != (HOST_WIDE_INT) reg_count * 4)
+       return false;
+      i = offs + 2;
     }
+  else
+    i = offs + 1;
 
-  /* Perhaps a sign-extended mem: [r].(b|w) or [r+].(b|w)?  */
-  if (code == SIGN_EXTEND)
+  if (!TARGET_V32)
     {
-      rtx tem = XEXP (op, 0);
-
-      if (GET_CODE (tem) != MEM)
-       return 0;
-
-      tem = XEXP (tem, 0);
-      if (mode == SImode
-         && (register_operand (tem, SImode)
-             || (GET_CODE (tem) == POST_INC
-                 && register_operand (XEXP (tem, 0), SImode))))
-       return 1;
-      else
-       return 0;
+      regno_dir = -1;
+      regno = reg_count - 1;
     }
 
-  return 0;
-}
-
-/* This is similar to cris_bdap_operand:
-   It checks a part of an address, the one that is not a plain register
-   for an addressing mode using BDAP *or* BIAP.
-   Allowed operands is either:
-   a) a register
-   b) a CONST operand (but not a symbol when generating PIC)
-   c) a mult of (1, 2 or 4) and a register
-   d) a [r] or [r+] in SImode, or sign-extend from HI or QI.  */
-
-int
-cris_bdap_biap_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  register enum rtx_code code = GET_CODE (op);
-  rtx reg;
-  rtx val;
-
-  /* Check for bdap operand.  */
-  if (cris_bdap_operand (op, mode))
-    return 1;
-
-  if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode))
-    return 0;
+  elt = XVECEXP (op, 0, offs);
+  src_addr = XEXP (SET_SRC (elt), 0);
 
-  /* Check that we're looking at a BIAP operand.  */
-  if (code != MULT)
-    return 0;
+  if (GET_CODE (elt) != SET
+      || !REG_P (SET_DEST (elt))
+      || GET_MODE (SET_DEST (elt)) != SImode
+      || REGNO (SET_DEST (elt)) != regno
+      || !MEM_P (SET_SRC (elt))
+      || GET_MODE (SET_SRC (elt)) != SImode
+      || !memory_address_p (SImode, src_addr))
+    return false;
 
-  /* Canonicalize register and multiplicand.  */
-  if (GET_CODE (XEXP (op, 0)) == CONST_INT)
+  for (setno = 1; i < XVECLEN (op, 0); setno++, i++)
     {
-      val = XEXP (op, 0);
-      reg = XEXP (op, 1);
+      rtx elt = XVECEXP (op, 0, i);
+      regno += regno_dir;
+
+      if (GET_CODE (elt) != SET
+         || !REG_P (SET_DEST (elt))
+         || GET_MODE (SET_DEST (elt)) != SImode
+         || REGNO (SET_DEST (elt)) != regno
+         || !MEM_P (SET_SRC (elt))
+         || GET_MODE (SET_SRC (elt)) != SImode
+         || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
+         || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+         || !CONST_INT_P (XEXP (XEXP (SET_SRC (elt), 0), 1))
+         || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != setno * 4)
+       return false;
     }
-  else
-    {
-      val = XEXP (op, 1);
-      reg = XEXP (op, 0);
-    }
-
-  /* Check that the operands are correct after canonicalization.  */
-  if (! register_operand (reg, SImode) || GET_CODE (val) != CONST_INT)
-    return 0;
-
-  /* Check that the multiplicand has a valid value.  */
-  if ((code == MULT
-       && (INTVAL (val) == 1 || INTVAL (val) == 2 || INTVAL (val) == 4)))
-    return 1;
-
-  return 0;
-}
-
-/* Check if MODE is same as mode for X, and X is PLUS, MINUS, IOR or
-   AND or UMIN.  */
-
-int
-cris_orthogonal_operator (x, mode)
-     rtx x;
-     enum machine_mode mode;
-{
-  enum rtx_code code = GET_CODE (x);
-
-  if (mode == VOIDmode)
-    mode = GET_MODE (x);
-
-  return (GET_MODE (x) == mode
-         && (code == PLUS || code == MINUS
-             || code == IOR || code == AND || code == UMIN));
-}
-
-/* Check if MODE is same as mode for X, and X is PLUS, IOR or AND or
-   UMIN.  */
-
-int
-cris_commutative_orth_op (x, mode)
-     rtx x;
-     enum machine_mode mode;
-{
-  enum rtx_code code = GET_CODE (x);
-
-  if (mode == VOIDmode)
-    mode = GET_MODE (x);
-
-  return (GET_MODE (x) == mode &&
-         (code == PLUS
-          || code == IOR || code == AND || code == UMIN));
-}
-
-/* Check if MODE is same as mode for X, and X is PLUS or MINUS or UMIN.  */
-
-int
-cris_operand_extend_operator (x, mode)
-     rtx x;
-     enum machine_mode mode;
-{
-  enum rtx_code code = GET_CODE (x);
-
-  if (mode == VOIDmode)
-    mode = GET_MODE (x);
 
-  return (GET_MODE (x) == mode
-         && (code == PLUS || code == MINUS || code == UMIN));
+  return true;
 }
 
-/* Check to see if MODE is same as mode for X, and X is SIGN_EXTEND or
-   ZERO_EXTEND.  */
+/* Worker function for predicate for the parallel contents in a movem
+   to-memory.  */
 
-int
-cris_extend_operator (x, mode)
-     rtx x;
-     enum machine_mode mode;
-{
-  enum rtx_code code = GET_CODE (x);
-
-  if (mode == VOIDmode)
-    mode = GET_MODE (x);
-
-  return
-    (GET_MODE (x) == mode && (code == SIGN_EXTEND || code == ZERO_EXTEND));
-}
-
-/* Check to see if MODE is same as mode for X, and X is PLUS or BOUND.  */
-
-int
-cris_plus_or_bound_operator (x, mode)
-     rtx x;
-     enum machine_mode mode;
+bool
+cris_store_multiple_op_p (rtx op)
 {
-  enum rtx_code code = GET_CODE (x);
-
-  if (mode == VOIDmode)
-    mode = GET_MODE (x);
-
-  return
-    (GET_MODE (x) == mode && (code == UMIN || code == PLUS));
-}
+  int reg_count = XVECLEN (op, 0);
+  rtx dest;
+  rtx dest_addr;
+  rtx dest_base;
+  int i;
+  rtx elt;
+  int setno;
+  int regno_dir = 1;
+  int regno = 0;
+  int offset = 0;
 
-/* Since with -fPIC, not all symbols are valid PIC symbols or indeed
-   general_operands, we have to have a predicate that matches it for the
-   "movsi" expander.  */
+  /* Perform a quick check so we don't blow up below.  FIXME: Adjust for
+     other than (MEM reg) and (MEM (PLUS reg const)).  */
+  if (reg_count <= 1)
+    return false;
 
-int
-cris_general_operand_or_symbol (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return general_operand (op, mode)
-    || (CONSTANT_P (op) && cris_symbol (op));
-}
+  elt = XVECEXP (op, 0, 0);
 
-/* Since a PIC symbol without a GOT entry is not a general_operand, we
-   have to have a predicate that matches it.  We use this in the expanded
-   "movsi" anonymous pattern for PIC symbols.  */
+  if (GET_CODE (elt) != SET)
+    return  false;
 
-int
-cris_general_operand_or_gotless_symbol (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return general_operand (op, mode)
-    || (CONSTANT_P (op) && cris_gotless_symbol (op));
-}
+  dest = SET_DEST (elt);
 
-/* Since a PLT symbol is not a general_operand, we have to have a
-   predicate that matches it when we need it.  We use this in the expanded
-   "call" and "call_value" anonymous patterns.  */
+  if (!REG_P (SET_SRC (elt)) || !MEM_P (dest))
+    return false;
 
-int
-cris_general_operand_or_plt_symbol (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return general_operand (op, mode)
-    || (GET_CODE (op) == CONST
-       && GET_CODE (XEXP (op, 0)) == UNSPEC
-       && !TARGET_AVOID_GOTPLT);
-}
+  dest_addr = XEXP (dest, 0);
 
-/* This matches a (MEM (general_operand)) or
-   (MEM (cris_general_operand_or_symbol)).  The second one isn't a valid
-   memory_operand, so we need this predicate to recognize call
-   destinations before we change them to a PLT operand (by wrapping in
-   UNSPEC 0).  */
+  /* Check a possible post-inc indicator.  */
+  if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS)
+    {
+      rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 0);
+      rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1);
+
+      reg_count--;
+
+      if (reg_count == 1
+         || !REG_P (reg)
+         || !REG_P (SET_DEST (XVECEXP (op, 0, 1)))
+         || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, 1)))
+         || !CONST_INT_P (inc)
+         /* Support increment by number of registers, and by the offset
+            of the destination, if it has the form (MEM (PLUS reg
+            offset)).  */
+         || !((REG_P (dest_addr)
+               && REGNO (dest_addr) == REGNO (reg)
+               && INTVAL (inc) == (HOST_WIDE_INT) reg_count * 4)
+              || (GET_CODE (dest_addr) == PLUS
+                  && REG_P (XEXP (dest_addr, 0))
+                  && REGNO (XEXP (dest_addr, 0)) == REGNO (reg)
+                  && CONST_INT_P (XEXP (dest_addr, 1))
+                  && INTVAL (XEXP (dest_addr, 1)) == INTVAL (inc))))
+       return false;
+
+      i = 2;
+    }
+  else
+    i = 1;
 
-int
-cris_mem_call_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  rtx xmem;
+  if (!TARGET_V32)
+    {
+      regno_dir = -1;
+      regno = reg_count - 1;
+    }
 
-  if (GET_CODE (op) != MEM)
-    return 0;
+  if (GET_CODE (elt) != SET
+      || !REG_P (SET_SRC (elt))
+      || GET_MODE (SET_SRC (elt)) != SImode
+      || REGNO (SET_SRC (elt)) != (unsigned int) regno
+      || !MEM_P (SET_DEST (elt))
+      || GET_MODE (SET_DEST (elt)) != SImode)
+    return false;
 
-  if (memory_operand (op, mode))
-    return 1;
+  if (REG_P (dest_addr))
+    {
+      dest_base = dest_addr;
+      offset = 0;
+    }
+  else if (GET_CODE (dest_addr) == PLUS
+          && REG_P (XEXP (dest_addr, 0))
+          && CONST_INT_P (XEXP (dest_addr, 1)))
+    {
+      dest_base = XEXP (dest_addr, 0);
+      offset = INTVAL (XEXP (dest_addr, 1));
+    }
+  else
+    return false;
 
-  xmem = XEXP (op, 0);
+  for (setno = 1; i < XVECLEN (op, 0); setno++, i++)
+    {
+      rtx elt = XVECEXP (op, 0, i);
+      regno += regno_dir;
+
+      if (GET_CODE (elt) != SET
+         || !REG_P (SET_SRC (elt))
+         || GET_MODE (SET_SRC (elt)) != SImode
+         || REGNO (SET_SRC (elt)) != (unsigned int) regno
+         || !MEM_P (SET_DEST (elt))
+         || GET_MODE (SET_DEST (elt)) != SImode
+         || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
+         || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_base)
+         || !CONST_INT_P (XEXP (XEXP (SET_DEST (elt), 0), 1))
+         || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != setno * 4 + offset)
+       return false;
+    }
 
-  return cris_general_operand_or_symbol (xmem, GET_MODE (op));
+  return true;
 }
 
-/* The CONDITIONAL_REGISTER_USAGE worker.   */
+/* The CONDITIONAL_REGISTER_USAGE worker.  */
 
 void
-cris_conditional_register_usage ()
+cris_conditional_register_usage (void)
 {
   /* FIXME: This isn't nice.  We should be able to use that register for
      something else if the PIC table isn't needed.  */
   if (flag_pic)
     fixed_regs[PIC_OFFSET_TABLE_REGNUM]
       = call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+
+  /* Allow use of ACR (PC in pre-V32) and tweak order.  */
+  if (TARGET_V32)
+    {
+      static const int reg_alloc_order_v32[] = REG_ALLOC_ORDER_V32;
+      unsigned int i;
+
+      fixed_regs[CRIS_ACR_REGNUM] = 0;
+
+      for (i = 0;
+          i < sizeof (reg_alloc_order_v32)/sizeof (reg_alloc_order_v32[0]);
+          i++)
+       reg_alloc_order[i] = reg_alloc_order_v32[i];
+    }
+
+  if (TARGET_HAS_MUL_INSNS)
+    fixed_regs[CRIS_MOF_REGNUM] = 0;
+
+  /* On early versions, we must use the 16-bit condition-code register,
+     which has another name.  */
+  if (cris_cpu_version < 8)
+    reg_names[CRIS_CC0_REGNUM] = "ccr";
 }
 
-/* Return current_function_uses_pic_offset_table.  For use in cris.md,
+/* Return crtl->uses_pic_offset_table.  For use in cris.md,
    since some generated files do not include function.h.  */
 
 int
-cris_cfun_uses_pic_table ()
+cris_cfun_uses_pic_table (void)
 {
-  return current_function_uses_pic_offset_table;
+  return crtl->uses_pic_offset_table;
 }
 
 /* Given an rtx, return the text string corresponding to the CODE of X.
@@ -438,8 +426,7 @@ cris_cfun_uses_pic_table ()
    define_insn.  */
 
 const char *
-cris_op_str (x)
-     rtx x;
+cris_op_str (rtx x)
 {
   cris_output_insn_is_bound = 0;
   switch (GET_CODE (x))
@@ -453,7 +440,11 @@ cris_op_str (x)
       break;
 
     case MULT:
-      return "mul";
+      /* This function is for retrieving a part of an instruction name for
+        an operator, for immediate output.  If that ever happens for
+        MULT, we need to apply TARGET_MUL_BUG in the caller.  Make sure
+        we notice.  */
+      internal_error ("MULT case in cris_op_str");
       break;
 
     case DIV:
@@ -489,7 +480,7 @@ cris_op_str (x)
       break;
 
     case UMIN:
-      /* Used to control the sign/zero-extend character for the 'e' modifier.
+      /* Used to control the sign/zero-extend character for the 'E' modifier.
         BOUND has none.  */
       cris_output_insn_is_bound = 1;
       return "bound";
@@ -507,9 +498,7 @@ cris_op_str (x)
    categorization of the error.  */
 
 static void
-cris_operand_lossage (msgid, op)
-     const char *msgid;
-     rtx op;
+cris_operand_lossage (const char *msgid, rtx op)
 {
   debug_rtx (op);
   output_operand_lossage ("%s", msgid);
@@ -518,15 +507,11 @@ cris_operand_lossage (msgid, op)
 /* Print an index part of an address to file.  */
 
 static void
-cris_print_index (index, file)
-     rtx index;
-     FILE * file;
+cris_print_index (rtx index, FILE *file)
 {
-  rtx inner = XEXP (index, 0);
-
   /* Make the index "additive" unless we'll output a negative number, in
      which case the sign character is free (as in free beer).  */
-  if (GET_CODE (index) != CONST_INT || INTVAL (index) >= 0)
+  if (!CONST_INT_P (index) || INTVAL (index) >= 0)
     putc ('+', file);
 
   if (REG_P (index))
@@ -540,9 +525,9 @@ cris_print_index (index, file)
 
       putc (INTVAL (XEXP (index, 1)) == 2 ? 'w' : 'd', file);
     }
-  else if (GET_CODE (index) == SIGN_EXTEND &&
-          GET_CODE (inner) == MEM)
+  else if (GET_CODE (index) == SIGN_EXTEND && MEM_P (XEXP (index, 0)))
     {
+      rtx inner = XEXP (index, 0);
       rtx inner_inner = XEXP (inner, 0);
 
       if (GET_CODE (inner_inner) == POST_INC)
@@ -558,8 +543,9 @@ cris_print_index (index, file)
          putc (GET_MODE (inner) == HImode ? 'w' : 'b', file);
        }
     }
-  else if (GET_CODE (index) == MEM)
+  else if (MEM_P (index))
     {
+      rtx inner = XEXP (index, 0);
       if (GET_CODE (inner) == POST_INC)
        fprintf (file, "[$%s+].d", reg_names[REGNO (XEXP (inner, 0))]);
       else
@@ -573,14 +559,15 @@ cris_print_index (index, file)
 /* Print a base rtx of an address to file.  */
 
 static void
-cris_print_base (base, file)
-     rtx base;
-     FILE *file;
+cris_print_base (rtx base, FILE *file)
 {
   if (REG_P (base))
     fprintf (file, "$%s", reg_names[REGNO (base)]);
   else if (GET_CODE (base) == POST_INC)
-    fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
+    {
+      gcc_assert (REGNO (XEXP (base, 0)) != CRIS_ACR_REGNUM);
+      fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
+    }
   else
     cris_operand_lossage ("unexpected base-type in cris_print_base",
                          base);
@@ -589,8 +576,7 @@ cris_print_base (base, file)
 /* Usable as a guard in expressions.  */
 
 int
-cris_fatal (arg)
-     char *arg;
+cris_fatal (char *arg)
 {
   internal_error (arg);
 
@@ -598,284 +584,31 @@ cris_fatal (arg)
   return 0;
 }
 
-/* Textual function prologue.  */
+/* Return nonzero if REGNO is an ordinary register that *needs* to be
+   saved together with other registers, possibly by a MOVEM instruction,
+   or is saved for target-independent reasons.  There may be
+   target-dependent reasons to save the register anyway; this is just a
+   wrapper for a complicated conditional.  */
 
-static void
-cris_target_asm_function_prologue (file, size)
-     FILE *file;
-     HOST_WIDE_INT size;
+static int
+cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used)
 {
-  int regno;
-
-  /* Shorten the used name for readability.  */
-  int cfoa_size = current_function_outgoing_args_size;
-  int last_movem_reg = -1;
-  int doing_dwarf = dwarf2out_do_frame ();
-  int framesize;
-  int faked_args_size = 0;
-  int cfa_write_offset = 0;
-  char *cfa_label = NULL;
-  int return_address_on_stack
-    = regs_ever_live[CRIS_SRP_REGNUM]
-    || cfun->machine->needs_return_address_on_stack != 0;
-
-  /* Don't do anything if no prologues or epilogues are wanted.  */
-  if (!TARGET_PROLOGUE_EPILOGUE)
-    return;
-
-  if (size < 0)
-    abort ();
-
-  /* Align the size to what's best for the CPU model.  */
-  if (TARGET_STACK_ALIGN)
-    size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
-
-  if (current_function_pretend_args_size)
-    {
-      int pretend = current_function_pretend_args_size;
-      for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1;
-          pretend > 0;
-          regno--, pretend -= 4)
-       {
-         fprintf (file, "\tpush $%s\n", reg_names[regno]);
-         faked_args_size += 4;
-       }
-    }
-
-  framesize = faked_args_size;
-
-  if (doing_dwarf)
-    {
-      /* FIXME: Slightly redundant calculation, as we do the same in
-        pieces below.  This offset must be the total adjustment of the
-        stack-pointer.  We can then def_cfa call at the end of this
-        function with the current implementation of execute_cfa_insn, but
-        that wouldn't really be clean.  */
-
-      int cfa_offset
-       = faked_args_size
-       + (return_address_on_stack ? 4 : 0)
-       + (frame_pointer_needed ? 4 : 0);
-
-      int cfa_reg;
-
-      if (frame_pointer_needed)
-       cfa_reg = FRAME_POINTER_REGNUM;
-      else
-       {
-         cfa_reg = STACK_POINTER_REGNUM;
-         cfa_offset += cris_initial_frame_pointer_offset ();
-       }
-
-      cfa_label = dwarf2out_cfi_label ();
-      dwarf2out_def_cfa (cfa_label, cfa_reg, cfa_offset);
-
-      cfa_write_offset = - faked_args_size - 4;
-    }
-
-  /* Save SRP if not a leaf function.  */
-  if (return_address_on_stack)
-    {
-      fprintf (file, "\tPush $srp\n");
-      framesize += 4;
-
-      if (doing_dwarf)
-       {
-         dwarf2out_return_save (cfa_label, cfa_write_offset);
-         cfa_write_offset -= 4;
-       }
-    }
-
-  /* Set up frame pointer if needed.  */
-  if (frame_pointer_needed)
-    {
-      fprintf (file, "\tpush $%s\n\tmove.d $sp,$%s\n",
-              reg_names[FRAME_POINTER_REGNUM],
-              reg_names[FRAME_POINTER_REGNUM]);
-      framesize += 4;
-
-      if (doing_dwarf)
-       {
-         dwarf2out_reg_save (cfa_label, FRAME_POINTER_REGNUM,
-                             cfa_write_offset);
-         cfa_write_offset -= 4;
-       }
-    }
-
-  /* Local vars are located above saved regs.  */
-  cfa_write_offset -= size;
-
-  /* Get a contiguous sequence of registers, starting with r0, that need
-     to be saved.  */
-  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-    {
-      if ((((regs_ever_live[regno]
-            && !call_used_regs[regno])
-           || (regno == (int) PIC_OFFSET_TABLE_REGNUM
-               && (current_function_uses_pic_offset_table
-                   /* It is saved anyway, if there would be a gap.  */
-                   || (flag_pic
-                       && regs_ever_live[regno + 1]
-                       && !call_used_regs[regno + 1]))))
-          && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
-          && regno != CRIS_SRP_REGNUM)
-         || (current_function_calls_eh_return
-             && (regno == EH_RETURN_DATA_REGNO (0)
-                 || regno == EH_RETURN_DATA_REGNO (1)
-                 || regno == EH_RETURN_DATA_REGNO (2)
-                 || regno == EH_RETURN_DATA_REGNO (3))))
-       {
-         /* Check if movem may be used for registers so far.  */
-         if (regno == last_movem_reg + 1)
-           /* Yes, update next expected register.  */
-           last_movem_reg++;
-         else
-           {
-             /* We cannot use movem for all registers.  We have to flush
-                any movem:ed registers we got so far.  */
-             if (last_movem_reg != -1)
-               {
-                 /* It is a win to use a side-effect assignment for
-                    64 <= size <= 128.  But side-effect on movem was
-                    not usable for CRIS v0..3.  Also only do it if
-                    side-effects insns are allowed.  */
-                 if ((last_movem_reg + 1) * 4 + size >= 64
-                     && (last_movem_reg + 1) * 4 + size <= 128
-                     && cris_cpu_version >= CRIS_CPU_SVINTO
-                     && TARGET_SIDE_EFFECT_PREFIXES)
-                   fprintf (file, "\tmovem $%s,[$sp=$sp-%d]\n",
-                            reg_names[last_movem_reg],
-                            (last_movem_reg + 1) * 4 + size);
-                 else
-                   {
-                     /* Avoid printing multiple subsequent sub:s for sp.  */
-                     fprintf (file, "\tsub%s %d,$sp\n",
-                              ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1)
-                                                      * 4 + size),
-                              (last_movem_reg + 1) * 4 + size);
-
-                     fprintf (file, "\tmovem $%s,[$sp]\n",
-                              reg_names[last_movem_reg]);
-                   }
-
-                 framesize += (last_movem_reg + 1) * 4 + size;
-
-                 if (TARGET_PDEBUG)
-                   fprintf (file, "; frame %d, #regs %d, bytes %d args %d\n",
-                            size,
-                            last_movem_reg + 1,
-                            (last_movem_reg + 1) * 4,
-                            current_function_args_size);
-
-                 last_movem_reg = -1;
-                 size = 0;
-               }
-             else if (size > 0)
-               {
-                 /* Local vars on stack, but there are no movem:s.
-                    Just allocate space.  */
-                 fprintf (file, "\tSub%s %d,$sp\n",
-                          ADDITIVE_SIZE_MODIFIER (size),
-                          size);
-                 framesize += size;
-                 size = 0;
-               }
-
-             fprintf (file, "\tPush $%s\n", reg_names[regno]);
-             framesize += 4;
-           }
-
-         if (doing_dwarf)
-           {
-             /* Registers are stored lowest numbered at highest address,
-                which matches the loop order; we just need to update the
-                write-offset.  */
-             dwarf2out_reg_save (cfa_label, regno, cfa_write_offset);
-             cfa_write_offset -= 4;
-           }
-       }
-    }
-
-  /* Check after, if we can movem all registers.  This is the normal
-     case.  */
-  if (last_movem_reg != -1)
-    {
-      /* Side-effect assignment on movem was not supported for CRIS v0..3,
-        and don't do it if we're asked not to.
-
-        The movem is already accounted for, for unwind.  */
-
-      if ((last_movem_reg + 1) * 4 + size >= 64
-         && (last_movem_reg + 1) * 4 + size <= 128
-         && cris_cpu_version >= CRIS_CPU_SVINTO
-         && TARGET_SIDE_EFFECT_PREFIXES)
-       fprintf (file, "\tmovem $%s,[$sp=$sp-%d]\n",
-                reg_names[last_movem_reg],
-                (last_movem_reg+1) * 4 + size);
-      else
-       {
-         /* Avoid printing multiple subsequent sub:s for sp.  FIXME:
-            Clean up the conditional expression.  */
-         fprintf (file, "\tsub%s %d,$sp\n",
-                  ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) * 4 + size),
-                  (last_movem_reg + 1) * 4 + size);
-         /* To be compatible with v0..v3 means we do not use an assignment
-            addressing mode with movem.  We normally don't need that
-            anyway.  It would only be slightly more efficient for 64..128
-            bytes frame size.  */
-         fprintf (file, "\tmovem $%s,[$sp]\n", reg_names[last_movem_reg]);
-       }
-
-      framesize += (last_movem_reg + 1) * 4 + size;
-
-      if (TARGET_PDEBUG)
-       fprintf (file, "; frame %d, #regs %d, bytes %d args %d\n",
-                size,
-                last_movem_reg + 1,
-                (last_movem_reg + 1) * 4,
-                current_function_args_size);
-
-      /* We have to put outgoing argument space after regs.  */
-      if (cfoa_size)
-       {
-         /* This does not need to be accounted for, for unwind.  */
-
-         fprintf (file, "\tSub%s %d,$sp\n",
-                  ADDITIVE_SIZE_MODIFIER (cfoa_size),
-                  cfoa_size);
-         framesize += cfoa_size;
-       }
-    }
-  else if ((size + cfoa_size) > 0)
-    {
-      /* This does not need to be accounted for, for unwind.  */
-
-      /* Local vars on stack, and we could not use movem.  Add a sub here.  */
-      fprintf (file, "\tSub%s %d,$sp\n",
-              ADDITIVE_SIZE_MODIFIER (size + cfoa_size),
-              cfoa_size + size);
-      framesize += size + cfoa_size;
-    }
-
-  /* Set up the PIC register.  */
-  if (current_function_uses_pic_offset_table)
-    asm_fprintf (file, "\tmove.d $pc,$%s\n\tsub.d .:GOTOFF,$%s\n",
-                reg_names[PIC_OFFSET_TABLE_REGNUM],
-                reg_names[PIC_OFFSET_TABLE_REGNUM]);
-
-  if (TARGET_PDEBUG)
-    fprintf (file,
-            "; parm #%d @ %d; frame %d, FP-SP is %d; leaf: %s%s; fp %s, outg: %d arg %d\n",
-            CRIS_MAX_ARGS_IN_REGS + 1, FIRST_PARM_OFFSET (0),
-            get_frame_size (),
-            cris_initial_frame_pointer_offset (),
-            leaf_function_p () ? "yes" : "no",
-            return_address_on_stack ? "no" :"yes",
-            frame_pointer_needed ? "yes" : "no",
-            cfoa_size, current_function_args_size);
-
-  if (cris_max_stackframe && framesize > cris_max_stackframe)
-    warning ("stackframe too big: %d bytes", framesize);
+  return
+    (((df_regs_ever_live_p (regno)
+       && !call_used_regs[regno])
+      || (regno == PIC_OFFSET_TABLE_REGNUM
+         && (got_really_used
+             /* It is saved anyway, if there would be a gap.  */
+             || (flag_pic
+                 && df_regs_ever_live_p (regno + 1)
+                 && !call_used_regs[regno + 1]))))
+     && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
+     && regno != CRIS_SRP_REGNUM)
+    || (crtl->calls_eh_return
+       && (regno == EH_RETURN_DATA_REGNO (0)
+           || regno == EH_RETURN_DATA_REGNO (1)
+           || regno == EH_RETURN_DATA_REGNO (2)
+           || regno == EH_RETURN_DATA_REGNO (3)));
 }
 
 /* Return nonzero if there are regs mentioned in the insn that are not all
@@ -883,8 +616,7 @@ cris_target_asm_function_prologue (file, size)
    can be put in the epilogue.  */
 
 static int
-saved_regs_mentioned (x)
-     rtx x;
+saved_regs_mentioned (rtx x)
 {
   int i;
   const char *fmt;
@@ -930,384 +662,98 @@ saved_regs_mentioned (x)
   return 0;
 }
 
-/* Figure out if the insn may be put in the epilogue.  */
+/* The PRINT_OPERAND worker.  */
 
-int
-cris_eligible_for_epilogue_delay (insn)
-     rtx insn;
+void
+cris_print_operand (FILE *file, rtx x, int code)
 {
-  /* First of all, it must be as slottable as for a delayed branch insn.  */
-  if (get_attr_slottable (insn) != SLOTTABLE_YES)
-    return 0;
+  rtx operand = x;
 
-  /* It must not refer to the stack pointer (may be valid for some cases
-     that I can't think of).  */
-  if (reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))
-    return 0;
+  /* Size-strings corresponding to MULT expressions.  */
+  static const char *const mults[] = { "BAD:0", ".b", ".w", "BAD:3", ".d" };
 
-  /* The frame pointer will be restored in the epilogue, before the
-     "ret", so it can't be referred to.  */
-  if (frame_pointer_needed
-      && reg_mentioned_p (frame_pointer_rtx, PATTERN (insn)))
-    return 0;
+  /* New code entries should just be added to the switch below.  If
+     handling is finished, just return.  If handling was just a
+     modification of the operand, the modified operand should be put in
+     "operand", and then do a break to let default handling
+     (zero-modifier) output the operand.  */
 
-  /* All saved regs are restored before the delayed insn.
-     This means that we cannot have any instructions that mention the
-     registers that are restored by the epilogue.  */
-  if (saved_regs_mentioned (PATTERN (insn)))
-    return 0;
+  switch (code)
+    {
+    case 'b':
+      /* Print the unsigned supplied integer as if it were signed
+        and < 0, i.e print 255 or 65535 as -1, 254, 65534 as -2, etc.  */
+      if (!CONST_INT_P (x)
+         || !CRIS_CONST_OK_FOR_LETTER_P (INTVAL (x), 'O'))
+       LOSE_AND_RETURN ("invalid operand for 'b' modifier", x);
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+              INTVAL (x)| (INTVAL (x) <= 255 ? ~255 : ~65535));
+      return;
 
-  /* It seems to be ok.  */
-  return 1;
-}
-
-/* Return the number of delay-slots in the epilogue: return 1 if it
-   contains "ret", else 0.  */
-
-int
-cris_delay_slots_for_epilogue ()
-{
-  /* Check if we use a return insn, which we only do for leaf functions.
-     Else there is no slot to fill.  */
-  if (regs_ever_live[CRIS_SRP_REGNUM]
-      || cfun->machine->needs_return_address_on_stack != 0)
-    return 0;
-
-  /* By calling function_epilogue with the same parameters as from gcc
-     we can get info about if the epilogue can fill the delay-slot by itself.
-     If it is filled from the epilogue, then the corresponding string
-     is in save_last.
-      This depends on that the "size" argument to function_epilogue
-     always is get_frame_size.
-     FIXME:  Kludgy.  At least make it a separate function that is not
-     misnamed or abuses the stream parameter.  */
-  cris_target_asm_function_epilogue (NULL, get_frame_size ());
-
-  if (*save_last)
-    return 1;
-  return 0;
-}
-
-/* Textual function epilogue.  When file is NULL, it serves doubly as
-   a test for whether the epilogue can fill any "ret" delay-slots by
-   itself by storing the delay insn in save_last.  */
-
-static void
-cris_target_asm_function_epilogue (file, size)
-     FILE *file;
-     HOST_WIDE_INT size;
-{
-  int regno;
-  int last_movem_reg = -1;
-  rtx insn = get_last_insn ();
-  int argspace_offset = current_function_outgoing_args_size;
-  int pretend =         current_function_pretend_args_size;
-  int return_address_on_stack
-    = regs_ever_live[CRIS_SRP_REGNUM]
-    || cfun->machine->needs_return_address_on_stack != 0;
-
-  save_last[0] = 0;
-
-  if (file && !TARGET_PROLOGUE_EPILOGUE)
-    return;
-
-  if (TARGET_PDEBUG && file)
-    fprintf (file, ";;\n");
-
-  /* Align byte count of stack frame.  */
-  if (TARGET_STACK_ALIGN)
-    size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
-
-  /* If the last insn was a BARRIER, we don't have to write any code,
-     then all returns were covered by "return" insns.  */
-  if (GET_CODE (insn) == NOTE)
-    insn = prev_nonnote_insn (insn);
-  if (insn
-      && (GET_CODE (insn) == BARRIER
-         /* We must make sure that the insn really is a "return" and
-            not a conditional branch.  Try to match the return exactly,
-            and if it doesn't match, assume it is a conditional branch
-            (and output an epilogue).  */
-         || (GET_CODE (insn) == JUMP_INSN
-             && GET_CODE (PATTERN (insn)) == RETURN)))
-    {
-      if (TARGET_PDEBUG && file)
-       fprintf (file, ";;;;;\n");
+    case 'x':
+      /* Print assembler code for operator.  */
+      fprintf (file, "%s", cris_op_str (operand));
       return;
-    }
-
-  /* Check how many saved regs we can movem.  They start at r0 and must
-     be contiguous.  */
-  for (regno = 0;
-       regno < FIRST_PSEUDO_REGISTER;
-       regno++)
-    if ((((regs_ever_live[regno]
-          && !call_used_regs[regno])
-         || (regno == (int) PIC_OFFSET_TABLE_REGNUM
-             && (current_function_uses_pic_offset_table
-                 /* It is saved anyway, if there would be a gap.  */
-                 || (flag_pic
-                     && regs_ever_live[regno + 1]
-                     && !call_used_regs[regno + 1]))))
-        && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
-        && regno != CRIS_SRP_REGNUM)
-       || (current_function_calls_eh_return
-           && (regno == EH_RETURN_DATA_REGNO (0)
-               || regno == EH_RETURN_DATA_REGNO (1)
-               || regno == EH_RETURN_DATA_REGNO (2)
-               || regno == EH_RETURN_DATA_REGNO (3))))
 
+    case 'o':
       {
-       if (regno == last_movem_reg + 1)
-         last_movem_reg++;
-       else
-         break;
-      }
+       /* A movem modifier working on a parallel; output the register
+          name.  */
+       int regno;
 
-  for (regno = FIRST_PSEUDO_REGISTER - 1;
-       regno > last_movem_reg;
-       regno--)
-    if ((((regs_ever_live[regno]
-          && !call_used_regs[regno])
-         || (regno == (int) PIC_OFFSET_TABLE_REGNUM
-             && (current_function_uses_pic_offset_table
-                 /* It is saved anyway, if there would be a gap.  */
-                 || (flag_pic
-                     && regs_ever_live[regno + 1]
-                     && !call_used_regs[regno + 1]))))
-        && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
-        && regno != CRIS_SRP_REGNUM)
-       || (current_function_calls_eh_return
-           && (regno == EH_RETURN_DATA_REGNO (0)
-               || regno == EH_RETURN_DATA_REGNO (1)
-               || regno == EH_RETURN_DATA_REGNO (2)
-               || regno == EH_RETURN_DATA_REGNO (3))))
-      {
-       if (argspace_offset)
-         {
-           /* There is an area for outgoing parameters located before
-              the saved registers.  We have to adjust for that.  */
-           if (file)
-             fprintf (file, "\tAdd%s %d,$sp\n",
-                      ADDITIVE_SIZE_MODIFIER (argspace_offset),
-                      argspace_offset);
+       if (GET_CODE (x) != PARALLEL)
+         LOSE_AND_RETURN ("invalid operand for 'o' modifier", x);
 
-           /* Make sure we only do this once.  */
-           argspace_offset = 0;
-         }
+       /* The second item can be (set reg (plus reg const)) to denote a
+          postincrement.  */
+       regno
+         = (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS
+            ? XVECLEN (x, 0) - 2
+            : XVECLEN (x, 0) - 1);
 
-       /* Flush previous non-movem:ed registers.  */
-       if (*save_last && file)
-         fprintf (file, save_last);
-       sprintf (save_last, "\tPop $%s\n", reg_names[regno]);
+       fprintf (file, "$%s", reg_names [regno]);
       }
-
-  if (last_movem_reg != -1)
-    {
-      if (argspace_offset)
-       {
-         /* Adjust for the outgoing parameters area, if that's not
-            handled yet.  */
-         if (*save_last && file)
-           {
-             fprintf (file, save_last);
-             *save_last = 0;
-           }
-
-         if (file)
-           fprintf (file, "\tAdd%s %d,$sp\n",
-                    ADDITIVE_SIZE_MODIFIER (argspace_offset),
-                    argspace_offset);
-         argspace_offset = 0;
-       }
-      /* Flush previous non-movem:ed registers.  */
-      else if (*save_last && file)
-       fprintf (file, save_last);
-      sprintf (save_last, "\tmovem [$sp+],$%s\n", reg_names[last_movem_reg]);
-    }
-
-  /* Restore frame pointer if necessary.  */
-  if (frame_pointer_needed)
-    {
-      if (*save_last && file)
-       fprintf (file, save_last);
-
-      if (file)
-       fprintf (file, "\tmove.d $%s,$sp\n",
-                reg_names[FRAME_POINTER_REGNUM]);
-      sprintf (save_last, "\tPop $%s\n",
-              reg_names[FRAME_POINTER_REGNUM]);
-    }
-  else
-    {
-      /* If there was no frame-pointer to restore sp from, we must
-        explicitly deallocate local variables.  */
-
-      /* Handle space for outgoing parameters that hasn't been handled
-        yet.  */
-      size += argspace_offset;
-
-      if (size)
-       {
-         if (*save_last && file)
-           fprintf (file, save_last);
-
-         sprintf (save_last, "\tadd%s %d,$sp\n",
-                  ADDITIVE_SIZE_MODIFIER (size), size);
-       }
-
-      /* If the size was not in the range for a "quick", we must flush
-        it here.  */
-      if (size > 63)
-       {
-         if (file)
-           fprintf (file, save_last);
-         *save_last = 0;
-       }
-    }
-
-  /* If this function has no pushed register parameters
-     (stdargs/varargs), and if it is not a leaf function, then we can
-     just jump-return here.  */
-  if (return_address_on_stack && pretend == 0)
-    {
-      if (*save_last && file)
-       fprintf (file, save_last);
-      *save_last = 0;
-
-      if (file)
-       {
-         if (current_function_calls_eh_return)
-           {
-             /* The installed EH-return address is in *this* frame, so we
-                need to pop it before we return.  */
-             fprintf (file, "\tpop $srp\n");
-             fprintf (file, "\tret\n");
-             fprintf (file, "\tadd.d $%s,$sp\n", reg_names[CRIS_STACKADJ_REG]);
-           }
-         else
-           fprintf (file, "\tJump [$sp+]\n");
-
-         /* Do a sanity check to avoid generating invalid code.  */
-         if (current_function_epilogue_delay_list)
-           internal_error ("allocated but unused delay list in epilogue");
-       }
       return;
-    }
-
-  /* Rather than add current_function_calls_eh_return conditions
-     everywhere in the following code (and not be able to test it
-     thoroughly), assert the assumption that all usage of
-     __builtin_eh_return are handled above.  */
-  if (current_function_calls_eh_return)
-    internal_error ("unexpected function type needing stack adjustment for\
- __builtin_eh_return");
-
-  /* If we pushed some register parameters, then adjust the stack for
-     them.  */
-  if (pretend)
-    {
-      /* Since srp is stored on the way, we need to restore it first.  */
-      if (return_address_on_stack)
-       {
-         if (*save_last && file)
-           fprintf (file, save_last);
-         *save_last = 0;
-
-         if (file)
-           fprintf (file, "\tpop $srp\n");
-       }
-
-      if (*save_last && file)
-       fprintf (file, save_last);
-
-      sprintf (save_last, "\tadd%s %d,$sp\n",
-              ADDITIVE_SIZE_MODIFIER (pretend), pretend);
-    }
-
-  /* Here's where we have a delay-slot we need to fill.  */
-  if (file && current_function_epilogue_delay_list)
-    {
-      /* If gcc has allocated an insn for the epilogue delay slot, but
-        things were arranged so we now thought we could do it
-        ourselves, don't forget to flush that insn.  */
-      if (*save_last)
-       fprintf (file, save_last);
-
-      fprintf (file, "\tRet\n");
-
-      /* Output the delay-slot-insn the mandated way.  */
-      final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
-                      file, 1, -2, 1);
-    }
-  else if (file)
-    {
-      fprintf (file, "\tRet\n");
-
-      /* If the GCC did not do it, we have to use whatever insn we have,
-        or a nop.  */
-      if (*save_last)
-       fprintf (file, save_last);
-      else
-       fprintf (file, "\tnOp\n");
-    }
-}
-
-/* The PRINT_OPERAND worker.  */
-
-void
-cris_print_operand (file, x, code)
-     FILE *file;
-     rtx x;
-     int code;
-{
-  rtx operand = x;
-
-  /* Size-strings corresponding to MULT expressions.  */
-  static const char *mults[] = { "BAD:0", ".b", ".w", "BAD:3", ".d" };
-
-  /* New code entries should just be added to the switch below.  If
-     handling is finished, just return.  If handling was just a
-     modification of the operand, the modified operand should be put in
-     "operand", and then do a break to let default handling
-     (zero-modifier) output the operand.  */
 
-  switch (code)
-    {
-    case 'b':
-      /* Print the unsigned supplied integer as if it was signed
-        and < 0, i.e print 255 or 65535 as -1, 254, 65534 as -2, etc.  */
-      if (GET_CODE (x) != CONST_INT
-         || ! CONST_OK_FOR_LETTER_P (INTVAL (x), 'O'))
-       LOSE_AND_RETURN ("invalid operand for 'b' modifier", x);
-      fprintf (file, "%d", INTVAL (x)| (INTVAL (x) <= 255 ? ~255 : ~65535));
-      return;
+    case 'O':
+      {
+       /* A similar movem modifier; output the memory operand.  */
+       rtx addr;
 
-    case 'x':
-      /* Print assembler code for operator.  */
-      fprintf (file, "%s", cris_op_str (operand));
-      return;
+       if (GET_CODE (x) != PARALLEL)
+         LOSE_AND_RETURN ("invalid operand for 'O' modifier", x);
 
-    case 'v':
-      /* Print the operand without the PIC register.  */
-      if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x))
-       LOSE_AND_RETURN ("invalid operand for 'v' modifier", x);
-      cris_pic_sympart_only++;
-      cris_output_addr_const (file, x);
-      cris_pic_sympart_only--;
-      return;
+       /* The lowest mem operand is in the first item, but perhaps it
+          needs to be output as postincremented.  */
+       addr = MEM_P (SET_SRC (XVECEXP (x, 0, 0)))
+         ? XEXP (SET_SRC (XVECEXP (x, 0, 0)), 0)
+         : XEXP (SET_DEST (XVECEXP (x, 0, 0)), 0);
 
-    case 'P':
-      /* Print the PIC register.  Applied to a GOT-less PIC symbol for
-         sanity.  */
-      if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x))
-       LOSE_AND_RETURN ("invalid operand for 'P' modifier", x);
-      fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]);
+       /* The second item can be a (set reg (plus reg const)) to denote
+          a modification.  */
+       if (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS)
+         {
+           /* It's a post-increment, if the address is a naked (reg).  */
+           if (REG_P (addr))
+             addr = gen_rtx_POST_INC (SImode, addr);
+           else
+             {
+               /* Otherwise, it's a side-effect; RN=RN+M.  */
+               fprintf (file, "[$%s=$%s%s%d]",
+                        reg_names [REGNO (SET_DEST (XVECEXP (x, 0, 1)))],
+                        reg_names [REGNO (XEXP (addr, 0))],
+                        INTVAL (XEXP (addr, 1)) < 0 ? "" : "+",
+                        (int) INTVAL (XEXP (addr, 1)));
+               return;
+             }
+         }
+       output_address (addr);
+      }
       return;
 
     case 'p':
       /* Adjust a power of two to its log2.  */
-      if (GET_CODE (x) != CONST_INT || exact_log2 (INTVAL (x)) < 0 )
+      if (!CONST_INT_P (x) || exact_log2 (INTVAL (x)) < 0 )
        LOSE_AND_RETURN ("invalid operand for 'p' modifier", x);
       fprintf (file, "%d", exact_log2 (INTVAL (x)));
       return;
@@ -1317,7 +763,7 @@ cris_print_operand (file, x, code)
         respectively.  This modifier also terminates the inhibiting
          effects of the 'x' modifier.  */
       cris_output_insn_is_bound = 0;
-      if (GET_MODE (x) == VOIDmode && GET_CODE (x) == CONST_INT)
+      if (GET_MODE (x) == VOIDmode && CONST_INT_P (x))
        {
          if (INTVAL (x) >= 0)
            {
@@ -1344,13 +790,23 @@ cris_print_operand (file, x, code)
 
     case 'z':
       /* Const_int: print b for -127 <= x <= 255,
-        w for -32768 <= x <= 65535, else abort.  */
-      if (GET_CODE (x) != CONST_INT
+        w for -32768 <= x <= 65535, else die.  */
+      if (!CONST_INT_P (x)
          || INTVAL (x) < -32768 || INTVAL (x) > 65535)
        LOSE_AND_RETURN ("invalid operand for 'z' modifier", x);
       putc (INTVAL (x) >= -128 && INTVAL (x) <= 255 ? 'b' : 'w', file);
       return;
 
+    case 'Z':
+      /* If this is a GOT-symbol, print the size-letter corresponding to
+        -fpic/-fPIC.  For everything else, print "d".  */
+      putc ((flag_pic == 1
+            && GET_CODE (x) == CONST
+            && GET_CODE (XEXP (x, 0)) == UNSPEC
+            && XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREAD)
+           ? 'w' : 'd', file);
+      return;
+
     case '#':
       /* Output a 'nop' if there's nothing for the delay slot.
         This method stolen from the sparc files.  */
@@ -1358,23 +814,46 @@ cris_print_operand (file, x, code)
        fputs ("\n\tnop", file);
       return;
 
+    case '!':
+      /* Output directive for alignment padded with "nop" insns.
+        Optimizing for size, it's plain 4-byte alignment, otherwise we
+        align the section to a cache-line (32 bytes) and skip at max 2
+        bytes, i.e. we skip if it's the last insn on a cache-line.  The
+        latter is faster by a small amount (for two test-programs 99.6%
+        and 99.9%) and larger by a small amount (ditto 100.1% and
+        100.2%).  This is supposed to be the simplest yet performance-
+        wise least intrusive way to make sure the immediately following
+        (supposed) muls/mulu insn isn't located at the end of a
+        cache-line.  */
+      if (TARGET_MUL_BUG)
+       fputs (optimize_size
+              ? ".p2alignw 2,0x050f\n\t"
+              : ".p2alignw 5,0x050f,2\n\t", file);
+      return;
+
+    case ':':
+      /* The PIC register.  */
+      if (! flag_pic)
+       internal_error ("invalid use of ':' modifier");
+      fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]);
+      return;
+
     case 'H':
       /* Print high (most significant) part of something.  */
       switch (GET_CODE (operand))
        {
        case CONST_INT:
-         if (HOST_BITS_PER_WIDE_INT == 32)
-           /* Sign-extension from a normal int to a long long.  */
-           fprintf (file, INTVAL (operand) < 0 ? "-1" : "0");
-         else
-           fprintf (file, "0x%x", (unsigned int)(INTVAL (x) >> 31 >> 1));
+         /* If we're having 64-bit HOST_WIDE_INTs, the whole (DImode)
+            value is kept here, and so may be other than 0 or -1.  */
+         fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                  INTVAL (operand_subword (operand, 1, 0, DImode)));
          return;
 
        case CONST_DOUBLE:
          /* High part of a long long constant.  */
          if (GET_MODE (operand) == VOIDmode)
            {
-             fprintf (file, "0x%x", CONST_DOUBLE_HIGH (x));
+             fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_HIGH (x));
              return;
            }
          else
@@ -1382,8 +861,12 @@ cris_print_operand (file, x, code)
 
        case REG:
          /* Print reg + 1.  Check that there's not an attempt to print
-            high-parts of registers like stack-pointer or higher.  */
-         if (REGNO (operand) > STACK_POINTER_REGNUM - 2)
+            high-parts of registers like stack-pointer or higher, except
+            for SRP (where the "high part" is MOF).  */
+         if (REGNO (operand) > STACK_POINTER_REGNUM - 2
+             && (REGNO (operand) != CRIS_SRP_REGNUM
+                 || CRIS_SRP_REGNUM + 1 != CRIS_MOF_REGNUM
+                 || fixed_regs[CRIS_MOF_REGNUM] != 0))
            LOSE_AND_RETURN ("bad register", operand);
          fprintf (file, "$%s", reg_names[REGNO (operand) + 1]);
          return;
@@ -1417,11 +900,17 @@ cris_print_operand (file, x, code)
       break;
 
     case 'e':
+      /* Like 'E', but ignore state set by 'x'.  FIXME: Use code
+        iterators and attributes in cris.md to avoid the need for %x
+        and %E (and %e) and state passed between those modifiers.  */
+      cris_output_insn_is_bound = 0;
+      /* FALL THROUGH.  */
+    case 'E':
       /* Print 's' if operand is SIGN_EXTEND or 'u' if ZERO_EXTEND unless
         cris_output_insn_is_bound is nonzero.  */
       if (GET_CODE (operand) != SIGN_EXTEND
          && GET_CODE (operand) != ZERO_EXTEND
-         && GET_CODE (operand) != CONST_INT)
+         && !CONST_INT_P (operand))
        LOSE_AND_RETURN ("invalid operand for 'e' modifier", x);
 
       if (cris_output_insn_is_bound)
@@ -1431,7 +920,7 @@ cris_print_operand (file, x, code)
        }
 
       putc (GET_CODE (operand) == SIGN_EXTEND
-           || (GET_CODE (operand) == CONST_INT && INTVAL (operand) < 0)
+           || (CONST_INT_P (operand) && INTVAL (operand) < 0)
            ? 's' : 'u', file);
       return;
 
@@ -1447,12 +936,12 @@ cris_print_operand (file, x, code)
       /* Print the least significant part of operand.  */
       if (GET_CODE (operand) == CONST_DOUBLE)
        {
-         fprintf (file, "0x%x", CONST_DOUBLE_LOW (x));
+         fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (x));
          return;
        }
-      else if (HOST_BITS_PER_WIDE_INT > 32 && GET_CODE (operand) == CONST_INT)
+      else if (HOST_BITS_PER_WIDE_INT > 32 && CONST_INT_P (operand))
        {
-         fprintf (file, "0x%x",
+         fprintf (file, HOST_WIDE_INT_PRINT_HEX,
                   INTVAL (x) & ((unsigned int) 0x7fffffff * 2 + 1));
          return;
        }
@@ -1463,15 +952,30 @@ cris_print_operand (file, x, code)
     case 'A':
       /* When emitting an add for the high part of a DImode constant, we
         want to use addq for 0 and adds.w for -1.  */
-      if (GET_CODE (operand) != CONST_INT)
+      if (!CONST_INT_P (operand))
        LOSE_AND_RETURN ("invalid operand for 'A' modifier", x);
       fprintf (file, INTVAL (operand) < 0 ? "adds.w" : "addq");
       return;
 
+    case 'd':
+      /* If this is a GOT symbol, force it to be emitted as :GOT and
+        :GOTPLT regardless of -fpic (i.e. not as :GOT16, :GOTPLT16).
+        Avoid making this too much of a special case.  */
+      if (flag_pic == 1 && CONSTANT_P (operand))
+       {
+         int flag_pic_save = flag_pic;
+
+         flag_pic = 2;
+         cris_output_addr_const (file, operand);
+         flag_pic = flag_pic_save;
+         return;
+       }
+      break;
+
     case 'D':
       /* When emitting an sub for the high part of a DImode constant, we
         want to use subq for 0 and subs.w for -1.  */
-      if (GET_CODE (operand) != CONST_INT)
+      if (!CONST_INT_P (operand))
        LOSE_AND_RETURN ("invalid operand for 'D' modifier", x);
       fprintf (file, INTVAL (operand) < 0 ? "subs.w" : "subq");
       return;
@@ -1485,11 +989,22 @@ cris_print_operand (file, x, code)
     case 'T':
       /* Print the size letter for an operand to a MULT, which must be a
         const_int with a suitable value.  */
-      if (GET_CODE (operand) != CONST_INT || INTVAL (operand) > 4)
+      if (!CONST_INT_P (operand) || INTVAL (operand) > 4)
        LOSE_AND_RETURN ("invalid operand for 'T' modifier", x);
       fprintf (file, "%s", mults[INTVAL (operand)]);
       return;
 
+    case 'u':
+      /* Print "u.w" if a GOT symbol and flag_pic == 1, else ".d".  */
+      if (flag_pic == 1
+         && GET_CODE (operand) == CONST
+         && GET_CODE (XEXP (operand, 0)) == UNSPEC
+         && XINT (XEXP (operand, 0), 1) == CRIS_UNSPEC_GOTREAD)
+       fprintf (file, "u.w");
+      else
+       fprintf (file, ".d");
+      return;
+
     case 0:
       /* No code, print as usual.  */
       break;
@@ -1502,7 +1017,10 @@ cris_print_operand (file, x, code)
   switch (GET_CODE (operand))
     {
     case REG:
-      if (REGNO (operand) > 15)
+      if (REGNO (operand) > 15
+         && REGNO (operand) != CRIS_MOF_REGNUM
+         && REGNO (operand) != CRIS_SRP_REGNUM
+         && REGNO (operand) != CRIS_CC0_REGNUM)
        internal_error ("internal error: bad register: %d", REGNO (operand));
       fprintf (file, "$%s", reg_names[REGNO (operand)]);
       return;
@@ -1532,9 +1050,7 @@ cris_print_operand (file, x, code)
       return;
 
     case UNSPEC:
-      ASSERT_PLT_UNSPEC (operand);
       /* Fall through.  */
-
     case CONST:
       cris_output_addr_const (file, operand);
       return;
@@ -1543,14 +1059,14 @@ cris_print_operand (file, x, code)
     case ASHIFT:
       {
        /* For a (MULT (reg X) const_int) we output "rX.S".  */
-       int i = GET_CODE (XEXP (operand, 1)) == CONST_INT
+       int i = CONST_INT_P (XEXP (operand, 1))
          ? INTVAL (XEXP (operand, 1)) : INTVAL (XEXP (operand, 0));
-       rtx reg = GET_CODE (XEXP (operand, 1)) == CONST_INT
+       rtx reg = CONST_INT_P (XEXP (operand, 1))
          ? XEXP (operand, 0) : XEXP (operand, 1);
 
-       if (GET_CODE (reg) != REG
-           || (GET_CODE (XEXP (operand, 0)) != CONST_INT
-               && GET_CODE (XEXP (operand, 1)) != CONST_INT))
+       if (!REG_P (reg)
+           || (!CONST_INT_P (XEXP (operand, 0))
+               && !CONST_INT_P (XEXP (operand, 1))))
          LOSE_AND_RETURN ("unexpected multiplicative operand", x);
 
        cris_print_base (reg, file);
@@ -1578,9 +1094,7 @@ cris_print_operand (file, x, code)
 /* The PRINT_OPERAND_ADDRESS worker.  */
 
 void
-cris_print_operand_address (file, x)
-     FILE *file;
-     rtx x;
+cris_print_operand_address (FILE *file, rtx x)
 {
   /* All these were inside MEM:s so output indirection characters.  */
   putc ('[', file);
@@ -1608,7 +1122,7 @@ cris_print_operand_address (file, x)
       else
        LOSE_AND_RETURN ("unrecognized address", x);
     }
-  else if (GET_CODE (x) == MEM)
+  else if (MEM_P (x))
     {
       /* A DIP.  Output more indirection characters.  */
       putc ('[', file);
@@ -1630,9 +1144,7 @@ cris_print_operand_address (file, x)
    initial-value machinery.  */
 
 rtx
-cris_return_addr_rtx (count, frameaddr)
-     int count;
-     rtx frameaddr ATTRIBUTE_UNUSED;
+cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED)
 {
   cfun->machine->needs_return_address_on_stack = 1;
 
@@ -1644,41 +1156,57 @@ cris_return_addr_rtx (count, frameaddr)
     : NULL_RTX;
 }
 
+/* Accessor used in cris.md:return because cfun->machine isn't available
+   there.  */
+
+bool
+cris_return_address_on_stack (void)
+{
+  return df_regs_ever_live_p (CRIS_SRP_REGNUM)
+    || cfun->machine->needs_return_address_on_stack;
+}
+
+/* Accessor used in cris.md:return because cfun->machine isn't available
+   there.  */
+
+bool
+cris_return_address_on_stack_for_return (void)
+{
+  return cfun->machine->return_type == CRIS_RETINSN_RET ? false
+    : cris_return_address_on_stack ();
+}
+
 /* This used to be the INITIAL_FRAME_POINTER_OFFSET worker; now only
    handles FP -> SP elimination offset.  */
 
 static int
-cris_initial_frame_pointer_offset ()
+cris_initial_frame_pointer_offset (void)
 {
   int regno;
 
   /* Initial offset is 0 if we don't have a frame pointer.  */
   int offs = 0;
+  bool got_really_used = false;
+
+  if (crtl->uses_pic_offset_table)
+    {
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (),
+                             NULL_RTX);
+      pop_topmost_sequence ();
+    }
 
   /* And 4 for each register pushed.  */
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-    if ((((regs_ever_live[regno]
-          && !call_used_regs[regno])
-         || (regno == (int) PIC_OFFSET_TABLE_REGNUM
-             && (current_function_uses_pic_offset_table
-                 /* It is saved anyway, if there would be a gap.  */
-                 || (flag_pic
-                     && regs_ever_live[regno + 1]
-                     && !call_used_regs[regno + 1]))))
-        && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
-        && regno != CRIS_SRP_REGNUM)
-       || (current_function_calls_eh_return
-           && (regno == EH_RETURN_DATA_REGNO (0)
-               || regno == EH_RETURN_DATA_REGNO (1)
-               || regno == EH_RETURN_DATA_REGNO (2)
-               || regno == EH_RETURN_DATA_REGNO (3))))
+    if (cris_reg_saved_in_regsave_area (regno, got_really_used))
       offs += 4;
 
   /* And then, last, we add the locals allocated.  */
   offs += get_frame_size ();
 
   /* And more; the accumulated args size.  */
-  offs += current_function_outgoing_args_size;
+  offs += crtl->outgoing_args_size;
 
   /* Then round it off, in case we use aligned stack.  */
   if (TARGET_STACK_ALIGN)
@@ -1693,20 +1221,16 @@ cris_initial_frame_pointer_offset ()
    and imaginary arg pointer.  */
 
 int
-cris_initial_elimination_offset (fromreg, toreg)
-     int fromreg;
-     int toreg;
+cris_initial_elimination_offset (int fromreg, int toreg)
 {
   int fp_sp_offset
     = cris_initial_frame_pointer_offset ();
 
   /* We should be able to use regs_ever_live and related prologue
      information here, or alpha should not as well.  */
-  int return_address_on_stack
-    = regs_ever_live[CRIS_SRP_REGNUM]
-    || cfun->machine->needs_return_address_on_stack != 0;
+  bool return_address_on_stack = cris_return_address_on_stack ();
 
-  /* Here we act as if the frame-pointer is needed.  */
+  /* Here we act as if the frame-pointer were needed.  */
   int ap_fp_offset = 4 + (return_address_on_stack ? 4 : 0);
 
   if (fromreg == ARG_POINTER_REGNUM
@@ -1724,366 +1248,636 @@ cris_initial_elimination_offset (fromreg, toreg)
       && toreg == STACK_POINTER_REGNUM)
     return ap_fp_offset + fp_sp_offset - 4;
 
-  abort ();
+  gcc_unreachable ();
 }
 
-/*  This function looks into the pattern to see how this insn affects
-    condition codes.
+/* Worker function for LEGITIMIZE_RELOAD_ADDRESS.  */
 
-    Used when to eliminate test insns before a condition-code user,
-    such as a "scc" insn or a conditional branch.  This includes
-    checking if the entities that cc was updated by, are changed by the
-    operation.
+bool
+cris_reload_address_legitimized (rtx x,
+                                enum machine_mode mode ATTRIBUTE_UNUSED,
+                                int opnum ATTRIBUTE_UNUSED,
+                                int itype,
+                                int ind_levels ATTRIBUTE_UNUSED)
+{
+  enum reload_type type = itype;
+  rtx op0, op1;
+  rtx *op0p;
+  rtx *op1p;
 
-    Currently a jumble of the old peek-inside-the-insn and the newer
-    check-cc-attribute methods.  */
+  if (GET_CODE (x) != PLUS)
+    return false;
 
-void
-cris_notice_update_cc (exp, insn)
-     rtx exp;
-     rtx insn;
-{
-  /* Check if user specified "-mcc-init" as a bug-workaround.  FIXME:
-     TARGET_CCINIT does not work; we must set CC_REVERSED as below.
-     Several test-cases will otherwise fail, for example
-     gcc.c-torture/execute/20000217-1.c -O0 and -O1.  */
-  if (TARGET_CCINIT)
-    {
-      CC_STATUS_INIT;
-      return;
-    }
+  if (TARGET_V32)
+    return false;
 
-  /* Slowly, we're converting to using attributes to control the setting
-     of condition-code status.  */
-  switch (get_attr_cc (insn))
+  op0 = XEXP (x, 0);
+  op0p = &XEXP (x, 0);
+  op1 = XEXP (x, 1);
+  op1p = &XEXP (x, 1);
+
+  if (!REG_P (op1))
+    return false;
+
+  if (GET_CODE (op0) == SIGN_EXTEND && MEM_P (XEXP (op0, 0)))
     {
-    case CC_NONE:
-      /* Even if it is "none", a setting may clobber a previous
-        cc-value, so check.  */
-      if (GET_CODE (exp) == SET)
+      rtx op00 = XEXP (op0, 0);
+      rtx op000 = XEXP (op00, 0);
+      rtx *op000p = &XEXP (op00, 0);
+
+      if ((GET_MODE (op00) == HImode || GET_MODE (op00) == QImode)
+         && (REG_P (op000)
+             || (GET_CODE (op000) == POST_INC && REG_P (XEXP (op000, 0)))))
        {
-         if (cc_status.value1
-             && cris_reg_overlap_mentioned_p (SET_DEST (exp),
-                                            cc_status.value1))
-           cc_status.value1 = 0;
+         bool something_reloaded = false;
+
+         if (GET_CODE (op000) == POST_INC
+             && REG_P (XEXP (op000, 0))
+             && REGNO (XEXP (op000, 0)) > CRIS_LAST_GENERAL_REGISTER)
+           /* No, this gets too complicated and is too rare to care
+              about trying to improve on the general code Here.
+              As the return-value is an all-or-nothing indicator, we
+              punt on the other register too.  */
+           return false;
+
+         if ((REG_P (op000)
+              && REGNO (op000) > CRIS_LAST_GENERAL_REGISTER))
+           {
+             /* The address of the inner mem is a pseudo or wrong
+                reg: reload that.  */
+             push_reload (op000, NULL_RTX, op000p, NULL, GENERAL_REGS,
+                          GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+             something_reloaded = true;
+           }
 
-         if (cc_status.value2
-             && cris_reg_overlap_mentioned_p (SET_DEST (exp),
-                                            cc_status.value2))
-           cc_status.value2 = 0;
+         if (REGNO (op1) > CRIS_LAST_GENERAL_REGISTER)
+           {
+             /* Base register is a pseudo or wrong reg: reload it.  */
+             push_reload (op1, NULL_RTX, op1p, NULL, GENERAL_REGS,
+                          GET_MODE (x), VOIDmode, 0, 0,
+                          opnum, type);
+             something_reloaded = true;
+           }
+
+         gcc_assert (something_reloaded);
+
+         return true;
        }
-      return;
+    }
 
-    case CC_CLOBBER:
-      CC_STATUS_INIT;
-      break;
+  return false;
+}
 
-    case CC_NORMAL:
-      /* Which means, for:
-        (set (cc0) (...)):
-        CC is (...).
+/* Worker function for REGISTER_MOVE_COST.  */
+
+int
+cris_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                        enum reg_class from, enum reg_class to) 
+{
+  if (!TARGET_V32)
+    {
+      /* Pretend that classes that we don't support are ALL_REGS, so
+        we give them the highest cost.  */
+      if (from != SPECIAL_REGS && from != MOF_REGS
+         && from != GENERAL_REGS && from != GENNONACR_REGS)
+       from = ALL_REGS;
+
+      if (to != SPECIAL_REGS && to != MOF_REGS
+         && to != GENERAL_REGS && to != GENNONACR_REGS)
+       to = ALL_REGS;
+    }
+
+  /* Can't move to and from a SPECIAL_REGS register, so we have to say
+     their move cost within that class is higher.  How about 7?  That's 3
+     for a move to a GENERAL_REGS register, 3 for the move from the
+     GENERAL_REGS register, and 1 for the increased register pressure.
+     Also, it's higher than the memory move cost, which is in order.  
+     We also do this for ALL_REGS, since we don't want that class to be
+     preferred (even to memory) at all where GENERAL_REGS doesn't fit.
+     Whenever it's about to be used, it's for SPECIAL_REGS.  If we don't
+     present a higher cost for ALL_REGS than memory, a SPECIAL_REGS may be
+     used when a GENERAL_REGS should be used, even if there are call-saved
+     GENERAL_REGS left to allocate.  This is because the fall-back when
+     the most preferred register class isn't available, isn't the next
+     (or next good) wider register class, but the *most widest* register
+     class.  */
+
+  if ((reg_classes_intersect_p (from, SPECIAL_REGS)
+       && reg_classes_intersect_p (to, SPECIAL_REGS))
+      || from == ALL_REGS || to == ALL_REGS)
+    return 7;
+
+  if (reg_classes_intersect_p (from, SPECIAL_REGS)
+      || reg_classes_intersect_p (to, SPECIAL_REGS))
+    return 3;
+
+  return 2;
+}
+
+/* Worker for cris_notice_update_cc; handles the "normal" cases.
+   FIXME: this code is historical; its functionality should be
+   refactored to look at insn attributes and moved to
+   cris_notice_update_cc.  Except, we better lose cc0 entirely.  */
 
-        (set (reg) (...)):
-        CC is (reg) and (...) - unless (...) is 0, then CC does not change.
-        CC_NO_OVERFLOW unless (...) is reg or mem.
+static void
+cris_normal_notice_update_cc (rtx exp, rtx insn)
+{
+  /* "Normal" means, for:
+     (set (cc0) (...)):
+     CC is (...).
 
-        (set (mem) (...)):
-        CC does not change.
+     (set (reg) (...)):
+     CC is (reg) and (...) - unless (...) is 0 or reg is a special
+        register or (v32 and (...) is -32..-1), then CC does not change.
+     CC_NO_OVERFLOW unless (...) is reg or mem.
 
-        (set (pc) (...)):
-        CC does not change.
+     (set (mem) (...)):
+     CC does not change.
 
-        (parallel
-         (set (reg1) (mem (bdap/biap)))
-         (set (reg2) (bdap/biap))):
-        CC is (reg1) and (mem (reg2))
+     (set (pc) (...)):
+     CC does not change.
 
-        (parallel
-         (set (mem (bdap/biap)) (reg1)) [or 0]
-         (set (reg2) (bdap/biap))):
-        CC does not change.
+     (parallel
+      (set (reg1) (mem (bdap/biap)))
+      (set (reg2) (bdap/biap))):
+     CC is (reg1) and (mem (reg2))
 
-        (where reg and mem includes strict_low_parts variants thereof)
+     (parallel
+      (set (mem (bdap/biap)) (reg1)) [or 0]
+      (set (reg2) (bdap/biap))):
+     CC does not change.
 
-        For all others, assume CC is clobbered.
-        Note that we do not have to care about setting CC_NO_OVERFLOW,
-        since the overflow flag is set to 0 (i.e. right) for
-        instructions where it does not have any sane sense, but where
-        other flags have meanings.  (This includes shifts; the carry is
-        not set by them).
+     (where reg and mem includes strict_low_parts variants thereof)
 
-        Note that there are other parallel constructs we could match,
-        but we don't do that yet.  */
+     For all others, assume CC is clobbered.
+     Note that we do not have to care about setting CC_NO_OVERFLOW,
+     since the overflow flag is set to 0 (i.e. right) for
+     instructions where it does not have any sane sense, but where
+     other flags have meanings.  (This includes shifts; the carry is
+     not set by them).
 
-      if (GET_CODE (exp) == SET)
+     Note that there are other parallel constructs we could match,
+     but we don't do that yet.  */
+
+  if (GET_CODE (exp) == SET)
+    {
+      /* FIXME: Check when this happens.  It looks like we should
+        actually do a CC_STATUS_INIT here to be safe.  */
+      if (SET_DEST (exp) == pc_rtx)
+       return;
+
+      /* Record CC0 changes, so we do not have to output multiple
+        test insns.  */
+      if (SET_DEST (exp) == cc0_rtx)
        {
-         /* FIXME: Check when this happens.  It looks like we should
-            actually do a CC_STATUS_INIT here to be safe.  */
-         if (SET_DEST (exp) == pc_rtx)
-           return;
+         CC_STATUS_INIT;
+         cc_status.value1 = SET_SRC (exp);
 
-         /* Record CC0 changes, so we do not have to output multiple
-            test insns.  */
-         if (SET_DEST (exp) == cc0_rtx)
+         /* Handle flags for the special btstq on one bit.  */
+         if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
+             && XEXP (SET_SRC (exp), 1) == const1_rtx)
            {
-             cc_status.value1 = SET_SRC (exp);
-             cc_status.value2 = 0;
-
-             /* Handle flags for the special btstq on one bit.  */
-             if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
-                 && XEXP (SET_SRC (exp), 1) == const1_rtx)
-               {
-                 if (GET_CODE (XEXP (SET_SRC (exp), 0)) == CONST_INT)
-                   /* Using cmpq.  */
-                   cc_status.flags = CC_INVERTED;
-                 else
-                   /* A one-bit btstq.  */
-                   cc_status.flags = CC_Z_IN_NOT_N;
-               }
+             if (CONST_INT_P (XEXP (SET_SRC (exp), 0)))
+               /* Using cmpq.  */
+               cc_status.flags = CC_INVERTED;
              else
-               cc_status.flags = 0;
-
-             if (GET_CODE (SET_SRC (exp)) == COMPARE)
-               {
-                 if (!REG_P (XEXP (SET_SRC (exp), 0))
-                     && XEXP (SET_SRC (exp), 1) != const0_rtx)
-                   /* For some reason gcc will not canonicalize compare
-                      operations, reversing the sign by itself if
-                      operands are in wrong order.  */
-                   /* (But NOT inverted; eq is still eq.) */
-                   cc_status.flags = CC_REVERSED;
-
-                 /* This seems to be overlooked by gcc.  FIXME: Check again.
-                    FIXME:  Is it really safe?  */
-                 cc_status.value2
-                   = gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
-                                    XEXP (SET_SRC (exp), 0),
-                                    XEXP (SET_SRC (exp), 1));
-               }
-             return;
+               /* A one-bit btstq.  */
+               cc_status.flags = CC_Z_IN_NOT_N;
            }
-         else if (REG_P (SET_DEST (exp))
-                  || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
-                      && REG_P (XEXP (SET_DEST (exp), 0))))
+
+         if (GET_CODE (SET_SRC (exp)) == COMPARE)
            {
-             /* A register is set; normally CC is set to show that no
-                test insn is needed.  Catch the exceptions.  */
+             if (!REG_P (XEXP (SET_SRC (exp), 0))
+                 && XEXP (SET_SRC (exp), 1) != const0_rtx)
+               /* For some reason gcc will not canonicalize compare
+                  operations, reversing the sign by itself if
+                  operands are in wrong order.  */
+               /* (But NOT inverted; eq is still eq.) */
+               cc_status.flags = CC_REVERSED;
+
+             /* This seems to be overlooked by gcc.  FIXME: Check again.
+                FIXME:  Is it really safe?  */
+             cc_status.value2
+               = gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
+                                XEXP (SET_SRC (exp), 0),
+                                XEXP (SET_SRC (exp), 1));
+           }
+         return;
+       }
+      else if (REG_P (SET_DEST (exp))
+              || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
+                  && REG_P (XEXP (SET_DEST (exp), 0))))
+       {
+         /* A register is set; normally CC is set to show that no
+            test insn is needed.  Catch the exceptions.  */
 
-             /* If not to cc0, then no "set"s in non-natural mode give
-                ok cc0...  */
-             if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
-                 || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
+         /* If not to cc0, then no "set"s in non-natural mode give
+            ok cc0...  */
+         if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
+             || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
+           {
+             /* ... except add:s and sub:s in DImode.  */
+             if (GET_MODE (SET_DEST (exp)) == DImode
+                 && (GET_CODE (SET_SRC (exp)) == PLUS
+                     || GET_CODE (SET_SRC (exp)) == MINUS))
                {
-                 /* ... except add:s and sub:s in DImode.  */
-                 if (GET_MODE (SET_DEST (exp)) == DImode
-                     && (GET_CODE (SET_SRC (exp)) == PLUS
-                         || GET_CODE (SET_SRC (exp)) == MINUS))
-                   {
-                     cc_status.flags = 0;
-                     cc_status.value1 = SET_DEST (exp);
-                     cc_status.value2 = SET_SRC (exp);
+                 CC_STATUS_INIT;
+                 cc_status.value1 = SET_DEST (exp);
+                 cc_status.value2 = SET_SRC (exp);
 
-                     if (cris_reg_overlap_mentioned_p (cc_status.value1,
-                                                       cc_status.value2))
-                       cc_status.value2 = 0;
+                 if (cris_reg_overlap_mentioned_p (cc_status.value1,
+                                                   cc_status.value2))
+                   cc_status.value2 = 0;
 
-                     /* Add and sub may set V, which gets us
-                        unoptimizable results in "gt" and "le" condition
-                        codes.  */
-                     cc_status.flags |= CC_NO_OVERFLOW;
-
-                     return;
-                   }
-               }
-             else if (SET_SRC (exp) == const0_rtx)
-               {
-                 /* There's no CC0 change when clearing a register or
-                    memory.  Just check for overlap.  */
-                 if ((cc_status.value1
-                      && cris_reg_overlap_mentioned_p (SET_DEST (exp),
-                                                       cc_status.value1)))
-                   cc_status.value1 = 0;
-
-                 if ((cc_status.value2
-                      && cris_reg_overlap_mentioned_p (SET_DEST (exp),
-                                                       cc_status.value2)))
-                   cc_status.value2 = 0;
-
-                 return;
-               }
-             else
-               {
-                 cc_status.flags = 0;
-                 cc_status.value1 = SET_DEST (exp);
-                 cc_status.value2 = SET_SRC (exp);
-
-                 if (cris_reg_overlap_mentioned_p (cc_status.value1,
-                                                   cc_status.value2))
-                   cc_status.value2 = 0;
-
-                 /* Some operations may set V, which gets us
+                 /* Add and sub may set V, which gets us
                     unoptimizable results in "gt" and "le" condition
                     codes.  */
-                 if (GET_CODE (SET_SRC (exp)) == PLUS
-                     || GET_CODE (SET_SRC (exp)) == MINUS
-                     || GET_CODE (SET_SRC (exp)) == NEG)
-                   cc_status.flags |= CC_NO_OVERFLOW;
+                 cc_status.flags |= CC_NO_OVERFLOW;
 
                  return;
                }
            }
-         else if (GET_CODE (SET_DEST (exp)) == MEM
-                  || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
-                      && GET_CODE (XEXP (SET_DEST (exp), 0)) == MEM))
+         else if (SET_SRC (exp) == const0_rtx
+                  || (REG_P (SET_SRC (exp))
+                      && (REGNO (SET_SRC (exp))
+                          > CRIS_LAST_GENERAL_REGISTER))
+                  || (TARGET_V32
+                      && GET_CODE (SET_SRC (exp)) == CONST_INT
+                      && CRIS_CONST_OK_FOR_LETTER_P (INTVAL (SET_SRC (exp)),
+                                                     'I')))
            {
-             /* When SET to MEM, then CC is not changed (except for
-                overlap).  */
-             if ((cc_status.value1
-                  && cris_reg_overlap_mentioned_p (SET_DEST (exp),
-                                                   cc_status.value1)))
+             /* There's no CC0 change for this case.  Just check
+                for overlap.  */
+             if (cc_status.value1
+                 && modified_in_p (cc_status.value1, insn))
                cc_status.value1 = 0;
 
-             if ((cc_status.value2
-                  && cris_reg_overlap_mentioned_p (SET_DEST (exp),
-                                                   cc_status.value2)))
+             if (cc_status.value2
+                 && modified_in_p (cc_status.value2, insn))
+               cc_status.value2 = 0;
+
+             return;
+           }
+         else
+           {
+             CC_STATUS_INIT;
+             cc_status.value1 = SET_DEST (exp);
+             cc_status.value2 = SET_SRC (exp);
+
+             if (cris_reg_overlap_mentioned_p (cc_status.value1,
+                                               cc_status.value2))
                cc_status.value2 = 0;
 
+             /* Some operations may set V, which gets us
+                unoptimizable results in "gt" and "le" condition
+                codes.  */
+             if (GET_CODE (SET_SRC (exp)) == PLUS
+                 || GET_CODE (SET_SRC (exp)) == MINUS
+                 || GET_CODE (SET_SRC (exp)) == NEG)
+               cc_status.flags |= CC_NO_OVERFLOW;
+
+             /* For V32, nothing with a register destination sets
+                C and V usefully.  */
+             if (TARGET_V32)
+               cc_status.flags |= CC_NO_OVERFLOW;
+
              return;
            }
        }
-      else if (GET_CODE (exp) == PARALLEL)
+      else if (MEM_P (SET_DEST (exp))
+              || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
+                  && MEM_P (XEXP (SET_DEST (exp), 0))))
+       {
+         /* When SET to MEM, then CC is not changed (except for
+            overlap).  */
+         if (cc_status.value1
+             && modified_in_p (cc_status.value1, insn))
+           cc_status.value1 = 0;
+
+         if (cc_status.value2
+             && modified_in_p (cc_status.value2, insn))
+           cc_status.value2 = 0;
+
+         return;
+       }
+    }
+  else if (GET_CODE (exp) == PARALLEL)
+    {
+      if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
+         && GET_CODE (XVECEXP (exp, 0, 1)) == SET
+         && REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
        {
-         if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
-             && GET_CODE (XVECEXP (exp, 0, 1)) == SET
-             && REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
+         if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
+             && MEM_P (XEXP (XVECEXP (exp, 0, 0), 1)))
            {
-             if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
-                 && GET_CODE (XEXP (XVECEXP (exp, 0, 0), 1)) == MEM)
-               {
-                 /* For "move.S [rx=ry+o],rz", say CC reflects
-                    value1=rz and value2=[rx] */
-                 cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
-                 cc_status.value2
-                   = gen_rtx_MEM (GET_MODE (XEXP (XVECEXP (exp, 0, 0), 0)),
-                                  XEXP (XVECEXP (exp, 0, 1), 0));
-                 cc_status.flags = 0;
-
-                 /* Huh?  A side-effect cannot change the destination
-                    register.  */
-                 if (cris_reg_overlap_mentioned_p (cc_status.value1,
-                                                   cc_status.value2))
-                   internal_error ("internal error: sideeffect-insn affecting main effect");
-                 return;
-               }
-             else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
-                       || XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
-                      && GET_CODE (XEXP (XVECEXP (exp, 0, 0), 0)) == MEM)
-               {
-                 /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]",
-                    say flags are not changed, except for overlap.  */
-                 if (cc_status.value1
-                     && cris_reg_overlap_mentioned_p (XEXP
-                                                      (XVECEXP
-                                                       (exp, 0, 0), 0),
-                                                      cc_status.value1))
-                   cc_status.value1 = 0;
-
-                 if (cc_status.value1
-                     && cris_reg_overlap_mentioned_p (XEXP
-                                                      (XVECEXP
-                                                       (exp, 0, 1), 0),
-                                                      cc_status.value1))
-                   cc_status.value1 = 0;
-
-                 if (cc_status.value2
-                     && cris_reg_overlap_mentioned_p (XEXP
-                                                      (XVECEXP
-                                                       (exp, 0, 0), 0),
-                                                      cc_status.value2))
-                   cc_status.value2 = 0;
+             CC_STATUS_INIT;
+
+             /* For "move.S [rx=ry+o],rz", say CC reflects
+                value1=rz and value2=[rx] */
+             cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
+             cc_status.value2
+               = replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
+                                        XEXP (XVECEXP (exp, 0, 1), 0));
+
+             /* Huh?  A side-effect cannot change the destination
+                register.  */
+             if (cris_reg_overlap_mentioned_p (cc_status.value1,
+                                               cc_status.value2))
+               internal_error ("internal error: sideeffect-insn affecting main effect");
+
+             /* For V32, moves to registers don't set C and V.  */
+             if (TARGET_V32)
+               cc_status.flags |= CC_NO_OVERFLOW;
+             return;
+           }
+         else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
+                   || XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
+                  && MEM_P (XEXP (XVECEXP (exp, 0, 0), 0)))
+           {
+             /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]",
+                say flags are not changed, except for overlap.  */
+             if (cc_status.value1
+                 && modified_in_p (cc_status.value1, insn))
+               cc_status.value1 = 0;
 
-                 if (cc_status.value2
-                     && cris_reg_overlap_mentioned_p (XEXP
-                                                      (XVECEXP
-                                                       (exp, 0, 1), 0),
-                                                      cc_status.value2))
-                   cc_status.value2 = 0;
+             if (cc_status.value2
+                 && modified_in_p (cc_status.value2, insn))
+               cc_status.value2 = 0;
 
-                 return;
-               }
+             return;
            }
        }
-      break;
+    }
+
+  /* If we got here, the case wasn't covered by the code above.  */
+  CC_STATUS_INIT;
+}
+
+/*  This function looks into the pattern to see how this insn affects
+    condition codes.
+
+    Used when to eliminate test insns before a condition-code user,
+    such as a "scc" insn or a conditional branch.  This includes
+    checking if the entities that cc was updated by, are changed by the
+    operation.
+
+    Currently a jumble of the old peek-inside-the-insn and the newer
+    check-cc-attribute methods.  */
+
+void
+cris_notice_update_cc (rtx exp, rtx insn)
+{
+  enum attr_cc attrval = get_attr_cc (insn);
+
+  /* Check if user specified "-mcc-init" as a bug-workaround.  Remember
+     to still set CC_REVERSED as below, since that's required by some
+     compare insn alternatives.  (FIXME: GCC should do this virtual
+     operand swap by itself.)  A test-case that may otherwise fail is
+     gcc.c-torture/execute/20000217-1.c -O0 and -O1.  */
+  if (TARGET_CCINIT)
+    {
+      CC_STATUS_INIT;
+
+      if (attrval == CC_REV)
+       cc_status.flags = CC_REVERSED;
+      return;
+    }
+
+  /* Slowly, we're converting to using attributes to control the setting
+     of condition-code status.  */
+  switch (attrval)
+    {
+    case CC_NONE:
+      /* Even if it is "none", a setting may clobber a previous
+        cc-value, so check.  */
+      if (GET_CODE (exp) == SET)
+       {
+         if (cc_status.value1
+             && modified_in_p (cc_status.value1, insn))
+           cc_status.value1 = 0;
+
+         if (cc_status.value2
+             && modified_in_p (cc_status.value2, insn))
+           cc_status.value2 = 0;
+       }
+      return;
+
+    case CC_CLOBBER:
+      CC_STATUS_INIT;
+      return;
+
+    case CC_REV:
+    case CC_NOOV32:
+    case CC_NORMAL:
+      cris_normal_notice_update_cc (exp, insn);
+
+      /* The "test" insn doesn't clear (carry and) overflow on V32.  We
+        can change bge => bpl and blt => bmi by passing on to the cc0
+        user that V should not be considered; bgt and ble are taken
+        care of by other methods (see {tst,cmp}{si,hi,qi}).  */
+      if (attrval == CC_NOOV32 && TARGET_V32)
+       cc_status.flags |= CC_NO_OVERFLOW;
+      return;
 
     default:
-      /* Unknown cc_attr value.  */
-      abort ();
+      internal_error ("unknown cc_attr value");
     }
 
   CC_STATUS_INIT;
 }
 
 /* Return != 0 if the return sequence for the current function is short,
-   like "ret" or "jump [sp+]".  Prior to reloading, we can't tell how
-   many registers must be saved, so return 0 then.  */
+   like "ret" or "jump [sp+]".  Prior to reloading, we can't tell if
+   registers must be saved, so return 0 then.  */
 
-int
-cris_simple_epilogue ()
+bool
+cris_simple_epilogue (void)
 {
-  int regno;
-  int reglimit = STACK_POINTER_REGNUM;
-  int lastreg = -1;
+  unsigned int regno;
+  unsigned int reglimit = STACK_POINTER_REGNUM;
+  bool got_really_used = false;
 
   if (! reload_completed
       || frame_pointer_needed
       || get_frame_size () != 0
-      || current_function_pretend_args_size
-      || current_function_args_size
-      || current_function_outgoing_args_size
-      || current_function_calls_eh_return
-
-      /* Kludge for 3.1: when reorg changes branches to the return label
-        into return insns, it does not handle the case where there's a
-        delay list for the epilogue: it just drops the insns in
-        current_function_epilogue_delay_list on the floor, resulting in
-        invalid code.  We kludge around it in that case by saying that
-        we don't have a simple enough epilogue to use return insns.  */
-      || current_function_epilogue_delay_list != NULL
+      || crtl->args.pretend_args_size
+      || crtl->args.size
+      || crtl->outgoing_args_size
+      || crtl->calls_eh_return
 
       /* If we're not supposed to emit prologue and epilogue, we must
         not emit return-type instructions.  */
       || !TARGET_PROLOGUE_EPILOGUE)
-    return 0;
+    return false;
+
+  /* Can't return from stacked return address with v32.  */
+  if (TARGET_V32 && cris_return_address_on_stack ())
+    return false;
+
+  if (crtl->uses_pic_offset_table)
+    {
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
+      pop_topmost_sequence ();
+    }
 
-  /* We allow a "movem [sp+],rN" to sit in front if the "jump [sp+]" or
-     in the delay-slot of the "ret".  */
+  /* No simple epilogue if there are saved registers.  */
   for (regno = 0; regno < reglimit; regno++)
-    if ((regs_ever_live[regno] && ! call_used_regs[regno])
-       || (regno == (int) PIC_OFFSET_TABLE_REGNUM
-           && (current_function_uses_pic_offset_table
-               /* It is saved anyway, if there would be a gap.  */
-               || (flag_pic
-                   && regs_ever_live[regno + 1]
-                   && !call_used_regs[regno + 1]))))
+    if (cris_reg_saved_in_regsave_area (regno, got_really_used))
+      return false;
+
+  return true;
+}
+
+/* Expand a return insn (just one insn) marked as using SRP or stack
+   slot depending on parameter ON_STACK.  */
+
+void
+cris_expand_return (bool on_stack)
+{
+  /* FIXME: emit a parallel with a USE for SRP or the stack-slot, to
+     tell "ret" from "jump [sp+]".  Some, but not all, other parts of
+     GCC expect just (return) to do the right thing when optimizing, so
+     we do that until they're fixed.  Currently, all return insns in a
+     function must be the same (not really a limiting factor) so we need
+     to check that it doesn't change half-way through.  */
+  emit_jump_insn (gen_rtx_RETURN (VOIDmode));
+
+  CRIS_ASSERT (cfun->machine->return_type != CRIS_RETINSN_RET || !on_stack);
+  CRIS_ASSERT (cfun->machine->return_type != CRIS_RETINSN_JUMP || on_stack);
+
+  cfun->machine->return_type
+    = on_stack ? CRIS_RETINSN_JUMP : CRIS_RETINSN_RET;
+}
+
+/* Compute a (partial) cost for rtx X.  Return true if the complete
+   cost has been computed, and false if subexpressions should be
+   scanned.  In either case, *TOTAL contains the cost result.  */
+
+static bool
+cris_rtx_costs (rtx x, int code, int outer_code, int *total,
+               bool speed)
+{
+  switch (code)
+    {
+    case CONST_INT:
       {
-       if (lastreg != regno - 1)
-         return 0;
-       lastreg = regno;
+       HOST_WIDE_INT val = INTVAL (x);
+       if (val == 0)
+         *total = 0;
+       else if (val < 32 && val >= -32)
+         *total = 1;
+       /* Eight or 16 bits are a word and cycle more expensive.  */
+       else if (val <= 32767 && val >= -32768)
+         *total = 2;
+       /* A 32-bit constant (or very seldom, unsigned 16 bits) costs
+          another word.  FIXME: This isn't linear to 16 bits.  */
+       else
+         *total = 4;
+       return true;
       }
 
-  return 1;
+    case LABEL_REF:
+      *total = 6;
+      return true;
+
+    case CONST:
+    case SYMBOL_REF:
+      *total = 6;
+      return true;
+
+    case CONST_DOUBLE:
+      if (x != CONST0_RTX (GET_MODE (x) == VOIDmode ? DImode : GET_MODE (x)))
+       *total = 12;
+      else
+        /* Make 0.0 cheap, else test-insns will not be used.  */
+       *total = 0;
+      return true;
+
+    case MULT:
+      /* If we have one arm of an ADDI, make sure it gets the cost of
+        one insn, i.e. zero cost for this operand, and just the cost
+        of the PLUS, as the insn is created by combine from a PLUS
+        and an ASHIFT, and the MULT cost below would make the
+        combined value be larger than the separate insns.  The insn
+        validity is checked elsewhere by combine.
+
+        FIXME: this case is a stop-gap for 4.3 and 4.4, this whole
+        function should be rewritten.  */
+      if (outer_code == PLUS && BIAP_INDEX_P (x))
+       {
+         *total = 0;
+         return true;
+       }
+
+      /* Identify values that are no powers of two.  Powers of 2 are
+         taken care of already and those values should not be changed.  */
+      if (!CONST_INT_P (XEXP (x, 1))
+          || exact_log2 (INTVAL (XEXP (x, 1)) < 0))
+       {
+         /* If we have a multiply insn, then the cost is between
+            1 and 2 "fast" instructions.  */
+         if (TARGET_HAS_MUL_INSNS)
+           {
+             *total = COSTS_N_INSNS (1) + COSTS_N_INSNS (1) / 2;
+             return true;
+           }
+
+         /* Estimate as 4 + 4 * #ofbits.  */
+         *total = COSTS_N_INSNS (132);
+         return true;
+       }
+      return false;
+
+    case UDIV:
+    case MOD:
+    case UMOD:
+    case DIV:
+      if (!CONST_INT_P (XEXP (x, 1))
+          || exact_log2 (INTVAL (XEXP (x, 1)) < 0))
+       {
+         /* Estimate this as 4 + 8 * #of bits.  */
+         *total = COSTS_N_INSNS (260);
+         return true;
+       }
+      return false;
+
+    case AND:
+      if (CONST_INT_P (XEXP (x, 1))
+          /* Two constants may actually happen before optimization.  */
+          && !CONST_INT_P (XEXP (x, 0))
+          && !CRIS_CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'I'))
+       {
+         *total = (rtx_cost (XEXP (x, 0), outer_code, speed) + 2
+                   + 2 * GET_MODE_NUNITS (GET_MODE (XEXP (x, 0))));
+         return true;
+       }
+      return false;
+
+    case ZERO_EXTEND: case SIGN_EXTEND:
+      *total = rtx_cost (XEXP (x, 0), outer_code, speed);
+      return true;
+
+    default:
+      return false;
+    }
 }
 
 /* The ADDRESS_COST worker.  */
 
-int
-cris_address_cost (x)
-     rtx x;
+static int
+cris_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
 {
   /* The metric to use for the cost-macros is unclear.
      The metric used here is (the number of cycles needed) / 2,
      where we consider equal a cycle for a word of code and a cycle to
-     read memory.  */
+     read memory.  FIXME: Adding "+ 1" to all values would avoid
+     returning 0, as tree-ssa-loop-ivopts.c as of r128272 "normalizes"
+     0 to 1, thereby giving equal costs to [rN + rM] and [rN].
+     Unfortunately(?) such a hack would expose other pessimizations,
+     at least with g++.dg/tree-ssa/ivopts-1.C, adding insns to the
+     loop there, without apparent reason.  */
 
   /* The cheapest addressing modes get 0, since nothing extra is needed.  */
   if (BASE_OR_AUTOINCR_P (x))
@@ -2091,16 +1885,13 @@ cris_address_cost (x)
 
   /* An indirect mem must be a DIP.  This means two bytes extra for code,
      and 4 bytes extra for memory read, i.e.  (2 + 4) / 2.  */
-  if (GET_CODE (x) == MEM)
+  if (MEM_P (x))
     return (2 + 4) / 2;
 
   /* Assume (2 + 4) / 2 for a single constant; a dword, since it needs
-     an extra DIP prefix and 4 bytes of constant in most cases.
-     For PIC and a symbol with a GOT entry, we double the cost since we
-     add a [rPIC+...] offset.  A GOT-less symbol uses a BDAP prefix
-     equivalent to the DIP prefix for non-PIC, hence the same cost.  */
+     an extra DIP prefix and 4 bytes of constant in most cases.  */
   if (CONSTANT_P (x))
-    return flag_pic && cris_got_symbol (x) ? 2 * (2 + 4) / 2 : (2 + 4) / 2;
+    return (2 + 4) / 2;
 
   /* Handle BIAP and BDAP prefixes.  */
   if (GET_CODE (x) == PLUS)
@@ -2108,36 +1899,40 @@ cris_address_cost (x)
       rtx tem1 = XEXP (x, 0);
       rtx tem2 = XEXP (x, 1);
 
-    /* A BIAP is 2 extra bytes for the prefix insn, nothing more.  We
-       recognize the typical MULT which is always in tem1 because of
-       insn canonicalization.  */
-    if ((GET_CODE (tem1) == MULT && BIAP_INDEX_P (tem1))
-       || REG_P (tem1))
-      return 2 / 2;
-
-    /* A BDAP (quick) is 2 extra bytes.  Any constant operand to the
-       PLUS is always found in tem2.  */
-    if (GET_CODE (tem2) == CONST_INT
-       && INTVAL (tem2) < 128 && INTVAL (tem2) >= -128)
-      return 2 / 2;
-
-    /* A BDAP -32768 .. 32767 is like BDAP quick, but with 2 extra
-       bytes.  */
-    if (GET_CODE (tem2) == CONST_INT
-       && CONST_OK_FOR_LETTER_P (INTVAL (tem2), 'L'))
-      return (2 + 2) / 2;
-
-    /* A BDAP with some other constant is 2 bytes extra.  */
-    if (CONSTANT_P (tem2))
+      /* Local extended canonicalization rule: the first operand must
+        be REG, unless it's an operation (MULT).  */
+      if (!REG_P (tem1) && GET_CODE (tem1) != MULT)
+       tem1 = tem2, tem2 = XEXP (x, 0);
+
+      /* We'll "assume" we have canonical RTX now.  */
+      gcc_assert (REG_P (tem1) || GET_CODE (tem1) == MULT);
+
+      /* A BIAP is 2 extra bytes for the prefix insn, nothing more.  We
+        recognize the typical MULT which is always in tem1 because of
+        insn canonicalization.  */
+      if ((GET_CODE (tem1) == MULT && BIAP_INDEX_P (tem1))
+         || REG_P (tem2))
+       return 2 / 2;
+
+      /* A BDAP (quick) is 2 extra bytes.  Any constant operand to the
+        PLUS is always found in tem2.  */
+      if (CONST_INT_P (tem2) && INTVAL (tem2) < 128 && INTVAL (tem2) >= -128)
+       return 2 / 2;
+
+      /* A BDAP -32768 .. 32767 is like BDAP quick, but with 2 extra
+        bytes.  */
+      if (CONST_INT_P (tem2)
+         && CRIS_CONST_OK_FOR_LETTER_P (INTVAL (tem2), 'L'))
+       return (2 + 2) / 2;
+
+      /* A BDAP with some other constant is 2 bytes extra.  */
+      if (CONSTANT_P (tem2))
+       return (2 + 2 + 2) / 2;
+
+      /* BDAP with something indirect should have a higher cost than
+        BIAP with register.   FIXME: Should it cost like a MEM or more?  */
       return (2 + 2 + 2) / 2;
-
-    /* BDAP with something indirect should have a higher cost than
-       BIAP with register.   FIXME: Should it cost like a MEM or more?  */
-    /* Don't need to check it, it's the only one left.
-       FIXME:  There was a REG test missing, perhaps there are others.
-       Think more.  */
-    return (2 + 2 + 2) / 2;
-  }
+    }
 
   /* What else?  Return a high cost.  It matters only for valid
      addressing modes.  */
@@ -2160,10 +1955,9 @@ cris_address_cost (x)
              whose mode we must consider.  */
 
 int
-cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op)
-     enum rtx_code code;
-     rtx *ops;
-     int lreg, rreg, rval, multop, other_op;
+cris_side_effect_mode_ok (enum rtx_code code, rtx *ops,
+                         int lreg, int rreg, int rval,
+                         int multop, int other_op)
 {
   /* Find what value to multiply with, for rx =ry + rz * n.  */
   int mult = multop < 0 ? 1 : INTVAL (ops[multop]);
@@ -2224,21 +2018,19 @@ cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op)
       /* Do not allow rx = rx + n if a normal add or sub with same size
         would do.  */
       if (rtx_equal_p (ops[lreg], reg_rtx)
-         && GET_CODE (val_rtx) == CONST_INT
+         && CONST_INT_P (val_rtx)
          && (INTVAL (val_rtx) <= 63 && INTVAL (val_rtx) >= -63))
        return 0;
 
-      /* Check allowed cases, like [r(+)?].[bwd] and const.
-        A symbol is not allowed with PIC.  */
+      /* Check allowed cases, like [r(+)?].[bwd] and const.  */
       if (CONSTANT_P (val_rtx))
-       return flag_pic == 0 || cris_symbol (val_rtx) == 0;
+       return 1;
 
-      if (GET_CODE (val_rtx) == MEM
-         && BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0)))
+      if (MEM_P (val_rtx) && BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0)))
        return 1;
 
       if (GET_CODE (val_rtx) == SIGN_EXTEND
-         && GET_CODE (XEXP (val_rtx, 0)) == MEM
+         && MEM_P (XEXP (val_rtx, 0))
          && BASE_OR_AUTOINCR_P (XEXP (XEXP (val_rtx, 0), 0)))
        return 1;
 
@@ -2270,6 +2062,49 @@ cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op)
   internal_error ("internal error: cris_side_effect_mode_ok with bad operands");
 }
 
+/* Whether next_cc0_user of insn is LE or GT or requires a real compare
+   insn for other reasons.  */
+
+bool
+cris_cc0_user_requires_cmp (rtx insn)
+{
+  rtx cc0_user = NULL;
+  rtx body;
+  rtx set;
+
+  gcc_assert (insn != NULL);
+
+  if (!TARGET_V32)
+    return false;
+
+  cc0_user = next_cc0_user (insn);
+  if (cc0_user == NULL)
+    return false;
+
+  body = PATTERN (cc0_user);
+  set = single_set (cc0_user);
+
+  /* Users can be sCC and bCC.  */
+  if (JUMP_P (cc0_user)
+      && GET_CODE (body) == SET
+      && SET_DEST (body) == pc_rtx
+      && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
+      && XEXP (XEXP (SET_SRC (body), 0), 0) == cc0_rtx)
+    {
+      return
+       GET_CODE (XEXP (SET_SRC (body), 0)) == GT
+       || GET_CODE (XEXP (SET_SRC (body), 0)) == LE;
+    }
+  else if (set)
+    {
+      return
+       GET_CODE (SET_SRC (body)) == GT
+       || GET_CODE (SET_SRC (body)) == LE;
+    }
+
+  gcc_unreachable ();
+}
+
 /* The function reg_overlap_mentioned_p in CVS (still as of 2001-05-16)
    does not handle the case where the IN operand is strict_low_part; it
    does handle it for X.  Test-case in Axis-20010516.  This function takes
@@ -2277,8 +2112,7 @@ cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op)
    anyway.  */
 
 static int
-cris_reg_overlap_mentioned_p (x, in)
-     rtx x, in;
+cris_reg_overlap_mentioned_p (rtx x, rtx in)
 {
   /* The function reg_overlap_mentioned now handles when X is
      strict_low_part, but not when IN is a STRICT_LOW_PART.  */
@@ -2292,174 +2126,224 @@ cris_reg_overlap_mentioned_p (x, in)
    We just dispatch to the functions for ELF and a.out.  */
 
 void
-cris_target_asm_named_section (name, flags)
-     const char *name;
-     unsigned int flags;
+cris_target_asm_named_section (const char *name, unsigned int flags,
+                              tree decl)
 {
   if (! TARGET_ELF)
-    default_no_named_section (name, flags);
+    default_no_named_section (name, flags, decl);
   else
-    default_elf_asm_named_section (name, flags);
+    default_elf_asm_named_section (name, flags, decl);
 }
 
-/* The LEGITIMATE_PIC_OPERAND_P worker.  */
+/* Return TRUE iff X is a CONST valid for e.g. indexing.
+   ANY_OPERAND is 0 if X is in a CALL_P insn or movsi, 1
+   elsewhere.  */
 
-int
-cris_legitimate_pic_operand (x)
-     rtx x;
+bool
+cris_valid_pic_const (rtx x, bool any_operand)
 {
-  /* The PIC representation of a symbol with a GOT entry will be (for
-     example; relocations differ):
-      sym => [rPIC+sym:GOT]
-     and for a GOT-less symbol it will be (for example, relocation differ):
-      sym => rPIC+sym:GOTOFF
-     so only a symbol with a GOT is by itself a valid operand, and it
-     can't be a sum of a symbol and an offset.  */
-  return ! cris_symbol (x) || cris_got_symbol (x);
-}
-
-/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P.  */
+  gcc_assert (flag_pic);
 
-int
-cris_symbol (x)
-     rtx x;
-{
   switch (GET_CODE (x))
     {
-    case SYMBOL_REF:
-    case LABEL_REF:
-      return 1;
-
-    case UNSPEC:
-      /* A PLT reference.  */
-      ASSERT_PLT_UNSPEC (x);
-      return 1;
-
-    case CONST:
-      return cris_symbol (XEXP (x, 0));
-
-    case PLUS:
-    case MINUS:
-      return cris_symbol (XEXP (x, 0)) || cris_symbol (XEXP (x, 1));
-
     case CONST_INT:
     case CONST_DOUBLE:
-    case CONSTANT_P_RTX:
-      return 0;
-
+      return true;
     default:
-      fatal_insn ("unrecognized supposed constant", x);
+      ;
     }
 
-  return 1;
+  if (GET_CODE (x) != CONST)
+    return false;
+
+  x = XEXP (x, 0);
+
+  /* Handle (const (plus (unspec .. UNSPEC_GOTREL) (const_int ...))).  */
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == UNSPEC
+      && (XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
+         || XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_PCREL)
+      && CONST_INT_P (XEXP (x, 1)))
+    x = XEXP (x, 0);
+
+  if (GET_CODE (x) == UNSPEC)
+    switch (XINT (x, 1))
+      {
+       /* A PCREL operand is only valid for call and movsi.  */
+      case CRIS_UNSPEC_PLT_PCREL:
+      case CRIS_UNSPEC_PCREL:
+       return !any_operand;
+
+      case CRIS_UNSPEC_PLT_GOTREL:
+      case CRIS_UNSPEC_PLTGOTREAD:
+      case CRIS_UNSPEC_GOTREAD:
+      case CRIS_UNSPEC_GOTREL:
+       return true;
+      default:
+       gcc_unreachable ();
+      }
+
+  return cris_pic_symbol_type_of (x) == cris_no_symbol;
 }
 
-/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P, and the symbol does not need a GOT entry.  Also set
-   current_function_uses_pic_offset_table if we're generating PIC and ever
-   see something that would need one.  */
+/* Helper function to find the right PIC-type symbol to generate,
+   given the original (non-PIC) representation.  */
 
-int
-cris_gotless_symbol (x)
-     rtx x;
+enum cris_pic_symbol_type
+cris_pic_symbol_type_of (rtx x)
 {
   switch (GET_CODE (x))
     {
-    case UNSPEC:
-      ASSERT_PLT_UNSPEC (x);
-      return 1;
-
     case SYMBOL_REF:
-      if (flag_pic && cfun != NULL)
-       current_function_uses_pic_offset_table = 1;
-      return SYMBOL_REF_FLAG (x);
+      return SYMBOL_REF_LOCAL_P (x)
+       ? cris_rel_symbol : cris_got_symbol;
 
     case LABEL_REF:
-      /* We don't set current_function_uses_pic_offset_table for
-        LABEL_REF:s in here, since they are almost always originating
-        from some branch.  The only time it does not come from a label is
-        when GCC does something like __builtin_setjmp.  Then we get the
-        LABEL_REF from the movsi expander, so we mark it there as a
-        special case.  */
-      return 1;
+      return cris_rel_symbol;
 
     case CONST:
-      return cris_gotless_symbol (XEXP (x, 0));
+      return cris_pic_symbol_type_of (XEXP (x, 0));
 
     case PLUS:
     case MINUS:
       {
-       int x0 = cris_gotless_symbol (XEXP (x, 0)) != 0;
-       int x1 = cris_gotless_symbol (XEXP (x, 1)) != 0;
-
-       /* One and only one of them must be a local symbol.  Neither must
-          be some other, more general kind of symbol.  */
-       return
-         (x0 ^ x1)
-         && ! (x0 == 0 && cris_symbol (XEXP (x, 0)))
-         && ! (x1 == 0 && cris_symbol (XEXP (x, 1)));
+       enum cris_pic_symbol_type t1 = cris_pic_symbol_type_of (XEXP (x, 0));
+       enum cris_pic_symbol_type t2 = cris_pic_symbol_type_of (XEXP (x, 1));
+
+       gcc_assert (t1 == cris_no_symbol || t2 == cris_no_symbol);
+
+       if (t1 == cris_got_symbol || t1 == cris_got_symbol)
+         return cris_got_symbol_needing_fixup;
+
+       return t1 != cris_no_symbol ? t1 : t2;
       }
 
     case CONST_INT:
     case CONST_DOUBLE:
-    case CONSTANT_P_RTX:
-      return 0;
+      return cris_no_symbol;
+
+    case UNSPEC:
+      /* Likely an offsettability-test attempting to add a constant to
+        a GOTREAD symbol, which can't be handled.  */
+      return cris_invalid_pic_symbol;
 
     default:
       fatal_insn ("unrecognized supposed constant", x);
     }
 
-  return 1;
+  gcc_unreachable ();
 }
 
-/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P, and the symbol needs a GOT entry.  */
+/* The LEGITIMATE_PIC_OPERAND_P worker.  */
 
 int
-cris_got_symbol (x)
-     rtx x;
+cris_legitimate_pic_operand (rtx x)
 {
-  switch (GET_CODE (x))
+  /* Symbols are not valid PIC operands as-is; just constants.  */
+  return cris_valid_pic_const (x, true);
+}
+
+/* The ASM_OUTPUT_CASE_END worker.  */
+
+void
+cris_asm_output_case_end (FILE *stream, int num, rtx table)
+{
+  if (TARGET_V32)
     {
-    case UNSPEC:
-      ASSERT_PLT_UNSPEC (x);
-      return 0;
+      rtx whole_jump_insn = PATTERN (PREV_INSN (PREV_INSN (table)));
+
+      /* This can be a SEQUENCE, meaning the delay-slot of the jump is
+        filled.  */
+      rtx parallel_jump
+       = (GET_CODE (whole_jump_insn) == SEQUENCE
+          ? PATTERN (XVECEXP (whole_jump_insn, 0, 0)) : whole_jump_insn);
+
+      asm_fprintf (stream,
+                  "\t.word %LL%d-.%s\n",
+                  CODE_LABEL_NUMBER (XEXP (XEXP (XEXP (XVECEXP
+                                                       (parallel_jump, 0, 0),
+                                                       1), 2), 0)),
+                  (TARGET_PDEBUG ? "; default" : ""));
+      return;
+    }
 
-    case SYMBOL_REF:
-      if (flag_pic && cfun != NULL)
-       current_function_uses_pic_offset_table = 1;
-      return ! SYMBOL_REF_FLAG (x);
+  asm_fprintf (stream,
+              "\t.word %LL%d-%LL%d%s\n",
+              CODE_LABEL_NUMBER (XEXP
+                                 (XEXP
+                                  (XEXP
+                                   (XVECEXP
+                                    (PATTERN
+                                     (PREV_INSN
+                                      (PREV_INSN (table))), 0, 0), 1),
+                                   2), 0)),
+              num,
+              (TARGET_PDEBUG ? "; default" : ""));
+}
 
-    case CONST:
-      return cris_got_symbol (XEXP (x, 0));
+/* TARGET_HANDLE_OPTION worker.  We just store the values into local
+   variables here.  Checks for correct semantics are in
+   cris_override_options.  */
 
-    case LABEL_REF:
-      /* A LABEL_REF is never visible as a symbol outside the local
-         function.  */
-    case PLUS:
-    case MINUS:
-      /* Nope, can't access the GOT for "symbol + offset".  */
-      return 0;
+static bool
+cris_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+                   int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case OPT_metrax100:
+      target_flags
+       |= (MASK_SVINTO
+           + MASK_ETRAX4_ADD
+           + MASK_ALIGN_BY_32);
+      break;
 
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONSTANT_P_RTX:
-      return 0;
+    case OPT_mno_etrax100:
+      target_flags
+       &= ~(MASK_SVINTO
+            + MASK_ETRAX4_ADD
+            + MASK_ALIGN_BY_32);
+      break;
+
+    case OPT_m32_bit:
+    case OPT_m32bit:
+      target_flags
+       |= (MASK_STACK_ALIGN
+           + MASK_CONST_ALIGN
+           + MASK_DATA_ALIGN
+           + MASK_ALIGN_BY_32);
+      break;
+
+    case OPT_m16_bit:
+    case OPT_m16bit:
+      target_flags
+       |= (MASK_STACK_ALIGN
+           + MASK_CONST_ALIGN
+           + MASK_DATA_ALIGN);
+      break;
+
+    case OPT_m8_bit:
+    case OPT_m8bit:
+      target_flags
+       &= ~(MASK_STACK_ALIGN
+            + MASK_CONST_ALIGN
+            + MASK_DATA_ALIGN);
+      break;
 
     default:
-      fatal_insn ("unrecognized supposed constant in cris_global_pic_symbol",
-                 x);
+      break;
     }
 
-  return 1;
+  CRIS_SUBTARGET_HANDLE_OPTION(code, arg, value);
+
+  return true;
 }
 
 /* The OVERRIDE_OPTIONS worker.
    As is the norm, this also parses -mfoo=bar type parameters.  */
 
 void
-cris_override_options ()
+cris_override_options (void)
 {
   if (cris_max_stackframe_str)
     {
@@ -2494,20 +2378,20 @@ cris_override_options ()
          || strcmp ("etrax100lx", cris_cpu_str) == 0)
        cris_cpu_version = 10;
 
-      if (cris_cpu_version < 0 || cris_cpu_version > 10)
+      if (cris_cpu_version < 0 || cris_cpu_version > 32)
        error ("unknown CRIS version specification in -march= or -mcpu= : %s",
               cris_cpu_str);
 
       /* Set the target flags.  */
       if (cris_cpu_version >= CRIS_CPU_ETRAX4)
-       target_flags |= TARGET_MASK_ETRAX4_ADD;
+       target_flags |= MASK_ETRAX4_ADD;
 
       /* If this is Svinto or higher, align for 32 bit accesses.  */
       if (cris_cpu_version >= CRIS_CPU_SVINTO)
        target_flags
-         |= (TARGET_MASK_SVINTO | TARGET_MASK_ALIGN_BY_32
-             | TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN
-             | TARGET_MASK_DATA_ALIGN);
+         |= (MASK_SVINTO | MASK_ALIGN_BY_32
+             | MASK_STACK_ALIGN | MASK_CONST_ALIGN
+             | MASK_DATA_ALIGN);
 
       /* Note that we do not add new flags when it can be completely
         described with a macro that uses -mcpu=X.  So
@@ -2530,7 +2414,7 @@ cris_override_options ()
          || strcmp ("etrax100lx", cris_tune_str) == 0)
        cris_tune = 10;
 
-      if (cris_tune < 0 || cris_tune > 10)
+      if (cris_tune < 0 || cris_tune > 32)
        error ("unknown CRIS cpu version specification in -mtune= : %s",
               cris_tune_str);
 
@@ -2538,10 +2422,13 @@ cris_override_options ()
        /* We have currently nothing more to tune than alignment for
           memory accesses.  */
        target_flags
-         |= (TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN
-             | TARGET_MASK_DATA_ALIGN | TARGET_MASK_ALIGN_BY_32);
+         |= (MASK_STACK_ALIGN | MASK_CONST_ALIGN
+             | MASK_DATA_ALIGN | MASK_ALIGN_BY_32);
     }
 
+  if (cris_cpu_version >= CRIS_CPU_V32)
+    target_flags &= ~(MASK_SIDE_EFFECT_PREFIXES|MASK_MUL_BUG);
+
   if (flag_pic)
     {
       /* Use error rather than warning, so invalid use is easily
@@ -2562,10 +2449,9 @@ cris_override_options ()
       flag_no_function_cse = 1;
     }
 
-  if ((write_symbols == DWARF_DEBUG
-       || write_symbols == DWARF2_DEBUG) && ! TARGET_ELF)
+  if (write_symbols == DWARF2_DEBUG && ! TARGET_ELF)
     {
-      warning ("that particular -g option is invalid with -maout and -melinux");
+      warning (0, "that particular -g option is invalid with -maout and -melinux");
       write_symbols = DBX_DEBUG;
     }
 
@@ -2573,147 +2459,105 @@ cris_override_options ()
   init_machine_status = cris_init_machine_status;
 }
 
-/* The ASM_OUTPUT_MI_THUNK worker.  */
+/* The TARGET_ASM_OUTPUT_MI_THUNK worker.  */
 
-void
-cris_asm_output_mi_thunk (stream, thunkdecl, delta, funcdecl)
-     FILE *stream;
-     tree thunkdecl ATTRIBUTE_UNUSED;
-     int delta;
-     tree funcdecl;
+static void
+cris_asm_output_mi_thunk (FILE *stream,
+                         tree thunkdecl ATTRIBUTE_UNUSED,
+                         HOST_WIDE_INT delta,
+                         HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
+                         tree funcdecl)
 {
   if (delta > 0)
-    asm_fprintf (stream, "\tadd%s %d,$%s\n",
-                ADDITIVE_SIZE_MODIFIER (delta), delta,
-                reg_names[CRIS_FIRST_ARG_REG]);
+    fprintf (stream, "\tadd%s " HOST_WIDE_INT_PRINT_DEC ",$%s\n",
+            ADDITIVE_SIZE_MODIFIER (delta), delta,
+            reg_names[CRIS_FIRST_ARG_REG]);
   else if (delta < 0)
-    asm_fprintf (stream, "\tsub%s %d,$%s\n",
-                ADDITIVE_SIZE_MODIFIER (-delta), -delta,
-                reg_names[CRIS_FIRST_ARG_REG]);
+    fprintf (stream, "\tsub%s " HOST_WIDE_INT_PRINT_DEC ",$%s\n",
+            ADDITIVE_SIZE_MODIFIER (-delta), -delta,
+            reg_names[CRIS_FIRST_ARG_REG]);
 
   if (flag_pic)
     {
       const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0);
 
-      STRIP_NAME_ENCODING (name, name);
-      fprintf (stream, "add.d ");
-      assemble_name (stream, name);
-      fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
+      name = (* targetm.strip_name_encoding) (name);
+
+      if (TARGET_V32)
+       {
+         fprintf (stream, "\tba ");
+         assemble_name (stream, name);
+         fprintf (stream, "%s\n", CRIS_PLT_PCOFFSET_SUFFIX);
+       }
+      else
+       {
+         fprintf (stream, "add.d ");
+         assemble_name (stream, name);
+         fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
+       }
     }
   else
     {
       fprintf (stream, "jump ");
       assemble_name (stream, XSTR (XEXP (DECL_RTL (funcdecl), 0), 0));
       fprintf (stream, "\n");
+
+      if (TARGET_V32)
+       fprintf (stream, "\tnop\n");
     }
 }
 
-/* The EXPAND_BUILTIN_VA_ARG worker.  This is modified from the
-   "standard" implementation of va_arg: read the value from the current
-   address and increment by the size of one or two registers.  The
-   important difference for CRIS is that if the type is
-   pass-by-reference, then perform an indirection.  */
+/* Boilerplate emitted at start of file.
 
-rtx
-cris_expand_builtin_va_arg (valist, type)
-     tree valist;
-     tree type;
-{
-  tree addr_tree, t;
-  rtx addr;
-  tree passed_size = size_zero_node;
-  tree type_size = NULL;
-  tree size3 = size_int (3);
-  tree size4 = size_int (4);
-  tree size8 = size_int (8);
-  tree rounded_size;
-
-  /* Get AP.  */
-  addr_tree = valist;
-
-  if (type == error_mark_node
-      || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL
-      || TREE_OVERFLOW (type_size))
-    /* Presumably an error; the size isn't computable.  A message has
-       supposedly been emitted elsewhere.  */
-    rounded_size = size_zero_node;
-  else
-    rounded_size
-      = fold (build (MULT_EXPR, sizetype,
-                    fold (build (TRUNC_DIV_EXPR, sizetype,
-                                 fold (build (PLUS_EXPR, sizetype,
-                                              type_size, size3)),
-                                 size4)),
-                    size4));
-
-  if (!integer_zerop (rounded_size))
-    {
-      /* Check if the type is passed by value or by reference.  Values up
-        to 8 bytes are passed by-value, padded to register-size (4
-        bytes).  Larger values and varying-size types are passed
-        by reference.  */
-      passed_size
-       = (!really_constant_p (type_size)
-          ? size4
-          : fold (build (COND_EXPR, sizetype,
-                         fold (build (GT_EXPR, sizetype,
-                                      rounded_size,
-                                      size8)),
-                         size4,
-                         rounded_size)));
-
-      addr_tree
-       = (!really_constant_p (type_size)
-          ? build1 (INDIRECT_REF, build_pointer_type (type), addr_tree)
-          : fold (build (COND_EXPR, TREE_TYPE (addr_tree),
-                         fold (build (GT_EXPR, sizetype,
-                                      rounded_size,
-                                      size8)),
-                         build1 (INDIRECT_REF, build_pointer_type (type),
-                                 addr_tree),
-                         addr_tree)));
-    }
-
-  addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
-  addr = copy_to_reg (addr);
-
-  if (!integer_zerop (rounded_size))
-    {
-      /* Compute new value for AP.  */
-      t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
-                build (PLUS_EXPR, TREE_TYPE (valist), valist,
-                       passed_size));
-      TREE_SIDE_EFFECTS (t) = 1;
-      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-    }
-
-  return addr;
+   NO_APP *only at file start* means faster assembly.  It also means
+   comments are not allowed.  In some cases comments will be output
+   for debugging purposes.  Make sure they are allowed then.
+
+   We want a .file directive only if TARGET_ELF.  */
+static void
+cris_file_start (void)
+{
+  /* These expressions can vary at run time, so we cannot put
+     them into TARGET_INITIALIZER.  */
+  targetm.file_start_app_off = !(TARGET_PDEBUG || flag_print_asm_name);
+  targetm.file_start_file_directive = TARGET_ELF;
+
+  default_file_start ();
+}
+
+/* Rename the function calls for integer multiply and divide.  */
+static void
+cris_init_libfuncs (void)
+{
+  set_optab_libfunc (smul_optab, SImode, "__Mul");
+  set_optab_libfunc (sdiv_optab, SImode, "__Div");
+  set_optab_libfunc (udiv_optab, SImode, "__Udiv");
+  set_optab_libfunc (smod_optab, SImode, "__Mod");
+  set_optab_libfunc (umod_optab, SImode, "__Umod");
 }
 
 /* The INIT_EXPANDERS worker sets the per-function-data initializer and
    mark functions.  */
 
 void
-cris_init_expanders ()
+cris_init_expanders (void)
 {
   /* Nothing here at the moment.  */
 }
 
 /* Zero initialization is OK for all current fields.  */
 
-static void
-cris_init_machine_status (p)
-     struct function *p;
+static struct machine_function *
+cris_init_machine_status (void)
 {
-  p->machine = xcalloc (1, sizeof (struct machine_function));
+  return GGC_CNEW (struct machine_function);
 }
 
 /* Split a 2 word move (DI or presumably DF) into component parts.
    Originally a copy of gen_split_move_double in m32r.c.  */
 
 rtx
-cris_split_movdx (operands)
-     rtx *operands;
+cris_split_movdx (rtx *operands)
 {
   enum machine_mode mode = GET_MODE (operands[0]);
   rtx dest = operands[0];
@@ -2723,16 +2567,15 @@ cris_split_movdx (operands)
   /* We used to have to handle (SUBREG (MEM)) here, but that should no
      longer happen; after reload there are no SUBREGs any more, and we're
      only called after reload.  */
-  if (GET_CODE (dest) == SUBREG || GET_CODE (src) == SUBREG)
-    abort ();
+  CRIS_ASSERT (GET_CODE (dest) != SUBREG && GET_CODE (src) != SUBREG);
 
   start_sequence ();
-  if (GET_CODE (dest) == REG)
+  if (REG_P (dest))
     {
       int dregno = REGNO (dest);
 
       /* Reg-to-reg copy.  */
-      if (GET_CODE (src) == REG)
+      if (REG_P (src))
        {
          int sregno = REGNO (src);
 
@@ -2750,7 +2593,7 @@ cris_split_movdx (operands)
                                  operand_subword (src, !reverse, TRUE, mode)));
        }
       /* Constant-to-reg copy.  */
-      else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE)
+      else if (CONST_INT_P (src) || GET_CODE (src) == CONST_DOUBLE)
        {
          rtx words[2];
          split_double (src, &words[0], &words[1]);
@@ -2763,7 +2606,7 @@ cris_split_movdx (operands)
                                  words[1]));
        }
       /* Mem-to-reg copy.  */
-      else if (GET_CODE (src) == MEM)
+      else if (MEM_P (src))
        {
          /* If the high-address word is used in the address, we must load it
             last.  Otherwise, load it first.  */
@@ -2771,7 +2614,7 @@ cris_split_movdx (operands)
          int reverse
            = (refers_to_regno_p (dregno, dregno + 1, addr, NULL) != 0);
 
-         /* The original code imples that we can't do
+         /* The original code implies that we can't do
             move.x [rN+],rM  move.x [rN],rM+1
             when rN is dead, because of REG_NOTES damage.  That is
             consistent with what I've seen, so don't try it.
@@ -2781,12 +2624,31 @@ cris_split_movdx (operands)
 
           if (GET_CODE (addr) == POST_INC)
            {
-             emit_insn (gen_rtx_SET (VOIDmode,
-                                     operand_subword (dest, 0, TRUE, mode),
-                                     change_address (src, SImode, addr)));
-             emit_insn (gen_rtx_SET (VOIDmode,
-                                     operand_subword (dest, 1, TRUE, mode),
-                                     change_address (src, SImode, addr)));
+             rtx mem;
+             rtx insn;
+
+             /* Whenever we emit insns with post-incremented
+                addresses ourselves, we must add a post-inc note
+                manually.  */
+             mem = change_address (src, SImode, addr);
+             insn
+               = gen_rtx_SET (VOIDmode,
+                              operand_subword (dest, 0, TRUE, mode), mem);
+             insn = emit_insn (insn);
+             if (GET_CODE (XEXP (mem, 0)) == POST_INC)
+               REG_NOTES (insn)
+                 = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
+                                    REG_NOTES (insn));
+
+             mem = copy_rtx (mem);
+             insn
+               = gen_rtx_SET (VOIDmode,
+                              operand_subword (dest, 1, TRUE, mode), mem);
+             insn = emit_insn (insn);
+             if (GET_CODE (XEXP (mem, 0)) == POST_INC)
+               REG_NOTES (insn)
+                 = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
+                                    REG_NOTES (insn));
            }
          else
            {
@@ -2815,11 +2677,11 @@ cris_split_movdx (operands)
            }
        }
       else
-       abort ();
+       internal_error ("Unknown src");
     }
   /* Reg-to-mem copy or clear mem.  */
-  else if (GET_CODE (dest) == MEM
-          && (GET_CODE (src) == REG
+  else if (MEM_P (dest)
+          && (REG_P (src)
               || src == const0_rtx
               || src == CONST0_RTX (DFmode)))
     {
@@ -2827,12 +2689,31 @@ cris_split_movdx (operands)
 
       if (GET_CODE (addr) == POST_INC)
        {
-         emit_insn (gen_rtx_SET (VOIDmode,
-                                 change_address (dest, SImode, addr),
-                                 operand_subword (src, 0, TRUE, mode)));
-         emit_insn (gen_rtx_SET (VOIDmode,
-                                 change_address (dest, SImode, addr),
-                                 operand_subword (src, 1, TRUE, mode)));
+         rtx mem;
+         rtx insn;
+
+         /* Whenever we emit insns with post-incremented addresses
+            ourselves, we must add a post-inc note manually.  */
+         mem = change_address (dest, SImode, addr);
+         insn
+           = gen_rtx_SET (VOIDmode,
+                          mem, operand_subword (src, 0, TRUE, mode));
+         insn = emit_insn (insn);
+         if (GET_CODE (XEXP (mem, 0)) == POST_INC)
+           REG_NOTES (insn)
+             = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
+                                REG_NOTES (insn));
+
+         mem = copy_rtx (mem);
+         insn
+           = gen_rtx_SET (VOIDmode,
+                          mem,
+                          operand_subword (src, 1, TRUE, mode));
+         insn = emit_insn (insn);
+         if (GET_CODE (XEXP (mem, 0)) == POST_INC)
+           REG_NOTES (insn)
+             = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
+                                REG_NOTES (insn));
        }
       else
        {
@@ -2857,214 +2738,1070 @@ cris_split_movdx (operands)
     }
 
   else
-    abort ();
+    internal_error ("Unknown dest");
 
-  val = gen_sequence ();
+  val = get_insns ();
   end_sequence ();
   return val;
 }
 
-/* This is in essence a copy of output_addr_const altered to output
-   symbolic operands as PIC.
-
-   FIXME: Add hooks similar to ASM_OUTPUT_SYMBOL_REF to get this effect in
-   the "real" output_addr_const.  All we need is one for LABEL_REF (and
-   one for CODE_LABEL?).  */
+/* The expander for the prologue pattern name.  */
 
 void
-cris_output_addr_const (file, x)
-     FILE *file;
-     rtx x;
+cris_expand_prologue (void)
 {
-  int is_plt = 0;
+  int regno;
+  int size = get_frame_size ();
+  /* Shorten the used name for readability.  */
+  int cfoa_size = crtl->outgoing_args_size;
+  int last_movem_reg = -1;
+  int framesize = 0;
+  rtx mem, insn;
+  int return_address_on_stack = cris_return_address_on_stack ();
+  int got_really_used = false;
+  int n_movem_regs = 0;
+  int pretend = crtl->args.pretend_args_size;
 
-restart:
-  switch (GET_CODE (x))
+  /* Don't do anything if no prologues or epilogues are wanted.  */
+  if (!TARGET_PROLOGUE_EPILOGUE)
+    return;
+
+  CRIS_ASSERT (size >= 0);
+
+  if (crtl->uses_pic_offset_table)
     {
-    case UNSPEC:
-      ASSERT_PLT_UNSPEC (x);
-      x = XVECEXP (x, 0, 0);
-      is_plt = 1;
+      /* A reference may have been optimized out (like the abort () in
+        fde_split in unwind-dw2-fde.c, at least 3.2.1) so check that
+        it's still used.  */
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
+      pop_topmost_sequence ();
+    }
 
-      /* Fall through.  */
-    case SYMBOL_REF:
-      if (flag_pic)
+  /* Align the size to what's best for the CPU model.  */
+  if (TARGET_STACK_ALIGN)
+    size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
+
+  if (pretend)
+    {
+      /* See also cris_setup_incoming_varargs where
+        cfun->machine->stdarg_regs is set.  There are other setters of
+        crtl->args.pretend_args_size than stdarg handling, like
+        for an argument passed with parts in R13 and stack.  We must
+        not store R13 into the pretend-area for that case, as GCC does
+        that itself.  "Our" store would be marked as redundant and GCC
+        will attempt to remove it, which will then be flagged as an
+        internal error; trying to remove a frame-related insn.  */
+      int stdarg_regs = cfun->machine->stdarg_regs;
+
+      framesize += pretend;
+
+      for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1;
+          stdarg_regs > 0;
+          regno--, pretend -= 4, stdarg_regs--)
        {
-         const char *origstr = XSTR (x, 0);
-         const char *str;
+         insn = emit_insn (gen_rtx_SET (VOIDmode,
+                                        stack_pointer_rtx,
+                                        plus_constant (stack_pointer_rtx,
+                                                       -4)));
+         /* FIXME: When dwarf2 frame output and unless asynchronous
+            exceptions, make dwarf2 bundle together all stack
+            adjustments like it does for registers between stack
+            adjustments.  */
+         RTX_FRAME_RELATED_P (insn) = 1;
+
+         mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
+         set_mem_alias_set (mem, get_varargs_alias_set ());
+         insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno));
+
+         /* Note the absence of RTX_FRAME_RELATED_P on the above insn:
+            the value isn't restored, so we don't want to tell dwarf2
+            that it's been stored to stack, else EH handling info would
+            get confused.  */
+       }
+
+      /* For other setters of crtl->args.pretend_args_size, we
+        just adjust the stack by leaving the remaining size in
+        "pretend", handled below.  */
+    }
+
+  /* Save SRP if not a leaf function.  */
+  if (return_address_on_stack)
+    {
+      insn = emit_insn (gen_rtx_SET (VOIDmode,
+                                    stack_pointer_rtx,
+                                    plus_constant (stack_pointer_rtx,
+                                                   -4 - pretend)));
+      pretend = 0;
+      RTX_FRAME_RELATED_P (insn) = 1;
+
+      mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
+      set_mem_alias_set (mem, get_frame_alias_set ());
+      insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM));
+      RTX_FRAME_RELATED_P (insn) = 1;
+      framesize += 4;
+    }
+
+  /* Set up the frame pointer, if needed.  */
+  if (frame_pointer_needed)
+    {
+      insn = emit_insn (gen_rtx_SET (VOIDmode,
+                                    stack_pointer_rtx,
+                                    plus_constant (stack_pointer_rtx,
+                                                   -4 - pretend)));
+      pretend = 0;
+      RTX_FRAME_RELATED_P (insn) = 1;
+
+      mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
+      set_mem_alias_set (mem, get_frame_alias_set ());
+      insn = emit_move_insn (mem, frame_pointer_rtx);
+      RTX_FRAME_RELATED_P (insn) = 1;
 
-         STRIP_NAME_ENCODING (str, origstr);
+      insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+      RTX_FRAME_RELATED_P (insn) = 1;
 
-         if (is_plt)
+      framesize += 4;
+    }
+
+  /* Between frame-pointer and saved registers lie the area for local
+     variables.  If we get here with "pretended" size remaining, count
+     it into the general stack size.  */
+  size += pretend;
+
+  /* Get a contiguous sequence of registers, starting with R0, that need
+     to be saved.  */
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    {
+      if (cris_reg_saved_in_regsave_area (regno, got_really_used))
+       {
+         n_movem_regs++;
+
+         /* Check if movem may be used for registers so far.  */
+         if (regno == last_movem_reg + 1)
+           /* Yes, update next expected register.  */
+           last_movem_reg = regno;
+         else
            {
-             if (cris_pic_sympart_only)
-               {
-                 assemble_name (file, str);
-                 fprintf (file, ":PLTG");
-               }
-             else
+             /* We cannot use movem for all registers.  We have to flush
+                any movem:ed registers we got so far.  */
+             if (last_movem_reg != -1)
                {
-                 if (TARGET_AVOID_GOTPLT)
-                   /* We shouldn't get here.  */
-                   abort ();
-
-                 fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-                 assemble_name (file, XSTR (x, 0));
+                 int n_saved
+                   = (n_movem_regs == 1) ? 1 : last_movem_reg + 1;
 
-                 if (flag_pic == 1)
-                   fprintf (file, ":GOTPLT16]");
+                 /* It is a win to use a side-effect assignment for
+                    64 <= size <= 128.  But side-effect on movem was
+                    not usable for CRIS v0..3.  Also only do it if
+                    side-effects insns are allowed.  */
+                 if ((last_movem_reg + 1) * 4 + size >= 64
+                     && (last_movem_reg + 1) * 4 + size <= 128
+                     && (cris_cpu_version >= CRIS_CPU_SVINTO || n_saved == 1)
+                     && TARGET_SIDE_EFFECT_PREFIXES)
+                   {
+                     mem
+                       = gen_rtx_MEM (SImode,
+                                      plus_constant (stack_pointer_rtx,
+                                                     -(n_saved * 4 + size)));
+                     set_mem_alias_set (mem, get_frame_alias_set ());
+                     insn
+                       = cris_emit_movem_store (mem, GEN_INT (n_saved),
+                                                -(n_saved * 4 + size),
+                                                true);
+                   }
                  else
-                   fprintf (file, ":GOTPLT]");
+                   {
+                     insn
+                       = gen_rtx_SET (VOIDmode,
+                                      stack_pointer_rtx,
+                                      plus_constant (stack_pointer_rtx,
+                                                     -(n_saved * 4 + size)));
+                     insn = emit_insn (insn);
+                     RTX_FRAME_RELATED_P (insn) = 1;
+
+                     mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
+                     set_mem_alias_set (mem, get_frame_alias_set ());
+                     insn = cris_emit_movem_store (mem, GEN_INT (n_saved),
+                                                   0, true);
+                   }
+
+                 framesize += n_saved * 4 + size;
+                 last_movem_reg = -1;
+                 size = 0;
                }
-           }
-         else if (cris_gotless_symbol (x))
-           {
-             if (! cris_pic_sympart_only)
-               fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-             assemble_name (file, str);
-             fprintf (file, ":GOTOFF");
-           }
-         else if (cris_got_symbol (x))
-           {
-             if (cris_pic_sympart_only)
-               abort ();
-             fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-             assemble_name (file, XSTR (x, 0));
 
-             if (flag_pic == 1)
-               fprintf (file, ":GOT16]");
-             else
-               fprintf (file, ":GOT]");
+             insn = emit_insn (gen_rtx_SET (VOIDmode,
+                                            stack_pointer_rtx,
+                                            plus_constant (stack_pointer_rtx,
+                                                           -4 - size)));
+             RTX_FRAME_RELATED_P (insn) = 1;
+
+             mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
+             set_mem_alias_set (mem, get_frame_alias_set ());
+             insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno));
+             RTX_FRAME_RELATED_P (insn) = 1;
+
+             framesize += 4 + size;
+             size = 0;
            }
-         else
-           LOSE_AND_RETURN ("unexpected PIC symbol", x);
+       }
+    }
+
+  /* Check after, if we could movem all registers.  This is the normal case.  */
+  if (last_movem_reg != -1)
+    {
+      int n_saved
+       = (n_movem_regs == 1) ? 1 : last_movem_reg + 1;
 
-         /* Sanity check.  */
-         if (! current_function_uses_pic_offset_table)
-           output_operand_lossage ("PIC register isn't set up");
+      /* Side-effect on movem was not usable for CRIS v0..3.  Also only
+        do it if side-effects insns are allowed.  */
+      if ((last_movem_reg + 1) * 4 + size >= 64
+         && (last_movem_reg + 1) * 4 + size <= 128
+         && (cris_cpu_version >= CRIS_CPU_SVINTO || n_saved == 1)
+         && TARGET_SIDE_EFFECT_PREFIXES)
+       {
+         mem
+           = gen_rtx_MEM (SImode,
+                          plus_constant (stack_pointer_rtx,
+                                         -(n_saved * 4 + size)));
+         set_mem_alias_set (mem, get_frame_alias_set ());
+         insn = cris_emit_movem_store (mem, GEN_INT (n_saved),
+                                       -(n_saved * 4 + size), true);
        }
       else
-       assemble_name (file, XSTR (x, 0));
-      break;
+       {
+         insn
+           = gen_rtx_SET (VOIDmode,
+                          stack_pointer_rtx,
+                          plus_constant (stack_pointer_rtx,
+                                         -(n_saved * 4 + size)));
+         insn = emit_insn (insn);
+         RTX_FRAME_RELATED_P (insn) = 1;
+
+         mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
+         set_mem_alias_set (mem, get_frame_alias_set ());
+         insn = cris_emit_movem_store (mem, GEN_INT (n_saved), 0, true);
+       }
 
-    case LABEL_REF:
-      /* If we get one of those here, it should be dressed as PIC.  Branch
-        labels are normally output with the 'l' specifier, which means it
-        will go directly to output_asm_label and not end up here.  */
-      if (GET_CODE (XEXP (x, 0)) != CODE_LABEL
-         && (GET_CODE (XEXP (x, 0)) != NOTE
-             || NOTE_LINE_NUMBER (XEXP (x, 0)) != NOTE_INSN_DELETED_LABEL))
-       fatal_insn ("unexpected address expression", x);
-
-      if (flag_pic)
+      framesize += n_saved * 4 + size;
+      /* We have to put outgoing argument space after regs.  */
+      if (cfoa_size)
        {
-         if (cris_gotless_symbol (x))
-           {
-             if (! cris_pic_sympart_only)
-               fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-             cris_output_addr_const (file, XEXP (x, 0));
+         insn = emit_insn (gen_rtx_SET (VOIDmode,
+                                        stack_pointer_rtx,
+                                        plus_constant (stack_pointer_rtx,
+                                                       -cfoa_size)));
+         RTX_FRAME_RELATED_P (insn) = 1;
+         framesize += cfoa_size;
+       }
+    }
+  else if ((size + cfoa_size) > 0)
+    {
+      insn = emit_insn (gen_rtx_SET (VOIDmode,
+                                    stack_pointer_rtx,
+                                    plus_constant (stack_pointer_rtx,
+                                                   -(cfoa_size + size))));
+      RTX_FRAME_RELATED_P (insn) = 1;
+      framesize += size + cfoa_size;
+    }
 
-             fprintf (file, ":GOTOFF");
-           }
-         else
-           /* Labels are never marked as global symbols.  */
-           fatal_insn ("unexpected PIC symbol", x);
+  /* Set up the PIC register, if it is used.  */
+  if (got_really_used)
+    {
+      rtx got
+       = gen_rtx_UNSPEC (SImode, gen_rtvec (1, const0_rtx), CRIS_UNSPEC_GOT);
+      emit_move_insn (pic_offset_table_rtx, got);
+
+      /* FIXME: This is a cover-up for flow2 messing up; it doesn't
+        follow exceptional paths and tries to delete the GOT load as
+        unused, if it isn't used on the non-exceptional paths.  Other
+        ports have similar or other cover-ups, or plain bugs marking
+        the GOT register load as maybe-dead.  To see this, remove the
+        line below and try libsupc++/vec.cc or a trivial
+        "static void y (); void x () {try {y ();} catch (...) {}}".  */
+      emit_use (pic_offset_table_rtx);
+    }
+
+  if (cris_max_stackframe && framesize > cris_max_stackframe)
+    warning (0, "stackframe too big: %d bytes", framesize);
+}
 
-         /* Sanity check.  */
-         if (! current_function_uses_pic_offset_table)
-           internal_error ("emitting PIC operand, but PIC register isn't set up");
+/* The expander for the epilogue pattern.  */
+
+void
+cris_expand_epilogue (void)
+{
+  int regno;
+  int size = get_frame_size ();
+  int last_movem_reg = -1;
+  int argspace_offset = crtl->outgoing_args_size;
+  int pretend =         crtl->args.pretend_args_size;
+  rtx mem;
+  bool return_address_on_stack = cris_return_address_on_stack ();
+  /* A reference may have been optimized out
+     (like the abort () in fde_split in unwind-dw2-fde.c, at least 3.2.1)
+     so check that it's still used.  */
+  int got_really_used = false;
+  int n_movem_regs = 0;
+
+  if (!TARGET_PROLOGUE_EPILOGUE)
+    return;
+
+  if (crtl->uses_pic_offset_table)
+    {
+      /* A reference may have been optimized out (like the abort () in
+        fde_split in unwind-dw2-fde.c, at least 3.2.1) so check that
+        it's still used.  */
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
+      pop_topmost_sequence ();
+    }
+
+  /* Align byte count of stack frame.  */
+  if (TARGET_STACK_ALIGN)
+    size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
+
+  /* Check how many saved regs we can movem.  They start at r0 and must
+     be contiguous.  */
+  for (regno = 0;
+       regno < FIRST_PSEUDO_REGISTER;
+       regno++)
+    if (cris_reg_saved_in_regsave_area (regno, got_really_used))
+      {
+       n_movem_regs++;
+
+       if (regno == last_movem_reg + 1)
+         last_movem_reg = regno;
+       else
          break;
+      }
+
+  /* If there was only one register that really needed to be saved
+     through movem, don't use movem.  */
+  if (n_movem_regs == 1)
+    last_movem_reg = -1;
+
+  /* Now emit "normal" move insns for all regs higher than the movem
+     regs.  */
+  for (regno = FIRST_PSEUDO_REGISTER - 1;
+       regno > last_movem_reg;
+       regno--)
+    if (cris_reg_saved_in_regsave_area (regno, got_really_used))
+      {
+       rtx insn;
+
+       if (argspace_offset)
+         {
+           /* There is an area for outgoing parameters located before
+              the saved registers.  We have to adjust for that.  */
+           emit_insn (gen_rtx_SET (VOIDmode,
+                                   stack_pointer_rtx,
+                                   plus_constant (stack_pointer_rtx,
+                                                  argspace_offset)));
+           /* Make sure we only do this once.  */
+           argspace_offset = 0;
+         }
+
+       mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
+                                                    stack_pointer_rtx));
+       set_mem_alias_set (mem, get_frame_alias_set ());
+       insn = emit_move_insn (gen_rtx_raw_REG (SImode, regno), mem);
+
+       /* Whenever we emit insns with post-incremented addresses
+          ourselves, we must add a post-inc note manually.  */
+       REG_NOTES (insn)
+         = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
+      }
+
+  /* If we have any movem-restore, do it now.  */
+  if (last_movem_reg != -1)
+    {
+      rtx insn;
+
+      if (argspace_offset)
+       {
+         emit_insn (gen_rtx_SET (VOIDmode,
+                                 stack_pointer_rtx,
+                                 plus_constant (stack_pointer_rtx,
+                                                argspace_offset)));
+         argspace_offset = 0;
        }
 
-      output_addr_const (file, x);
-      break;
+      mem = gen_rtx_MEM (SImode,
+                        gen_rtx_POST_INC (SImode, stack_pointer_rtx));
+      set_mem_alias_set (mem, get_frame_alias_set ());
+      insn
+       = emit_insn (cris_gen_movem_load (mem,
+                                         GEN_INT (last_movem_reg + 1), 0));
+      /* Whenever we emit insns with post-incremented addresses
+        ourselves, we must add a post-inc note manually.  */
+      if (side_effects_p (PATTERN (insn)))
+       REG_NOTES (insn)
+         = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
+    }
 
-    case NOTE:
-      if (NOTE_LINE_NUMBER (x) != NOTE_INSN_DELETED_LABEL)
-       fatal_insn ("unexpected NOTE as addr_const:", x);
-    case CODE_LABEL:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case ZERO_EXTEND:
-    case SIGN_EXTEND:
-      output_addr_const (file, x);
-      break;
+  /* If we don't clobber all of the allocated stack area (we've already
+     deallocated saved registers), GCC might want to schedule loads from
+     the stack to *after* the stack-pointer restore, which introduces an
+     interrupt race condition.  This happened for the initial-value
+     SRP-restore for g++.dg/eh/registers1.C (noticed by inspection of
+     other failure for that test).  It also happened for the stack slot
+     for the return value in (one version of)
+     linux/fs/dcache.c:__d_lookup, at least with "-O2
+     -fno-omit-frame-pointer".  */
 
-    case CONST:
-      /* This used to output parentheses around the expression,
-        but that does not work on the 386 (either ATT or BSD assembler).  */
-      cris_output_addr_const (file, XEXP (x, 0));
-      break;
+  /* Restore frame pointer if necessary.  */
+  if (frame_pointer_needed)
+    {
+      rtx insn;
 
-    case PLUS:
-      /* Some assemblers need integer constants to appear last (eg masm).  */
-      if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+      emit_insn (gen_cris_frame_deallocated_barrier ());
+
+      emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+      mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
+                                                  stack_pointer_rtx));
+      set_mem_alias_set (mem, get_frame_alias_set ());
+      insn = emit_move_insn (frame_pointer_rtx, mem);
+
+      /* Whenever we emit insns with post-incremented addresses
+        ourselves, we must add a post-inc note manually.  */
+      REG_NOTES (insn)
+       = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
+    }
+  else if ((size + argspace_offset) != 0)
+    {
+      emit_insn (gen_cris_frame_deallocated_barrier ());
+
+      /* If there was no frame-pointer to restore sp from, we must
+        explicitly deallocate local variables.  */
+
+      /* Handle space for outgoing parameters that hasn't been handled
+        yet.  */
+      size += argspace_offset;
+
+      emit_insn (gen_rtx_SET (VOIDmode,
+                             stack_pointer_rtx,
+                             plus_constant (stack_pointer_rtx, size)));
+    }
+
+  /* If this function has no pushed register parameters
+     (stdargs/varargs), and if it is not a leaf function, then we have
+     the return address on the stack.  */
+  if (return_address_on_stack && pretend == 0)
+    {
+      if (TARGET_V32 || crtl->calls_eh_return)
        {
-         cris_output_addr_const (file, XEXP (x, 1));
-         if (INTVAL (XEXP (x, 0)) >= 0)
-           fprintf (file, "+");
-         output_addr_const (file, XEXP (x, 0));
+         rtx mem;
+         rtx insn;
+         rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM);
+         mem = gen_rtx_MEM (SImode,
+                            gen_rtx_POST_INC (SImode,
+                                              stack_pointer_rtx));
+         set_mem_alias_set (mem, get_frame_alias_set ());
+         insn = emit_move_insn (srpreg, mem);
+
+         /* Whenever we emit insns with post-incremented addresses
+            ourselves, we must add a post-inc note manually.  */
+         REG_NOTES (insn)
+           = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
+
+         if (crtl->calls_eh_return)
+           emit_insn (gen_addsi3 (stack_pointer_rtx,
+                                  stack_pointer_rtx,
+                                  gen_rtx_raw_REG (SImode,
+                                                   CRIS_STACKADJ_REG)));
+         cris_expand_return (false);
        }
       else
+       cris_expand_return (true);
+
+      return;
+    }
+
+  /* If we pushed some register parameters, then adjust the stack for
+     them.  */
+  if (pretend != 0)
+    {
+      /* If SRP is stored on the way, we need to restore it first.  */
+      if (return_address_on_stack)
        {
-         cris_output_addr_const (file, XEXP (x, 0));
-         if (GET_CODE (XEXP (x, 1)) != CONST_INT
-             || INTVAL (XEXP (x, 1)) >= 0)
-           fprintf (file, "+");
-         cris_output_addr_const (file, XEXP (x, 1));
+         rtx mem;
+         rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM);
+         rtx insn;
+
+         mem = gen_rtx_MEM (SImode,
+                            gen_rtx_POST_INC (SImode,
+                                              stack_pointer_rtx));
+         set_mem_alias_set (mem, get_frame_alias_set ());
+         insn = emit_move_insn (srpreg, mem);
+
+         /* Whenever we emit insns with post-incremented addresses
+            ourselves, we must add a post-inc note manually.  */
+         REG_NOTES (insn)
+           = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
        }
-      break;
 
-    case MINUS:
-      /* Avoid outputting things like x-x or x+5-x,
-        since some assemblers can't handle that.  */
-      x = simplify_subtraction (x);
-      if (GET_CODE (x) != MINUS)
-       goto restart;
-
-      cris_output_addr_const (file, XEXP (x, 0));
-      fprintf (file, "-");
-      if ((GET_CODE (XEXP (x, 1)) == CONST_INT
-          && INTVAL (XEXP (x, 1)) < 0)
-         || GET_CODE (XEXP (x, 1)) != CONST_INT)
+      emit_insn (gen_rtx_SET (VOIDmode,
+                             stack_pointer_rtx,
+                             plus_constant (stack_pointer_rtx, pretend)));
+    }
+
+  /* Perform the "physical" unwinding that the EH machinery calculated.  */
+  if (crtl->calls_eh_return)
+    emit_insn (gen_addsi3 (stack_pointer_rtx,
+                          stack_pointer_rtx,
+                          gen_rtx_raw_REG (SImode,
+                                           CRIS_STACKADJ_REG)));
+  cris_expand_return (false);
+}
+
+/* Worker function for generating movem from mem for load_multiple.  */
+
+rtx
+cris_gen_movem_load (rtx src, rtx nregs_rtx, int nprefix)
+{
+  int nregs = INTVAL (nregs_rtx);
+  rtvec vec;
+  int eltno = 1;
+  int i;
+  rtx srcreg = XEXP (src, 0);
+  unsigned int regno = nregs - 1;
+  int regno_inc = -1;
+
+  if (TARGET_V32)
+    {
+      regno = 0;
+      regno_inc = 1;
+    }
+
+  if (GET_CODE (srcreg) == POST_INC)
+    srcreg = XEXP (srcreg, 0);
+
+  CRIS_ASSERT (REG_P (srcreg));
+
+  /* Don't use movem for just one insn.  The insns are equivalent except
+     for the pipeline hazard (on v32); movem does not forward the loaded
+     registers so there's a three cycles penalty for their use.  */
+  if (nregs == 1)
+    return gen_movsi (gen_rtx_REG (SImode, 0), src);
+
+  vec = rtvec_alloc (nprefix + nregs
+                    + (GET_CODE (XEXP (src, 0)) == POST_INC));
+
+  if (GET_CODE (XEXP (src, 0)) == POST_INC)
+    {
+      RTVEC_ELT (vec, nprefix + 1)
+       = gen_rtx_SET (VOIDmode, srcreg, plus_constant (srcreg, nregs * 4));
+      eltno++;
+    }
+
+  src = replace_equiv_address (src, srcreg);
+  RTVEC_ELT (vec, nprefix)
+    = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), src);
+  regno += regno_inc;
+
+  for (i = 1; i < nregs; i++, eltno++)
+    {
+      RTVEC_ELT (vec, nprefix + eltno)
+       = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno),
+                      adjust_address_nv (src, SImode, i * 4));
+      regno += regno_inc;
+    }
+
+  return gen_rtx_PARALLEL (VOIDmode, vec);
+}
+
+/* Worker function for generating movem to mem.  If FRAME_RELATED, notes
+   are added that the dwarf2 machinery understands.  */
+
+rtx
+cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
+                      bool frame_related)
+{
+  int nregs = INTVAL (nregs_rtx);
+  rtvec vec;
+  int eltno = 1;
+  int i;
+  rtx insn;
+  rtx destreg = XEXP (dest, 0);
+  unsigned int regno = nregs - 1;
+  int regno_inc = -1;
+
+  if (TARGET_V32)
+    {
+      regno = 0;
+      regno_inc = 1;
+    }
+
+  if (GET_CODE (destreg) == POST_INC)
+    increment += nregs * 4;
+
+  if (GET_CODE (destreg) == POST_INC || GET_CODE (destreg) == PLUS)
+    destreg = XEXP (destreg, 0);
+
+  CRIS_ASSERT (REG_P (destreg));
+
+  /* Don't use movem for just one insn.  The insns are equivalent except
+     for the pipeline hazard (on v32); movem does not forward the loaded
+     registers so there's a three cycles penalty for use.  */
+  if (nregs == 1)
+    {
+      rtx mov = gen_rtx_SET (VOIDmode, dest, gen_rtx_REG (SImode, 0));
+
+      if (increment == 0)
        {
-         fprintf (file, "%s", targetm.asm_out.open_paren);
-         cris_output_addr_const (file, XEXP (x, 1));
-         fprintf (file, "%s", targetm.asm_out.close_paren);
+         insn = emit_insn (mov);
+         if (frame_related)
+           RTX_FRAME_RELATED_P (insn) = 1;
+         return insn;
        }
-      else
-       output_addr_const (file, XEXP (x, 1));
-      break;
 
-    default:
-      LOSE_AND_RETURN ("unexpected address expression", x);
+      /* If there was a request for a side-effect, create the ordinary
+         parallel.  */
+      vec = rtvec_alloc (2);
+
+      RTVEC_ELT (vec, 0) = mov;
+      RTVEC_ELT (vec, 1) = gen_rtx_SET (VOIDmode, destreg,
+                                       plus_constant (destreg, increment));
+      if (frame_related)
+       {
+         RTX_FRAME_RELATED_P (mov) = 1;
+         RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1;
+       }
+    }
+  else
+    {
+      vec = rtvec_alloc (nregs + (increment != 0 ? 1 : 0));
+      RTVEC_ELT (vec, 0)
+       = gen_rtx_SET (VOIDmode,
+                      replace_equiv_address (dest,
+                                             plus_constant (destreg,
+                                                            increment)),
+                      gen_rtx_REG (SImode, regno));
+      regno += regno_inc;
+
+      /* The dwarf2 info wants this mark on each component in a parallel
+        that's part of the prologue (though it's optional on the first
+        component).  */
+      if (frame_related)
+       RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 0)) = 1;
+
+      if (increment != 0)
+       {
+         RTVEC_ELT (vec, 1)
+           = gen_rtx_SET (VOIDmode, destreg,
+                          plus_constant (destreg,
+                                         increment != 0
+                                         ? increment : nregs * 4));
+         eltno++;
+
+         if (frame_related)
+           RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1;
+
+         /* Don't call adjust_address_nv on a post-incremented address if
+            we can help it.  */
+         if (GET_CODE (XEXP (dest, 0)) == POST_INC)
+           dest = replace_equiv_address (dest, destreg);
+       }
+
+      for (i = 1; i < nregs; i++, eltno++)
+       {
+         RTVEC_ELT (vec, eltno)
+           = gen_rtx_SET (VOIDmode, adjust_address_nv (dest, SImode, i * 4),
+                          gen_rtx_REG (SImode, regno));
+         if (frame_related)
+           RTX_FRAME_RELATED_P (RTVEC_ELT (vec, eltno)) = 1;
+         regno += regno_inc;
+       }
+    }
+
+  insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, vec));
+
+  /* Because dwarf2out.c handles the insns in a parallel as a sequence,
+     we need to keep the stack adjustment separate, after the
+     MEM-setters.  Else the stack-adjustment in the second component of
+     the parallel would be mishandled; the offsets for the SETs that
+     follow it would be wrong.  We prepare for this by adding a
+     REG_FRAME_RELATED_EXPR with the MEM-setting parts in a SEQUENCE
+     followed by the increment.  Note that we have FRAME_RELATED_P on
+     all the SETs, including the original stack adjustment SET in the
+     parallel.  */
+  if (frame_related)
+    {
+      if (increment != 0)
+       {
+         rtx seq = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (nregs + 1));
+         XVECEXP (seq, 0, 0) = copy_rtx (XVECEXP (PATTERN (insn), 0, 0));
+         for (i = 1; i < nregs; i++)
+           XVECEXP (seq, 0, i)
+             = copy_rtx (XVECEXP (PATTERN (insn), 0, i + 1));
+         XVECEXP (seq, 0, nregs) = copy_rtx (XVECEXP (PATTERN (insn), 0, 1));
+         REG_NOTES (insn)
+           = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, seq,
+                                REG_NOTES (insn));
+       }
+
+      RTX_FRAME_RELATED_P (insn) = 1;
     }
+
+  return insn;
 }
 
-/* The ENCODE_SECTION_INFO worker.  Code-in whether we can get away
-   without a GOT entry (needed for externally visible objects but not for
-   functions) into SYMBOL_REF_FLAG and add the PLT suffix for global
-   functions.  */
+/* Worker function for expanding the address for PIC function calls.  */
 
 void
-cris_encode_section_info (exp)
-     tree exp;
+cris_expand_pic_call_address (rtx *opp)
 {
-  if (flag_pic)
+  rtx op = *opp;
+
+  gcc_assert (MEM_P (op));
+  op = XEXP (op, 0);
+
+  /* It might be that code can be generated that jumps to 0 (or to a
+     specific address).  Don't die on that.  (There is a
+     testcase.)  */
+  if (CONSTANT_ADDRESS_P (op) && !CONST_INT_P (op))
     {
-      if (DECL_P (exp))
+      enum cris_pic_symbol_type t = cris_pic_symbol_type_of (op);
+
+      CRIS_ASSERT (can_create_pseudo_p ());
+
+      /* For local symbols (non-PLT), just get the plain symbol
+        reference into a register.  For symbols that can be PLT, make
+        them PLT.  */
+      if (t == cris_rel_symbol)
+       {
+         /* For v32, we're fine as-is; just PICify the symbol.  Forcing
+            into a register caused performance regression for 3.2.1,
+            observable in __floatdidf and elsewhere in libgcc.  */
+         if (TARGET_V32)
+           {
+             rtx sym = GET_CODE (op) != CONST ? op : get_related_value (op);
+             HOST_WIDE_INT offs = get_integer_term (op);
+
+             /* We can't get calls to sym+N, N integer, can we?  */
+             gcc_assert (offs == 0);
+
+             op = gen_rtx_CONST (Pmode,
+                                 gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
+                                                 CRIS_UNSPEC_PCREL));
+           }
+         else
+           op = force_reg (Pmode, op);
+       }
+      else if (t == cris_got_symbol)
        {
-         if (TREE_CODE (exp) == FUNCTION_DECL
-             && (TREE_PUBLIC (exp) || DECL_WEAK (exp)))
-           SYMBOL_REF_FLAG (XEXP (DECL_RTL (exp), 0)) = 0;
+         if (TARGET_AVOID_GOTPLT)
+           {
+             /* Change a "jsr sym" into (allocate register rM, rO)
+                "move.d (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_GOTREL)),rM"
+                "add.d rPIC,rM,rO", "jsr rO" for pre-v32 and
+                "jsr (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_PCREL))"
+                for v32.  */
+             rtx tem, rm, ro;
+             gcc_assert (can_create_pseudo_p ());
+             crtl->uses_pic_offset_table = 1;
+             tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op),
+                                   TARGET_V32
+                                   ? CRIS_UNSPEC_PLT_PCREL
+                                   : CRIS_UNSPEC_PLT_GOTREL);
+             tem = gen_rtx_CONST (Pmode, tem);
+             if (TARGET_V32)
+               op = tem;
+             else
+               {
+                 rm = gen_reg_rtx (Pmode);
+                 emit_move_insn (rm, tem);
+                 ro = gen_reg_rtx (Pmode);
+                 if (expand_binop (Pmode, add_optab, rm,
+                                   pic_offset_table_rtx,
+                                   ro, 0, OPTAB_LIB_WIDEN) != ro)
+                   internal_error ("expand_binop failed in movsi got");
+                 op = ro;
+               }
+           }
          else
-           SYMBOL_REF_FLAG (XEXP (DECL_RTL (exp), 0))
-             = ! TREE_PUBLIC (exp) && ! DECL_WEAK (exp);
+           {
+             /* Change a "jsr sym" into (allocate register rM, rO)
+                "move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOTREAD)),rM"
+                "add.d rPIC,rM,rO" "jsr [rO]" with the memory access
+                marked as not trapping and not aliasing.  No "move.d
+                [rO],rP" as that would invite to re-use of a value
+                that should not be reused.  FIXME: Need a peephole2
+                for cases when this is cse:d from the call, to change
+                back to just get the PLT entry address, so we don't
+                resolve the same symbol over and over (the memory
+                access of the PLTGOT isn't constant).  */
+             rtx tem, mem, rm, ro;
+
+             gcc_assert (can_create_pseudo_p ());
+             crtl->uses_pic_offset_table = 1;
+             tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op),
+                                   CRIS_UNSPEC_PLTGOTREAD);
+             rm = gen_reg_rtx (Pmode);
+             emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+             ro = gen_reg_rtx (Pmode);
+             if (expand_binop (Pmode, add_optab, rm,
+                               pic_offset_table_rtx,
+                               ro, 0, OPTAB_LIB_WIDEN) != ro)
+               internal_error ("expand_binop failed in movsi got");
+             mem = gen_rtx_MEM (Pmode, ro);
+
+             /* This MEM doesn't alias anything.  Whether it aliases
+                other same symbols is unimportant.  */
+             set_mem_alias_set (mem, new_alias_set ());
+             MEM_NOTRAP_P (mem) = 1;
+             op = mem;
+           }
        }
       else
-       /* Others are local entities.  */
-       SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (exp), 0)) = 1;
+       /* Can't possibly get a GOT-needing-fixup for a function-call,
+          right?  */
+       fatal_insn ("Unidentifiable call op", op);
+
+      *opp = replace_equiv_address (*opp, op);
+    }
+}
+
+/* Make sure operands are in the right order for an addsi3 insn as
+   generated by a define_split.  Nothing but REG_P as the first
+   operand is recognized by addsi3 after reload.  OPERANDS contains
+   the operands, with the first at OPERANDS[N] and the second at
+   OPERANDS[N+1].  */
+
+void
+cris_order_for_addsi3 (rtx *operands, int n)
+{
+  if (!REG_P (operands[n]))
+    {
+      rtx tem = operands[n];
+      operands[n] = operands[n + 1];
+      operands[n + 1] = tem;
+    }
+}
+
+/* Use from within code, from e.g. PRINT_OPERAND and
+   PRINT_OPERAND_ADDRESS.  Macros used in output_addr_const need to emit
+   different things depending on whether code operand or constant is
+   emitted.  */
+
+static void
+cris_output_addr_const (FILE *file, rtx x)
+{
+  in_code++;
+  output_addr_const (file, x);
+  in_code--;
+}
+
+/* Worker function for ASM_OUTPUT_SYMBOL_REF.  */
+
+void
+cris_asm_output_symbol_ref (FILE *file, rtx x)
+{
+  gcc_assert (GET_CODE (x) == SYMBOL_REF);
+
+  if (flag_pic && in_code > 0)
+    {
+     const char *origstr = XSTR (x, 0);
+     const char *str;
+     str = (* targetm.strip_name_encoding) (origstr);
+     assemble_name (file, str);
+
+     /* Sanity check.  */
+     if (!TARGET_V32 && !crtl->uses_pic_offset_table)
+       output_operand_lossage ("PIC register isn't set up");
+    }
+  else
+    assemble_name (file, XSTR (x, 0));
+}
+
+/* Worker function for ASM_OUTPUT_LABEL_REF.  */
+
+void
+cris_asm_output_label_ref (FILE *file, char *buf)
+{
+  if (flag_pic && in_code > 0)
+    {
+      assemble_name (file, buf);
+
+      /* Sanity check.  */
+      if (!TARGET_V32 && !crtl->uses_pic_offset_table)
+       internal_error ("emitting PIC operand, but PIC register isn't set up");
+    }
+  else
+    assemble_name (file, buf);
+}
+
+/* Worker function for OUTPUT_ADDR_CONST_EXTRA.  */
+
+bool
+cris_output_addr_const_extra (FILE *file, rtx xconst)
+{
+  switch (GET_CODE (xconst))
+    {
+      rtx x;
+
+    case UNSPEC:
+      x = XVECEXP (xconst, 0, 0);
+      CRIS_ASSERT (GET_CODE (x) == SYMBOL_REF
+                  || GET_CODE (x) == LABEL_REF
+                  || GET_CODE (x) == CONST);
+      output_addr_const (file, x);
+      switch (XINT (xconst, 1))
+       {
+       case CRIS_UNSPEC_PCREL:
+         /* We only get this with -fpic/PIC to tell it apart from an
+            invalid symbol.  We can't tell here, but it should only
+            be the operand of a call or movsi.  */
+         gcc_assert (TARGET_V32 && flag_pic);
+         break;
+
+       case CRIS_UNSPEC_PLT_PCREL:
+         gcc_assert (TARGET_V32);
+         fprintf (file, ":PLT");
+         break;
+
+       case CRIS_UNSPEC_PLT_GOTREL:
+         gcc_assert (!TARGET_V32);
+         fprintf (file, ":PLTG");
+         break;
+
+       case CRIS_UNSPEC_GOTREL:
+         gcc_assert (!TARGET_V32);
+         fprintf (file, ":GOTOFF");
+         break;
+
+       case CRIS_UNSPEC_GOTREAD:
+         if (flag_pic == 1)
+           fprintf (file, ":GOT16");
+         else
+           fprintf (file, ":GOT");
+         break;
+
+       case CRIS_UNSPEC_PLTGOTREAD:
+         if (flag_pic == 1)
+           fprintf (file, CRIS_GOTPLT_SUFFIX "16");
+         else
+           fprintf (file, CRIS_GOTPLT_SUFFIX);
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX.  */
+
+static rtx
+cris_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+                      int incoming ATTRIBUTE_UNUSED)
+{
+  return gen_rtx_REG (Pmode, CRIS_STRUCT_VALUE_REGNUM);
+}
+
+/* Worker function for TARGET_SETUP_INCOMING_VARARGS.  */
+
+static void
+cris_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
+                            enum machine_mode mode ATTRIBUTE_UNUSED,
+                            tree type ATTRIBUTE_UNUSED,
+                            int *pretend_arg_size,
+                            int second_time)
+{
+  if (ca->regs < CRIS_MAX_ARGS_IN_REGS)
+    {
+      int stdarg_regs = CRIS_MAX_ARGS_IN_REGS - ca->regs;
+      cfun->machine->stdarg_regs = stdarg_regs;
+      *pretend_arg_size = stdarg_regs * 4;
+    }
+
+  if (TARGET_PDEBUG)
+    fprintf (asm_out_file,
+            "\n; VA:: ANSI: %d args before, anon @ #%d, %dtime\n",
+            ca->regs, *pretend_arg_size, second_time);
+}
+
+/* Return true if TYPE must be passed by invisible reference.
+   For cris, we pass <= 8 bytes by value, others by reference.  */
+
+static bool
+cris_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+                       enum machine_mode mode, const_tree type,
+                       bool named ATTRIBUTE_UNUSED)
+{
+  return (targetm.calls.must_pass_in_stack (mode, type)
+         || CRIS_FUNCTION_ARG_SIZE (mode, type) > 8);
+}
+
+
+static int
+cris_arg_partial_bytes (CUMULATIVE_ARGS *ca, enum machine_mode mode,
+                       tree type, bool named ATTRIBUTE_UNUSED)
+{
+  if (ca->regs == CRIS_MAX_ARGS_IN_REGS - 1
+      && !targetm.calls.must_pass_in_stack (mode, type)
+      && CRIS_FUNCTION_ARG_SIZE (mode, type) > 4
+      && CRIS_FUNCTION_ARG_SIZE (mode, type) <= 8)
+    return UNITS_PER_WORD;
+  else
+    return 0;
+}
+
+/* Worker function for TARGET_MD_ASM_CLOBBERS.  */
+
+static tree
+cris_md_asm_clobbers (tree outputs, tree inputs, tree in_clobbers)
+{
+  HARD_REG_SET mof_set;
+  tree clobbers;
+  tree t;
+
+  CLEAR_HARD_REG_SET (mof_set);
+  SET_HARD_REG_BIT (mof_set, CRIS_MOF_REGNUM);
+
+  /* For the time being, all asms clobber condition codes.  Revisit when
+     there's a reasonable use for inputs/outputs that mention condition
+     codes.  */
+  clobbers
+    = tree_cons (NULL_TREE,
+                build_string (strlen (reg_names[CRIS_CC0_REGNUM]),
+                              reg_names[CRIS_CC0_REGNUM]),
+                in_clobbers);
+
+  for (t = outputs; t != NULL; t = TREE_CHAIN (t))
+    {
+      tree val = TREE_VALUE (t);
+
+      /* The constraint letter for the singleton register class of MOF
+        is 'h'.  If it's mentioned in the constraints, the asm is
+        MOF-aware and adding it to the clobbers would cause it to have
+        impossible constraints.  */
+      if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
+                 'h') != NULL
+         || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
+       return clobbers;
     }
+
+  for (t = inputs; t != NULL; t = TREE_CHAIN (t))
+    {
+      tree val = TREE_VALUE (t);
+
+      if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
+                 'h') != NULL
+         || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
+       return clobbers;
+    }
+
+  return tree_cons (NULL_TREE,
+                   build_string (strlen (reg_names[CRIS_MOF_REGNUM]),
+                                 reg_names[CRIS_MOF_REGNUM]),
+                   clobbers);
 }
 
 #if 0
@@ -3072,49 +3809,42 @@ cris_encode_section_info (exp)
    debugger.  They might collide with gcc functions or system functions,
    so only emit them when '#if 1' above.  */
 
-enum rtx_code Get_code PARAMS ((rtx));
+enum rtx_code Get_code (rtx);
 
 enum rtx_code
-Get_code (x)
-     rtx x;
+Get_code (rtx x)
 {
   return GET_CODE (x);
 }
 
-const char *Get_mode PARAMS ((rtx));
+const char *Get_mode (rtx);
 
 const char *
-Get_mode (x)
-     rtx x;
+Get_mode (rtx x)
 {
   return GET_MODE_NAME (GET_MODE (x));
 }
 
-rtx Xexp PARAMS ((rtx, int));
+rtx Xexp (rtx, int);
 
 rtx
-Xexp (x, n)
-     rtx x;
-     int n;
+Xexp (rtx x, int n)
 {
   return XEXP (x, n);
 }
 
-rtx Xvecexp PARAMS ((rtx, int, int));
+rtx Xvecexp (rtx, int, int);
 
 rtx
-Xvecexp (x, n, m)
-     rtx x;
-     int n;
+Xvecexp (rtx x, int n, int m)
 {
   return XVECEXP (x, n, m);
 }
 
-int Get_rtx_len PARAMS ((rtx));
+int Get_rtx_len (rtx);
 
 int
-Get_rtx_len (x)
-     rtx x;
+Get_rtx_len (rtx x)
 {
   return GET_RTX_LENGTH (GET_CODE (x));
 }
@@ -3122,25 +3852,25 @@ Get_rtx_len (x)
 /* Use upper-case to distinguish from local variables that are sometimes
    called next_insn and prev_insn.  */
 
-rtx Next_insn PARAMS ((rtx));
+rtx Next_insn (rtx);
 
 rtx
-Next_insn (insn)
-     rtx insn;
+Next_insn (rtx insn)
 {
   return NEXT_INSN (insn);
 }
 
-rtx Prev_insn PARAMS ((rtx));
+rtx Prev_insn (rtx);
 
 rtx
-Prev_insn (insn)
-     rtx insn;
+Prev_insn (rtx insn)
 {
   return PREV_INSN (insn);
 }
 #endif
 
+#include "gt-cris.h"
+
 /*
  * Local variables:
  * eval: (c-set-style "gnu")