ping

On Wed, 11 Sep 2019 11:25:58 +0100
Jozef Lawrynowicz <joze...@mittosystems.com> wrote:

> The MSP430 target has a "430X" extension which increases the directly
> addressable memory range from 64KB (16-bit) to 1MB (20-bit).
> This 1MB memory range is split into a "lower" region (below address 0x10000)
> and "upper" region (at or above address 0x10000).
> When data in the upper region is addressed, 430 instructions cannot be used, 
> as
> their 16-bit capability will be exceeded; 430X instructions must be used
> instead. Most 430X instructions require an additional word of op-code, and 
> also
> require more cycles to execute compared to their 430 equivalent.
> 
> Currently, when the large memory model is specified (-mlarge), 430X 
> instructions
> will always be used when addressing a symbol_ref using the absolute addressing
> mode e.g. MOVX #1, &foo.
> The attached patch modifies code generation so that 430X instructions will 
> only
> be used when the symbol being addressed will not be placed in the lower memory
> region. This is determined by checking if -mdata-region=lower (the new 
> default)
> is passed, or if the "lower" attribute is set on the variable.
> 
> Since code will be generated to assume all variables are in the lower memory
> region with -mdata-region=lower, object files built with this option cannot
> be linked with objects files built with other -mdata-region= values.
> To facilitate the checking of this, a patch for binutils (to be submitted
> after this is accepted) is also attached.
> 
> The compiler will now generate assembler directives indicating the ISA, memory
> model and data region the source file was compiled with. The assembler will
> check these directives match the options it has been invoked with, and then
> add the attribute to the object file.
> 
> Successfully regtested for msp430-elf in the small and large memory model, and
> with -mdata-region=upper. Testing with -mdata-region=upper should expose any
> cases where a 430X instruction is not used where it is required, since all 
> data
> is forced into the upper region so a lack of 430X insn would cause a 
> relocation
> overflow. In fact the attached patch fixes some relocation overflows by adding
> missing "%X" operand selectors to some insns. One relocation overflow remains
> (pr65077.c), but that is a separate binutils issue.
> 
> Ok for trunk?

>From 91371f9a2721e1459429ff7ebdb258b2ef063b04 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <joze...@mittosystems.com>
Date: Wed, 14 Aug 2019 13:25:03 +0100
Subject: [PATCH] MSP430: Only generate 430X instructions with -mlarge if data
 will be in the upper region

gcc/ChangeLog:

2019-09-11  Jozef Lawrynowicz  <joze...@mittosystems.com>

	* config.in: Regenerate.
	* config/msp430/constraints.md: Fix docstring for "Ys" constraint.
	Add new "Yx" constraint.
	* config/msp430/driver-msp430.c (msp430_propagate_region_opt): New spec
	function.
	* config/msp430/msp430-protos.h (msp430_op_not_in_high_mem): New
	prototype.
	* config/msp430/msp430.c (msp430_option_override): Allow the lower
	code/data region to be selected in the small memory model.
	(msp430_section_attr): Don't warn if the "section" and "lower"
	attributes are used together.
	(msp430_handle_generic_attribute): Likewise.
	(msp430_var_in_low_mem): New function.
	(TARGET_ENCODE_SECTION_INFO): Define.
	(msp430_encode_section_info): New function.
	(gen_prefix): Return early in the small memory model.
	Require TARGET_USE_LOWER_REGION_PREFIX to be set before adding the
	".lower" prefix if -m{code,data}-region=lower have been passed.
	(msp430_output_aligned_decl_common): Emit common symbols when
	-mdata-region=lower is passed unless TARGET_USE_LOWER_REGION_PREFIX is
	set. 
	(TARGET_ASM_FILE_END): Define.
	(msp430_file_end): New function.
	(msp430_do_not_relax_short_jumps): Allow relaxation when
	function will be in the lower region.
	(msp430_op_not_in_high_mem): New function.
	(msp430_print_operand): Check "msp430_op_not_in_high_mem" for
	the 'X' operand selector. 
	Clarify comment for 'x' operand selector.
	* config/msp430/msp430.h (LINK_SPEC): Propagate
	-m{code,data}-region to the linker via spec function
	msp430_propagate_region_opt.
	(msp430_propagate_region_opt): New prototype.
	(EXTRA_SPEC_FUNCTIONS): Add msp430_propagate_region_opt.
	(SYMBOL_FLAG_LOW_MEM): Define.
	* config/msp430/msp430.md (addsipsi3): Add missing "%X" operand
	selector.
	(zero_extendqihi2): Fix operand number used by "%X" selector.
	(zero_extendqisi2): Likewise.
	(zero_extendhisi2): Likewise.
	(movqi): Use "Yx" constraint in place of "%X" operand selector.
	(movhi): Likewise.
	(addqi3): Likewise.
	(addhi3): Likewise.
	(addsi3): Likewise.
	(addhi3_cy): Likewise.
	(addchi4_cy): Likewise.
	(subqi3): Likewise.
	(subhi3): Likewise.
	(subsi3): Likewise.
	(bic<mode>3): Likewise.
	(and<mode>3): Likewise.
	(ior<mode>3): Likewise.
	(xor<mode>3): Likewise.
	(slli_1): Add missing "%X" operand selector.
	(slll_1): Likewise.
	(slll_2): Likewise.
	(srai_1): Likewise.
	(sral_1): Likewise.
	(sral_2): Likewise.
	(srli_1): Likewise.
	(srll_1): Likewise.
	(cbranchqi4_real): Use "Yx" constraint in place of "%X" operand
	selector.
	(cbranchhi4_real): Likewise.
	(cbranchqi4_reversed): Likewise.
	(cbranchhi4_reversed): Likewise.
	(*bitbranch<mode>4): Likewise.
	(*bitbranch<mode>4_z): Remove unnecessary "%x" operand selector.
	* config/msp430/msp430.opt (mcode-region=): Set default to
	MSP430_REGION_LOWER. Improve docstring.
	(mdata-region=): Likewise.
	(muse-lower-region-prefix): New option.
	* config/msp430/t-msp430 (MULTILIB_OPTIONS): Add
	mdata-region=none multilib. 
	(MULTILIB_MATCHES): Set mdata-region={upper,either} to match
	mdata-region=none multilib. 
	MULTILIB_EXCEPTIONS: Remove.
	MULTILIB_REQUIRED: Define.
	* configure: Regenerate.
	* configure.ac: Define HAVE_AS_GNU_ATTRIBUTE and
	HAVE_AS_MSPABI_ATTRIBUTE if GAS version >= 2.32.51.
	* doc/extend.texi: Clarify comment for {upper,lower,either}
	function attributes.
	Add separate description for "lower" variable attribute.

gcc/testsuite/ChangeLog:

2019-09-11  Jozef Lawrynowicz  <joze...@mittosystems.com>

	* gcc.target/msp430/430x-insns.c: New test.
	* gcc.target/msp430/data-attributes-2.c: Remove dg-warning
	directives for conflicts between the "section" and "lower" attributes.
	* gcc.target/msp430/msp430.exp
	(check_effective_target_msp430_region_not_lower): New.
	(check_effective_target_msp430_region_lower): New.
	* gcc.target/msp430/object-attributes-430.c: New test.
	* gcc.target/msp430/object-attributes-default.c: New test.
	* gcc.target/msp430/object-attributes-mlarge-any-region.c: New test.
	* gcc.target/msp430/object-attributes-mlarge.c: New test.

---
 gcc/config.in                                 |    6 +
 gcc/config/msp430/constraints.md              |   10 +-
 gcc/config/msp430/driver-msp430.c             |   13 +
 gcc/config/msp430/msp430-protos.h             |    1 +
 gcc/config/msp430/msp430.c                    |  214 ++-
 gcc/config/msp430/msp430.h                    |   13 +-
 gcc/config/msp430/msp430.md                   |  176 +-
 gcc/config/msp430/msp430.opt                  |   12 +-
 gcc/config/msp430/t-msp430                    |   11 +-
 gcc/configure                                 |   74 +
 gcc/configure.ac                              |   14 +
 gcc/doc/extend.texi                           |   25 +-
 gcc/testsuite/gcc.target/msp430/430x-insns.c  | 1646 +++++++++++++++++
 .../gcc.target/msp430/data-attributes-2.c     |    4 +-
 gcc/testsuite/gcc.target/msp430/msp430.exp    |    8 +
 .../gcc.target/msp430/object-attributes-430.c |   14 +
 .../msp430/object-attributes-default.c        |   16 +
 .../object-attributes-mlarge-any-region.c     |   14 +
 .../msp430/object-attributes-mlarge.c         |   15 +
 19 files changed, 2155 insertions(+), 131 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/430x-insns.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/object-attributes-430.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/object-attributes-default.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/object-attributes-mlarge-any-region.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/object-attributes-mlarge.c

diff --git a/gcc/config.in b/gcc/config.in
index 13fd7959dd7..9b54a4715db 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -553,6 +553,12 @@
 #endif
 
 
+/* Define if your assembler supports .mspabi_attribute. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_AS_MSPABI_ATTRIBUTE
+#endif
+
+
 /* Define if the assembler understands -mnan=. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_AS_NAN
diff --git a/gcc/config/msp430/constraints.md b/gcc/config/msp430/constraints.md
index 7ef249d4ac2..4422b2b6454 100644
--- a/gcc/config/msp430/constraints.md
+++ b/gcc/config/msp430/constraints.md
@@ -69,9 +69,11 @@
 
 
 ;; These are memory references that are safe to use without the X suffix,
-;; because we know/assume they need not index across the 64k boundary.
+;; because we know/assume they need not index across the 64K boundary.
+;; Note that for a PSImode memory operand, we always need to use the X suffix,
+;; regardless of what this constraint decides.
 (define_constraint "Ys"
-  "Memory reference, stack only."
+  "Memory reference, indexed or indirect register addressing modes."
   (and (match_code "mem")
        (ior
 	(and (match_code "plus" "0")
@@ -93,3 +95,7 @@
 		  (match_test ("REGNO (XEXP (XEXP (op, 0), 0)) != SP_REGNO")))
 	     ))))
 
+(define_constraint "Yx"
+  "Memory reference, in lower memory below address 0x10000."
+  (and (match_code "mem")
+       (match_test "msp430_op_not_in_high_mem (op)")))
diff --git a/gcc/config/msp430/driver-msp430.c b/gcc/config/msp430/driver-msp430.c
index 0a3d1e14c0a..c37b169ff8b 100644
--- a/gcc/config/msp430/driver-msp430.c
+++ b/gcc/config/msp430/driver-msp430.c
@@ -149,3 +149,16 @@ msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED,
 
   return "-lmul_none";
 }
+
+/* Spec function.  Propagate -m{code,data}-region= to the linker, unless the
+   lower region has been specified without -muse-lower-region-prefix also being
+   used.  */
+const char *
+msp430_propagate_region_opt (int argc, const char **argv)
+{
+  if (strcmp (argv[0], "lower") != 0)
+    return argv[0];
+  else if ((argc == 2) && (strcmp (argv[1], "-muse-lower-region-prefix") == 0))
+    return argv[0]; /* argv[0] == "lower".  */
+  return "none";
+}
diff --git a/gcc/config/msp430/msp430-protos.h b/gcc/config/msp430/msp430-protos.h
index 267b6f59471..1c1757fc7ab 100644
--- a/gcc/config/msp430/msp430-protos.h
+++ b/gcc/config/msp430/msp430-protos.h
@@ -47,5 +47,6 @@ void	msp430_split_movsi (rtx *);
 void    msp430_start_function (FILE *, const char *, tree);
 rtx	msp430_subreg (machine_mode, rtx, machine_mode, int);
 bool    msp430_use_f5_series_hwmult (void);
+bool msp430_op_not_in_high_mem (rtx op);
 
 #endif /* GCC_MSP430_PROTOS_H */
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 521d9baf733..67a5ca60828 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -35,6 +35,7 @@
 #include "tm_p.h"
 #include "regs.h"
 #include "emit-rtl.h"
+#include "varasm.h"
 #include "diagnostic-core.h"
 #include "fold-const.h"
 #include "stor-layout.h"
@@ -263,9 +264,6 @@ msp430_option_override (void)
   else if (!TARGET_LARGE && msp430_code_region == MSP430_REGION_UPPER)
     error ("%<-mcode-region=upper%> requires the large memory model "
 	   "(%<-mlarge%>)");
