OSDN Git Service

2009-08-28 Sebastian Pop <sebastian.pop@amd.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / cris / cris.c
index 9c9fb16..6b3fd0f 100644 (file)
@@ -1,13 +1,13 @@
 /* Definitions for GCC.  Part of the machine description for CRIS.
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-   Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+   2008, 2009  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,
@@ -16,9 +16,8 @@ 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"
@@ -38,6 +37,7 @@ 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"
@@ -45,21 +45,13 @@ Boston, MA 02111-1307, USA.  */
 #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 (XINT (x, 1) != CRIS_UNSPEC_PLT                       \
-         || (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                                           \
     {                                          \
@@ -71,7 +63,7 @@ enum cris_retinsn_type
  { CRIS_RETINSN_UNKNOWN = 0, CRIS_RETINSN_RET, CRIS_RETINSN_JUMP };
 
 /* Per-function machine data.  */
-struct machine_function GTY(())
+struct GTY(()) machine_function
  {
    int needs_return_address_on_stack;
 
@@ -86,12 +78,6 @@ struct machine_function GTY(())
    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;
@@ -99,6 +85,9 @@ static int in_code = 0;
 /* Fix for reg_overlap_mentioned_p.  */
 static int cris_reg_overlap_mentioned_p (rtx, rtx);
 
+static enum machine_mode cris_promote_function_mode (const_tree, enum machine_mode,
+                                                    int *, const_tree, int);
+
 static void cris_print_base (rtx, FILE *);
 
 static void cris_print_index (rtx, FILE *);
@@ -126,27 +115,17 @@ static void cris_asm_output_mi_thunk
 static void cris_file_start (void);
 static void cris_init_libfuncs (void);
 
-static bool cris_rtx_costs (rtx, int, int, int *);
-static int cris_address_cost (rtx);
+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,
-                                   tree, bool);
+                                   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);
 
 static bool cris_handle_option (size_t, const char *, int);
 
-/* This is the argument from the "-max-stack-stackframe=" option.  */
-const char *cris_max_stackframe_str;
-
-/* This is the argument from the "-march=" option.  */
-const char *cris_cpu_str;
-
-/* This is the argument from the "-mtune=" option.  */
-const char *cris_tune_str;
-
-/* This is the argument from the "-melinux-stacksize=" option.  */
-const char *cris_elinux_stacksize_str;
+static bool cris_frame_pointer_required (void);
 
 /* This is the parsed result of the "-max-stack-stackframe=" option.  If
    it (still) is zero, then there was no such option given.  */
@@ -190,8 +169,9 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION;
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST cris_address_cost
 
-#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE cris_promote_function_mode
+
 #undef TARGET_STRUCT_VALUE_RTX
 #define TARGET_STRUCT_VALUE_RTX cris_struct_value_rtx
 #undef TARGET_SETUP_INCOMING_VARARGS
@@ -206,6 +186,8 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION;
 #define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | CRIS_SUBTARGET_DEFAULT)
 #undef TARGET_HANDLE_OPTION
 #define TARGET_HANDLE_OPTION cris_handle_option
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED cris_frame_pointer_required
 
 struct gcc_target targetm = TARGET_INITIALIZER;
 
@@ -226,8 +208,8 @@ cris_movem_load_rest_p (rtx op, int offs)
      other than (MEM reg).  */
   if (reg_count <= 1
       || GET_CODE (XVECEXP (op, 0, offs)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, offs))) != REG
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, offs))) != MEM)
+      || !REG_P (SET_DEST (XVECEXP (op, 0, offs)))
+      || !MEM_P (SET_SRC (XVECEXP (op, 0, offs))))
     return false;
 
   /* Check a possible post-inc indicator.  */
@@ -242,7 +224,7 @@ cris_movem_load_rest_p (rtx op, int offs)
          || !REG_P (reg)
          || !REG_P (SET_DEST (XVECEXP (op, 0, offs + 1)))
          || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, offs + 1)))
-         || GET_CODE (inc) != CONST_INT
+         || !CONST_INT_P (inc)
          || INTVAL (inc) != (HOST_WIDE_INT) reg_count * 4)
        return false;
       i = offs + 2;
@@ -250,18 +232,20 @@ cris_movem_load_rest_p (rtx op, int offs)
   else
     i = offs + 1;
 
-  /* FIXME: These two only for pre-v32.  */
-  regno_dir = -1;
-  regno = reg_count - 1;
+  if (!TARGET_V32)
+    {
+      regno_dir = -1;
+      regno = reg_count - 1;
+    }
 
   elt = XVECEXP (op, 0, offs);
   src_addr = XEXP (SET_SRC (elt), 0);
 
   if (GET_CODE (elt) != SET
-      || GET_CODE (SET_DEST (elt)) != REG
+      || !REG_P (SET_DEST (elt))
       || GET_MODE (SET_DEST (elt)) != SImode
       || REGNO (SET_DEST (elt)) != regno
-      || GET_CODE (SET_SRC (elt)) != MEM
+      || !MEM_P (SET_SRC (elt))
       || GET_MODE (SET_SRC (elt)) != SImode
       || !memory_address_p (SImode, src_addr))
     return false;
