The new operand modifiers can be used to select odd-numbered bytes of a memory
reference or constant value.

Successfully regtested on trunk for msp430-elf in the default, -mlarge,
-mcpu=msp430 and -mlarge/-mcode-region=either/-mdata-region=either
configurations.

Ok for trunk?

Thanks,
Jozef
>From c48ad2ae243a101afe8d021e847c56a482a60f20 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <joze...@mittosystems.com>
Date: Wed, 9 Sep 2020 13:06:46 +0100
Subject: [PATCH] MSP430: Add 'd', 'e', 'f' and 'g' asm operand modifiers

The new operand modifiers can be used to select odd-numbered bytes of a memory
reference or constant value.

gcc/ChangeLog:

        * config/msp430/msp430.c (msp430_print_operand): Update comment.
        Cast to long when printing values formatted as long.
        Support 'd', 'e', 'f' and 'g' modifiers.
        Extract operand value with a single operation for all modifiers.
        * doc/extend.texi (msp430Operandmodifiers): New.

gcc/testsuite/ChangeLog:

        * gcc.target/msp430/operand-modifiers.c: Extend test to handle new
        modifiers.
        * gcc.target/msp430/operand-modifiers-bad.c: New test.
---
 gcc/config/msp430/msp430.c                    | 152 ++++++++--------
 gcc/doc/extend.texi                           |  36 ++++
 .../gcc.target/msp430/operand-modifiers-bad.c |  15 ++
 .../gcc.target/msp430/operand-modifiers.c     | 167 +++++++++++++++---
 4 files changed, 277 insertions(+), 93 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/operand-modifiers-bad.c

diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index a299ed7f9d1..e46a8d23bc7 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -3475,30 +3475,43 @@ msp430_op_not_in_high_mem (rtx op)
 #undef  TARGET_PRINT_OPERAND
 #define TARGET_PRINT_OPERAND           msp430_print_operand
 
-/* A   low 16-bits of int/lower of register pair
-   B   high 16-bits of int/higher of register pair
-   C   bits 32-47 of a 64-bit value/reg 3 of a DImode value
-   D   bits 48-63 of a 64-bit value/reg 4 of a DImode value
-   H   like %B (for backwards compatibility)
-   I   inverse of value
-   J   an integer without a # prefix
-   L   like %A (for backwards compatibility)
-   O   offset of the top of the stack
-   Q   like X but generates an A postfix
-   R   inverse of condition code, unsigned.
-   W   value - 16
-   X   X instruction postfix in large mode
-   Y   value - 4
-   Z   value - 1
-   b   .B or .W or .A, depending upon the mode
-   p   bit position
-   r   inverse of condition code
-   x   like X but only for pointers.  */
+/* A   Select low 16-bits of the constant/register/memory operand.
+   B   Select high 16-bits of the constant/register/memory
+       operand.
+   C   Select bits 32-47 of the constant/register/memory operand.
+   D   Select bits 48-63 of the constant/register/memory operand.
+   H   Equivalent to @code{B} (for backwards compatibility).
+   I   Print the inverse (logical @code{NOT}) of the constant
+       value.
+   J   Print an integer without a @code{#} prefix.
+   L   Equivalent to @code{A} (for backwards compatibility).
+   O   Offset of the current frame from the top of the stack.
+   Q   Use the @code{A} instruction postfix.
+   R   Inverse of condition code, for unsigned comparisons.
+   W   Subtract 16 from the constant value.
+   X   Use the @code{X} instruction postfix.
+   Y   Subtract 4 from the constant value.
+   Z   Subtract 1 from the constant value.
+   b   Append @code{.B}, @code{.W} or @code{.A} to the
+       instruction, depending on the mode.
+   d   Offset 1 byte of a memory reference or constant value.
+   e   Offset 3 bytes of a memory reference or constant value.
+   f   Offset 5 bytes of a memory reference or constant value.
+   g   Offset 7 bytes of a memory reference or constant value.
+   p   Print the value of 2, raised to the power of the given
+       constant.  Used to select the specified bit position.
+   r   Inverse of condition code, for signed comparisons.
+   x   Equivialent to @code{X}, but only for pointers.  */
 
 static void
 msp430_print_operand (FILE * file, rtx op, int letter)
 {
   rtx addr;
+  /* These are used by the 'A', 'B', 'C', 'D', 'd', 'e', 'f' and 'g' modifiers
+     to describe how to process the operand to get the requested value.  */
+  int mem_off = 0;
+  int reg_off = 0;
+  int const_shift = 0;
 
   /* We can't use c, n, a, or l.  */
   switch (letter)
@@ -3506,17 +3519,17 @@ msp430_print_operand (FILE * file, rtx op, int letter)
     case 'Z':
       gcc_assert (CONST_INT_P (op));
       /* Print the constant value, less one.  */
-      fprintf (file, "#%ld", INTVAL (op) - 1);
+      fprintf (file, "#%ld", (long) (INTVAL (op) - 1));
       return;
     case 'Y':
       gcc_assert (CONST_INT_P (op));
       /* Print the constant value, less four.  */
-      fprintf (file, "#%ld", INTVAL (op) - 4);
+      fprintf (file, "#%ld", (long) (INTVAL (op) - 4));
       return;
     case 'W':
       gcc_assert (CONST_INT_P (op));
       /* Print the constant value, less 16.  */
-      fprintf (file, "#%ld", INTVAL (op) - 16);
+      fprintf (file, "#%ld", (long) (INTVAL (op) - 16));
       return;
     case 'I':
       if (GET_CODE (op) == CONST_INT)
@@ -3573,76 +3586,71 @@ msp430_print_operand (FILE * file, rtx op, int letter)
        default:
          return;
        }