-  else if (!TARGET_LARGE && msp430_code_region == MSP430_REGION_LOWER)
-    error ("%<-mcode-region=lower%> requires the large memory model "
-	   "(%<-mlarge%>)");
 
   if (!TARGET_LARGE && msp430_data_region == MSP430_REGION_EITHER)
     error ("%<-mdata-region=either%> requires the large memory model "
@@ -273,10 +271,6 @@ msp430_option_override (void)
   else if (!TARGET_LARGE && msp430_data_region == MSP430_REGION_UPPER)
     error ("%<-mdata-region=upper%> requires the large memory model "
 	   "(%<-mlarge%>)");
-  else if (!TARGET_LARGE && msp430_data_region == MSP430_REGION_LOWER)
-    error ("%<-mdata-region=lower%> requires the large memory model "
-	   "(%<-mlarge%>)");
-
 
   if (flag_exceptions || flag_non_call_exceptions
       || flag_unwind_tables || flag_asynchronous_unwind_tables)
@@ -1386,7 +1380,7 @@ msp430_section_attr (tree * node,
   if (has_attr (ATTR_NOINIT, *node))
     message = G_("ignoring attribute %qE because it conflicts with "
 		 "attribute %<noinit%>");
-  else if (has_attr ("section", *node))
+  else if (has_attr ("section", *node) && !TREE_NAME_EQ (name, "lower"))
     message = G_("ignoring attribute %qE because it conflicts with "
 		 "attribute %<section%>");
   /* It does not make sense to use upper/lower/either attributes without
@@ -1564,12 +1558,14 @@ msp430_handle_generic_attribute (tree *node,
 {
   const char *message = NULL;
 
+  /* The front end has set up an exclusion between the "noinit" and "section"
+     attributes.  */
   if (!(TREE_NAME_EQ (name, ATTR_NOINIT) || TREE_NAME_EQ (name, "section")))
     return NULL_TREE;
 
-  /* The front end has set up an exclusion between the "noinit" and "section"
-     attributes.  */
-  if (has_attr (ATTR_LOWER, *node))
+  /* We allow the "lower" attribute to be used on variables with the "section"
+     attribute.  */
+  if (has_attr (ATTR_LOWER, *node) && !TREE_NAME_EQ (name, "section"))
     message = G_("ignoring attribute %qE because it conflicts with "
 		 "attribute %<lower%>");
   else if (has_attr (ATTR_UPPER, *node))
@@ -1591,6 +1587,55 @@ msp430_handle_generic_attribute (tree *node,
   return NULL_TREE;
 }
 
+/* Given a non-automatic VAR_DECL which can possibly have a section, return
+   true if the variable will definitely be placed in the lower memory
+   region (below address 0x10000).  */
+static bool
+msp430_var_in_low_mem (tree decl)
+{
+  gcc_assert (VAR_P (decl));
+
+  /* "noinit" variables are always placed in the lower memory region.  */
+  if (has_attr (ATTR_UPPER, decl)
+      || has_attr (ATTR_EITHER, decl)
+      || has_attr (ATTR_PERSIST, decl)
+      /* Unless the variable is marked with the lower or noinit attribute, we
+	 cannot assume that it is in the lower region if it is marked with the
+	 section attribute or -mdata-region={upper,either,none} have been
+	 passed.
+	 The noinit and section attributes conflict.  */
+      || (!has_attr (ATTR_LOWER, decl) && !has_attr (ATTR_NOINIT, decl)
+	  && (has_attr ("section", decl)
+	      || msp430_data_region == MSP430_REGION_UPPER
+	      || msp430_data_region == MSP430_REGION_EITHER
+	      || msp430_data_region == MSP430_REGION_ANY)))
+    return false;
+  return true;
+}
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO msp430_encode_section_info
+
+/* Encode whether a SYMBOL_REF is definitely in the lower memory region.  */
+static void
+msp430_encode_section_info (tree decl, rtx rtl, int first)
+{
+  rtx symbol;
+  default_encode_section_info (decl, rtl, first);
+
+  /* Careful not to prod global register variables.  */
+  if (!MEM_P (rtl))
+    return;
+  symbol = XEXP (rtl, 0);
+  if (GET_CODE (symbol) != SYMBOL_REF)
+    return;
+
+  if (VAR_P (decl)
+      && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+      && msp430_var_in_low_mem (decl))
+    SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOW_MEM;
+}
+
 #undef  TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE	msp430_start_function
 
@@ -1744,15 +1789,17 @@ gen_prefix (tree decl)
   if (has_section_name (".lowtext", decl))
     return NULL;
 
-  /* If the object has __attribute__((lower)) then use the ".lower." prefix.  */
+  /* Memory regions require the large memory model.  */
+  if (!TARGET_LARGE)
+    return NULL;
+
+  /* Note that we always apply the lower prefix when the attribute has been
+     used.  But we only apply the lower prefix when the lower region has been
+     specified by a command line option if -muse-lower-region-prefix has also
+     been passed.  */
   if (has_attr (ATTR_LOWER, decl))
     return lower_prefix;
 
-  /* If we are compiling for the MSP430 then we do not support the upper
-     region.  */
-  if (! msp430x)
-    return NULL;
-
   if (has_attr (ATTR_UPPER, decl))
     return upper_prefix;
 
@@ -1761,7 +1808,8 @@ gen_prefix (tree decl)
 
   if (TREE_CODE (decl) == FUNCTION_DECL)
     {
-      if (msp430_code_region == MSP430_REGION_LOWER)
+      if ((msp430_code_region == MSP430_REGION_LOWER)
+	  && TARGET_USE_LOWER_REGION_PREFIX)
 	return lower_prefix;
 
       if (msp430_code_region == MSP430_REGION_UPPER)
@@ -1772,7 +1820,8 @@ gen_prefix (tree decl)
     }
   else
     {
-      if (msp430_data_region == MSP430_REGION_LOWER)
+      if ((msp430_data_region == MSP430_REGION_LOWER)
+	  && TARGET_USE_LOWER_REGION_PREFIX)
 	return lower_prefix;
 
       if (msp430_data_region == MSP430_REGION_UPPER)
@@ -1966,7 +2015,6 @@ msp430_unique_section (tree decl, int reloc)
 /* Emit a declaration of a common symbol.
    If a data region is in use then put the symbol into the
    equivalent .bss section instead.  */
-
 void
 msp430_output_aligned_decl_common (FILE *		  stream,
 				   const tree		  decl,
@@ -1976,7 +2024,9 @@ msp430_output_aligned_decl_common (FILE *		  stream,
 {
   /* Only emit a common symbol if the variable does not have a specific section
      assigned.  */
-  if (msp430_data_region == MSP430_REGION_ANY
+  if ((msp430_data_region == MSP430_REGION_ANY
+       || ((msp430_data_region == MSP430_REGION_LOWER)
+	   && !TARGET_USE_LOWER_REGION_PREFIX))
       && !(decl != NULL_TREE && DECL_SECTION_NAME (decl))
       && !has_attr (ATTR_EITHER, decl)
       && !has_attr (ATTR_LOWER, decl)
@@ -2021,6 +2071,78 @@ msp430_output_aligned_decl_common (FILE *		  stream,
     }
 }
 
+#undef TARGET_ASM_FILE_END
+#define TARGET_ASM_FILE_END msp430_file_end
+
+/* Emit MSPABI and GNU object attributes.
+   Tags and values for MSPABI attributes are:
+   OFBA_MSPABI_Tag_ISA		4
+     MSP430	1
+     MSP430X	2
+   OFBA_MSPABI_Tag_Code_Model	6
+     Small 	1
+     Large	2
+   OFBA_MSPABI_Tag_Data_Model	8
+     Small 	1
+     Large	2
+     Restricted	3 (Unused by GNU)
+   OFBA_MSPABI_Tag_enum_size	10 (Unused by GNU)
+   Note that Code_Model and Data_Model are always equal for GNU.
+   We define a new .gnu_attribute to keep track of the data region used.
+   Tag_GNU_MSP430_Data_Region	4
+     LOWER	1
+     ANY	2
+   See binutils-gdb/include/elf/msp430.h for the full details.  */
+static void
+msp430_file_end (void)
+{
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+  /* Enum for tag names.  */
+  enum
+    {
+      OFBA_MSPABI_Tag_ISA = 4,
+      OFBA_MSPABI_Tag_Code_Model = 6,
+      OFBA_MSPABI_Tag_Data_Model = 8,
+      Tag_GNU_MSP430_Data_Region = 4
+    };
+  /* Enum for tag values.  */
+  enum
+    {
+      OFBA_MSPABI_Val_ISA_MSP430 = 1,
+      OFBA_MSPABI_Val_ISA_MSP430X = 2,
+      OFBA_MSPABI_Val_Model_Small = 1,
+      OFBA_MSPABI_Val_Model_Large = 2,
+      Tag_GNU_MSP430_Data_Region_Lower = 1,
+      Tag_GNU_MSP430_Data_Region_Any = 2
+    };
+  /* .mspabi_attribute is a GNU assembler directive only.  The assembler will
+     construct a .MSP430.attributes section based on the options it is invoked
+     with.  The values it reads from these directives are used for validating
+     those options.  */
+  const char *msp_attr = ".mspabi_attribute";
+  const char *gnu_attr = ".gnu_attribute";
+
+  /* Emit .mspabi_attribute directive for OFBA_MSPABI_Tag_ISA.  */
+  fprintf (asm_out_file, "\t%s %d, %d\n", msp_attr, OFBA_MSPABI_Tag_ISA,
+	   msp430x ? OFBA_MSPABI_Val_ISA_MSP430X : OFBA_MSPABI_Val_ISA_MSP430);
+  /* Emit .mspabi_attribute directive for OFBA_MSPABI_Tag_Code_Model.  */
+  fprintf (asm_out_file, "\t%s %d, %d\n", msp_attr, OFBA_MSPABI_Tag_Code_Model,
+	   TARGET_LARGE ? OFBA_MSPABI_Val_Model_Large
+	   : OFBA_MSPABI_Val_Model_Small);
+  /* Emit .mspabi_attribute directive for OFBA_MSPABI_Tag_Data_Model.  */
+  fprintf (asm_out_file, "\t%s %d, %d\n", msp_attr, OFBA_MSPABI_Tag_Data_Model,
+	   TARGET_LARGE ? OFBA_MSPABI_Val_Model_Large
+	   : OFBA_MSPABI_Val_Model_Small);
+#ifdef HAVE_AS_MSPABI_ATTRIBUTE
+  /* Emit .gnu_attribute directive for Tag_GNU_MSP430_Data_Region.  */
+  fprintf (asm_out_file, "\t%s %d, %d\n", gnu_attr, Tag_GNU_MSP430_Data_Region,
+	   msp430_data_region == MSP430_REGION_LOWER
+	   ? Tag_GNU_MSP430_Data_Region_Lower
+	   : Tag_GNU_MSP430_Data_Region_Any);
+#endif
+#endif
+}
+
 bool
 msp430_do_not_relax_short_jumps (void)
 {
@@ -2031,9 +2153,7 @@ msp430_do_not_relax_short_jumps (void)
      end up in a low section.  */
   return
     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);
+    || has_attr (ATTR_EITHER, current_function_decl);
 }
 
 enum msp430_builtin
@@ -3074,6 +3194,36 @@ msp430_print_operand_addr (FILE * file, machine_mode /*mode*/, rtx addr)
   msp430_print_operand_raw (file, addr);
 }
 
+/* Determine whether an RTX is definitely not a MEM referencing an address in
+   the upper memory region.  Returns true if we've decided the address will be
+   in the lower memory region, or the RTX is not a MEM.  Returns false
+   otherwise.  */
+bool
+msp430_op_not_in_high_mem (rtx op)
+{
+  rtx op0;
+
+  if (!TARGET_LARGE || !MEM_P (op))
+    return true;
+
+  op0 = XEXP (op, 0);
+
+  if (SYMBOL_REF_P (op0) && (SYMBOL_REF_FLAGS (op0) & SYMBOL_FLAG_LOW_MEM))
+    /* msp430_encode_section_info decided this mem will be in lower
+       memory.  */
+    return true;
+
+  /* Catch (mem (const (plus ((symbol_ref) (const_int))))) e.g. &addr+2.  */
+  if ((GET_CODE (op0) == CONST)
+      && (GET_CODE (XEXP (op0, 0)) == PLUS)
+      && (SYMBOL_REF_P (XEXP (XEXP (op0, 0), 0)))
+      && (SYMBOL_REF_FLAGS (XEXP (XEXP (op0, 0), 0)) & SYMBOL_FLAG_LOW_MEM))
+    return true;
+
+  /* Return false when undecided.  */
+  return false;
+}
+
 #undef  TARGET_PRINT_OPERAND
 #define TARGET_PRINT_OPERAND		msp430_print_operand
 
@@ -3245,15 +3395,21 @@ msp430_print_operand (FILE * file, rtx op, int letter)
 
     case 'X':
       /* This is used to turn, for example, an ADD opcode into an ADDX
-	 opcode when we're using 20-bit addresses.  */
-      if (TARGET_LARGE || GET_MODE (op) == PSImode)
+	 opcode when we're using 20-bit addresses.
+	 This can be used for insns which have only one operand which might be
+	 a mem.
+	 If an insn has two different operands which could be memory operands,
+	 then the "Yx" constraint must be used to determine if the X suffix is
+	 required by checking both operands.  */
+      if (GET_MODE (op) == PSImode
+	  || !msp430_op_not_in_high_mem (op))
 	fprintf (file, "X");
-      /* We don't care which operand we use, but we want 'X' in the MD
-	 file, so we do it this way.  */
       return;
 
     case 'x':
-      /* Similarly, but only for PSImodes.  BIC, for example, needs this.  */
+      /* Similarly, but only for PSImodes.  BIC, and other insn patterns using
+	 the QHI mode iterator (which includes, QI, HI, and PSImode) use
+	 this.  */
       if (GET_MODE (op) == PSImode)
 	fprintf (file, "X");
       return;
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index 36b715d0d0f..3696afb8279 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -71,7 +71,10 @@ extern bool msp430x;
    is enabled  (the GDB testsuite relies upon unused entities not being
    deleted).  */
 #define LINK_SPEC "%{mrelax:--relax} %{mlarge:%{!r:%{!g:--gc-sections}}} " \
-  "%{mcode-region=*:--code-region=%*} %{mdata-region=*:--data-region=%*}"
+  "%{mcode-region=*:--code-region=%:" \
+    "msp430_propagate_region_opt(%* %{muse-lower-region-prefix})} " \
+  "%{mdata-region=*:--data-region=%:" \
+    "msp430_propagate_region_opt(%* %{muse-lower-region-prefix})} " \
 
 #define DRIVER_SELF_SPECS \
   " %{!mlarge:%{mcode-region=*:%{mdata-region=*:%e-mcode-region and "	\
@@ -90,12 +93,16 @@ extern const char * msp430_select_hwmult_lib (int, const char **);
 extern const char * msp430_select_cpu (int, const char **);
 extern const char * msp430_set_driver_var (int, const char **);
 extern const char * msp430_check_path_for_devices (int, const char **);
+extern const char *msp430_propagate_region_opt (int, const char **);
 
+/* There must be a trailing comma after the last item, see gcc.c
+   "static_spec_functions".  */
 # define EXTRA_SPEC_FUNCTIONS				\
   { "msp430_hwmult_lib", msp430_select_hwmult_lib },	\
   { "msp430_select_cpu", msp430_select_cpu },		\
   { "msp430_set_driver_var", msp430_set_driver_var },		\
-  { "msp430_check_path_for_devices", msp430_check_path_for_devices },
+  { "msp430_check_path_for_devices", msp430_check_path_for_devices }, \
+  { "msp430_propagate_region_opt", msp430_propagate_region_opt },
 
 /* Specify the libraries to include on the linker command line.
 
@@ -482,3 +489,5 @@ typedef struct
 
 #define ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN)	\
   msp430_output_aligned_decl_common ((FILE), (DECL), (NAME), (SIZE), (ALIGN))
+
+#define SYMBOL_FLAG_LOW_MEM (SYMBOL_FLAG_MACH_DEP << 0)
diff --git a/gcc/config/msp430/msp430.md b/gcc/config/msp430/msp430.md
index f6d688950cb..c72f7aade30 100644
--- a/gcc/config/msp430/msp430.md
+++ b/gcc/config/msp430/msp430.md
@@ -190,22 +190,22 @@
 )
 
 (define_insn "movqi"
-  [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=rYs,rm")
-	(match_operand:QI 1 "msp_general_operand" "riYs,rmi"))]
+  [(set (match_operand:QI 0 "msp_nonimmediate_operand" "=rYsYx,rm")
+	(match_operand:QI 1 "msp_general_operand" "riYsYx,rmi"))]
   ""
   "@
   MOV.B\t%1, %0
-  MOV%X0.B\t%1, %0"
+  MOVX.B\t%1, %0"
 )
 
 (define_insn "movhi"
-  [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=r,rYs,rm")
-	(match_operand:HI 1 "msp_general_operand" "N,riYs,rmi"))]
+  [(set (match_operand:HI 0 "msp_nonimmediate_operand" "=r,rYsYx,rm")
+	(match_operand:HI 1 "msp_general_operand" "N,riYsYx,rmi"))]
   ""
   "@
   MOV.B\t%1, %0
   MOV.W\t%1, %0
-  MOV%X0.W\t%1, %0"
+  MOVX.W\t%1, %0"
 )
 
 (define_expand "movsi"
@@ -241,7 +241,7 @@
   "msp430_split_movsi (operands);"
 )
 
-;; Some MOVX.A cases can be done with MOVA, this is only a few of them.
+;; FIXME: Some MOVX.A cases can be done with MOVA, this is only a few of them.
 (define_insn "movpsi"
   [(set (match_operand:PSI 0 "msp_nonimmediate_operand" "=r,r,r,Ya,rm")
 	(match_operand:PSI 1 "msp_general_operand" "N,O,riYa,r,rmi"))]
@@ -289,23 +289,23 @@
 )
 
 (define_insn "addqi3"
-  [(set (match_operand:QI          0 "msp_nonimmediate_operand" "=rYs,rm")
+  [(set (match_operand:QI          0 "msp_nonimmediate_operand" "=rYsYx,rm")
 	(plus:QI (match_operand:QI 1 "msp_nonimmediate_operand" "%0,0")
-		 (match_operand:QI 2 "msp_general_operand"      "riYs,rmi")))]
+		 (match_operand:QI 2 "msp_general_operand"      "riYsYx,rmi")))]
   ""
   "@
    ADD.B\t%2, %0
-   ADD%X0.B\t%2, %0"
+   ADDX.B\t%2, %0"
 )
 
 (define_insn "addhi3"
-  [(set (match_operand:HI           0 "msp_nonimmediate_operand" "=rYs,rm")
+  [(set (match_operand:HI           0 "msp_nonimmediate_operand" "=rYsYx,rm")
 	(plus:HI (match_operand:HI  1 "msp_nonimmediate_operand" "%0,0")
-		  (match_operand:HI 2 "msp_general_operand"      "riYs,rmi")))]
+		  (match_operand:HI 2 "msp_general_operand"      "riYsYx,rmi")))]
   ""
   "@
    ADD.W\t%2, %0
-   ADD%X0.W\t%2, %0"
+   ADDX.W\t%2, %0"
 )
 
 ; This pattern is needed in order to avoid reload problems.
@@ -317,17 +317,17 @@
 	(plus:SI (match_operand:SI    1 "register_operand" "0")
 		 (match_operand       2 "general_operand" "rmi")))]
   ""
-  "ADD.W\t%L2, %L0 { ADDC.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %0"
+  "ADD%X2.W\t%L2, %L0 { ADDC%X2.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %0"
 )
 
 (define_insn "addsi3"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,rm")
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=&rYsYx,rm")
 	(plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
-		 (match_operand:SI 2 "general_operand" "r,mi")))]
+		 (match_operand:SI 2 "general_operand" "rYsYxi,mi")))]
   ""
   "@
    ADD\t%L2, %L0 { ADDC\t%H2, %H0
-   ADD%X0\t%L2, %L0 { ADDC%X0\t%H2, %H0"
+   ADDX\t%L2, %L0 { ADDCX\t%H2, %H0"
 )
 
 ; Version of addhi that exposes the carry operations, for SImode adds.
@@ -358,9 +358,9 @@
 ; that are not single_set() very well.
 
 (define_insn "addhi3_cy"
-  [(set (match_operand:HI          0 "msp_nonimmediate_operand" "=r,rm")
+  [(set (match_operand:HI          0 "msp_nonimmediate_operand" "=rYsYx,rm")
 	(plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0")
-		 (match_operand:HI 2 "msp_nonimmediate_operand" "r,rm")))
+		 (match_operand:HI 2 "msp_nonimmediate_operand" "rYsYxi,rm")))
    (set (reg:BI CARRY)
 	(truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1))
 					   (zero_extend:SI (match_dup 2)))
@@ -369,7 +369,7 @@
   ""
   "@
    ADD\t%2, %1 ; cy
-   ADD%X0\t%2, %1 ; cy"
+   ADDX\t%2, %1 ; cy"
   )
 
 (define_insn "addhi3_cy_i"
@@ -389,15 +389,15 @@
 
 ; Version of addhi that adds the carry, for SImode adds.
 (define_insn "addchi4_cy"
-  [(set (match_operand:HI                   0 "msp_nonimmediate_operand" "=r,rm")
+  [(set (match_operand:HI                   0 "msp_nonimmediate_operand" "=rYsYx,rm")
 	(plus:HI (plus:HI (match_operand:HI 1 "msp_nonimmediate_operand" "%0,0")
-			  (match_operand:HI 2 "msp_general_operand"      "ri,rmi"))
+			  (match_operand:HI 2 "msp_general_operand"      "riYsYx,rmi"))
 		 (zero_extend:HI (reg:BI CARRY))))
    ]
   ""
   "@
    ADDC\t%2, %1
-   ADDC%X0\t%2, %1"
+   ADDCX\t%2, %1"
   )
 
 ; Split an SImode add into two HImode adds, keeping track of the carry
@@ -458,36 +458,38 @@
 
 ;; Alternatives 2 and 3 are to handle cases generated by reload.
 (define_insn "subqi3"
-  [(set (match_operand:QI           0 "nonimmediate_operand" "=rYs,  rm,  &?r, ?&r")
+  [(set (match_operand:QI           0 "nonimmediate_operand" "=rYsYx,  rm,  &?r, ?&r")
 	(minus:QI (match_operand:QI 1 "general_operand"       "0,    0,    !r,  !i")
-		  (match_operand:QI 2 "general_operand"      " riYs, rmi, rmi,   r")))]
+		  (match_operand:QI 2 "general_operand"      " riYsYx, rmi, rmi,   r")))]
   ""
   "@
   SUB.B\t%2, %0
-  SUB%X0.B\t%2, %0
-  MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0
+  SUBX.B\t%2, %0
+  MOV%X2.B\t%1, %0 { SUB%X2.B\t%2, %0
   MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0"
 )
 
 ;; Alternatives 2 and 3 are to handle cases generated by reload.
 (define_insn "subhi3"
-  [(set (match_operand:HI           0 "nonimmediate_operand" "=rYs,  rm,  &?r, ?&r")
+  [(set (match_operand:HI           0 "nonimmediate_operand" "=rYsYx,  rm,  &?r, ?&r")
 	(minus:HI (match_operand:HI 1 "general_operand"       "0,    0,    !r,  !i")
-		  (match_operand:HI 2 "general_operand"      " riYs, rmi, rmi,   r")))]
+		  (match_operand:HI 2 "general_operand"      " riYsYx, rmi, rmi,   r")))]
   ""
   "@
   SUB.W\t%2, %0
-  SUB%X0.W\t%2, %0
-  MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0
+  SUBX.W\t%2, %0
+  MOV%X2.W\t%1, %0 { SUB%X2.W\t%2, %0
   MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0"
 )
 
 (define_insn "subsi3"
-  [(set (match_operand:SI           0 "nonimmediate_operand" "=&rm")
-	(minus:SI (match_operand:SI 1 "nonimmediate_operand"   "0")
-		  (match_operand:SI 2 "general_operand"        "rmi")))]
+  [(set (match_operand:SI           0 "nonimmediate_operand" "=&rYsYx,m")
+	(minus:SI (match_operand:SI 1 "nonimmediate_operand"   "0,0")
+		  (match_operand:SI 2 "general_operand"        "riYsYx,mi")))]
   ""
-  "SUB%X0\t%L2, %L0 { SUBC%X0\t%H2, %H0"
+  "@
+  SUB\t%L2, %L0 { SUBC\t%H2, %H0
+  SUBX\t%L2, %L0 { SUBCX\t%H2, %H0"
 )
 
 (define_insn "*bic<mode>_cg"
@@ -501,44 +503,44 @@
 )
 
 (define_insn "bic<mode>3"
-  [(set (match_operand:QHI                   0 "msp_nonimmediate_operand" "=rYs,rm")
-	(and:QHI (not:QHI (match_operand:QHI 1 "msp_general_operand"       "rYs,rmn"))
+  [(set (match_operand:QHI                   0 "msp_nonimmediate_operand" "=rYsYx,rm")
+	(and:QHI (not:QHI (match_operand:QHI 1 "msp_general_operand"       "rYsYx,rmn"))
 		 (match_operand:QHI          2 "msp_nonimmediate_operand"  "0,0")))]
   ""
   "@
    BIC%x0%b0\t%1, %0
-   BIC%X0%b0\t%1, %0"
+   BICX%b0\t%1, %0"
 )
 
 (define_insn "and<mode>3"
-  [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=r,rYs,rm")
+  [(set (match_operand:QHI 0 "msp_nonimmediate_operand" "=r,rYsYx,rm")
 	(and:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0,0")
-		 (match_operand:QHI 2 "msp_general_operand" "N,riYs,rmi")))]
+		 (match_operand:QHI 2 "msp_general_operand" "N,riYsYx,rmi")))]
   ""
   "@
    AND%x0.B\t%2, %0
    AND%x0%b0\t%2, %0
-   AND%X0%b0\t%2, %0"
+   ANDX%b0\t%2, %0"
 )
 
 (define_insn "ior<mode>3"
-  [(set (match_operand:QHI          0 "msp_nonimmediate_operand" "=rYs,rm")
+  [(set (match_operand:QHI          0 "msp_nonimmediate_operand" "=rYsYx,rm")
 	(ior:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0")
-		 (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))]
+		 (match_operand:QHI 2 "msp_general_operand" "riYsYx,rmi")))]
   ""
   "@
    BIS%x0%b0\t%2, %0
-   BIS%X0%b0\t%2, %0"
+   BISX%b0\t%2, %0"
 )
 
 (define_insn "xor<mode>3"
-  [(set (match_operand:QHI          0 "msp_nonimmediate_operand" "=rYs,rm")
+  [(set (match_operand:QHI          0 "msp_nonimmediate_operand" "=rYsYx,rm")
 	(xor:QHI (match_operand:QHI 1 "msp_nonimmediate_operand" "%0,0")
-		 (match_operand:QHI 2 "msp_general_operand" "riYs,rmi")))]
+		 (match_operand:QHI 2 "msp_general_operand" "riYsYx,rmi")))]
   ""
   "@
    XOR%x0%b0\t%2, %0
-   XOR%X0%b0\t%2, %0"
+   XORX%b0\t%2, %0"
 )
 
 ;; Macro : XOR #~0, %0
@@ -567,7 +569,7 @@
   "@
    AND\t#0xff, %0
    MOV.B\t%1, %0
-   MOV%X0.B\t%1, %0
+   MOV%X1.B\t%1, %0
    AND%X0\t#0xff, %0"
 )
 
@@ -621,7 +623,7 @@
   [(set (match_operand:SI 0 "nonimmediate_operand" "=r")
 	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "rm")))]
   ""
-  "MOV.B\t%1,%L0 { CLR\t%H0"
+  "MOV%X1.B\t%1,%L0 { CLR\t%H0"
 )
 
 (define_insn "zero_extendhisi2"
@@ -629,7 +631,7 @@
 	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,r")))]
   ""
   "@
-  MOV.W\t#0,%H0
+  MOV%X0.W\t#0,%H0
   MOV.W\t%1,%L0 { MOV.W\t#0,%H0"
 )
 
@@ -782,7 +784,7 @@
 	(ashift:HI (match_operand:HI 1 "general_operand"       "0")
 		   (const_int 1)))]
   ""
-  "RLA.W\t%0" ;; Note - this is a macro for ADD
+  "RLA%X0.W\t%0" ;; Note - this is a macro for ADD
 )
 
 (define_insn "430x_shift_left"
@@ -802,7 +804,7 @@
 	(ashift:SI (match_operand:SI 1 "general_operand"       "0")
 		   (const_int 1)))]
   ""
-  "RLA.W\t%L0 { RLC.W\t%H0"
+  "RLA%X0.W\t%L0 { RLC%X0.W\t%H0"
 )
 
 (define_insn "slll_2"
@@ -810,7 +812,7 @@
 	(ashift:SI (match_operand:SI 1 "general_operand"       "0")
 		   (const_int 2)))]
   ""
-  "RLA.W\t%L0 { RLC.W\t%H0 { RLA.W\t%L0 { RLC.W\t%H0"
+  "RLA%X0.W\t%L0 { RLC%X0.W\t%H0 { RLA%X0.W\t%L0 { RLC%X0.W\t%H0"
 )
 
 (define_expand "ashlsi3"
@@ -867,7 +869,7 @@
 	(ashiftrt:HI (match_operand:HI 1 "msp_general_operand"      "0")
 		     (const_int 1)))]
   ""
-  "RRA.W\t%0"
+  "RRA%X0.W\t%0"
 )
 
 (define_insn "430x_arithmetic_shift_right"
@@ -903,7 +905,7 @@
 	(ashiftrt:SI (match_operand:SI 1 "general_operand"       "0")
 		     (const_int 1)))]
   ""
-  "RRA.W\t%H0 { RRC.W\t%L0"
+  "RRA%X0.W\t%H0 { RRC%X0.W\t%L0"
 )
 
 (define_insn "sral_2"
@@ -911,7 +913,7 @@
 	(ashiftrt:SI (match_operand:SI 1 "general_operand"       "0")
 		     (const_int 2)))]
   ""
-  "RRA.W\t%H0 { RRC.W\t%L0 { RRA.W\t%H0 { RRC.W\t%L0"
+  "RRA%X0.W\t%H0 { RRC%X0.W\t%L0 { RRA%X0.W\t%H0 { RRC%X0.W\t%L0"
 )
 
 (define_expand "ashrsi3"
@@ -968,7 +970,7 @@
 	(lshiftrt:HI (match_operand:HI 1 "general_operand"       "0")
 		     (const_int 1)))]
   ""
-  "CLRC { RRC.W\t%0"
+  "CLRC { RRC%X0.W\t%0"
 )
 
 (define_insn "430x_logical_shift_right"
@@ -994,7 +996,7 @@
 	(lshiftrt:SI (match_operand:SI 1 "general_operand"       "0")
 		     (const_int 1)))]
   ""
-  "CLRC { RRC.W\t%H0 { RRC.W\t%L0"
+  "CLRC { RRC%X0.W\t%H0 { RRC%X0.W\t%L0"
 )
 
 (define_insn "srll_2x"
@@ -1188,8 +1190,8 @@
 (define_insn "cbranchqi4_real"
   [(set (pc) (if_then_else
 	      (match_operator                    0 "msp430_cmp_operator"
-			      [(match_operand:QI 1 "nonimmediate_operand" "rYs,rm")
-			       (match_operand:QI 2 "general_operand"      "rYsi,rmi")])
+			      [(match_operand:QI 1 "nonimmediate_operand" "rYsYx,rm")
+			       (match_operand:QI 2 "general_operand"      "rYsYxi,rmi")])
               (label_ref (match_operand          3 "" ""))
 	      (pc)))
    (clobber (reg:BI CARRY))
@@ -1197,14 +1199,14 @@
   ""
   "@
    CMP.B\t%2, %1 { J%0\t%l3
-   CMP%X0.B\t%2, %1 { J%0\t%l3"
+   CMPX.B\t%2, %1 { J%0\t%l3"
   )
 
 (define_insn "cbranchhi4_real"
   [(set (pc) (if_then_else
 	      (match_operator                    0 "msp430_cmp_operator"
-			      [(match_operand:HI 1 "nonimmediate_operand" "rYs,rm")
-			       (match_operand:HI 2 "general_operand"      "rYsi,rmi")])
+			      [(match_operand:HI 1 "nonimmediate_operand" "rYsYx,rm")
+			       (match_operand:HI 2 "general_operand"      "rYsYxi,rmi")])
               (label_ref (match_operand          3 "" ""))
 	      (pc)))
    (clobber (reg:BI CARRY))
@@ -1222,12 +1224,12 @@
       {
         return which_alternative == 0 ?
             \"CMP.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\" :
-         \"CMP%X0.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\";
+	    \"CMPX.W\t%2, %1 { J%r0 1f { BRA #%l3 { 1:\";
       }
 
     return which_alternative == 0 ?
          \"CMP.W\t%2, %1 { J%0\t%l3\" :
-      \"CMP%X0.W\t%2, %1 { J%0\t%l3\";
+	 \"CMPX.W\t%2, %1 { J%0\t%l3\";
   "
   [(set (attr "length")
 	(if_then_else
@@ -1257,8 +1259,8 @@
 (define_insn "cbranchqi4_reversed"
   [(set (pc) (if_then_else
 	      (match_operator                    0 "msp430_reversible_cmp_operator"
-			      [(match_operand:QI 1 "general_operand" "rYsi,rmi")
-			       (match_operand:QI 2 "general_operand" "rYs,rm")])
+			      [(match_operand:QI 1 "general_operand" "rYsYxi,rmi")
+			       (match_operand:QI 2 "general_operand" "rYsYx,rm")])
               (label_ref (match_operand          3 "" ""))
 	      (pc)))
    (clobber (reg:BI CARRY))
@@ -1266,14 +1268,14 @@
   ""
   "@
    CMP.B\t%1, %2 { J%R0\t%l3
-   CMP%X0.B\t%1, %2 { J%R0\t%l3"
+   CMPX.B\t%1, %2 { J%R0\t%l3"
   )
 
 (define_insn "cbranchhi4_reversed"
   [(set (pc) (if_then_else
 	      (match_operator                    0 "msp430_reversible_cmp_operator"
-			      [(match_operand:HI 1 "general_operand" "rYsi,rmi")
-			       (match_operand:HI 2 "general_operand" "rYs,rm")])
+			      [(match_operand:HI 1 "general_operand" "rYsYxi,rmi")
+			       (match_operand:HI 2 "general_operand" "rYsYx,rm")])
               (label_ref (match_operand          3 "" ""))
 	      (pc)))
    (clobber (reg:BI CARRY))
@@ -1281,13 +1283,13 @@
   ""
   "@
    CMP.W\t%1, %2 { J%R0\t%l3
-   CMP%X0.W\t%1, %2 { J%R0\t%l3"
+   CMPX.W\t%1, %2 { J%R0\t%l3"
   )
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
-	      (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYs,rm")
-			   (match_operand:QHI 1 "msp_general_operand" "rYsi,rmi"))
+	      (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYsYx,rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rYsYxi,rmi"))
 		  (const_int 0))
               (label_ref (match_operand 2 "" ""))
 	      (pc)))
@@ -1296,46 +1298,52 @@
   ""
   "@
    BIT%x0%b0\t%1, %0 { JNE\t%l2
-   BIT%X0%b0\t%1, %0 { JNE\t%l2"
+   BITX%b0\t%1, %0 { JNE\t%l2"
   )
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
-	      (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
-			   (match_operand:QHI 1 "msp_general_operand" "rmi"))
+	      (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYsYx,rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rYsYxi,rmi"))
 		  (const_int 0))
               (label_ref (match_operand 2 "" ""))
 	      (pc)))
    (clobber (reg:BI CARRY))
    ]
   ""
-  "BIT%x0%b0\t%1, %0 { JEQ\t%l2"
+  "@
+   BIT%x0%b0\t%1, %0 { JEQ\t%l2
+   BITX%b0\t%1, %0 { JEQ\t%l2"
   )
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
-	      (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
-			   (match_operand:QHI 1 "msp_general_operand" "rmi"))
+	      (eq (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYsYx,rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rYsYxi,rmi"))
 		  (const_int 0))
               (pc)
 	      (label_ref (match_operand 2 "" ""))))
    (clobber (reg:BI CARRY))
    ]
   ""
-  "BIT%X0%b0\t%1, %0 { JNE\t%l2"
+  "@
+  BIT%x0%b0\t%1, %0 { JNE\t%l2
+  BITX%b0\t%1, %0 { JNE\t%l2"
   )
 
 (define_insn "*bitbranch<mode>4"
   [(set (pc) (if_then_else
-	      (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rm")
-			   (match_operand:QHI 1 "msp_general_operand" "rmi"))
+	      (ne (and:QHI (match_operand:QHI 0 "msp_nonimmediate_operand" "rYsYx,rm")
+			   (match_operand:QHI 1 "msp_general_operand" "rYsYxi,rmi"))
 		  (const_int 0))
               (pc)
 	      (label_ref (match_operand 2 "" ""))))
    (clobber (reg:BI CARRY))
    ]
   ""
-  "BIT%X0%b0\t%1, %0 { JEQ\t%l2"
+  "@
+  BIT%x0%b0\t%1, %0 { JEQ\t%l2
+  BITX%b0\t%1, %0 { JEQ\t%l2"
   )
 
 ;;------------------------------------------------------------
@@ -1368,7 +1376,7 @@
    (clobber (reg:BI CARRY))
    ]
   ""
-  "BIT%x0%X0%b0\t%p1, %0 { JEQ\t%l2"
+  "BIT%X0%b0\t%p1, %0 { JEQ\t%l2"
   )
 
 (define_insn "*bitbranch<mode>4_z"
diff --git a/gcc/config/msp430/msp430.opt b/gcc/config/msp430/msp430.opt
index cbbe0faa6a2..2db2906ca11 100644
--- a/gcc/config/msp430/msp430.opt
+++ b/gcc/config/msp430/msp430.opt
@@ -67,12 +67,16 @@ EnumValue
 Enum(msp430_hwmult_types) String(f5series) Value(MSP430_HWMULT_F5SERIES)
 
 mcode-region=
-Target Joined RejectNegative Report ToLower Var(msp430_code_region) Enum(msp430_regions) Init(MSP430_REGION_ANY)
-Specify whether functions should be placed into low or high memory.
+Target Joined RejectNegative Report ToLower Var(msp430_code_region) Enum(msp430_regions) Init(MSP430_REGION_LOWER)
+Specify whether functions should be placed into the lower or upper memory regions, or if they should be shuffled between the regions (either) for best fit (default: lower).
 
 mdata-region=
-Target Joined RejectNegative Report ToLower Var(msp430_data_region) Enum(msp430_regions) Init(MSP430_REGION_ANY)
-Specify whether variables should be placed into low or high memory.
+Target Joined RejectNegative Report ToLower Var(msp430_data_region) Enum(msp430_regions) Init(MSP430_REGION_LOWER)
+Specify whether variables should be placed into the lower or upper memory regions, or if they should be shuffled between the regions (either) for best fit (default: lower).
+
+muse-lower-region-prefix
+Target Mask(USE_LOWER_REGION_PREFIX) Report
+Add the .lower prefix to section names when compiling with -m{code,data}-region=lower (disabled by default).
 
 Enum
 Name(msp430_regions) Type(enum msp430_regions)
diff --git a/gcc/config/msp430/t-msp430 b/gcc/config/msp430/t-msp430
index b9565103e9a..f8ba7751123 100644
--- a/gcc/config/msp430/t-msp430
+++ b/gcc/config/msp430/t-msp430
@@ -28,17 +28,22 @@ msp430-devices.o: $(srcdir)/config/msp430/msp430-devices.c \
 
 # Enable multilibs:
 
-MULTILIB_OPTIONS    = mcpu=msp430 mlarge 
-MULTILIB_DIRNAMES   = 430          large
+MULTILIB_OPTIONS    = mcpu=msp430 mlarge  mdata-region=none
+MULTILIB_DIRNAMES   = 430	   large  full-memory-range
 
 # Match -mcpu=430
 MULTILIB_MATCHES    = mcpu?msp430=mcpu?430
+# These options are equivalent in terms of the multilib required for them
+MULTILIB_MATCHES   += mdata-region?none=mdata-region?upper
+MULTILIB_MATCHES   += mdata-region?none=mdata-region?either
 
 # The correct multilib for a given mmcu is selected without the need for
 # hard-coded data here, because DRIVER_SELF_SPECS will place the correct
 # -mcpu option for a given mcu onto the command line.
 
-MULTILIB_EXCEPTIONS = mcpu=msp430/mlarge
+MULTILIB_REQUIRED = mcpu=msp430
+MULTILIB_REQUIRED += mlarge
+MULTILIB_REQUIRED += mlarge/mdata-region=none
 
 
 MULTILIB_EXTRA_OPTS =
diff --git a/gcc/configure b/gcc/configure
index 22cf194a897..89eb2146e80 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -27751,6 +27751,80 @@ fi
        && test x$with_nan != x; then
       as_fn_error $? "Requesting --with-nan= requires assembler support for -mnan=" "$LINENO" 5
     fi
+    ;;
+    msp430-*-*)
+    # Earlier GAS versions generically support .gnu_attribute, but the
+    # msp430 assembler will not do anything with it.
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .gnu_attribute support" >&5
+$as_echo_n "checking assembler for .gnu_attribute support... " >&6; }
+if ${gcc_cv_as_msp430_gnu_attribute+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_msp430_gnu_attribute=no
+    if test $in_tree_gas = yes; then
+    if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 32 \) \* 1000 + 51`
+  then gcc_cv_as_msp430_gnu_attribute=yes
+fi
+  elif test x$gcc_cv_as != x; then
+    $as_echo '.gnu_attribute 4,1' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_msp430_gnu_attribute=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_msp430_gnu_attribute" >&5
+$as_echo "$gcc_cv_as_msp430_gnu_attribute" >&6; }
+if test $gcc_cv_as_msp430_gnu_attribute = yes; then
+
+$as_echo "#define HAVE_AS_GNU_ATTRIBUTE 1" >>confdefs.h
+
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .mspabi_attribute support" >&5
+$as_echo_n "checking assembler for .mspabi_attribute support... " >&6; }
+if ${gcc_cv_as_msp430_mspabi_attribute+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_msp430_mspabi_attribute=no
+    if test $in_tree_gas = yes; then
+    if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 32 \) \* 1000 + 51`
+  then gcc_cv_as_msp430_mspabi_attribute=yes
+fi
+  elif test x$gcc_cv_as != x; then
+    $as_echo '.mspabi_attribute 4,1' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_msp430_mspabi_attribute=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_msp430_mspabi_attribute" >&5
+$as_echo "$gcc_cv_as_msp430_mspabi_attribute" >&6; }
+if test $gcc_cv_as_msp430_mspabi_attribute = yes; then
+
+$as_echo "#define HAVE_AS_MSPABI_ATTRIBUTE 1" >>confdefs.h
+
+fi
+
     ;;
     riscv*-*-*)
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .attribute support" >&5
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 5c60d0f8dfd..9b866b90031 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -4939,6 +4939,20 @@ pointers into PC-relative form.])
 	[Requesting --with-nan= requires assembler support for -mnan=])
     fi
     ;;
+    msp430-*-*)
+    # Earlier GAS versions generically support .gnu_attribute, but the
+    # msp430 assembler will not do anything with it.
+    gcc_GAS_CHECK_FEATURE([.gnu_attribute support],
+      gcc_cv_as_msp430_gnu_attribute, [2,32,51],,
+      [.gnu_attribute 4,1],,
+      [AC_DEFINE(HAVE_AS_GNU_ATTRIBUTE, 1,
+	  [Define if your assembler supports .gnu_attribute.])])
+    gcc_GAS_CHECK_FEATURE([.mspabi_attribute support],
+      gcc_cv_as_msp430_mspabi_attribute, [2,32,51],,
+      [.mspabi_attribute 4,1],,
+      [AC_DEFINE(HAVE_AS_MSPABI_ATTRIBUTE, 1,
+	  [Define if your assembler supports .mspabi_attribute.])])
+    ;;
     riscv*-*-*)
     gcc_GAS_CHECK_FEATURE([.attribute support],
       gcc_cv_as_riscv_attribute, [2,32,0],,
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cec15e5034c..dbf260e4c7c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5206,7 +5206,7 @@ On the MSP430 target these attributes can be used to specify whether
 the function or variable should be placed into low memory, high
 memory, or the placement should be left to the linker to decide.  The
 attributes are only significant if compiling for the MSP430X
-architecture.
+architecture in the large memory model.
 
 The attributes work in conjunction with a linker script that has been
 augmented to specify where to place sections with a @code{.lower} and
@@ -7537,15 +7537,30 @@ value will be retained across resets.  The linker script being used to
 create the application should ensure that persistent data is correctly
 placed.
 
-@item lower
-@itemx upper
+@item upper
 @itemx either
-@cindex @code{lower} variable attribute, MSP430 
 @cindex @code{upper} variable attribute, MSP430 
 @cindex @code{either} variable attribute, MSP430 
 These attributes are the same as the MSP430 function attributes of the
 same name (@pxref{MSP430 Function Attributes}).  
-These attributes can be applied to both functions and variables.
+
+@item lower
+@cindex @code{lower} variable attribute, MSP430
+This option behaves mostly the same as the MSP430 function attribute of the
+same name (@pxref{MSP430 Function Attributes}), but it has some additional
+functionality.
+
+If @option{-mdata-region=}@{@code{upper,either,none}@} has been passed, or
+the @code{section} attribute is applied to a variable, the compiler will
+generate 430X instructions to handle it.  This is because the compiler has
+to assume that the variable could get placed in the upper memory region
+(above address 0xFFFF).  Marking the variable with the @code{lower} attribute
+informs the compiler that the variable will be placed in lower memory so it
+is safe to use 430 instructions to handle it.
+
+In the case of the @code{section} attribute, the section name given
+will be used, and the @code{.lower} prefix will not be added.
+
 @end table
 
 @node Nvidia PTX Variable Attributes
diff --git a/gcc/testsuite/gcc.target/msp430/430x-insns.c b/gcc/testsuite/gcc.target/msp430/430x-insns.c
new file mode 100644
index 00000000000..a67f778371a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/430x-insns.c
@@ -0,0 +1,1646 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430" "-mcpu=430" "-msmall" } { "" } } */
+/* { dg-options "-O1 -mlarge" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* The purpose of this test is to check that all insn patters in msp430.md
+   which use the "Yx" constraint work as expected.
+   So when both of the operands are in lower memory, a 430 instruction is
+   generated, and when at least one of the operands might be in upper memory,
+   a 430X instruction is generated.
+   We do not need to extensively test the situation where one of the operands
+   in an insn is not a mem (i.e. it is a register or immediate).  A single test
+   will verify that the constraint correctly assumes that a reg
+   or immediate does not itself require a 430X instruction.  */
+
+typedef char qi;
+typedef int hi;
+/* For insns which use the QHI mode iterator (therefore accepting QI, HI and 
+   PSI modes), we also check the PSImode version.  All insns should be 430x
+   in that case.  */
+typedef __int20 psi;
+typedef long si;
+
+#define ATTR_EITHER __attribute__((either))
+#define ATTR_LOWER __attribute__((lower))
+
+/* Use these to generate 430X insns.  */
+qi ATTR_EITHER eqi1, eqi2, eqi3, eqi4, eqi5, eqi6;
+hi ATTR_EITHER ehi1, ehi2, ehi3, ehi4, ehi5, ehi6;
+psi ATTR_EITHER epsi1, epsi2, epsi3, epsi4, epsi5, epsi6;
+si ATTR_EITHER esi1, esi2, esi3, esi4, esi5, esi6;
+
+/* Use these to generate 430 insns.  */
+qi ATTR_LOWER lqi1, lqi2, lqi3, lqi4, lqi5, lqi6;
+hi ATTR_LOWER lhi1, lhi2, lhi3, lhi4, lhi5, lhi6;
+psi ATTR_LOWER lpsi1, lpsi2, lpsi3, lpsi4, lpsi5, lpsi6;
+si ATTR_LOWER lsi1, lsi2, lsi3, lsi4, lsi5, lsi6;
+
+/* The default data region is "lower", so these behave the same as the above
+   "l*" variables unless -mdata-region={upper,either,none} is passed.  */
+qi qi1, qi2, qi3, qi4, qi5, qi6;
+hi hi1, hi2, hi3, hi4, hi5, hi6;
+psi psi1, psi2, psi3, psi4, psi5, psi6;
+si si1, si2, si3, si4, si5, si6;
+
+qi use_qi(qi a);
+hi use_hi(hi a);
+psi use_psi(psi a);
+si use_si(si a);
+
+#define USE_MODE(MODE) use_ ## MODE
+#define USE_MODE_N(MODE,N) use_ ## N ## MODE
+
+#define E_VAR(MODE,N) e ## MODE ## N
+#define L_VAR(MODE,N) l ## MODE ## N
+#define VAR(MODE,N) MODE ## N
+
+#define REG_VAR(MODE,N) MODE ## r ## N
+
+/* Verify that a register operand does not influence whether a 430X instruction
+   is used or not.  */
+/*
+** register_operand:  { target msp430_region_lower }
+** ...
+**	MOV.B	&lqi1, R12
+** ...
+**	MOV.B	&qi1, R12
+** ...
+**	MOVX.B	&eqi1, R12
+** ...
+*/
+/*
+** register_operand:  { target msp430_region_not_lower }
+** ...
+**	MOV.B	&lqi1, R12
+** ...
+**	MOVX.B	&qi1, R12
+** ...
+**	MOVX.B	&eqi1, R12
+** ...
+*/
+
+void
+register_operand (void)
+{
+  use_qi (lqi1);
+  use_qi (qi1);
+  use_qi (eqi1);
+}
+
+/* Verify that an immediate operand does not influence whether a 430X instruction
+   is used or not.  */
+/*
+** immediate_operand: { target msp430_region_lower }
+** ...
+**	MOV.B	#1, &lqi1
+**	MOV.B	#2, &qi1
+**	MOVX.B	#3, &eqi1
+** ...
+*/
+/*
+** immediate_operand: { target msp430_region_not_lower }
+** ...
+**	MOV.B	#1, &lqi1
+**	MOVX.B	#2, &qi1
+**	MOVX.B	#3, &eqi1
+** ...
+*/
+void
+immediate_operand (void)
+{
+  lqi1 = 1;
+  qi1 = 2;
+  eqi1 = 3;
+}
+
+/* Verify that variables marked with the noinit, persistent, section and lower
+   attributes have the appropriate format instructions generated to handle
+   them.  */
+int __attribute__((persistent)) pp = 10;
+int __attribute__((noinit)) nn;
+int __attribute__((section(".data.foo"))) s;
+int __attribute__((section(".data.foo"),lower)) sl1;
+int __attribute__((lower,section(".data.foo"))) sl2;
+
+/*
+** attrs:
+** ...
+**	MOVX.W	#1, &pp
+**	MOV.W	#2, &nn
+**	MOVX.W	#3, &s
+**	MOV.W	#4, &sl1
+**	MOV.W	#5, &sl2
+** ...
+*/
+void
+attrs (void)
+{
+  pp = 1;
+  nn = 2;
+  s = 3;
+  sl1 = 4;
+  sl2 = 5;
+}
+
+#define MOV_INSNS(MODE) \
+  E_VAR(MODE, 1) = E_VAR(MODE, 2); \
+  E_VAR(MODE, 3) = L_VAR(MODE, 1); \
+  E_VAR(MODE, 4) = VAR(MODE, 1); \
+  L_VAR(MODE, 4) = E_VAR(MODE, 5); \
+  VAR(MODE, 5) = E_VAR(MODE, 6); \
+  L_VAR(MODE, 2) = L_VAR(MODE, 3); \
+  L_VAR(MODE, 5) = VAR(MODE, 2); \
+  VAR(MODE, 3) = VAR(MODE, 4); \
+  VAR(MODE, 6) = L_VAR(MODE, 6);
+
+
+/*
+** movqi: { target msp430_region_lower }
+** ...
+**	MOVX.B	&eqi2, &eqi1
+**	MOVX.B	&lqi1, &eqi3
+**	MOVX.B	&qi1, &eqi4
+**	MOVX.B	&eqi5, &lqi4
+**	MOVX.B	&eqi6, &qi5
+**	MOV.B	&lqi3, &lqi2
+**	MOV.B	&qi2, &lqi5
+**	MOV.B	&qi4, &qi3
+**	MOV.B	&lqi6, &qi6
+** ...
+*/
+/*
+** movqi: { target msp430_region_not_lower }
+** ...
+**	MOVX.B	&eqi2, &eqi1
+**	MOVX.B	&lqi1, &eqi3
+**	MOVX.B	&qi1, &eqi4
+**	MOVX.B	&eqi5, &lqi4
+**	MOVX.B	&eqi6, &qi5
+**	MOV.B	&lqi3, &lqi2
+**	MOVX.B	&qi2, &lqi5
+**	MOVX.B	&qi4, &qi3
+**	MOVX.B	&lqi6, &qi6
+** ...
+*/
+void
+movqi (void)
+{
+  MOV_INSNS (qi)
+}
+
+/*
+** movhi: { target msp430_region_lower }
+** ...
+**	MOVX.W	&ehi2, &ehi1
+**	MOVX.W	&lhi1, &ehi3
+**	MOVX.W	&hi1, &ehi4
+**	MOVX.W	&ehi5, &lhi4
+**	MOVX.W	&ehi6, &hi5
+**	MOV.W	&lhi3, &lhi2
+**	MOV.W	&hi2, &lhi5
+**	MOV.W	&hi4, &hi3
+**	MOV.W	&lhi6, &hi6
+** ...
+*/
+/*
+** movhi: { target msp430_region_not_lower }
+** ...
+**	MOVX.W	&ehi2, &ehi1
+**	MOVX.W	&lhi1, &ehi3
+**	MOVX.W	&hi1, &ehi4
+**	MOVX.W	&ehi5, &lhi4
+**	MOVX.W	&ehi6, &hi5
+**	MOV.W	&lhi3, &lhi2
+**	MOVX.W	&hi2, &lhi5
+**	MOVX.W	&hi4, &hi3
+**	MOVX.W	&lhi6, &hi6
+** ...
+*/
+void
+movhi (void)
+{
+  MOV_INSNS (hi)
+}
+
+/* There is no specific movsi3 pattern defined for msp430, but we check
+   this is synthesized correctly anyway.  */
+/*
+** movsi: { target msp430_region_lower }
+** ...
+**	MOVX.W	&esi2, &esi1
+**	MOVX.W	&esi2\+2, &esi1\+2
+**	MOVX.W	&lsi1, &esi3
+**	MOVX.W	&lsi1\+2, &esi3\+2
+**	MOVX.W	&si1, &esi4
+**	MOVX.W	&si1\+2, &esi4\+2
+**	MOVX.W	&esi5, &lsi4
+**	MOVX.W	&esi5\+2, &lsi4\+2
+**	MOVX.W	&esi6, &si5
+**	MOVX.W	&esi6\+2, &si5\+2
+**	MOV.W	&lsi3, &lsi2
+**	MOV.W	&lsi3\+2, &lsi2\+2
+**	MOV.W	&si2, &lsi5
+**	MOV.W	&si2\+2, &lsi5\+2
+**	MOV.W	&si4, &si3
+**	MOV.W	&si4\+2, &si3\+2
+**	MOV.W	&lsi6, &si6
+**	MOV.W	&lsi6\+2, &si6\+2
+** ...
+*/
+/*
+** movsi: { target msp430_region_not_lower }
+** ...
+**	MOVX.W	&esi2, &esi1
+**	MOVX.W	&esi2\+2, &esi1\+2
+**	MOVX.W	&lsi1, &esi3
+**	MOVX.W	&lsi1\+2, &esi3\+2
+**	MOVX.W	&si1, &esi4
+**	MOVX.W	&si1\+2, &esi4\+2
+**	MOVX.W	&esi5, &lsi4
+**	MOVX.W	&esi5\+2, &lsi4\+2
+**	MOVX.W	&esi6, &si5
+**	MOVX.W	&esi6\+2, &si5\+2
+**	MOV.W	&lsi3, &lsi2
+**	MOV.W	&lsi3\+2, &lsi2\+2
+**	MOVX.W	&si2, &lsi5
+**	MOVX.W	&si2\+2, &lsi5\+2
+**	MOVX.W	&si4, &si3
+**	MOVX.W	&si4\+2, &si3\+2
+**	MOVX.W	&lsi6, &si6
+**	MOVX.W	&lsi6\+2, &si6\+2
+** ...
+*/
+void
+movsi (void)
+{
+  MOV_INSNS (si)
+}
+
+#define ADD_INSNS(MODE) \
+  E_VAR(MODE,1) += E_VAR(MODE,2); \
+  E_VAR(MODE,3) += L_VAR(MODE,1); \
+  E_VAR(MODE,4) += VAR(MODE,1); \
+  L_VAR(MODE,2) += E_VAR(MODE,5); \
+  VAR(MODE,3) += E_VAR(MODE,6); \
+  L_VAR(MODE,3) += L_VAR(MODE,4); \
+  L_VAR(MODE,5) += VAR(MODE,2); \
+  VAR(MODE,4) += L_VAR(MODE,6); \
+  VAR(MODE,5) += VAR(MODE,6);
+
+/*
+** addqi3: { target msp430_region_lower }
+** ...
+**	ADDX.B	&eqi2, &eqi1
+**	ADDX.B	&lqi1, &eqi3
+**	ADDX.B	&qi1, &eqi4
+**	ADDX.B	&eqi5, &lqi2
+**	ADDX.B	&eqi6, &qi3
+**	ADD.B	&lqi4, &lqi3
+**	ADD.B	&qi2, &lqi5
+**	ADD.B	&lqi6, &qi4
+**	ADD.B	&qi6, &qi5
+** ...
+*/
+/*
+** addqi3: { target msp430_region_not_lower }
+** ...
+**	ADDX.B	&eqi2, &eqi1
+**	ADDX.B	&lqi1, &eqi3
+**	ADDX.B	&qi1, &eqi4
+**	ADDX.B	&eqi5, &lqi2
+**	ADDX.B	&eqi6, &qi3
+**	ADD.B	&lqi4, &lqi3
+**	ADDX.B	&qi2, &lqi5
+**	ADDX.B	&lqi6, &qi4
+**	ADDX.B	&qi6, &qi5
+** ...
+*/
+void
+addqi3 (void)
+{
+  ADD_INSNS(qi)
+}
+
+/*
+** addhi3: { target msp430_region_lower }
+** ...
+**	ADDX.W	&ehi2, &ehi1
+**	ADDX.W	&lhi1, &ehi3
+**	ADDX.W	&hi1, &ehi4
+**	ADDX.W	&ehi5, &lhi2
+**	ADDX.W	&ehi6, &hi3
+**	ADD.W	&lhi4, &lhi3
+**	ADD.W	&hi2, &lhi5
+**	ADD.W	&lhi6, &hi4
+**	ADD.W	&hi6, &hi5
+** ...
+*/
+/*
+** addhi3: { target msp430_region_not_lower }
+** ...
+**	ADDX.W	&ehi2, &ehi1
+**	ADDX.W	&lhi1, &ehi3
+**	ADDX.W	&hi1, &ehi4
+**	ADDX.W	&ehi5, &lhi2
+**	ADDX.W	&ehi6, &hi3
+**	ADD.W	&lhi4, &lhi3
+**	ADDX.W	&hi2, &lhi5
+**	ADDX.W	&lhi6, &hi4
+**	ADDX.W	&hi6, &hi5
+** ...
+*/
+void
+addhi3 (void)
+{
+  ADD_INSNS(hi)
+}
+
+/*
+** addsi3: { target msp430_region_lower }
+** ...
+**	ADDX	&esi2, &esi1 { ADDCX	&esi2\+2, &esi1\+2
+**	ADDX	&lsi1, &esi3 { ADDCX	&lsi1\+2, &esi3\+2
+**	ADDX	&si1, &esi4 { ADDCX	&si1\+2, &esi4\+2
+**	ADDX	&esi5, &lsi2 { ADDCX	&esi5\+2, &lsi2\+2
+**	ADDX	&esi6, &si3 { ADDCX	&esi6\+2, &si3\+2
+**	ADD	&lsi4, &lsi3 { ADDC	&lsi4\+2, &lsi3\+2
+**	ADD	&si2, &lsi5 { ADDC	&si2\+2, &lsi5\+2
+**	ADD	&lsi6, &si4 { ADDC	&lsi6\+2, &si4\+2
+**	ADD	&si6, &si5 { ADDC	&si6\+2, &si5\+2
+** ...
+*/
+/*
+** addsi3: { target msp430_region_not_lower }
+** ...
+**	ADDX	&esi2, &esi1 { ADDCX	&esi2\+2, &esi1\+2
+**	ADDX	&lsi1, &esi3 { ADDCX	&lsi1\+2, &esi3\+2
+**	ADDX	&si1, &esi4 { ADDCX	&si1\+2, &esi4\+2
+**	ADDX	&esi5, &lsi2 { ADDCX	&esi5\+2, &lsi2\+2
+**	ADDX	&esi6, &si3 { ADDCX	&esi6\+2, &si3\+2
+**	ADD	&lsi4, &lsi3 { ADDC	&lsi4\+2, &lsi3\+2
+**	ADDX	&si2, &lsi5 { ADDCX	&si2\+2, &lsi5\+2
+**	ADDX	&lsi6, &si4 { ADDCX	&lsi6\+2, &si4\+2
+**	ADDX	&si6, &si5 { ADDCX	&si6\+2, &si5\+2
+** ...
+*/
+void
+addsi3 (void)
+{
+  ADD_INSNS(si)
+}
+
+#define SUB_INSNS(MODE) \
+  E_VAR(MODE,1) -= E_VAR(MODE,2); \
+  E_VAR(MODE,3) -= L_VAR(MODE,1); \
+  E_VAR(MODE,4) -= VAR(MODE,1); \
+  L_VAR(MODE,2) -= E_VAR(MODE,5); \
+  VAR(MODE,3) -= E_VAR(MODE,6); \
+  L_VAR(MODE,3) -= L_VAR(MODE,4); \
+  L_VAR(MODE,5) -= VAR(MODE,2); \
+  VAR(MODE,4) -= L_VAR(MODE,6); \
+  VAR(MODE,5) -= VAR(MODE,6);
+
+/*
+** subqi3: { target msp430_region_lower }
+** ...
+**	SUBX.B	&eqi2, &eqi1
+**	SUBX.B	&lqi1, &eqi3
+**	SUBX.B	&qi1, &eqi4
+**	SUBX.B	&eqi5, &lqi2
+**	SUBX.B	&eqi6, &qi3
+**	SUB.B	&lqi4, &lqi3
+**	SUB.B	&qi2, &lqi5
+**	SUB.B	&lqi6, &qi4
+**	SUB.B	&qi6, &qi5
+** ...
+*/
+/*
+** subqi3: { target msp430_region_not_lower }
+** ...
+**	SUBX.B	&eqi2, &eqi1
+**	SUBX.B	&lqi1, &eqi3
+**	SUBX.B	&qi1, &eqi4
+**	SUBX.B	&eqi5, &lqi2
+**	SUBX.B	&eqi6, &qi3
+**	SUB.B	&lqi4, &lqi3
+**	SUBX.B	&qi2, &lqi5
+**	SUBX.B	&lqi6, &qi4
+**	SUBX.B	&qi6, &qi5
+** ...
+*/
+void
+subqi3 (void)
+{
+  SUB_INSNS(qi)
+}
+
+/*
+** subhi3: { target msp430_region_lower }
+** ...
+**	SUBX.W	&ehi2, &ehi1
+**	SUBX.W	&lhi1, &ehi3
+**	SUBX.W	&hi1, &ehi4
+**	SUBX.W	&ehi5, &lhi2
+**	SUBX.W	&ehi6, &hi3
+**	SUB.W	&lhi4, &lhi3
+**	SUB.W	&hi2, &lhi5
+**	SUB.W	&lhi6, &hi4
+**	SUB.W	&hi6, &hi5
+** ...
+*/
+/*
+** subhi3: { target msp430_region_not_lower }
+** ...
+**	SUBX.W	&ehi2, &ehi1
+**	SUBX.W	&lhi1, &ehi3
+**	SUBX.W	&hi1, &ehi4
+**	SUBX.W	&ehi5, &lhi2
+**	SUBX.W	&ehi6, &hi3
+**	SUB.W	&lhi4, &lhi3
+**	SUBX.W	&hi2, &lhi5
+**	SUBX.W	&lhi6, &hi4
+**	SUBX.W	&hi6, &hi5
+** ...
+*/
+void
+subhi3 (void)
+{
+  SUB_INSNS(hi)
+}
+
+/*
+** subsi3: { target msp430_region_lower }
+** ...
+**	SUBX	&esi2, &esi1 { SUBCX	&esi2\+2, &esi1\+2
+**	SUBX	&lsi1, &esi3 { SUBCX	&lsi1\+2, &esi3\+2
+**	SUBX	&si1, &esi4 { SUBCX	&si1\+2, &esi4\+2
+**	SUBX	&esi5, &lsi2 { SUBCX	&esi5\+2, &lsi2\+2
+**	SUBX	&esi6, &si3 { SUBCX	&esi6\+2, &si3\+2
+**	SUB	&lsi4, &lsi3 { SUBC	&lsi4\+2, &lsi3\+2
+**	SUB	&si2, &lsi5 { SUBC	&si2\+2, &lsi5\+2
+**	SUB	&lsi6, &si4 { SUBC	&lsi6\+2, &si4\+2
+**	SUB	&si6, &si5 { SUBC	&si6\+2, &si5\+2
+** ...
+*/
+/*
+** subsi3: { target msp430_region_not_lower }
+** ...
+**	SUBX	&esi2, &esi1 { SUBCX	&esi2\+2, &esi1\+2
+**	SUBX	&lsi1, &esi3 { SUBCX	&lsi1\+2, &esi3\+2
+**	SUBX	&si1, &esi4 { SUBCX	&si1\+2, &esi4\+2
+**	SUBX	&esi5, &lsi2 { SUBCX	&esi5\+2, &lsi2\+2
+**	SUBX	&esi6, &si3 { SUBCX	&esi6\+2, &si3\+2
+**	SUB	&lsi4, &lsi3 { SUBC	&lsi4\+2, &lsi3\+2
+**	SUBX	&si2, &lsi5 { SUBCX	&si2\+2, &lsi5\+2
+**	SUBX	&lsi6, &si4 { SUBCX	&lsi6\+2, &si4\+2
+**	SUBX	&si6, &si5 { SUBCX	&si6\+2, &si5\+2
+** ...
+*/
+void
+subsi3 (void)
+{
+  SUB_INSNS(si)
+}
+
+#define BIC_INSN(MODE) \
+  E_VAR(MODE,1) &= (E_VAR(MODE,2) ^ E_VAR(MODE,1)); \
+  E_VAR(MODE,3) &= (L_VAR(MODE,1) ^ E_VAR(MODE,3)); \
+  E_VAR(MODE,4) &= (VAR(MODE,1) ^ E_VAR(MODE,4)); \
+  L_VAR(MODE,2) &= (E_VAR(MODE,5) ^ L_VAR(MODE,2)); \
+  VAR(MODE,2) &= (E_VAR(MODE,6) ^ VAR(MODE,2)); \
+  L_VAR(MODE,3) &= (L_VAR(MODE,4) ^ L_VAR(MODE,3)); \
+  L_VAR(MODE,5) &= (VAR(MODE,3) ^ L_VAR(MODE,5)); \
+  VAR(MODE,4) &= (L_VAR(MODE,6) ^ VAR(MODE,4)); \
+  VAR(MODE,5) &= (VAR(MODE,6) ^ VAR(MODE,5)); \
+
+/*
+** bicqi3: { target msp430_region_lower }
+** ...
+**	BICX.B	&eqi2, &eqi1
+**	BICX.B	&lqi1, &eqi3
+**	BICX.B	&qi1, &eqi4
+**	BICX.B	&eqi5, &lqi2
+**	BICX.B	&eqi6, &qi2
+**	BIC.B	&lqi4, &lqi3
+**	BIC.B	&qi3, &lqi5
+**	BIC.B	&lqi6, &qi4
+**	BIC.B	&qi6, &qi5
+** ...
+*/
+/*
+** bicqi3: { target msp430_region_not_lower }
+** ...
+**	BICX.B	&eqi2, &eqi1
+**	BICX.B	&lqi1, &eqi3
+**	BICX.B	&qi1, &eqi4
+**	BICX.B	&eqi5, &lqi2
+**	BICX.B	&eqi6, &qi2
+**	BIC.B	&lqi4, &lqi3
+**	BICX.B	&qi3, &lqi5
+**	BICX.B	&lqi6, &qi4
+**	BICX.B	&qi6, &qi5
+** ...
+*/
+void
+bicqi3 (void)
+{
+  BIC_INSN(qi)
+}
+
+/*
+** bichi3: { target msp430_region_lower }
+** ...
+**	BICX.W	&ehi2, &ehi1
+**	BICX.W	&lhi1, &ehi3
+**	BICX.W	&hi1, &ehi4
+**	BICX.W	&ehi5, &lhi2
+**	BICX.W	&ehi6, &hi2
+**	BIC.W	&lhi4, &lhi3
+**	BIC.W	&hi3, &lhi5
+**	BIC.W	&lhi6, &hi4
+**	BIC.W	&hi6, &hi5
+** ...
+*/
+/*
+** bichi3: { target msp430_region_not_lower }
+** ...
+**	BICX.W	&ehi2, &ehi1
+**	BICX.W	&lhi1, &ehi3
+**	BICX.W	&hi1, &ehi4
+**	BICX.W	&ehi5, &lhi2
+**	BICX.W	&ehi6, &hi2
+**	BIC.W	&lhi4, &lhi3
+**	BICX.W	&hi3, &lhi5
+**	BICX.W	&lhi6, &hi4
+**	BICX.W	&hi6, &hi5
+** ...
+*/
+void
+bichi3 (void)
+{
+  BIC_INSN(hi)
+}
+
+/*
+** bicpsi3:
+** ...
+**	BICX.A	&epsi2, &epsi1
+**	BICX.A	&lpsi1, &epsi3
+**	BICX.A	&psi1, &epsi4
+**	BICX.A	&epsi5, &lpsi2
+**	BICX.A	&epsi6, &psi2
+**	BICX.A	&lpsi4, &lpsi3
+**	BICX.A	&psi3, &lpsi5
+**	BICX.A	&lpsi6, &psi4
+**	BICX.A	&psi6, &psi5
+** ...
+*/
+void
+bicpsi3 (void)
+{
+  BIC_INSN(psi)
+}
+
+/* There is no specific bicsi3 pattern defined for msp430, but we check
+   this is synthesized correctly anyway.  */
+/*
+** bicsi3: { target msp430_region_lower }
+** ...
+**	BICX.W	&esi2, &esi1
+**	BICX.W	&esi2\+2, &esi1\+2
+**	BICX.W	&lsi1, &esi3
+**	BICX.W	&lsi1\+2, &esi3\+2
+**	BICX.W	&si1, &esi4
+**	BICX.W	&si1\+2, &esi4\+2
+**	BICX.W	&esi5, &lsi2
+**	BICX.W	&esi5\+2, &lsi2\+2
+**	BICX.W	&esi6, &si2
+**	BICX.W	&esi6\+2, &si2\+2
+**	BIC.W	&lsi4, &lsi3
+**	BIC.W	&lsi4\+2, &lsi3\+2
+**	BIC.W	&si3, &lsi5
+**	BIC.W	&si3\+2, &lsi5\+2
+**	BIC.W	&lsi6, &si4
+**	BIC.W	&lsi6\+2, &si4\+2
+**	BIC.W	&si6, &si5
+**	BIC.W	&si6\+2, &si5\+2
+** ...
+*/
+/*
+** bicsi3: { target msp430_region_not_lower }
+** ...
+**	BICX.W	&esi2, &esi1
+**	BICX.W	&esi2\+2, &esi1\+2
+**	BICX.W	&lsi1, &esi3
+**	BICX.W	&lsi1\+2, &esi3\+2
+**	BICX.W	&si1, &esi4
+**	BICX.W	&si1\+2, &esi4\+2
+**	BICX.W	&esi5, &lsi2
+**	BICX.W	&esi5\+2, &lsi2\+2
+**	BICX.W	&esi6, &si2
+**	BICX.W	&esi6\+2, &si2\+2
+**	BIC.W	&lsi4, &lsi3
+**	BIC.W	&lsi4\+2, &lsi3\+2
+**	BICX.W	&si3, &lsi5
+**	BICX.W	&si3\+2, &lsi5\+2
+**	BICX.W	&lsi6, &si4
+**	BICX.W	&lsi6\+2, &si4\+2
+**	BICX.W	&si6, &si5
+**	BICX.W	&si6\+2, &si5\+2
+** ...
+*/
+void
+bicsi3 (void)
+{
+  BIC_INSN(si)
+}
+
+#define BIC_CG_INSN(MODE) \
+  E_VAR(MODE,1) &= (1 ^ E_VAR(MODE,1)); \
+  E_VAR(MODE,2) &= (2 ^ E_VAR(MODE,2)); \
+  L_VAR(MODE,1) &= (4 ^ L_VAR(MODE,1)); \
+  VAR(MODE,1) &= (8 ^ VAR(MODE,1)); \
+
+/*
+** bic_cg_qi3: { target msp430_region_lower }
+** ...
+**	BICX.B	#1, &eqi1
+**	BICX.B	#2, &eqi2
+**	BIC.B	#4, &lqi1
+**	BIC.B	#8, &qi1
+** ...
+*/
+/*
+** bic_cg_qi3: { target msp430_region_not_lower }
+** ...
+**	BICX.B	#1, &eqi1
+**	BICX.B	#2, &eqi2
+**	BIC.B	#4, &lqi1
+**	BICX.B	#8, &qi1
+** ...
+*/
+void
+bic_cg_qi3 (void)
+{
+  BIC_CG_INSN(qi)
+}
+
+/*
+** bic_cg_hi3: { target msp430_region_lower }
+** ...
+**	BICX.W	#1, &ehi1
+**	BICX.W	#2, &ehi2
+**	BIC.W	#4, &lhi1
+**	BIC.W	#8, &hi1
+** ...
+*/
+/*
+** bic_cg_hi3: { target msp430_region_not_lower }
+** ...
+**	BICX.W	#1, &ehi1
+**	BICX.W	#2, &ehi2
+**	BIC.W	#4, &lhi1
+**	BICX.W	#8, &hi1
+** ...
+*/
+void
+bic_cg_hi3 (void)
+{
+  BIC_CG_INSN(hi)
+}
+
+/*
+** bic_cg_psi3:
+** ...
+**	BICX.A	#1, &epsi1
+**	BICX.A	#2, &epsi2
+**	BICX.A	#4, &lpsi1
+**	BICX.A	#8, &psi1
+** ...
+*/
+void
+bic_cg_psi3 (void)
+{
+  BIC_CG_INSN(psi)
+}
+
+/* There is no specific bic_cg_si3 pattern defined for msp430, but we check
+   this is synthesized correctly anyway.  */
+/*
+** bic_cg_si3: { target msp430_region_lower }
+** ...
+**	BICX.W	#1, &esi1
+**	BICX.W	#2, &esi2
+**	BIC.W	#4, &lsi1
+**	BIC.W	#8, &si1
+** ...
+*/
+/*
+** bic_cg_si3: { target msp430_region_not_lower }
+** ...
+**	BICX.W	#1, &esi1
+**	BICX.W	#2, &esi2
+**	BIC.W	#4, &lsi1
+**	BICX.W	#8, &si1
+** ...
+*/
+void
+bic_cg_si3 (void)
+{
+  BIC_CG_INSN(si)
+}
+
+#define AND_INSN(MODE) \
+  E_VAR(MODE,1) &= E_VAR(MODE,2); \
+  E_VAR(MODE,3) &= L_VAR(MODE,1); \
+  E_VAR(MODE,4) &= VAR(MODE,1); \
+  L_VAR(MODE,2) &= E_VAR(MODE,5); \
+  VAR(MODE,2) &= E_VAR(MODE,6); \
+  L_VAR(MODE,3) &= L_VAR(MODE,4); \
+  L_VAR(MODE,5) &= VAR(MODE,3); \
+  VAR(MODE,4) &= VAR(MODE,5); \
+  VAR(MODE,6) &= L_VAR(MODE,6);
+
+/*
+** andqi3: { target msp430_region_lower }
+** ...
+**	ANDX.B	&eqi2, &eqi1
+**	ANDX.B	&lqi1, &eqi3
+**	ANDX.B	&qi1, &eqi4
+**	ANDX.B	&eqi5, &lqi2
+**	ANDX.B	&eqi6, &qi2
+**	AND.B	&lqi4, &lqi3
+**	AND.B	&qi3, &lqi5
+**	AND.B	&qi5, &qi4
+**	AND.B	&lqi6, &qi6
+** ...
+*/
+/*
+** andqi3: { target msp430_region_not_lower }
+** ...
+**	ANDX.B	&eqi2, &eqi1
+**	ANDX.B	&lqi1, &eqi3
+**	ANDX.B	&qi1, &eqi4
+**	ANDX.B	&eqi5, &lqi2
+**	ANDX.B	&eqi6, &qi2
+**	AND.B	&lqi4, &lqi3
+**	ANDX.B	&qi3, &lqi5
+**	ANDX.B	&qi5, &qi4
+**	ANDX.B	&lqi6, &qi6
+** ...
+*/
+void
+andqi3 (void)
+{
+  AND_INSN(qi)
+}
+
+/*
+** andhi3: { target msp430_region_lower }
+** ...
+**	ANDX.W	&ehi2, &ehi1
+**	ANDX.W	&lhi1, &ehi3
+**	ANDX.W	&hi1, &ehi4
+**	ANDX.W	&ehi5, &lhi2
+**	ANDX.W	&ehi6, &hi2
+**	AND.W	&lhi4, &lhi3
+**	AND.W	&hi3, &lhi5
+**	AND.W	&hi5, &hi4
+**	AND.W	&lhi6, &hi6
+** ...
+*/
+/*
+** andhi3: { target msp430_region_not_lower }
+** ...
+**	ANDX.W	&ehi2, &ehi1
+**	ANDX.W	&lhi1, &ehi3
+**	ANDX.W	&hi1, &ehi4
+**	ANDX.W	&ehi5, &lhi2
+**	ANDX.W	&ehi6, &hi2
+**	AND.W	&lhi4, &lhi3
+**	ANDX.W	&hi3, &lhi5
+**	ANDX.W	&hi5, &hi4
+**	ANDX.W	&lhi6, &hi6
+** ...
+*/
+void
+andhi3 (void)
+{
+  AND_INSN(hi)
+}
+
+/*
+** andpsi3:
+** ...
+**	ANDX.A	&epsi2, &epsi1
+**	ANDX.A	&lpsi1, &epsi3
+**	ANDX.A	&psi1, &epsi4
+**	ANDX.A	&epsi5, &lpsi2
+**	ANDX.A	&epsi6, &psi2
+**	ANDX.A	&lpsi4, &lpsi3
+**	ANDX.A	&psi3, &lpsi5
+**	ANDX.A	&psi5, &psi4
+**	ANDX.A	&lpsi6, &psi6
+** ...
+*/
+void
+andpsi3 (void)
+{
+  AND_INSN(psi)
+}
+
+/* There is no specific andsi3 pattern defined for msp430, but we check
+   this is synthesized correctly anyway.  */
+/*
+** andsi3: { target msp430_region_lower }
+** ...
+**	ANDX.W	&esi2, &esi1
+**	ANDX.W	&esi2\+2, &esi1\+2
+**	ANDX.W	&lsi1, &esi3
+**	ANDX.W	&lsi1\+2, &esi3\+2
+**	ANDX.W	&si1, &esi4
+**	ANDX.W	&si1\+2, &esi4\+2
+**	ANDX.W	&esi5, &lsi2
+**	ANDX.W	&esi5\+2, &lsi2\+2
+**	ANDX.W	&esi6, &si2
+**	ANDX.W	&esi6\+2, &si2\+2
+**	AND.W	&lsi4, &lsi3
+**	AND.W	&lsi4\+2, &lsi3\+2
+**	AND.W	&si3, &lsi5
+**	AND.W	&si3\+2, &lsi5\+2
+**	AND.W	&si5, &si4
+**	AND.W	&si5\+2, &si4\+2
+**	AND.W	&lsi6, &si6
+**	AND.W	&lsi6\+2, &si6\+2
+** ...
+*/
+/*
+** andsi3: { target msp430_region_not_lower }
+** ...
+**	ANDX.W	&esi2, &esi1
+**	ANDX.W	&esi2\+2, &esi1\+2
+**	ANDX.W	&lsi1, &esi3
+**	ANDX.W	&lsi1\+2, &esi3\+2
+**	ANDX.W	&si1, &esi4
+**	ANDX.W	&si1\+2, &esi4\+2
+**	ANDX.W	&esi5, &lsi2
+**	ANDX.W	&esi5\+2, &lsi2\+2
+**	ANDX.W	&esi6, &si2
+**	ANDX.W	&esi6\+2, &si2\+2
+**	AND.W	&lsi4, &lsi3
+**	AND.W	&lsi4\+2, &lsi3\+2
+**	ANDX.W	&si3, &lsi5
+**	ANDX.W	&si3\+2, &lsi5\+2
+**	ANDX.W	&si5, &si4
+**	ANDX.W	&si5\+2, &si4\+2
+**	ANDX.W	&lsi6, &si6
+**	ANDX.W	&lsi6\+2, &si6\+2
+** ...
+*/
+void
+andsi3 (void)
+{
+  AND_INSN(si)
+}
+
+#define IOR_INSN(MODE) \
+  E_VAR(MODE,1) |= E_VAR(MODE,2); \
+  E_VAR(MODE,3) |= L_VAR(MODE,1); \
+  E_VAR(MODE,4) |= VAR(MODE,1); \
+  L_VAR(MODE,2) |= E_VAR(MODE,5); \
+  VAR(MODE,2) |= E_VAR(MODE,6); \
+  L_VAR(MODE,3) |= L_VAR(MODE,4); \
+  L_VAR(MODE,5) |= VAR(MODE,3); \
+  VAR(MODE,4) |= VAR(MODE,5); \
+  VAR(MODE,6) |= L_VAR(MODE,6);
+
+/*
+** iorqi3: { target msp430_region_lower }
+** ...
+**	BISX.B	&eqi2, &eqi1
+**	BISX.B	&lqi1, &eqi3
+**	BISX.B	&qi1, &eqi4
+**	BISX.B	&eqi5, &lqi2
+**	BISX.B	&eqi6, &qi2
+**	BIS.B	&lqi4, &lqi3
+**	BIS.B	&qi3, &lqi5
+**	BIS.B	&qi5, &qi4
+**	BIS.B	&lqi6, &qi6
+** ...
+*/
+/*
+** iorqi3: { target msp430_region_not_lower }
+** ...
+**	BISX.B	&eqi2, &eqi1
+**	BISX.B	&lqi1, &eqi3
+**	BISX.B	&qi1, &eqi4
+**	BISX.B	&eqi5, &lqi2
+**	BISX.B	&eqi6, &qi2
+**	BIS.B	&lqi4, &lqi3
+**	BISX.B	&qi3, &lqi5
+**	BISX.B	&qi5, &qi4
+**	BISX.B	&lqi6, &qi6
+** ...
+*/
+void
+iorqi3 (void)
+{
+  IOR_INSN(qi)
+}
+
+/*
+** iorhi3: { target msp430_region_lower }
+** ...
+**	BISX.W	&ehi2, &ehi1
+**	BISX.W	&lhi1, &ehi3
+**	BISX.W	&hi1, &ehi4
+**	BISX.W	&ehi5, &lhi2
+**	BISX.W	&ehi6, &hi2
+**	BIS.W	&lhi4, &lhi3
+**	BIS.W	&hi3, &lhi5
+**	BIS.W	&hi5, &hi4
+**	BIS.W	&lhi6, &hi6
+** ...
+*/
+/*
+** iorhi3: { target msp430_region_not_lower }
+** ...
+**	BISX.W	&ehi2, &ehi1
+**	BISX.W	&lhi1, &ehi3
+**	BISX.W	&hi1, &ehi4
+**	BISX.W	&ehi5, &lhi2
+**	BISX.W	&ehi6, &hi2
+**	BIS.W	&lhi4, &lhi3
+**	BISX.W	&hi3, &lhi5
+**	BISX.W	&hi5, &hi4
+**	BISX.W	&lhi6, &hi6
+** ...
+*/
+void
+iorhi3 (void)
+{
+  IOR_INSN(hi)
+}
+
+/*
+** iorpsi3:
+** ...
+**	BISX.A	&epsi2, &epsi1
+**	BISX.A	&lpsi1, &epsi3
+**	BISX.A	&psi1, &epsi4
+**	BISX.A	&epsi5, &lpsi2
+**	BISX.A	&epsi6, &psi2
+**	BISX.A	&lpsi4, &lpsi3
+**	BISX.A	&psi3, &lpsi5
+**	BISX.A	&psi5, &psi4
+**	BISX.A	&lpsi6, &psi6
+** ...
+*/
+void
+iorpsi3 (void)
+{
+  IOR_INSN(psi)
+}
+
+/* There is no specific iorsi3 pattern defined for msp430, but we check
+   this is synthesized correctly anyway.  */
+/*
+** iorsi3: { target msp430_region_lower }
+** ...
+**	BISX.W	&esi2, &esi1
+**	BISX.W	&esi2\+2, &esi1\+2
+**	BISX.W	&lsi1, &esi3
+**	BISX.W	&lsi1\+2, &esi3\+2
+**	BISX.W	&si1, &esi4
+**	BISX.W	&si1\+2, &esi4\+2
+**	BISX.W	&esi5, &lsi2
+**	BISX.W	&esi5\+2, &lsi2\+2
+**	BISX.W	&esi6, &si2
+**	BISX.W	&esi6\+2, &si2\+2
+**	BIS.W	&lsi4, &lsi3
+**	BIS.W	&lsi4\+2, &lsi3\+2
+**	BIS.W	&si3, &lsi5
+**	BIS.W	&si3\+2, &lsi5\+2
+**	BIS.W	&si5, &si4
+**	BIS.W	&si5\+2, &si4\+2
+**	BIS.W	&lsi6, &si6
+**	BIS.W	&lsi6\+2, &si6\+2
+** ...
+*/
+/*
+** iorsi3: { target msp430_region_not_lower }
+** ...
+**	BISX.W	&esi2, &esi1
+**	BISX.W	&esi2\+2, &esi1\+2
+**	BISX.W	&lsi1, &esi3
+**	BISX.W	&lsi1\+2, &esi3\+2
+**	BISX.W	&si1, &esi4
+**	BISX.W	&si1\+2, &esi4\+2
+**	BISX.W	&esi5, &lsi2
+**	BISX.W	&esi5\+2, &lsi2\+2
+**	BISX.W	&esi6, &si2
+**	BISX.W	&esi6\+2, &si2\+2
+**	BIS.W	&lsi4, &lsi3
+**	BIS.W	&lsi4\+2, &lsi3\+2
+**	BISX.W	&si3, &lsi5
+**	BISX.W	&si3\+2, &lsi5\+2
+**	BISX.W	&si5, &si4
+**	BISX.W	&si5\+2, &si4\+2
+**	BISX.W	&lsi6, &si6
+**	BISX.W	&lsi6\+2, &si6\+2
+** ...
+*/
+void
+iorsi3 (void)
+{
+  IOR_INSN(si)
+}
+
+#define XOR_INSN(MODE) \
+  E_VAR(MODE,1) ^= E_VAR(MODE,2); \
+  E_VAR(MODE,3) ^= L_VAR(MODE,1); \
+  E_VAR(MODE,4) ^= VAR(MODE,1); \
+  L_VAR(MODE,2) ^= E_VAR(MODE,5); \
+  VAR(MODE,2) ^= E_VAR(MODE,6); \
+  L_VAR(MODE,3) ^= L_VAR(MODE,4); \
+  L_VAR(MODE,5) ^= VAR(MODE,3); \
+  VAR(MODE,4) ^= VAR(MODE,5); \
+  VAR(MODE,6) ^= L_VAR(MODE,6);
+
+/*
+** xorqi3: { target msp430_region_lower }
+** ...
+**	XORX.B	&eqi2, &eqi1
+**	XORX.B	&lqi1, &eqi3
+**	XORX.B	&qi1, &eqi4
+**	XORX.B	&eqi5, &lqi2
+**	XORX.B	&eqi6, &qi2
+**	XOR.B	&lqi4, &lqi3
+**	XOR.B	&qi3, &lqi5
+**	XOR.B	&qi5, &qi4
+**	XOR.B	&lqi6, &qi6
+** ...
+*/
+/*
+** xorqi3: { target msp430_region_not_lower }
+** ...
+**	XORX.B	&eqi2, &eqi1
+**	XORX.B	&lqi1, &eqi3
+**	XORX.B	&qi1, &eqi4
+**	XORX.B	&eqi5, &lqi2
+**	XORX.B	&eqi6, &qi2
+**	XOR.B	&lqi4, &lqi3
+**	XORX.B	&qi3, &lqi5
+**	XORX.B	&qi5, &qi4
+**	XORX.B	&lqi6, &qi6
+** ...
+*/
+void
+xorqi3 (void)
+{
+  XOR_INSN(qi)
+}
+
+/*
+** xorhi3: { target msp430_region_lower }
+** ...
+**	XORX.W	&ehi2, &ehi1
+**	XORX.W	&lhi1, &ehi3
+**	XORX.W	&hi1, &ehi4
+**	XORX.W	&ehi5, &lhi2
+**	XORX.W	&ehi6, &hi2
+**	XOR.W	&lhi4, &lhi3
+**	XOR.W	&hi3, &lhi5
+**	XOR.W	&hi5, &hi4
+**	XOR.W	&lhi6, &hi6
+** ...
+*/
+/*
+** xorhi3: { target msp430_region_not_lower }
+** ...
+**	XORX.W	&ehi2, &ehi1
+**	XORX.W	&lhi1, &ehi3
+**	XORX.W	&hi1, &ehi4
+**	XORX.W	&ehi5, &lhi2
+**	XORX.W	&ehi6, &hi2
+**	XOR.W	&lhi4, &lhi3
+**	XORX.W	&hi3, &lhi5
+**	XORX.W	&hi5, &hi4
+**	XORX.W	&lhi6, &hi6
+** ...
+*/
+void
+xorhi3 (void)
+{
+  XOR_INSN(hi)
+}
+
+/*
+** xorpsi3:
+** ...
+**	XORX.A	&epsi2, &epsi1
+**	XORX.A	&lpsi1, &epsi3
+**	XORX.A	&psi1, &epsi4
+**	XORX.A	&epsi5, &lpsi2
+**	XORX.A	&epsi6, &psi2
+**	XORX.A	&lpsi4, &lpsi3
+**	XORX.A	&psi3, &lpsi5
+**	XORX.A	&psi5, &psi4
+**	XORX.A	&lpsi6, &psi6
+** ...
+*/
+void
+xorpsi3 (void)
+{
+  XOR_INSN(psi)
+}
+
+/* There is no specific xorsi3 pattern defined for msp430, but we check
+   this is synthesized correctly anyway.  */
+/*
+** xorsi3: { target msp430_region_lower }
+** ...
+**	XORX.W	&esi2, &esi1
+**	XORX.W	&esi2\+2, &esi1\+2
+**	XORX.W	&lsi1, &esi3
+**	XORX.W	&lsi1\+2, &esi3\+2
+**	XORX.W	&si1, &esi4
+**	XORX.W	&si1\+2, &esi4\+2
+**	XORX.W	&esi5, &lsi2
+**	XORX.W	&esi5\+2, &lsi2\+2
+**	XORX.W	&esi6, &si2
+**	XORX.W	&esi6\+2, &si2\+2
+**	XOR.W	&lsi4, &lsi3
+**	XOR.W	&lsi4\+2, &lsi3\+2
+**	XOR.W	&si3, &lsi5
+**	XOR.W	&si3\+2, &lsi5\+2
+**	XOR.W	&si5, &si4
+**	XOR.W	&si5\+2, &si4\+2
+**	XOR.W	&lsi6, &si6
+**	XOR.W	&lsi6\+2, &si6\+2
+** ...
+*/
+/*
+** xorsi3: { target msp430_region_not_lower }
+** ...
+**	XORX.W	&esi2, &esi1
+**	XORX.W	&esi2\+2, &esi1\+2
+**	XORX.W	&lsi1, &esi3
+**	XORX.W	&lsi1\+2, &esi3\+2
+**	XORX.W	&si1, &esi4
+**	XORX.W	&si1\+2, &esi4\+2
+**	XORX.W	&esi5, &lsi2
+**	XORX.W	&esi5\+2, &lsi2\+2
+**	XORX.W	&esi6, &si2
+**	XORX.W	&esi6\+2, &si2\+2
+**	XOR.W	&lsi4, &lsi3
+**	XOR.W	&lsi4\+2, &lsi3\+2
+**	XORX.W	&si3, &lsi5
+**	XORX.W	&si3\+2, &lsi5\+2
+**	XORX.W	&si5, &si4
+**	XORX.W	&si5\+2, &si4\+2
+**	XORX.W	&lsi6, &si6
+**	XORX.W	&lsi6\+2, &si6\+2
+** ...
+*/
+void
+xorsi3 (void)
+{
+  XOR_INSN(si)
+}
+
+#define DO1 \
+{ \
+  qi z; \
+  z += use_qi(z); \
+  use_qi(z); \
+}
+
+#define DO2 \
+{ \
+  hi z; \
+  z += use_hi(z); \
+  use_hi(z); \
+}
+
+#define DO3 \
+{ \
+  si z; \
+  z += use_si(z); \
+  use_si(z); \
+}
+
+#define CBRANCH_INSN(MODE) \
+  if (E_VAR(MODE,1) == E_VAR(MODE,2)) \
+    DO1 \
+  else if (E_VAR(MODE,3) == L_VAR(MODE,1)) \
+    DO2 \
+  else if (E_VAR(MODE,4) == VAR(MODE,1)) \
+    DO1 \
+  else if (L_VAR(MODE,2) == E_VAR(MODE,5)) \
+    DO2 \
+  else if (VAR(MODE,2) == E_VAR(MODE,6)) \
+    DO1 \
+  else if (L_VAR(MODE,3) == L_VAR(MODE,4)) \
+    DO2 \
+  else if (L_VAR(MODE,5) == VAR(MODE,3)) \
+    DO2 \
+  else if (VAR(MODE,4) == VAR(MODE,5)) \
+    DO1 \
+  else if (VAR(MODE,6) == L_VAR(MODE,6)) \
+    DO2
+
+/*
+** cbranchqi4_real: { target msp430_region_lower }
+** ...
+**	CMPX.B	&eqi2, &eqi1 { JEQ	.L[0-9]+
+**	CMPX.B	&lqi1, &eqi3 { JEQ	.L[0-9]+
+**	CMPX.B	&qi1, &eqi4 { JEQ	.L[0-9]+
+**	CMPX.B	&eqi5, &lqi2 { JEQ	.L[0-9]+
+**	CMPX.B	&eqi6, &qi2 { JEQ	.L[0-9]+
+**	CMP.B	&lqi4, &lqi3 { JEQ	.L[0-9]+
+**	CMP.B	&qi3, &lqi5 { JEQ	.L[0-9]+
+**	CMP.B	&qi5, &qi4 { JEQ	.L[0-9]+
+**	CMP.B	&lqi6, &qi6 { JNE	.L[0-9]+
+** ...
+*/
+/*
+** cbranchqi4_real: { target msp430_region_not_lower }
+** ...
+**	CMPX.B	&eqi2, &eqi1 { JEQ	.L[0-9]+
+**	CMPX.B	&lqi1, &eqi3 { JEQ	.L[0-9]+
+**	CMPX.B	&qi1, &eqi4 { JEQ	.L[0-9]+
+**	CMPX.B	&eqi5, &lqi2 { JEQ	.L[0-9]+
+**	CMPX.B	&eqi6, &qi2 { JEQ	.L[0-9]+
+**	CMP.B	&lqi4, &lqi3 { JEQ	.L[0-9]+
+**	CMPX.B	&qi3, &lqi5 { JEQ	.L[0-9]+
+**	CMPX.B	&qi5, &qi4 { JEQ	.L[0-9]+
+**	CMPX.B	&lqi6, &qi6 { JNE	.L[0-9]+
+** ...
+*/
+void
+cbranchqi4_real (void)
+{
+  CBRANCH_INSN(qi)
+}
+
+/*
+** cbranchhi4_real: { target msp430_region_lower }
+** ...
+**	CMPX.W	&ehi2, &ehi1 { JEQ	.L[0-9]+
+**	CMPX.W	&lhi1, &ehi3 { JEQ	.L[0-9]+
+**	CMPX.W	&hi1, &ehi4 { JEQ	.L[0-9]+
+**	CMPX.W	&ehi5, &lhi2 { JEQ	.L[0-9]+
+**	CMPX.W	&ehi6, &hi2 { JEQ	.L[0-9]+
+**	CMP.W	&lhi4, &lhi3 { JEQ	.L[0-9]+
+**	CMP.W	&hi3, &lhi5 { JEQ	.L[0-9]+
+**	CMP.W	&hi5, &hi4 { JEQ	.L[0-9]+
+**	CMP.W	&lhi6, &hi6 { JNE	.L[0-9]+
+** ...
+*/
+/*
+** cbranchhi4_real: { target msp430_region_not_lower }
+** ...
+**	CMPX.W	&ehi2, &ehi1 { JEQ	.L[0-9]+
+**	CMPX.W	&lhi1, &ehi3 { JEQ	.L[0-9]+
+**	CMPX.W	&hi1, &ehi4 { JEQ	.L[0-9]+
+**	CMPX.W	&ehi5, &lhi2 { JEQ	.L[0-9]+
+**	CMPX.W	&ehi6, &hi2 { JEQ	.L[0-9]+
+**	CMP.W	&lhi4, &lhi3 { JEQ	.L[0-9]+
+**	CMPX.W	&hi3, &lhi5 { JEQ	.L[0-9]+
+**	CMPX.W	&hi5, &hi4 { JEQ	.L[0-9]+
+**	CMPX.W	&lhi6, &hi6 { JNE	.L[0-9]+
+** ...
+*/
+void
+cbranchhi4_real (void)
+{
+  CBRANCH_INSN(hi)
+}
+
+/* There is no specific cbranchsi4_real pattern defined for msp430, but we
+   check this is synthesized correctly anyway.  */
+/*
+** cbranchsi4_real: { target msp430_region_lower }
+** ...
+**	CMPX.W	&esi2, &esi1 { JEQ	.L[0-9]+
+**	CMPX.W	&lsi1, &esi3 { JEQ	.L[0-9]+
+**	CMPX.W	&si1, &esi4 { JEQ	.L[0-9]+
+**	CMPX.W	&esi5, &lsi2 { JEQ	.L[0-9]+
+**	CMPX.W	&esi6, &si2 { JEQ	.L[0-9]+
+**	CMP.W	&lsi4, &lsi3 { JEQ	.L[0-9]+
+**	CMP.W	&si3, &lsi5 { JEQ	.L[0-9]+
+**	CMP.W	&si5, &si4 { JEQ	.L[0-9]+
+**	CMP.W	&lsi6, &si6 { JNE	.L[0-9]+
+**	CMP.W	&lsi6\+2, &si6\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&esi2\+2, &esi1\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&lsi1\+2, &esi3\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&si1\+2, &esi4\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&esi5\+2, &lsi2\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&esi6\+2, &si2\+2 { JNE	.L[0-9]+
+** ...
+**	CMP.W	&lsi4\+2, &lsi3\+2 { JNE	.L[0-9]+
+** ...
+**	CMP.W	&si3\+2, &lsi5\+2 { JNE	.L[0-9]+
+** ...
+**	CMP.W	&si5\+2, &si4\+2 { JNE	.L[0-9]+
+** ...
+*/
+/*
+** cbranchsi4_real: { target msp430_region_not_lower }
+** ...
+**	CMPX.W	&esi2, &esi1 { JEQ	.L[0-9]+
+**	CMPX.W	&lsi1, &esi3 { JEQ	.L[0-9]+
+**	CMPX.W	&si1, &esi4 { JEQ	.L[0-9]+
+**	CMPX.W	&esi5, &lsi2 { JEQ	.L[0-9]+
+**	CMPX.W	&esi6, &si2 { JEQ	.L[0-9]+
+**	CMP.W	&lsi4, &lsi3 { JEQ	.L[0-9]+
+**	CMPX.W	&si3, &lsi5 { JEQ	.L[0-9]+
+**	CMPX.W	&si5, &si4 { JEQ	.L[0-9]+
+**	CMPX.W	&lsi6, &si6 { JNE	.L[0-9]+
+**	CMPX.W	&lsi6\+2, &si6\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&esi2\+2, &esi1\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&lsi1\+2, &esi3\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&si1\+2, &esi4\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&esi5\+2, &lsi2\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&esi6\+2, &si2\+2 { JNE	.L[0-9]+
+** ...
+**	CMP.W	&lsi4\+2, &lsi3\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&si3\+2, &lsi5\+2 { JNE	.L[0-9]+
+** ...
+**	CMPX.W	&si5\+2, &si4\+2 { JNE	.L[0-9]+
+** ...
+*/
+void
+cbranchsi4_real (void)
+{
+  CBRANCH_INSN(si)
+}
+
+#define CBRANCH_REVERSE_INSN(MODE) \
+  if (E_VAR(MODE,1) > E_VAR(MODE,2)) \
+    DO1 \
+  else if (E_VAR(MODE,3) > L_VAR(MODE,1)) \
+    DO2 \
+  else if (E_VAR(MODE,4) > VAR(MODE,1)) \
+    DO1 \
+  else if (L_VAR(MODE,2) > E_VAR(MODE,5)) \
+    DO2 \
+  else if (VAR(MODE,2) > E_VAR(MODE,6)) \
+    DO1 \
+  else if (L_VAR(MODE,3) > L_VAR(MODE,4)) \
+    DO2 \
+  else if (L_VAR(MODE,5) > VAR(MODE,3)) \
+    DO2 \
+  else if (VAR(MODE,4) > VAR(MODE,5)) \
+    DO1 \
+  else if (VAR(MODE,6) > L_VAR(MODE,6)) \
+    DO2
+
+/*
+** cbranchqi4_reversed: { target msp430_region_lower }
+** ...
+**	CMPX.B	&eqi1, &eqi2 { JLO	.L[0-9]+
+**	CMPX.B	&eqi3, &lqi1 { JLO	.L[0-9]+
+**	CMPX.B	&eqi4, &qi1 { JLO	.L[0-9]+
+**	CMPX.B	&lqi2, &eqi5 { JLO	.L[0-9]+
+**	CMPX.B	&qi2, &eqi6 { JLO	.L[0-9]+
+**	CMP.B	&lqi3, &lqi4 { JLO	.L[0-9]+
+**	CMP.B	&lqi5, &qi3 { JLO	.L[0-9]+
+**	CMP.B	&qi4, &qi5 { JLO	.L[0-9]+
+**	CMP.B	&qi6, &lqi6 { JHS	.L[0-9]+
+** ...
+*/
+/*
+** cbranchqi4_reversed: { target msp430_region_not_lower }
+** ...
+**	CMPX.B	&eqi1, &eqi2 { JLO	.L[0-9]+
+**	CMPX.B	&eqi3, &lqi1 { JLO	.L[0-9]+
+**	CMPX.B	&eqi4, &qi1 { JLO	.L[0-9]+
+**	CMPX.B	&lqi2, &eqi5 { JLO	.L[0-9]+
+**	CMPX.B	&qi2, &eqi6 { JLO	.L[0-9]+
+**	CMP.B	&lqi3, &lqi4 { JLO	.L[0-9]+
+**	CMPX.B	&lqi5, &qi3 { JLO	.L[0-9]+
+**	CMPX.B	&qi4, &qi5 { JLO	.L[0-9]+
+**	CMPX.B	&qi6, &lqi6 { JHS	.L[0-9]+
+** ...
+*/
+void
+cbranchqi4_reversed (void)
+{
+  CBRANCH_REVERSE_INSN(qi)
+}
+
+/*
+** cbranchhi4_reversed: { target msp430_region_lower }
+** ...
+**	CMPX.W	&ehi1, &ehi2 { JL	.L[0-9]+
+**	CMPX.W	&ehi3, &lhi1 { JL	.L[0-9]+
+**	CMPX.W	&ehi4, &hi1 { JL	.L[0-9]+
+**	CMPX.W	&lhi2, &ehi5 { JL	.L[0-9]+
+**	CMPX.W	&hi2, &ehi6 { JL	.L[0-9]+
+**	CMP.W	&lhi3, &lhi4 { JL	.L[0-9]+
+**	CMP.W	&lhi5, &hi3 { JL	.L[0-9]+
+**	CMP.W	&hi4, &hi5 { JL	.L[0-9]+
+**	CMP.W	&hi6, &lhi6 { JGE	.L[0-9]+
+** ...
+*/
+/*
+** cbranchhi4_reversed: { target msp430_region_not_lower }
+** ...
+**	CMPX.W	&ehi1, &ehi2 { JL	.L[0-9]+
+**	CMPX.W	&ehi3, &lhi1 { JL	.L[0-9]+
+**	CMPX.W	&ehi4, &hi1 { JL	.L[0-9]+
+**	CMPX.W	&lhi2, &ehi5 { JL	.L[0-9]+
+**	CMPX.W	&hi2, &ehi6 { JL	.L[0-9]+
+**	CMP.W	&lhi3, &lhi4 { JL	.L[0-9]+
+**	CMPX.W	&lhi5, &hi3 { JL	.L[0-9]+
+**	CMPX.W	&hi4, &hi5 { JL	.L[0-9]+
+**	CMPX.W	&hi6, &lhi6 { JGE	.L[0-9]+
+** ...
+*/
+void
+cbranchhi4_reversed (void)
+{
+  CBRANCH_REVERSE_INSN(hi)
+}
+
+/* There is no specific cbranchsi4_reversed pattern defined for msp430, but
+   we check this is synthesized correctly anyway.
+   This output assembly for this one is quite long and convoluted so we only
+   check part of it.  */
+/*
+** cbranchsi4_reversed: { target msp430_region_lower }
+** ...
+**	CMPX.W	&esi1\+2, &esi2\+2 { JL	.L[0-9]+
+**	CMPX.W	&esi2\+2, &esi1\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&esi3\+2, &lsi1\+2 { JL	.L[0-9]+
+**	CMPX.W	&lsi1\+2, &esi3\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&esi4\+2, &si1\+2 { JL	.L[0-9]+
+**	CMPX.W	&si1\+2, &esi4\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&lsi2\+2, &esi5\+2 { JL	.L[0-9]+
+**	CMPX.W	&esi5\+2, &lsi2\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&si2\+2, &esi6\+2 { JL	.L[0-9]+
+**	CMPX.W	&esi6\+2, &si2\+2 { JEQ	.L[0-9]+
+**	CMP.W	&lsi3\+2, &lsi4\+2 { JL	.L[0-9]+
+**	CMP.W	&lsi4\+2, &lsi3\+2 { JEQ	.L[0-9]+
+**	CMP.W	&lsi5\+2, &si3\+2 { JL	.L[0-9]+
+**	CMP.W	&si3\+2, &lsi5\+2 { JEQ	.L[0-9]+
+**	CMP.W	&si4\+2, &si5\+2 { JL	.L[0-9]+
+**	CMP.W	&si5\+2, &si4\+2 { JEQ	.L[0-9]+
+**	CMP.W	&si6\+2, &lsi6\+2 { JL	.L[0-9]+
+**	CMP.W	&lsi6\+2, &si6\+2 { JNE	.L[0-9]+
+** ...
+*/
+/*
+** cbranchsi4_reversed: { target msp430_region_not_lower }
+** ...
+**	CMPX.W	&esi1\+2, &esi2\+2 { JL	.L[0-9]+
+**	CMPX.W	&esi2\+2, &esi1\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&esi3\+2, &lsi1\+2 { JL	.L[0-9]+
+**	CMPX.W	&lsi1\+2, &esi3\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&esi4\+2, &si1\+2 { JL	.L[0-9]+
+**	CMPX.W	&si1\+2, &esi4\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&lsi2\+2, &esi5\+2 { JL	.L[0-9]+
+**	CMPX.W	&esi5\+2, &lsi2\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&si2\+2, &esi6\+2 { JL	.L[0-9]+
+**	CMPX.W	&esi6\+2, &si2\+2 { JEQ	.L[0-9]+
+**	CMP.W	&lsi3\+2, &lsi4\+2 { JL	.L[0-9]+
+**	CMP.W	&lsi4\+2, &lsi3\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&lsi5\+2, &si3\+2 { JL	.L[0-9]+
+**	CMPX.W	&si3\+2, &lsi5\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&si4\+2, &si5\+2 { JL	.L[0-9]+
+**	CMPX.W	&si5\+2, &si4\+2 { JEQ	.L[0-9]+
+**	CMPX.W	&si6\+2, &lsi6\+2 { JL	.L[0-9]+
+**	CMPX.W	&lsi6\+2, &si6\+2 { JNE	.L[0-9]+
+** ...
+*/
+void
+cbranchsi4_reversed (void)
+{
+  CBRANCH_REVERSE_INSN(si)
+}
+
+#define BITBRANCH_NE_INSN(MODE) \
+  if (E_VAR(MODE,1) & E_VAR(MODE,2)) \
+    DO1 \
+  else if (E_VAR(MODE,3) & L_VAR(MODE,1)) \
+    DO2 \
+  else if (E_VAR(MODE,4) & VAR(MODE,1)) \
+    DO1 \
+  else if (L_VAR(MODE,2) & E_VAR(MODE,5)) \
+    DO2 \
+  else if (VAR(MODE,2) & E_VAR(MODE,6)) \
+    DO1 \
+  else if (L_VAR(MODE,3) & L_VAR(MODE,4)) \
+    DO2 \
+  else if (L_VAR(MODE,5) & VAR(MODE,3)) \
+    DO2 \
+  else if (VAR(MODE,4) & VAR(MODE,5)) \
+    DO1 \
+  else if (VAR(MODE,6) & L_VAR(MODE,6)) \
+    DO2
+/*
+** bitbranchqi4: { target msp430_region_lower }
+** ...
+**	BITX.B	&eqi2, &eqi1 { JNE	.L[0-9]+
+**	BITX.B	&lqi1, &eqi3 { JNE	.L[0-9]+
+**	BITX.B	&qi1, &eqi4 { JNE	.L[0-9]+
+**	BITX.B	&eqi5, &lqi2 { JNE	.L[0-9]+
+**	BITX.B	&eqi6, &qi2 { JNE	.L[0-9]+
+**	BIT.B	&lqi4, &lqi3 { JNE	.L[0-9]+
+**	BIT.B	&qi3, &lqi5 { JNE	.L[0-9]+
+**	BIT.B	&qi5, &qi4 { JNE	.L[0-9]+
+**	BIT.B	&lqi6, &qi6 { JEQ	.L[0-9]+
+** ...
+*/
+/*
+** bitbranchqi4: { target msp430_region_not_lower }
+** ...
+**	BITX.B	&eqi2, &eqi1 { JNE	.L[0-9]+
+**	BITX.B	&lqi1, &eqi3 { JNE	.L[0-9]+
+**	BITX.B	&qi1, &eqi4 { JNE	.L[0-9]+
+**	BITX.B	&eqi5, &lqi2 { JNE	.L[0-9]+
+**	BITX.B	&eqi6, &qi2 { JNE	.L[0-9]+
+**	BIT.B	&lqi4, &lqi3 { JNE	.L[0-9]+
+**	BITX.B	&qi3, &lqi5 { JNE	.L[0-9]+
+**	BITX.B	&qi5, &qi4 { JNE	.L[0-9]+
+**	BITX.B	&lqi6, &qi6 { JEQ	.L[0-9]+
+** ...
+*/
+void
+bitbranchqi4 (void)
+{
+  BITBRANCH_NE_INSN(qi)
+}
+
+/*
+** bitbranchhi4: { target msp430_region_lower }
+** ...
+**	BITX.W	&ehi2, &ehi1 { JNE	.L[0-9]+
+**	BITX.W	&lhi1, &ehi3 { JNE	.L[0-9]+
+**	BITX.W	&hi1, &ehi4 { JNE	.L[0-9]+
+**	BITX.W	&ehi5, &lhi2 { JNE	.L[0-9]+
+**	BITX.W	&ehi6, &hi2 { JNE	.L[0-9]+
+**	BIT.W	&lhi4, &lhi3 { JNE	.L[0-9]+
+**	BIT.W	&hi3, &lhi5 { JNE	.L[0-9]+
+**	BIT.W	&hi5, &hi4 { JNE	.L[0-9]+
+**	BIT.W	&lhi6, &hi6 { JEQ	.L[0-9]+
+** ...
+*/
+/*
+** bitbranchhi4: { target msp430_region_not_lower }
+** ...
+**	BITX.W	&ehi2, &ehi1 { JNE	.L[0-9]+
+**	BITX.W	&lhi1, &ehi3 { JNE	.L[0-9]+
+**	BITX.W	&hi1, &ehi4 { JNE	.L[0-9]+
+**	BITX.W	&ehi5, &lhi2 { JNE	.L[0-9]+
+**	BITX.W	&ehi6, &hi2 { JNE	.L[0-9]+
+**	BIT.W	&lhi4, &lhi3 { JNE	.L[0-9]+
+**	BITX.W	&hi3, &lhi5 { JNE	.L[0-9]+
+**	BITX.W	&hi5, &hi4 { JNE	.L[0-9]+
+**	BITX.W	&lhi6, &hi6 { JEQ	.L[0-9]+
+** ...
+*/
+void
+bitbranchhi4 (void)
+{
+  BITBRANCH_NE_INSN(hi)
+}
+
+/*
+** bitbranchpsi4:
+** ...
+**	BITX.A	&epsi2, &epsi1 { JNE	.L[0-9]+
+**	BITX.A	&lpsi1, &epsi3 { JNE	.L[0-9]+
+**	BITX.A	&psi1, &epsi4 { JNE	.L[0-9]+
+**	BITX.A	&epsi5, &lpsi2 { JNE	.L[0-9]+
+**	BITX.A	&epsi6, &psi2 { JNE	.L[0-9]+
+**	BITX.A	&lpsi4, &lpsi3 { JNE	.L[0-9]+
+**	BITX.A	&psi3, &lpsi5 { JNE	.L[0-9]+
+**	BITX.A	&psi5, &psi4 { JNE	.L[0-9]+
+**	BITX.A	&lpsi6, &psi6 { JEQ	.L[0-9]+
+** ...
+*/
+void
+bitbranchpsi4 (void)
+{
+  BITBRANCH_NE_INSN(psi)
+}
+
+
+/* "bitbranch" using SImode operands is omitted since the resulting assembly
+   uses many temporary registers to perform the bitwise and comparison
+   operations.  */
diff --git a/gcc/testsuite/gcc.target/msp430/data-attributes-2.c b/gcc/testsuite/gcc.target/msp430/data-attributes-2.c
index d049194425d..6113e99f052 100644
--- a/gcc/testsuite/gcc.target/msp430/data-attributes-2.c
+++ b/gcc/testsuite/gcc.target/msp430/data-attributes-2.c
@@ -27,7 +27,7 @@ int __attribute__((lower,upper)) lu = 20; /* { dg-warning "ignoring attribute 'u
 int __attribute__((lower,either)) le = 20; /* { dg-warning "ignoring attribute 'either' because it conflicts with attribute 'lower'" } */
 int __attribute__((lower,persistent)) lp = 20; /* { dg-warning "ignoring attribute 'persistent' because it conflicts with attribute 'lower'" } */
 int __attribute__((lower,noinit)) ln; /* { dg-warning "ignoring attribute 'noinit' because it conflicts with attribute 'lower'" } */
-int __attribute__((lower,section(".data.foo"))) ls = 30; /* { dg-warning "ignoring attribute 'section' because it conflicts with attribute 'lower'" } */
+int __attribute__((lower,section(".data.foo"))) ls = 30;
 
 int __attribute__((upper)) u = 20;
 int __attribute__((upper,lower)) ul = 20; /* { dg-warning "ignoring attribute 'lower' because it conflicts with attribute 'upper'" } */
@@ -46,6 +46,6 @@ int __attribute__((either,section(".data.foo"))) es = 30; /* { dg-warning "ignor
 int __attribute__((section(".data.foo"))) s = 20;
 int __attribute__((section(".data.foo"),noinit)) sn; /* { dg-warning "ignoring attribute 'noinit' because it conflicts with attribute 'section'" } */
 int __attribute__((section(".data.foo"),persistent)) sp = 20; /* { dg-warning "ignoring attribute 'persistent' because it conflicts with attribute 'section'" } */
-int __attribute__((section(".data.foo"),lower)) sl = 2; /* { dg-warning "ignoring attribute 'lower' because it conflicts with attribute 'section'" } */
+int __attribute__((section(".data.foo"),lower)) sl = 2;
 int __attribute__((section(".data.foo"),upper)) su = 20; /* { dg-warning "ignoring attribute 'upper' because it conflicts with attribute 'section'" } */
 int __attribute__((section(".data.foo"),either)) se = 2; /* { dg-warning "ignoring attribute 'either' because it conflicts with attribute 'section'" } */
diff --git a/gcc/testsuite/gcc.target/msp430/msp430.exp b/gcc/testsuite/gcc.target/msp430/msp430.exp
index b97f4dcdc16..bcc56861abc 100644
--- a/gcc/testsuite/gcc.target/msp430/msp430.exp
+++ b/gcc/testsuite/gcc.target/msp430/msp430.exp
@@ -36,6 +36,14 @@ proc check_effective_target_msp430_mlarge_selected { } {
     return [check-flags [list "" { *-*-* } { "-mlarge" } { "" } ]]
 }
 
+proc check_effective_target_msp430_region_not_lower { } {
+    return [check-flags [list "" { *-*-* } { "-mdata-region=none" "-mdata-region=upper" "-mdata-region=either" } { "" } ]]
+}
+
+proc check_effective_target_msp430_region_lower { } {
+    return [check-flags [list "" { *-*-* } { "*" } { "-mdata-region=none" "-mdata-region=upper" "-mdata-region=either" } ]]
+}
+
 proc check_effective_target_msp430_hwmul_not_none { } {
     return [check-flags [list "" { *-*-* } \
     { "-mhwmult=16bit" "-mhwmult=32bit" "-mhwmult=f5series" } { "" } ]]
diff --git a/gcc/testsuite/gcc.target/msp430/object-attributes-430.c b/gcc/testsuite/gcc.target/msp430/object-attributes-430.c
new file mode 100644
index 00000000000..4296be22edb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/object-attributes-430.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430x" "-mlarge" } { "" } } */
+/* { dg-options "-mcpu=msp430" } */
+/* { dg-final { scan-assembler ".mspabi_attribute 4, 1" } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 6, 1" } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 8, 1" } } */
+/* { dg-final { scan-assembler ".gnu_attribute 4, 1" } } */
+
+int
+main (void)
+{
+  while (1);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/object-attributes-default.c b/gcc/testsuite/gcc.target/msp430/object-attributes-default.c
new file mode 100644
index 00000000000..39c1f3eab44
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/object-attributes-default.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-final { scan-assembler ".mspabi_attribute 4, 1" { target msp430_430_selected } } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 4, 2" { target msp430_430x_selected } } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 6, 1" { target { ! msp430_mlarge_selected } } } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 8, 1" { target { ! msp430_mlarge_selected } } } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 6, 2" { target msp430_mlarge_selected } } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 8, 2" { target msp430_mlarge_selected } } } */
+/* { dg-final { scan-assembler ".gnu_attribute 4, 1" { target { ! msp430_region_not_lower } } } } */
+/* { dg-final { scan-assembler ".gnu_attribute 4, 2" { target msp430_region_not_lower } } } */
+
+int
+main (void)
+{
+  while (1);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/object-attributes-mlarge-any-region.c b/gcc/testsuite/gcc.target/msp430/object-attributes-mlarge-any-region.c
new file mode 100644
index 00000000000..2aa8bfb3663
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/object-attributes-mlarge-any-region.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430" "-mdata-region=lower" } { "" } } */
+/* { dg-options "-mlarge -mdata-region=none" } */
+/* { dg-final { scan-assembler ".mspabi_attribute 4, 2" } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 6, 2" } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 8, 2" } } */
+/* { dg-final { scan-assembler ".gnu_attribute 4, 2" } } */
+
+int
+main (void)
+{
+  while (1);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/object-attributes-mlarge.c b/gcc/testsuite/gcc.target/msp430/object-attributes-mlarge.c
new file mode 100644
index 00000000000..06738ea795a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/object-attributes-mlarge.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430" } { "" } } */
+/* { dg-options "-mlarge" } */
+/* { dg-final { scan-assembler ".mspabi_attribute 4, 2" } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 6, 2" } } */
+/* { dg-final { scan-assembler ".mspabi_attribute 8, 2" } } */
+/* { dg-final { scan-assembler ".gnu_attribute 4, 1" { target msp430_region_lower } } } */
+/* { dg-final { scan-assembler ".gnu_attribute 4, 2" { target { ! msp430_region_lower } } } } */
+
+int
+main (void)
+{
+  while (1);
+  return 0;
+}
-- 
2.17.1

>From 0385e82a5e543b98343b84c2f641e33a5e267eab Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <joze...@mittosystems.com>
Date: Fri, 16 Aug 2019 14:16:58 +0100
Subject: [PATCH] MSP430: Add new Data Region GNU object attribute

---
 bfd/elf32-msp430.c                            |  82 ++++++----
 binutils/readelf.c                            |  32 +++-
 gas/config/tc-msp430.c                        | 140 +++++++++++++++++-
 gas/doc/as.texi                               |  17 +++
 gas/doc/c-msp430.texi                         |  12 ++
 gas/testsuite/gas/msp430/attr-430-small-bad.d |   4 +
 gas/testsuite/gas/msp430/attr-430-small-bad.l |   4 +
 .../gas/msp430/attr-430-small-good.d          |   6 +
 gas/testsuite/gas/msp430/attr-430-small.s     |   3 +
 .../gas/msp430/attr-430x-large-any-bad.d      |   4 +
 .../gas/msp430/attr-430x-large-any-bad.l      |   4 +
 .../gas/msp430/attr-430x-large-any-good.d     |   6 +
 .../gas/msp430/attr-430x-large-any.s          |   4 +
 .../gas/msp430/attr-430x-large-lower-bad.d    |   4 +
 .../gas/msp430/attr-430x-large-lower-bad.l    |   3 +
 .../gas/msp430/attr-430x-large-lower-good.d   |   6 +
 .../gas/msp430/attr-430x-large-lower.s        |   4 +
 gas/testsuite/gas/msp430/msp430.exp           |   6 +
 include/elf/msp430.h                          |  42 ++++++
 ld/testsuite/ld-msp430-elf/attr-gnu-main.s    |   8 +
 ld/testsuite/ld-msp430-elf/attr-gnu-obj.s     |   2 +
 .../attr-gnu-region-lower-upper.d             |   6 +
 .../ld-msp430-elf/attr-gnu-region-lower.d     |  12 ++
 .../ld-msp430-elf/attr-gnu-region-upper.d     |  13 ++
 ld/testsuite/ld-msp430-elf/msp430-elf.exp     |  12 +-
 25 files changed, 402 insertions(+), 34 deletions(-)
 create mode 100644 gas/testsuite/gas/msp430/attr-430-small-bad.d
 create mode 100644 gas/testsuite/gas/msp430/attr-430-small-bad.l
 create mode 100644 gas/testsuite/gas/msp430/attr-430-small-good.d
 create mode 100644 gas/testsuite/gas/msp430/attr-430-small.s
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-any-bad.d
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-any-bad.l
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-any-good.d
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-any.s
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-lower-bad.d
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-lower-bad.l
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-lower-good.d
 create mode 100644 gas/testsuite/gas/msp430/attr-430x-large-lower.s
 create mode 100644 ld/testsuite/ld-msp430-elf/attr-gnu-main.s
 create mode 100644 ld/testsuite/ld-msp430-elf/attr-gnu-obj.s
 create mode 100644 ld/testsuite/ld-msp430-elf/attr-gnu-region-lower-upper.d
 create mode 100644 ld/testsuite/ld-msp430-elf/attr-gnu-region-lower.d
 create mode 100644 ld/testsuite/ld-msp430-elf/attr-gnu-region-upper.d

diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c
index fe5fd8ff8c..35cb71e3f1 100644
--- a/bfd/elf32-msp430.c
+++ b/bfd/elf32-msp430.c
@@ -2408,15 +2408,15 @@ data_model (int model)
     }
 }
 
-/* Merge MSPABI object attributes from IBFD into OBFD.
+/* Merge MSPABI and GNU object attributes from IBFD into OBFD.
    Raise an error if there are conflicting attributes.  */
 
 static bfd_boolean
-elf32_msp430_merge_mspabi_attributes (bfd *ibfd, struct bfd_link_info *info)
+elf32_msp430_merge_msp430_attributes (bfd *ibfd, struct bfd_link_info *info)
 {
   bfd *obfd = info->output_bfd;
-  obj_attribute *in_attr;
-  obj_attribute *out_attr;
+  obj_attribute *in_msp_attr, *in_gnu_attr;
+  obj_attribute *out_msp_attr, *out_gnu_attr;
   bfd_boolean result = TRUE;
   static bfd * first_input_bfd = NULL;
 
@@ -2435,45 +2435,47 @@ elf32_msp430_merge_mspabi_attributes (bfd *ibfd, struct bfd_link_info *info)
     {
       _bfd_elf_copy_obj_attributes (ibfd, obfd);
 
-      out_attr = elf_known_obj_attributes_proc (obfd);
+      out_msp_attr = elf_known_obj_attributes_proc (obfd);
 
       /* Use the Tag_null value to indicate that
 	 the attributes have been initialized.  */
-      out_attr[0].i = 1;
+      out_msp_attr[0].i = 1;
 
       first_input_bfd = ibfd;
       return TRUE;
     }
 
-  in_attr = elf_known_obj_attributes_proc (ibfd);
-  out_attr = elf_known_obj_attributes_proc (obfd);
+  in_msp_attr = elf_known_obj_attributes_proc (ibfd);
+  out_msp_attr = elf_known_obj_attributes_proc (obfd);
+  in_gnu_attr = elf_known_obj_attributes (ibfd) [OBJ_ATTR_GNU];
+  out_gnu_attr = elf_known_obj_attributes (obfd) [OBJ_ATTR_GNU];
 
   /* The ISAs must be the same.  */
-  if (in_attr[OFBA_MSPABI_Tag_ISA].i != out_attr[OFBA_MSPABI_Tag_ISA].i)
+  if (in_msp_attr[OFBA_MSPABI_Tag_ISA].i != out_msp_attr[OFBA_MSPABI_Tag_ISA].i)
     {
       _bfd_error_handler
 	/* xgettext:c-format */
 	(_("error: %pB uses %s instructions but %pB uses %s"),
-	 ibfd, isa_type (in_attr[OFBA_MSPABI_Tag_ISA].i),
-	 first_input_bfd, isa_type (out_attr[OFBA_MSPABI_Tag_ISA].i));
+	 ibfd, isa_type (in_msp_attr[OFBA_MSPABI_Tag_ISA].i),
+	 first_input_bfd, isa_type (out_msp_attr[OFBA_MSPABI_Tag_ISA].i));
       result = FALSE;
     }
 
   /* The code models must be the same.  */
-  if (in_attr[OFBA_MSPABI_Tag_Code_Model].i !=
-      out_attr[OFBA_MSPABI_Tag_Code_Model].i)
+  if (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i !=
+      out_msp_attr[OFBA_MSPABI_Tag_Code_Model].i)
     {
       _bfd_error_handler
 	/* xgettext:c-format */
 	(_("error: %pB uses the %s code model whereas %pB uses the %s code model"),
-	 ibfd, code_model (in_attr[OFBA_MSPABI_Tag_Code_Model].i),
-	 first_input_bfd, code_model (out_attr[OFBA_MSPABI_Tag_Code_Model].i));
+	 ibfd, code_model (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i),
+	 first_input_bfd, code_model (out_msp_attr[OFBA_MSPABI_Tag_Code_Model].i));
       result = FALSE;
     }
 
   /* The large code model is only supported by the MSP430X.  */
-  if (in_attr[OFBA_MSPABI_Tag_Code_Model].i == 2
-      && out_attr[OFBA_MSPABI_Tag_ISA].i != 2)
+  if (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i == 2
+      && out_msp_attr[OFBA_MSPABI_Tag_ISA].i != 2)
     {
       _bfd_error_handler
 	/* xgettext:c-format */
@@ -2483,41 +2485,65 @@ elf32_msp430_merge_mspabi_attributes (bfd *ibfd, struct bfd_link_info *info)
     }
 
   /* The data models must be the same.  */
-  if (in_attr[OFBA_MSPABI_Tag_Data_Model].i !=
-      out_attr[OFBA_MSPABI_Tag_Data_Model].i)
+  if (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i !=
+      out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i)
     {
       _bfd_error_handler
 	/* xgettext:c-format */
 	(_("error: %pB uses the %s data model whereas %pB uses the %s data model"),
-	 ibfd, data_model (in_attr[OFBA_MSPABI_Tag_Data_Model].i),
-	 first_input_bfd, data_model (out_attr[OFBA_MSPABI_Tag_Data_Model].i));
+	 ibfd, data_model (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i),
+	 first_input_bfd, data_model (out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i));
       result = FALSE;
     }
 
   /* The small code model requires the use of the small data model.  */
-  if (in_attr[OFBA_MSPABI_Tag_Code_Model].i == 1
-      && out_attr[OFBA_MSPABI_Tag_Data_Model].i != 1)
+  if (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i == 1
+      && out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i != 1)
     {
       _bfd_error_handler
 	/* xgettext:c-format */
 	(_("error: %pB uses the small code model but %pB uses the %s data model"),
 	 ibfd, first_input_bfd,
-	 data_model (out_attr[OFBA_MSPABI_Tag_Data_Model].i));
+	 data_model (out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i));
       result = FALSE;
     }
 
   /* The large data models are only supported by the MSP430X.  */
-  if (in_attr[OFBA_MSPABI_Tag_Data_Model].i > 1
-      && out_attr[OFBA_MSPABI_Tag_ISA].i != 2)
+  if (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i > 1
+      && out_msp_attr[OFBA_MSPABI_Tag_ISA].i != 2)
     {
       _bfd_error_handler
 	/* xgettext:c-format */
 	(_("error: %pB uses the %s data model but %pB only uses MSP430 instructions"),
-	 ibfd, data_model (in_attr[OFBA_MSPABI_Tag_Data_Model].i),
+	 ibfd, data_model (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i),
 	 first_input_bfd);
       result = FALSE;
     }
 
+  /* Just ignore the data region unless the large memory model is in use.
+     We have already checked that ibfd and obfd use the same memory model.  */
+  if ((in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i
+       == OFBA_MSPABI_Val_Code_Model_LARGE)
+      && (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i
+	  == OFBA_MSPABI_Val_Data_Model_LARGE))
+    {
+      /* We cannot allow "lower region only" to be linked with any other
+	 values (i.e. ANY or NONE).
+	 Before this attribute existed, "ANY" region was the default.  */
+      bfd_boolean ibfd_lower_region_used
+	= (in_gnu_attr[Tag_GNU_MSP430_Data_Region].i == Val_GNU_MSP430_Data_Region_Lower);
+      bfd_boolean obfd_lower_region_used
+	= (out_gnu_attr[Tag_GNU_MSP430_Data_Region].i == Val_GNU_MSP430_Data_Region_Lower);
+      if (ibfd_lower_region_used != obfd_lower_region_used)
+	{
+	  _bfd_error_handler
+	    (_("error: %pB can use the upper region for data, but %pB assumes data is exclusively in lower memory"),
+	     ibfd_lower_region_used ? obfd : ibfd,
+	     ibfd_lower_region_used ? ibfd : obfd);
+	  result = FALSE;
+	}
+    }
+
   return result;
 }
 
@@ -2536,7 +2562,7 @@ elf32_msp430_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 			       max (bfd_get_mach (ibfd), bfd_get_mach (obfd)));
 #undef max
 
