PRU GAS Port
authorDimitar Dimitrov <dimitar@dinux.eu>
Tue, 27 Dec 2016 20:43:38 +0000 (22:43 +0200)
committerAlan Modra <amodra@gmail.com>
Sat, 31 Dec 2016 01:33:35 +0000 (12:03 +1030)
* NEWS: Mention new PRU target.
* Makefile.am: Add PRU target.
* config/obj-elf.c: Ditto.
* configure.tgt: Ditto.
* config/tc-pru.c: New file.
* config/tc-pru.h: New file.
* doc/Makefile.am: Add documentation for PRU GAS port.
* doc/all.texi, Ditto.
* doc/as.texinfo: Ditto.
* doc/c-pru.texi: Document PRU GAS options.
* Makefile.in: Regenerate.
* doc/Makefile.in: Regenerate.
* po/POTFILES.in: Regenerate.
* testsuite/gas/pru/alu.d: New file for PRU GAS testsuite.
* testsuite/gas/pru/alu.s: Ditto.
* testsuite/gas/pru/branch.d: Ditto.
* testsuite/gas/pru/branch.s: Ditto.
* testsuite/gas/pru/illegal.l: Ditto.
* testsuite/gas/pru/illegal.s: Ditto.
* testsuite/gas/pru/ldi.d: Ditto.
* testsuite/gas/pru/ldi.s: Ditto.
* testsuite/gas/pru/ldst.d: Ditto.
* testsuite/gas/pru/ldst.s: Ditto.
* testsuite/gas/pru/loop.d: Ditto.
* testsuite/gas/pru/loop.s: Ditto.
* testsuite/gas/pru/misc.d: Ditto.
* testsuite/gas/pru/misc.s: Ditto.
* testsuite/gas/pru/pru.exp: Ditto.
* testsuite/gas/pru/pseudo.d: Ditto.
* testsuite/gas/pru/pseudo.s: Ditto.
* testsuite/gas/pru/warn_reglabel.l: Ditto.
* testsuite/gas/pru/warn_reglabel.s: Ditto.
* testsuite/gas/pru/xfr.d: Ditto.
* testsuite/gas/pru/xfr.s: Ditto.
* testsuite/gas/lns/lns.exp: Mark lns-common-1-alt variant for PRU.

Signed-off-by: Dimitar Dimitrov <dimitar@dinux.eu>
36 files changed:
gas/ChangeLog
gas/Makefile.am
gas/Makefile.in
gas/NEWS
gas/config/obj-elf.c
gas/config/tc-pru.c [new file with mode: 0644]
gas/config/tc-pru.h [new file with mode: 0644]
gas/configure.tgt
gas/doc/Makefile.am
gas/doc/Makefile.in
gas/doc/all.texi
gas/doc/as.texinfo
gas/doc/c-pru.texi [new file with mode: 0644]
gas/po/POTFILES.in
gas/testsuite/gas/lns/lns.exp
gas/testsuite/gas/pru/alu.d [new file with mode: 0644]
gas/testsuite/gas/pru/alu.s [new file with mode: 0644]
gas/testsuite/gas/pru/branch.d [new file with mode: 0644]
gas/testsuite/gas/pru/branch.s [new file with mode: 0644]
gas/testsuite/gas/pru/illegal.l [new file with mode: 0644]
gas/testsuite/gas/pru/illegal.s [new file with mode: 0644]
gas/testsuite/gas/pru/ldi.d [new file with mode: 0644]
gas/testsuite/gas/pru/ldi.s [new file with mode: 0644]
gas/testsuite/gas/pru/ldst.d [new file with mode: 0644]
gas/testsuite/gas/pru/ldst.s [new file with mode: 0644]
gas/testsuite/gas/pru/loop.d [new file with mode: 0644]
gas/testsuite/gas/pru/loop.s [new file with mode: 0644]
gas/testsuite/gas/pru/misc.d [new file with mode: 0644]
gas/testsuite/gas/pru/misc.s [new file with mode: 0644]
gas/testsuite/gas/pru/pru.exp [new file with mode: 0644]
gas/testsuite/gas/pru/pseudo.d [new file with mode: 0644]
gas/testsuite/gas/pru/pseudo.s [new file with mode: 0644]
gas/testsuite/gas/pru/warn_reglabel.l [new file with mode: 0644]
gas/testsuite/gas/pru/warn_reglabel.s [new file with mode: 0644]
gas/testsuite/gas/pru/xfr.d [new file with mode: 0644]
gas/testsuite/gas/pru/xfr.s [new file with mode: 0644]

index 16575d1e64f6bd5b1ab6cf2892ab0fa9c85caee7..1b6fd9dd9a56031ad08766acd797d3f6956053f9 100644 (file)
@@ -1,3 +1,41 @@
+2016-12-31  Dimitar Dimitrov  <dimitar@dinux.eu>
+
+       * NEWS: Mention new PRU target.
+       * Makefile.am: Add PRU target.
+       * config/obj-elf.c: Ditto.
+       * configure.tgt: Ditto.
+       * config/tc-pru.c: New file.
+       * config/tc-pru.h: New file.
+       * doc/Makefile.am: Add documentation for PRU GAS port.
+       * doc/all.texi, Ditto.
+       * doc/as.texinfo: Ditto.
+       * doc/c-pru.texi: Document PRU GAS options.
+       * Makefile.in: Regenerate.
+       * doc/Makefile.in: Regenerate.
+       * po/POTFILES.in: Regenerate.
+       * testsuite/gas/pru/alu.d: New file for PRU GAS testsuite.
+       * testsuite/gas/pru/alu.s: Ditto.
+       * testsuite/gas/pru/branch.d: Ditto.
+       * testsuite/gas/pru/branch.s: Ditto.
+       * testsuite/gas/pru/illegal.l: Ditto.
+       * testsuite/gas/pru/illegal.s: Ditto.
+       * testsuite/gas/pru/ldi.d: Ditto.
+       * testsuite/gas/pru/ldi.s: Ditto.
+       * testsuite/gas/pru/ldst.d: Ditto.
+       * testsuite/gas/pru/ldst.s: Ditto.
+       * testsuite/gas/pru/loop.d: Ditto.
+       * testsuite/gas/pru/loop.s: Ditto.
+       * testsuite/gas/pru/misc.d: Ditto.
+       * testsuite/gas/pru/misc.s: Ditto.
+       * testsuite/gas/pru/pru.exp: Ditto.
+       * testsuite/gas/pru/pseudo.d: Ditto.
+       * testsuite/gas/pru/pseudo.s: Ditto.
+       * testsuite/gas/pru/warn_reglabel.l: Ditto.
+       * testsuite/gas/pru/warn_reglabel.s: Ditto.
+       * testsuite/gas/pru/xfr.d: Ditto.
+       * testsuite/gas/pru/xfr.s: Ditto.
+       * testsuite/gas/lns/lns.exp: Mark lns-common-1-alt variant for PRU.
+
 2016-12-23  Maciej W. Rozycki  <macro@imgtec.com>
 
        * testsuite/gas/mips/mips16-asmacro.d: New test.
index cac7c7f8fdc67d79f9fc1e3537089076ec0a99e4..3bfab34959dff0d0dfdaaa9b4c6b517cbe0b569a 100644 (file)
@@ -177,6 +177,7 @@ TARGET_CPU_CFILES = \
        config/tc-pdp11.c \
        config/tc-pj.c \
        config/tc-ppc.c \
+       config/tc-pru.c \
        config/tc-riscv.c \
        config/tc-rl78.c \
        config/tc-rx.c \
@@ -251,6 +252,7 @@ TARGET_CPU_HFILES = \
        config/tc-pdp11.h \
        config/tc-pj.h \
        config/tc-ppc.h \
+       config/tc-pru.h \
        config/tc-riscv.h \
        config/tc-rl78.h \
        config/tc-rx.h \
index 70578ad72fb4eab6010cb65c3718e7b1aa063c8d..a3962dabb27014aad4aa29c8612e1a1c4b093f29 100644 (file)
@@ -473,6 +473,7 @@ TARGET_CPU_CFILES = \
        config/tc-pdp11.c \
        config/tc-pj.c \
        config/tc-ppc.c \
+       config/tc-pru.c \
        config/tc-riscv.c \
        config/tc-rl78.c \
        config/tc-rx.c \
@@ -547,6 +548,7 @@ TARGET_CPU_HFILES = \
        config/tc-pdp11.h \
        config/tc-pj.h \
        config/tc-ppc.h \
+       config/tc-pru.h \
        config/tc-riscv.h \
        config/tc-rl78.h \
        config/tc-rx.h \
@@ -906,6 +908,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-pdp11.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-pj.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-ppc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-pru.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-riscv.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-rl78.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-rx.Po@am__quote@
@@ -1627,6 +1630,20 @@ tc-ppc.obj: config/tc-ppc.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-ppc.obj `if test -f 'config/tc-ppc.c'; then $(CYGPATH_W) 'config/tc-ppc.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-ppc.c'; fi`
 
+tc-pru.o: config/tc-pru.c
+@am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-pru.o -MD -MP -MF $(DEPDIR)/tc-pru.Tpo -c -o tc-pru.o `test -f 'config/tc-pru.c' || echo '$(srcdir)/'`config/tc-pru.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/tc-pru.Tpo $(DEPDIR)/tc-pru.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='config/tc-pru.c' object='tc-pru.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-pru.o `test -f 'config/tc-pru.c' || echo '$(srcdir)/'`config/tc-pru.c
+
+tc-pru.obj: config/tc-pru.c
+@am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-pru.obj -MD -MP -MF $(DEPDIR)/tc-pru.Tpo -c -o tc-pru.obj `if test -f 'config/tc-pru.c'; then $(CYGPATH_W) 'config/tc-pru.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-pru.c'; fi`
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/tc-pru.Tpo $(DEPDIR)/tc-pru.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='config/tc-pru.c' object='tc-pru.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-pru.obj `if test -f 'config/tc-pru.c'; then $(CYGPATH_W) 'config/tc-pru.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-pru.c'; fi`
+
 tc-riscv.o: config/tc-riscv.c
 @am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-riscv.o -MD -MP -MF $(DEPDIR)/tc-riscv.Tpo -c -o tc-riscv.o `test -f 'config/tc-riscv.c' || echo '$(srcdir)/'`config/tc-riscv.c
 @am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/tc-riscv.Tpo $(DEPDIR)/tc-riscv.Po
index 8a62c2f272a962c7328c3f0bb49ddb2d3b287859..0d62a6dbba0aba47bb349d1d2fa5ccfcd093a708 100644 (file)
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,7 @@
 -*- text -*-
 
+* Add support for the Texas Instruments PRU processor.
+
 Changes in 2.28:
 
 * Add support for the RISC-V architecture.
index 8d80c77f1535e03a39602702df6f80d7cfee6a97..a1b882e6555e90265d3dbd837f280443e3024574 100644 (file)
 #include "elf/nios2.h"
 #endif
 
+#ifdef TC_PRU
+#include "elf/pru.h"
+#endif
+
 static void obj_elf_line (int);
 static void obj_elf_size (int);
 static void obj_elf_type (int);