-    case 'A':
-    case 'L': /* Low half.  */
-      switch (GET_CODE (op))
+    case 'd': case 'e': case 'f': case 'g':
+      if (REG_P (op))
        {
-       case MEM:
-         op = adjust_address (op, Pmode, 0);
-         break;
-       case REG:
-         break;
-       case CONST_INT:
-         op = GEN_INT (INTVAL (op) & 0xffff);
-         letter = 0;
-         break;
-       default:
-         /* If you get here, figure out a test case :-) */
-         gcc_unreachable ();
+         output_operand_lossage ("%%d, %%e, %%f, %%g operand modifiers are "
+                                 "for memory references or constant values "
+                                 "only");
+         return;
        }
-      break;
-    case 'B':
-    case 'H': /* high half */
-      switch (GET_CODE (op))
+      /* fallthru */
+    case 'B': case 'H': /* high half */
+    case 'C':
+    case 'D':
+      switch (letter)
        {
-       case MEM:
-         /* We don't need to adjust the address for post_inc.  */
-         op = adjust_address (op, Pmode,
-                              (GET_CODE (XEXP (op, 0)) == POST_INC) ? 0 : 2);
+       case 'd':
+         mem_off = 1;
+         const_shift = 8;
          break;
-       case REG:
-         op = gen_rtx_REG (Pmode, REGNO (op) + 1);
+       case 'B':
+       case 'H':
+         mem_off = 2;
+         reg_off = 1;
+         const_shift = 16;
          break;
-       case CONST_INT:
-         op = GEN_INT (INTVAL (op) >> 16);
-         letter = 0;
+       case 'e':
+         mem_off = 3;
+         const_shift = 24;
          break;
-       default:
-         /* If you get here, figure out a test case :-) */
-         gcc_unreachable ();
-       }
-      break;
-    case 'C':
-      switch (GET_CODE (op))
-       {
-       case MEM:
-         op = adjust_address (op, Pmode,
-                              (GET_CODE (XEXP (op, 0)) == POST_INC) ? 0 : 4);
+       case 'C':
+         mem_off = 4;
+         reg_off = 2;
+         const_shift = 32;
          break;
-       case REG:
-         op = gen_rtx_REG (Pmode, REGNO (op) + 2);
+       case 'f':
+         mem_off = 5;
+         const_shift = 40;
          break;
-       case CONST_INT:
-         op = GEN_INT ((long long) INTVAL (op) >> 32);
-         letter = 0;
+       case 'D':
+         mem_off = 6;
+         reg_off = 3;
+         const_shift = 48;
+         break;
+       case 'g':
+         mem_off = 7;
+         const_shift = 56;
          break;
        default:
-         /* If you get here, figure out a test case :-) */
          gcc_unreachable ();
+         break;
        }
