+2020-01-15  Jozef Lawrynowicz  <jozef.l@mittosystems.com>
+
+       * config/tc-msp430.c (CHECK_RELOC_MSP430): Always generate 430X
+       relocations when the target is 430X, except when extracting part of an
+       expression.
+       (msp430_srcoperand): Adjust comment.
+       Initialize the expp member of the msp430_operand_s struct as
+       appropriate.
+       (msp430_dstoperand): Likewise.
+       * testsuite/gas/msp430/msp430.exp: Run new test.
+       * testsuite/gas/msp430/reloc-lo-430x.d: New test.
+       * testsuite/gas/msp430/reloc-lo-430x.s: New test.
+
 2020-01-15  Alan Modra  <amodra@gmail.com>
 
        * configure.tgt: Add sparc-*-freebsd case.
 
   return selected_isa == MSP_ISA_430Xv2;
 }
 
-/* Generate an absolute 16-bit relocation.
-   For the 430X we generate a relocation without linker range checking
-    if the value is being used in an extended (ie 20-bit) instruction,
-    otherwise if have a shifted expression we use a HI reloc.
+/* Generate an absolute 16-bit relocation, for 430 (!extended_op) instructions
+   only.
+   For the 430X we generate a 430 relocation only for the case where part of an
+   expression is being extracted (e.g. #hi(EXP), #lo(EXP). Otherwise generate
+   a 430X relocation.
    For the 430 we generate a relocation without assembler range checking
-    if we are handling an immediate value or a byte-width instruction.  */
+   if we are handling an immediate value or a byte-width instruction.  */
 
 #undef  CHECK_RELOC_MSP430
 #define CHECK_RELOC_MSP430(OP)                         \
   (target_is_430x ()                                   \
-  ? (extended_op                                       \
-     ? BFD_RELOC_16                                    \
-     : ((OP).vshift == 1)                              \
-     ? BFD_RELOC_MSP430_ABS_HI16                       \
-     : BFD_RELOC_MSP430X_ABS16)                                \
+   ? ((OP).expp == MSP_EXPP_ALL                                \
+       ? BFD_RELOC_MSP430X_ABS16                       \
+       : ((OP).vshift == 1                             \
+         ? BFD_RELOC_MSP430_ABS_HI16 : BFD_RELOC_16))  \
    : ((imm_op || byte_op)                              \
       ? BFD_RELOC_MSP430_16_BYTE : BFD_RELOC_MSP430_16))
 
       char *h = l;
       int vshift = -1;
       int rval = 0;
+      /* Use all parts of the constant expression by default.  */
+      enum msp430_expp_e expp = MSP_EXPP_ALL;
 
       /* Check if there is:
         llo(x) - least significant 16 bits, x &= 0xffff
         lhi(x) - x = (x >> 16) & 0xffff,
         hlo(x) - x = (x >> 32) & 0xffff,
         hhi(x) - x = (x >> 48) & 0xffff
-        The value _MUST_ be constant expression: #hlo(1231231231).  */
+        The value _MUST_ be an immediate expression: #hlo(1231231231).  */
 
       *imm_op = TRUE;
 
        {
          vshift = 0;
          rval = 3;
+         expp = MSP_EXPP_LLO;
        }
       else if (strncasecmp (h, "#lhi(", 5) == 0)
        {
          vshift = 1;
          rval = 3;
+         expp = MSP_EXPP_LHI;
        }
       else if (strncasecmp (h, "#hlo(", 5) == 0)
        {
          vshift = 2;
          rval = 3;
+         expp = MSP_EXPP_HLO;
        }
       else if (strncasecmp (h, "#hhi(", 5) == 0)
        {
          vshift = 3;
          rval = 3;
+         expp = MSP_EXPP_HHI;
        }
       else if (strncasecmp (h, "#lo(", 4) == 0)
        {
          vshift = 0;
          rval = 2;
+         expp = MSP_EXPP_LO;
        }
       else if (strncasecmp (h, "#hi(", 4) == 0)
        {
          vshift = 1;
          rval = 2;
+         expp = MSP_EXPP_HI;
        }
 
       op->reg = 0;             /* Reg PC.  */
       __tl = h + 1 + rval;
       op->mode = OP_EXP;
       op->vshift = vshift;
+      op->expp = expp;
 
       end = parse_exp (__tl, &(op->exp));
       if (end != NULL && *end != 0 && *end != ')' )
        }
       op->mode = OP_EXP;
       op->vshift = 0;
+      op->expp = MSP_EXPP_ALL;
       if (op->exp.X_op == O_constant)
        {
          int x = op->exp.X_add_number;
       *h = 0;
       op->mode = OP_EXP;
       op->vshift = 0;
+      op->expp = MSP_EXPP_ALL;
       end = parse_exp (__tl, &(op->exp));
       if (end != NULL && *end != 0)
        {
   op->am = (*l == '-' ? 3 : 1);
   op->ol = 1;
   op->vshift = 0;
+  op->expp = MSP_EXPP_ALL;
   __tl = l;
   end = parse_exp (__tl, &(op->exp));
   if (end != NULL && * end != 0)
       op->am = 1;
       op->ol = 1;
       op->vshift = 0;
+      op->expp = MSP_EXPP_ALL;
       (void) parse_exp (__tl, &(op->exp));
 
       if (op->exp.X_op != O_constant || op->exp.X_add_number != 0)
 
 #ifndef __MSP430_H_
 #define __MSP430_H_
 
+enum msp430_expp_e
+{
+  MSP_EXPP_ALL = 0,    /* Use full the value of the expression - default.  */
+  MSP_EXPP_LO,         /* Extract least significant word from expression.  */
+  MSP_EXPP_HI,         /* Extract 2nd word from expression.  */
+  MSP_EXPP_LLO,                /* Extract least significant word from an
+                          immediate value.  */
+  MSP_EXPP_LHI,                /* Extract 2nd word from an immediate value.  */
+  MSP_EXPP_HLO,                /* Extract 3rd word from an immediate value.  */
+  MSP_EXPP_HHI,                /* Extract 4th word from an immediate value.  */
+};
+
 struct msp430_operand_s
 {
   int ol;      /* Operand length words.  */
   int reg;     /* Register.  */
   int mode;    /* Operand mode.  */
   int vshift;   /* Number of bytes to shift operand down.  */
+  enum msp430_expp_e expp;     /* For when the operand is a constant
+                                  expression, the part of the expression to
+                                  extract.  */
 #define OP_REG         0
 #define OP_EXP         1
 #ifndef DASM_SECTION