diff --git a/gas/config/tc-pru.c b/gas/config/tc-pru.c
new file mode 100644 (file)
index 0000000..63aca6e
--- /dev/null
@@ -0,0 +1,1946 @@
+/* TI PRU assembler.
+   Copyright (C) 2014-2016 Free Software Foundation, Inc.
+   Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
+   Based on tc-nios2.c
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "bfd_stdint.h"
+#include "opcode/pru.h"
+#include "elf/pru.h"
+#include "tc-pru.h"
+#include "bfd.h"
+#include "dwarf2dbg.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+#include "dw2gencfi.h"
+
+#ifndef OBJ_ELF
+/* We are not supporting any other target so we throw a compile time error.  */
+  #error "OBJ_ELF not defined"
+#endif
+
+/* This array holds the chars that always start a comment.  If the
+   pre-processor is disabled, these aren't very useful.  */
+const char comment_chars[] = "#;";
+
+/* This array holds the chars that only start a comment at the beginning of
+   a line.  If the line seems to have the form '# 123 filename'
+   .line and .file directives will appear in the pre-processed output.  */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+   first line of the input file.  This is because the compiler outputs
+   #NO_APP at the beginning of its output.  */
+/* Also note that C style comments are always supported.  */
+const char line_comment_chars[] = "#;*";
+
+/* This array holds machine specific line separator characters.  */
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums.  */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant.
+   As in 0f12.456
+   or   0d1.2345e12  */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Machine-dependent command-line options.  */
+
+struct pru_opt_s
+{
+  /* -mno-link-relax / -mlink-relax: generate (or not)
+     relocations for linker relaxation.  */
+  bfd_boolean link_relax;
+
+  /* -mno-warn-regname-label: do not output a warning that a label name
+     matches a register name.  */
+  bfd_boolean warn_regname_label;
+};
+
+static struct pru_opt_s pru_opt = { TRUE, TRUE };
+
+const char *md_shortopts = "r";
+
+enum options
+{
+  OPTION_LINK_RELAX = OPTION_MD_BASE + 1,
+  OPTION_NO_LINK_RELAX,
+  OPTION_NO_WARN_REGNAME_LABEL,
+};
+
+struct option md_longopts[] = {
+  { "mlink-relax",  no_argument, NULL, OPTION_LINK_RELAX  },
+  { "mno-link-relax",  no_argument, NULL, OPTION_NO_LINK_RELAX  },
+  { "mno-warn-regname-label",  no_argument, NULL,
+    OPTION_NO_WARN_REGNAME_LABEL  },
+  { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+typedef struct pru_insn_reloc
+{
+  /* Any expression in the instruction is parsed into this field,
+     which is passed to fix_new_exp () to generate a fixup.  */
+  expressionS reloc_expression;
+
+  /* The type of the relocation to be applied.  */
+  bfd_reloc_code_real_type reloc_type;
+
+  /* PC-relative.  */
+  unsigned int reloc_pcrel;
+
+  /* The next relocation to be applied to the instruction.  */
+  struct pru_insn_reloc *reloc_next;
+} pru_insn_relocS;
+
+/* This struct is used to hold state when assembling instructions.  */
+typedef struct pru_insn_info
+{
+  /* Assembled instruction.  */
+  unsigned long insn_code;
+  /* Used for assembling LDI32.  */
+  unsigned long ldi32_imm32;
+
+  /* Pointer to the relevant bit of the opcode table.  */
+  const struct pru_opcode *insn_pru_opcode;
+  /* After parsing ptrs to the tokens in the instruction fill this array
+     it is terminated with a null pointer (hence the first +1).
+     The second +1 is because in some parts of the code the opcode
+     is not counted as a token, but still placed in this array.  */
+  const char *insn_tokens[PRU_MAX_INSN_TOKENS + 1 + 1];
+
+  /* This holds information used to generate fixups
+     and eventually relocations if it is not null.  */
+  pru_insn_relocS *insn_reloc;
+} pru_insn_infoS;
+
+/* Opcode hash table.  */
+static struct hash_control *pru_opcode_hash = NULL;
+#define pru_opcode_lookup(NAME) \
+  ((struct pru_opcode *) hash_find (pru_opcode_hash, (NAME)))
+
+/* Register hash table.  */
+static struct hash_control *pru_reg_hash = NULL;
+#define pru_reg_lookup(NAME) \
+  ((struct pru_reg *) hash_find (pru_reg_hash, (NAME)))
+
+/* The known current alignment of the current section.  */
+static int pru_current_align;
+static segT pru_current_align_seg;
+
+static int pru_auto_align_on = 1;
+
+/* The last seen label in the current section.  This is used to auto-align
+   labels preceeding instructions.  */
+static symbolS *pru_last_label;
+
+\f
+/** Utility routines.  */
+/* Function md_chars_to_number takes the sequence of
+   bytes in buf and returns the corresponding value
+   in an int.  n must be 1, 2, 4 or 8.  */
+static uint64_t
+md_chars_to_number (char *buf, int n)
+{
+  int i;
+  uint64_t val;
+
+  gas_assert (n == 1 || n == 2 || n == 4 || n == 8);
+
+  val = 0;
+  for (i = 0; i < n; ++i)
+    val = val | ((buf[i] & 0xff) << 8 * i);
+  return val;
+}
+
+
+/* This function turns a C long int, short int or char
+   into the series of bytes that represent the number
+   on the target machine.  */
+void
+md_number_to_chars (char *buf, uint64_t val, int n)
+{
+  gas_assert (n == 1 || n == 2 || n == 4 || n == 8);
+  number_to_chars_littleendian (buf, val, n);
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+   of type TYPE, and store the appropriate bytes in *LITP.  The number
+   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
+   returned, or NULL on OK.  */
+const char *
+md_atof (int type, char *litP, int *sizeP)
+{
+  return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+/* Return true if STR starts with PREFIX, which should be a string literal.  */
+#define strprefix(STR, PREFIX) \
+  (strncmp ((STR), PREFIX, strlen (PREFIX)) == 0)
+
+/* nop fill pattern for text section.  */
+static char const nop[4] = { 0xe0, 0xe0, 0xe0, 0x12 };
+
+/* Handles all machine-dependent alignment needs.  */
+static void
+pru_align (int log_size, const char *pfill, symbolS *label)
+{
+  int align;
+  long max_alignment = 15;
+
+  /* The front end is prone to changing segments out from under us
+     temporarily when -g is in effect.  */
+  int switched_seg_p = (pru_current_align_seg != now_seg);
+
+  align = log_size;
+  if (align > max_alignment)
+    {
+      align = max_alignment;
+      as_bad (_("Alignment too large: %d assumed"), align);
+    }
+  else if (align < 0)
+    {
+      as_warn (_("Alignment negative: 0 assumed"));
+      align = 0;
+    }
+
+  if (align != 0)
+    {
+      if (subseg_text_p (now_seg) && align >= 2)
+       {
+         /* First, make sure we're on a four-byte boundary, in case
+            someone has been putting .byte values the text section.  */
+         if (pru_current_align < 2 || switched_seg_p)
+           frag_align (2, 0, 0);
+
+         /* Now fill in the alignment pattern.  */
+         if (pfill != NULL)
+           frag_align_pattern (align, pfill, sizeof nop, 0);
+         else
+           frag_align (align, 0, 0);
+       }
+      else
+       frag_align (align, 0, 0);
+
+      if (!switched_seg_p)
+       pru_current_align = align;
+
+      /* If the last label was in a different section we can't align it.  */
+      if (label != NULL && !switched_seg_p)
+       {
+         symbolS *sym;
+         int label_seen = FALSE;
+         struct frag *old_frag;
+         valueT old_value;
+         valueT new_value;
+
+         gas_assert (S_GET_SEGMENT (label) == now_seg);
+
+         old_frag = symbol_get_frag (label);
+         old_value = S_GET_VALUE (label);
+         new_value = (valueT) frag_now_fix ();
+
+         /* It is possible to have more than one label at a particular
+            address, especially if debugging is enabled, so we must
+            take care to adjust all the labels at this address in this
+            fragment.  To save time we search from the end of the symbol
+            list, backwards, since the symbols we are interested in are
+            almost certainly the ones that were most recently added.
+            Also to save time we stop searching once we have seen at least
+            one matching label, and we encounter a label that is no longer
+            in the target fragment.  Note, this search is guaranteed to
+            find at least one match when sym == label, so no special case
+            code is necessary.  */
+         for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym))
+           if (symbol_get_frag (sym) == old_frag
+               && S_GET_VALUE (sym) == old_value)
+             {
+               label_seen = TRUE;
+               symbol_set_frag (sym, frag_now);
+               S_SET_VALUE (sym, new_value);
+             }
+           else if (label_seen && symbol_get_frag (sym) != old_frag)
+             break;
+       }
+      record_alignment (now_seg, align);
+    }
+}
+
+\f
+/** Support for self-check mode.  */
+
+/* Mode of the assembler.  */
+typedef enum
+{
+  PRU_MODE_ASSEMBLE,           /* Ordinary operation.  */
+  PRU_MODE_TEST                /* Hidden mode used for self testing.  */
+} PRU_MODE;
+
+static PRU_MODE pru_mode = PRU_MODE_ASSEMBLE;
+
+/* This function is used to in self-checking mode
+   to check the assembled instruction
+   opcode should be the assembled opcode, and exp_opcode
+   the parsed string representing the expected opcode.  */
+static void
+pru_check_assembly (unsigned int opcode, const char *exp_opcode)
+{
+  if (pru_mode == PRU_MODE_TEST)
+    {
+      if (exp_opcode == NULL)
+       as_bad (_("expecting opcode string in self test mode"));
+      else if (opcode != strtoul (exp_opcode, NULL, 16))
+       as_bad (_("assembly 0x%08x, expected %s"), opcode, exp_opcode);
+    }
+}
+
+\f
+/** Support for machine-dependent assembler directives.  */
+/* Handle the .align pseudo-op.  This aligns to a power of two.  It
+   also adjusts any current instruction label.  We treat this the same
+   way the MIPS port does: .align 0 turns off auto alignment.  */
+static void
+s_pru_align (int ignore ATTRIBUTE_UNUSED)
+{
+  int align;
+  char fill;
+  const char *pfill = NULL;
+  long max_alignment = 15;
+
+  align = get_absolute_expression ();
+  if (align > max_alignment)
+    {
+      align = max_alignment;
+      as_bad (_("Alignment too large: %d assumed"), align);
+    }
+  else if (align < 0)
+    {
+      as_warn (_("Alignment negative: 0 assumed"));
+      align = 0;
+    }
+
+  if (*input_line_pointer == ',')
+    {
+      input_line_pointer++;
+      fill = get_absolute_expression ();
+      pfill = (const char *) &fill;
+    }
+  else if (subseg_text_p (now_seg))
+    pfill = (const char *) &nop;
+  else
+    {
+      pfill = NULL;
+      pru_last_label = NULL;
+    }
+
+  if (align != 0)
+    {
+      pru_auto_align_on = 1;
+      pru_align (align, pfill, pru_last_label);
+      pru_last_label = NULL;
+    }
+  else
+    pru_auto_align_on = 0;
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .text pseudo-op.  This is like the usual one, but it
+   clears the saved last label and resets known alignment.  */
+static void
+s_pru_text (int i)
+{
+  s_text (i);
+  pru_last_label = NULL;
+  pru_current_align = 0;
+  pru_current_align_seg = now_seg;
+}
+
+/* Handle the .data pseudo-op.  This is like the usual one, but it
+   clears the saved last label and resets known alignment.  */
+static void
+s_pru_data (int i)
+{
+  s_data (i);
+  pru_last_label = NULL;
+  pru_current_align = 0;
+  pru_current_align_seg = now_seg;
+}
+
+/* Handle the .section pseudo-op.  This is like the usual one, but it
+   clears the saved last label and resets known alignment.  */
+static void
+s_pru_section (int ignore)
+{
+  obj_elf_section (ignore);
+  pru_last_label = NULL;
+  pru_current_align = 0;
+  pru_current_align_seg = now_seg;
+}
+
+/* Explicitly unaligned cons.  */
+static void
+s_pru_ucons (int nbytes)
+{
+  int hold;
+  hold = pru_auto_align_on;
+  pru_auto_align_on = 0;
+  cons (nbytes);
+  pru_auto_align_on = hold;
+}
+
+/* .set sets assembler options.  */
+static void
+s_pru_set (int equiv)
+{
+  char *save = input_line_pointer;
+  char *directive;
+  char delim = get_symbol_name (&directive);
+  char *endline = input_line_pointer;
+
+  (void) restore_line_pointer (delim);
+
+  /* We only want to handle ".set XXX" if the
+     user has tried ".set XXX, YYY" they are not
+     trying a directive.  This prevents
+     us from polluting the name space.  */
+  SKIP_WHITESPACE ();
+  if (is_end_of_line[(unsigned char) *input_line_pointer])
+    {
+      bfd_boolean done = TRUE;
+      *endline = 0;
+
+      if (!strcmp (directive, "no_warn_regname_label"))
+         pru_opt.warn_regname_label = FALSE;
+      else
+       done = FALSE;
+
+      if (done)
+       {
+         *endline = delim;
+         demand_empty_rest_of_line ();
+         return;
+       }
+    }
+
+  /* If we fall through to here, either we have ".set XXX, YYY"
+     or we have ".set XXX" where XXX is unknown or we have
+     a syntax error.  */
+  input_line_pointer = save;
+  s_set (equiv);
+}
+
+/* Machine-dependent assembler directives.
+   Format of each entry is:
+   { "directive", handler_func, param }         */
+const pseudo_typeS md_pseudo_table[] = {
+  {"align", s_pru_align, 0},
+  {"text", s_pru_text, 0},
+  {"data", s_pru_data, 0},
+  {"section", s_pru_section, 0},
+  {"section.s", s_pru_section, 0},
+  {"sect", s_pru_section, 0},
+  {"sect.s", s_pru_section, 0},
+  /* .dword and .half are included for compatibility with MIPS.  */
+  {"dword", cons, 8},
+  {"half", cons, 2},
+  /* PRU native word size is 4 bytes, so we override
+     the GAS default of 2.  */
+  {"word", cons, 4},
+  /* Explicitly unaligned directives.  */
+  {"2byte", s_pru_ucons, 2},
+  {"4byte", s_pru_ucons, 4},
+  {"8byte", s_pru_ucons, 8},
+  {"16byte", s_pru_ucons, 16},
+  {"set", s_pru_set, 0},
+  {NULL, NULL, 0}
+};
+
+\f
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+                              asection *seg ATTRIBUTE_UNUSED)
+{
+  abort ();
+  return 0;
+}
+
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED,
+                fragS *fragp ATTRIBUTE_UNUSED)
+{
+  abort ();
+}
+
+\f
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+  return ((sec->flags & SEC_DEBUGGING) == 0
+         && (sec->flags & SEC_CODE) != 0
+         && (sec->flags & SEC_ALLOC) != 0);
+}
+
+/* Does whatever the xtensa port does.  */
+int
+pru_validate_fix_sub (fixS *fix)
+{
+  segT add_symbol_segment, sub_symbol_segment;
+
+  /* The difference of two symbols should be resolved by the assembler when
+     linkrelax is not set.  If the linker may relax the section containing
+     the symbols, then an Xtensa DIFF relocation must be generated so that
+     the linker knows to adjust the difference value.  */
+  if (!linkrelax || fix->fx_addsy == NULL)
+    return 0;
+
+  /* Make sure both symbols are in the same segment, and that segment is
+     "normal" and relaxable.  If the segment is not "normal", then the
+     fix is not valid.  If the segment is not "relaxable", then the fix
+     should have been handled earlier.  */
+  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+  if (! SEG_NORMAL (add_symbol_segment)
+      || ! relaxable_section (add_symbol_segment))
+    return 0;
+
+  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+  return (sub_symbol_segment == add_symbol_segment);
+}
+
+/* TC_FORCE_RELOCATION hook.  */
+
+/* If linkrelax is turned on, and the symbol to relocate
+   against is in a relaxable segment, don't compute the value -
+   generate a relocation instead.  */
+int
+pru_force_relocation (fixS *fix)
+{
+  if (linkrelax && fix->fx_addsy
+      && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+    return 1;
+
+  return generic_force_reloc (fix);
+}
+
+
+\f
+/** Fixups and overflow checking.  */
+
+/* Check a fixup for overflow.  */
+static bfd_reloc_status_type
+pru_check_overflow (valueT fixup, reloc_howto_type *howto)
+{
+  bfd_reloc_status_type ret;
+
+  ret = bfd_check_overflow (howto->complain_on_overflow,
+                           howto->bitsize,
+                           howto->rightshift,
+                           bfd_get_reloc_size (howto) * 8,
+                           fixup);
+
+  return ret;
+}
+
+/* Emit diagnostic for fixup overflow.  */
+static void
+pru_diagnose_overflow (valueT fixup, reloc_howto_type *howto,
+                        fixS *fixP, valueT value)
+{
+  if (fixP->fx_r_type == BFD_RELOC_8
+      || fixP->fx_r_type == BFD_RELOC_16
+      || fixP->fx_r_type == BFD_RELOC_32)
+    /* These relocs are against data, not instructions.  */
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("immediate value 0x%x truncated to 0x%x"),
+                 (unsigned int) fixup,
+                 (unsigned int) (~(~(valueT) 0 << howto->bitsize) & fixup));
+  else
+    {
+      /* What opcode is the instruction?  This will determine
+        whether we check for overflow in immediate values
+        and what error message we get.  */
+      const struct pru_opcode *opcode;
+      enum overflow_type overflow_msg_type;
+      unsigned int range_min;
+      unsigned int range_max;
+      unsigned int address;
+      gas_assert (fixP->fx_size == 4);
+      opcode = pru_find_opcode (value);
+      gas_assert (opcode);
+      overflow_msg_type = opcode->overflow_msg;
+      switch (overflow_msg_type)
+       {
+       case call_target_overflow:
+         range_min
+           = ((fixP->fx_frag->fr_address + fixP->fx_where) & 0xf0000000);
+         range_max = range_min + 0x0fffffff;
+         address = fixup | range_min;
+
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("call target address 0x%08x out of range 0x%08x to 0x%08x"),
+                       address, range_min, range_max);
+         break;
+       case qbranch_target_overflow:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("quick branch offset %d out of range %d to %d"),
+                       (int)fixup, -((1<<9) * 4), (1 << 9) * 4);
+         break;
+       case address_offset_overflow:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("%s offset %d out of range %d to %d"),
+                       opcode->name, (int)fixup, -32768, 32767);
+         break;
+       case signed_immed16_overflow:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("immediate value %d out of range %d to %d"),
+                       (int)fixup, -32768, 32767);
+         break;
+       case unsigned_immed32_overflow:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("immediate value %llu out of range %u to %lu"),
+                       (unsigned long long)fixup, 0, 0xfffffffflu);
+         break;
+       case unsigned_immed16_overflow:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("immediate value %u out of range %u to %u"),
+                       (unsigned int)fixup, 0, 65535);
+         break;
+       case unsigned_immed5_overflow:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("immediate value %u out of range %u to %u"),
+                       (unsigned int)fixup, 0, 31);
+         break;
+       default:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("overflow in immediate argument"));
+         break;
+       }
+    }
+}
+
+/* Apply a fixup to the object file.  */
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+  unsigned char *where;
+  valueT value = *valP;
+  long n;
+
+  /* Assert that the fixup is one we can handle.  */
+  gas_assert (fixP != NULL && valP != NULL
+             && (fixP->fx_r_type == BFD_RELOC_8
+                 || fixP->fx_r_type == BFD_RELOC_16
+                 || fixP->fx_r_type == BFD_RELOC_32
+                 || fixP->fx_r_type == BFD_RELOC_64
+                 || fixP->fx_r_type == BFD_RELOC_PRU_LDI32
+                 || fixP->fx_r_type == BFD_RELOC_PRU_U16
+                 || fixP->fx_r_type == BFD_RELOC_PRU_U16_PMEMIMM
+                 || fixP->fx_r_type == BFD_RELOC_PRU_S10_PCREL
+                 || fixP->fx_r_type == BFD_RELOC_PRU_U8_PCREL
+                 || fixP->fx_r_type == BFD_RELOC_PRU_32_PMEM
+                 || fixP->fx_r_type == BFD_RELOC_PRU_16_PMEM
+                 /* Add other relocs here as we generate them.  */
+             ));
+
+  if (fixP->fx_r_type == BFD_RELOC_64)
+    {
+      /* We may reach here due to .8byte directives, but we never output
+        BFD_RELOC_64; it must be resolved.  */
+      if (fixP->fx_addsy != NULL)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("cannot create 64-bit relocation"));
+      else
+       {
+         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                             *valP, 8);
+         fixP->fx_done = 1;
+       }
+      return;
+    }
+
+  /* gas_assert (had_errors () || !fixP->fx_subsy); */
+
+  /* In general, fix instructions with immediate
+     constants.  But leave LDI32 for the linker,
+     which is prepared to shorten insns.  */
+  if (fixP->fx_addsy == (symbolS *) NULL
+      && fixP->fx_r_type != BFD_RELOC_PRU_LDI32)
+    fixP->fx_done = 1;
+
+  else if (fixP->fx_pcrel)
+    {
+      segT s = S_GET_SEGMENT (fixP->fx_addsy);
+
+      if (s == seg || s == absolute_section)
+       {
+         /* Blindly copied from AVR, but I don't understand why
+            this is needed in the first place.  Fail hard to catch
+            when this curious code snippet is utilized.  */
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("unexpected PC relative expression"));
+         value += S_GET_VALUE (fixP->fx_addsy);
+         fixP->fx_done = 1;
+       }
+    }
+  else if (linkrelax && fixP->fx_subsy)
+    {
+      /* For a subtraction relocation expression, generate one
+        of the DIFF relocs, with the value being the difference.
+        Note that a sym1 - sym2 expression is adjusted into a
+        section_start_sym + sym4_offset_from_section_start - sym1
+        expression.  fixP->fx_addsy holds the section start symbol,
+        fixP->fx_offset holds sym2's offset, and fixP->fx_subsy
+        holds sym1.  Calculate the current difference and write value,
+        but leave fx_offset as is - during relaxation,
+        fx_offset - value gives sym1's value.  */
+
+      offsetT diffval; /* valueT is unsigned, so use offsetT.  */
+
+      diffval = S_GET_VALUE (fixP->fx_addsy)
+               + fixP->fx_offset - S_GET_VALUE (fixP->fx_subsy);
+
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_8:
+         fixP->fx_r_type = BFD_RELOC_PRU_GNU_DIFF8;
+         break;
+       case BFD_RELOC_16:
+         fixP->fx_r_type = BFD_RELOC_PRU_GNU_DIFF16;
+         break;
+       case BFD_RELOC_32:
+         fixP->fx_r_type = BFD_RELOC_PRU_GNU_DIFF32;
+         break;
+       case BFD_RELOC_PRU_16_PMEM:
+         fixP->fx_r_type = BFD_RELOC_PRU_GNU_DIFF16_PMEM;
+         if (diffval % 4)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("residual low bits in pmem diff relocation"));
+         diffval /= 4;
+         break;
+       case BFD_RELOC_PRU_32_PMEM:
+         fixP->fx_r_type = BFD_RELOC_PRU_GNU_DIFF32_PMEM;
+         if (diffval % 4)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("residual low bits in pmem diff relocation"));
+         diffval /= 4;
+         break;
+       default:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("expression too complex"));
+         break;
+       }
+
+      value = *valP = diffval;
+
+      fixP->fx_subsy = NULL;
+  }
+  /* We don't actually support subtracting a symbol.  */
+  if (fixP->fx_subsy != (symbolS *) NULL)
+    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+  /* For the DIFF relocs, write the value into the object file while still
+     keeping fx_done FALSE, as both the difference (recorded in the object file)
+     and the sym offset (part of fixP) are needed at link relax time.  */
+  where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_PRU_GNU_DIFF8:
+      *where = value;
+      break;
+    case BFD_RELOC_PRU_GNU_DIFF16:
+    case BFD_RELOC_PRU_GNU_DIFF16_PMEM:
+      bfd_putl16 ((bfd_vma) value, where);
+      break;
+    case BFD_RELOC_PRU_GNU_DIFF32:
+    case BFD_RELOC_PRU_GNU_DIFF32_PMEM:
+      bfd_putl32 ((bfd_vma) value, where);
+      break;
+    default:
+      break;
+    }
+
+  if (fixP->fx_done)
+    /* Fully resolved fixup.  */
+    {
+      reloc_howto_type *howto
+       = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+
+      if (howto == NULL)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("relocation is not supported"));
+      else
+       {
+         valueT fixup = value;
+         uint64_t insn;
+         char *buf;
+
+         /* Get the instruction or data to be fixed up.  */
+         buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+         insn = md_chars_to_number (buf, fixP->fx_size);
+
+         /* Check for overflow, emitting a diagnostic if necessary.  */
+         if (pru_check_overflow (fixup, howto) != bfd_reloc_ok)
+           pru_diagnose_overflow (fixup, howto, fixP, insn);
+
+         /* Apply the right shift.  */
+         fixup = ((offsetT)fixup) >> howto->rightshift;
+
+         /* Truncate the fixup to right size.  */
+         n = sizeof (fixup) * 8 - howto->bitsize;
+         fixup = (fixup << n) >> n;
+
+         /* Fix up the instruction.  Non-contiguous bitfields need
+            special handling.  */
+         if (fixP->fx_r_type == BFD_RELOC_PRU_S10_PCREL)
+           SET_BROFF_URAW (insn, fixup);
+         else if (fixP->fx_r_type == BFD_RELOC_PRU_LDI32)
+           {
+             /* As the only 64-bit "insn", LDI32 needs special handling. */
+             uint32_t insn1 = insn & 0xffffffff;
+             uint32_t insn2 = insn >> 32;
+             SET_INSN_FIELD (IMM16, insn1, fixup & 0xffff);
+             SET_INSN_FIELD (IMM16, insn2, fixup >> 16);
+             insn = insn1 | ((uint64_t)insn2 << 32);
+           }
+         else
+           insn = (insn & ~howto->dst_mask) | (fixup << howto->bitpos);
+         md_number_to_chars (buf, insn, fixP->fx_size);
+       }
+
+      fixP->fx_done = 1;
+    }
+
+  if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT)
+    {
+      fixP->fx_done = 0;
+      if (fixP->fx_addsy
+         && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy))
+       S_SET_WEAK (fixP->fx_addsy);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    fixP->fx_done = 0;
+}
+
+
+\f
+/** Instruction parsing support.  */
+
+/* Creates a new pru_insn_relocS and returns a pointer to it.  */
+static pru_insn_relocS *
+pru_insn_reloc_new (bfd_reloc_code_real_type reloc_type, unsigned int pcrel)
+{
+  pru_insn_relocS *retval;
+  retval = XNEW (pru_insn_relocS);
+  if (retval == NULL)
+    {
+      as_bad (_("can't create relocation"));
+      abort ();
+    }
+
+  /* Fill out the fields with default values.  */
+  retval->reloc_next = NULL;
+  retval->reloc_type = reloc_type;
+  retval->reloc_pcrel = pcrel;
+  return retval;
+}
+
+/* Frees up memory previously allocated by pru_insn_reloc_new ().  */
+static void
+pru_insn_reloc_destroy (pru_insn_relocS *reloc)
+{
+  pru_insn_relocS *next;
+
+  while (reloc)
+    {
+      next = reloc->reloc_next;
+      free (reloc);
+      reloc = next;
+    }
+}
+
+/* The various pru_assemble_* functions call this
+   function to generate an expression from a string representing an expression.
+   It then tries to evaluate the expression, and if it can, returns its value.
+   If not, it creates a new pru_insn_relocS and stores the expression and
+   reloc_type for future use.  */
+static unsigned long
+pru_assemble_expression (const char *exprstr,
+                          pru_insn_infoS *insn,
+                          pru_insn_relocS *prev_reloc,
+                          bfd_reloc_code_real_type reloc_type,
+                          unsigned int pcrel)
+{
+  expressionS *ep;
+  pru_insn_relocS *reloc;
+  char *saved_line_ptr;
+  unsigned short value;
+
+  gas_assert (exprstr != NULL);
+  gas_assert (insn != NULL);
+
+  /* We use this blank keyword to distinguish register from
+     label operands.  */
+  if (strstr (exprstr, "%label") != NULL)
+    {
+      exprstr += strlen ("%label") + 1;
+    }
+
+  /* Check for pmem relocation operator.
+     Change the relocation type and advance the ptr to the start of
+     the expression proper.  */
+  if (strstr (exprstr, "%pmem") != NULL)
+    {
+      reloc_type = BFD_RELOC_PRU_U16_PMEMIMM;
+      exprstr += strlen ("%pmem") + 1;
+    }
+
+  /* We potentially have a relocation.  */
+  reloc = pru_insn_reloc_new (reloc_type, pcrel);
+  if (prev_reloc != NULL)
+    prev_reloc->reloc_next = reloc;
+  else
+    insn->insn_reloc = reloc;
+
+  /* Parse the expression string.  */
+  ep = &reloc->reloc_expression;
+  saved_line_ptr = input_line_pointer;
+  input_line_pointer = (char *) exprstr;
+  SKIP_WHITESPACE ();
+  expression (ep);
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer)
+    as_bad (_("trailing garbage after expression: %s"), input_line_pointer);
+  input_line_pointer = saved_line_ptr;
+
+
+  if (ep->X_op == O_illegal || ep->X_op == O_absent)
+    as_bad (_("expected expression, got %s"), exprstr);
+
+  /* This is redundant as the fixup will put this into
+     the instruction, but it is included here so that
+     self-test mode (-r) works.  */
+  value = 0;
+  if (pru_mode == PRU_MODE_TEST && ep->X_op == O_constant)
+    value = ep->X_add_number;
+
+  return (unsigned long) value;
+}
+
+/* Try to parse a non-relocatable expression.  */
+static unsigned long
+pru_assemble_noreloc_expression (const char *exprstr)
+{
+  expressionS exp;
+  char *saved_line_ptr;
+  unsigned long val;
+
+  gas_assert (exprstr != NULL);
+
+  saved_line_ptr = input_line_pointer;
+  input_line_pointer = (char *) exprstr;
+  SKIP_WHITESPACE ();
+  expression (&exp);
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer)
+    as_bad (_("trailing garbage after expression: %s"), input_line_pointer);
+  input_line_pointer = saved_line_ptr;
+
+  val = 0;
+  if (exp.X_op != O_constant)
+    as_bad (_("expected constant expression, got %s"), exprstr);
+  else
+    val = exp.X_add_number;
+
+  return val;
+}
+
+/* Argument assemble functions.
+   All take an instruction argument string, and a pointer
+   to an instruction opcode.  Upon return the insn_opcode
+   has the relevant fields filled in to represent the arg
+   string.  The return value is NULL if successful, or
+   an error message if an error was detected.  */
+
+static void
+pru_assemble_arg_d (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *dst = pru_reg_lookup (argstr);
+
+  if (dst == NULL)
+    as_bad (_("unknown register %s"), argstr);
+  else
+    {
+      SET_INSN_FIELD (RD, insn_info->insn_code, dst->index);
+      SET_INSN_FIELD (RDSEL, insn_info->insn_code, dst->regsel);
+    }
+}
+
+static void
+pru_assemble_arg_D (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *dst;
+
+  /* The leading & before an address register is optional.  */
+  if (*argstr == '&')
+    argstr++;
+
+  dst = pru_reg_lookup (argstr);
+
+  if (dst == NULL)
+    as_bad (_("unknown register %s"), argstr);
+  else
+    {
+      unsigned long rxb = 0;
+
+      switch (dst->regsel)
+       {
+       case RSEL_31_0: rxb = 0; break; /* whole register defaults to .b0  */
+       case RSEL_7_0: rxb = 0; break;
+       case RSEL_15_8: rxb = 1; break;
+       case RSEL_23_16: rxb = 2; break;
+       case RSEL_31_24: rxb = 3; break;
+       default:
+         as_bad (_("data transfer register cannot be halfword"));
+       }
+
+      SET_INSN_FIELD (RD, insn_info->insn_code, dst->index);
+      SET_INSN_FIELD (RDB, insn_info->insn_code, rxb);
+    }
+}
+
+static void
+pru_assemble_arg_R (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *dst = pru_reg_lookup (argstr);
+
+  if (dst == NULL)
+    as_bad (_("unknown register %s"), argstr);
+  else
+    {
+      if (dst->regsel != RSEL_31_0)
+       {
+         as_bad (_("destination register must be full-word"));
+       }
+
+      SET_INSN_FIELD (RD, insn_info->insn_code, dst->index);
+      SET_INSN_FIELD (RDSEL, insn_info->insn_code, dst->regsel);
+    }
+}
+
+static void
+pru_assemble_arg_s (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *src1 = pru_reg_lookup (argstr);
+
+  if (src1 == NULL)
+    as_bad (_("unknown register %s"), argstr);
+  else
+    {
+      SET_INSN_FIELD (RS1, insn_info->insn_code, src1->index);
+      SET_INSN_FIELD (RS1SEL, insn_info->insn_code, src1->regsel);
+    }
+}
+
+static void
+pru_assemble_arg_S (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *src1 = pru_reg_lookup (argstr);
+
+  if (src1 == NULL)
+    as_bad (_("unknown register %s"), argstr);
+  else
+    {
+      if (src1->regsel != RSEL_31_0)
+       as_bad (_("cannot use partial register %s for addressing"), argstr);
+      SET_INSN_FIELD (RS1, insn_info->insn_code, src1->index);
+    }
+}
+
+static void
+pru_assemble_arg_b (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *src2 = pru_reg_lookup (argstr);
+  if (src2 == NULL)
+    {
+      unsigned long imm8 = pru_assemble_noreloc_expression (argstr);
+      SET_INSN_FIELD (IMM8, insn_info->insn_code, imm8);
+      SET_INSN_FIELD (IO, insn_info->insn_code, 1);
+    }
+  else
+    {
+      SET_INSN_FIELD (IO, insn_info->insn_code, 0);
+      SET_INSN_FIELD (RS2, insn_info->insn_code, src2->index);
+      SET_INSN_FIELD (RS2SEL, insn_info->insn_code, src2->regsel);
+    }
+
+}
+
+static void
+pru_assemble_arg_B (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *src2 = pru_reg_lookup (argstr);
+  if (src2 == NULL)
+    {
+      unsigned long imm8;
+      imm8 = pru_assemble_noreloc_expression (argstr);
+      if (!imm8 || imm8 > 0xff)
+       as_bad (_("loop count constant %ld is out of range [1..%d]"),
+               imm8, 0xff);
+      /* Note: HW expects the immediate loop count field
+        to be one less than the actual loop count.  */
+      SET_INSN_FIELD (IMM8, insn_info->insn_code, imm8 - 1);
+      SET_INSN_FIELD (IO, insn_info->insn_code, 1);
+    }
+  else
+    {
+      SET_INSN_FIELD (IO, insn_info->insn_code, 0);
+      SET_INSN_FIELD (RS2, insn_info->insn_code, src2->index);
+      SET_INSN_FIELD (RS2SEL, insn_info->insn_code, src2->regsel);
+    }
+}
+
+static void
+pru_assemble_arg_i (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long imm32;
+
+  /* We must not generate PRU_LDI32 relocation if relaxation is disabled in
+     GAS. Consider the following scenario: GAS relaxation is disabled, so
+     DIFF* expressions are fixed and not emitted as relocations. Then if LD
+     has relaxation enabled, it may shorten LDI32 but will not update
+     accordingly the DIFF expressions.  */
+  if (pru_opt.link_relax)
+    imm32 = pru_assemble_expression (argstr, insn_info,
+                                    insn_info->insn_reloc,
+                                    BFD_RELOC_PRU_LDI32, 0);
+  else
+    imm32 = pru_assemble_noreloc_expression (argstr);
+
+  /* QUIRK: LDI must clear IO bit high, even though it has immediate arg. */
+  SET_INSN_FIELD (IO, insn_info->insn_code, 0);
+  SET_INSN_FIELD (IMM16, insn_info->insn_code, imm32 & 0xffff);
+  insn_info->ldi32_imm32 = imm32;
+}
+
+static void
+pru_assemble_arg_j (pru_insn_infoS *insn_info, const char *argstr)
+{
+  struct pru_reg *src2 = pru_reg_lookup (argstr);
+
+  if (src2 == NULL)
+    {
+      unsigned long imm16 = pru_assemble_expression (argstr, insn_info,
+                                                    insn_info->insn_reloc,
+                                                    BFD_RELOC_PRU_U16_PMEMIMM,
+                                                    0);
+      SET_INSN_FIELD (IMM16, insn_info->insn_code, imm16);
+      SET_INSN_FIELD (IO, insn_info->insn_code, 1);
+    }
+  else
+    {
+      SET_INSN_FIELD (IO, insn_info->insn_code, 0);
+      SET_INSN_FIELD (RS2, insn_info->insn_code, src2->index);
+      SET_INSN_FIELD (RS2SEL, insn_info->insn_code, src2->regsel);
+    }
+}
+
+static void
+pru_assemble_arg_W (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long imm16 = pru_assemble_expression (argstr, insn_info,
+                                                insn_info->insn_reloc,
+                                                BFD_RELOC_PRU_U16, 0);
+  /* QUIRK: LDI must clear IO bit high, even though it has immediate arg.  */
+  SET_INSN_FIELD (IO, insn_info->insn_code, 0);
+  SET_INSN_FIELD (IMM16, insn_info->insn_code, imm16);
+}
+
+static void
+pru_assemble_arg_o (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long imm10 = pru_assemble_expression (argstr, insn_info,
+                                                insn_info->insn_reloc,
+                                                BFD_RELOC_PRU_S10_PCREL, 1);
+  SET_BROFF_URAW (insn_info->insn_code, imm10);
+}
+
+static void
+pru_assemble_arg_O (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long imm8 = pru_assemble_expression (argstr, insn_info,
+                                               insn_info->insn_reloc,
+                                               BFD_RELOC_PRU_U8_PCREL, 1);
+  SET_INSN_FIELD (LOOP_JMPOFFS, insn_info->insn_code, imm8);
+}
+
+static void
+pru_assemble_arg_l (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long burstlen = 0;
+  struct pru_reg *blreg = pru_reg_lookup (argstr);
+
+  if (blreg == NULL)
+    {
+      burstlen = pru_assemble_noreloc_expression (argstr);
+      if (!burstlen || burstlen > LSSBBO_BYTECOUNT_R0_BITS7_0)
+       as_bad (_("byte count constant %ld is out of range [1..%d]"),
+               burstlen, LSSBBO_BYTECOUNT_R0_BITS7_0);
+      burstlen--;
+    }
+  else
+    {
+      if (blreg->index != 0)
+       as_bad (_("only r0 can be used as byte count register"));
+      else if (blreg->regsel > RSEL_31_24)
+       as_bad (_("only r0.bX byte fields of r0 can be used as byte count"));
+      else
+       burstlen = LSSBBO_BYTECOUNT_R0_BITS7_0 + blreg->regsel;
+    }
+    SET_BURSTLEN (insn_info->insn_code, burstlen);
+}
+
+static void
+pru_assemble_arg_n (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long burstlen = 0;
+  struct pru_reg *blreg = pru_reg_lookup (argstr);
+
+  if (blreg == NULL)
+    {
+      burstlen = pru_assemble_noreloc_expression (argstr);
+      if (!burstlen || burstlen > LSSBBO_BYTECOUNT_R0_BITS7_0)
+       as_bad (_("byte count constant %ld is out of range [1..%d]"),
+               burstlen, LSSBBO_BYTECOUNT_R0_BITS7_0);
+      burstlen--;
+    }
+  else
+    {
+      if (blreg->index != 0)
+       as_bad (_("only r0 can be used as byte count register"));
+      else if (blreg->regsel > RSEL_31_24)
+       as_bad (_("only r0.bX byte fields of r0 can be used as byte count"));
+      else
+       burstlen = LSSBBO_BYTECOUNT_R0_BITS7_0 + blreg->regsel;
+    }
+    SET_INSN_FIELD (XFR_LENGTH, insn_info->insn_code, burstlen);
+}
+
+static void
+pru_assemble_arg_c (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long cb = pru_assemble_noreloc_expression (argstr);
+
+  if (cb > 31)
+    as_bad (_("invalid constant table offset %ld"), cb);
+  else
+    SET_INSN_FIELD (CB, insn_info->insn_code, cb);
+}
+
+static void
+pru_assemble_arg_w (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long wk = pru_assemble_noreloc_expression (argstr);
+
+  if (wk != 0 && wk != 1)
+    as_bad (_("invalid WakeOnStatus %ld"), wk);
+  else
+    SET_INSN_FIELD (WAKEONSTATUS, insn_info->insn_code, wk);
+}
+
+static void
+pru_assemble_arg_x (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long wba = pru_assemble_noreloc_expression (argstr);
+
+  if (wba > 255)
+    as_bad (_("invalid XFR WideBus Address %ld"), wba);
+  else
+    SET_INSN_FIELD (XFR_WBA, insn_info->insn_code, wba);
+}
+
+/* The function consume_arg takes a pointer into a string
+   of instruction tokens (args) and a pointer into a string
+   representing the expected sequence of tokens and separators.
+   It checks whether the first argument in argstr is of the
+   expected type, throwing an error if it is not, and returns
+   the pointer argstr.  */
+static char *
+pru_consume_arg (char *argstr, const char *parsestr)
+{
+  char *temp;
+
+  switch (*parsestr)
+    {
+    case 'W':
+      if (*argstr == '%')
+       {
+         if (strprefix (argstr, "%pmem") || strprefix (argstr, "%label"))
+           {
+             /* We zap the parentheses because we don't want them confused
+                with separators.  */
+             temp = strchr (argstr, '(');
+             if (temp != NULL)
+               *temp = ' ';
+             temp = strchr (argstr, ')');
+             if (temp != NULL)
+               *temp = ' ';
+           }
+         else
+           as_bad (_("badly formed expression near %s"), argstr);
+       }
+      break;
+
+    case 'j':
+    case 'o':
+    case 'O':
+      if (*argstr == '%')
+       {
+         /* Only 'j' really requires %label for distinguishing registers
+            from labels, but we include 'o' and 'O' here to avoid
+            confusing assembler programmers. Thus for completeness all
+            jump operands can be prefixed with %label.  */
+         if (strprefix (argstr, "%label"))
+           {
+             /* We zap the parentheses because we don't want them confused
+                with separators.  */
+             temp = strchr (argstr, '(');
+             if (temp != NULL)
+               *temp = ' ';
+             temp = strchr (argstr, ')');
+             if (temp != NULL)
+               *temp = ' ';
+           }
+         else
+           as_bad (_("badly formed expression near %s"), argstr);
+       }
+      break;
+
+    case 'b':
+    case 'B':
+    case 'c':
+    case 'd':
+    case 'D':
+    case 'E':
+    case 'i':
+    case 's':
+    case 'S':
+    case 'l':
+    case 'n':
+    case 'R':
+    case 'w':
+    case 'x':
+      /* We can't have %pmem here.  */
+      if (*argstr == '%')
+       as_bad (_("badly formed expression near %s"), argstr);
+      break;
+    default:
+      BAD_CASE (*parsestr);
+      break;
+    }
+
+  return argstr;
+}
+
+/* The function consume_separator takes a pointer into a string
+   of instruction tokens (args) and a pointer into a string representing
+   the expected sequence of tokens and separators.  It finds the first
+   instance of the character pointed to by separator in argstr, and
+   returns a pointer to the next element of argstr, which is the
+   following token in the sequence.  */
+static char *
+pru_consume_separator (char *argstr, const char *separator)
+{
+  char *p;
+
+  p = strchr (argstr, *separator);
+
+  if (p != NULL)
+    *p++ = 0;
+  else
+    as_bad (_("expecting %c near %s"), *separator, argstr);
+  return p;
+}
+
+
+/* The principal argument parsing function which takes a string argstr
+   representing the instruction arguments for insn, and extracts the argument
+   tokens matching parsestr into parsed_args.  */
+static void
+pru_parse_args (pru_insn_infoS *insn ATTRIBUTE_UNUSED, char *argstr,
+                 const char *parsestr, char **parsed_args)
+{
+  char *p;
+  char *end = NULL;
+  int i;
+  p = argstr;
+  i = 0;
+  bfd_boolean terminate = FALSE;
+
+  /* This rest of this function is it too fragile and it mostly works,
+     therefore special case this one.  */
+  if (*parsestr == 0 && argstr != 0)
+    {
+      as_bad (_("too many arguments"));
+      parsed_args[0] = NULL;
+      return;
+    }
+
+  while (p != NULL && !terminate && i < PRU_MAX_INSN_TOKENS)
+    {
+      parsed_args[i] = pru_consume_arg (p, parsestr);
+      ++parsestr;
+      if (*parsestr != '\0')
+       {
+         p = pru_consume_separator (p, parsestr);
+         ++parsestr;
+       }
+      else
+       {
+         /* Check that the argument string has no trailing arguments.  */
+         /* If we've got a %pmem relocation, we've zapped the parens with
+            spaces.  */
+         if (strprefix (p, "%pmem") || strprefix (p, "%label"))
+           end = strpbrk (p, ",");
+         else
+           end = strpbrk (p, " ,");
+
+         if (end != NULL)
+           as_bad (_("too many arguments"));
+       }
+
+      if (*parsestr == '\0' || (p != NULL && *p == '\0'))
+       terminate = TRUE;
+      ++i;
+    }
+
+  parsed_args[i] = NULL;
+
+  /* There are no instructions with optional arguments; complain.  */
+  if (*parsestr != '\0')
+    as_bad (_("missing argument"));
+}
+
+\f
+/** Assembler output support.  */
+
+/* Output a normal instruction.  */
+static void
+output_insn (pru_insn_infoS *insn)
+{
+  char *f;
+  pru_insn_relocS *reloc;
+
+  f = frag_more (4);
+  /* This allocates enough space for the instruction
+     and puts it in the current frag.  */
+  md_number_to_chars (f, insn->insn_code, 4);
+  /* Emit debug info.  */
+  dwarf2_emit_insn (4);
+  /* Create any fixups to be acted on later.  */
+  for (reloc = insn->insn_reloc; reloc != NULL; reloc = reloc->reloc_next)
+    fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                &reloc->reloc_expression, reloc->reloc_pcrel,
+                reloc->reloc_type);
+}
+
+/* Output two LDI instructions from LDI32 macro */
+static void
+output_insn_ldi32 (pru_insn_infoS *insn)
+{
+  char *f;
+  pru_insn_relocS *reloc;
+  unsigned long insn2;
+
+  f = frag_more (8);
+  md_number_to_chars (f, insn->insn_code, 4);
+
+  insn2 = insn->insn_code;
+  SET_INSN_FIELD (IMM16, insn2, insn->ldi32_imm32 >> 16);
+  SET_INSN_FIELD (RDSEL, insn2, RSEL_31_16);
+  md_number_to_chars (f + 4, insn2, 4);
+
+  /* Emit debug info.  */
+  dwarf2_emit_insn (8);
+
+  /* Create any fixups to be acted on later.  */
+  for (reloc = insn->insn_reloc; reloc != NULL; reloc = reloc->reloc_next)
+    fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                &reloc->reloc_expression, reloc->reloc_pcrel,
+                reloc->reloc_type);
+}
+
+\f
+/** External interfaces.  */
+
+/* The following functions are called by machine-independent parts of
+   the assembler.  */
+int
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
+{
+  switch (c)
+    {
+    case 'r':
+      /* Hidden option for self-test mode.  */
+      pru_mode = PRU_MODE_TEST;
+      break;
+    case OPTION_LINK_RELAX:
+      pru_opt.link_relax = TRUE;
+      break;
+    case OPTION_NO_LINK_RELAX:
+      pru_opt.link_relax = FALSE;
+      break;
+    case OPTION_NO_WARN_REGNAME_LABEL:
+      pru_opt.warn_regname_label = FALSE;
+      break;
+    default:
+      return 0;
+      break;
+    }
+
+  return 1;
+}
+
+const char *
+pru_target_format (void)
+{
+  return "elf32-pru";
+}
+
+/* Machine-dependent usage message.  */
+void
+md_show_usage (FILE *stream)
+{
+  fprintf (stream,
+    _("PRU options:\n"
+      "  -mlink-relax     generate relocations for linker relaxation (default).\n"
+      "  -mno-link-relax  don't generate relocations for linker relaxation.\n"
+    ));
+
+}
+
+/* This function is called once, at assembler startup time.
+   It should set up all the tables, etc.  that the MD part of the
+   assembler will need.  */
+void
+md_begin (void)
+{
+  int i;
+  const char *inserted;
+
+  /* Create and fill a hashtable for the PRU opcodes, registers and
+     arguments.  */
+  pru_opcode_hash = hash_new ();
+  pru_reg_hash = hash_new ();
+
+  for (i = 0; i < NUMOPCODES; ++i)
+    {
+      inserted
+       = hash_insert (pru_opcode_hash, pru_opcodes[i].name,
+                      (PTR) & pru_opcodes[i]);
+      if (inserted != NULL)
+       {
+         fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+                  pru_opcodes[i].name, inserted);
+         /* Probably a memory allocation problem?  Give up now.  */
+         as_fatal (_("Broken assembler.  No assembly attempted."));
+       }
+    }
+
+  for (i = 0; i < pru_num_regs; ++i)
+    {
+      inserted
+       = hash_insert (pru_reg_hash, pru_regs[i].name,
+                      (PTR) & pru_regs[i]);
+      if (inserted != NULL)
+       {
+         fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+                  pru_regs[i].name, inserted);
+         /* Probably a memory allocation problem?  Give up now.  */
+         as_fatal (_("Broken assembler.  No assembly attempted."));
+       }
+
+    }
+
+  linkrelax = pru_opt.link_relax;
+  /* Initialize the alignment data.  */
+  pru_current_align_seg = now_seg;
+  pru_last_label = NULL;
+  pru_current_align = 0;
+}
+
+
+/* Assembles a single line of PRU assembly language.  */
+void
+md_assemble (char *op_str)
+{
+  char *argstr;
+  char *op_strdup = NULL;
+  pru_insn_infoS thisinsn;
+  pru_insn_infoS *insn = &thisinsn;
+
+  /* Make sure we are aligned on a 4-byte boundary.  */
+  if (pru_current_align < 2)
+    pru_align (2, NULL, pru_last_label);
+  else if (pru_current_align > 2)
+    pru_current_align = 2;
+  pru_last_label = NULL;
+
+  /* We don't want to clobber to op_str
+     because we want to be able to use it in messages.  */
+  op_strdup = strdup (op_str);
+  insn->insn_tokens[0] = strtok (op_strdup, " ");
+  argstr = strtok (NULL, "");
+
+  /* Assemble the opcode.  */
+  insn->insn_pru_opcode = pru_opcode_lookup (insn->insn_tokens[0]);
+  insn->insn_reloc = NULL;
+
+  if (insn->insn_pru_opcode != NULL)
+    {
+      const char *argsfmt = insn->insn_pru_opcode->args;
+      const char **argtk = &insn->insn_tokens[1];
+      const char *argp;
+
+      /* Set the opcode for the instruction.  */
+      insn->insn_code = insn->insn_pru_opcode->match;
+
+      if (pru_mode == PRU_MODE_TEST)
+       {
+         /* Add the "expected" instruction parameter used for validation.  */
+         argsfmt = malloc (strlen (argsfmt) + 3);
+         sprintf ((char *)argsfmt, "%s,E", insn->insn_pru_opcode->args);
+       }
+      pru_parse_args (insn, argstr, argsfmt,
+                     (char **) &insn->insn_tokens[1]);
+
+      for (argp = argsfmt; !had_errors () && *argp && *argtk; ++argp)
+       {
+         gas_assert (argtk <= &insn->insn_tokens[PRU_MAX_INSN_TOKENS]);
+
+         switch (*argp)
+           {
+           case ',':
+             continue;
+
+           case 'd':
+             pru_assemble_arg_d (insn, *argtk++);
+             continue;
+           case 'D':
+             pru_assemble_arg_D (insn, *argtk++);
+             continue;
+           case 'R':
+             pru_assemble_arg_R (insn, *argtk++);
+             continue;
+           case 's':
+             pru_assemble_arg_s (insn, *argtk++);
+             continue;
+           case 'S':
+             pru_assemble_arg_S (insn, *argtk++);
+             continue;
+           case 'b':
+             pru_assemble_arg_b (insn, *argtk++);
+             continue;
+           case 'B':
+             pru_assemble_arg_B (insn, *argtk++);
+             continue;
+           case 'i':
+             pru_assemble_arg_i (insn, *argtk++);
+             continue;
+           case 'j':
+             pru_assemble_arg_j (insn, *argtk++);
+             continue;
+           case 'W':
+             pru_assemble_arg_W (insn, *argtk++);
+             continue;
+           case 'o':
+             pru_assemble_arg_o (insn, *argtk++);
+             continue;
+           case 'O':
+             pru_assemble_arg_O (insn, *argtk++);
+             continue;
+           case 'l':
+             pru_assemble_arg_l (insn, *argtk++);
+             continue;
+           case 'n':
+             pru_assemble_arg_n (insn, *argtk++);
+             continue;
+           case 'c':
+             pru_assemble_arg_c (insn, *argtk++);
+             continue;
+           case 'w':
+             pru_assemble_arg_w (insn, *argtk++);
+             continue;
+           case 'x':
+             pru_assemble_arg_x (insn, *argtk++);
+             continue;
+
+           case 'E':
+             pru_check_assembly (insn->insn_code, *argtk++);
+           default:
+             BAD_CASE (*argp);
+           }
+       }
+
+      if (*argp && !had_errors ())
+       as_bad (_("missing argument"));
+
+      if (!had_errors ())
+       {
+         if (insn->insn_pru_opcode->pinfo & PRU_INSN_LDI32)
+           {
+             output_insn_ldi32 (insn);
+           }
+         else
+           {
+             output_insn (insn);
+           }
+       }
+
+      if (pru_mode == PRU_MODE_TEST)
+       free ((char *)argsfmt);
+    }
+  else
+    /* Unrecognised instruction - error.  */
+    as_bad (_("unrecognised instruction %s"), insn->insn_tokens[0]);
+
+  /* Don't leak memory.  */
+  pru_insn_reloc_destroy (insn->insn_reloc);
+  free (op_strdup);
+}
+
+/* Round up section size.  */
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+  int align = bfd_get_section_alignment (stdoutput, seg);
+  return ((addr + (1 << align) - 1) & (-((valueT) 1 << align)));
+}
+
+/* Implement tc_fix_adjustable.  */
+int
+pru_fix_adjustable (fixS *fixp)
+{
+  if (fixp->fx_addsy == NULL)
+    return 1;
+
+  /* Prevent all adjustments to global symbols.  */
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && (S_IS_EXTERNAL (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)))
+    return 0;
+
+  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 0;
+
+  /* Preserve relocations against symbols with function type.  */
+  if (symbol_get_bfdsym (fixp->fx_addsy)->flags & BSF_FUNCTION)
+    return 0;
+
+  return 1;
+}
+
+/* The function tc_gen_reloc creates a relocation structure for the
+   fixup fixp, and returns a pointer to it.  This structure is passed
+   to bfd_install_relocation so that it can be written to the object
+   file for linking.  */
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+  arelent *reloc = XNEW (arelent);
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+  reloc->addend = fixp->fx_offset;  /* fixp->fx_addnumber; */
+
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+  if (reloc->howto == NULL)
+    {
+      as_bad_where (fixp->fx_file, fixp->fx_line,
+                   _("can't represent relocation type %s"),
+                   bfd_get_reloc_code_name (fixp->fx_r_type));
+
+      /* Set howto to a garbage value so that we can keep going.  */
+      reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+      gas_assert (reloc->howto != NULL);
+    }
+  return reloc;
+}
+
+long
+md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
+{
+  return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Called just before the assembler exits.  */
+void
+md_end (void)
+{
+  hash_die (pru_opcode_hash);
+  hash_die (pru_reg_hash);
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+  return NULL;
+}
+
+/* Implement tc_frob_label.  */
+void
+pru_frob_label (symbolS *lab)
+{
+  /* Emit dwarf information.  */
+  dwarf2_emit_label (lab);
+
+  /* Update the label's address with the current output pointer.  */
+  symbol_set_frag (lab, frag_now);
+  S_SET_VALUE (lab, (valueT) frag_now_fix ());
+
+  /* Record this label for future adjustment after we find out what
+     kind of data it references, and the required alignment therewith.  */
+  pru_last_label = lab;
+
+  if (pru_opt.warn_regname_label && pru_reg_lookup (S_GET_NAME (lab)))
+    as_warn (_("Label \"%s\" matches a CPU register name"), S_GET_NAME (lab));
+}
+
+static inline char *
+skip_space (char *s)
+{
+  while (*s == ' ' || *s == '\t')
+    ++s;
+  return s;
+}
+
+/* Parse special CONS expression: pmem (expression).  Idea from AVR.
+
+   Used to catch and mark code (program memory) in constant expression
+   relocations.  Return non-zero for program memory.  */
+
+int
+pru_parse_cons_expression (expressionS *exp, int nbytes)
+{
+  int is_pmem = FALSE;
+  char *tmp;
+
+  tmp = input_line_pointer = skip_space (input_line_pointer);
+
+  if (nbytes == 4 || nbytes == 2)
+    {
+      const char *pmem_str = "%pmem";
+      int len = strlen (pmem_str);
+
+      if (strncasecmp (input_line_pointer, pmem_str, len) == 0)
+       {
+         input_line_pointer = skip_space (input_line_pointer + len);
+
+         if (*input_line_pointer == '(')
+           {
+             input_line_pointer = skip_space (input_line_pointer + 1);
+             is_pmem = TRUE;
+             expression (exp);
+
+             if (*input_line_pointer == ')')
+               ++input_line_pointer;
+             else
+               {
+                 as_bad (_("`)' required"));
+                 is_pmem = FALSE;
+               }
+
+             return is_pmem;
+           }
+
+         input_line_pointer = tmp;
+       }
+    }
+
+  expression (exp);
+
+  return is_pmem;
+}
+
+/* Implement TC_CONS_FIX_NEW.  */
+void
+pru_cons_fix_new (fragS *frag, int where, unsigned int nbytes,
+                   expressionS *exp, const int is_pmem)
+{
+  bfd_reloc_code_real_type r;
+
+  switch (nbytes | (!!is_pmem << 8))
+    {
+    case 1 | (0 << 8): r = BFD_RELOC_8; break;
+    case 2 | (0 << 8): r = BFD_RELOC_16; break;
+    case 4 | (0 << 8): r = BFD_RELOC_32; break;
+    case 8 | (0 << 8): r = BFD_RELOC_64; break;
+    case 2 | (1 << 8): r = BFD_RELOC_PRU_16_PMEM; break;
+    case 4 | (1 << 8): r = BFD_RELOC_PRU_32_PMEM; break;
+    default:
+      as_bad (_("illegal %s relocation size: %d"),
+             is_pmem ? "text" : "data", nbytes);
+      return;
+    }
+
+  fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+}
+
+/* Implement tc_regname_to_dw2regnum, to convert REGNAME to a DWARF-2
+   register number.  */
+int
+pru_regname_to_dw2regnum (char *regname)
+{
+  struct pru_reg *r = pru_reg_lookup (regname);
+  if (r == NULL)
+    return -1;
+  return r->index;
+}
+
+/* Implement tc_cfi_frame_initial_instructions, to initialize the DWARF-2
+   unwind information for this procedure.  */
+void
+pru_frame_initial_instructions (void)
+{
+  const unsigned fp_regno = 4;
+  cfi_add_CFA_def_cfa (fp_regno, 0);
+}
+
+bfd_boolean
+pru_allow_local_subtract (expressionS * left,
+                            expressionS * right,
+                            segT section)
+{
+  /* If we are not in relaxation mode, subtraction is OK.  */
+  if (!linkrelax)
+    return TRUE;
+
+  /* If the symbols are not in a code section then they are OK.  */
+  if ((section->flags & SEC_CODE) == 0)
+    return TRUE;
+
+  if (left->X_add_symbol == right->X_add_symbol)
+    return TRUE;
+
+  /* We have to assume that there may be instructions between the
+     two symbols and that relaxation may increase the distance between
+     them.  */
+  return FALSE;
+}
diff --git a/gas/config/tc-pru.h b/gas/config/tc-pru.h
new file mode 100644 (file)
index 0000000..d3d0d78
--- /dev/null
@@ -0,0 +1,154 @@
+/* Definitions for TI PRU assembler.
+   Copyright (C) 2014-2016 Free Software Foundation, Inc.
+   Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifndef __TC_PRU__
+#define __TC_PRU__
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* Words are big enough to hold addresses.  */
+#define WORKING_DOT_WORD       1
+
+extern const char *pru_target_format (void);
+#define TARGET_FORMAT  pru_target_format ()
+#define TARGET_ARCH    bfd_arch_pru
+
+/* A PRU instruction consists of tokens and separator characters
+   the tokens are things like the instruction name (add, or jmp etc),
+   the register indices ($5, $7 etc), and constant expressions.  The
+   separator characters are commas, brackets and space.
+   The instruction name is always separated from other tokens by a space
+   The maximum number of tokens in an instruction is 6 (the instruction name,
+   4 arguments, and a 4th string representing the expected instruction opcode
+   after assembly.  The latter is only used when the assemble is running in
+   self test mode, otherwise its presence will generate an error.  */
+#define PRU_MAX_INSN_TOKENS    7
+
+/* There are no machine-specific operands so we #define this to nothing.  */
+#define md_operand(x)
+
+/* Function prototypes exported to rest of GAS.  */
+extern void md_assemble (char *op_str);
+extern void md_end (void);
+extern void md_begin (void);
+
+#define tc_fix_adjustable(fixp) pru_fix_adjustable (fixp)
+extern int pru_fix_adjustable (struct fix *);
+
+#define tc_frob_label(lab) pru_frob_label (lab)
+extern void pru_frob_label (symbolS *);
+
+extern void md_convert_frag (bfd * headers, segT sec, fragS * fragP);
+
+#define DIFF_EXPR_OK
+
+/* FIXME This seems appropriate, given that we intentionally prevent
+   PRU's .text from being used in a DIFF expression with symbols from
+   other sections.  Revisit once GDB is ported.  */
+#define CFI_DIFF_EXPR_OK 0
+
+#define TC_PARSE_CONS_RETURN_TYPE int
+#define TC_PARSE_CONS_RETURN_NONE 0
+
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+       pru_parse_cons_expression (EXP, NBYTES)
+extern int pru_parse_cons_expression (expressionS *exp, int size);
+
+#define TC_CONS_FIX_NEW pru_cons_fix_new
+extern void pru_cons_fix_new (struct frag *frag, int where,
+                               unsigned int nbytes, struct expressionS *exp,
+                               const int is_pmem);
+
+/* If you define this macro, it means that `tc_gen_reloc' may return
+   multiple relocation entries for a single fixup.  In this case, the
+   return value of `tc_gen_reloc' is a pointer to a null terminated
+   array.  */
+#undef RELOC_EXPANSION_POSSIBLE
+
+/* No shared lib support, so we don't need to ensure externally
+   visible symbols can be overridden.  */
+#define EXTERN_FORCE_RELOC 0
+
+/* If defined, this macro allows control over whether fixups for a
+   given section will be processed when the linkrelax variable is
+   set.  Define it to zero and handle things in md_apply_fix instead.  */
+#define TC_LINKRELAX_FIXUP(SEG) 0
+
+/* If this macro returns non-zero, it guarantees that a relocation will be
+   emitted even when the value can be resolved locally.  Do that if
+   linkrelax is turned on.  */
+#define TC_FORCE_RELOCATION(fix)       pru_force_relocation (fix)
+#define TC_FORCE_RELOCATION_SUB_SAME(fix, seg) \
+  (! SEG_NORMAL (seg) || pru_force_relocation (fix))
+extern int pru_force_relocation (struct fix *);
+
+/* Do not use PC relative fixups and relocations for
+   anything but real PCREL relocations.  */
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \
+  (((FIX)->fx_r_type != BFD_RELOC_PRU_S10_PCREL) \
+   && ((FIX)->fx_r_type != BFD_RELOC_PRU_U8_PCREL))
+
+/* Values passed to md_apply_fix don't include the symbol value.  */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* We don't want gas to fixup the following memory related relocations.
+   We will need them in case that we want to do linker relaxation.
+   We could in principle keep these fixups in gas when not relaxing.
+   However, there is no serious performance penalty when making the linker
+   make the fixup work.  Check also that fx_addsy is not NULL, in order to
+   make sure that the fixup refers to some sort of label.  */
+#define TC_VALIDATE_FIX(FIXP,SEG,SKIP)                       \
+  if ((FIXP->fx_r_type == BFD_RELOC_PRU_LDI32                \
+       || FIXP->fx_r_type == BFD_RELOC_PRU_U16               \
+       || FIXP->fx_r_type == BFD_RELOC_PRU_U16_PMEMIMM       \
+       || FIXP->fx_r_type == BFD_RELOC_PRU_S10_PCREL         \
+       || FIXP->fx_r_type == BFD_RELOC_PRU_U8_PCREL          \
+       || FIXP->fx_r_type == BFD_RELOC_PRU_32_PMEM           \
+       || FIXP->fx_r_type == BFD_RELOC_PRU_16_PMEM)          \
+      && FIXP->fx_addsy != NULL                                      \
+      && FIXP->fx_subsy == NULL)                             \
+    {                                                        \
+      symbol_mark_used_in_reloc (FIXP->fx_addsy);            \
+      goto SKIP;                                             \
+    }
+
+/* This macro is evaluated for any fixup with a fx_subsy that
+   fixup_segment cannot reduce to a number.  If the macro returns
+   false an error will be reported.  */
+#define TC_VALIDATE_FIX_SUB(fix, seg)   pru_validate_fix_sub (fix)
+extern int pru_validate_fix_sub (struct fix *);
+
+/* We want .cfi_* pseudo-ops for generating unwind info.  */
+#define TARGET_USE_CFIPOP 1
+#define DWARF2_DEFAULT_RETURN_COLUMN 31
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+#define tc_regname_to_dw2regnum pru_regname_to_dw2regnum
+extern int pru_regname_to_dw2regnum (char *regname);
+#define tc_cfi_frame_initial_instructions  pru_frame_initial_instructions
+extern void pru_frame_initial_instructions (void);
+
+/* The difference between same-section symbols may be affected by linker
+   relaxation, so do not resolve such expressions in the assembler.  */
+#define md_allow_local_subtract(l,r,s) pru_allow_local_subtract (l, r, s)
+extern bfd_boolean pru_allow_local_subtract (expressionS *, expressionS *,
+                                            segT);
+
+#endif /* __TC_PRU__ */
index 495155539b7bca2bb66163ad94abe5d87aa3dc8d..4abf83fb5eb50f66775e28d8adbb5c9d7c0bc8f4 100644 (file)
@@ -396,6 +396,8 @@ case ${generic_target} in
   ppc-*-kaos*)                         fmt=elf ;;
   ppc-*-lynxos*)                       fmt=elf em=lynx ;;
 