-      break;
-    case 'D':
+      /* fallthru */
+    case 'A': case 'L': /* Low half.  */
       switch (GET_CODE (op))
        {
        case MEM:
+         /* We don't need to adjust the address for post_inc.  */
          op = adjust_address (op, Pmode,
-                              (GET_CODE (XEXP (op, 0)) == POST_INC) ? 0 : 6);
+                              (GET_CODE (XEXP (op, 0)) == POST_INC)
+                              ? 0 : mem_off);
          break;
        case REG:
-         op = gen_rtx_REG (Pmode, REGNO (op) + 3);
+         op = gen_rtx_REG (Pmode, REGNO (op) + reg_off);
          break;
        case CONST_INT:
-         op = GEN_INT ((long long) INTVAL (op) >> 48);
+         op = GEN_INT (((long long) INTVAL (op) >> const_shift) & 0xffff);
          letter = 0;
          break;
        default:
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 3b37aba5795..5571c4f2ff2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10737,6 +10737,42 @@ for the compiler to know that @code{fyl2xp1} pops both 
inputs.
 asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)");
 @end smallexample
 
+@anchor{msp430Operandmodifiers}
+@subsubsection MSP430 Operand Modifiers
+
+The list below describes the supported modifiers and their effects for MSP430.
+
+@multitable @columnfractions .10 .90
+@headitem Modifier @tab Description
+@item @code{A} @tab Select low 16-bits of the constant/register/memory operand.
+@item @code{B} @tab Select high 16-bits of the constant/register/memory
+operand.
+@item @code{C} @tab Select bits 32-47 of the constant/register/memory operand.
+@item @code{D} @tab Select bits 48-63 of the constant/register/memory operand.
+@item @code{H} @tab Equivalent to @code{B} (for backwards compatibility).
+@item @code{I} @tab Print the inverse (logical @code{NOT}) of the constant
+value.
+@item @code{J} @tab Print an integer without a @code{#} prefix.
+@item @code{L} @tab Equivalent to @code{A} (for backwards compatibility).
+@item @code{O} @tab Offset of the current frame from the top of the stack.
+@item @code{Q} @tab Use the @code{A} instruction postfix.
+@item @code{R} @tab Inverse of condition code, for unsigned comparisons.
+@item @code{W} @tab Subtract 16 from the constant value.
+@item @code{X} @tab Use the @code{X} instruction postfix.
+@item @code{Y} @tab Subtract 4 from the constant value.
+@item @code{Z} @tab Subtract 1 from the constant value.
+@item @code{b} @tab Append @code{.B}, @code{.W} or @code{.A} to the
+instruction, depending on the mode.
+@item @code{d} @tab Offset 1 byte of a memory reference or constant value.
+@item @code{e} @tab Offset 3 bytes of a memory reference or constant value.
+@item @code{f} @tab Offset 5 bytes of a memory reference or constant value.
+@item @code{g} @tab Offset 7 bytes of a memory reference or constant value.
+@item @code{p} @tab Print the value of 2, raised to the power of the given
+constant.  Used to select the specified bit position.
+@item @code{r} @tab Inverse of condition code, for signed comparisons.
+@item @code{x} @tab Equivialent to @code{X}, but only for pointers.
+@end multitable
+
 @lowersections
 @include md.texi
 @raisesections
diff --git a/gcc/testsuite/gcc.target/msp430/operand-modifiers-bad.c 
b/gcc/testsuite/gcc.target/msp430/operand-modifiers-bad.c
new file mode 100644
index 00000000000..3ff47931329
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/operand-modifiers-bad.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+
+void
+foo (void)
+{
+  char reg = 10;
+  __asm__ ("mov.b %d1, %0\n" : "=r" (reg) : "r" (reg));
+  /* { dg-error "invalid 'asm': %d, %e, %f, %g operand modifiers are for 
memory references or constant values only" "" { target *-*-* } .-1 } */
+  __asm__ ("mov.b %e1, %0\n" : "=r" (reg) : "r" (reg));
+  /* { dg-error "invalid 'asm': %d, %e, %f, %g operand modifiers are for 
memory references or constant values only" "" { target *-*-* } .-1 } */
+  __asm__ ("mov.b %f1, %0\n" : "=r" (reg) : "r" (reg));
+  /* { dg-error "invalid 'asm': %d, %e, %f, %g operand modifiers are for 
memory references or constant values only" "" { target *-*-* } .-1 } */
+  __asm__ ("mov.b %g1, %0\n" : "=r" (reg) : "r" (reg));
+  /* { dg-error "invalid 'asm': %d, %e, %f, %g operand modifiers are for 
memory references or constant values only" "" { target *-*-* } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.target/msp430/operand-modifiers.c 
b/gcc/testsuite/gcc.target/msp430/operand-modifiers.c
index ad0a5310839..76e453df2c9 100644
--- a/gcc/testsuite/gcc.target/msp430/operand-modifiers.c
+++ b/gcc/testsuite/gcc.target/msp430/operand-modifiers.c
@@ -1,30 +1,155 @@
-volatile unsigned long si = 0x89abcdef;
-volatile unsigned long long di = 0xfedcba9876543210;
+/* { dg-do run } */
 
-unsigned int a, b, c, d;
+#include <stdio.h>
+
+/* Test A/L, B/H, C, D, d, e, f, g operand modifiers on 32-bit, 64-bit and,
+   where appropriate, 16-bit values.  */
+
+#define MEM16_VAL 0x2345
+#define MEM32_VAL 0x89abcdef
+#define MEM64_VAL 0xfedcba9876543210
+
+#define CONST16_VAL 0xbcde
+#define CONST32_VAL 0x99aabbcc
+#define CONST64_VAL 0x8899aabbccddeeff
+
+#define REG32_VAL 0x12345678
+#define REG64_VAL 0x123456789abcdef
+
+volatile unsigned long mem16 = MEM16_VAL;
+volatile unsigned long mem32 = MEM32_VAL;
+volatile unsigned long long mem64 = MEM64_VAL;
+
+unsigned int word0, word1, word2, word3;
+unsigned char byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7;
+
+#define CHECK_BYTES_IN_16BIT_VAL(VAL)          \
+  if (byte0 != ((unsigned char)VAL)                    \
+      || byte1 != ((unsigned char)(VAL >> 8))) \
+    return 1;
+
+#define CHECK_WORDS_IN_32BIT_VAL(VAL)          \
+  if (word0 != ((unsigned)VAL)                 \
+      || word1 != ((unsigned)(VAL >> 16)))     \
+    return 1;
+
+#define CHECK_WORDS_IN_64BIT_VAL(VAL)          \
+  if (word0 != ((unsigned)VAL)         \
+      || word1 != ((unsigned)(VAL >> 16))      \
+      || word2 != ((unsigned)(VAL >> 32))      \
+      || word3 != ((unsigned)(VAL >> 48)))     \
+    return 1;
+
+#define CHECK_BYTES_IN_32BIT_VAL(VAL)          \
+  if (byte0 != ((unsigned char)VAL)            \
+      || byte1 != ((unsigned char)(VAL >> 8))  \
+      || byte2 != ((unsigned char)(VAL >> 16)) \
+      || byte3 != ((unsigned char)(VAL >> 24)))        \
+    return 1;
+
+#define CHECK_BYTES_IN_64BIT_VAL(VAL)          \
+  if (byte0 != ((unsigned char)VAL)            \
+      || byte1 != ((unsigned char)(VAL >> 8))  \
+      || byte2 != ((unsigned char)(VAL >> 16)) \
+      || byte3 != ((unsigned char)(VAL >> 24)) \
+      || byte4 != ((unsigned char)(VAL >> 32)) \
+      || byte5 != ((unsigned char)(VAL >> 40)) \
+      || byte6 != ((unsigned char)(VAL >> 48)) \
+      || byte7 != ((unsigned char)(VAL >> 56)))        \
+    return 1;
 
 int
 main (void)
 {
-  /* Check that %A and %B extract the low and high words of a 32-bit value,
-     respectively.  */
-  __asm__("mov %A1, %0\n" : "=m" (a) : "m" (si));
-  __asm__("mov %B1, %0\n" : "=m" (b) : "m" (si));
-  if (a != ((unsigned)si)
-      || b != ((unsigned)(si >> 16)))
-    return 1;
+  unsigned long register reg32 = REG32_VAL;
+  unsigned long long register reg64 = REG64_VAL;
 
-  /* Check that %A, %B, %C and %D extract the 1st, 2nd, 3rd and 4th words of a
-     64-bit value, respectively.  */
-  __asm__("mov %A1, %0\n" : "=m" (a) : "m" (di));
-  __asm__("mov %B1, %0\n" : "=m" (b) : "m" (di));
-  __asm__("mov %C1, %0\n" : "=m" (c) : "m" (di));
-  __asm__("mov %D1, %0\n" : "=m" (d) : "m" (di));
-  if (a != ((unsigned)di)
-      || b != ((unsigned)(di >> 16))
-      || c != ((unsigned)(di >> 32))
-      || d != ((unsigned)(di >> 48)))
-    return 1;
+  /* *** MEMORY OPERAND TESTS *** */
+  /* Test byte extraction of a 16-bit value.  */
+  __asm__("mov.b %A1, %0\n" : "=m" (byte0) : "m" (mem16));
+  __asm__("mov.b %d1, %0\n" : "=m" (byte1) : "m" (mem16));
+  CHECK_BYTES_IN_16BIT_VAL (MEM16_VAL);
+
+  /* Test extraction of high and low words from 32-bit value.  */
+  __asm__("mov %A1, %0\n" : "=m" (word0) : "m" (mem32));
+  __asm__("mov %B1, %0\n" : "=m" (word1) : "m" (mem32));
+  CHECK_WORDS_IN_32BIT_VAL (MEM32_VAL);
+
+  /* Test extraction of each word of a 64-bit value.  */
+  __asm__("mov %A1, %0\n" : "=m" (word0) : "m" (mem64));
+  __asm__("mov %B1, %0\n" : "=m" (word1) : "m" (mem64));
+  __asm__("mov %C1, %0\n" : "=m" (word2) : "m" (mem64));
+  __asm__("mov %D1, %0\n" : "=m" (word3) : "m" (mem64));
+  CHECK_WORDS_IN_64BIT_VAL (MEM64_VAL);
+
+  /* Test extraction of each byte of a 32-bit value.  */
+  __asm__("mov.b %A1, %0\n" : "=m" (byte0) : "m" (mem32));
+  __asm__("mov.b %d1, %0\n" : "=m" (byte1) : "m" (mem32));
+  __asm__("mov.b %B1, %0\n" : "=m" (byte2) : "m" (mem32));
+  __asm__("mov.b %e1, %0\n" : "=m" (byte3) : "m" (mem32));
+  CHECK_BYTES_IN_32BIT_VAL (MEM32_VAL);
+
+  /* Test extraction of each byte of a 64-bit value.  */
+  __asm__("mov.b %A1, %0\n" : "=m" (byte0) : "m" (mem64));
+  __asm__("mov.b %d1, %0\n" : "=m" (byte1) : "m" (mem64));
+  __asm__("mov.b %B1, %0\n" : "=m" (byte2) : "m" (mem64));
+  __asm__("mov.b %e1, %0\n" : "=m" (byte3) : "m" (mem64));
+  __asm__("mov.b %C1, %0\n" : "=m" (byte4) : "m" (mem64));
+  __asm__("mov.b %f1, %0\n" : "=m" (byte5) : "m" (mem64));
+  __asm__("mov.b %D1, %0\n" : "=m" (byte6) : "m" (mem64));
+  __asm__("mov.b %g1, %0\n" : "=m" (byte7) : "m" (mem64));
+  CHECK_BYTES_IN_64BIT_VAL (MEM64_VAL);
+
+  /* *** IMMEDIATE OPERAND TESTS *** */
+  /* Test byte extraction of a 16-bit value.  */
+  __asm__("mov.b %A1, %0\n" : "=m" (byte0) : "i" (CONST16_VAL));
+  __asm__("mov.b %d1, %0\n" : "=m" (byte1) : "i" (CONST16_VAL));
+  CHECK_BYTES_IN_16BIT_VAL (CONST16_VAL);
+
+  /* Test extraction of high and low words from 32-bit value.  */
+  __asm__("mov %A1, %0\n" : "=m" (word0) : "i" (CONST32_VAL));
+  __asm__("mov %B1, %0\n" : "=m" (word1) : "i" (CONST32_VAL));
+  CHECK_WORDS_IN_32BIT_VAL (CONST32_VAL);
+
+  /* Test extraction of each word of a 64-bit value.  */
+  __asm__("mov %A1, %0\n" : "=m" (word0) : "i" (CONST64_VAL));
+  __asm__("mov %B1, %0\n" : "=m" (word1) : "i" (CONST64_VAL));
+  __asm__("mov %C1, %0\n" : "=m" (word2) : "i" (CONST64_VAL));
+  __asm__("mov %D1, %0\n" : "=m" (word3) : "i" (CONST64_VAL));
+  CHECK_WORDS_IN_64BIT_VAL (CONST64_VAL);
+
+  /* Test extraction of each byte of a 32-bit value.  */
+  __asm__("mov.b %A1, %0\n" : "=m" (byte0) : "i" (CONST32_VAL));
+  __asm__("mov.b %d1, %0\n" : "=m" (byte1) : "i" (CONST32_VAL));
+  __asm__("mov.b %B1, %0\n" : "=m" (byte2) : "i" (CONST32_VAL));
+  __asm__("mov.b %e1, %0\n" : "=m" (byte3) : "i" (CONST32_VAL));
+  CHECK_BYTES_IN_32BIT_VAL (CONST32_VAL);
+
+  /* Test extraction of each byte of a 64-bit value.  */
+  __asm__("mov.b %A1, %0\n" : "=m" (byte0) : "i" (CONST64_VAL));
+  __asm__("mov.b %d1, %0\n" : "=m" (byte1) : "i" (CONST64_VAL));
+  __asm__("mov.b %B1, %0\n" : "=m" (byte2) : "i" (CONST64_VAL));
+  __asm__("mov.b %e1, %0\n" : "=m" (byte3) : "i" (CONST64_VAL));
+  __asm__("mov.b %C1, %0\n" : "=m" (byte4) : "i" (CONST64_VAL));
+  __asm__("mov.b %f1, %0\n" : "=m" (byte5) : "i" (CONST64_VAL));
+  __asm__("mov.b %D1, %0\n" : "=m" (byte6) : "i" (CONST64_VAL));
+  __asm__("mov.b %g1, %0\n" : "=m" (byte7) : "i" (CONST64_VAL));
+  CHECK_BYTES_IN_64BIT_VAL (CONST64_VAL);
+
+  /* *** REGISTER OPERAND TESTS *** */
+  /* No extraction of bytes from a single register.  */
+
+  /* Test extraction of high and low words from 32-bit value.  */
+  __asm__("mov %A1, %0\n" : "=m" (word0) : "r" (reg32));
+  __asm__("mov %B1, %0\n" : "=m" (word1) : "r" (reg32));
+  CHECK_WORDS_IN_32BIT_VAL (REG32_VAL);
+
+  /* Test extraction of each word of a 64-bit value.  */
+  __asm__("mov %A1, %0\n" : "=m" (word0) : "r" (reg64));
+  __asm__("mov %B1, %0\n" : "=m" (word1) : "r" (reg64));
+  __asm__("mov %C1, %0\n" : "=m" (word2) : "r" (reg64));
+  __asm__("mov %D1, %0\n" : "=m" (word3) : "r" (reg64));
+  CHECK_WORDS_IN_64BIT_VAL (REG64_VAL);
 
   return 0;
 }
-- 
2.28.0

Reply via email to