@@ -272,14 +256,14 @@ cris_movem_load_rest_p (rtx op, int offs)
       regno += regno_dir;
 
       if (GET_CODE (elt) != SET
-         || GET_CODE (SET_DEST (elt)) != REG
+         || !REG_P (SET_DEST (elt))
          || GET_MODE (SET_DEST (elt)) != SImode
          || REGNO (SET_DEST (elt)) != regno
-         || GET_CODE (SET_SRC (elt)) != MEM
+         || !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)
-         || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
+         || !CONST_INT_P (XEXP (XEXP (SET_SRC (elt), 0), 1))
          || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != setno * 4)
        return false;
     }
@@ -316,8 +300,7 @@ cris_store_multiple_op_p (rtx op)
 
   dest = SET_DEST (elt);
 
-  if (GET_CODE (SET_SRC (elt)) != REG
-      || GET_CODE (dest) != MEM)
+  if (!REG_P (SET_SRC (elt)) || !MEM_P (dest))
     return false;
 
   dest_addr = XEXP (dest, 0);
@@ -334,7 +317,7 @@ cris_store_multiple_op_p (rtx op)
          || !REG_P (reg)
          || !REG_P (SET_DEST (XVECEXP (op, 0, 1)))
          || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, 1)))
-         || GET_CODE (inc) != CONST_INT
+         || !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)).  */
@@ -344,7 +327,7 @@ cris_store_multiple_op_p (rtx op)
               || (GET_CODE (dest_addr) == PLUS
                   && REG_P (XEXP (dest_addr, 0))
                   && REGNO (XEXP (dest_addr, 0)) == REGNO (reg)
-                  && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT
+                  && CONST_INT_P (XEXP (dest_addr, 1))
                   && INTVAL (XEXP (dest_addr, 1)) == INTVAL (inc))))
        return false;
 
@@ -353,15 +336,17 @@ cris_store_multiple_op_p (rtx op)
   else
     i = 1;
 
-  /* FIXME: These two only for pre-v32.  */
-  regno_dir = -1;
-  regno = reg_count - 1;
+  if (!TARGET_V32)
+    {
+      regno_dir = -1;
+      regno = reg_count - 1;
+    }
 
   if (GET_CODE (elt) != SET
-      || GET_CODE (SET_SRC (elt)) != REG
+      || !REG_P (SET_SRC (elt))
       || GET_MODE (SET_SRC (elt)) != SImode
       || REGNO (SET_SRC (elt)) != (unsigned int) regno
-      || GET_CODE (SET_DEST (elt)) != MEM
+      || !MEM_P (SET_DEST (elt))
       || GET_MODE (SET_DEST (elt)) != SImode)
     return false;
 
@@ -372,7 +357,7 @@ cris_store_multiple_op_p (rtx op)
     }
   else if (GET_CODE (dest_addr) == PLUS
           && REG_P (XEXP (dest_addr, 0))
-          && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT)
+          && CONST_INT_P (XEXP (dest_addr, 1)))
     {
       dest_base = XEXP (dest_addr, 0);
       offset = INTVAL (XEXP (dest_addr, 1));
@@ -386,14 +371,14 @@ cris_store_multiple_op_p (rtx op)
       regno += regno_dir;
 
       if (GET_CODE (elt) != SET
-         || GET_CODE (SET_SRC (elt)) != REG
+         || !REG_P (SET_SRC (elt))
          || GET_MODE (SET_SRC (elt)) != SImode
          || REGNO (SET_SRC (elt)) != (unsigned int) regno
-         || GET_CODE (SET_DEST (elt)) != MEM
+         || !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)
-         || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
+         || !CONST_INT_P (XEXP (XEXP (SET_DEST (elt), 0), 1))
          || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != setno * 4 + offset)
        return false;
     }
@@ -412,17 +397,36 @@ cris_conditional_register_usage (void)
     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 (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.
@@ -448,7 +452,7 @@ cris_op_str (rtx x)
         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.  */
-      abort ();
+      internal_error ("MULT case in cris_op_str");
       break;
 
     case DIV:
@@ -484,7 +488,7 @@ cris_op_str (rtx 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";
@@ -513,11 +517,9 @@ cris_operand_lossage (const char *msgid, rtx op)
 static void
 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))
