OSDN Git Service

* common.opt (main_input_filename, main_input_basename,
[pf3gnuchains/gcc-fork.git] / gcc / config / m32c / m32c.c
index b0733dd..3a74139 100644 (file)
@@ -1,5 +1,5 @@
 /* Target Code for R8C/M16C/M32C
-   Copyright (C) 2005, 2006, 2007
+   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
    Free Software Foundation, Inc.
    Contributed by Red Hat.
 
@@ -26,7 +26,6 @@
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
-#include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
 #include "insn-flags.h"
@@ -35,7 +34,7 @@
 #include "flags.h"
 #include "recog.h"
 #include "reload.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 #include "obstack.h"
 #include "tree.h"
 #include "expr.h"
@@ -47,7 +46,7 @@
 #include "target-def.h"
 #include "tm_p.h"
 #include "langhooks.h"
-#include "tree-gimple.h"
+#include "gimple.h"
 #include "df.h"
 
 /* Prototypes */
@@ -60,23 +59,37 @@ typedef enum
   PP_justcount
 } Push_Pop_Type;
 
+static bool m32c_function_needs_enter (void);
 static tree interrupt_handler (tree *, tree, tree, int, bool *);
 static tree function_vector_handler (tree *, tree, tree, int, bool *);
 static int interrupt_p (tree node);
+static int bank_switch_p (tree node);
+static int fast_interrupt_p (tree node);
+static int interrupt_p (tree node);
 static bool m32c_asm_integer (rtx, unsigned int, int);
 static int m32c_comp_type_attributes (const_tree, const_tree);
 static bool m32c_fixed_condition_code_regs (unsigned int *, unsigned int *);
 static struct machine_function *m32c_init_machine_status (void);
 static void m32c_insert_attributes (tree, tree *);
+static bool m32c_legitimate_address_p (enum machine_mode, rtx, bool);
+static bool m32c_addr_space_legitimate_address_p (enum machine_mode, rtx, bool, addr_space_t);
+static rtx m32c_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+                             const_tree, bool);
 static bool m32c_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
                                    const_tree, bool);
-static bool m32c_promote_prototypes (const_tree);
+static void m32c_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+                                      const_tree, bool);
+static unsigned int m32c_function_arg_boundary (enum machine_mode, const_tree);
 static int m32c_pushm_popm (Push_Pop_Type);
 static bool m32c_strict_argument_naming (CUMULATIVE_ARGS *);
 static rtx m32c_struct_value_rtx (tree, int);
 static rtx m32c_subreg (enum machine_mode, rtx, enum machine_mode, int);
 static int need_to_save (int);
-int current_function_special_page_vector (rtx);
+static rtx m32c_function_value (const_tree, const_tree, bool);
+static rtx m32c_libcall_value (enum machine_mode, const_rtx);
+
+/* Returns true if an address is specified, else false.  */
+static bool m32c_get_pragma_address (const char *varname, unsigned *addr);
 
 #define SYMBOL_FLAG_FUNCVEC_FUNCTION    (SYMBOL_FLAG_MACH_DEP << 0)
 
@@ -109,6 +122,18 @@ static GTY(()) rtx patternr[30];
 #define IS_CR_REGNO(regno) ((regno) >= SB_REGNO && (regno) <= PC_REGNO)
 #define IS_CR_REG(rtx) (GET_CODE (rtx) == REG && IS_CR_REGNO (REGNO (rtx)))
 
+static int
+far_addr_space_p (rtx x)
+{
+  if (GET_CODE (x) != MEM)
+    return 0;
+#if DEBUG0
+  fprintf(stderr, "\033[35mfar_addr_space: "); debug_rtx(x);
+  fprintf(stderr, " = %d\033[0m\n", MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR);
+#endif
+  return MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR;
+}
+
 /* We do most RTX matching by converting the RTX into a string, and
    using string compares.  This vastly simplifies the logic in many of
    the functions in this file.
@@ -151,6 +176,16 @@ encode_pattern_1 (rtx x)
     case CONST:
       encode_pattern_1 (XEXP (x, 0));
       break;
+    case SIGN_EXTEND:
+      *patternp++ = '^';
+      *patternp++ = 'S';
+      encode_pattern_1 (XEXP (x, 0));
+      break;
+    case ZERO_EXTEND:
+      *patternp++ = '^';
+      *patternp++ = 'Z';
+      encode_pattern_1 (XEXP (x, 0));
+      break;
     case PLUS:
       *patternp++ = '+';
       encode_pattern_1 (XEXP (x, 0));
@@ -329,47 +364,33 @@ reduce_class (int original_class, int limiting_class, int returned_if_empty)
   return best;
 }
 
-/* Returns TRUE If there are any registers that exist in both register
-   classes.  */
-static int
-classes_intersect (int class1, int class2)
-{
-  return class_contents[class1][0] & class_contents[class2][0];
-}
-
 /* Used by m32c_register_move_cost to determine if a move is
    impossibly expensive.  */
-static int
-class_can_hold_mode (int class, enum machine_mode mode)
+static bool
+class_can_hold_mode (reg_class_t rclass, enum machine_mode mode)
 {
   /* Cache the results:  0=untested  1=no  2=yes */
   static char results[LIM_REG_CLASSES][MAX_MACHINE_MODE];
-  if (results[class][mode] == 0)
+
+  if (results[(int) rclass][mode] == 0)
     {
-      int r, n, i;
-      results[class][mode] = 1;
+      int r;
+      results[rclass][mode] = 1;
       for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
-       if (class_contents[class][0] & (1 << r)
+       if (in_hard_reg_set_p (reg_class_contents[(int) rclass], mode, r)
            && HARD_REGNO_MODE_OK (r, mode))
          {
-           int ok = 1;
-           n = HARD_REGNO_NREGS (r, mode);
-           for (i = 1; i < n; i++)
-             if (!(class_contents[class][0] & (1 << (r + i))))
-               ok = 0;
-           if (ok)
-             {
-               results[class][mode] = 2;
-               break;
-             }
+           results[rclass][mode] = 2;
+           break;
          }
     }
+
 #if DEBUG0
   fprintf (stderr, "class %s can hold %s? %s\n",
-          class_names[class], mode_name[mode],
-          (results[class][mode] == 2) ? "yes" : "no");
+          class_names[(int) rclass], mode_name[mode],
+          (results[rclass][mode] == 2) ? "yes" : "no");
 #endif
-  return results[class][mode] == 2;
+  return results[(int) rclass][mode] == 2;
 }
 
 /* Run-time Target Specification.  */
@@ -405,11 +426,15 @@ m32c_handle_option (size_t code,
   return TRUE;
 }
 
-/* Implements OVERRIDE_OPTIONS.  We limit memregs to 0..16, and
-   provide a default.  */
-void
-m32c_override_options (void)
+/* Implements TARGET_OPTION_OVERRIDE.  */
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE m32c_option_override
+
+static void
+m32c_option_override (void)
 {
+  /* We limit memregs to 0..16, and provide a default.  */
   if (target_memregs_set)
     {
       if (target_memregs < 0 || target_memregs > 16)
@@ -417,6 +442,28 @@ m32c_override_options (void)
     }
   else
     target_memregs = 16;
+
+  if (TARGET_A24)
+    flag_ivopts = 0;
+
+  /* This target defaults to strict volatile bitfields.  */
+  if (flag_strict_volatile_bitfields < 0)
+    flag_strict_volatile_bitfields = 1;
+
+  /* r8c/m16c have no 16-bit indirect call, so thunks are involved.
+     This is always worse than an absolute call.  */
+  if (TARGET_A16)
+    flag_no_function_cse = 1;
+}
+
+#undef TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE
+#define TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE m32c_override_options_after_change
+
+static void
+m32c_override_options_after_change (void)
+{
+  if (TARGET_A16)
+    flag_no_function_cse = 1;
 }
 
 /* Defining data structures for per-function information */