+  pru-*-*)                             fmt=elf ;;
+
   riscv*-*-*)                  fmt=elf endian=little em=linux ;;
 
   s390-*-linux-*)                      fmt=elf em=linux ;;
index 54d7ef1b5cad47eabb6824096c2ebfcfecd9a52f..c604a29071504d9b255f1256510df5bd967d345e 100644 (file)
@@ -80,6 +80,7 @@ CPU_DOCS = \
        c-pdp11.texi \
        c-pj.texi \
        c-ppc.texi \
+       c-pru.texi \
        c-rl78.texi \
        c-riscv.texi \
        c-rx.texi \
index 71101928e27b83e10a3b4bb0894719d35df15711..58b0e360d44b430d9a2db9d4b5b3555037d1fca2 100644 (file)
@@ -355,6 +355,7 @@ CPU_DOCS = \
        c-pdp11.texi \
        c-pj.texi \
        c-ppc.texi \
+       c-pru.texi \
        c-rl78.texi \
        c-riscv.texi \
        c-rx.texi \
index 3c25d397965e1715ca5548578857d2d597425c61..79fcaba0d9a9912c730fc6518c9587a0d82e3757 100644 (file)
@@ -62,6 +62,7 @@
 @set PDP11
 @set PJ
 @set PPC
+@set PRU
 @set RL78
 @set RISCV
 @set RX