-  return elf32_msp430_merge_mspabi_attributes (ibfd, info);
+  return elf32_msp430_merge_msp430_attributes (ibfd, info);
 }
 
 static bfd_boolean
diff --git a/binutils/readelf.c b/binutils/readelf.c
index cc168163b2..f890d56a13 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -15731,6 +15731,36 @@ display_msp430x_attribute (unsigned char * p,
   return p;
 }
 
+static unsigned char *
+display_msp430_gnu_attribute (unsigned char * p,
+			      unsigned int tag,
+			      const unsigned char * const end)
+{
+  if (tag == Tag_GNU_MSP430_Data_Region)
+    {
+      unsigned int len;
+      int val;
+
+      val = read_uleb128 (p, &len, end);
+      p += len;
+      printf ("  Tag_GNU_MSP430_Data_Region: ");
+
+      switch (val)
+	{
+	case Val_GNU_MSP430_Data_Region_Any:
+	  printf (_("Any Region\n"));
+	  break;
+	case Val_GNU_MSP430_Data_Region_Lower:
+	  printf (_("Lower Region Only\n"));
+	  break;
+	default:
+	  printf ("??? (%d)\n", val);
+	}
+      return p;
+    }
+  return display_tag_value (tag & 1, p, end);
+}
+
 struct riscv_attr_tag_t {
   const char *name;
   int tag;
@@ -19555,7 +19585,7 @@ process_arch_specific (Filedata * filedata)
     case EM_MSP430:
      return process_attributes (filedata, "mspabi", SHT_MSP430_ATTRIBUTES,
 				display_msp430x_attribute,
-				display_generic_attribute);
+				display_msp430_gnu_attribute);
 
     case EM_RISCV:
      return process_attributes (filedata, "riscv", SHT_RISCV_ATTRIBUTES,
diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c
index 5821b1c811..73e740a365 100644
--- a/gas/config/tc-msp430.c
+++ b/gas/config/tc-msp430.c
@@ -689,6 +689,8 @@ static bfd_boolean do_unknown_interrupt_nops = TRUE;
 static bfd_boolean move_data = FALSE;
 #define OPTION_DATA_REGION 'r'
 static bfd_boolean upper_data_region_in_use = FALSE;
+/* The default is to use the lower region only.  */
+static bfd_boolean lower_data_region_only = TRUE;
 
 enum
 {
@@ -1473,6 +1475,13 @@ md_parse_option (int c, const char * arg)
       if (strcmp (arg, "upper") == 0
 	  || strcmp (arg, "either") == 0)
 	upper_data_region_in_use = TRUE;
+      if (strcmp (arg, "upper") == 0
+	  || strcmp (arg, "either") == 0
+	  /* With data-region=none, the compiler has generated code assuming data
+	     could be in the upper region, but nothing has been explicitly placed
+	     there.  */
+	  || strcmp (arg, "none") == 0)
+	lower_data_region_only = FALSE;
       return 1;
     }
 
@@ -1598,6 +1607,114 @@ msp430_refsym (int arg ATTRIBUTE_UNUSED)
   (void) symbol_find_or_make (sym_name);
 }
 
