]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/config/msp430/msp430.c
Wrap option names in gcc internal messages with %< and %>.
[thirdparty/gcc.git] / gcc / config / msp430 / msp430.c
index 3fdaab5b041f2bacaaa814734fc74d221db7e515..48d16a4d2ec7df45a343ddbe10b73928b947cb17 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines used for code generation on TI MSP430 processors.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2019 Free Software Foundation, Inc.
    Contributed by Red Hat.
 
    This file is part of GCC.
@@ -18,6 +18,8 @@
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
+#define IN_TARGET_CODE 1
+
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
 #include "rtl.h"
 #include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
 #include "gimple-expr.h"
 #include "df.h"
+#include "memmodel.h"
 #include "tm_p.h"
 #include "regs.h"
 #include "emit-rtl.h"
@@ -39,6 +44,7 @@
 #include "expr.h"
 #include "langhooks.h"
 #include "builtins.h"
+#include "intl.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -90,10 +96,10 @@ msp430_init_machine_status (void)
 #define TARGET_OPTION_OVERRIDE         msp430_option_override
 
 /* This is a copy of the same data structure found in gas/config/tc-msp430.c
-   Also another (sort-of) copy can be found in gcc/config/msp430/t-msp430.
+   Also another (sort-of) copy can be found in gcc/config/msp430/t-msp430
    Keep these three structures in sync.
-   The data in this structure has been extracted from the devices.csv file
-   released by TI, updated as of 8 October 2015.  */
+   The data in this structure has been extracted from version 1.194 of the
+   devices.csv file released by TI in September 2016.  */
 
 struct msp430_mcu_data
 {
@@ -519,7 +525,15 @@ msp430_mcu_data [] =
   { "msp430fg6626",2,8 },
   { "msp430fr2032",2,0 },
   { "msp430fr2033",2,0 },
+  { "msp430fr2110",2,0 },
+  { "msp430fr2111",2,0 },
+  { "msp430fr2310",2,0 },
+  { "msp430fr2311",2,0 },
   { "msp430fr2433",2,8 },
+  { "msp430fr2532",2,8 },
+  { "msp430fr2533",2,8 },
+  { "msp430fr2632",2,8 },
+  { "msp430fr2633",2,8 },
   { "msp430fr2xx_4xxgeneric",2,8 },
   { "msp430fr4131",2,0 },
   { "msp430fr4132",2,0 },
@@ -572,6 +586,8 @@ msp430_mcu_data [] =
   { "msp430fr5957",2,8 },
   { "msp430fr5958",2,8 },
   { "msp430fr5959",2,8 },
+  { "msp430fr5962",2,8 },
+  { "msp430fr5964",2,8 },
   { "msp430fr5967",2,8 },
   { "msp430fr5968",2,8 },
   { "msp430fr5969",2,8 },
@@ -584,6 +600,9 @@ msp430_mcu_data [] =
   { "msp430fr5988",2,8 },
   { "msp430fr5989",2,8 },
   { "msp430fr59891",2,8 },
+  { "msp430fr5992",2,8 },
+  { "msp430fr5994",2,8 },
+  { "msp430fr59941",2,8 },
   { "msp430fr5xx_6xxgeneric",2,8 },
   { "msp430fr6820",2,8 },
   { "msp430fr6822",2,8 },
@@ -706,10 +725,25 @@ msp430_mcu_name (void)
   if (target_mcu)
     {
       unsigned int i;
-      static char mcu_name [64];
+      unsigned int start_upper;
+      unsigned int end_upper;
+      static char mcu_name[64];
 
-      snprintf (mcu_name, sizeof (mcu_name) - 1, "__%s__", target_mcu);
-      for (i = strlen (mcu_name); i--;)
+      /* The 'i' in the device name symbol for msp430i* devices must be lower
+        case, to match the expected symbol in msp430.h.  */
+      if (strncmp (target_mcu, "msp430i", 7) == 0)
+       {
+         snprintf (mcu_name, sizeof (mcu_name) - 1, "__MSP430i%s__",
+                   target_mcu + 7);
+         start_upper = 9;
+       }
+      else
+       {
+         snprintf (mcu_name, sizeof (mcu_name) - 1, "__%s__", target_mcu);
+         start_upper = 2;
+       }
+      end_upper = strlen (mcu_name) - 2;
+      for (i = start_upper; i < end_upper; i++)
        mcu_name[i] = TOUPPER (mcu_name[i]);
       return mcu_name;
     }
@@ -717,51 +751,134 @@ msp430_mcu_name (void)
   return msp430x ? "__MSP430XGENERIC__" : "__MSP430GENERIC__";
 }
 