@@ -531,9 +533,9 @@ cris_print_index (rtx index, FILE *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)
@@ -549,8 +551,9 @@ cris_print_index (rtx index, FILE *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
@@ -569,7 +572,10 @@ 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);
@@ -596,17 +602,17 @@ static int
 cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used)
 {
   return
-    (((regs_ever_live[regno]
+    (((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
-                 && regs_ever_live[regno + 1]
+                 && df_regs_ever_live_p (regno + 1)
                  && !call_used_regs[regno + 1]))))
      && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
      && regno != CRIS_SRP_REGNUM)
-    || (current_function_calls_eh_return
+    || (crtl->calls_eh_return
        && (regno == EH_RETURN_DATA_REGNO (0)
            || regno == EH_RETURN_DATA_REGNO (1)
            || regno == EH_RETURN_DATA_REGNO (2)
@@ -685,8 +691,8 @@ cris_print_operand (FILE *file, rtx x, int 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 (GET_CODE (x) != CONST_INT
-         || ! CONST_OK_FOR_LETTER_P (INTVAL (x), 'O'))
+      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));
@@ -697,15 +703,6 @@ cris_print_operand (FILE *file, rtx x, int code)
       fprintf (file, "%s", cris_op_str (operand));
       return;
 
-    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;
-
     case 'o':
       {
        /* A movem modifier working on a parallel; output the register
@@ -736,7 +733,7 @@ cris_print_operand (FILE *file, rtx x, int code)
 
        /* The lowest mem operand is in the first item, but perhaps it
           needs to be output as postincremented.  */
-       addr = GET_CODE (SET_SRC (XVECEXP (x, 0, 0))) == MEM
+       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);
 
@@ -762,17 +759,9 @@ cris_print_operand (FILE *file, rtx x, int code)
       }
       return;
 
-    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]);
-      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;
@@ -782,7 +771,7 @@ cris_print_operand (FILE *file, rtx x, int 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)
            {
@@ -809,13 +798,23 @@ cris_print_operand (FILE *file, rtx x, int 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.  */
@@ -840,16 +839,22 @@ cris_print_operand (FILE *file, rtx x, int code)
               : ".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:
@@ -864,8 +869,12 @@ cris_print_operand (FILE *file, rtx x, int 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;
@@ -899,11 +908,17 @@ cris_print_operand (FILE *file, rtx x, int 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)
@@ -913,7 +928,7 @@ cris_print_operand (FILE *file, rtx x, int 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;
 
@@ -932,7 +947,7 @@ cris_print_operand (FILE *file, rtx x, int code)
          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, HOST_WIDE_INT_PRINT_HEX,
                   INTVAL (x) & ((unsigned int) 0x7fffffff * 2 + 1));
@@ -945,17 +960,22 @@ cris_print_operand (FILE *file, rtx x, int 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, print it as :GOT regardless of -fpic.  */
-      if (flag_pic && CONSTANT_P (operand) && cris_got_symbol (operand))
+      /* 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);
-         fprintf (file, ":GOT");
+         flag_pic = flag_pic_save;
          return;
        }
       break;
@@ -963,7 +983,7 @@ cris_print_operand (FILE *file, rtx x, int code)
     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;
@@ -977,11 +997,22 @@ cris_print_operand (FILE *file, rtx x, int 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;
@@ -996,7 +1027,8 @@ cris_print_operand (FILE *file, rtx x, int code)
     case REG:
       if (REGNO (operand) > 15
          && REGNO (operand) != CRIS_MOF_REGNUM
-         && REGNO (operand) != CRIS_SRP_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;
@@ -1026,9 +1058,7 @@ cris_print_operand (FILE *file, rtx x, int code)
       return;
 
     case UNSPEC:
-      ASSERT_PLT_UNSPEC (operand);
       /* Fall through.  */
-
     case CONST:
       cris_output_addr_const (file, operand);
       return;
@@ -1037,14 +1067,14 @@ cris_print_operand (FILE *file, rtx x, int 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);
@@ -1100,7 +1130,7 @@ cris_print_operand_address (FILE *file, rtx 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);
@@ -1140,7 +1170,7 @@ cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED)
 bool
 cris_return_address_on_stack (void)
 {
-  return regs_ever_live[CRIS_SRP_REGNUM]
+  return df_regs_ever_live_p (CRIS_SRP_REGNUM)
     || cfun->machine->needs_return_address_on_stack;
 }
 
@@ -1164,7 +1194,16 @@ cris_initial_frame_pointer_offset (void)
 
   /* Initial offset is 0 if we don't have a frame pointer.  */
   int offs = 0;
-  bool got_really_used = current_function_uses_pic_offset_table;
+  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++)
@@ -1175,7 +1214,7 @@ cris_initial_frame_pointer_offset (void)
   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)
@@ -1217,190 +1256,245 @@ cris_initial_elimination_offset (int fromreg, int 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 (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 testcases 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
-             && modified_in_p (cc_status.value1, insn))
-           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
-             && modified_in_p (cc_status.value2, insn))
-           cc_status.value2 = 0;
-       }
-      return;
+         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;
+           }
 
-    case CC_CLOBBER:
-      CC_STATUS_INIT;
-      break;
+         gcc_assert (something_reloaded);
 
