diff options
36 files changed, 2830 insertions, 0 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 16575d1..1b6fd9d 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -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. diff --git a/gas/Makefile.am b/gas/Makefile.am index cac7c7f..3bfab34 100644 --- a/gas/Makefile.am +++ b/gas/Makefile.am @@ -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 \ diff --git a/gas/Makefile.in b/gas/Makefile.in index 70578ad..a3962da 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -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 @@ -1,5 +1,7 @@ -*- text -*- +* Add support for the Texas Instruments PRU processor. + Changes in 2.28: * Add support for the RISC-V architecture. diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c index 8d80c77..a1b882e 100644 --- a/gas/config/obj-elf.c +++ b/gas/config/obj-elf.c @@ -64,6 +64,10 @@ #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 index 0000000..63aca6e --- /dev/null +++ b/gas/config/tc-pru.c @@ -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; + + +/** 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); + } +} + + +/** 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); + } +} + + +/** 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} +}; + + +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 (); +} + + +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); +} + + + +/** 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; +} + + + +/** 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")); +} + + +/** 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); +} + + +/** 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 index 0000000..d3d0d78 --- /dev/null +++ b/gas/config/tc-pru.h @@ -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__ */ diff --git a/gas/configure.tgt b/gas/configure.tgt index 4951555..4abf83f 100644 --- a/gas/configure.tgt +++ b/gas/configure.tgt @@ -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 ;; diff --git a/gas/doc/Makefile.am b/gas/doc/Makefile.am index 54d7ef1..c604a29 100644 --- a/gas/doc/Makefile.am +++ b/gas/doc/Makefile.am @@ -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 \ diff --git a/gas/doc/Makefile.in b/gas/doc/Makefile.in index 7110192..58b0e36 100644 --- a/gas/doc/Makefile.in +++ b/gas/doc/Makefile.in @@ -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 \ diff --git a/gas/doc/all.texi b/gas/doc/all.texi index 3c25d39..79fcaba 100644 --- a/gas/doc/all.texi +++ b/gas/doc/all.texi @@ -62,6 +62,7 @@ @set PDP11 @set PJ @set PPC +@set PRU @set RL78 @set RISCV @set RX diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index d83d2b3..f03c2ef 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -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 index 0000000..6aba8b4 --- /dev/null +++ b/gas/doc/c-pru.texi @@ -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 diff --git a/gas/po/POTFILES.in b/gas/po/POTFILES.in index ad7833d..98861f7 100644 --- a/gas/po/POTFILES.in +++ b/gas/po/POTFILES.in @@ -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 diff --git a/gas/testsuite/gas/lns/lns.exp b/gas/testsuite/gas/lns/lns.exp index 81e0396..acf9947 100644 --- a/gas/testsuite/gas/lns/lns.exp +++ b/gas/testsuite/gas/lns/lns.exp @@ -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 index 0000000..d91ad06 --- /dev/null +++ b/gas/testsuite/gas/pru/alu.d @@ -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 index 0000000..e61e101 --- /dev/null +++ b/gas/testsuite/gas/pru/alu.s @@ -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 index 0000000..f5b50a7 --- /dev/null +++ b/gas/testsuite/gas/pru/branch.d @@ -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 index 0000000..ab43c74 --- /dev/null +++ b/gas/testsuite/gas/pru/branch.s @@ -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 index 0000000..64de14b --- /dev/null +++ b/gas/testsuite/gas/pru/illegal.l @@ -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 index 0000000..1571f74 --- /dev/null +++ b/gas/testsuite/gas/pru/illegal.s @@ -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 index 0000000..8851504 --- /dev/null +++ b/gas/testsuite/gas/pru/ldi.d @@ -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 index 0000000..201a0f2 --- /dev/null +++ b/gas/testsuite/gas/pru/ldi.s @@ -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 index 0000000..7e44b6d --- /dev/null +++ b/gas/testsuite/gas/pru/ldst.d @@ -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 index 0000000..e8ad3a2 --- /dev/null +++ b/gas/testsuite/gas/pru/ldst.s @@ -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 index 0000000..b6d4a8a --- /dev/null +++ b/gas/testsuite/gas/pru/loop.d @@ -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 index 0000000..ae057a1 --- /dev/null +++ b/gas/testsuite/gas/pru/loop.s @@ -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 index 0000000..7c791e6 --- /dev/null +++ b/gas/testsuite/gas/pru/misc.d @@ -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 index 0000000..cfe4d88 --- /dev/null +++ b/gas/testsuite/gas/pru/misc.s @@ -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 index 0000000..397f3da --- /dev/null +++ b/gas/testsuite/gas/pru/pru.exp @@ -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 index 0000000..8da6a11 --- /dev/null +++ b/gas/testsuite/gas/pru/pseudo.d @@ -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 index 0000000..87b7dea --- /dev/null +++ b/gas/testsuite/gas/pru/pseudo.s @@ -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 index 0000000..eb077f1 --- /dev/null +++ b/gas/testsuite/gas/pru/warn_reglabel.l @@ -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 index 0000000..d5e46f7 --- /dev/null +++ b/gas/testsuite/gas/pru/warn_reglabel.s @@ -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 index 0000000..fd9b889 --- /dev/null +++ b/gas/testsuite/gas/pru/xfr.d @@ -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 index 0000000..875e1ca --- /dev/null +++ b/gas/testsuite/gas/pru/xfr.s @@ -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 |