+/* Handle a .mspabi_attribute or .gnu_attribute directive.
+   attr_type is 0 for .mspabi_attribute or 1 for .gnu_attribute.
+   This is only used for validating the attributes in the assembly file against
+   the options gas has been invoked with.  If the attributes and options are
+   compatible then we add the attributes to the assembly file in
+   msp430_md_end.  */
+static void
+msp430_object_attribute (int attr_type)
+{
+  char tag_name_s[32];
+  char tag_value_s[32];
+  int tag_name, tag_value;
+  /* First operand is the tag name, second is the tag value e.g.
+     ".mspabi_attribute 4, 2".  */
+  input_line_pointer = extract_operand (input_line_pointer, tag_name_s, 32);
+  input_line_pointer = extract_operand (input_line_pointer, tag_value_s, 32);
+  tag_name = atoi (tag_name_s);
+  tag_value = atoi (tag_value_s);
+  /* If the attribute directive is present, the tag_value should never be set
+     to 0.  */
+  if (tag_name == 0 || tag_value == 0)
+    as_bad (_("bad arguments \"%s\" and/or \"%s\" in %s directive"),
+	      tag_name_s, tag_value_s, (attr_type ? ".gnu_attribute"
+					: ".mspabi_attribute"));
+  else if (attr_type == 0)
+    /* Handle .mspabi_attribute.  */
+    switch (tag_name)
+      {
+      case OFBA_MSPABI_Tag_ISA:
+	switch (tag_value)
+	  {
+	  case OFBA_MSPABI_Val_ISA_MSP430:
+	    if (target_is_430x ())
+	      as_bad (_("file was compiled for the 430 ISA but the %s ISA is selected"),
+		      (target_is_430xv2 () ? "430X" : "430Xv2"));
+	    break;
+	  case OFBA_MSPABI_Val_ISA_MSP430X:
+	    if (!target_is_430x ())
+	      as_bad (_("file was compiled for the 430X ISA but the 430 ISA is selected"));
+	    break;
+	  default:
+	    as_bad (_("unknown MSPABI build attribute value '%d' for OFBA_MSPABI_Tag_ISA(%d) "
+		      "in .mspabi_attribute directive"), tag_value, OFBA_MSPABI_Tag_ISA);
+	    break;
+	  }
+	break;
+      case OFBA_MSPABI_Tag_Code_Model:
+	/* Fall through.  */
+      case OFBA_MSPABI_Tag_Data_Model:
+	/* FIXME: Might we want to set the memory model to large if the assembly
+	   file has the large model attribute, but -ml has not been passed?  */
+	switch (tag_value)
+	  {
+	  case OFBA_MSPABI_Val_Code_Model_SMALL: 
+	    if (large_model)
+	      as_bad (_("file was compiled for the small memory model, but the large memory "
+			"model is selected"));
+	    break;
+	  case OFBA_MSPABI_Val_Code_Model_LARGE: 
+	    if (!large_model)
+	      as_bad (_("file was compiled for the large memory model, but the small memory "
+			"model is selected"));
+	    break;
+	  default:
+	    as_bad (_("unknown MSPABI build attribute value '%d' for %s(%d) "
+		      "in .mspabi_attribute directive"), tag_value,
+		    (tag_name == OFBA_MSPABI_Tag_Code_Model
+		     ? "OFBA_MSPABI_Tag_Code_Model"
+		     : "OFBA_MSPABI_Tag_Data_Model"),
+		    (tag_name == OFBA_MSPABI_Tag_Code_Model
+		     ? OFBA_MSPABI_Tag_Code_Model
+		     : OFBA_MSPABI_Tag_Data_Model));
+	    break;
+	  }
+	break;
+      default:
+	as_bad (_("unknown MSPABI build attribute tag '%d' in .mspabi_attribute directive"), tag_name);
+	break;
+      }
+  else if (attr_type == 1)
+    /* Handle .gnu_attribute.  */
+    switch (tag_name)
+      {
+      case Tag_GNU_MSP430_Data_Region: 
+	/* This attribute is only applicable in the large memory model.  */
+	if (!large_model)
+	  break;
+	switch (tag_value)
+	  {
+	  case Val_GNU_MSP430_Data_Region_Lower:
+	    if (!lower_data_region_only)
+	      as_bad (_("file was compiled assuming all data will be in the lower memory region, "
+			"but the upper region is in use"));
+	    break;
+	  case Val_GNU_MSP430_Data_Region_Any:
+	    if (lower_data_region_only)
+	      as_bad (_("file was compiled assuming data could be in the upper memory region, "
+			"but the lower data region is exclusively in use"));
+	    break;
+	  default:
+	    as_bad (_("unknown GNU build attribute value '%d' for Tag_GNU_MSP430_Data_Region(%d) "
+		      "in .gnu_attribute directive"), tag_value, Tag_GNU_MSP430_Data_Region);
+	  }
+      }
+  else
+    as_bad (_("internal: unexpected argument '%d' to msp430_object_attribute"), attr_type);
+}
+
 const pseudo_typeS md_pseudo_table[] =
 {
   {"arch", msp430_set_arch, OPTION_MMCU},
@@ -1611,6 +1728,8 @@ const pseudo_typeS md_pseudo_table[] =
   {"refsym", msp430_refsym, 0},
   {"comm", msp430_comm, 0},
   {"lcomm", msp430_lcomm, 0},
+  {"mspabi_attribute", msp430_object_attribute, 0},
+  {"gnu_attribute", msp430_object_attribute, 1},
   {NULL, NULL, 0}
 };
 