-    case CC_NORMAL:
-      /* Which means, for:
-        (set (cc0) (...)):
-        CC is (...).
+         return true;
+       }
+    }
 
-        (set (reg) (...)):
-        CC is (reg) and (...) - unless (...) is 0, then CC does not change.
-        CC_NO_OVERFLOW unless (...) is reg or mem.
+  return false;
+}
 
-        (set (mem) (...)):
-        CC does not change.
+/* Worker function for REGISTER_MOVE_COST.  */
 
-        (set (pc) (...)):
-        CC does not change.
+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;
+    }
 
-        (parallel
-         (set (reg1) (mem (bdap/biap)))
-         (set (reg2) (bdap/biap))):
-        CC is (reg1) and (mem (reg2))
+  /* 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;
+}
 
-        (parallel
-         (set (mem (bdap/biap)) (reg1)) [or 0]
-         (set (reg2) (bdap/biap))):
-        CC does not change.
+/* 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.  */
 
-        (where reg and mem includes strict_low_parts variants thereof)
+static void
+cris_normal_notice_update_cc (rtx exp, rtx insn)
+{
+  /* "Normal" means, for:
+     (set (cc0) (...)):
+     CC is (...).
 
-        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).
+     (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.
 
-        Note that there are other parallel constructs we could match,
-        but we don't do that yet.  */
+     (set (mem) (...)):
+     CC does not change.
 
-      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;
+     (set (pc) (...)):
+     CC does not change.
 
-         /* Record CC0 changes, so we do not have to output multiple
-            test insns.  */
-         if (SET_DEST (exp) == cc0_rtx)
-           {
-             cc_status.value1 = SET_SRC (exp);
-             cc_status.value2 = 0;
+     (parallel
+      (set (reg1) (mem (bdap/biap)))
+      (set (reg2) (bdap/biap))):
+     CC is (reg1) and (mem (reg2))
 
-             /* 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;
-               }
-             else
-               cc_status.flags = 0;
+     (parallel
+      (set (mem (bdap/biap)) (reg1)) [or 0]
+      (set (reg2) (bdap/biap))):
+     CC does not change.
 
-             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;
-           }
-         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.  */
+     (where reg and mem includes strict_low_parts variants thereof)
 
-             /* 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))
-                   {
-                     cc_status.flags = 0;
-                     cc_status.value1 = SET_DEST (exp);
-                     cc_status.value2 = SET_SRC (exp);
+     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 (cris_reg_overlap_mentioned_p (cc_status.value1,
-                                                       cc_status.value2))
-                       cc_status.value2 = 0;
+     Note that there are other parallel constructs we could match,
+     but we don't do that yet.  */
 
-                     /* Add and sub may set V, which gets us
-                        unoptimizable results in "gt" and "le" condition
-                        codes.  */
-                     cc_status.flags |= CC_NO_OVERFLOW;
+  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;
 
-                     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
-                     && 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;
+      /* Record CC0 changes, so we do not have to output multiple
+        test insns.  */
+      if (SET_DEST (exp) == cc0_rtx)
+       {
+         CC_STATUS_INIT;
 
-                 return;
-               }
+         if (GET_CODE (SET_SRC (exp)) == COMPARE
+             && XEXP (SET_SRC (exp), 1) == const0_rtx)
+           cc_status.value1 = XEXP (SET_SRC (exp), 0);
+         else
+           cc_status.value1 = SET_SRC (exp);
+
+          /* Handle flags for the special btstq on one bit.  */
+         if (GET_CODE (cc_status.value1) == ZERO_EXTRACT
+             && XEXP (cc_status.value1, 1) == const1_rtx)
+           {
+             if (CONST_INT_P (XEXP (cc_status.value1, 0)))
+               /* Using cmpq.  */
+               cc_status.flags = CC_INVERTED;
              else
+               /* A one-bit btstq.  */
+               cc_status.flags = CC_Z_IN_NOT_N;
+           }
+
+         else 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;
+       }
+      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)
+           {
+             /* ... 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_INIT;
                  cc_status.value1 = SET_DEST (exp);
                  cc_status.value2 = SET_SRC (exp);
 
@@ -1408,23 +1502,25 @@ cris_notice_update_cc (rtx exp, rtx insn)
                                                    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).  */
+             /* 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;
@@ -1435,54 +1531,169 @@ cris_notice_update_cc (rtx exp, rtx insn)
 
              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 (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)
+    }
+  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
-                   = replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
-                                            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
-                     && 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;
+             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;
 
-                 return;
-               }
+             if (cc_status.value2
+                 && modified_in_p (cc_status.value2, insn))
+               cc_status.value2 = 0;
+
+             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;
@@ -1497,21 +1708,33 @@ cris_simple_epilogue (void)
 {
   unsigned int regno;
   unsigned int reglimit = STACK_POINTER_REGNUM;
-  bool got_really_used = current_function_uses_pic_offset_table;
+  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
+      || 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 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 ();
+    }
+
   /* No simple epilogue if there are saved registers.  */
   for (regno = 0; regno < reglimit; regno++)
     if (cris_reg_saved_in_regsave_area (regno, got_really_used))
@@ -1534,9 +1757,8 @@ cris_expand_return (bool on_stack)
      to check that it doesn't change half-way through.  */
   emit_jump_insn (gen_rtx_RETURN (VOIDmode));
 
-  if ((cfun->machine->return_type == CRIS_RETINSN_RET && on_stack)
-      || (cfun->machine->return_type == CRIS_RETINSN_JUMP && !on_stack))
-    abort ();
+  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;
@@ -1547,7 +1769,8 @@ cris_expand_return (bool on_stack)
    scanned.  In either case, *TOTAL contains the cost result.  */
 
 static bool
-cris_rtx_costs (rtx x, int code, int outer_code, int *total)
+cris_rtx_costs (rtx x, int code, int outer_code, int *total,
+               bool speed)
 {
   switch (code)
     {
@@ -1561,7 +1784,7 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
        /* 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
+       /* A 32-bit constant (or very seldom, unsigned 16 bits) costs
           another word.  FIXME: This isn't linear to 16 bits.  */
        else
          *total = 4;
@@ -1574,18 +1797,7 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
 
     case CONST:
     case SYMBOL_REF:
-      /* For PIC, we need a prefix (if it isn't already there),
-        and the PIC register.  For a global PIC symbol, we also
-        need a read of the GOT.  */
-      if (flag_pic)
-       {
-         if (cris_got_symbol (x))
-           *total = 2 + 4 + 6;
-         else
-           *total = 2 + 6;
-       }
-      else
-       *total = 6;
+      *total = 6;
       return true;
 
     case CONST_DOUBLE:
@@ -1597,9 +1809,24 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
       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 (GET_CODE (XEXP (x, 1)) != CONST_INT
+      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
@@ -1620,7 +1847,7 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
     case MOD:
     case UMOD:
     case DIV:
-      if (GET_CODE (XEXP (x, 1)) != CONST_INT
+      if (!CONST_INT_P (XEXP (x, 1))
           || exact_log2 (INTVAL (XEXP (x, 1)) < 0))
        {
          /* Estimate this as 4 + 8 * #of bits.  */
@@ -1630,19 +1857,24 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
       return false;
 
     case AND:
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+      if (CONST_INT_P (XEXP (x, 1))
           /* Two constants may actually happen before optimization.  */
-          && GET_CODE (XEXP (x, 0)) != CONST_INT
-          && !CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'I'))
+          && !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) + 2
+         *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_EXTRACT:
+      if (outer_code != COMPARE)
+        return false;
+      /* fall through */
+
     case ZERO_EXTEND: case SIGN_EXTEND:
-      *total = rtx_cost (XEXP (x, 0), outer_code);
+      *total = rtx_cost (XEXP (x, 0), outer_code, speed);
       return true;
 
     default:
@@ -1653,12 +1885,17 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
 /* The ADDRESS_COST worker.  */
 
 static int
-cris_address_cost (rtx x)
+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))
@@ -1666,16 +1903,13 @@ cris_address_cost (rtx 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)
@@ -1683,36 +1917,40 @@ cris_address_cost (rtx 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.  */
@@ -1798,21 +2036,19 @@ cris_side_effect_mode_ok (enum rtx_code code, rtx *ops,
       /* 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;
 
@@ -1844,6 +2080,49 @@ cris_side_effect_mode_ok (enum rtx_code code, rtx *ops,
   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
@@ -1874,168 +2153,150 @@ cris_target_asm_named_section (const char *name, unsigned int 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 (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 nonzero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P.  */
+  gcc_assert (flag_pic);
 
-int
-cris_symbol (rtx x)
-{
   switch (GET_CODE (x))
     {
-    case SYMBOL_REF:
-    case LABEL_REF:
-      return 1;
-
-    case UNSPEC:
-      if (XINT (x, 1) == CRIS_UNSPEC_GOT || XINT (x, 1) != CRIS_UNSPEC_PLT)
-       return 0;
-      /* 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:
-      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 nonzero 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 (rtx x)
+enum cris_pic_symbol_type
+cris_pic_symbol_type_of (rtx x)
 {
-#ifdef ENABLE_CHECKING
-  if (!flag_pic)
-    abort ();
-#endif
-
   switch (GET_CODE (x))
     {
-    case UNSPEC:
-      if (XINT (x, 1) == CRIS_UNSPEC_GOT)
-       return 1;
-      if (XINT (x, 1) != CRIS_UNSPEC_PLT)
-       return 0;
-      ASSERT_PLT_UNSPEC (x);
-      return 1;
-
     case SYMBOL_REF:
-      if (cfun != NULL)
-       current_function_uses_pic_offset_table = 1;
-      return SYMBOL_REF_LOCAL_P (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:
-      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 nonzero 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 (rtx x)
+cris_legitimate_pic_operand (rtx x)
 {
-#ifdef ENABLE_CHECKING
-  if (!flag_pic)
-    abort ();
-#endif
-
-  switch (GET_CODE (x))
-    {
-    case UNSPEC:
-      if (XINT (x, 1) == CRIS_UNSPEC_GOT)
-       return 0;
-      ASSERT_PLT_UNSPEC (x);
-      return 0;
-
-    case SYMBOL_REF:
-      if (cfun != NULL)
-       current_function_uses_pic_offset_table = 1;
-      return ! SYMBOL_REF_LOCAL_P (x);
-
-    case CONST:
-      return cris_got_symbol (XEXP (x, 0));
+  /* Symbols are not valid PIC operands as-is; just constants.  */
+  return cris_valid_pic_const (x, true);
+}
 
-    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;
+/* The ASM_OUTPUT_CASE_END worker.  */
 
-    case CONST_INT:
-    case CONST_DOUBLE:
-      return 0;
-
-    default:
-      fatal_insn ("unrecognized supposed constant in cris_global_pic_symbol",
-                 x);
+void
+cris_asm_output_case_end (FILE *stream, int num, rtx table)
+{
+  if (TARGET_V32)
+    {
+      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;
     }
 
-  return 1;
+  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" : ""));
 }
 
 /* TARGET_HANDLE_OPTION worker.  We just store the values into local
@@ -2043,7 +2304,8 @@ cris_got_symbol (rtx x)
    cris_override_options.  */
 
 static bool
-cris_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+cris_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+                   int value ATTRIBUTE_UNUSED)
 {
   switch (code)
     {
@@ -2086,20 +2348,6 @@ cris_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
             + MASK_DATA_ALIGN);
       break;
 
-    case OPT_max_stackframe_:
-    case OPT_mmax_stackframe_:
-      cris_max_stackframe_str = arg;
-      break;
-
-    case OPT_march_:
-    case OPT_mcpu_:
-      cris_cpu_str = arg;
-      break;
-
-    case OPT_mtune_:
-      cris_tune_str = arg;
-      break;
-
     default:
       break;
     }
@@ -2148,7 +2396,7 @@ cris_override_options (void)
          || 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);
 
@@ -2184,7 +2432,7 @@ cris_override_options (void)
          || 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);
 
@@ -2196,6 +2444,9 @@ cris_override_options (void)
              | 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
@@ -2218,7 +2469,7 @@ cris_override_options (void)
 
   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;
     }
 
@@ -2249,19 +2500,32 @@ cris_asm_output_mi_thunk (FILE *stream,
       const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0);
 
       name = (* targetm.strip_name_encoding) (name);
-      fprintf (stream, "add.d ");
-      assemble_name (stream, name);
-      fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
+
+      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");
     }
 }
 