index d83d2b305e201c20aa3f873db3f58f3d2c3a6d51..f03c2ef3c9bef094654f788251dad50986a691f8 100644 (file)
@@ -493,6 +493,13 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
    [@b{-msolaris}|@b{-mno-solaris}]
    [@b{-nops=@var{count}}]
 @end ifset
+@ifset PRU
+
+@emph{Target PRU options:}
+   [@b{-link-relax}]
+   [@b{-mnolink-relax}]
+   [@b{-mno-warn-regname-label}]
+@end ifset
 @ifset RL78
 
 @emph{Target RL78 options:}
@@ -1232,6 +1239,24 @@ Generate ``little endian'' format output.
 @end table
 @end ifset
 
+@ifset PRU
+
+@ifclear man
+@xref{PRU Options}, for the options available when @value{AS} is configured
+for a PRU processor.
+@end ifclear
+
+@ifset man
+@c man begin OPTIONS
+The following options are available when @value{AS} is configured for a
+PRU processor.
+@c man end
+@c man begin INCLUDE
+@include c-pru.texi
+@c ended inside the included file
+@end ifset
+@end ifset
+
 @ifset M68HC11
 The following options are available when @value{AS} is configured for the
 Motorola 68HC11 or 68HC12 series.
@@ -7596,6 +7621,9 @@ subject, see the hardware manufacturer's manual.
 @ifset PPC
 * PPC-Dependent::               PowerPC Dependent Features
 @end ifset
