From 2f8022873c72e4abed7987cfa6b8ab9de6e53971 Mon Sep 17 00:00:00 2001 From: Jozef Lawrynowicz Date: Fri, 18 Sep 2020 13:07:19 +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 68a67bd8c98..de4b16bf23b 100644 --- a/gcc/config/msp430/msp430.c +++ b/gcc/config/msp430/msp430.c @@ -3521,30 +3521,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) @@ -3552,17 +3565,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) @@ -3619,76 +3632,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 + +/* 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.30.2