-/* Boilerplate emitted at start of file.  
+/* Boilerplate emitted at start of file.
 
    NO_APP *only at file start* means faster assembly.  It also means
    comments are not allowed.  In some cases comments will be output
@@ -2304,7 +2568,7 @@ cris_init_expanders (void)
 static struct machine_function *
 cris_init_machine_status (void)
 {
-  return ggc_alloc_cleared (sizeof (struct machine_function));
+  return GGC_CNEW (struct machine_function);
 }
 
 /* Split a 2 word move (DI or presumably DF) into component parts.
@@ -2321,16 +2585,15 @@ cris_split_movdx (rtx *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);
 
@@ -2348,7 +2611,7 @@ cris_split_movdx (rtx *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]);
@@ -2361,7 +2624,7 @@ cris_split_movdx (rtx *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.  */
@@ -2379,12 +2642,31 @@ cris_split_movdx (rtx *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
            {
@@ -2413,11 +2695,11 @@ cris_split_movdx (rtx *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)))
     {
@@ -2425,12 +2707,31 @@ cris_split_movdx (rtx *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
        {
@@ -2455,7 +2756,7 @@ cris_split_movdx (rtx *operands)
     }
 
   else
-    abort ();
+    internal_error ("Unknown dest");
 
   val = get_insns ();
   end_sequence ();
@@ -2470,21 +2771,31 @@ cris_expand_prologue (void)
   int regno;
   int size = get_frame_size ();
   /* Shorten the used name for readability.  */
-  int cfoa_size = current_function_outgoing_args_size;
+  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 = current_function_uses_pic_offset_table;
+  int got_really_used = false;
   int n_movem_regs = 0;
-  int pretend = current_function_pretend_args_size;
+  int pretend = crtl->args.pretend_args_size;
 
   /* Don't do anything if no prologues or epilogues are wanted.  */
   if (!TARGET_PROLOGUE_EPILOGUE)
     return;
 
-  if (size < 0)
-    abort ();
+  CRIS_ASSERT (size >= 0);
+
+  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 the size to what's best for the CPU model.  */
   if (TARGET_STACK_ALIGN)
@@ -2494,7 +2805,7 @@ cris_expand_prologue (void)
     {
       /* See also cris_setup_incoming_varargs where
         cfun->machine->stdarg_regs is set.  There are other setters of
-        current_function_pretend_args_size than stdarg handling, like
+        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
@@ -2528,7 +2839,7 @@ cris_expand_prologue (void)
             get confused.  */
        }
 
-      /* For other setters of current_function_pretend_args_size, we
+      /* For other setters of crtl->args.pretend_args_size, we
         just adjust the stack by leaving the remaining size in
         "pretend", handled below.  */
     }
@@ -2726,11 +3037,11 @@ cris_expand_prologue (void)
         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_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
+      emit_use (pic_offset_table_rtx);
     }
 
   if (cris_max_stackframe && framesize > cris_max_stackframe)