@@ -4919,7 +5038,7 @@ msp430_fix_adjustable (struct fix *fixp ATTRIBUTE_UNUSED)
   return FALSE;
 }
 
-/* Set the contents of the .MSP430.attributes section.  */
+/* Set the contents of the .MSP430.attributes and .GNU.attributes sections.  */
 
 void
 msp430_md_end (void)
@@ -4936,14 +5055,27 @@ msp430_md_end (void)
 	as_warn (_(WARN_NOP_AT_EOF));
     }
 
+  /* We have already emitted an error if any of the following attributes
+     disagree with the attributes in the input assembly file.  See
+     msp430_object_attribute.  */
   bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_ISA,
-			     target_is_430x () ? 2 : 1);
+			     target_is_430x () ? OFBA_MSPABI_Val_ISA_MSP430X
+			     : OFBA_MSPABI_Val_ISA_MSP430);
 
   bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Code_Model,
-			     large_model ? 2 : 1);
+			     large_model ? OFBA_MSPABI_Val_Code_Model_LARGE
+			     : OFBA_MSPABI_Val_Code_Model_SMALL);
 
   bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Data_Model,
-			     large_model ? 2 : 1);
+			     large_model ? OFBA_MSPABI_Val_Code_Model_LARGE
+			     : OFBA_MSPABI_Val_Code_Model_SMALL);
+
+  /* The data region GNU attribute is ignored for the small memory model.  */
+  if (large_model)
+    bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, Tag_GNU_MSP430_Data_Region,
+			      lower_data_region_only
+			      ? Val_GNU_MSP430_Data_Region_Lower
+			      : Val_GNU_MSP430_Data_Region_Any );
 }
 
 /* Returns FALSE if there is a msp430 specific reason why the
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index b4598e86dc..93e80f9a43 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -7585,6 +7585,23 @@ The vector ABI used by this object file.  The value will be:
 @end itemize
 @end table
 
+@subsection MSP430 Attributes
+
+@table @r
+@item Tag_GNU_MSP430_Data_Region (4)
+The data region used by this object file.  The value will be:
+
+@itemize @bullet
+@item
+0 for files not using the large memory model.
+@item
+1 for files which have been compiled with the condition that all
+data is in the lower memory region, i.e. below address 0x10000.
+@item
+2 for files which allow data to be placed in the full 20-bit memory range.
+@end itemize
+@end table
+
 @node Defining New Object Attributes
 @section Defining New Object Attributes
 
diff --git a/gas/doc/c-msp430.texi b/gas/doc/c-msp430.texi
index dfa435a461..bc7007b2a4 100644
--- a/gas/doc/c-msp430.texi
+++ b/gas/doc/c-msp430.texi
@@ -322,6 +322,18 @@ exist purely for pulling in object files from archives.  Note that
 this reloc is not sufficient to prevent garbage collection; use a
 KEEP() directive in the linker file to preserve such objects.
 
+@cindex @code{mspabi_attribute} directive, MSP430
+@item .mspabi_attribute
+This directive tells the assembler what the MSPABI build attributes for this
+file are.  This is used for validating the command line options passed to
+the assembler against the options the original source file was compiled with.
+The expected format is:
+@samp{.mspabi_attribute tag_name, tag_value}
+For example, to set the tag @code{OFBA_MSPABI_Tag_ISA} to @code{MSP430X}:
+@samp{.mspabi_attribute 4, 2}
+
+See the @cite{MSP430 EABI, document slaa534} for the details on tag names and
+values.
 @end table
 
 @node MSP430 Opcodes
diff --git a/gas/testsuite/gas/msp430/attr-430-small-bad.d b/gas/testsuite/gas/msp430/attr-430-small-bad.d
new file mode 100644
index 0000000000..302ba57070
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430-small-bad.d
@@ -0,0 +1,4 @@
+#name: Error when 430 ISA and small memory model object attributes conflict with options
+#source: attr-430-small.s
+#as: -mdata-region=none -ml
+#error_output: attr-430-small-bad.l
diff --git a/gas/testsuite/gas/msp430/attr-430-small-bad.l b/gas/testsuite/gas/msp430/attr-430-small-bad.l
new file mode 100644
index 0000000000..c339ecfa35
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430-small-bad.l
@@ -0,0 +1,4 @@
+[^:]*: Assembler messages:
+[^:]*:1: Error: file was compiled for the 430 ISA but the 430X ISA is selected
+[^:]*:2: Error: file was compiled for the small memory model, but the large memory model is selected
+[^:]*:3: Error: file was compiled for the small memory model, but the large memory model is selected
diff --git a/gas/testsuite/gas/msp430/attr-430-small-good.d b/gas/testsuite/gas/msp430/attr-430-small-good.d
new file mode 100644
index 0000000000..5f3137f947
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430-small-good.d
@@ -0,0 +1,6 @@
+#name: 430 ISA and small memory model object attributes
+#source: attr-430-small.s
+#as: -mcpu=msp430
+#objdump: -t
+
+#pass
diff --git a/gas/testsuite/gas/msp430/attr-430-small.s b/gas/testsuite/gas/msp430/attr-430-small.s
new file mode 100644
index 0000000000..2bdb1dbf83
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430-small.s
@@ -0,0 +1,3 @@
+.mspabi_attribute 4, 1 ; OFBA_MSPABI_Tag_ISA == 430
+.mspabi_attribute 6, 1 ; OFBA_MSPABI_Tag_Code_Model == Small
+.mspabi_attribute 8, 1 ; OFBA_MSPABI_Tag_Data_Model == Small
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-any-bad.d b/gas/testsuite/gas/msp430/attr-430x-large-any-bad.d
new file mode 100644
index 0000000000..6793f7cb1c
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-any-bad.d
@@ -0,0 +1,4 @@
+#name: Error when 430X ISA, large memory model and any data region object attributes conflict with options
+#source: attr-430x-large-any.s
+#as: -mcpu=msp430 -ml
+#error_output: attr-430x-large-any-bad.l
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-any-bad.l b/gas/testsuite/gas/msp430/attr-430x-large-any-bad.l
new file mode 100644
index 0000000000..3c049a23f8
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-any-bad.l
@@ -0,0 +1,4 @@
+[^:]*: Assembler messages:
+[^:]*:1: Error: file was compiled for the 430X ISA but the 430 ISA is selected
+[^:]*:4: Error: file was compiled assuming data could be in the upper memory region, but the lower data region is exclusively in use
+
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-any-good.d b/gas/testsuite/gas/msp430/attr-430x-large-any-good.d
new file mode 100644
index 0000000000..603aab11b5
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-any-good.d
@@ -0,0 +1,6 @@
+#name: 430X ISA, large memory model and any data region object attributes
+#source: attr-430x-large-any.s
+#as: -ml -mdata-region=none
+#objdump: -t
+
+#pass
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-any.s b/gas/testsuite/gas/msp430/attr-430x-large-any.s
new file mode 100644
index 0000000000..593c1710a1
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-any.s
@@ -0,0 +1,4 @@
+.mspabi_attribute 4, 2 ; OFBA_MSPABI_Tag_ISA == 430x
+.mspabi_attribute 6, 2 ; OFBA_MSPABI_Tag_Code_Model == Large
+.mspabi_attribute 8, 2 ; OFBA_MSPABI_Tag_Data_Model == Large
+.gnu_attribute 4, 2 ; Tag_GNU_MSP430_Data_Region == Any
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-lower-bad.d b/gas/testsuite/gas/msp430/attr-430x-large-lower-bad.d
new file mode 100644
index 0000000000..fb7e5cad7d
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-lower-bad.d
@@ -0,0 +1,4 @@
+#name: Error when 430X ISA, large memory model and lower data region object attributes conflict with options
+#source: attr-430x-large-lower.s
+#as: -mdata-region=none -mcpu=msp430 -ml
+#error_output: attr-430x-large-lower-bad.l
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-lower-bad.l b/gas/testsuite/gas/msp430/attr-430x-large-lower-bad.l
new file mode 100644
index 0000000000..a21b640915
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-lower-bad.l
@@ -0,0 +1,3 @@
+[^:]*: Assembler messages:
+[^:]*:1: Error: file was compiled for the 430X ISA but the 430 ISA is selected
+[^:]*:4: Error: file was compiled assuming all data will be in the lower memory region, but the upper region is in use
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-lower-good.d b/gas/testsuite/gas/msp430/attr-430x-large-lower-good.d
new file mode 100644
index 0000000000..c4a395cd80
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-lower-good.d
@@ -0,0 +1,6 @@
+#name: 430X ISA, large memory model and lower data region object attributes
+#source: attr-430x-large-lower.s
+#as: -ml
+#objdump: -t
+
+#pass
diff --git a/gas/testsuite/gas/msp430/attr-430x-large-lower.s b/gas/testsuite/gas/msp430/attr-430x-large-lower.s
new file mode 100644
index 0000000000..4ab0b5d13a
--- /dev/null
+++ b/gas/testsuite/gas/msp430/attr-430x-large-lower.s
@@ -0,0 +1,4 @@
+.mspabi_attribute 4, 2 ; OFBA_MSPABI_Tag_ISA == 430x
+.mspabi_attribute 6, 2 ; OFBA_MSPABI_Tag_Code_Model == Large
+.mspabi_attribute 8, 2 ; OFBA_MSPABI_Tag_Data_Model == Large
+.gnu_attribute 4, 1 ; Tag_GNU_MSP430_Data_Region == Lower
diff --git a/gas/testsuite/gas/msp430/msp430.exp b/gas/testsuite/gas/msp430/msp430.exp
index 9c7a1975f9..ef3164da2b 100644
--- a/gas/testsuite/gas/msp430/msp430.exp
+++ b/gas/testsuite/gas/msp430/msp430.exp
@@ -46,4 +46,10 @@ if [expr [istarget "msp430-*-*"]]  then {
     run_dump_test "preinit-array"
     run_dump_test "init-array"
     run_dump_test "fini-array"
+    run_dump_test "attr-430-small-bad"
+    run_dump_test "attr-430-small-good"
+    run_dump_test "attr-430x-large-lower-bad"
+    run_dump_test "attr-430x-large-lower-good"
+    run_dump_test "attr-430x-large-any-bad"
+    run_dump_test "attr-430x-large-any-good"
 }
diff --git a/include/elf/msp430.h b/include/elf/msp430.h
index e060804b66..3319b961d8 100644
--- a/include/elf/msp430.h
+++ b/include/elf/msp430.h
@@ -58,6 +58,48 @@
 #define OFBA_MSPABI_Tag_ISA		4
 #define OFBA_MSPABI_Tag_Code_Model	6
 #define OFBA_MSPABI_Tag_Data_Model	8
+#define OFBA_MSPABI_Tag_enum_size	10	/* Unused by GNU.  */
+
+/* GNU Object attribute tags.  */
+enum
+{
+  /* 0-3 are generic.  */
+
+  /* Define a GNU attribute for keeping track of whether the compiler has
+     generated code assuming that the upper region could be in use.
+     Added by the assembler based on the -mdata-region option.
+     FIXME: GCC should be generating the attribute.
+     This tag is ignored unless the large memory model is in use.  */
+  Tag_GNU_MSP430_Data_Region = 4,
+};
+
+/* Object attribute values.  */
+enum
+{
+  /* Values defined for OFBA_MSPABI_Tag_ISA.  */
+  OFBA_MSPABI_Val_ISA_NONE = 0,
+  OFBA_MSPABI_Val_ISA_MSP430 = 1,
+  OFBA_MSPABI_Val_ISA_MSP430X = 2,
+
+  /* Values defined for OFBA_MSPABI_Tag_Code_Model.  */
+  OFBA_MSPABI_Val_Code_Model_NONE = 0,
+  OFBA_MSPABI_Val_Code_Model_SMALL = 1,
+  OFBA_MSPABI_Val_Code_Model_LARGE = 2,
+
+  /* Values defined for OFBA_MSPABI_Tag_Data_Model.  */
+  OFBA_MSPABI_Val_Data_Model_NONE = 0,
+  OFBA_MSPABI_Val_Data_Model_SMALL = 1,
+  OFBA_MSPABI_Val_Data_Model_LARGE = 2,
+  OFBA_MSPABI_Val_Data_Model_RESTRICTED = 3,	/* Unused by GNU.  */
+
+  /* Values defined for Tag_GNU_MSP430_Data_Region.  */
+  Val_GNU_MSP430_Data_Region_NONE = 0,
+  /* The default data region.  Assumes all data is below address 0x10000.  */
+  Val_GNU_MSP430_Data_Region_Lower = 1,
+  /* Set if -mdata-region={none,upper,either}.  Assumes
+     data could be placed at or above address 0x10000.  */
+  Val_GNU_MSP430_Data_Region_Any = 2,
+};
 
 /* Relocations.  */
 START_RELOC_NUMBERS (elf_msp430_reloc_type)