+static const char *
+hwmult_name (unsigned int val)
+{
+  switch (val)
+    {
+    case 0: return "none";
+    case 1: return "16-bit";
+    case 2: return "16-bit";
+    case 4: return "32-bit";
+    case 8: return "32-bit (5xx)";
+    default: gcc_unreachable ();
+    }
+}
+
 static void
 msp430_option_override (void)
 {
+  /* The MSP430 architecture can safely dereference a NULL pointer. In fact,
+  there are memory mapped registers there.  */
+  flag_delete_null_pointer_checks = 0;
+
   init_machine_status = msp430_init_machine_status;
 
   if (target_cpu)
     {
+      /* gcc/common/config/msp430-common.c will have
+        already canonicalised the string in target_cpu.  */
       if (strcasecmp (target_cpu, "msp430x") == 0)
        msp430x = true;
       else /* target_cpu == "msp430" - already handled by the front end.  */
        msp430x = false;
     }
-  /* Note - the front end has already ensured at most
-     one of target_cpu and target_mcu will be set.  */
-  else if (target_mcu)
+
+  if (target_mcu)
     {
       int i;
 
-      /* If we are given an MCU name, we assume that it supports 430X.
-        Then we check to see if it is one of the known MCUs that only
-        supports 430.  */
-      msp430x = true;
-
-      /* FIXME: This array is alpha sorted, so we could use a binary search.  */
+      /* FIXME: If the array were alpha sorted, we could use a binary search.  */
       for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
        if (strcasecmp (msp430_mcu_data[i].name, target_mcu) == 0)
          {
-           msp430x = msp430_mcu_data[i].revision >= 1;
+           bool xisa = msp430_mcu_data[i].revision >= 1; 
+
+           if (msp430_warn_mcu)
+             {
+               if (target_cpu&& msp430x != xisa)
+                 warning (0, "MCU '%s' supports %s ISA but %<-mcpu%> option "
+                          "is set to %s",
+                          target_mcu, xisa ? "430X" : "430", msp430x ? "430X" : "430");
+
+               if (msp430_mcu_data[i].hwmpy == 0
+                   && msp430_hwmult_type != MSP430_HWMULT_AUTO
+                   && msp430_hwmult_type != MSP430_HWMULT_NONE)
+                 warning (0, "MCU '%s' does not have hardware multiply "
+                          "support, but %<-mhwmult%> is set to %s",
+                          target_mcu,
+                          msp430_hwmult_type == MSP430_HWMULT_SMALL ? "16-bit"
+                          : msp430_hwmult_type == MSP430_HWMULT_LARGE ? "32-bit" : "f5series");
+               else if (msp430_hwmult_type == MSP430_HWMULT_SMALL
+                   && msp430_mcu_data[i].hwmpy != 1
+                   && msp430_mcu_data[i].hwmpy != 2 )
+                 warning (0, "MCU '%s' supports %s hardware multiply, "
+                          "but %<-mhwmult%> is set to 16-bit",
+                          target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+               else if (msp430_hwmult_type == MSP430_HWMULT_LARGE && msp430_mcu_data[i].hwmpy != 4)
+                 warning (0, "MCU '%s' supports %s hardware multiply, "
+                          "but %<-mhwmult%> is set to 32-bit",
+                          target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+               else if (msp430_hwmult_type == MSP430_HWMULT_F5SERIES && msp430_mcu_data[i].hwmpy != 8)
+                 warning (0, "MCU '%s' supports %s hardware multiply, "
+                          "but %<-mhwmult%> is set to f5series",
+                          target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+             }
+
+           msp430x = xisa;
            break;
          }
+
       if (i < 0)
        {
-         warning (0, "Unrecognised MCU name '%s', assuming that it is just a MSP430 with no hardware multiply",
-                  target_mcu);
-         msp430x = false;
+         if (msp430_hwmult_type == MSP430_HWMULT_AUTO)
+           {
+             if (msp430_warn_mcu)
+               {
+                 if (target_cpu == NULL)
+                   warning (0,
+                            "Unrecognized MCU name '%s', assuming that it is "
+                            "just a MSP430 with no hardware multiply.\n"
+                            "Use the %<-mcpu%> and %<-mhwmult%> options to "
+                            "set these explicitly.",
+                            target_mcu);
+                 else
+                   warning (0,
+                            "Unrecognized MCU name '%s', assuming that it "
+                            "has no hardware multiply.\nUse the %<-mhwmult%> "
+                            "option to set this explicitly.",
+                            target_mcu);
+               }
+
+             msp430_hwmult_type = MSP430_HWMULT_NONE;
+           }
+         else if (target_cpu == NULL)
+           {
+             if (msp430_warn_mcu)
+               warning (0,
+                        "Unrecognized MCU name '%s', assuming that it just "
+                        "supports the MSP430 ISA.\nUse the %<-mcpu%> option "
+                        "to set the ISA explicitly.",
+                        target_mcu);
+
+             msp430x = false;
+           }
+         else if (msp430_warn_mcu)
+           warning (0, "Unrecognized MCU name '%s'.", target_mcu);
        }
     }
 
+  /* The F5 series are all able to support the 430X ISA.  */
+  if (target_cpu == NULL && target_mcu == NULL && msp430_hwmult_type == MSP430_HWMULT_F5SERIES)
+    msp430x = true;
+
   if (TARGET_LARGE && !msp430x)
-    error ("-mlarge requires a 430X-compatible -mmcu=");
+    error ("%<-mlarge%> requires a 430X-compatible %<-mmcu=%>");
 
-  if (msp430_code_region == UPPER && ! msp430x)
-    error ("-mcode-region=upper requires 430X-compatible cpu");
-  if (msp430_data_region == UPPER && ! msp430x)
-    error ("-mdata-region=upper requires 430X-compatible cpu");
+  if (msp430_code_region == MSP430_REGION_UPPER && ! msp430x)
+    error ("%<-mcode-region=upper%> requires 430X-compatible cpu");
+  if (msp430_data_region == MSP430_REGION_UPPER && ! msp430x)
+    error ("%<-mdata-region=upper%> requires 430X-compatible cpu");
 
   if (flag_exceptions || flag_non_call_exceptions
       || flag_unwind_tables || flag_asynchronous_unwind_tables)
@@ -781,7 +898,7 @@ msp430_option_override (void)
 #define TARGET_SCALAR_MODE_SUPPORTED_P msp430_scalar_mode_supported_p
 
 static bool
-msp430_scalar_mode_supported_p (machine_mode m)
+msp430_scalar_mode_supported_p (scalar_mode m)
 {
   if (m == PSImode && msp430x)
     return true;
@@ -809,14 +926,16 @@ msp430_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
 
 /* Register Usage */
 
-/* Implements HARD_REGNO_NREGS.  MSP430X registers can hold a single
-   PSImode value, but not an SImode value.  */
-int
-msp430_hard_regno_nregs (int regno ATTRIBUTE_UNUSED,
-                        machine_mode mode)
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS msp430_hard_regno_nregs
+
+static unsigned int
+msp430_hard_regno_nregs (unsigned int, machine_mode mode)
 {
   if (mode == PSImode && msp430x)
     return 1;
+  if (mode == CPSImode && msp430x)
+    return 2;
   return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
          / UNITS_PER_WORD);
 }
@@ -839,19 +958,25 @@ msp430_hard_regno_nregs_with_padding (int regno ATTRIBUTE_UNUSED,
 {
   if (mode == PSImode)
     return 2;
+  if (mode == CPSImode)
+    return 4;
   return msp430_hard_regno_nregs (regno, mode);
 }
 
-/* Implements HARD_REGNO_MODE_OK.  */
-int
-msp430_hard_regno_mode_ok (int regno ATTRIBUTE_UNUSED,
-                          machine_mode mode)
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK msp430_hard_regno_mode_ok
+
+static bool
+msp430_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
 {
-  return regno <= (ARG_POINTER_REGNUM - msp430_hard_regno_nregs (regno, mode));
+  return regno <= (ARG_POINTER_REGNUM
+                  - (unsigned int) msp430_hard_regno_nregs (regno, mode));
 }
 
-/* Implements MODES_TIEABLE_P.  */
-bool
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P msp430_modes_tieable_p
+
+static bool
 msp430_modes_tieable_p (machine_mode mode1, machine_mode mode2)
 {
   if ((mode1 == PSImode || mode2 == SImode)
@@ -932,7 +1057,7 @@ msp430_initial_elimination_offset (int from, int to)
 #undef  TARGET_ADDR_SPACE_ADDRESS_MODE
 #define TARGET_ADDR_SPACE_ADDRESS_MODE msp430_addr_space_pointer_mode
 
-static machine_mode
+static scalar_int_mode
 msp430_addr_space_pointer_mode (addr_space_t addrspace)
 {
   switch (addrspace)
@@ -952,10 +1077,11 @@ msp430_addr_space_pointer_mode (addr_space_t addrspace)
 #undef  TARGET_UNWIND_WORD_MODE
 #define TARGET_UNWIND_WORD_MODE msp430_unwind_word_mode
 
-static machine_mode
+static scalar_int_mode
 msp430_unwind_word_mode (void)
 {
-  return TARGET_LARGE ? PSImode : HImode;
+  /* This needs to match msp430_init_dwarf_reg_sizes_extra (below).  */
+  return msp430x ? PSImode : HImode;
 }
 
 /* Determine if one named address space is a subset of another.  */
@@ -1302,16 +1428,17 @@ msp430_return_in_memory (const_tree ret_type, const_tree fntype ATTRIBUTE_UNUSED
 #undef  TARGET_GET_RAW_ARG_MODE
 #define TARGET_GET_RAW_ARG_MODE msp430_get_raw_arg_mode
 
-static machine_mode
+static fixed_size_mode
 msp430_get_raw_arg_mode (int regno)
 {
-  return (regno == ARG_POINTER_REGNUM) ? VOIDmode : Pmode;
+  return as_a <fixed_size_mode> (regno == ARG_POINTER_REGNUM
+                                ? VOIDmode : Pmode);
 }
 
 #undef  TARGET_GET_RAW_RESULT_MODE
 #define TARGET_GET_RAW_RESULT_MODE msp430_get_raw_result_mode
 
-static machine_mode
+static fixed_size_mode
 msp430_get_raw_result_mode (int regno ATTRIBUTE_UNUSED)
 {
   return Pmode;
@@ -1379,7 +1506,7 @@ msp430_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   if (boundary < TYPE_ALIGN (type))
     {
       type = build_variant_type_copy (type);
-      TYPE_ALIGN (type) = boundary;
+      SET_TYPE_ALIGN (type, boundary);
     }
 
   /* Compute the rounded size of the type.  */
@@ -1407,6 +1534,9 @@ msp430_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   return addr;
 }
 \f
+#undef TARGET_LRA_P
+#define TARGET_LRA_P hook_bool_void_false
+
 /* Addressing Modes */
 
 #undef  TARGET_LEGITIMATE_ADDRESS_P
@@ -1458,7 +1588,7 @@ msp430_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
     case REG:
       if (!reg_ok_for_addr (x, strict))
        return false;
-      /* else... */
+      /* FALLTHRU */
     case CONST:
     case SYMBOL_REF:
     case CONST_INT:
@@ -1726,6 +1856,15 @@ is_critical_func (tree decl = current_function_decl)
   return has_attr (ATTR_CRIT, decl);
 }
 
+static bool
+has_section_name (const char * name, tree decl = current_function_decl)
+{
+  if (decl == NULL_TREE)
+    return false;
+  return (DECL_SECTION_NAME (decl)
+    && (strcmp (name, DECL_SECTION_NAME (decl)) == 0));
+}
+
 #undef  TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
 #define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS   msp430_allocate_stack_slots_for_args
 
@@ -1736,6 +1875,17 @@ msp430_allocate_stack_slots_for_args (void)
   return ! is_naked_func ();
 }
 
+#undef TARGET_WARN_FUNC_RETURN
+#define TARGET_WARN_FUNC_RETURN msp430_warn_func_return
+
+static bool
+msp430_warn_func_return (tree decl)
+{
+  /* Naked functions are implemented entirely in assembly, including the
+     return sequence, so suppress warnings about this.  */
+  return !is_naked_func (decl);
+}
+
 /* Verify MSP430 specific attributes.  */
 #define TREE_NAME_EQ(NAME, STR) (strcmp (IDENTIFIER_POINTER (NAME), (STR)) == 0)
 
@@ -1748,10 +1898,9 @@ msp430_attr (tree * node,
 {
   gcc_assert (DECL_P (* node));
 
+  /* Only the interrupt attribute takes an argument.  */
   if (args != NULL)
     {
-      gcc_assert (TREE_NAME_EQ (name, ATTR_INTR));
-
       tree value = TREE_VALUE (args);
 
       switch (TREE_CODE (value))
@@ -1763,12 +1912,12 @@ msp430_attr (tree * node,
            /* Allow the attribute to be added - the linker script
               being used may still recognise this name.  */
            warning (OPT_Wattributes,
-                    "unrecognised interrupt vector argument of %qE attribute",
+                    "unrecognized interrupt vector argument of %qE attribute",
                     name);
          break;
 
        case INTEGER_CST:
-         if (wi::gtu_p (value, 63))
+         if (wi::gtu_p (wi::to_wide (value), 63))
            /* Allow the attribute to be added - the linker script
               being used may still recognise this value.  */
            warning (OPT_Wattributes,
@@ -1796,6 +1945,19 @@ msp430_attr (tree * node,
       if (TREE_CODE (TREE_TYPE (* node)) == FUNCTION_TYPE
          && ! VOID_TYPE_P (TREE_TYPE (TREE_TYPE (* node))))
        message = "interrupt handlers must be void";
+      else
+       {
+         /* Ensure interrupt handlers never get optimised out.  */
+         TREE_USED (* node) = 1;
+         DECL_PRESERVE_P (* node) = 1;
+       }
+      if (is_critical_func (* node))
+       {
+         warning (OPT_Wattributes,
+                  "critical attribute has no effect on interrupt functions");
+         DECL_ATTRIBUTES (*node) = remove_attribute (ATTR_CRIT,
+                                                     DECL_ATTRIBUTES (* node));
+       }
     }
   else if (TREE_NAME_EQ (name, ATTR_REENT))
     {
@@ -1810,6 +1972,8 @@ msp430_attr (tree * node,
        message = "naked functions cannot be critical";
       else if (is_reentrant_func (* node))
        message = "reentrant functions cannot be critical";
+      else if (is_interrupt_func ( *node))
+       message = "critical attribute has no effect on interrupt functions";
     }
   else if (TREE_NAME_EQ (name, ATTR_NAKED))
     {
@@ -1888,10 +2052,24 @@ msp430_data_attr (tree * node,
   gcc_assert (args == NULL);
 
   if (TREE_CODE (* node) != VAR_DECL)
-    message = "%qE attribute only applies to variables";
+    message = G_("%qE attribute only applies to variables");
+
+  /* Check that it's possible for the variable to have a section.  */
+  if ((TREE_STATIC (* node) || DECL_EXTERNAL (* node) || in_lto_p)
+      && DECL_SECTION_NAME (* node))
+    message = G_("%qE attribute cannot be applied to variables with specific sections");
 
-  if (DECL_SECTION_NAME (* node))
-    message = "%qE attribute cannot be applied to variables with specific sections";
+  if (!message && TREE_NAME_EQ (name, ATTR_PERSIST) && !TREE_STATIC (* node)
+      && !TREE_PUBLIC (* node) && !DECL_EXTERNAL (* node))
+    message = G_("%qE attribute has no effect on automatic variables");
+
+  /* It's not clear if there is anything that can be set here to prevent the
+     front end placing the variable before the back end can handle it, in a
+     similar way to how DECL_COMMON is used below.
+     So just place the variable in the .persistent section now.  */
+  if ((TREE_STATIC (* node) || DECL_EXTERNAL (* node) || in_lto_p)
+      && TREE_NAME_EQ (name, ATTR_PERSIST))
+    set_decl_section_name (* node, ".persistent");
 
   /* If this var is thought to be common, then change this.  Common variables
      are assigned to sections before the backend has a chance to process them.  */
@@ -1914,30 +2092,35 @@ msp430_data_attr (tree * node,
 /* Table of MSP430-specific attributes.  */
 const struct attribute_spec msp430_attribute_table[] =
 {
-  /* Name        min_num_args     type_req,             affects_type_identity
-                      max_num_args,     fn_type_req
-                          decl_req               handler.  */
-  { ATTR_INTR,        0, 1, true,  false, false, msp430_attr, false },
-  { ATTR_NAKED,       0, 0, true,  false, false, msp430_attr, false },
-  { ATTR_REENT,       0, 0, true,  false, false, msp430_attr, false },
-  { ATTR_CRIT,        0, 0, true,  false, false, msp430_attr, false },
-  { ATTR_WAKEUP,      0, 0, true,  false, false, msp430_attr, false },
-
-  { ATTR_LOWER,       0, 0, true,  false, false, msp430_section_attr, false },
-  { ATTR_UPPER,       0, 0, true,  false, false, msp430_section_attr, false },
-  { ATTR_EITHER,      0, 0, true,  false, false, msp430_section_attr, false },
-
-  { ATTR_NOINIT,      0, 0, true,  false, false, msp430_data_attr, false },
-  { ATTR_PERSIST,     0, 0, true,  false, false, msp430_data_attr, false },
-
-  { NULL,             0, 0, false, false, false, NULL,        false }
+  /* Name        min_num_args     type_req,             handler
+                     max_num_args,     fn_type_req             exclude
+                          decl_req               affects_type_identity.  */
+  { ATTR_INTR,        0, 1, true,  false, false, false, msp430_attr, NULL },
+  { ATTR_NAKED,       0, 0, true,  false, false, false, msp430_attr, NULL },
+  { ATTR_REENT,       0, 0, true,  false, false, false, msp430_attr, NULL },
+  { ATTR_CRIT,        0, 0, true,  false, false, false, msp430_attr, NULL },
+  { ATTR_WAKEUP,      0, 0, true,  false, false, false, msp430_attr, NULL },
+
+  { ATTR_LOWER,       0, 0, true,  false, false, false, msp430_section_attr,
+    NULL },
+  { ATTR_UPPER,       0, 0, true,  false, false, false, msp430_section_attr,
+    NULL },
+  { ATTR_EITHER,      0, 0, true,  false, false, false, msp430_section_attr,
+    NULL },
+
+  { ATTR_NOINIT,      0, 0, true,  false, false, false, msp430_data_attr,
+    NULL },
+  { ATTR_PERSIST,     0, 0, true,  false, false, false, msp430_data_attr,
+    NULL },
+
+  { NULL,             0, 0, false, false, false, false, NULL,  NULL }
 };
 
 #undef  TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE   msp430_start_function
 
 static void
-msp430_start_function (FILE *outfile, HOST_WIDE_INT hwi_local ATTRIBUTE_UNUSED)
+msp430_start_function (FILE *outfile)
 {
   int r, n;
 
@@ -2021,6 +2204,13 @@ msp430_start_function (FILE *file, const char *name, tree decl)
        {
          char buf[101];
 
+         /* Interrupt vector sections should be unique, but use of weak
+            functions implies multiple definitions.  */
+         if (DECL_WEAK (decl))
+           {
+             error ("argument to interrupt attribute is unsupported for weak functions");
+           }
+
          intr_vector = TREE_VALUE (intr_vector);
 
          /* The interrupt attribute has a vector value.  Turn this into a
@@ -2043,6 +2233,7 @@ msp430_start_function (FILE *file, const char *name, tree decl)
     }
 
   switch_to_section (function_section (decl));
+  ASM_OUTPUT_TYPE_DIRECTIVE(file, name, "function");
   ASM_OUTPUT_FUNCTION_LABEL (file, name, decl);
 }
 
@@ -2063,6 +2254,12 @@ gen_prefix (tree decl)
   if (has_attr ("section", decl))
     return NULL;
 
+  /* If the function has been put in the .lowtext section (because it is an
+     interrupt handler, and the large memory model is used), then do not add
+     any prefixes.  */
+  if (has_section_name (".lowtext", decl))
+    return NULL;
+
   /* If the object has __attribute__((lower)) then use the ".lower." prefix.  */
   if (has_attr (ATTR_LOWER, decl))
     return lower_prefix;
@@ -2079,24 +2276,24 @@ gen_prefix (tree decl)
 
   if (TREE_CODE (decl) == FUNCTION_DECL)
     {
-      if (msp430_code_region == LOWER)
+      if (msp430_code_region == MSP430_REGION_LOWER)
        return lower_prefix;
 
-      if (msp430_code_region == UPPER)
+      if (msp430_code_region == MSP430_REGION_UPPER)
        return upper_prefix;
 
-      if (msp430_code_region == EITHER)
+      if (msp430_code_region == MSP430_REGION_EITHER)
        return either_prefix;
     }
   else
     {
-      if (msp430_data_region == LOWER)
+      if (msp430_data_region == MSP430_REGION_LOWER)
        return lower_prefix;
 
-      if (msp430_data_region == UPPER)
+      if (msp430_data_region == MSP430_REGION_UPPER)
        return upper_prefix;
 
-      if (msp430_data_region == EITHER)
+      if (msp430_data_region == MSP430_REGION_EITHER)
        return either_prefix;
     }
 
@@ -2267,7 +2464,7 @@ msp430_output_aligned_decl_common (FILE *                 stream,
                                   unsigned HOST_WIDE_INT size,
                                   unsigned int           align)
 {
-  if (msp430_data_region == ANY)
+  if (msp430_data_region == MSP430_REGION_ANY)
     {
       fprintf (stream, COMMON_ASM_OP);
       assemble_name (stream, name);
@@ -2283,9 +2480,9 @@ msp430_output_aligned_decl_common (FILE *                 stream,
       else
        switch (msp430_data_region)
          {
-         case UPPER: sec = get_named_section (NULL, ".upper.bss", 0); break;
-         case LOWER: sec = get_named_section (NULL, ".lower.bss", 0); break;
-         case EITHER: sec = get_named_section (NULL, ".either.bss", 0); break;
+         case MSP430_REGION_UPPER: sec = get_named_section (NULL, ".upper.bss", 0); break;
+         case MSP430_REGION_LOWER: sec = get_named_section (NULL, ".lower.bss", 0); break;
+         case MSP430_REGION_EITHER: sec = get_named_section (NULL, ".either.bss", 0); break;
          default:
            gcc_unreachable ();
          }
@@ -2309,8 +2506,8 @@ msp430_do_not_relax_short_jumps (void)
      short jumps when there is a chance that the instructions will end up in a low
      section.  */
   return
-    msp430_code_region == EITHER
-    || msp430_code_region == LOWER
+    msp430_code_region == MSP430_REGION_EITHER
+    || msp430_code_region == MSP430_REGION_LOWER
     || has_attr (ATTR_EITHER, current_function_decl)
     || has_attr (ATTR_LOWER, current_function_decl);
 }
@@ -2397,7 +2594,7 @@ msp430_expand_delay_cycles (rtx arg)
     {
       if (c < 0)
        {
-         error ("__delay_cycles only takes non-negative cycle counts.");
+         error ("__delay_cycles only takes non-negative cycle counts");
          return NULL_RTX;
        }
     }
@@ -2417,7 +2614,7 @@ msp430_expand_delay_cycles (rtx arg)
          c %= 4;
          if ((unsigned long long) i > 0xffffffffULL)
            {
-             error ("__delay_cycles is limited to 32-bit loop counts.");
+             error ("__delay_cycles is limited to 32-bit loop counts");
              return NULL_RTX;
            }
        }
@@ -2795,6 +2992,7 @@ msp430_init_dwarf_reg_sizes_extra (tree address)
   rtx addr = expand_normal (address);
   rtx mem = gen_rtx_MEM (BLKmode, addr);
 
+  /* This needs to match msp430_unwind_word_mode (above).  */
   if (!msp430x)
     return;
 
@@ -3089,8 +3287,8 @@ static const struct
   { "__divsi3", "__mspabi_divli" },
   { "__divdi3", "__mspabi_divlli" },
   { "__udivhi3", "__mspabi_divu" },
-  { "__udivsi3", "__mspabi_divlu" },
-  { "__udivdi3", "__mspabi_divllu" },
+  { "__udivsi3", "__mspabi_divul" },
+  { "__udivdi3", "__mspabi_divull" },
   { "__modhi3", "__mspabi_remi" },
   { "__modsi3", "__mspabi_remli" },
   { "__moddi3", "__mspabi_remlli" },
@@ -3116,10 +3314,10 @@ msp430_use_f5_series_hwmult (void)
   static const char * cached_match = NULL;
   static bool         cached_result;
 
-  if (msp430_hwmult_type == F5SERIES)
+  if (msp430_hwmult_type == MSP430_HWMULT_F5SERIES)
     return true;
 
-  if (target_mcu == NULL || msp430_hwmult_type != AUTO)
+  if (target_mcu == NULL || msp430_hwmult_type != MSP430_HWMULT_AUTO)
     return false;
 
   if (target_mcu == cached_match)
@@ -3154,10 +3352,10 @@ use_32bit_hwmult (void)
   static bool         cached_result;
   int i;
 
-  if (msp430_hwmult_type == LARGE)
+  if (msp430_hwmult_type == MSP430_HWMULT_LARGE)
     return true;
 
-  if (target_mcu == NULL || msp430_hwmult_type != AUTO)
+  if (target_mcu == NULL || msp430_hwmult_type != MSP430_HWMULT_AUTO)
     return false;
 
   if (target_mcu == cached_match)
@@ -3183,12 +3381,15 @@ msp430_no_hwmult (void)
   static bool         cached_result;
   int i;
 
-  if (msp430_hwmult_type == NONE)
+  if (msp430_hwmult_type == MSP430_HWMULT_NONE)
     return true;
 
-  if (target_mcu == NULL || msp430_hwmult_type != AUTO)
+  if (msp430_hwmult_type != MSP430_HWMULT_AUTO)
     return false;
 
+  if (target_mcu == NULL)
+    return true;
+
   if (target_mcu == cached_match)
     return cached_result;
 
@@ -3221,7 +3422,7 @@ msp430_output_labelref (FILE *file, const char *name)
 
   /* If we have been given a specific MCU name then we may be
      able to make use of its hardware multiply capabilities.  */
-  if (msp430_hwmult_type != NONE)
+  if (msp430_hwmult_type != MSP430_HWMULT_NONE)
     {
       if (strcmp ("__mspabi_mpyi", name) == 0)
        {
@@ -3241,6 +3442,9 @@ msp430_output_labelref (FILE *file, const char *name)
        }
     }
 
+  if (user_label_prefix[0] != 0)
+    fputs (user_label_prefix, file);
+
   fputs (name, file);
 }
 
@@ -3279,6 +3483,11 @@ msp430_print_operand_raw (FILE * file, rtx op)
     }
 }
 
+#undef  TARGET_ASM_ALIGNED_PSI_OP
+#define TARGET_ASM_ALIGNED_PSI_OP "\t.long\t"
+#undef  TARGET_ASM_UNALIGNED_PSI_OP
+#define TARGET_ASM_UNALIGNED_PSI_OP TARGET_ASM_ALIGNED_PSI_OP
+
 #undef  TARGET_PRINT_OPERAND_ADDRESS
 #define TARGET_PRINT_OPERAND_ADDRESS   msp430_print_operand_addr
 
@@ -3287,7 +3496,7 @@ msp430_print_operand_raw (FILE * file, rtx op)
    is ADDR.  */
 
 static void
-msp430_print_operand_addr (FILE * file, rtx addr)
+msp430_print_operand_addr (FILE * file, machine_mode /*mode*/, rtx addr)
 {
   switch (GET_CODE (addr))
     {
@@ -3403,10 +3612,10 @@ msp430_print_operand (FILE * file, rtx op, int letter)
     case 'b':
       switch (GET_MODE (op))
        {
-       case QImode: fprintf (file, ".B"); return;
-       case HImode: fprintf (file, ".W"); return;
-       case PSImode: fprintf (file, ".A"); return;
-       case SImode: fprintf (file, ".A"); return;
+       case E_QImode: fprintf (file, ".B"); return;
+       case E_HImode: fprintf (file, ".W"); return;
+       case E_PSImode: fprintf (file, ".A"); return;
+       case E_SImode: fprintf (file, ".A"); return;
        default:
          return;
        }
@@ -3531,7 +3740,7 @@ msp430_print_operand (FILE * file, rtx op, int letter)
 
     case MEM:
       addr = XEXP (op, 0);
-      msp430_print_operand_addr (file, addr);
+      msp430_print_operand_addr (file, GET_MODE (op), addr);
       break;
 
     case CONST:
@@ -3664,7 +3873,26 @@ msp430x_logical_shift_right (rtx amount)
      right shift instruction to perform the rest of the shift.  */
   return "rrum.w\t#1, %0 { rpt\t%Z2 { rrax.w\t%0"; /* Six bytes.  */
 }
+
+/* Stop GCC from thinking that it can eliminate (SUBREG:PSI (SI)).  */
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS msp430_can_change_mode_class
+
+static bool
+msp430_can_change_mode_class (machine_mode from, machine_mode to, reg_class_t)
+{
+  if ((to == PSImode && from == SImode)
+      || (to == SImode && from == PSImode)
+      || (to == DImode && from == PSImode)
+      || (to == PSImode && from == DImode))
+    return false;
+  return true;
+}
 \f
+#undef  TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-msp430.h"