-    warning ("stackframe too big: %d bytes", framesize);
+    warning (0, "stackframe too big: %d bytes", framesize);
 }
 
 /* The expander for the epilogue pattern.  */
@@ -2741,19 +3052,30 @@ cris_expand_epilogue (void)
   int regno;
   int size = get_frame_size ();
   int last_movem_reg = -1;
-  int argspace_offset = current_function_outgoing_args_size;
-  int pretend =         current_function_pretend_args_size;
+  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 = current_function_uses_pic_offset_table;
+  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;
@@ -2785,6 +3107,8 @@ cris_expand_epilogue (void)
        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
@@ -2800,12 +3124,19 @@ cris_expand_epilogue (void)
        mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
                                                     stack_pointer_rtx));
        set_mem_alias_set (mem, get_frame_alias_set ());
-       emit_move_insn (gen_rtx_raw_REG (SImode, regno), mem);
+       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,
@@ -2818,7 +3149,14 @@ cris_expand_epilogue (void)
       mem = gen_rtx_MEM (SImode,
                         gen_rtx_POST_INC (SImode, stack_pointer_rtx));
       set_mem_alias_set (mem, get_frame_alias_set ());
-      emit_insn (cris_gen_movem_load (mem, GEN_INT (last_movem_reg + 1), 0));
+      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));
     }
 
   /* If we don't clobber all of the allocated stack area (we've already
@@ -2834,13 +3172,20 @@ cris_expand_epilogue (void)
   /* Restore frame pointer if necessary.  */
   if (frame_pointer_needed)
     {
+      rtx insn;
+
       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 ());