diff --git a/ld/testsuite/ld-msp430-elf/attr-gnu-main.s b/ld/testsuite/ld-msp430-elf/attr-gnu-main.s
new file mode 100644
index 0000000000..e07a58ca70
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/attr-gnu-main.s
@@ -0,0 +1,8 @@
+.text
+	.balign 2
+	.global	main
+	.type	main, @function
+main:
+.L2:
+	BRA	#.L2
+	.size	main, .-main
diff --git a/ld/testsuite/ld-msp430-elf/attr-gnu-obj.s b/ld/testsuite/ld-msp430-elf/attr-gnu-obj.s
new file mode 100644
index 0000000000..d256e409c2
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/attr-gnu-obj.s
@@ -0,0 +1,2 @@
+.text
+	.comm	a,2,2
diff --git a/ld/testsuite/ld-msp430-elf/attr-gnu-region-lower-upper.d b/ld/testsuite/ld-msp430-elf/attr-gnu-region-lower-upper.d
new file mode 100644
index 0000000000..70f8e35004
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/attr-gnu-region-lower-upper.d
@@ -0,0 +1,6 @@
+#name: prevent merging of lower and upper attributes
+#source: attr-gnu-main.s -ml -mdata-region=lower
+#source: attr-gnu-obj.s -ml -mdata-region=upper
+#ld:
+#error: .*can use the upper region for data, but.*assumes data is exclusively in lower memory.*
+#error: .*failed to merge target specific data of file.*
diff --git a/ld/testsuite/ld-msp430-elf/attr-gnu-region-lower.d b/ld/testsuite/ld-msp430-elf/attr-gnu-region-lower.d
new file mode 100644
index 0000000000..57345aadeb
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/attr-gnu-region-lower.d
@@ -0,0 +1,12 @@
+#source: attr-gnu-main.s -ml -mdata-region=lower
+#source: attr-gnu-obj.s -ml
+#readelf: -A
+
+Attribute Section: mspabi
+File Attributes
+  Tag_ISA: MSP430X
+  Tag_Code_Model: Large
+  Tag_Data_Model: Large
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_MSP430_Data_Region: Lower Region Only
diff --git a/ld/testsuite/ld-msp430-elf/attr-gnu-region-upper.d b/ld/testsuite/ld-msp430-elf/attr-gnu-region-upper.d
new file mode 100644
index 0000000000..225968dc44
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/attr-gnu-region-upper.d
@@ -0,0 +1,13 @@
+#source: attr-gnu-main.s -ml -mdata-region=upper
+#source: attr-gnu-obj.s -ml -mdata-region=none
+#ld: --data-region=either
+#readelf: -A
+
+Attribute Section: mspabi
+File Attributes
+  Tag_ISA: MSP430X
+  Tag_Code_Model: Large
+  Tag_Data_Model: Large
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_MSP430_Data_Region: Any Region
diff --git a/ld/testsuite/ld-msp430-elf/msp430-elf.exp b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
index b6f3151c80..08620b57ff 100644
--- a/ld/testsuite/ld-msp430-elf/msp430-elf.exp
+++ b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
@@ -46,6 +46,7 @@ if { ![istarget "msp430*elf*"] } {
 #     treated as a sign of an error and FAILs the test.
 #
 #
+
 set msp430regionprefixtests {
   {"Move main() to .upper.text" "-T msp430.ld --code-region=upper"
     "" "" {main-with-text-rodata.s} {{objdump -d main-text-upper.d}} "main-upper"}
@@ -162,7 +163,7 @@ set msp430warntests {
         {{ld warn-no-lower-data.r}} "warn-no-lower-data"}
 }
 
-# Don't run section shuffle tests when msp430 ISA is selected
+# Don't run further tests when msp430 ISA is selected
 if {[string match "*-mcpu=msp430 *" [board_info [target_info name] multilib_flags]]
   || [string match "*-mcpu=msp430" [board_info [target_info name] multilib_flags]]} {
     return
@@ -173,3 +174,12 @@ run_ld_link_tests $msp430eithershuffletests
 run_ld_link_tests $msp430warntests
 
 run_dump_test valid-map
+
+# Don't run data region tests if a data region is specified
+if {[string match "*-mdata-region*" [board_info [target_info name] multilib_flags]]} {
+    return
+}
+# GNU object attribute dump tests
+run_dump_test attr-gnu-region-lower
+run_dump_test attr-gnu-region-upper
+run_dump_test attr-gnu-region-lower-upper
-- 
2.17.1

Reply via email to