+@ifset PRU
+* PRU-Dependent::               PRU Dependent Features
+@end ifset
 @ifset RL78
 * RL78-Dependent::              RL78 Dependent Features
 @end ifset
@@ -7825,6 +7853,10 @@ family.
 @include c-ppc.texi
 @end ifset
 
+@ifset PRU
+@include c-pru.texi
+@end ifset
+
 @ifset RL78
 @include c-rl78.texi
 @end ifset
diff --git a/gas/doc/c-pru.texi b/gas/doc/c-pru.texi
new file mode 100644 (file)
index 0000000..6aba8b4
--- /dev/null
@@ -0,0 +1,150 @@
+@c Copyright (C) 2015-2016 Free Software Foundation, Inc.
+@c This is part of the GAS manual.
+@c For copying conditions, see the file as.texinfo.
+@c man end
+@ifset GENERIC
+@page
+@node PRU-Dependent
+@chapter PRU Dependent Features
+@end ifset
+
+@cindex PRU support
+@menu
+* PRU Options::              Options
+* PRU Syntax::               Syntax
+* PRU Relocations::          Relocations
+* PRU Directives::           PRU Machine Directives
+* PRU Opcodes::              Opcodes
+@end menu
+
+@node PRU Options
+@section Options
+@cindex PRU options
+@cindex options for PRU
+
+@c man begin OPTIONS
+@table @gcctabopt
+
+@cindex @code{mlink-relax} command line option, PRU
+@item -mlink-relax
+Assume that LD would optimize LDI32 instructions by checking the upper
+16 bits of the @var{expression}. If they are all zeros, then LD would
+shorten the LDI32 instruction to a single LDI. In such case @code{@value{AS}}
+will output DIFF relocations for diff expressions.
+
+@cindex @code{mno-link-relax} command line option, PRU
+@item -mno-link-relax
+Assume that LD would not optimize LDI32 instructions. As a consequence,
+DIFF relocations will not be emitted.
+
+@cindex @code{mno-warn-regname-label} command line option, PRU
+@item -mno-warn-regname-label
+Do not warn if a label name matches a register name. Usually assembler
+programmers will want this warning to be emitted. C compilers may want
+to turn this off.
+
+@end table
+@c man end
+
+@node PRU Syntax
+@section Syntax
+@menu
+* PRU Chars::                Special Characters
+@end menu
+
+
+@node PRU Chars
+@subsection Special Characters
+
+@cindex line comment character, PRU
+@cindex PRU line comment character
+@samp{#} and @samp{;} are the line comment characters.
+
+
+@node PRU Relocations
+@section PRU Machine Relocations
+
+@cindex machine relocations, PRU
+@cindex PRU machine relocations
+
+@table @code
+
+@cindex @code{pmem} directive, PRU
+@item %pmem(@var{expression})
+Convert @var{expression} from byte-address to a
+word-address.  In other words, shift right by two.
+
+@item %label(@var{expression})
+Mark the given operand as a label. This is useful if you need to jump to
+a label that matches a register name.
+
+@smallexample
+@group
+r1:
+    jmp r1             ; Will jump to register R1
+    jmp %label(r1)     ; Will jump to label r1
+@end group
+@end smallexample
+
+@end table
+
+
+@node PRU Directives
+@section PRU Machine Directives
+
+@cindex machine directives, PRU
+@cindex PRU machine directives
+
+@table @code
+
+@cindex @code{align} directive, PRU
+@item .align @var{expression} [, @var{expression}]
+This is the generic @code{.align} directive, however
+this aligns to a power of two.
+
+@cindex @code{word} directive, PRU
+@item .word @var{expression}
+Create an aligned constant 4 bytes in size.
+
+@cindex @code{dword} directive, PRU
+@item .dword @var{expression}
+Create an aligned constant 8 bytes in size.
+
+@cindex @code{2byte} directive, PRU
+@item .2byte @var{expression}
+Create an unaligned constant 2 bytes in size.
+
+@cindex @code{4byte} directive, PRU
+@item .4byte @var{expression}
+Create an unaligned constant 4 bytes in size.
+
+@cindex @code{8byte} directive, PRU
+@item .8byte @var{expression}
+Create an unaligned constant 8 bytes in size.
+
+@cindex @code{16byte} directive, PRU
+@item .16byte @var{expression}
+Create an unaligned constant 16 bytes in size.
+
+@cindex @code{set no_warn_regname_label} directive, PRU
+@item .set no_warn_regname_label
+Do not output warnings when a label name matches a register name. Equivalent
+to passing the @code{-mno-warn-regname-label} command line option.
+
+@end table
+
+@node PRU Opcodes
+@section Opcodes
+
+@cindex PRU opcodes
+@cindex opcodes for PRU
+@code{@value{AS}} implements all the standard PRU core V3 opcodes in the
+original pasm assembler.  Older cores are not supported by @code{@value{AS}}.
+
+GAS also implements the LDI32 pseudo instruction for loading a 32-bit
+immediate value into a register.
+
+@smallexample
+       ldi32   sp, __stack_top
+       ldi32   r14, 0x12345678
+@end smallexample
index ad7833db9e82b09be305959dbefc3454fa5bb536..98861f7e8e128db538af851b2b89b0a130e0ae67 100644 (file)
@@ -131,6 +131,8 @@ config/tc-pj.c
 config/tc-pj.h
 config/tc-ppc.c
 config/tc-ppc.h
+config/tc-pru.c
+config/tc-pru.h
 config/tc-riscv.c
 config/tc-riscv.h
 config/tc-rl78.c
index 81e0396740c2f745c03ce89816fab6ec36058a94..acf9947d130ddd8a0bff325d36e4c4f0e5f5bd30 100644 (file)
@@ -37,6 +37,7 @@ if {
         || [istarget mn10*-*-*]
         || [istarget msp430-*-*]
         || [istarget nds32*-*-*]
+        || [istarget pru-*-*]
         || [istarget rl78-*-*]
         || [istarget xtensa*-*-*] } {
       run_dump_test "lns-common-1-alt"
diff --git a/gas/testsuite/gas/pru/alu.d b/gas/testsuite/gas/pru/alu.d
new file mode 100644 (file)
index 0000000..d91ad06
--- /dev/null
@@ -0,0 +1,32 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU ALU
+
+# Test the ALU instructions
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 00e4e4e4        add     fp, fp, fp
+0+0004 <[^>]*> 01ffe4e4        add     fp, fp, 255
+0+0008 <[^>]*> 0100e4e4        add     fp, fp, 0
+0+000c <[^>]*> 0100e4e4        add     fp, fp, 0
+0+0010 <[^>]*> 0100a424        add     fp.b1, fp.w1, 0
+0+0014 <[^>]*> 00634221        add     r1.b1, sp.b2, ra.b3
+0+0018 <[^>]*> 02634221        adc     r1.b1, sp.b2, ra.b3
+0+001c <[^>]*> 03634221        adc     r1.b1, sp.b2, 99
+0+0020 <[^>]*> 00e0e0e0        add     r0, r0, r0
+0+0024 <[^>]*> 02e0e0e0        adc     r0, r0, r0
+0+0028 <[^>]*> 050affe1        sub     r1, r31, 10
+0+002c <[^>]*> 070affe1        suc     r1, r31, 10
+0+0030 <[^>]*> 090affff        lsl     r31, r31, 10
+0+0034 <[^>]*> 0b0affff        lsr     r31, r31, 10
+0+0038 <[^>]*> 0d0a70f0        rsb     r16, r16.b3, 10
+0+003c <[^>]*> 0f0a70f0        rsc     r16, r16.b3, 10
+0+0040 <[^>]*> 11aa61a1        and     r1.w1, r1.b3, 170
+0+0044 <[^>]*> 13aa61a1        or      r1.w1, r1.b3, 170
+0+0048 <[^>]*> 15aa61a1        xor     r1.w1, r1.b3, 170
+0+004c <[^>]*> 1700e1e2        not     sp, r1
+0+0050 <[^>]*> 18e2e1e1        min     r1, r1, sp
+0+0054 <[^>]*> 1ac3e2e1        max     r1, sp, ra.w2
+0+0058 <[^>]*> 1cc3e2e1        clr     r1, sp, ra.w2
+0+005c <[^>]*> 1f0ce2e1        set     r1, sp, 12
diff --git a/gas/testsuite/gas/pru/alu.s b/gas/testsuite/gas/pru/alu.s
new file mode 100644 (file)
index 0000000..e61e101
--- /dev/null
@@ -0,0 +1,30 @@
+# Source file used to test the ALU class of instructions.
+
+foo:
+       # Test various addressing modes
+       add     fp, fp, fp
+       add     fp, fp, 0xff
+       add     fp, fp, 0
+       add     fp, fp, 0
+       add     fp.b1, fp.w1, 0
+       add     r1.b1, r2.b2, r3.b3
+       adc     r1.b1, r2.b2, r3.b3
+       adc     r1.b1, r2.b2, 101-2
+
+       # Test ALU opcodes
+       add     r0, r0, r0
+       adc     r0, r0, r0
+       sub     r1, r31, 10
+       suc     r1, r31, 10
+       lsl     r31, r31, 10
+       lsr     r31, r31, 10
+       rsb     r16, r16.b3, 10
+       rsc     r16, r16.b3, 10
+       and     r1.w1, r1.b3, 0xaa
+       or      r1.w1, r1.b3, 0xaa
+       xor     r1.w1, r1.b3, 0xaa
+       not     r2, r1
+       min     r1, r1, r2
+       max     r1, r2, r3.w2
+       clr     r1, r2, r3.w2
+       set     r1, r2, 12
diff --git a/gas/testsuite/gas/pru/branch.d b/gas/testsuite/gas/pru/branch.d
new file mode 100644 (file)
index 0000000..f5b50a7
--- /dev/null
@@ -0,0 +1,63 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU branch
+
+# Test the branch instructions
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 20ea0000        jmp     r10
+0+0004 <[^>]*> 208a0000        jmp     r10.w0
+0+0008 <[^>]*> 21004000        jmp     00000100 <[^>]*>
+0+000c <[^>]*> 22ca00f6        jal     r22, r10.w2
+0+0010 <[^>]*> 230000f7        jal     r23, 00000000 <[^>]*>
+0+0014 <[^>]*> 23ffffb7        jal     r23.w1, 0003fffc <[^>]*>
+0+0018 <[^>]*> 6100f700        qbgt    00000018 <[^>]*>, r23, 0
+[\t ]*18: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+001c <[^>]*> 71ff5700        qbge    0000001c <[^>]*>, r23.b2, 255
+[\t ]*1c: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+0020 <[^>]*> 4820b600        qblt    00000020 <[^>]*>, r22.w1, r0.b1
+[\t ]*20: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+0024 <[^>]*> 58210000        qble    00000024 <[^>]*>, r0.b0, r1.b1
+[\t ]*24: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+0028 <[^>]*> 50034100        qbeq    00000028 <[^>]*>, r1.b2, ra.b0
+[\t ]*28: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+002c <[^>]*> 68f6f500        qbne    0000002c <[^>]*>, r21, r22
+[\t ]*2c: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+0030 <[^>]*> 78000000        qba     00000030 <[^>]*>
+[\t ]*30: R_PRU_S10_PCREL[\t ]*.text\+0x60
+#0+0034 <[^>]*> d0edec00       qbbs    00000034 <[^>]*>, r12, r13
+0+0034 <[^>]*> d0edec00        wbc     r12, r13
+[\t ]*34: R_PRU_S10_PCREL[\t ]*.text\+0x60
+#0+0038 <[^>]*> d105ec00       qbbs    00000038 <[^>]*>, r12, 5
+0+0038 <[^>]*> d105ec00        wbc     r12, 5
+[\t ]*38: R_PRU_S10_PCREL[\t ]*.text\+0x60
+#0+003c <[^>]*> c8edec00       qbbc    0000003c <[^>]*>, r12, r13
+0+003c <[^>]*> c8edec00        wbs     r12, r13
+[\t ]*3c: R_PRU_S10_PCREL[\t ]*.text\+0x60
+#0+0040 <[^>]*> c905ec00       qbbc    00000040 <[^>]*>, r12, 5
+0+0040 <[^>]*> c905ec00        wbs     r12, 5
+[\t ]*40: R_PRU_S10_PCREL[\t ]*.text\+0x60
+0+0044 <[^>]*> 6100f700        qbgt    00000044 <[^>]*>, r23, 0
+[\t ]*44: R_PRU_S10_PCREL[\t ]*.text\+0xc
+0+0048 <[^>]*> 71ff5700        qbge    00000048 <[^>]*>, r23.b2, 255
+[\t ]*48: R_PRU_S10_PCREL[\t ]*.text\+0xc
+0+004c <[^>]*> 4820b600        qblt    0000004c <[^>]*>, r22.w1, r0.b1
+[\t ]*4c: R_PRU_S10_PCREL[\t ]*.text\+0xc
+0+0050 <[^>]*> 58210000        qble    00000050 <[^>]*>, r0.b0, r1.b1
+[\t ]*50: R_PRU_S10_PCREL[\t ]*.text\+0xc
+0+0054 <[^>]*> 50034100        qbeq    00000054 <[^>]*>, r1.b2, ra.b0
+[\t ]*54: R_PRU_S10_PCREL[\t ]*.text\+0xc
+0+0058 <[^>]*> 68f6f500        qbne    00000058 <[^>]*>, r21, r22
+[\t ]*58: R_PRU_S10_PCREL[\t ]*.text\+0xc
+0+005c <[^>]*> 78000000        qba     0000005c <[^>]*>
+[\t ]*5c: R_PRU_S10_PCREL[\t ]*.text\+0xc
+#0+0060 <[^>]*> d0edec00       qbbs    00000060 <[^>]*>, r12, r13
+0+0060 <[^>]*> d0edec00        wbc     r12, r13
+[\t ]*60: R_PRU_S10_PCREL[\t ]*.text\+0xc
+#0+0064 <[^>]*> d105ec00       qbbs    00000064 <[^>]*>, r12, 5
+0+0064 <[^>]*> d105ec00        wbc     r12, 5
+[\t ]*64: R_PRU_S10_PCREL[\t ]*.text\+0xc
+#0+0068 <[^>]*> c8edec00       qbbc    00000068 <[^>]*>, r12, r13
+0+0068 <[^>]*> c8edec00        wbs     r12, r13
+[\t ]*68: R_PRU_S10_PCREL[\t ]*.text\+0xc
diff --git a/gas/testsuite/gas/pru/branch.s b/gas/testsuite/gas/pru/branch.s
new file mode 100644 (file)
index 0000000..ab43c74
--- /dev/null
@@ -0,0 +1,42 @@
+# Source file used to test the miscellaneous instructions.
+
+foo:
+L1:
+       jmp     r10
+       jmp     r10.w0
+       jmp     0x100
+
+L2:
+       jal     r22, r10.w2
+       jal     r23, 0
+       jal     r23.w1, 0x3fffc
+
+       # relative branches - forward jump
+L3:
+       qbgt    L5, r23, 0
+       qbge    L5, r23.b2, 255
+       qblt    L5, r22.w1, r0.b1
+       qble    L5, r0.b0, r1.b1
+       qbeq    L5, r1.b2, r3.b0
+       qbne    L5, r21, r22
+       qba     L5
+
+       qbbs    L5, r12, r13
+       qbbs    L5, r12, 5
+       qbbc    L5, r12, r13
+       qbbc    L5, r12, 5
+
+       # relative branches - backward jump
+L4:
+       qbgt    L2, r23, 0
+       qbge    L2, r23.b2, 255
+       qblt    L2, r22.w1, r0.b1
+       qble    L2, r0.b0, r1.b1
+       qbeq    L2, r1.b2, r3.b0
+       qbne    L2, r21, r22
+       qba     L2
+
+L5:
+       qbbs    L2, r12, r13
+       qbbs    L2, r12, 5
+       qbbc    L2, r12, r13
diff --git a/gas/testsuite/gas/pru/illegal.l b/gas/testsuite/gas/pru/illegal.l
new file mode 100644 (file)
index 0000000..64de14b
--- /dev/null
@@ -0,0 +1,5 @@
+.*illegal.s: Assembler messages:
+.*illegal.s:5: Error: unknown register r56
+.*illegal.s:8: Error: unrecognised instruction fop
+.*illegal.s:10: Error: too many arguments
+.*illegal.s:11: Error: too many arguments
diff --git a/gas/testsuite/gas/pru/illegal.s b/gas/testsuite/gas/pru/illegal.s
new file mode 100644 (file)
index 0000000..1571f74
--- /dev/null
@@ -0,0 +1,11 @@
+# Source file used to test illegal operands.
+
+foo:
+# Illegal registers
+       add r56,r4,r5
+       add r4,r0,r2
+# Illegal opcodes
+       fop r3,r4,r5
+# Extra operands
+       nop Crapola
+       add r2, r2, r2, r4
diff --git a/gas/testsuite/gas/pru/ldi.d b/gas/testsuite/gas/pru/ldi.d
new file mode 100644 (file)
index 0000000..8851504
--- /dev/null
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU ldi
+
+# Test the load/store operations
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 240000f0        ldi     r16, 0
+[\t ]*0: R_PRU_LDI32   \*ABS\*\+0x12345678
+0+0004 <[^>]*> 240000d0        ldi     r16.w2, 0
+0+0008 <[^>]*> 241234f0        ldi     r16, 4660
+0+000c <[^>]*> 240000f0        ldi     r16, 0
+[\t ]*c: R_PRU_U16_PMEMIMM     .text
+0+0010 <[^>]*> 240000f0        ldi     r16, 0
+[\t ]*10: R_PRU_LDI32  var1
+0+0014 <[^>]*> 240000d0        ldi     r16.w2, 0
diff --git a/gas/testsuite/gas/pru/ldi.s b/gas/testsuite/gas/pru/ldi.s
new file mode 100644 (file)
index 0000000..201a0f2
--- /dev/null
@@ -0,0 +1,9 @@
+# Source file used to test the LDI instructions.
+
+       .extern var1
+foo:
+       # immediate load
+       ldi32   r16, 0x12345678
+       ldi     r16, 0x1234
+       ldi     r16, %pmem(foo)
+       ldi32   r16, var1
diff --git a/gas/testsuite/gas/pru/ldst.d b/gas/testsuite/gas/pru/ldst.d
new file mode 100644 (file)
index 0000000..7e44b6d
--- /dev/null
@@ -0,0 +1,33 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU load-store
+
+# Test the load/store operations
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 240000f0        ldi     r16, 0
+0+0004 <[^>]*> 24fffff0        ldi     r16, 65535
+0+0008 <[^>]*> 2401fff0        ldi     r16, 511
+0+000c <[^>]*> f0611e20        lbbo    r0.b1, r30, r1.b3, 1
+0+0010 <[^>]*> fe41bec0        lbbo    r0.b2, r30, r1.b2, 124
+0+0014 <[^>]*> f1ff1e60        lbbo    r0.b3, r30, 255, 1
+0+0018 <[^>]*> f1011e80        lbbo    r0.b0, r30, 1, 2
+0+001c <[^>]*> fb005e00        lbbo    r0.b0, r30, 0, 85
+0+0020 <[^>]*> fea1d912        lbbo    r18.b0, r25, r1.w1, r0.b0
+0+0024 <[^>]*> ff65d992        lbbo    r18.b0, r25, 101, r0.b1
+0+0028 <[^>]*> fee1f992        lbbo    r18.b0, r25, r1, r0.b3
+0+002c <[^>]*> e0611e20        sbbo    r0.b1, r30, r1.b3, 1
+0+0030 <[^>]*> ee41bec0        sbbo    r0.b2, r30, r1.b2, 124
+0+0034 <[^>]*> e1ff1e60        sbbo    r0.b3, r30, 255, 1
+0+0038 <[^>]*> e1011e80        sbbo    r0.b0, r30, 1, 2
+0+003c <[^>]*> eb005e00        sbbo    r0.b0, r30, 0, 85
+0+0040 <[^>]*> eee1d912        sbbo    r18.b0, r25, r1, r0.b0
+0+0044 <[^>]*> ef65d992        sbbo    r18.b0, r25, 101, r0.b1
+0+0048 <[^>]*> eee1f992        sbbo    r18.b0, r25, r1, r0.b3
+0+004c <[^>]*> 9105608a        lbco    r10.b0, 0, 5, 8
+0+0050 <[^>]*> 90ab618a        lbco    r10.b0, 1, r11.w1, 8
+0+0054 <[^>]*> 91057f8a        lbco    r10.b0, 31, 5, 8
+0+0058 <[^>]*> 8105608a        sbco    r10.b0, 0, 5, 8
+0+005c <[^>]*> 80ab618a        sbco    r10.b0, 1, r11.w1, 8
+0+0060 <[^>]*> 81057f8a        sbco    r10.b0, 31, 5, 8
diff --git a/gas/testsuite/gas/pru/ldst.s b/gas/testsuite/gas/pru/ldst.s
new file mode 100644 (file)
index 0000000..e8ad3a2
--- /dev/null
@@ -0,0 +1,37 @@
+# Source file used to test the load/store instructions.
+
+foo:
+       # immediate load
+       ldi     r16, 0
+       ldi     r16, 0xffff
+       ldi     r16, 511
+
+       # load
+       lbbo    &r0.b1, r30, r1.b3, 1
+       lbbo    r0.b2, r30, r1.b2, 124
+       lbbo    r0.b3, r30, 255, 1
+       lbbo    &r0, r30, 1, 2
+       lbbo    r0, r30, 0, 0x55
+       lbbo    r18, r25, r1.w1, r0.b0
+       lbbo    r18, r25, 101, r0.b1
+       lbbo    r18, r25, r1, r0.b3
+
+       # store
+       sbbo    &r0.b1, r30, r1.b3, 1
+       sbbo    r0.b2, r30, r1.b2, 124
+       sbbo    r0.b3, r30, 255, 1
+       sbbo    &r0, r30, 1, 2
+       sbbo    r0, r30, 0, 0x55
+       sbbo    r18, r25, r1, r0.b0
+       sbbo    r18, r25, 101, r0.b1
+       sbbo    r18, r25, r1, r0.b3
+
+       # load with constant table address
+       lbco    r10, 0, 5, 8
+       lbco    r10, 1, r11.w1, 8
+       lbco    r10, 31, 5, 8
+
+       # store with constant table address
+       sbco    r10, 0, 5, 8
+       sbco    r10, 1, r11.w1, 8
+       sbco    r10, 31, 5, 8
diff --git a/gas/testsuite/gas/pru/loop.d b/gas/testsuite/gas/pru/loop.d
new file mode 100644 (file)
index 0000000..b6d4a8a
--- /dev/null
@@ -0,0 +1,15 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU loop
+
+# Test the loop instructions
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 304a0000        loop    00000000 <[^>]*>, r10.b2
+[\t ]*0: R_PRU_U8_PCREL[\t ]*.text\+0x14
+0+0004 <[^>]*> 30eb8000        iloop   00000004 <[^>]*>, r11
+[\t ]*4: R_PRU_U8_PCREL[\t ]*.text\+0x14
+0+0008 <[^>]*> 00e0e0e0        add     r0, r0, r0
+0+000c <[^>]*> 00e0e0e0        add     r0, r0, r0
+0+0010 <[^>]*> 00e0e0e0        add     r0, r0, r0
diff --git a/gas/testsuite/gas/pru/loop.s b/gas/testsuite/gas/pru/loop.s
new file mode 100644 (file)
index 0000000..ae057a1
--- /dev/null
@@ -0,0 +1,10 @@
+# Source file used to test the loop instructions.
+
+foo:
+L1:
+       loop    L2, r10.b2
+       iloop   L2, r11
+       add     r0, r0, r0
+       add     r0, r0, r0
+       add     r0, r0, r0
+L2:
diff --git a/gas/testsuite/gas/pru/misc.d b/gas/testsuite/gas/pru/misc.d
new file mode 100644 (file)
index 0000000..7c791e6
--- /dev/null
@@ -0,0 +1,11 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU misc
+
+# Test the miscellaneous instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 2a000000        halt
+0+0004 <[^>]*> 3e800000        slp     1
+0+0008 <[^>]*> 3e000000        slp     0
diff --git a/gas/testsuite/gas/pru/misc.s b/gas/testsuite/gas/pru/misc.s
new file mode 100644 (file)
index 0000000..cfe4d88
--- /dev/null
@@ -0,0 +1,6 @@
+# Source file used to test the miscellaneous instructions.
+
+foo:
+       halt
+       slp     1
+       slp     0
diff --git a/gas/testsuite/gas/pru/pru.exp b/gas/testsuite/gas/pru/pru.exp
new file mode 100644 (file)
index 0000000..397f3da
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (C) 2014-2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+#
+# Some generic PRU tests
+#
+
+if { [istarget pru-*-*] } {
+    run_dump_tests [lsort [glob -nocomplain $srcdir/$subdir/*.d]]
+
+    run_list_test "illegal" ""
+    run_list_test "warn_reglabel" ""
+}
diff --git a/gas/testsuite/gas/pru/pseudo.d b/gas/testsuite/gas/pru/pseudo.d
new file mode 100644 (file)
index 0000000..8da6a11
--- /dev/null
@@ -0,0 +1,15 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU pseudo
+
+# Test the pseudo instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 1300e2e1        mov     r1, sp
+0+0004 <[^>]*> 12e0e0e0        nop
+0+0008 <[^>]*> 230100c3        call    00000400 <[^>]*>
+0+000c <[^>]*> 22ea00c3        call    r10
+0+0010 <[^>]*> 20c30000        ret
+0+0014 <[^>]*> d10cac00        wbc     r12.w1, 12
+0+0018 <[^>]*> c8e1ec00        wbs     r12, r1
diff --git a/gas/testsuite/gas/pru/pseudo.s b/gas/testsuite/gas/pru/pseudo.s
new file mode 100644 (file)
index 0000000..87b7dea
--- /dev/null
@@ -0,0 +1,10 @@
+# Source file used to test the pseudo instructions.
+
+foo:
+       mov     r1, r2
+       nop
+       call    0x400
+       call    r10
+       ret
+       wbc     r12.w1, 12
+       wbs     r12, r1
diff --git a/gas/testsuite/gas/pru/warn_reglabel.l b/gas/testsuite/gas/pru/warn_reglabel.l
new file mode 100644 (file)
index 0000000..eb077f1
--- /dev/null
@@ -0,0 +1,3 @@
+.*warn_reglabel.s: Assembler messages:
+.*warn_reglabel.s:3: Warning: Label "r30" matches a CPU register name
+.*warn_reglabel.s:5: Warning: Label "r1.b2" matches a CPU register name
diff --git a/gas/testsuite/gas/pru/warn_reglabel.s b/gas/testsuite/gas/pru/warn_reglabel.s
new file mode 100644 (file)
index 0000000..d5e46f7
--- /dev/null
@@ -0,0 +1,6 @@
+# Source file used to test warnings
+
+r30:
+       nop
+r1.b2:
+       nop
diff --git a/gas/testsuite/gas/pru/xfr.d b/gas/testsuite/gas/pru/xfr.d
new file mode 100644 (file)
index 0000000..fd9b889
--- /dev/null
@@ -0,0 +1,44 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU xfr
+
+# Test the XFR class of instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 2eff8002        zero    sp.b0, 1
+0+0004 <[^>]*> 2eff81d7        zero    r23.b2, 4
+0+0008 <[^>]*> 2effbd80        zero    r0.b0, 124
+0+000c <[^>]*> 2eff0002        fill    sp.b0, 1
+0+0010 <[^>]*> 2eff01b7        fill    r23.b1, 4
+0+0014 <[^>]*> 2eff3d80        fill    r0.b0, 124
+0+0018 <[^>]*> 2e80000a        xin     0, r10.b0, 1
+0+001c <[^>]*> 2e803daa        xin     0, r10.b1, 124
+0+0020 <[^>]*> 2efe806a        xin     253, r10.b3, 1
+0+0024 <[^>]*> 2efebdca        xin     253, r10.b2, 124
+0+0028 <[^>]*> 2eaaaa0c        xin     85, r12.b0, 85
+0+002c <[^>]*> 2f00000a        xout    0, r10.b0, 1
+0+0030 <[^>]*> 2f003daa        xout    0, r10.b1, 124
+0+0034 <[^>]*> 2f7e806a        xout    253, r10.b3, 1
+0+0038 <[^>]*> 2f7ebdca        xout    253, r10.b2, 124
+0+003c <[^>]*> 2f2aaa0c        xout    85, r12.b0, 85
+0+0040 <[^>]*> 2f80000a        xchg    0, r10.b0, 1
+0+0044 <[^>]*> 2f803daa        xchg    0, r10.b1, 124
+0+0048 <[^>]*> 2ffe806a        xchg    253, r10.b3, 1
+0+004c <[^>]*> 2ffebdca        xchg    253, r10.b2, 124
+0+0050 <[^>]*> 2faaaa0c        xchg    85, r12.b0, 85
+0+0054 <[^>]*> 2e80400a        sxin    0, r10.b0, 1
+0+0058 <[^>]*> 2e807daa        sxin    0, r10.b1, 124
+0+005c <[^>]*> 2efec06a        sxin    253, r10.b3, 1
+0+0060 <[^>]*> 2efefdca        sxin    253, r10.b2, 124
+0+0064 <[^>]*> 2eaaea0c        sxin    85, r12.b0, 85
+0+0068 <[^>]*> 2f00400a        sxout   0, r10.b0, 1
+0+006c <[^>]*> 2f007daa        sxout   0, r10.b1, 124
+0+0070 <[^>]*> 2f7ec06a        sxout   253, r10.b3, 1
+0+0074 <[^>]*> 2f7efdca        sxout   253, r10.b2, 124
+0+0078 <[^>]*> 2f2aea0c        sxout   85, r12.b0, 85
+0+007c <[^>]*> 2f80400a        sxchg   0, r10.b0, 1
+0+0080 <[^>]*> 2f807daa        sxchg   0, r10.b1, 124
+0+0084 <[^>]*> 2ffec06a        sxchg   253, r10.b3, 1
+0+0088 <[^>]*> 2ffefdca        sxchg   253, r10.b2, 124
+0+008c <[^>]*> 2faaea0c        sxchg   85, r12.b0, 85
diff --git a/gas/testsuite/gas/pru/xfr.s b/gas/testsuite/gas/pru/xfr.s
new file mode 100644 (file)
index 0000000..875e1ca
--- /dev/null
@@ -0,0 +1,52 @@
+# Source file used to test the XFR-class of instructions.
+
+foo:
+       # register clear and fill
+       zero    r2, 1
+       zero    r23.b2, 4
+       zero    r0, 124
+       fill    r2, 1
+       fill    r23.b1, 4
+       fill    r0, 124
+
+       # XIN
+       xin     0, r10, 1
+       xin     0, r10.b1, 124
+       xin     253, r10.b3, 1
+       xin     253, r10.b2, 124
+       xin     85, r12.b0, 85
+
+       # XOUT
+       xout    0, r10, 1
+       xout    0, r10.b1, 124
+       xout    253, r10.b3, 1
+       xout    253, r10.b2, 124
+       xout    85, r12.b0, 85
+
+       # XCHG
+       xchg    0, r10, 1
+       xchg    0, r10.b1, 124
+       xchg    253, r10.b3, 1
+       xchg    253, r10.b2, 124
+       xchg    85, r12.b0, 85
+
+       # SXIN
+       sxin    0, r10, 1
+       sxin    0, r10.b1, 124
+       sxin    253, r10.b3, 1
+       sxin    253, r10.b2, 124
+       sxin    85, r12.b0, 85
+
+       # SXOUT
+       sxout   0, r10, 1
+       sxout   0, r10.b1, 124
+       sxout   253, r10.b3, 1
+       sxout   253, r10.b2, 124
+       sxout   85, r12.b0, 85
+
+       # XCHG
+       sxchg   0, r10, 1
+       sxchg   0, r10.b1, 124
+       sxchg   253, r10.b3, 1
+       sxchg   253, r10.b2, 124
+       sxchg   85, r12.b0, 85