-      emit_move_insn (frame_pointer_rtx, mem);
+      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)
     {
@@ -2863,20 +3208,27 @@ cris_expand_epilogue (void)
      the return address on the stack.  */
   if (return_address_on_stack && pretend == 0)
     {
-      if (current_function_calls_eh_return)
+      if (TARGET_V32 || crtl->calls_eh_return)
        {
          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 ());
-         emit_move_insn (srpreg, mem);
+         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));
 
-         emit_insn (gen_addsi3 (stack_pointer_rtx,
-                                stack_pointer_rtx,
-                                gen_rtx_raw_REG (SImode,
-                                                 CRIS_STACKADJ_REG)));
+         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
@@ -2894,11 +3246,18 @@ cris_expand_epilogue (void)
        {
          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 ());
-         emit_move_insn (srpreg, mem);
+         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));
        }
 
       emit_insn (gen_rtx_SET (VOIDmode,
@@ -2907,7 +3266,7 @@ cris_expand_epilogue (void)
     }
 
   /* Perform the "physical" unwinding that the EH machinery calculated.  */
-  if (current_function_calls_eh_return)
+  if (crtl->calls_eh_return)
     emit_insn (gen_addsi3 (stack_pointer_rtx,
                           stack_pointer_rtx,
                           gen_rtx_raw_REG (SImode,
@@ -2928,11 +3287,16 @@ cris_gen_movem_load (rtx src, rtx nregs_rtx, int nprefix)
   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);
 
-  if (!REG_P (srcreg))
-    abort ();
+  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
@@ -2982,14 +3346,19 @@ cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
   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);
 
-  if (!REG_P (destreg))
-    abort ();
+  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
@@ -3081,10 +3450,11 @@ cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
       if (increment != 0)
        {
          rtx seq = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (nregs + 1));
-         XVECEXP (seq, 0, 0) = XVECEXP (PATTERN (insn), 0, 0);
+         XVECEXP (seq, 0, 0) = copy_rtx (XVECEXP (PATTERN (insn), 0, 0));
          for (i = 1; i < nregs; i++)
-           XVECEXP (seq, 0, i) = XVECEXP (PATTERN (insn), 0, i + 1);
-         XVECEXP (seq, 0, nregs) = XVECEXP (PATTERN (insn), 0, 1);
+           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));
@@ -3096,6 +3466,139 @@ cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
   return insn;
 }
 