@@ -425,11 +472,7 @@ m32c_override_options (void)
 static struct machine_function *
 m32c_init_machine_status (void)
 {
-  struct machine_function *machine;
-  machine =
-    (machine_function *) ggc_alloc_cleared (sizeof (machine_function));
-
-  return machine;
+  return ggc_alloc_cleared_machine_function ();
 }
 
 /* Implements INIT_EXPANDERS.  We just set up to call the above
@@ -442,14 +485,6 @@ m32c_init_expanders (void)
 
 /* Storage Layout */
 
-#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN m32c_promote_function_return
-bool
-m32c_promote_function_return (const_tree fntype ATTRIBUTE_UNUSED)
-{
-  return false;
-}
-
 /* Register Basics */
 
 /* Basic Characteristics of Registers */
@@ -487,10 +522,12 @@ static struct
   { 1, 1, 0, 0, 0 },           /* mem7 */
 };
 
-/* Implements CONDITIONAL_REGISTER_USAGE.  We adjust the number of
-   available memregs, and select which registers need to be preserved
+/* Implements TARGET_CONDITIONAL_REGISTER_USAGE.  We adjust the number
+   of available memregs, and select which registers need to be preserved
    across calls based on the chip family.  */
 
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE m32c_conditional_register_usage
 void
 m32c_conditional_register_usage (void)
 {
@@ -500,7 +537,7 @@ m32c_conditional_register_usage (void)
     {
       /* The command line option is bytes, but our "registers" are
         16-bit words.  */
-      for (i = target_memregs/2; i < 8; i++)
+      for (i = (target_memregs+1)/2; i < 8; i++)
        {
          fixed_regs[MEM0_REGNO + i] = 1;
          CLEAR_HARD_REG_BIT (reg_class_contents[MEM_REGS], MEM0_REGNO + i);
@@ -538,7 +575,7 @@ m32c_hard_regno_nregs_1 (int regno, enum machine_mode mode)
     return nregs_table[regno].qi_regs;
   if (GET_MODE_SIZE (mode) <= 2)
     return nregs_table[regno].hi_regs;
-  if (regno == A0_REGNO && mode == PSImode && TARGET_A16)
+  if (regno == A0_REGNO && mode == SImode && TARGET_A16)
     return 2;
   if ((GET_MODE_SIZE (mode) <= 3 || mode == PSImode) && TARGET_A24)
     return nregs_table[regno].pi_regs;
@@ -585,7 +622,7 @@ m32c_modes_tieable_p (enum machine_mode m1, enum machine_mode m2)
 /* Register Classes */
 
 /* Implements REGNO_REG_CLASS.  */
-enum machine_mode
+enum reg_class
 m32c_regno_reg_class (int regno)
 {
   switch (regno)
@@ -641,6 +678,8 @@ m32c_reg_class_from_constraint (char c ATTRIBUTE_UNUSED, const char *s)
     return R3_REGS;
   if (memcmp (s, "R02", 3) == 0)
     return R02_REGS;
+  if (memcmp (s, "R13", 3) == 0)
+    return R13_REGS;
   if (memcmp (s, "R03", 3) == 0)
     return R03_REGS;
   if (memcmp (s, "Rdi", 3) == 0)
@@ -728,7 +767,7 @@ m32c_preferred_reload_class (rtx x, int rclass)
   if (rclass == NO_REGS)
     rclass = GET_MODE (x) == QImode ? HL_REGS : R03_REGS;
 
-  if (classes_intersect (rclass, CR_REGS))
+  if (reg_classes_intersect_p (rclass, CR_REGS))
     {
       switch (GET_MODE (x))
        {
@@ -814,21 +853,26 @@ m32c_secondary_reload_class (int rclass, enum machine_mode mode, rtx x)
   if (mode == QImode
       && GET_CODE (x) == MEM && (cc & ~class_contents[R23_REGS][0]) == 0)
     return QI_REGS;
-  if (classes_intersect (rclass, CR_REGS)
+  if (reg_classes_intersect_p (rclass, CR_REGS)
       && GET_CODE (x) == REG
       && REGNO (x) >= SB_REGNO && REGNO (x) <= SP_REGNO)
     return TARGET_A16 ? HI_REGS : A_REGS;
   return NO_REGS;
 }
 
-/* Implements CLASS_LIKELY_SPILLED_P.  A_REGS is needed for address
+/* Implements TARGET_CLASS_LIKELY_SPILLED_P.  A_REGS is needed for address
    reloads.  */
-int
-m32c_class_likely_spilled_p (int regclass)
+
+#undef TARGET_CLASS_LIKELY_SPILLED_P
+#define TARGET_CLASS_LIKELY_SPILLED_P m32c_class_likely_spilled_p
+
+static bool
+m32c_class_likely_spilled_p (reg_class_t regclass)
 {
   if (regclass == A_REGS)
-    return 1;
-  return reg_class_size[regclass] == 1;
+    return true;
+
+  return (reg_class_size[(int) regclass] == 1);
 }
 
 /* Implements CLASS_MAX_NREGS.  We calculate this according to its
@@ -949,6 +993,11 @@ m32c_const_ok_for_constraint_p (HOST_WIDE_INT value,
       int b = exact_log2 ((value ^ 0xff) & 0xff);
       return (b >= 0 && b <= 7);
     }
+  if (memcmp (str, "ImB", 3) == 0)
+    {
+      int b = exact_log2 ((value ^ 0xffff) & 0xffff);
+      return (b >= 0 && b <= 7);
+    }
   if (memcmp (str, "Ilw", 3) == 0)
     {
       int b = exact_log2 (value);
@@ -966,6 +1015,8 @@ m32c_const_ok_for_constraint_p (HOST_WIDE_INT value,
   return 0;
 }
 
+#define A0_OR_PSEUDO(x) (IS_REG(x, A0_REGNO) || REGNO (x) >= FIRST_PSEUDO_REGISTER)
+
 /* Implements EXTRA_CONSTRAINT_STR (see next function too).  'S' is
    for memory constraints, plus "Rpa" for PARALLEL rtx's we use for
    call return values.  */
@@ -973,6 +1024,29 @@ int
 m32c_extra_constraint_p2 (rtx value, char c ATTRIBUTE_UNUSED, const char *str)
 {
   encode_pattern (value);
+
+  if (far_addr_space_p (value))
+    {
+      if (memcmp (str, "SF", 2) == 0)
+       {
+         return (   (RTX_IS ("mr")
+                     && A0_OR_PSEUDO (patternr[1])
+                     && GET_MODE (patternr[1]) == SImode)
+                    || (RTX_IS ("m+^Sri")
+                        && A0_OR_PSEUDO (patternr[4])
+                        && GET_MODE (patternr[4]) == HImode)
+                    || (RTX_IS ("m+^Srs")
+                        && A0_OR_PSEUDO (patternr[4])
+                        && GET_MODE (patternr[4]) == HImode)
+                    || (RTX_IS ("m+^S+ris")
+                        && A0_OR_PSEUDO (patternr[5])
+                        && GET_MODE (patternr[5]) == HImode)
+                    || RTX_IS ("ms")
+                    );
+       }
+      return 0;
+    }
+
   if (memcmp (str, "Sd", 2) == 0)
     {
       /* This is the common "src/dest" address */
@@ -1040,6 +1114,10 @@ m32c_extra_constraint_p2 (rtx value, char c ATTRIBUTE_UNUSED, const char *str)
     {
       return r1h_operand (value, QImode);
     }
+  else if (memcmp (str, "SF", 2) == 0)
+    {
+      return 0;
+    }
 
   gcc_assert (str[0] != 'S');
 
@@ -1257,7 +1335,10 @@ need_to_save (int regno)
   if (regno == FP_REGNO)
     return 0;
   if (cfun->machine->is_interrupt
-      && (!cfun->machine->is_leaf || regno == A0_REGNO))
+      && (!cfun->machine->is_leaf
+         || (regno == A0_REGNO
+             && m32c_function_needs_enter ())
+         ))
     return 1;
   if (df_regs_ever_live_p (regno)
       && (!call_used_regs[regno] || cfun->machine->is_interrupt))
@@ -1369,8 +1450,7 @@ m32c_pushm_popm (Push_Pop_Type ppt)
 
          pushm = F (emit_insn (gen_pushm (GEN_INT (reg_mask))));
 
-         REG_NOTES (pushm) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, note,
-                                                REG_NOTES (pushm));
+         add_reg_note (pushm, REG_FRAME_RELATED_EXPR, note);
        }
 
       if (cfun->machine->is_interrupt)
@@ -1435,17 +1515,9 @@ m32c_initial_elimination_offset (int from, int to)
 
 /* Passing Function Arguments on the Stack */
 
-#undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES m32c_promote_prototypes
-static bool
-m32c_promote_prototypes (const_tree fntype ATTRIBUTE_UNUSED)
-{
-  return 0;
-}
-
 /* Implements PUSH_ROUNDING.  The R8C and M16C have byte stacks, the
    M32C has word stacks.  */
-int
+unsigned int
 m32c_push_rounding (int n)
 {
   if (TARGET_R8C || TARGET_M16C)
@@ -1455,10 +1527,11 @@ m32c_push_rounding (int n)
 
 /* Passing Arguments in Registers */
 
-/* Implements FUNCTION_ARG.  Arguments are passed partly in registers,
-   partly on stack.  If our function returns a struct, a pointer to a
-   buffer for it is at the top of the stack (last thing pushed).  The
-   first few real arguments may be in registers as follows:
+/* Implements TARGET_FUNCTION_ARG.  Arguments are passed partly in
+   registers, partly on stack.  If our function returns a struct, a
+   pointer to a buffer for it is at the top of the stack (last thing
+   pushed).  The first few real arguments may be in registers as
+   follows:
 
    R8C/M16C:   arg1 in r1 if it's QI or HI (else it's pushed on stack)
                arg2 in r2 if it's HI (else pushed on stack)
@@ -1471,9 +1544,11 @@ m32c_push_rounding (int n)
 
    Note that when arg1 doesn't fit in r1, arg2 may still be passed in
    r2 if it fits.  */
-rtx
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG m32c_function_arg
+static rtx
 m32c_function_arg (CUMULATIVE_ARGS * ca,
-                  enum machine_mode mode, tree type, int named)
+                  enum machine_mode mode, const_tree type, bool named)
 {
   /* Can return a reg, parallel, or 0 for stack */
   rtx rv = NULL_RTX;
@@ -1546,15 +1621,17 @@ m32c_init_cumulative_args (CUMULATIVE_ARGS * ca,
   ca->parm_num = 1;
 }
 
-/* Implements FUNCTION_ARG_ADVANCE.  force_mem is set for functions
-   returning structures, so we always reset that.  Otherwise, we only
-   need to know the sequence number of the argument to know what to do
-   with it.  */
-void
+/* Implements TARGET_FUNCTION_ARG_ADVANCE.  force_mem is set for
+   functions returning structures, so we always reset that.  Otherwise,
+   we only need to know the sequence number of the argument to know what
+   to do with it.  */
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE m32c_function_arg_advance
+static void
 m32c_function_arg_advance (CUMULATIVE_ARGS * ca,
                           enum machine_mode mode ATTRIBUTE_UNUSED,
-                          tree type ATTRIBUTE_UNUSED,
-                          int named ATTRIBUTE_UNUSED)
+                          const_tree type ATTRIBUTE_UNUSED,
+                          bool named ATTRIBUTE_UNUSED)
 {
   if (ca->force_mem)
     ca->force_mem = 0;
@@ -1562,6 +1639,16 @@ m32c_function_arg_advance (CUMULATIVE_ARGS * ca,
     ca->parm_num++;
 }
 
+/* Implements TARGET_FUNCTION_ARG_BOUNDARY.  */
+#undef TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY m32c_function_arg_boundary
+static unsigned int
+m32c_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
+                           const_tree type ATTRIBUTE_UNUSED)
+{
+  return (TARGET_A16 ? 8 : 16);
+}
+
 /* Implements FUNCTION_ARG_REGNO_P.  */
 int
 m32c_function_arg_regno_p (int r)
@@ -1589,15 +1676,19 @@ m32c_valid_pointer_mode (enum machine_mode mode)
 
 /* How Scalar Function Values Are Returned */
 
-/* Implements LIBCALL_VALUE.  Most values are returned in $r0, or some
+/* Implements TARGET_LIBCALL_VALUE.  Most values are returned in $r0, or some
    combination of registers starting there (r2r0 for longs, r3r1r2r0
    for long long, r3r2r1r0 for doubles), except that that ABI
    currently doesn't work because it ends up using all available
    general registers and gcc often can't compile it.  So, instead, we
    return anything bigger than 16 bits in "mem0" (effectively, a
    memory location).  */
-rtx
-m32c_libcall_value (enum machine_mode mode)
+
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE m32c_libcall_value
+
+static rtx
+m32c_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
 {
   /* return reg or parallel */
 #if 0
@@ -1647,14 +1738,31 @@ m32c_libcall_value (enum machine_mode mode)
   return gen_rtx_REG (mode, R0_REGNO);
 }
 
-/* Implements FUNCTION_VALUE.  Functions and libcalls have the same
+/* Implements TARGET_FUNCTION_VALUE.  Functions and libcalls have the same
    conventions.  */
-rtx
-m32c_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE m32c_function_value
+
+static rtx
+m32c_function_value (const_tree valtype,
+                    const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+                    bool outgoing ATTRIBUTE_UNUSED)
 {
   /* return reg or parallel */
   const enum machine_mode mode = TYPE_MODE (valtype);
-  return m32c_libcall_value (mode);
+  return m32c_libcall_value (mode, NULL_RTX);
+}
+
+/* Implements TARGET_FUNCTION_VALUE_REGNO_P.  */
+
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P m32c_function_value_regno_p
+
+static bool
+m32c_function_value_regno_p (const unsigned int regno)
+{
+  return (regno == R0_REGNO || regno == MEM0_REGNO);
 }
 
 /* How Large Values Are Returned */
@@ -1720,11 +1828,16 @@ m32c_trampoline_alignment (void)
   return 2;
 }
 
-/* Implements INITIALIZE_TRAMPOLINE.  */
-void
-m32c_initialize_trampoline (rtx tramp, rtx function, rtx chainval)
+/* Implements TARGET_TRAMPOLINE_INIT.  */
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT m32c_trampoline_init
+static void
+m32c_trampoline_init (rtx m_tramp, tree fndecl, rtx chainval)
 {
-#define A0(m,i) gen_rtx_MEM (m, plus_constant (tramp, i))
+  rtx function = XEXP (DECL_RTL (fndecl), 0);
+
+#define A0(m,i) adjust_address (m_tramp, m, i)
   if (TARGET_A16)
     {
       /* Note: we subtract a "word" because the moves want signed
@@ -1757,39 +1870,38 @@ m32c_initialize_trampoline (rtx tramp, rtx function, rtx chainval)
 static void
 m32c_init_libfuncs (void)
 {
+  /* We do this because the M32C has an HImode operand, but the
+     M16C has an 8-bit operand.  Since gcc looks at the match data
+     and not the expanded rtl, we have to reset the optab so that
+     the right modes are found. */
   if (TARGET_A24)
     {
-      /* We do this because the M32C has an HImode operand, but the
-        M16C has an 8-bit operand.  Since gcc looks at the match data
-        and not the expanded rtl, we have to reset the array so that
-        the right modes are found. */
-      setcc_gen_code[EQ] = CODE_FOR_seq_24;
-      setcc_gen_code[NE] = CODE_FOR_sne_24;
-      setcc_gen_code[GT] = CODE_FOR_sgt_24;
-      setcc_gen_code[GE] = CODE_FOR_sge_24;
-      setcc_gen_code[LT] = CODE_FOR_slt_24;
-      setcc_gen_code[LE] = CODE_FOR_sle_24;
-      setcc_gen_code[GTU] = CODE_FOR_sgtu_24;
-      setcc_gen_code[GEU] = CODE_FOR_sgeu_24;
-      setcc_gen_code[LTU] = CODE_FOR_sltu_24;
-      setcc_gen_code[LEU] = CODE_FOR_sleu_24;
+      set_optab_handler (cstore_optab, QImode, CODE_FOR_cstoreqi4_24);
+      set_optab_handler (cstore_optab, HImode, CODE_FOR_cstorehi4_24);
+      set_optab_handler (cstore_optab, PSImode, CODE_FOR_cstorepsi4_24);
     }
 }
 
 /* Addressing Modes */
 
-/* Used by GO_IF_LEGITIMATE_ADDRESS.  The r8c/m32c family supports a
-   wide range of non-orthogonal addressing modes, including the
-   ability to double-indirect on *some* of them.  Not all insns
-   support all modes, either, but we rely on predicates and
-   constraints to deal with that.  */
-int
-m32c_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+/* The r8c/m32c family supports a wide range of non-orthogonal
+   addressing modes, including the ability to double-indirect on *some*
+   of them.  Not all insns support all modes, either, but we rely on
+   predicates and constraints to deal with that.  */
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P m32c_legitimate_address_p
+bool
+m32c_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
 {
   int mode_adjust;
   if (CONSTANT_P (x))
     return 1;
 
+  if (TARGET_A16 && GET_MODE (x) != HImode && GET_MODE (x) != SImode)
+    return 0;
+  if (TARGET_A24 && GET_MODE (x) != PSImode)
+    return 0;
+
   /* Wide references to memory will be split after reload, so we must
      ensure that all parts of such splits remain legitimate
      addresses.  */
@@ -1824,11 +1936,13 @@ m32c_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
         to please the assembler.  */
       switch (REGNO (patternr[0]))
        {
-       case A0_REGNO:
        case A1_REGNO:
        case SB_REGNO:
        case FB_REGNO:
        case SP_REGNO:
+         if (TARGET_A16 && GET_MODE (x) == SImode)
+           return 0;
+       case A0_REGNO:
          return 1;
 
        default:
@@ -1837,6 +1951,10 @@ m32c_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
          return 0;
        }
     }
+
+  if (TARGET_A16 && GET_MODE (x) == SImode)
+    return 0;
+
   if (RTX_IS ("+ri"))
     {
       /* This is more interesting, because different base registers
@@ -1949,33 +2067,33 @@ m32c_reg_ok_for_base_p (rtx x, int strict)
    displacement range.  We deal with this by attempting to reload $fb
    itself into an address register; that seems to result in the best
    code.  */
-int
-m32c_legitimize_address (rtx * x ATTRIBUTE_UNUSED,
-                        rtx oldx ATTRIBUTE_UNUSED,
-                        enum machine_mode mode ATTRIBUTE_UNUSED)
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS m32c_legitimize_address
+static rtx
+m32c_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+                        enum machine_mode mode)
 {
 #if DEBUG0
   fprintf (stderr, "m32c_legitimize_address for mode %s\n", mode_name[mode]);
-  debug_rtx (*x);
+  debug_rtx (x);
   fprintf (stderr, "\n");
 #endif
 
-  if (GET_CODE (*x) == PLUS
-      && GET_CODE (XEXP (*x, 0)) == REG
-      && REGNO (XEXP (*x, 0)) == FB_REGNO
-      && GET_CODE (XEXP (*x, 1)) == CONST_INT
-      && (INTVAL (XEXP (*x, 1)) < -128
-         || INTVAL (XEXP (*x, 1)) > (128 - GET_MODE_SIZE (mode))))
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == REG
+      && REGNO (XEXP (x, 0)) == FB_REGNO
+      && GET_CODE (XEXP (x, 1)) == CONST_INT
+      && (INTVAL (XEXP (x, 1)) < -128
+         || INTVAL (XEXP (x, 1)) > (128 - GET_MODE_SIZE (mode))))
     {
       /* reload FB to A_REGS */
       rtx temp = gen_reg_rtx (Pmode);
-      *x = copy_rtx (*x);
-      emit_insn (gen_rtx_SET (VOIDmode, temp, XEXP (*x, 0)));
-      XEXP (*x, 0) = temp;
-      return 1;
+      x = copy_rtx (x);
+      emit_insn (gen_rtx_SET (VOIDmode, temp, XEXP (x, 0)));
+      XEXP (x, 0) = temp;
     }
 
-  return 0;
+  return x;
 }
 
 /* Implements LEGITIMIZE_RELOAD_ADDRESS.  See comment above.  */
@@ -2018,7 +2136,7 @@ m32c_legitimize_reload_address (rtx * x,
        type = RELOAD_FOR_OTHER_ADDRESS;
       push_reload (sum, NULL_RTX, &XEXP (*x, 0), NULL,
                   A_REGS, Pmode, VOIDmode, 0, 0, opnum,
-                  type);
+                  (enum reload_type) type);
       return 1;
     }
 
@@ -2034,7 +2152,7 @@ m32c_legitimize_reload_address (rtx * x,
        type = RELOAD_FOR_OTHER_ADDRESS;
       push_reload (XEXP (*x, 0), NULL_RTX, &XEXP (*x, 0), NULL,
                   A_REGS, Pmode, VOIDmode, 0, 0, opnum,
-                  type);
+                  (enum reload_type) type);
       return 1;
     }
 
@@ -2050,6 +2168,204 @@ m32c_legitimate_constant_p (rtx x ATTRIBUTE_UNUSED)
 }
 
 
+/* Return the appropriate mode for a named address pointer.  */
+#undef TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE m32c_addr_space_pointer_mode
+static enum machine_mode
+m32c_addr_space_pointer_mode (addr_space_t addrspace)
+{
+  switch (addrspace)
+    {
+    case ADDR_SPACE_GENERIC:
+      return TARGET_A24 ? PSImode : HImode;
+    case ADDR_SPACE_FAR:
+      return SImode;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return the appropriate mode for a named address address.  */
+#undef TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE m32c_addr_space_address_mode
+static enum machine_mode
+m32c_addr_space_address_mode (addr_space_t addrspace)
+{
+  switch (addrspace)
+    {
+    case ADDR_SPACE_GENERIC:
+      return TARGET_A24 ? PSImode : HImode;
+    case ADDR_SPACE_FAR:
+      return SImode;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Like m32c_legitimate_address_p, except with named addresses.  */
+#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P \
+  m32c_addr_space_legitimate_address_p
+static bool
+m32c_addr_space_legitimate_address_p (enum machine_mode mode, rtx x,
+                                     bool strict, addr_space_t as)
+{
+  if (as == ADDR_SPACE_FAR)
+    {
+      if (TARGET_A24)
+       return 0;
+      encode_pattern (x);
+      if (RTX_IS ("r"))
+       {
+         if (GET_MODE (x) != SImode)
+           return 0;
+         switch (REGNO (patternr[0]))
+           {
+           case A0_REGNO:
+             return 1;
+
+           default:
+             if (IS_PSEUDO (patternr[0], strict))
+               return 1;
+             return 0;
+           }
+       }
+      if (RTX_IS ("+^Sri"))
+       {
+         int rn = REGNO (patternr[3]);
+         HOST_WIDE_INT offs = INTVAL (patternr[4]);
+         if (GET_MODE (patternr[3]) != HImode)
+           return 0;
+         switch (rn)
+           {
+           case A0_REGNO:
+             return (offs >= 0 && offs <= 0xfffff);
+
+           default:
+             if (IS_PSEUDO (patternr[3], strict))
+               return 1;
+             return 0;
+           }
+       }
+      if (RTX_IS ("+^Srs"))
+       {
+         int rn = REGNO (patternr[3]);
+         if (GET_MODE (patternr[3]) != HImode)
+           return 0;
+         switch (rn)
+           {
+           case A0_REGNO:
+             return 1;
+
+           default:
+             if (IS_PSEUDO (patternr[3], strict))
+               return 1;
+             return 0;
+           }
+       }
+      if (RTX_IS ("+^S+ris"))
+       {
+         int rn = REGNO (patternr[4]);
+         if (GET_MODE (patternr[4]) != HImode)
+           return 0;
+         switch (rn)
+           {
+           case A0_REGNO:
+             return 1;
+
+           default:
+             if (IS_PSEUDO (patternr[4], strict))
+               return 1;
+             return 0;
+           }
+       }
+      if (RTX_IS ("s"))
+       {
+         return 1;
+       }
+      return 0;
+    }
+
+  else if (as != ADDR_SPACE_GENERIC)
+    gcc_unreachable ();
+
+  return m32c_legitimate_address_p (mode, x, strict);
+}
+
+/* Like m32c_legitimate_address, except with named address support.  */
+#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
+#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS m32c_addr_space_legitimize_address
+static rtx
+m32c_addr_space_legitimize_address (rtx x, rtx oldx, enum machine_mode mode,
+                                   addr_space_t as)
+{
+  if (as != ADDR_SPACE_GENERIC)
+    {
+#if DEBUG0
+      fprintf (stderr, "\033[36mm32c_addr_space_legitimize_address for mode %s\033[0m\n", mode_name[mode]);
+      debug_rtx (x);
+      fprintf (stderr, "\n");
+#endif
+
+      if (GET_CODE (x) != REG)
+       {
+         x = force_reg (SImode, x);
+       }
+      return x;
+    }
+
+  return m32c_legitimize_address (x, oldx, mode);
+}
+
+/* Determine if one named address space is a subset of another.  */
+#undef TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P m32c_addr_space_subset_p
+static bool
+m32c_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
+{
+  gcc_assert (subset == ADDR_SPACE_GENERIC || subset == ADDR_SPACE_FAR);
+  gcc_assert (superset == ADDR_SPACE_GENERIC || superset == ADDR_SPACE_FAR);
+
+  if (subset == superset)
+    return true;
+
+  else
+    return (subset == ADDR_SPACE_GENERIC && superset == ADDR_SPACE_FAR);
+}
+
+#undef TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT m32c_addr_space_convert
+/* Convert from one address space to another.  */
+static rtx
+m32c_addr_space_convert (rtx op, tree from_type, tree to_type)
+{
+  addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
+  addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
+  rtx result;
+
+  gcc_assert (from_as == ADDR_SPACE_GENERIC || from_as == ADDR_SPACE_FAR);
+  gcc_assert (to_as == ADDR_SPACE_GENERIC || to_as == ADDR_SPACE_FAR);
+
+  if (to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_FAR)
+    {
+      /* This is unpredictable, as we're truncating off usable address
+        bits.  */
+
+      result = gen_reg_rtx (HImode);
+      emit_move_insn (result, simplify_subreg (HImode, op, SImode, 0));
+      return result;
+    }
+  else if (to_as == ADDR_SPACE_FAR && from_as == ADDR_SPACE_GENERIC)
+    {
+      /* This always works.  */
+      result = gen_reg_rtx (SImode);
+      emit_insn (gen_zero_extendhisi2 (result, op));
+      return result;
+    }
+  else
+    gcc_unreachable ();
+}
+
 /* Condition Code Status */
 
 #undef TARGET_FIXED_CONDITION_CODE_REGS
@@ -2064,19 +2380,29 @@ m32c_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
 
 /* Describing Relative Costs of Operations */
 
-/* Implements REGISTER_MOVE_COST.  We make impossible moves
+/* Implements TARGET_REGISTER_MOVE_COST.  We make impossible moves
    prohibitively expensive, like trying to put QIs in r2/r3 (there are
    no opcodes to do that).  We also discourage use of mem* registers
    since they're really memory.  */
-int
-m32c_register_move_cost (enum machine_mode mode, int from, int to)
+
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST m32c_register_move_cost
+
+static int
+m32c_register_move_cost (enum machine_mode mode, reg_class_t from,
+                        reg_class_t to)
 {
   int cost = COSTS_N_INSNS (3);
-  int cc = class_contents[from][0] | class_contents[to][0];
-  /* FIXME: pick real values, but not 2 for now.  */
-  if (mode == QImode && (cc & class_contents[R23_REGS][0]))
+  HARD_REG_SET cc;
+
+/* FIXME: pick real values, but not 2 for now.  */
+  COPY_HARD_REG_SET (cc, reg_class_contents[(int) from]);
+  IOR_HARD_REG_SET (cc, reg_class_contents[(int) to]);
+
+  if (mode == QImode
+      && hard_reg_set_intersect_p (cc, reg_class_contents[R23_REGS]))
     {
-      if (!(cc & ~class_contents[R23_REGS][0]))
+      if (hard_reg_set_subset_p (cc, reg_class_contents[R23_REGS]))
        cost = COSTS_N_INSNS (1000);
       else
        cost = COSTS_N_INSNS (80);
@@ -2085,30 +2411,35 @@ m32c_register_move_cost (enum machine_mode mode, int from, int to)
   if (!class_can_hold_mode (from, mode) || !class_can_hold_mode (to, mode))
     cost = COSTS_N_INSNS (1000);
 
-  if (classes_intersect (from, CR_REGS))
+  if (reg_classes_intersect_p (from, CR_REGS))
     cost += COSTS_N_INSNS (5);
 
-  if (classes_intersect (to, CR_REGS))
+  if (reg_classes_intersect_p (to, CR_REGS))
     cost += COSTS_N_INSNS (5);
 
   if (from == MEM_REGS || to == MEM_REGS)
     cost += COSTS_N_INSNS (50);
-  else if (classes_intersect (from, MEM_REGS)
-          || classes_intersect (to, MEM_REGS))
+  else if (reg_classes_intersect_p (from, MEM_REGS)
+          || reg_classes_intersect_p (to, MEM_REGS))
     cost += COSTS_N_INSNS (10);
 
 #if DEBUG0
   fprintf (stderr, "register_move_cost %s from %s to %s = %d\n",
-          mode_name[mode], class_names[from], class_names[to], cost);
+          mode_name[mode], class_names[(int) from], class_names[(int) to],
+          cost);
 #endif
   return cost;
 }
 
-/*  Implements MEMORY_MOVE_COST.  */
-int
+/*  Implements TARGET_MEMORY_MOVE_COST.  */
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST m32c_memory_move_cost
+
+static int
 m32c_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
-                      int reg_class ATTRIBUTE_UNUSED,
-                      int in ATTRIBUTE_UNUSED)
+                      reg_class_t rclass ATTRIBUTE_UNUSED,
+                      bool in ATTRIBUTE_UNUSED)
 {
   /* FIXME: pick real values.  */
   return COSTS_N_INSNS (10);
@@ -2119,7 +2450,8 @@ m32c_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS m32c_rtx_costs
 static bool
-m32c_rtx_costs (rtx x, int code, int outer_code, int *total)
+m32c_rtx_costs (rtx x, int code, int outer_code, int *total,
+               bool speed ATTRIBUTE_UNUSED)
 {
   switch (code)
     {
@@ -2198,7 +2530,7 @@ m32c_rtx_costs (rtx x, int code, int outer_code, int *total)
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST m32c_address_cost
 static int
-m32c_address_cost (rtx addr)
+m32c_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
 {
   int i;
   /*  fprintf(stderr, "\naddress_cost\n");
@@ -2287,6 +2619,12 @@ const conversions[] = {
   { 0, "mr", "z[1]" },
   { 0, "m+ri", "3[2]" },
   { 0, "m+rs", "3[2]" },
+  { 0, "m+^Zrs", "5[4]" },
+  { 0, "m+^Zri", "5[4]" },
+  { 0, "m+^Z+ris", "7+6[5]" },
+  { 0, "m+^Srs", "5[4]" },
+  { 0, "m+^Sri", "5[4]" },
+  { 0, "m+^S+ris", "7+6[5]" },
   { 0, "m+r+si", "4+5[2]" },
   { 0, "ms", "1" },
   { 0, "mi", "1" },
@@ -2695,8 +3033,13 @@ m32c_print_operand_punct_valid_p (int c)
 void
 m32c_print_operand_address (FILE * stream, rtx address)
 {
-  gcc_assert (GET_CODE (address) == MEM);
-  m32c_print_operand (stream, XEXP (address, 0), 0);
+  if (GET_CODE (address) == MEM)
+    address = XEXP (address, 0);
+  else
+    /* cf: gcc.dg/asm-4.c.  */
+    gcc_assert (GET_CODE (address) == REG);
+
+  m32c_print_operand (stream, address, 0);
 }
 
 /* Implements ASM_OUTPUT_REG_PUSH.  Control registers are pushed
@@ -2743,6 +3086,34 @@ interrupt_p (tree node ATTRIBUTE_UNUSED)
        return 1;
       list = TREE_CHAIN (list);
     }
+  return fast_interrupt_p (node);
+}
+
+/* Returns TRUE if the given tree has the "bank_switch" attribute.  */
+static int
+bank_switch_p (tree node ATTRIBUTE_UNUSED)
+{
+  tree list = M32C_ATTRIBUTES (node);
+  while (list)
+    {
+      if (is_attribute_p ("bank_switch", TREE_PURPOSE (list)))
+       return 1;
+      list = TREE_CHAIN (list);
+    }
+  return 0;
+}
+
+/* Returns TRUE if the given tree has the "fast_interrupt" attribute.  */
+static int
+fast_interrupt_p (tree node ATTRIBUTE_UNUSED)
+{
+  tree list = M32C_ATTRIBUTES (node);
+  while (list)
+    {
+      if (is_attribute_p ("fast_interrupt", TREE_PURPOSE (list)))
+       return 1;
+      list = TREE_CHAIN (list);
+    }
   return 0;
 }
 
@@ -2760,10 +3131,12 @@ interrupt_handler (tree * node ATTRIBUTE_UNUSED,
 int
 m32c_special_page_vector_p (tree func)
 {
+  tree list;
+
   if (TREE_CODE (func) != FUNCTION_DECL)
     return 0;
 
-  tree list = M32C_ATTRIBUTES (func);
+  list = M32C_ATTRIBUTES (func);
   while (list)
     {
       if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
@@ -2784,24 +3157,24 @@ function_vector_handler (tree * node ATTRIBUTE_UNUSED,
     {
       /* The attribute is not supported for R8C target.  */
       warning (OPT_Wattributes,
-                "`%s' attribute is not supported for R8C target",
-                IDENTIFIER_POINTER (name));
+                "%qE attribute is not supported for R8C target",
+                name);
       *no_add_attrs = true;
     }
   else if (TREE_CODE (*node) != FUNCTION_DECL)
     {
       /* The attribute must be applied to functions only.  */
       warning (OPT_Wattributes,
-                "`%s' attribute applies only to functions",
-                IDENTIFIER_POINTER (name));
+                "%qE attribute applies only to functions",
+                name);
       *no_add_attrs = true;
     }
   else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
     {
       /* The argument must be a constant integer.  */
       warning (OPT_Wattributes,
-                "`%s' attribute argument not an integer constant",
-                IDENTIFIER_POINTER (name));
+                "%qE attribute argument not an integer constant",
+                name);
       *no_add_attrs = true;
     }
   else if (TREE_INT_CST_LOW (TREE_VALUE (args)) < 18
@@ -2809,8 +3182,8 @@ function_vector_handler (tree * node ATTRIBUTE_UNUSED,
     {
       /* The argument value must be between 18 to 255.  */
       warning (OPT_Wattributes,
-                "`%s' attribute argument should be between 18 to 255",
-                IDENTIFIER_POINTER (name));
+                "%qE attribute argument should be between 18 to 255",
+                name);
       *no_add_attrs = true;
     }
   return NULL_TREE;
@@ -2826,12 +3199,13 @@ current_function_special_page_vector (rtx x)
   if ((GET_CODE(x) == SYMBOL_REF)
       && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
     {
+      tree list;
       tree t = SYMBOL_REF_DECL (x);
 
       if (TREE_CODE (t) != FUNCTION_DECL)
         return 0;
 
-      tree list = M32C_ATTRIBUTES (t);
+      list = M32C_ATTRIBUTES (t);
       while (list)
         {
           if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
@@ -2853,6 +3227,8 @@ current_function_special_page_vector (rtx x)
 #define TARGET_ATTRIBUTE_TABLE m32c_attribute_table
 static const struct attribute_spec m32c_attribute_table[] = {
   {"interrupt", 0, 0, false, false, false, interrupt_handler},
+  {"bank_switch", 0, 0, false, false, false, interrupt_handler},
+  {"fast_interrupt", 0, 0, false, false, false, interrupt_handler},
   {"function_vector", 1, 1, true,  false, false, function_vector_handler},
   {0, 0, 0, 0, 0, 0, 0}
 };
@@ -2873,7 +3249,108 @@ static void
 m32c_insert_attributes (tree node ATTRIBUTE_UNUSED,
                        tree * attr_ptr ATTRIBUTE_UNUSED)
 {
-  /* Nothing to do here.  */
+  unsigned addr;
+  /* See if we need to make #pragma address variables volatile.  */
+
+  if (TREE_CODE (node) == VAR_DECL)
+    {
+      const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
+      if (m32c_get_pragma_address  (name, &addr))
+       {
+         TREE_THIS_VOLATILE (node) = true;
+       }
+    }  
+}
+
+
+struct GTY(()) pragma_entry {
+  const char *varname;
+  unsigned address;
+};
+typedef struct pragma_entry pragma_entry;
+
+/* Hash table of pragma info.  */
+static GTY((param_is (pragma_entry))) htab_t pragma_htab;
+
+static int
+pragma_entry_eq (const void *p1, const void *p2)
+{
+  const pragma_entry *old = (const pragma_entry *) p1;
+  const char *new_name = (const char *) p2;
+
+  return strcmp (old->varname, new_name) == 0;
+}
+
+static hashval_t
+pragma_entry_hash (const void *p)
+{
+  const pragma_entry *old = (const pragma_entry *) p;
+  return htab_hash_string (old->varname);
+}
+
+void
+m32c_note_pragma_address (const char *varname, unsigned address)
+{
+  pragma_entry **slot;
+
+  if (!pragma_htab)
+    pragma_htab = htab_create_ggc (31, pragma_entry_hash,
+                                   pragma_entry_eq, NULL);
+
+  slot = (pragma_entry **)
+    htab_find_slot_with_hash (pragma_htab, varname,
+                             htab_hash_string (varname), INSERT);
+
+  if (!*slot)
+    {
+      *slot = ggc_alloc_pragma_entry ();
+      (*slot)->varname = ggc_strdup (varname);
+    }
+  (*slot)->address = address;
+}
+
+static bool
+m32c_get_pragma_address (const char *varname, unsigned *address)
+{
+  pragma_entry **slot;
+
+  if (!pragma_htab)
+    return false;
+
+  slot = (pragma_entry **)
+    htab_find_slot_with_hash (pragma_htab, varname,
+                             htab_hash_string (varname), NO_INSERT);
+  if (slot && *slot)
+    {
+      *address = (*slot)->address;
+      return true;
+    }
+  return false;
+}
+
+void
+m32c_output_aligned_common (FILE *stream, tree decl ATTRIBUTE_UNUSED,
+                           const char *name,
+                           int size, int align, int global)
+{
+  unsigned address;
+
+  if (m32c_get_pragma_address (name, &address))
+    {
+      /* We never output these as global.  */
+      assemble_name (stream, name);
+      fprintf (stream, " = 0x%04x\n", address);
+      return;
+    }
+  if (!global)
+    {
+      fprintf (stream, "\t.local\t");
+      assemble_name (stream, name);
+      fprintf (stream, "\n");
+    }
+  fprintf (stream, "\t.comm\t");
+  assemble_name (stream, name);
+  fprintf (stream, ",%u,%u\n", size, align / BITS_PER_UNIT);
 }
 
 /* Predicates */
@@ -2904,7 +3381,7 @@ static const struct {
 };
 
 /* Returns TRUE if OP is a subreg of a hard reg which we don't
-   support.  */
+   support.  We also bail on MEMs with illegal addresses.  */
 bool
 m32c_illegal_subreg_p (rtx op)
 {
@@ -2912,6 +3389,12 @@ m32c_illegal_subreg_p (rtx op)
   unsigned int i;
   int src_mode, dest_mode;
 
+  if (GET_CODE (op) == MEM
+      && ! m32c_legitimate_address_p (Pmode, XEXP (op, 0), false))
+    {
+      return true;
+    }
+
   if (GET_CODE (op) != SUBREG)
     return false;
 
@@ -3125,7 +3608,19 @@ m32c_subreg (enum machine_mode outer,
     return gen_rtx_MEM (outer, XEXP (XEXP (x, 0), 0));
 
   if (GET_CODE (x) != REG)
-    return simplify_gen_subreg (outer, x, inner, byte);
+    {
+      rtx r = simplify_gen_subreg (outer, x, inner, byte);
+      if (GET_CODE (r) == SUBREG
+         && GET_CODE (x) == MEM
+         && MEM_VOLATILE_P (x))
+       {
+         /* Volatile MEMs don't get simplified, but we need them to
+            be.  We are little endian, so the subreg byte is the
+            offset.  */
+         r = adjust_address (x, outer, byte);
+       }
+      return r;
+    }
 
   r = REGNO (x);
   if (r >= FIRST_PSEUDO_REGISTER || r == AP_REGNO)
@@ -3173,6 +3668,11 @@ m32c_subreg (enum machine_mode outer,
 int
 m32c_prepare_move (rtx * operands, enum machine_mode mode)
 {
+  if (far_addr_space_p (operands[0])
+      && CONSTANT_P (operands[1]))
+    {
+      operands[1] = force_reg (GET_MODE (operands[0]), operands[1]);
+    }
   if (TARGET_A16 && mode == PSImode)
     return m32c_split_move (operands, mode, 1);
   if ((GET_CODE (operands[0]) == MEM)
@@ -3278,6 +3778,11 @@ m32c_split_move (rtx * operands, enum machine_mode mode, int split_all)
   if (m32c_extra_constraint_p (operands[0], 'S', "Ss"))
     split_all = 3;
 
+  if (TARGET_A16
+      && (far_addr_space_p (operands[0])
+         || far_addr_space_p (operands[1])))
+    split_all |= 1;
+
   /* We don't need to split these.  */
   if (TARGET_A24
       && split_all != 3
@@ -3626,13 +4131,12 @@ m32c_prepare_shift (rtx * operands, int scale, int shift_code)
         undefined to skip one of the comparisons.  */
 
       rtx count;
-      rtx label, lref, insn, tempvar;
+      rtx label, insn, tempvar;
 
       emit_move_insn (operands[0], operands[1]);
 
       count = temp;
       label = gen_label_rtx ();
-      lref = gen_rtx_LABEL_REF (VOIDmode, label);
       LABEL_NUSES (label) ++;
 
       tempvar = gen_reg_rtx (mode);
@@ -3693,84 +4197,29 @@ m32c_expand_neg_mulpsi3 (rtx * operands)
   emit_insn (gen_truncsipsi2 (operands[0], temp2));
 }
 
-static rtx compare_op0, compare_op1;
-
-void
-m32c_pend_compare (rtx *operands)
-{
-  compare_op0 = operands[0];
-  compare_op1 = operands[1];
-}
-
-void
-m32c_unpend_compare (void)
-{
-  switch (GET_MODE (compare_op0))
-    {
-    case QImode:
-      emit_insn (gen_cmpqi_op (compare_op0, compare_op1));
-    case HImode:
-      emit_insn (gen_cmphi_op (compare_op0, compare_op1));
-    case PSImode:
-      emit_insn (gen_cmppsi_op (compare_op0, compare_op1));
-    default:
-      /* Just to silence the "missing case" warnings.  */ ;
-    }
-}
-
-void
-m32c_expand_scc (int code, rtx *operands)
-{
-  enum machine_mode mode = TARGET_A16 ? QImode : HImode;
-
-  emit_insn (gen_rtx_SET (mode,
-                         operands[0],
-                         gen_rtx_fmt_ee (code,
-                                         mode,
-                                         compare_op0,
-                                         compare_op1)));
-}
-
 /* Pattern Output Functions */
 
-/* Returns a (OP (reg:CC FLG_REGNO) (const_int 0)) from some other
-   match_operand rtx's OP.  */
-rtx
-m32c_cmp_flg_0 (rtx cmp)
-{
-  return gen_rtx_fmt_ee (GET_CODE (cmp),
-                        GET_MODE (cmp),
-                        gen_rtx_REG (CCmode, FLG_REGNO),
-                        GEN_INT (0));
-}
-
 int
 m32c_expand_movcc (rtx *operands)
 {
   rtx rel = operands[1];
-  rtx cmp;
 
   if (GET_CODE (rel) != EQ && GET_CODE (rel) != NE)
     return 1;
   if (GET_CODE (operands[2]) != CONST_INT
       || GET_CODE (operands[3]) != CONST_INT)
     return 1;
-  emit_insn (gen_cmpqi(XEXP (rel, 0), XEXP (rel, 1)));
   if (GET_CODE (rel) == NE)
     {
       rtx tmp = operands[2];
       operands[2] = operands[3];
       operands[3] = tmp;
+      rel = gen_rtx_EQ (GET_MODE (rel), XEXP (rel, 0), XEXP (rel, 1));
     }
 
-  cmp = gen_rtx_fmt_ee (GET_CODE (rel),
-                       GET_MODE (rel),
-                       compare_op0,
-                       compare_op1);
-
   emit_move_insn (operands[0],
                  gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
-                                       cmp,
+                                       rel,
                                        operands[2],
                                        operands[3]));
   return 0;
@@ -3816,7 +4265,7 @@ m32c_expand_insv (rtx *operands)
 
   if (GET_MODE (op0) == HImode
       && INTVAL (operands[2]) >= 8
-      && GET_MODE (op0) == MEM)
+      && GET_CODE (op0) == MEM)
     {
       /* We are little endian.  */
       rtx new_mem = gen_rtx_MEM (QImode, plus_constant (XEXP (op0, 0), 1));
@@ -3859,6 +4308,7 @@ m32c_expand_insv (rtx *operands)
     case 5: p = gen_iorqi3_24 (op0, src0, GEN_INT (mask)); break;
     case 6: p = gen_iorhi3_16 (op0, src0, GEN_INT (mask)); break;
     case 7: p = gen_iorhi3_24 (op0, src0, GEN_INT (mask)); break;
+    default: p = NULL_RTX; break; /* Not reached, but silences a warning.  */
     }
 
   emit_insn (p);
@@ -3988,16 +4438,23 @@ m32c_emit_prologue (void)
       cfun->machine->is_interrupt = 1;
       complex_prologue = 1;
     }
+  else if (bank_switch_p (cfun->decl))
+    warning (OPT_Wattributes,
+            "%<bank_switch%> has no effect on non-interrupt functions");
 
   reg_save_size = m32c_pushm_popm (PP_justcount);
 
   if (interrupt_p (cfun->decl))
-    emit_insn (gen_pushm (GEN_INT (cfun->machine->intr_pushm)));
+    {
+      if (bank_switch_p (cfun->decl))
+       emit_insn (gen_fset_b ());
+      else if (cfun->machine->intr_pushm)
+       emit_insn (gen_pushm (GEN_INT (cfun->machine->intr_pushm)));
+    }
 
   frame_size =
     m32c_initial_elimination_offset (FB_REGNO, SP_REGNO) - reg_save_size;
   if (frame_size == 0
-      && !cfun->machine->is_interrupt
       && !m32c_function_needs_enter ())
     cfun->machine->use_rts = 1;
 
@@ -4048,16 +4505,47 @@ m32c_emit_epilogue (void)
     {
       enum machine_mode spmode = TARGET_A16 ? HImode : PSImode;
 
-      emit_move_insn (gen_rtx_REG (spmode, A0_REGNO),
-                     gen_rtx_REG (spmode, FP_REGNO));
-      emit_move_insn (gen_rtx_REG (spmode, SP_REGNO),
-                     gen_rtx_REG (spmode, A0_REGNO));
-      if (TARGET_A16)
-       emit_insn (gen_pophi_16 (gen_rtx_REG (HImode, FP_REGNO)));
-      else
-       emit_insn (gen_poppsi (gen_rtx_REG (PSImode, FP_REGNO)));
-      emit_insn (gen_popm (GEN_INT (cfun->machine->intr_pushm)));
-      if (TARGET_A16)
+      /* REIT clears B flag and restores $fp for us, but we still
+        have to fix up the stack.  USE_RTS just means we didn't
+        emit ENTER.  */
+      if (!cfun->machine->use_rts)
+       {
+         emit_move_insn (gen_rtx_REG (spmode, A0_REGNO),
+                         gen_rtx_REG (spmode, FP_REGNO));
+         emit_move_insn (gen_rtx_REG (spmode, SP_REGNO),
+                         gen_rtx_REG (spmode, A0_REGNO));
+         /* We can't just add this to the POPM because it would be in
+            the wrong order, and wouldn't fix the stack if we're bank
+            switching.  */
+         if (TARGET_A16)
+           emit_insn (gen_pophi_16 (gen_rtx_REG (HImode, FP_REGNO)));
+         else
+           emit_insn (gen_poppsi (gen_rtx_REG (PSImode, FP_REGNO)));
+       }
+      if (!bank_switch_p (cfun->decl) && cfun->machine->intr_pushm)
+       emit_insn (gen_popm (GEN_INT (cfun->machine->intr_pushm)));
+
+      /* The FREIT (Fast REturn from InTerrupt) instruction should be
+         generated only for M32C/M32CM targets (generate the REIT
+         instruction otherwise).  */
+      if (fast_interrupt_p (cfun->decl))
+        {
+          /* Check if fast_attribute is set for M32C or M32CM.  */
+          if (TARGET_A24)
+            {
+              emit_jump_insn (gen_epilogue_freit ());
+            }
+          /* If fast_interrupt attribute is set for an R8C or M16C
+             target ignore this attribute and generated REIT
+             instruction.  */
+          else
+           {
+             warning (OPT_Wattributes,
+                      "%<fast_interrupt%> attribute directive ignored");
+             emit_jump_insn (gen_epilogue_reit_16 ());
+           }
+        }
+      else if (TARGET_A16)
        emit_jump_insn (gen_epilogue_reit_16 ());
       else
        emit_jump_insn (gen_epilogue_reit_24 ());
@@ -4119,14 +4607,13 @@ m32c_compare_redundant (rtx cmp, rtx *operands)
   int flags_needed;
   int pflags;
   rtx prev, pp, next;
-  rtx op0, op1, op2;
+  rtx op0, op1;
 #if DEBUG_CMP
   int prev_icode, i;
 #endif
 
   op0 = operands[0];
   op1 = operands[1];
-  op2 = operands[2];
 
 #if DEBUG_CMP
   fprintf(stderr, "\n\033[32mm32c_compare_redundant\033[0m\n");
@@ -4227,6 +4714,40 @@ m32c_compare_redundant (rtx cmp, rtx *operands)
 #endif
        return false;
       }
+
+    /* Check for comparisons against memory - between volatiles and
+       aliases, we just can't risk this one.  */
+    if (GET_CODE (operands[0]) == MEM
+       || GET_CODE (operands[0]) == MEM)
+      {
+#if DEBUG_CMP
+       fprintf(stderr, "comparisons with memory:\n");
+       debug_rtx(prev);
+#endif
+       return false;
+      }
+
+    /* Check for PREV changing a register that's used to compute a
+       value in CMP, even if it doesn't otherwise change flags.  */
+    if (GET_CODE (operands[0]) == REG
+       && rtx_referenced_p (SET_DEST (PATTERN (prev)), operands[0]))
+      {
+#if DEBUG_CMP
+       fprintf(stderr, "sub-value affected, op0:\n");
+       debug_rtx(prev);
+#endif
+       return false;
+      }
+    if (GET_CODE (operands[1]) == REG
+       && rtx_referenced_p (SET_DEST (PATTERN (prev)), operands[1]))
+      {
+#if DEBUG_CMP
+       fprintf(stderr, "sub-value affected, op1:\n");
+       debug_rtx(prev);
+#endif
+       return false;
+      }
+
   } while (pflags == FLAGS_N);
 #if DEBUG_CMP
   fprintf(stderr, "previous flag-setting insn:\n");
@@ -4298,27 +4819,34 @@ m32c_compare_redundant (rtx cmp, rtx *operands)
 char *
 m32c_output_compare (rtx insn, rtx *operands)
 {
-  static char template[] = ";cmp.b\t%1,%0";
+  static char templ[] = ";cmp.b\t%1,%0";
   /*                             ^ 5  */
 
-  template[5] = " bwll"[GET_MODE_SIZE(GET_MODE(operands[0]))];
+  templ[5] = " bwll"[GET_MODE_SIZE(GET_MODE(operands[0]))];
   if (m32c_compare_redundant (insn, operands))
     {
 #if DEBUG_CMP
       fprintf(stderr, "cbranch: cmp not needed\n");
 #endif
-      return template;
+      return templ;
     }
 
 #if DEBUG_CMP
-  fprintf(stderr, "cbranch: cmp needed: `%s'\n", template);
+  fprintf(stderr, "cbranch: cmp needed: `%s'\n", templ + 1);
 #endif
-  return template + 1;
+  return templ + 1;
 }
 
 #undef TARGET_ENCODE_SECTION_INFO
 #define TARGET_ENCODE_SECTION_INFO m32c_encode_section_info
 
+/* If the frame pointer isn't used, we detect it manually.  But the
+   stack pointer doesn't have as flexible addressing as the frame
+   pointer, so we always assume we have it.  */
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED hook_bool_void_true
+
 /* The Global `targetm' Variable. */
 
 struct gcc_target targetm = TARGET_INITIALIZER;