+/* Worker function for expanding the address for PIC function calls.  */
+
+void
+cris_expand_pic_call_address (rtx *opp)
+{
+  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))
+    {
+      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 (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
+           {
+             /* 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
+       /* 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
@@ -3114,38 +3617,18 @@ cris_output_addr_const (FILE *file, rtx x)
 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);
-
-      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]");
-       }
-      else
-       LOSE_AND_RETURN ("unexpected PIC symbol", x);
-
-      /* Sanity check.  */
-      if (! current_function_uses_pic_offset_table)
-       output_operand_lossage ("PIC register isn't set up");
+     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));
@@ -3158,14 +3641,10 @@ cris_asm_output_label_ref (FILE *file, char *buf)
 {
   if (flag_pic && in_code > 0)
     {
-      if (! cris_pic_sympart_only)
-       fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
       assemble_name (file, buf);
 
-      fprintf (file, ":GOTOFF");
-
       /* Sanity check.  */
-      if (! current_function_uses_pic_offset_table)
+      if (!TARGET_V32 && !crtl->uses_pic_offset_table)
        internal_error ("emitting PIC operand, but PIC register isn't set up");
     }
   else
@@ -3175,36 +3654,58 @@ cris_asm_output_label_ref (FILE *file, char *buf)
 /* Worker function for OUTPUT_ADDR_CONST_EXTRA.  */
 
 bool
-cris_output_addr_const_extra (FILE *file, rtx x)
+cris_output_addr_const_extra (FILE *file, rtx xconst)
 {
-  switch (GET_CODE (x))
+  switch (GET_CODE (xconst))
     {
-      const char *origstr;
-      const char *str;
+      rtx x;
 
     case UNSPEC:
-      ASSERT_PLT_UNSPEC (x);
-      x = XVECEXP (x, 0, 0);
-      origstr = XSTR (x, 0);
-      str = (* targetm.strip_name_encoding) (origstr);
-      if (cris_pic_sympart_only)
+      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))
        {
-         assemble_name (file, str);
+       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");
-       }
-      else
-       {
-         if (TARGET_AVOID_GOTPLT)
-           /* We shouldn't get here.  */
-           abort ();
+         break;
 
-         fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-         assemble_name (file, XSTR (x, 0));
+       case CRIS_UNSPEC_GOTREL:
+         gcc_assert (!TARGET_V32);
+         fprintf (file, ":GOTOFF");
+         break;
 
+       case CRIS_UNSPEC_GOTREAD:
          if (flag_pic == 1)
-           fprintf (file, ":GOTPLT16]");
+           fprintf (file, ":GOT16");
          else
-           fprintf (file, ":GOTPLT]");
+           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;
 
@@ -3249,13 +3750,32 @@ cris_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
 
 static bool
 cris_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
-                       enum machine_mode mode, tree type,
+                       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);
 }
 
+/* A combination of defining TARGET_PROMOTE_FUNCTION_MODE, promoting arguments
+   and *not* defining TARGET_PROMOTE_PROTOTYPES or PROMOTE_MODE gives the
+   best code size and speed for gcc, ipps and products in gcc-2.7.2.  */
+
+enum machine_mode
+cris_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
+                            enum machine_mode mode,
+                            int *punsignedp ATTRIBUTE_UNUSED,
+                           const_tree fntype ATTRIBUTE_UNUSED,
+                            int for_return)
+{
+  /* Defining PROMOTE_FUNCTION_RETURN in gcc-2.7.2 uncovered bug 981110 (even
+     when modifying FUNCTION_VALUE to return the promoted mode).  Maybe
+     pointless as of now, but let's keep the old behavior.  */
+  if (for_return == 1)
+    return mode;
+  return CRIS_PROMOTED_MODE (mode, *punsignedp, type);
+} 
+
 
 static int
 cris_arg_partial_bytes (CUMULATIVE_ARGS *ca, enum machine_mode mode,
@@ -3273,14 +3793,24 @@ cris_arg_partial_bytes (CUMULATIVE_ARGS *ca, enum machine_mode mode,
 /* Worker function for TARGET_MD_ASM_CLOBBERS.  */
 
 static tree
-cris_md_asm_clobbers (tree outputs, tree inputs, tree clobbers)
+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);
@@ -3291,7 +3821,7 @@ cris_md_asm_clobbers (tree outputs, tree inputs, tree clobbers)
         impossible constraints.  */
       if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
                  'h') != NULL
-         || decl_overlaps_hard_reg_set_p (val, mof_set))
+         || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
        return clobbers;
     }
 
@@ -3301,7 +3831,7 @@ cris_md_asm_clobbers (tree outputs, tree inputs, tree clobbers)
 
       if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
                  'h') != NULL
-         || decl_overlaps_hard_reg_set_p (val, mof_set))
+         || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
        return clobbers;
     }
 
@@ -3311,6 +3841,18 @@ cris_md_asm_clobbers (tree outputs, tree inputs, tree clobbers)
                    clobbers);
 }
 
+/* Implement TARGET_FRAME_POINTER_REQUIRED.
+
+   Really only needed if the stack frame has variable length (alloca
+   or variable sized local arguments (GNU C extension).  See PR39499 and
+   PR38609 for the reason this isn't just 0.  */
+
+bool
+cris_frame_pointer_required (void)
+{
+  return !current_function_sp_is_unchanging;
+}
+
 #if 0
 /* Various small functions to replace macros.  Only called from a
    debugger.  They might collide with gcc functions or system functions,