From e0001a05d2e4967ee86f4468cdc4fafea66b92d1 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 1 Apr 2003 15:50:31 +0000 Subject: [PATCH] Add Xtensa port --- bfd/ChangeLog | 30 + bfd/Makefile.am | 20 +- bfd/Makefile.in | 24 +- bfd/archures.c | 4 + bfd/bfd-in2.h | 34 + bfd/config.bfd | 6 + bfd/configure | 30 +- bfd/configure.in | 2 + bfd/cpu-xtensa.c | 38 + bfd/elf32-xtensa.c | 5846 +++++++++++++ bfd/libbfd.h | 10 + bfd/reloc.c | 43 + bfd/targets.c | 4 + bfd/xtensa-isa.c | 593 ++ bfd/xtensa-modules.c | 6090 +++++++++++++ gas/ChangeLog | 29 + gas/Makefile.am | 27 +- gas/Makefile.in | 35 +- gas/config/tc-xtensa.c | 9014 ++++++++++++++++++++ gas/config/tc-xtensa.h | 200 + gas/config/xtensa-relax.c | 1766 ++++ gas/config/xtensa-relax.h | 142 + gas/configure | 370 +- gas/configure.in | 10 + gas/doc/Makefile.am | 1 + gas/doc/Makefile.in | 1 + gas/doc/all.texi | 1 + gas/doc/as.texinfo | 62 +- gas/doc/c-xtensa.texi | 740 ++ gas/doc/internals.texi | 4 + gas/testsuite/ChangeLog | 10 + gas/testsuite/gas/xtensa/all.exp | 98 + gas/testsuite/gas/xtensa/entry_align.s | 4 + gas/testsuite/gas/xtensa/entry_misalign.s | 4 + gas/testsuite/gas/xtensa/entry_misalign2.s | 6 + gas/testsuite/gas/xtensa/j_too_far.s | 8 + gas/testsuite/gas/xtensa/loop_align.s | 5 + gas/testsuite/gas/xtensa/loop_misalign.s | 5 + gas/write.c | 4 + include/ChangeLog | 7 + include/dis-asm.h | 1 + include/elf/ChangeLog | 5 + include/elf/common.h | 4 + include/elf/xtensa.h | 87 + include/xtensa-config.h | 70 + include/xtensa-isa-internal.h | 114 + include/xtensa-isa.h | 230 + ld/ChangeLog | 13 + ld/Makefile.am | 6 + ld/Makefile.in | 6 + ld/configure.tgt | 1 + ld/emulparams/elf32xtensa.sh | 32 + ld/emulparams/xtensa-config.sh | 8 + ld/emultempl/xtensaelf.em | 1586 ++++ ld/gen-doc.texi | 1 + ld/ld.texinfo | 77 +- ld/scripttempl/elfxtensa.sc | 397 + ld/testsuite/ChangeLog | 15 + ld/testsuite/ld-elf/merge.d | 2 +- ld/testsuite/ld-scripts/crossref.exp | 7 + ld/testsuite/ld-srec/srec.exp | 5 + ld/testsuite/ld-xtensa/coalesce.exp | 93 + ld/testsuite/ld-xtensa/coalesce.t | 6 + ld/testsuite/ld-xtensa/coalesce1.s | 15 + ld/testsuite/ld-xtensa/coalesce2.s | 9 + ld/testsuite/ld-xtensa/lcall.exp | 107 + ld/testsuite/ld-xtensa/lcall.t | 6 + ld/testsuite/ld-xtensa/lcall1.s | 12 + ld/testsuite/ld-xtensa/lcall2.s | 5 + opcodes/Makefile.am | 5 + opcodes/Makefile.in | 9 +- opcodes/configure | 387 +- opcodes/configure.in | 1 + opcodes/disassemble.c | 6 + opcodes/xtensa-dis.c | 526 ++ 75 files changed, 28789 insertions(+), 392 deletions(-) create mode 100644 bfd/cpu-xtensa.c create mode 100644 bfd/elf32-xtensa.c create mode 100644 bfd/xtensa-isa.c create mode 100644 bfd/xtensa-modules.c create mode 100644 gas/config/tc-xtensa.c create mode 100644 gas/config/tc-xtensa.h create mode 100644 gas/config/xtensa-relax.c create mode 100644 gas/config/xtensa-relax.h create mode 100644 gas/doc/c-xtensa.texi create mode 100644 gas/testsuite/gas/xtensa/all.exp create mode 100644 gas/testsuite/gas/xtensa/entry_align.s create mode 100644 gas/testsuite/gas/xtensa/entry_misalign.s create mode 100644 gas/testsuite/gas/xtensa/entry_misalign2.s create mode 100644 gas/testsuite/gas/xtensa/j_too_far.s create mode 100644 gas/testsuite/gas/xtensa/loop_align.s create mode 100644 gas/testsuite/gas/xtensa/loop_misalign.s create mode 100644 include/elf/xtensa.h create mode 100644 include/xtensa-config.h create mode 100644 include/xtensa-isa-internal.h create mode 100644 include/xtensa-isa.h create mode 100644 ld/emulparams/elf32xtensa.sh create mode 100644 ld/emulparams/xtensa-config.sh create mode 100644 ld/emultempl/xtensaelf.em create mode 100644 ld/scripttempl/elfxtensa.sc create mode 100644 ld/testsuite/ld-xtensa/coalesce.exp create mode 100644 ld/testsuite/ld-xtensa/coalesce.t create mode 100644 ld/testsuite/ld-xtensa/coalesce1.s create mode 100644 ld/testsuite/ld-xtensa/coalesce2.s create mode 100644 ld/testsuite/ld-xtensa/lcall.exp create mode 100644 ld/testsuite/ld-xtensa/lcall.t create mode 100644 ld/testsuite/ld-xtensa/lcall1.s create mode 100644 ld/testsuite/ld-xtensa/lcall2.s create mode 100644 opcodes/xtensa-dis.c diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 8ea9e6e14b3..0ce8c01b1cc 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,33 @@ +2003-04-01 Bob Wilson + + * Makefile.am (ALL_MACHINES): Add cpu-xtensa.lo. + (ALL_MACHINES_CFILES): Add cpu-xtensa.c. + (BFD32_BACKENDS): Add elf32-xtensa.lo, xtensa-isa.lo, and + xtensa-modules.lo. + (BFD32_BACKENDS_CFILES): Add elf32-xtensa.c, xtensa-isa.c, and + xtensa-modules.c. + (cpu-xtensa.lo): New target. + (elf32-xtensa.lo): Likewise. + (xtensa-isa.lo): Likewise. + (xtensa-modules.lo): Likewise. + * Makefile.in: Regenerate. + * archures.c (bfd_architecture): Add bfd_{arch,mach}_xtensa. + (bfd_archures_list): Add bfd_xtensa_arch. + * config.bfd: Handle xtensa-*-*. + * configure.in: Handle bfd_elf32_xtensa_{le,be}_vec. + * configure: Regenerate. + * reloc.c: Add BFD_RELOC_XTENSA_{RTLD,GLOB_DAT,JMP_SLOT,RELATIVE, + PLT,OP0,OP1,OP2,ASM_EXPAND,ASM_SIMPLIFY}. + * targets.c (bfd_elf32_xtensa_be_vec): Declare. + (bfd_elf32_xtensa_le_vec): Likewise. + (bfd_target_vector): Add bfd_elf32_xtensa_{be,le}_vec. + * cpu-xtensa.c: New file. + * elf32-xtensa.c: Likewise. + * xtensa-isa.c: Likewise. + * xtensa-modules.c: Likewise. + * libbfd.h: Regenerate. + * bfd-in2.h: Likewise. + 2003-04-01 Nick Clifton * archures.c (bfd_mach_arm_unknown): Define. diff --git a/bfd/Makefile.am b/bfd/Makefile.am index 401568fa7d9..ee04261549e 100644 --- a/bfd/Makefile.am +++ b/bfd/Makefile.am @@ -101,6 +101,7 @@ ALL_MACHINES = \ cpu-we32k.lo \ cpu-w65.lo \ cpu-xstormy16.lo \ + cpu-xtensa.lo \ cpu-z8k.lo ALL_MACHINES_CFILES = \ @@ -155,6 +156,7 @@ ALL_MACHINES_CFILES = \ cpu-we32k.c \ cpu-w65.c \ cpu-xstormy16.c \ + cpu-xtensa.c \ cpu-z8k.c # The .o files needed by all of the 32 bit vectors that are configured into @@ -249,6 +251,7 @@ BFD32_BACKENDS = \ elf32-v850.lo \ elf32-vax.lo \ elf32-xstormy16.lo \ + elf32-xtensa.lo \ elf32.lo \ elflink.lo \ elf-strtab.lo \ @@ -317,7 +320,9 @@ BFD32_BACKENDS = \ vms-misc.lo \ vms-tir.lo \ xcofflink.lo \ - xsym.lo + xsym.lo \ + xtensa-isa.lo \ + xtensa-modules.lo BFD32_BACKENDS_CFILES = \ aout-adobe.c \ @@ -408,6 +413,7 @@ BFD32_BACKENDS_CFILES = \ elf32-v850.c \ elf32-vax.c \ elf32-xstormy16.c \ + elf32-xtensa.c \ elf32.c \ elflink.c \ elf-strtab.c \ @@ -475,7 +481,9 @@ BFD32_BACKENDS_CFILES = \ vms-misc.c \ vms-tir.c \ xcofflink.c \ - xsym.c + xsym.c \ + xtensa-isa.c \ + xtensa-modules.c # The .o files needed by all of the 64 bit vectors that are configured into # target_vector in targets.c if configured with --enable-targets=all @@ -957,6 +965,7 @@ cpu-vax.lo: cpu-vax.c $(INCDIR)/filenames.h cpu-we32k.lo: cpu-we32k.c $(INCDIR)/filenames.h cpu-w65.lo: cpu-w65.c $(INCDIR)/filenames.h cpu-xstormy16.lo: cpu-xstormy16.c $(INCDIR)/filenames.h +cpu-xtensa.lo: cpu-xtensa.c $(INCDIR)/filenames.h cpu-z8k.lo: cpu-z8k.c $(INCDIR)/filenames.h aout-adobe.lo: aout-adobe.c $(INCDIR)/filenames.h $(INCDIR)/aout/adobe.h \ $(INCDIR)/aout/stab_gnu.h $(INCDIR)/aout/stab.def libaout.h \ @@ -1286,6 +1295,9 @@ elf32-xstormy16.lo: elf32-xstormy16.c $(INCDIR)/filenames.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(INCDIR)/elf/xstormy16.h \ $(INCDIR)/elf/reloc-macros.h $(INCDIR)/libiberty.h \ elf32-target.h +elf32-xtensa.lo: elf32-xtensa.c $(INCDIR)/bfdlink.h elf-bfd.h \ + $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \ + $(INCDIR)/elf/xtensa.h $(INCDIR)/xtensa-isa.h elf32-target.h elf32.lo: elf32.c elfcode.h $(INCDIR)/filenames.h $(INCDIR)/libiberty.h \ $(INCDIR)/bfdlink.h elf-bfd.h $(INCDIR)/elf/common.h \ $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h elfcore.h \ @@ -1490,6 +1502,10 @@ xcofflink.lo: xcofflink.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h \ $(INCDIR)/coff/internal.h $(INCDIR)/coff/xcoff.h libcoff.h \ libxcoff.h xsym.lo: xsym.c xsym.h $(INCDIR)/filenames.h +xtensa-isa.lo: xtensa-isa.c $(INCDIR)/xtensa-isa.h \ + $(INCDIR)/xtensa-isa-internal.h +xtensa-modules.lo: xtensa-modules.c $(INCDIR)/xtensa-isa.h \ + $(INCDIR)/xtensa-isa-internal.h aix5ppc-core.lo: aix5ppc-core.c aout64.lo: aout64.c aoutx.h $(INCDIR)/filenames.h $(INCDIR)/safe-ctype.h \ $(INCDIR)/bfdlink.h libaout.h $(INCDIR)/aout/aout64.h \ diff --git a/bfd/Makefile.in b/bfd/Makefile.in index a05b3423e79..3de6121d8e4 100644 --- a/bfd/Makefile.in +++ b/bfd/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation @@ -226,6 +226,7 @@ ALL_MACHINES = \ cpu-we32k.lo \ cpu-w65.lo \ cpu-xstormy16.lo \ + cpu-xtensa.lo \ cpu-z8k.lo @@ -281,6 +282,7 @@ ALL_MACHINES_CFILES = \ cpu-we32k.c \ cpu-w65.c \ cpu-xstormy16.c \ + cpu-xtensa.c \ cpu-z8k.c @@ -376,6 +378,7 @@ BFD32_BACKENDS = \ elf32-v850.lo \ elf32-vax.lo \ elf32-xstormy16.lo \ + elf32-xtensa.lo \ elf32.lo \ elflink.lo \ elf-strtab.lo \ @@ -444,7 +447,9 @@ BFD32_BACKENDS = \ vms-misc.lo \ vms-tir.lo \ xcofflink.lo \ - xsym.lo + xsym.lo \ + xtensa-isa.lo \ + xtensa-modules.lo BFD32_BACKENDS_CFILES = \ @@ -536,6 +541,7 @@ BFD32_BACKENDS_CFILES = \ elf32-v850.c \ elf32-vax.c \ elf32-xstormy16.c \ + elf32-xtensa.c \ elf32.c \ elflink.c \ elf-strtab.c \ @@ -603,7 +609,9 @@ BFD32_BACKENDS_CFILES = \ vms-misc.c \ vms-tir.c \ xcofflink.c \ - xsym.c + xsym.c \ + xtensa-isa.c \ + xtensa-modules.c # The .o files needed by all of the 64 bit vectors that are configured into @@ -799,7 +807,7 @@ configure.in version.h DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) -TAR = tar +TAR = gtar GZIP_ENV = --best SOURCES = $(libbfd_a_SOURCES) $(libbfd_la_SOURCES) OBJECTS = $(libbfd_a_OBJECTS) $(libbfd_la_OBJECTS) @@ -1490,6 +1498,7 @@ cpu-vax.lo: cpu-vax.c $(INCDIR)/filenames.h cpu-we32k.lo: cpu-we32k.c $(INCDIR)/filenames.h cpu-w65.lo: cpu-w65.c $(INCDIR)/filenames.h cpu-xstormy16.lo: cpu-xstormy16.c $(INCDIR)/filenames.h +cpu-xtensa.lo: cpu-xtensa.c $(INCDIR)/filenames.h cpu-z8k.lo: cpu-z8k.c $(INCDIR)/filenames.h aout-adobe.lo: aout-adobe.c $(INCDIR)/filenames.h $(INCDIR)/aout/adobe.h \ $(INCDIR)/aout/stab_gnu.h $(INCDIR)/aout/stab.def libaout.h \ @@ -1819,6 +1828,9 @@ elf32-xstormy16.lo: elf32-xstormy16.c $(INCDIR)/filenames.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(INCDIR)/elf/xstormy16.h \ $(INCDIR)/elf/reloc-macros.h $(INCDIR)/libiberty.h \ elf32-target.h +elf32-xtensa.lo: elf32-xtensa.c $(INCDIR)/bfdlink.h elf-bfd.h \ + $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \ + $(INCDIR)/elf/xtensa.h $(INCDIR)/xtensa-isa.h elf32-target.h elf32.lo: elf32.c elfcode.h $(INCDIR)/filenames.h $(INCDIR)/libiberty.h \ $(INCDIR)/bfdlink.h elf-bfd.h $(INCDIR)/elf/common.h \ $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h elfcore.h \ @@ -2023,6 +2035,10 @@ xcofflink.lo: xcofflink.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h \ $(INCDIR)/coff/internal.h $(INCDIR)/coff/xcoff.h libcoff.h \ libxcoff.h xsym.lo: xsym.c xsym.h $(INCDIR)/filenames.h +xtensa-isa.lo: xtensa-isa.c $(INCDIR)/xtensa-isa.h \ + $(INCDIR)/xtensa-isa-internal.h +xtensa-modules.lo: xtensa-modules.c $(INCDIR)/xtensa-isa.h \ + $(INCDIR)/xtensa-isa-internal.h aix5ppc-core.lo: aix5ppc-core.c aout64.lo: aout64.c aoutx.h $(INCDIR)/filenames.h $(INCDIR)/safe-ctype.h \ $(INCDIR)/bfdlink.h libaout.h $(INCDIR)/aout/aout64.h \ diff --git a/bfd/archures.c b/bfd/archures.c index 28e27dffe78..9337fb19273 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -308,6 +308,8 @@ DESCRIPTION .#define bfd_mach_msp44 44 .#define bfd_mach_msp15 15 .#define bfd_mach_msp16 16 +. bfd_arch_xtensa, {* Tensilica's Xtensa cores. *} +.#define bfd_mach_xtensa 1 . bfd_arch_last . }; */ @@ -399,6 +401,7 @@ extern const bfd_arch_info_type bfd_vax_arch; extern const bfd_arch_info_type bfd_we32k_arch; extern const bfd_arch_info_type bfd_w65_arch; extern const bfd_arch_info_type bfd_xstormy16_arch; +extern const bfd_arch_info_type bfd_xtensa_arch; extern const bfd_arch_info_type bfd_z8k_arch; static const bfd_arch_info_type * const bfd_archures_list[] = @@ -456,6 +459,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] = &bfd_w65_arch, &bfd_we32k_arch, &bfd_xstormy16_arch, + &bfd_xtensa_arch, &bfd_z8k_arch, #endif 0 diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index d15bcda8236..a5ff0c0599b 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -1774,6 +1774,8 @@ enum bfd_architecture #define bfd_mach_msp44 44 #define bfd_mach_msp15 15 #define bfd_mach_msp16 16 + bfd_arch_xtensa, /* Tensilica's Xtensa cores. */ +#define bfd_mach_xtensa 1 bfd_arch_last }; @@ -3437,6 +3439,38 @@ to follow the 16K memory bank of 68HC12 (seen as mapped in the window). */ BFD_RELOC_IQ2000_OFFSET_16, BFD_RELOC_IQ2000_OFFSET_21, BFD_RELOC_IQ2000_UHI16, + +/* Special Xtensa relocation used only by PLT entries in ELF shared +objects to indicate that the runtime linker should set the value +to one of its own internal functions or data structures. */ + BFD_RELOC_XTENSA_RTLD, + +/* Xtensa relocations for ELF shared objects. */ + BFD_RELOC_XTENSA_GLOB_DAT, + BFD_RELOC_XTENSA_JMP_SLOT, + BFD_RELOC_XTENSA_RELATIVE, + +/* Xtensa relocation used in ELF object files for symbols that may require +PLT entries. Otherwise, this is just a generic 32-bit relocation. */ + BFD_RELOC_XTENSA_PLT, + +/* Generic Xtensa relocations. Only the operand number is encoded +in the relocation. The details are determined by extracting the +instruction opcode. */ + BFD_RELOC_XTENSA_OP0, + BFD_RELOC_XTENSA_OP1, + BFD_RELOC_XTENSA_OP2, + +/* Xtensa relocation to mark that the assembler expanded the +instructions from an original target. The expansion size is +encoded in the reloc size. */ + BFD_RELOC_XTENSA_ASM_EXPAND, + +/* Xtensa relocation to mark that the linker should simplify +assembler-expanded instructions. This is commonly used +internally by the linker after analysis of a +BFD_RELOC_XTENSA_ASM_EXPAND. */ + BFD_RELOC_XTENSA_ASM_SIMPLIFY, BFD_RELOC_UNUSED }; typedef enum bfd_reloc_code_real bfd_reloc_code_real_type; reloc_howto_type * diff --git a/bfd/config.bfd b/bfd/config.bfd index c678496dbd3..d034ce8e9e8 100644 --- a/bfd/config.bfd +++ b/bfd/config.bfd @@ -58,6 +58,7 @@ thumb*) targ_archs=bfd_arm_arch ;; v850*) targ_archs=bfd_v850_arch ;; x86_64) targ_archs=bfd_i386_arch ;; xscale*) targ_archs=bfd_arm_arch ;; +xtensa*) targ_archs=bfd_xtensa_arch ;; z8k*) targ_archs=bfd_z8k_arch ;; *) targ_archs=bfd_${targ_cpu}_arch ;; esac @@ -1214,6 +1215,11 @@ case "${targ}" in targ_defvec=bfd_elf32_xstormy16_vec ;; + xtensa-*-*) + targ_defvec=bfd_elf32_xtensa_le_vec + targ_selvecs=bfd_elf32_xtensa_be_vec + ;; + z8k*-*-*) targ_defvec=z8kcoff_vec targ_underscore=yes diff --git a/bfd/configure b/bfd/configure index 6616dc01b36..ad1475dc32c 100755 --- a/bfd/configure +++ b/bfd/configure @@ -6155,6 +6155,8 @@ do bfd_elf32_v850_vec) tb="$tb elf32-v850.lo elf32.lo $elf" ;; bfd_elf32_vax_vec) tb="$tb elf32-vax.lo elf32.lo $elf" ;; bfd_elf32_xstormy16_vec) tb="$tb elf32-xstormy16.lo elf32.lo $elf" ;; + bfd_elf32_xtensa_le_vec) tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;; + bfd_elf32_xtensa_be_vec) tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;; bfd_elf64_alpha_freebsd_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_alpha_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_big_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;; @@ -6372,10 +6374,10 @@ case ${host64}-${target64}-${want64} in if test -n "$GCC" ; then bad_64bit_gcc=no; echo $ac_n "checking for gcc version with buggy 64-bit support""... $ac_c" 1>&6 -echo "configure:6376: checking for gcc version with buggy 64-bit support" >&5 +echo "configure:6378: checking for gcc version with buggy 64-bit support" >&5 # Add more tests for gcc versions with non-working 64-bit support here. cat > conftest.$ac_ext <&6 -echo "configure:6425: checking for $ac_hdr" >&5 +echo "configure:6427: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:6435: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:6437: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -6460,12 +6462,12 @@ done for ac_func in getpagesize do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:6464: checking for $ac_func" >&5 +echo "configure:6466: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6494: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -6513,7 +6515,7 @@ fi done echo $ac_n "checking for working mmap""... $ac_c" 1>&6 -echo "configure:6517: checking for working mmap" >&5 +echo "configure:6519: checking for working mmap" >&5 if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -6521,7 +6523,7 @@ else ac_cv_func_mmap_fixed_mapped=no else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:6667: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_func_mmap_fixed_mapped=yes else @@ -6686,12 +6688,12 @@ fi for ac_func in madvise mprotect do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:6690: checking for $ac_func" >&5 +echo "configure:6692: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6720: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else diff --git a/bfd/configure.in b/bfd/configure.in index ddb5e8b287a..347ad97c1e4 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -639,6 +639,8 @@ do bfd_elf32_v850_vec) tb="$tb elf32-v850.lo elf32.lo $elf" ;; bfd_elf32_vax_vec) tb="$tb elf32-vax.lo elf32.lo $elf" ;; bfd_elf32_xstormy16_vec) tb="$tb elf32-xstormy16.lo elf32.lo $elf" ;; + bfd_elf32_xtensa_le_vec) tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;; + bfd_elf32_xtensa_be_vec) tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;; bfd_elf64_alpha_freebsd_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_alpha_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_big_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;; diff --git a/bfd/cpu-xtensa.c b/bfd/cpu-xtensa.c new file mode 100644 index 00000000000..fbfff64b611 --- /dev/null +++ b/bfd/cpu-xtensa.c @@ -0,0 +1,38 @@ +/* BFD support for the Xtensa processor. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" + +const bfd_arch_info_type bfd_xtensa_arch = +{ + 32, /* Bits per word. */ + 32, /* Bits per address. */ + 8, /* Bits per byte. */ + bfd_arch_xtensa, /* Architecture. */ + bfd_mach_xtensa, /* Machine. */ + "xtensa", /* Architecture name. */ + "xtensa", /* Printable name. */ + 4, /* Section align power. */ + TRUE, /* The default? */ + bfd_default_compatible, /* Architecture comparison fn. */ + bfd_default_scan, /* String to architecture convert fn. */ + NULL /* Next in list. */ +}; diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c new file mode 100644 index 00000000000..92fb98c7721 --- /dev/null +++ b/bfd/elf32-xtensa.c @@ -0,0 +1,5846 @@ +/* Xtensa-specific support for 32-bit ELF. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" + +#ifdef ANSI_PROTOTYPES +#include +#else +#include +#endif +#include + +#include "bfdlink.h" +#include "libbfd.h" +#include "elf-bfd.h" +#include "elf/xtensa.h" +#include "xtensa-isa.h" +#include "xtensa-config.h" + +/* Main interface functions. */ +static void elf_xtensa_info_to_howto_rela + PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); +static reloc_howto_type *elf_xtensa_reloc_type_lookup + PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); +extern int xtensa_read_table_entries + PARAMS ((bfd *, asection *, property_table_entry **, const char *)); +static bfd_boolean elf_xtensa_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *)); +static void elf_xtensa_hide_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean)); +static void elf_xtensa_copy_indirect_symbol + PARAMS ((struct elf_backend_data *, struct elf_link_hash_entry *, + struct elf_link_hash_entry *)); +static asection *elf_xtensa_gc_mark_hook + PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, + struct elf_link_hash_entry *, Elf_Internal_Sym *)); +static bfd_boolean elf_xtensa_gc_sweep_hook + PARAMS ((bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *)); +static bfd_boolean elf_xtensa_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static bfd_boolean elf_xtensa_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static bfd_boolean elf_xtensa_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static bfd_boolean elf_xtensa_modify_segment_map + PARAMS ((bfd *)); +static bfd_boolean elf_xtensa_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); +static bfd_boolean elf_xtensa_relax_section + PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *again)); +static bfd_boolean elf_xtensa_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, + Elf_Internal_Sym *)); +static bfd_boolean elf_xtensa_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static bfd_boolean elf_xtensa_merge_private_bfd_data + PARAMS ((bfd *, bfd *)); +static bfd_boolean elf_xtensa_set_private_flags + PARAMS ((bfd *, flagword)); +extern flagword elf_xtensa_get_private_bfd_flags + PARAMS ((bfd *)); +static bfd_boolean elf_xtensa_print_private_bfd_data + PARAMS ((bfd *, PTR)); +static bfd_boolean elf_xtensa_object_p + PARAMS ((bfd *)); +static void elf_xtensa_final_write_processing + PARAMS ((bfd *, bfd_boolean)); +static enum elf_reloc_type_class elf_xtensa_reloc_type_class + PARAMS ((const Elf_Internal_Rela *)); +static bfd_boolean elf_xtensa_discard_info + PARAMS ((bfd *, struct elf_reloc_cookie *, struct bfd_link_info *)); +static bfd_boolean elf_xtensa_ignore_discarded_relocs + PARAMS ((asection *)); +static bfd_boolean elf_xtensa_grok_prstatus + PARAMS ((bfd *, Elf_Internal_Note *)); +static bfd_boolean elf_xtensa_grok_psinfo + PARAMS ((bfd *, Elf_Internal_Note *)); +static bfd_boolean elf_xtensa_new_section_hook + PARAMS ((bfd *, asection *)); + + +/* Local helper functions. */ + +static int property_table_compare + PARAMS ((const PTR, const PTR)); +static bfd_boolean elf_xtensa_in_literal_pool + PARAMS ((property_table_entry *, int, bfd_vma)); +static void elf_xtensa_make_sym_local + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static bfd_boolean add_extra_plt_sections + PARAMS ((bfd *, int)); +static bfd_boolean elf_xtensa_fix_refcounts + PARAMS ((struct elf_link_hash_entry *, PTR)); +static bfd_boolean elf_xtensa_allocate_plt_size + PARAMS ((struct elf_link_hash_entry *, PTR)); +static bfd_boolean elf_xtensa_allocate_got_size + PARAMS ((struct elf_link_hash_entry *, PTR)); +static void elf_xtensa_allocate_local_got_size + PARAMS ((struct bfd_link_info *, asection *)); +static bfd_reloc_status_type elf_xtensa_do_reloc + PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_vma, bfd_byte *, + bfd_vma, bfd_boolean, char **)); +static char * vsprint_msg + VPARAMS ((const char *, const char *, int, ...)); +static char *build_encoding_error_message + PARAMS ((xtensa_opcode, xtensa_encode_result)); +static bfd_reloc_status_type bfd_elf_xtensa_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static void do_fix_for_relocateable_link + PARAMS ((Elf_Internal_Rela *, bfd *, asection *)); +static void do_fix_for_final_link + PARAMS ((Elf_Internal_Rela *, asection *, bfd_vma *)); +static bfd_boolean xtensa_elf_dynamic_symbol_p + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static bfd_vma elf_xtensa_create_plt_entry + PARAMS ((bfd *, bfd *, unsigned)); +static int elf_xtensa_combine_prop_entries + PARAMS ((bfd *, const char *)); +static bfd_boolean elf_xtensa_discard_info_for_section + PARAMS ((bfd *, struct elf_reloc_cookie *, struct bfd_link_info *, + asection *)); + +/* Local functions to handle Xtensa configurability. */ + +static void init_call_opcodes + PARAMS ((void)); +static bfd_boolean is_indirect_call_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_direct_call_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_windowed_call_opcode + PARAMS ((xtensa_opcode)); +static xtensa_opcode get_l32r_opcode + PARAMS ((void)); +static bfd_vma l32r_offset + PARAMS ((bfd_vma, bfd_vma)); +static int get_relocation_opnd + PARAMS ((Elf_Internal_Rela *)); +static xtensa_opcode get_relocation_opcode + PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *)); +static bfd_boolean is_l32r_relocation + PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *)); + +/* Functions for link-time code simplifications. */ + +static bfd_reloc_status_type elf_xtensa_do_asm_simplify + PARAMS ((bfd_byte *, bfd_vma, bfd_vma)); +static bfd_reloc_status_type contract_asm_expansion + PARAMS ((bfd_byte *, bfd_vma, Elf_Internal_Rela *)); +static xtensa_opcode swap_callx_for_call_opcode + PARAMS ((xtensa_opcode)); +static xtensa_opcode get_expanded_call_opcode + PARAMS ((bfd_byte *, int)); + +/* Access to internal relocations, section contents and symbols. */ + +static Elf_Internal_Rela *retrieve_internal_relocs + PARAMS ((bfd *, asection *, bfd_boolean)); +static void pin_internal_relocs + PARAMS ((asection *, Elf_Internal_Rela *)); +static void release_internal_relocs + PARAMS ((asection *, Elf_Internal_Rela *)); +static bfd_byte *retrieve_contents + PARAMS ((bfd *, asection *, bfd_boolean)); +static void pin_contents + PARAMS ((asection *, bfd_byte *)); +static void release_contents + PARAMS ((asection *, bfd_byte *)); +static Elf_Internal_Sym *retrieve_local_syms + PARAMS ((bfd *)); + +/* Miscellaneous utility functions. */ + +static asection *elf_xtensa_get_plt_section + PARAMS ((bfd *, int)); +static asection *elf_xtensa_get_gotplt_section + PARAMS ((bfd *, int)); +static asection *get_elf_r_symndx_section + PARAMS ((bfd *, unsigned long)); +static struct elf_link_hash_entry *get_elf_r_symndx_hash_entry + PARAMS ((bfd *, unsigned long)); +static bfd_vma get_elf_r_symndx_offset + PARAMS ((bfd *, unsigned long)); +static bfd_boolean pcrel_reloc_fits + PARAMS ((xtensa_operand, bfd_vma, bfd_vma)); +static bfd_boolean xtensa_is_property_section + PARAMS ((asection *)); +static bfd_boolean is_literal_section + PARAMS ((asection *)); +static int internal_reloc_compare + PARAMS ((const PTR, const PTR)); +static bfd_boolean get_is_linkonce_section + PARAMS ((bfd *, asection *)); +extern char *xtensa_get_property_section_name + PARAMS ((bfd *, asection *, const char *)); + +/* Other functions called directly by the linker. */ + +typedef void (*deps_callback_t) + PARAMS ((asection *, bfd_vma, asection *, bfd_vma, PTR)); +extern bfd_boolean xtensa_callback_required_dependence + PARAMS ((bfd *, asection *, struct bfd_link_info *, + deps_callback_t, PTR)); + + +typedef struct xtensa_relax_info_struct xtensa_relax_info; + + +/* Total count of PLT relocations seen during check_relocs. + The actual PLT code must be split into multiple sections and all + the sections have to be created before size_dynamic_sections, + where we figure out the exact number of PLT entries that will be + needed. It is OK is this count is an overestimate, e.g., some + relocations may be removed by GC. */ + +static int plt_reloc_count = 0; + + +/* When this is true, relocations may have been modified to refer to + symbols from other input files. The per-section list of "fix" + records needs to be checked when resolving relocations. */ + +static bfd_boolean relaxing_section = FALSE; + + +static reloc_howto_type elf_howto_table[] = +{ + HOWTO (R_XTENSA_NONE, 0, 0, 0, FALSE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_NONE", + FALSE, 0x00000000, 0x00000000, FALSE), + HOWTO (R_XTENSA_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_xtensa_reloc, "R_XTENSA_32", + TRUE, 0xffffffff, 0xffffffff, FALSE), + /* Replace a 32-bit value with a value from the runtime linker (only + used by linker-generated stub functions). The r_addend value is + special: 1 means to substitute a pointer to the runtime linker's + dynamic resolver function; 2 means to substitute the link map for + the shared object. */ + HOWTO (R_XTENSA_RTLD, 0, 2, 32, FALSE, 0, complain_overflow_dont, + NULL, "R_XTENSA_RTLD", + FALSE, 0x00000000, 0x00000000, FALSE), + HOWTO (R_XTENSA_GLOB_DAT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_XTENSA_GLOB_DAT", + FALSE, 0xffffffff, 0xffffffff, FALSE), + HOWTO (R_XTENSA_JMP_SLOT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_XTENSA_JMP_SLOT", + FALSE, 0xffffffff, 0xffffffff, FALSE), + HOWTO (R_XTENSA_RELATIVE, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_XTENSA_RELATIVE", + FALSE, 0xffffffff, 0xffffffff, FALSE), + HOWTO (R_XTENSA_PLT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, + bfd_elf_xtensa_reloc, "R_XTENSA_PLT", + FALSE, 0xffffffff, 0xffffffff, FALSE), + EMPTY_HOWTO (7), + HOWTO (R_XTENSA_OP0, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_OP0", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_OP1, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_OP1", + FALSE, 0x00000000, 0x00000000, TRUE), + HOWTO (R_XTENSA_OP2, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_OP2", + FALSE, 0x00000000, 0x00000000, TRUE), + /* Assembly auto-expansion. */ + HOWTO (R_XTENSA_ASM_EXPAND, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_ASM_EXPAND", + FALSE, 0x00000000, 0x00000000, FALSE), + /* Relax assembly auto-expansion. */ + HOWTO (R_XTENSA_ASM_SIMPLIFY, 0, 0, 0, TRUE, 0, complain_overflow_dont, + bfd_elf_xtensa_reloc, "R_XTENSA_ASM_SIMPLIFY", + FALSE, 0x00000000, 0x00000000, TRUE), + EMPTY_HOWTO (13), + EMPTY_HOWTO (14), + /* GNU extension to record C++ vtable hierarchy. */ + HOWTO (R_XTENSA_GNU_VTINHERIT, 0, 2, 0, FALSE, 0, complain_overflow_dont, + NULL, "R_XTENSA_GNU_VTINHERIT", + FALSE, 0x00000000, 0x00000000, FALSE), + /* GNU extension to record C++ vtable member usage. */ + HOWTO (R_XTENSA_GNU_VTENTRY, 0, 2, 0, FALSE, 0, complain_overflow_dont, + _bfd_elf_rel_vtable_reloc_fn, "R_XTENSA_GNU_VTENTRY", + FALSE, 0x00000000, 0x00000000, FALSE) +}; + +#ifdef DEBUG_GEN_RELOC +#define TRACE(str) \ + fprintf (stderr, "Xtensa bfd reloc lookup %d (%s)\n", code, str) +#else +#define TRACE(str) +#endif + +static reloc_howto_type * +elf_xtensa_reloc_type_lookup (abfd, code) + bfd *abfd ATTRIBUTE_UNUSED; + bfd_reloc_code_real_type code; +{ + switch (code) + { + case BFD_RELOC_NONE: + TRACE ("BFD_RELOC_NONE"); + return &elf_howto_table[(unsigned) R_XTENSA_NONE ]; + + case BFD_RELOC_32: + TRACE ("BFD_RELOC_32"); + return &elf_howto_table[(unsigned) R_XTENSA_32 ]; + + case BFD_RELOC_XTENSA_RTLD: + TRACE ("BFD_RELOC_XTENSA_RTLD"); + return &elf_howto_table[(unsigned) R_XTENSA_RTLD ]; + + case BFD_RELOC_XTENSA_GLOB_DAT: + TRACE ("BFD_RELOC_XTENSA_GLOB_DAT"); + return &elf_howto_table[(unsigned) R_XTENSA_GLOB_DAT ]; + + case BFD_RELOC_XTENSA_JMP_SLOT: + TRACE ("BFD_RELOC_XTENSA_JMP_SLOT"); + return &elf_howto_table[(unsigned) R_XTENSA_JMP_SLOT ]; + + case BFD_RELOC_XTENSA_RELATIVE: + TRACE ("BFD_RELOC_XTENSA_RELATIVE"); + return &elf_howto_table[(unsigned) R_XTENSA_RELATIVE ]; + + case BFD_RELOC_XTENSA_PLT: + TRACE ("BFD_RELOC_XTENSA_PLT"); + return &elf_howto_table[(unsigned) R_XTENSA_PLT ]; + + case BFD_RELOC_XTENSA_OP0: + TRACE ("BFD_RELOC_XTENSA_OP0"); + return &elf_howto_table[(unsigned) R_XTENSA_OP0 ]; + + case BFD_RELOC_XTENSA_OP1: + TRACE ("BFD_RELOC_XTENSA_OP1"); + return &elf_howto_table[(unsigned) R_XTENSA_OP1 ]; + + case BFD_RELOC_XTENSA_OP2: + TRACE ("BFD_RELOC_XTENSA_OP2"); + return &elf_howto_table[(unsigned) R_XTENSA_OP2 ]; + + case BFD_RELOC_XTENSA_ASM_EXPAND: + TRACE ("BFD_RELOC_XTENSA_ASM_EXPAND"); + return &elf_howto_table[(unsigned) R_XTENSA_ASM_EXPAND ]; + + case BFD_RELOC_XTENSA_ASM_SIMPLIFY: + TRACE ("BFD_RELOC_XTENSA_ASM_SIMPLIFY"); + return &elf_howto_table[(unsigned) R_XTENSA_ASM_SIMPLIFY ]; + + case BFD_RELOC_VTABLE_INHERIT: + TRACE ("BFD_RELOC_VTABLE_INHERIT"); + return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTINHERIT ]; + + case BFD_RELOC_VTABLE_ENTRY: + TRACE ("BFD_RELOC_VTABLE_ENTRY"); + return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ]; + + default: + break; + } + + TRACE ("Unknown"); + return NULL; +} + + +/* Given an ELF "rela" relocation, find the corresponding howto and record + it in the BFD internal arelent representation of the relocation. */ + +static void +elf_xtensa_info_to_howto_rela (abfd, cache_ptr, dst) + bfd *abfd ATTRIBUTE_UNUSED; + arelent *cache_ptr; + Elf_Internal_Rela *dst; +{ + unsigned int r_type = ELF32_R_TYPE (dst->r_info); + + BFD_ASSERT (r_type < (unsigned int) R_XTENSA_max); + cache_ptr->howto = &elf_howto_table[r_type]; +} + + +/* Functions for the Xtensa ELF linker. */ + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ + +#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so" + +/* The size in bytes of an entry in the procedure linkage table. + (This does _not_ include the space for the literals associated with + the PLT entry.) */ + +#define PLT_ENTRY_SIZE 16 + +/* For _really_ large PLTs, we may need to alternate between literals + and code to keep the literals within the 256K range of the L32R + instructions in the code. It's unlikely that anyone would ever need + such a big PLT, but an arbitrary limit on the PLT size would be bad. + Thus, we split the PLT into chunks. Since there's very little + overhead (2 extra literals) for each chunk, the chunk size is kept + small so that the code for handling multiple chunks get used and + tested regularly. With 254 entries, there are 1K of literals for + each chunk, and that seems like a nice round number. */ + +#define PLT_ENTRIES_PER_CHUNK 254 + +/* PLT entries are actually used as stub functions for lazy symbol + resolution. Once the symbol is resolved, the stub function is never + invoked. Note: the 32-byte frame size used here cannot be changed + without a corresponding change in the runtime linker. */ + +static const bfd_byte elf_xtensa_be_plt_entry[PLT_ENTRY_SIZE] = +{ + 0x6c, 0x10, 0x04, /* entry sp, 32 */ + 0x18, 0x00, 0x00, /* l32r a8, [got entry for rtld's resolver] */ + 0x1a, 0x00, 0x00, /* l32r a10, [got entry for rtld's link map] */ + 0x1b, 0x00, 0x00, /* l32r a11, [literal for reloc index] */ + 0x0a, 0x80, 0x00, /* jx a8 */ + 0 /* unused */ +}; + +static const bfd_byte elf_xtensa_le_plt_entry[PLT_ENTRY_SIZE] = +{ + 0x36, 0x41, 0x00, /* entry sp, 32 */ + 0x81, 0x00, 0x00, /* l32r a8, [got entry for rtld's resolver] */ + 0xa1, 0x00, 0x00, /* l32r a10, [got entry for rtld's link map] */ + 0xb1, 0x00, 0x00, /* l32r a11, [literal for reloc index] */ + 0xa0, 0x08, 0x00, /* jx a8 */ + 0 /* unused */ +}; + + +static int +property_table_compare (ap, bp) + const PTR ap; + const PTR bp; +{ + const property_table_entry *a = (const property_table_entry *) ap; + const property_table_entry *b = (const property_table_entry *) bp; + + /* Check if one entry overlaps with the other; this shouldn't happen + except when searching for a match. */ + if ((b->address >= a->address && b->address < (a->address + a->size)) + || (a->address >= b->address && a->address < (b->address + b->size))) + return 0; + + return (a->address - b->address); +} + + +/* Get the literal table or instruction table entries for the given + section. Sets TABLE_P and returns the number of entries. On error, + returns a negative value. */ + +int +xtensa_read_table_entries (abfd, section, table_p, sec_name) + bfd *abfd; + asection *section; + property_table_entry **table_p; + const char *sec_name; +{ + asection *table_section; + char *table_section_name; + bfd_size_type table_size = 0; + bfd_byte *table_data; + property_table_entry *blocks; + int block_count; + bfd_size_type num_records; + Elf_Internal_Rela *internal_relocs; + + table_section_name = + xtensa_get_property_section_name (abfd, section, sec_name); + table_section = bfd_get_section_by_name (abfd, table_section_name); + if (table_section != NULL) + table_size = bfd_get_section_size_before_reloc (table_section); + + if (table_size == 0) + { + *table_p = NULL; + return 0; + } + + num_records = table_size / sizeof (property_table_entry); + table_data = retrieve_contents (abfd, table_section, TRUE); + blocks = (property_table_entry *) + bfd_malloc (num_records * sizeof (property_table_entry)); + block_count = 0; + + /* If the file has not yet been relocated, process the relocations + and sort out the table entries that apply to the specified section. */ + internal_relocs = retrieve_internal_relocs (abfd, table_section, TRUE); + if (internal_relocs) + { + unsigned i; + + for (i = 0; i < table_section->reloc_count; i++) + { + Elf_Internal_Rela *rel = &internal_relocs[i]; + unsigned long r_symndx; + + if (ELF32_R_TYPE (rel->r_info) == R_XTENSA_NONE) + continue; + + BFD_ASSERT (ELF32_R_TYPE (rel->r_info) == R_XTENSA_32); + r_symndx = ELF32_R_SYM (rel->r_info); + + if (get_elf_r_symndx_section (abfd, r_symndx) == section) + { + bfd_vma sym_off = get_elf_r_symndx_offset (abfd, r_symndx); + blocks[block_count].address = + (section->vma + sym_off + rel->r_addend + + bfd_get_32 (abfd, table_data + rel->r_offset)); + blocks[block_count].size = + bfd_get_32 (abfd, table_data + rel->r_offset + 4); + block_count++; + } + } + } + else + { + /* No relocations. Presumably the file has been relocated + and the addresses are already in the table. */ + bfd_vma off; + + for (off = 0; off < table_size; off += sizeof (property_table_entry)) + { + bfd_vma address = bfd_get_32 (abfd, table_data + off); + + if (address >= section->vma + && address < ( section->vma + section->_raw_size)) + { + blocks[block_count].address = address; + blocks[block_count].size = + bfd_get_32 (abfd, table_data + off + 4); + block_count++; + } + } + } + + release_contents (table_section, table_data); + release_internal_relocs (table_section, internal_relocs); + + if (block_count > 0) + { + /* Now sort them into address order for easy reference. */ + qsort (blocks, block_count, sizeof (property_table_entry), + property_table_compare); + } + + *table_p = blocks; + return block_count; +} + + +static bfd_boolean +elf_xtensa_in_literal_pool (lit_table, lit_table_size, addr) + property_table_entry *lit_table; + int lit_table_size; + bfd_vma addr; +{ + property_table_entry entry; + + if (lit_table_size == 0) + return FALSE; + + entry.address = addr; + entry.size = 1; + + if (bsearch (&entry, lit_table, lit_table_size, + sizeof (property_table_entry), property_table_compare)) + return TRUE; + + return FALSE; +} + + +/* Look through the relocs for a section during the first phase, and + calculate needed space in the dynamic reloc sections. */ + +static bfd_boolean +elf_xtensa_check_relocs (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + const Elf_Internal_Rela *relocs; +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + property_table_entry *lit_table; + int ltblsize; + + if (info->relocateable) + return TRUE; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + + ltblsize = xtensa_read_table_entries (abfd, sec, &lit_table, + XTENSA_LIT_SEC_NAME); + if (ltblsize < 0) + return FALSE; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + unsigned int r_type; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + + r_symndx = ELF32_R_SYM (rel->r_info); + r_type = ELF32_R_TYPE (rel->r_info); + + if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) + { + (*_bfd_error_handler) (_("%s: bad symbol index: %d"), + bfd_archive_filename (abfd), + r_symndx); + return FALSE; + } + + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + + switch (r_type) + { + case R_XTENSA_32: + if (h == NULL) + goto local_literal; + + if ((sec->flags & SEC_ALLOC) != 0) + { + if ((sec->flags & SEC_READONLY) != 0 + && !elf_xtensa_in_literal_pool (lit_table, ltblsize, + sec->vma + rel->r_offset)) + h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF; + + if (h->got.refcount <= 0) + h->got.refcount = 1; + else + h->got.refcount += 1; + } + break; + + case R_XTENSA_PLT: + /* If this relocation is against a local symbol, then it's + exactly the same as a normal local GOT entry. */ + if (h == NULL) + goto local_literal; + + if ((sec->flags & SEC_ALLOC) != 0) + { + if ((sec->flags & SEC_READONLY) != 0 + && !elf_xtensa_in_literal_pool (lit_table, ltblsize, + sec->vma + rel->r_offset)) + h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF; + + if (h->plt.refcount <= 0) + { + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + h->plt.refcount = 1; + } + else + h->plt.refcount += 1; + + /* Keep track of the total PLT relocation count even if we + don't yet know whether the dynamic sections will be + created. */ + plt_reloc_count += 1; + + if (elf_hash_table (info)->dynamic_sections_created) + { + if (!add_extra_plt_sections (elf_hash_table (info)->dynobj, + plt_reloc_count)) + return FALSE; + } + } + break; + + local_literal: + if ((sec->flags & SEC_ALLOC) != 0) + { + bfd_signed_vma *local_got_refcounts; + + /* This is a global offset table entry for a local symbol. */ + local_got_refcounts = elf_local_got_refcounts (abfd); + if (local_got_refcounts == NULL) + { + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= sizeof (bfd_signed_vma); + local_got_refcounts = ((bfd_signed_vma *) + bfd_zalloc (abfd, size)); + if (local_got_refcounts == NULL) + return FALSE; + elf_local_got_refcounts (abfd) = local_got_refcounts; + } + local_got_refcounts[r_symndx] += 1; + + /* If the relocation is not inside the GOT, the DF_TEXTREL + flag needs to be set. */ + if (info->shared + && (sec->flags & SEC_READONLY) != 0 + && !elf_xtensa_in_literal_pool (lit_table, ltblsize, + sec->vma + rel->r_offset)) + info->flags |= DF_TEXTREL; + } + break; + + case R_XTENSA_OP0: + case R_XTENSA_OP1: + case R_XTENSA_OP2: + case R_XTENSA_ASM_EXPAND: + case R_XTENSA_ASM_SIMPLIFY: + /* Nothing to do for these. */ + break; + + case R_XTENSA_GNU_VTINHERIT: + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) + return FALSE; + break; + + case R_XTENSA_GNU_VTENTRY: + /* This relocation describes which C++ vtable entries are actually + used. Record for later use during GC. */ + if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return FALSE; + break; + + default: + break; + } + } + + free (lit_table); + return TRUE; +} + + +static void +elf_xtensa_hide_symbol (info, h, force_local) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; + bfd_boolean force_local; +{ + /* For a shared link, move the plt refcount to the got refcount to leave + space for RELATIVE relocs. */ + elf_xtensa_make_sym_local (info, h); + + _bfd_elf_link_hash_hide_symbol (info, h, force_local); +} + + +static void +elf_xtensa_copy_indirect_symbol (bed, dir, ind) + struct elf_backend_data *bed; + struct elf_link_hash_entry *dir, *ind; +{ + _bfd_elf_link_hash_copy_indirect (bed, dir, ind); + + /* The standard function doesn't copy the NEEDS_PLT flag. */ + dir->elf_link_hash_flags |= + (ind->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT); +} + + +/* Return the section that should be marked against GC for a given + relocation. */ + +static asection * +elf_xtensa_gc_mark_hook (sec, info, rel, h, sym) + asection *sec; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + Elf_Internal_Rela *rel; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + if (h != NULL) + { + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_XTENSA_GNU_VTINHERIT: + case R_XTENSA_GNU_VTENTRY: + break; + + default: + switch (h->root.type) + { + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + return h->root.u.def.section; + + case bfd_link_hash_common: + return h->root.u.c.p->section; + + default: + break; + } + } + } + else + return bfd_section_from_elf_index (sec->owner, sym->st_shndx); + + return NULL; +} + +/* Update the GOT & PLT entry reference counts + for the section being removed. */ + +static bfd_boolean +elf_xtensa_gc_sweep_hook (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + asection *sec; + const Elf_Internal_Rela *relocs; +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel, *relend; + + if ((sec->flags & SEC_ALLOC) == 0) + return TRUE; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); + + relend = relocs + sec->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + unsigned long r_symndx; + unsigned int r_type; + struct elf_link_hash_entry *h = NULL; + + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + r_type = ELF32_R_TYPE (rel->r_info); + switch (r_type) + { + case R_XTENSA_32: + if (h == NULL) + goto local_literal; + if (h->got.refcount > 0) + h->got.refcount--; + break; + + case R_XTENSA_PLT: + if (h == NULL) + goto local_literal; + if (h->plt.refcount > 0) + h->plt.refcount--; + break; + + local_literal: + if (local_got_refcounts[r_symndx] > 0) + local_got_refcounts[r_symndx] -= 1; + break; + + default: + break; + } + } + + return TRUE; +} + + +/* Create all the dynamic sections. */ + +static bfd_boolean +elf_xtensa_create_dynamic_sections (dynobj, info) + bfd *dynobj; + struct bfd_link_info *info; +{ + flagword flags; + asection *s; + + /* First do all the standard stuff. */ + if (! _bfd_elf_create_dynamic_sections (dynobj, info)) + return FALSE; + + /* Create any extra PLT sections in case check_relocs has already + been called on all the non-dynamic input files. */ + if (!add_extra_plt_sections (dynobj, plt_reloc_count)) + return FALSE; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED | SEC_READONLY); + + /* Mark the ".got.plt" section READONLY. */ + s = bfd_get_section_by_name (dynobj, ".got.plt"); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, flags)) + return FALSE; + + /* Create ".rela.got". */ + s = bfd_make_section (dynobj, ".rela.got"); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, flags) + || ! bfd_set_section_alignment (dynobj, s, 2)) + return FALSE; + + /* Create ".xt.lit.plt" (literal table for ".got.plt*"). */ + s = bfd_make_section (dynobj, ".xt.lit.plt"); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, flags) + || ! bfd_set_section_alignment (dynobj, s, 2)) + return FALSE; + + return TRUE; +} + + +static bfd_boolean +add_extra_plt_sections (dynobj, count) + bfd *dynobj; + int count; +{ + int chunk; + + /* Iterate over all chunks except 0 which uses the standard ".plt" and + ".got.plt" sections. */ + for (chunk = count / PLT_ENTRIES_PER_CHUNK; chunk > 0; chunk--) + { + char *sname; + flagword flags; + asection *s; + + /* Stop when we find a section has already been created. */ + if (elf_xtensa_get_plt_section (dynobj, chunk)) + break; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED | SEC_READONLY); + + sname = (char *) bfd_malloc (10); + sprintf (sname, ".plt.%u", chunk); + s = bfd_make_section (dynobj, sname); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, flags | SEC_CODE) + || ! bfd_set_section_alignment (dynobj, s, 2)) + return FALSE; + + sname = (char *) bfd_malloc (14); + sprintf (sname, ".got.plt.%u", chunk); + s = bfd_make_section (dynobj, sname); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, flags) + || ! bfd_set_section_alignment (dynobj, s, 2)) + return FALSE; + } + + return TRUE; +} + + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static bfd_boolean +elf_xtensa_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info ATTRIBUTE_UNUSED; + struct elf_link_hash_entry *h; +{ + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->weakdef != NULL) + { + BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined + || h->weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->weakdef->root.u.def.section; + h->root.u.def.value = h->weakdef->root.u.def.value; + return TRUE; + } + + /* This is a reference to a symbol defined by a dynamic object. The + reference must go through the GOT, so there's no need for COPY relocs, + .dynbss, etc. */ + + return TRUE; +} + + +static void +elf_xtensa_make_sym_local (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + if (info->shared) + { + if (h->plt.refcount > 0) + { + /* Will use RELATIVE relocs instead of JMP_SLOT relocs. */ + if (h->got.refcount < 0) + h->got.refcount = 0; + h->got.refcount += h->plt.refcount; + h->plt.refcount = 0; + } + } + else + { + /* Don't need any dynamic relocations at all. */ + h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF; + h->plt.refcount = 0; + h->got.refcount = 0; + } +} + + +static bfd_boolean +elf_xtensa_fix_refcounts (h, arg) + struct elf_link_hash_entry *h; + PTR arg; +{ + struct bfd_link_info *info = (struct bfd_link_info *) arg; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + if (! xtensa_elf_dynamic_symbol_p (info, h)) + elf_xtensa_make_sym_local (info, h); + + /* If the symbol has a relocation outside the GOT, set the + DF_TEXTREL flag. */ + if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) != 0) + info->flags |= DF_TEXTREL; + + return TRUE; +} + + +static bfd_boolean +elf_xtensa_allocate_plt_size (h, arg) + struct elf_link_hash_entry *h; + PTR arg; +{ + asection *srelplt = (asection *) arg; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + if (h->plt.refcount > 0) + srelplt->_raw_size += (h->plt.refcount * sizeof (Elf32_External_Rela)); + + return TRUE; +} + + +static bfd_boolean +elf_xtensa_allocate_got_size (h, arg) + struct elf_link_hash_entry *h; + PTR arg; +{ + asection *srelgot = (asection *) arg; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + if (h->got.refcount > 0) + srelgot->_raw_size += (h->got.refcount * sizeof (Elf32_External_Rela)); + + return TRUE; +} + + +static void +elf_xtensa_allocate_local_got_size (info, srelgot) + struct bfd_link_info *info; + asection *srelgot; +{ + bfd *i; + + for (i = info->input_bfds; i; i = i->link_next) + { + bfd_signed_vma *local_got_refcounts; + bfd_size_type j, cnt; + Elf_Internal_Shdr *symtab_hdr; + + local_got_refcounts = elf_local_got_refcounts (i); + if (!local_got_refcounts) + continue; + + symtab_hdr = &elf_tdata (i)->symtab_hdr; + cnt = symtab_hdr->sh_info; + + for (j = 0; j < cnt; ++j) + { + if (local_got_refcounts[j] > 0) + srelgot->_raw_size += (local_got_refcounts[j] + * sizeof (Elf32_External_Rela)); + } + } +} + + +/* Set the sizes of the dynamic sections. */ + +static bfd_boolean +elf_xtensa_size_dynamic_sections (output_bfd, info) + bfd *output_bfd ATTRIBUTE_UNUSED; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *s, *srelplt, *splt, *sgotplt, *srelgot, *spltlittbl; + bfd_boolean relplt, relgot; + int plt_entries, plt_chunks, chunk; + + plt_entries = 0; + plt_chunks = 0; + srelgot = 0; + + dynobj = elf_hash_table (info)->dynobj; + if (dynobj == NULL) + abort (); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (! info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + if (s == NULL) + abort (); + s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + + /* Allocate room for one word in ".got". */ + s = bfd_get_section_by_name (dynobj, ".got"); + if (s == NULL) + abort (); + s->_raw_size = 4; + + /* Adjust refcounts for symbols that we now know are not "dynamic". */ + elf_link_hash_traverse (elf_hash_table (info), + elf_xtensa_fix_refcounts, + (PTR) info); + + /* Allocate space in ".rela.got" for literals that reference + global symbols. */ + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + if (srelgot == NULL) + abort (); + elf_link_hash_traverse (elf_hash_table (info), + elf_xtensa_allocate_got_size, + (PTR) srelgot); + + /* If we are generating a shared object, we also need space in + ".rela.got" for R_XTENSA_RELATIVE relocs for literals that + reference local symbols. */ + if (info->shared) + elf_xtensa_allocate_local_got_size (info, srelgot); + + /* Allocate space in ".rela.plt" for literals that have PLT entries. */ + srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + if (srelplt == NULL) + abort (); + elf_link_hash_traverse (elf_hash_table (info), + elf_xtensa_allocate_plt_size, + (PTR) srelplt); + + /* Allocate space in ".plt" to match the size of ".rela.plt". For + each PLT entry, we need the PLT code plus a 4-byte literal. + For each chunk of ".plt", we also need two more 4-byte + literals, two corresponding entries in ".rela.got", and an + 8-byte entry in ".xt.lit.plt". */ + spltlittbl = bfd_get_section_by_name (dynobj, ".xt.lit.plt"); + if (spltlittbl == NULL) + abort (); + + plt_entries = srelplt->_raw_size / sizeof (Elf32_External_Rela); + plt_chunks = + (plt_entries + PLT_ENTRIES_PER_CHUNK - 1) / PLT_ENTRIES_PER_CHUNK; + + /* Iterate over all the PLT chunks, including any extra sections + created earlier because the initial count of PLT relocations + was an overestimate. */ + for (chunk = 0; + (splt = elf_xtensa_get_plt_section (dynobj, chunk)) != NULL; + chunk++) + { + int chunk_entries; + + sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); + if (sgotplt == NULL) + abort (); + + if (chunk < plt_chunks - 1) + chunk_entries = PLT_ENTRIES_PER_CHUNK; + else if (chunk == plt_chunks - 1) + chunk_entries = plt_entries - (chunk * PLT_ENTRIES_PER_CHUNK); + else + chunk_entries = 0; + + if (chunk_entries != 0) + { + sgotplt->_raw_size = 4 * (chunk_entries + 2); + splt->_raw_size = PLT_ENTRY_SIZE * chunk_entries; + srelgot->_raw_size += 2 * sizeof (Elf32_External_Rela); + spltlittbl->_raw_size += 8; + } + else + { + sgotplt->_raw_size = 0; + splt->_raw_size = 0; + } + } + } + + /* Allocate memory for dynamic sections. */ + relplt = FALSE; + relgot = FALSE; + for (s = dynobj->sections; s != NULL; s = s->next) + { + const char *name; + bfd_boolean strip; + + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + /* It's OK to base decisions on the section name, because none + of the dynobj section names depend upon the input files. */ + name = bfd_get_section_name (dynobj, s); + + strip = FALSE; + + if (strncmp (name, ".rela", 5) == 0) + { + if (strcmp (name, ".rela.plt") == 0) + relplt = TRUE; + else if (strcmp (name, ".rela.got") == 0) + relgot = TRUE; + + /* We use the reloc_count field as a counter if we need + to copy relocs into the output file. */ + s->reloc_count = 0; + } + else if (strncmp (name, ".plt.", 5) == 0 + || strncmp (name, ".got.plt.", 9) == 0) + { + if (s->_raw_size == 0) + { + /* If we don't need this section, strip it from the output + file. We must create the ".plt*" and ".got.plt*" + sections in create_dynamic_sections and/or check_relocs + based on a conservative estimate of the PLT relocation + count, because the sections must be created before the + linker maps input sections to output sections. The + linker does that before size_dynamic_sections, where we + compute the exact size of the PLT, so there may be more + of these sections than are actually needed. */ + strip = TRUE; + } + } + else if (strcmp (name, ".got") != 0 + && strcmp (name, ".plt") != 0 + && strcmp (name, ".got.plt") != 0 + && strcmp (name, ".xt.lit.plt") != 0) + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + if (strip) + _bfd_strip_section_from_output (info, s); + else + { + /* Allocate memory for the section contents. */ + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + return FALSE; + } + } + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Add the special XTENSA_RTLD relocations now. The offsets won't be + known until finish_dynamic_sections, but we need to get the relocs + in place before they are sorted. */ + if (srelgot == NULL) + abort (); + for (chunk = 0; chunk < plt_chunks; chunk++) + { + Elf_Internal_Rela irela; + bfd_byte *loc; + + irela.r_offset = 0; + irela.r_info = ELF32_R_INFO (0, R_XTENSA_RTLD); + irela.r_addend = 0; + + loc = (srelgot->contents + + srelgot->reloc_count * sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (output_bfd, &irela, loc); + bfd_elf32_swap_reloca_out (output_bfd, &irela, + loc + sizeof (Elf32_External_Rela)); + srelgot->reloc_count += 2; + } + + /* Add some entries to the .dynamic section. We fill in the + values later, in elf_xtensa_finish_dynamic_sections, but we + must add the entries now so that we get the correct size for + the .dynamic section. The DT_DEBUG entry is filled in by the + dynamic linker and used by the debugger. */ +#define add_dynamic_entry(TAG, VAL) \ + bfd_elf32_add_dynamic_entry (info, (bfd_vma) (TAG), (bfd_vma) (VAL)) + + if (! info->shared) + { + if (!add_dynamic_entry (DT_DEBUG, 0)) + return FALSE; + } + + if (relplt) + { + if (!add_dynamic_entry (DT_PLTGOT, 0) + || !add_dynamic_entry (DT_PLTRELSZ, 0) + || !add_dynamic_entry (DT_PLTREL, DT_RELA) + || !add_dynamic_entry (DT_JMPREL, 0)) + return FALSE; + } + + if (relgot) + { + if (!add_dynamic_entry (DT_RELA, 0) + || !add_dynamic_entry (DT_RELASZ, 0) + || !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela))) + return FALSE; + } + + if ((info->flags & DF_TEXTREL) != 0) + { + if (!add_dynamic_entry (DT_TEXTREL, 0)) + return FALSE; + } + + if (!add_dynamic_entry (DT_XTENSA_GOT_LOC_OFF, 0) + || !add_dynamic_entry (DT_XTENSA_GOT_LOC_SZ, 0)) + return FALSE; + } +#undef add_dynamic_entry + + return TRUE; +} + + +/* Remove any PT_LOAD segments with no allocated sections. Prior to + binutils 2.13, this function used to remove the non-SEC_ALLOC + sections from PT_LOAD segments, but that task has now been moved + into elf.c. We still need this function to remove any empty + segments that result, but there's nothing Xtensa-specific about + this and it probably ought to be moved into elf.c as well. */ + +static bfd_boolean +elf_xtensa_modify_segment_map (abfd) + bfd *abfd; +{ + struct elf_segment_map **m_p; + + m_p = &elf_tdata (abfd)->segment_map; + while (*m_p != NULL) + { + if ((*m_p)->p_type == PT_LOAD && (*m_p)->count == 0) + *m_p = (*m_p)->next; + else + m_p = &(*m_p)->next; + } + return TRUE; +} + + +/* Perform the specified relocation. The instruction at (contents + address) + is modified to set one operand to represent the value in "relocation". The + operand position is determined by the relocation type recorded in the + howto. */ + +#define CALL_SEGMENT_BITS (30) +#define CALL_SEGMENT_SIZE (1<type) + { + case R_XTENSA_NONE: + return bfd_reloc_ok; + + case R_XTENSA_ASM_EXPAND: + if (!is_weak_undef) + { + /* Check for windowed CALL across a 1GB boundary. */ + xtensa_opcode opcode = + get_expanded_call_opcode (contents + address, + input_section->_raw_size - address); + if (is_windowed_call_opcode (opcode)) + { + self_address = (input_section->output_section->vma + + input_section->output_offset + + address); + if ((self_address >> CALL_SEGMENT_BITS) != + (relocation >> CALL_SEGMENT_BITS)) + { + *error_message = "windowed longcall crosses 1GB boundary; " + "return may fail"; + return bfd_reloc_dangerous; + } + } + } + return bfd_reloc_ok; + + case R_XTENSA_ASM_SIMPLIFY: + { + /* Convert the L32R/CALLX to CALL. */ + bfd_reloc_status_type retval = + elf_xtensa_do_asm_simplify (contents, address, + input_section->_raw_size); + if (retval != bfd_reloc_ok) + return retval; + + /* The CALL needs to be relocated. Continue below for that part. */ + address += 3; + howto = &elf_howto_table[(unsigned) R_XTENSA_OP0 ]; + } + break; + + case R_XTENSA_32: + case R_XTENSA_PLT: + { + bfd_vma x; + x = bfd_get_32 (abfd, contents + address); + x = x + relocation; + bfd_put_32 (abfd, x, contents + address); + } + return bfd_reloc_ok; + } + + /* Read the instruction into a buffer and decode the opcode. */ + ibuff = xtensa_insnbuf_alloc (isa); + xtensa_insnbuf_from_chars (isa, ibuff, contents + address); + opcode = xtensa_decode_insn (isa, ibuff); + + /* Determine which operand is being relocated. */ + if (opcode == XTENSA_UNDEFINED) + { + *error_message = "cannot decode instruction"; + return bfd_reloc_dangerous; + } + + if (howto->type < R_XTENSA_OP0 || howto->type > R_XTENSA_OP2) + { + *error_message = "unexpected relocation"; + return bfd_reloc_dangerous; + } + + opnd = howto->type - R_XTENSA_OP0; + + /* Calculate the PC address for this instruction. */ + if (!howto->pc_relative) + { + *error_message = "expected PC-relative relocation"; + return bfd_reloc_dangerous; + } + + self_address = (input_section->output_section->vma + + input_section->output_offset + + address); + + /* Apply the relocation. */ + operand = xtensa_get_operand (isa, opcode, opnd); + newval = xtensa_operand_do_reloc (operand, relocation, self_address); + encode_result = xtensa_operand_encode (operand, &newval); + xtensa_operand_set_field (operand, ibuff, newval); + + /* Write the modified instruction back out of the buffer. */ + xtensa_insnbuf_to_chars (isa, ibuff, contents + address); + free (ibuff); + + if (encode_result != xtensa_encode_result_ok) + { + char *message = build_encoding_error_message (opcode, encode_result); + *error_message = message; + return bfd_reloc_dangerous; + } + + /* Final check for call. */ + if (is_direct_call_opcode (opcode) + && is_windowed_call_opcode (opcode)) + { + if ((self_address >> CALL_SEGMENT_BITS) != + (relocation >> CALL_SEGMENT_BITS)) + { + *error_message = "windowed call crosses 1GB boundary; " + "return may fail"; + return bfd_reloc_dangerous; + } + } + + return bfd_reloc_ok; +} + + +static char * +vsprint_msg VPARAMS ((const char *origmsg, const char *fmt, int arglen, ...)) +{ + /* To reduce the size of the memory leak, + we only use a single message buffer. */ + static bfd_size_type alloc_size = 0; + static char *message = NULL; + bfd_size_type orig_len, len = 0; + bfd_boolean is_append; + + VA_OPEN (ap, arglen); + VA_FIXEDARG (ap, const char *, origmsg); + + is_append = (origmsg == message); + + orig_len = strlen (origmsg); + len = orig_len + strlen (fmt) + arglen + 20; + if (len > alloc_size) + { + message = (char *) bfd_realloc (message, len); + alloc_size = len; + } + if (!is_append) + memcpy (message, origmsg, orig_len); + vsprintf (message + orig_len, fmt, ap); + VA_CLOSE (ap); + return message; +} + + +static char * +build_encoding_error_message (opcode, encode_result) + xtensa_opcode opcode; + xtensa_encode_result encode_result; +{ + const char *opname = xtensa_opcode_name (xtensa_default_isa, opcode); + const char *msg = NULL; + + switch (encode_result) + { + case xtensa_encode_result_ok: + msg = "unexpected valid encoding"; + break; + case xtensa_encode_result_align: + msg = "misaligned encoding"; + break; + case xtensa_encode_result_not_in_table: + msg = "encoding not in lookup table"; + break; + case xtensa_encode_result_too_low: + msg = "encoding out of range: too low"; + break; + case xtensa_encode_result_too_high: + msg = "encoding out of range: too high"; + break; + case xtensa_encode_result_not_ok: + default: + msg = "could not encode"; + break; + } + + if (is_direct_call_opcode (opcode) + && (encode_result == xtensa_encode_result_too_low + || encode_result == xtensa_encode_result_too_high)) + + msg = "direct call out of range"; + + else if (opcode == get_l32r_opcode ()) + { + /* L32Rs have the strange interaction with encoding in that they + have an unsigned immediate field, so libisa returns "too high" + when the absolute value is out of range and never returns "too + low", but I leave the "too low" message in case anything + changes. */ + if (encode_result == xtensa_encode_result_too_low) + msg = "literal out of range"; + else if (encode_result == xtensa_encode_result_too_high) + msg = "literal placed after use"; + } + + return vsprint_msg (opname, ": %s", strlen (msg) + 2, msg); +} + + +/* This function is registered as the "special_function" in the + Xtensa howto for handling simplify operations. + bfd_perform_relocation / bfd_install_relocation use it to + perform (install) the specified relocation. Since this replaces the code + in bfd_perform_relocation, it is basically an Xtensa-specific, + stripped-down version of bfd_perform_relocation. */ + +static bfd_reloc_status_type +bfd_elf_xtensa_reloc (abfd, reloc_entry, symbol, data, input_section, + output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma relocation; + bfd_reloc_status_type flag; + bfd_size_type octets = reloc_entry->address * bfd_octets_per_byte (abfd); + bfd_vma output_base = 0; + reloc_howto_type *howto = reloc_entry->howto; + asection *reloc_target_output_section; + bfd_boolean is_weak_undef; + + /* ELF relocs are against symbols. If we are producing relocateable + output, and the reloc is against an external symbol, the resulting + reloc will also be against the same symbol. In such a case, we + don't want to change anything about the way the reloc is handled, + since it will all be done at final link time. This test is similar + to what bfd_elf_generic_reloc does except that it lets relocs with + howto->partial_inplace go through even if the addend is non-zero. + (The real problem is that partial_inplace is set for XTENSA_32 + relocs to begin with, but that's a long story and there's little we + can do about it now....) */ + + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + /* Is the address of the relocation really within the section? */ + if (reloc_entry->address > (input_section->_cooked_size + / bfd_octets_per_byte (abfd))) + return bfd_reloc_outofrange; + + /* Work out which section the relocation is targetted at and the + initial relocation command value. */ + + /* Get symbol value. (Common symbols are special.) */ + if (bfd_is_com_section (symbol->section)) + relocation = 0; + else + relocation = symbol->value; + + reloc_target_output_section = symbol->section->output_section; + + /* Convert input-section-relative symbol value to absolute. */ + if ((output_bfd && !howto->partial_inplace) + || reloc_target_output_section == NULL) + output_base = 0; + else + output_base = reloc_target_output_section->vma; + + relocation += output_base + symbol->section->output_offset; + + /* Add in supplied addend. */ + relocation += reloc_entry->addend; + + /* Here the variable relocation holds the final address of the + symbol we are relocating against, plus any addend. */ + if (output_bfd) + { + if (!howto->partial_inplace) + { + /* This is a partial relocation, and we want to apply the relocation + to the reloc entry rather than the raw data. Everything except + relocations against section symbols has already been handled + above. */ + + BFD_ASSERT (symbol->flags & BSF_SECTION_SYM); + reloc_entry->addend = relocation; + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + else + { + reloc_entry->address += input_section->output_offset; + reloc_entry->addend = 0; + } + } + + is_weak_undef = (bfd_is_und_section (symbol->section) + && (symbol->flags & BSF_WEAK) != 0); + flag = elf_xtensa_do_reloc (howto, abfd, input_section, relocation, + (bfd_byte *) data, (bfd_vma) octets, + is_weak_undef, error_message); + + if (flag == bfd_reloc_dangerous) + { + /* Add the symbol name to the error message. */ + if (! *error_message) + *error_message = ""; + *error_message = vsprint_msg (*error_message, ": (%s + 0x%lx)", + strlen (symbol->name) + 17, + symbol->name, reloc_entry->addend); + } + + return flag; +} + + +/* Set up an entry in the procedure linkage table. */ + +static bfd_vma +elf_xtensa_create_plt_entry (dynobj, output_bfd, reloc_index) + bfd *dynobj; + bfd *output_bfd; + unsigned reloc_index; +{ + asection *splt, *sgotplt; + bfd_vma plt_base, got_base; + bfd_vma code_offset, lit_offset; + int chunk; + + chunk = reloc_index / PLT_ENTRIES_PER_CHUNK; + splt = elf_xtensa_get_plt_section (dynobj, chunk); + sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); + BFD_ASSERT (splt != NULL && sgotplt != NULL); + + plt_base = splt->output_section->vma + splt->output_offset; + got_base = sgotplt->output_section->vma + sgotplt->output_offset; + + lit_offset = 8 + (reloc_index % PLT_ENTRIES_PER_CHUNK) * 4; + code_offset = (reloc_index % PLT_ENTRIES_PER_CHUNK) * PLT_ENTRY_SIZE; + + /* Fill in the literal entry. This is the offset of the dynamic + relocation entry. */ + bfd_put_32 (output_bfd, reloc_index * sizeof (Elf32_External_Rela), + sgotplt->contents + lit_offset); + + /* Fill in the entry in the procedure linkage table. */ + memcpy (splt->contents + code_offset, + (bfd_big_endian (output_bfd) + ? elf_xtensa_be_plt_entry + : elf_xtensa_le_plt_entry), + PLT_ENTRY_SIZE); + bfd_put_16 (output_bfd, l32r_offset (got_base + 0, + plt_base + code_offset + 3), + splt->contents + code_offset + 4); + bfd_put_16 (output_bfd, l32r_offset (got_base + 4, + plt_base + code_offset + 6), + splt->contents + code_offset + 7); + bfd_put_16 (output_bfd, l32r_offset (got_base + lit_offset, + plt_base + code_offset + 9), + splt->contents + code_offset + 10); + + return plt_base + code_offset; +} + + +static bfd_boolean +xtensa_elf_dynamic_symbol_p (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + if (h == NULL) + return FALSE; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + if (h->dynindx == -1) + return FALSE; + + if (h->root.type == bfd_link_hash_undefweak + || h->root.type == bfd_link_hash_defweak) + return TRUE; + + switch (ELF_ST_VISIBILITY (h->other)) + { + case STV_DEFAULT: + break; + case STV_HIDDEN: + case STV_INTERNAL: + return FALSE; + case STV_PROTECTED: + if (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) + return FALSE; + break; + } + + if ((info->shared && !info->symbolic) + || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + return TRUE; + + return FALSE; +} + + +/* Relocate an Xtensa ELF section. This is invoked by the linker for + both relocateable and final links. */ + +static bfd_boolean +elf_xtensa_relocate_section (output_bfd, info, input_bfd, + input_section, contents, relocs, + local_syms, local_sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + struct elf_link_hash_entry **sym_hashes; + asection *srelgot, *srelplt; + bfd *dynobj; + char *error_message = NULL; + + if (xtensa_default_isa == NULL) + xtensa_isa_init (); + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + + srelgot = NULL; + srelplt = NULL; + if (dynobj != NULL) + { + srelgot = bfd_get_section_by_name (dynobj, ".rela.got");; + srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + } + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + int r_type; + reloc_howto_type *howto; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sec; + bfd_vma relocation; + bfd_reloc_status_type r; + bfd_boolean is_weak_undef; + bfd_boolean unresolved_reloc; + + r_type = ELF32_R_TYPE (rel->r_info); + if (r_type == (int) R_XTENSA_GNU_VTINHERIT + || r_type == (int) R_XTENSA_GNU_VTENTRY) + continue; + + if (r_type < 0 || r_type >= (int) R_XTENSA_max) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + howto = &elf_howto_table[r_type]; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (info->relocateable) + { + /* This is a relocateable link. + 1) If the reloc is against a section symbol, adjust + according to the output section. + 2) If there is a new target for this relocation, + the new target will be in the same output section. + We adjust the relocation by the output section + difference. */ + + if (relaxing_section) + { + /* Check if this references a section in another input file. */ + do_fix_for_relocateable_link (rel, input_bfd, input_section); + r_type = ELF32_R_TYPE (rel->r_info); + } + + if (r_type == R_XTENSA_ASM_SIMPLIFY) + { + /* Convert ASM_SIMPLIFY into the simpler relocation + so that they never escape a relaxing link. */ + contract_asm_expansion (contents, input_section->_raw_size, rel); + r_type = ELF32_R_TYPE (rel->r_info); + } + + /* This is a relocateable link, so we don't have to change + anything unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sec = local_sections[r_symndx]; + rel->r_addend += sec->output_offset + sym->st_value; + } + } + + /* If there is an addend with a partial_inplace howto, + then move the addend to the contents. This is a hack + to work around problems with DWARF in relocateable links + with some previous version of BFD. Now we can't easily get + rid of the hack without breaking backward compatibility.... */ + if (rel->r_addend) + { + howto = &elf_howto_table[r_type]; + if (howto->partial_inplace) + { + r = elf_xtensa_do_reloc (howto, input_bfd, input_section, + rel->r_addend, contents, + rel->r_offset, FALSE, + &error_message); + if (r != bfd_reloc_ok) + { + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } + rel->r_addend = 0; + } + } + + /* Done with work for relocateable link; continue with next reloc. */ + continue; + } + + /* This is a final link. */ + + h = NULL; + sym = NULL; + sec = NULL; + is_weak_undef = FALSE; + unresolved_reloc = FALSE; + + if (howto->partial_inplace) + { + /* Because R_XTENSA_32 was made partial_inplace to fix some + problems with DWARF info in partial links, there may be + an addend stored in the contents. Take it out of there + and move it back into the addend field of the reloc. */ + rel->r_addend += bfd_get_32 (input_bfd, contents + rel->r_offset); + bfd_put_32 (input_bfd, 0, contents + rel->r_offset); + } + + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel); + } + else + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + relocation = 0; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sec = h->root.u.def.section; + + if (sec->output_section == NULL) + /* Set a flag that will be cleared later if we find a + relocation value for this symbol. output_section + is typically NULL for symbols satisfied by a shared + library. */ + unresolved_reloc = TRUE; + else + relocation = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + else if (h->root.type == bfd_link_hash_undefweak) + is_weak_undef = TRUE; + else if (info->shared + && !info->no_undefined + && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) + ; + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset, + (!info->shared || info->no_undefined + || ELF_ST_VISIBILITY (h->other))))) + return FALSE; + + /* To avoid any more warning messages, like "call out of + range", we continue immediately to the next relocation. */ + continue; + } + } + + if (relaxing_section) + { + /* Check if this references a section in another input file. */ + do_fix_for_final_link (rel, input_section, &relocation); + + /* Update some already cached values. */ + r_type = ELF32_R_TYPE (rel->r_info); + howto = &elf_howto_table[r_type]; + } + + /* Sanity check the address. */ + if (rel->r_offset >= input_section->_raw_size + && ELF32_R_TYPE (rel->r_info) != R_XTENSA_NONE) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + /* Generate dynamic relocations. */ + if (elf_hash_table (info)->dynamic_sections_created) + { + bfd_boolean dynamic_symbol = xtensa_elf_dynamic_symbol_p (info, h); + + if (dynamic_symbol && (r_type == R_XTENSA_OP0 + || r_type == R_XTENSA_OP1 + || r_type == R_XTENSA_OP2)) + { + /* This is an error. The symbol's real value won't be known + until runtime and it's likely to be out of range anyway. */ + const char *name = h->root.root.string; + error_message = vsprint_msg ("invalid relocation for dynamic " + "symbol", ": %s", + strlen (name) + 2, name); + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } + else if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT) + && (input_section->flags & SEC_ALLOC) != 0 + && (dynamic_symbol || info->shared)) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + asection *srel; + + if (dynamic_symbol && r_type == R_XTENSA_PLT) + srel = srelplt; + else + srel = srelgot; + + BFD_ASSERT (srel != NULL); + + outrel.r_offset = + _bfd_elf_section_offset (output_bfd, info, + input_section, rel->r_offset); + + if ((outrel.r_offset | 1) == (bfd_vma) -1) + memset (&outrel, 0, sizeof outrel); + else + { + outrel.r_offset = (input_section->output_section->vma + + input_section->output_offset); + + if (dynamic_symbol) + { + outrel.r_addend = rel->r_addend; + rel->r_addend = 0; + + if (r_type == R_XTENSA_32) + { + outrel.r_info = + ELF32_R_INFO (h->dynindx, R_XTENSA_GLOB_DAT); + relocation = 0; + } + else /* r_type == R_XTENSA_PLT */ + { + outrel.r_info = + ELF32_R_INFO (h->dynindx, R_XTENSA_JMP_SLOT); + + /* Create the PLT entry and set the initial + contents of the literal entry to the address of + the PLT entry. */ + relocation = + elf_xtensa_create_plt_entry (dynobj, output_bfd, + srel->reloc_count); + } + unresolved_reloc = FALSE; + } + else + { + /* Generate a RELATIVE relocation. */ + outrel.r_info = ELF32_R_INFO (0, R_XTENSA_RELATIVE); + outrel.r_addend = 0; + } + } + + loc = (srel->contents + + srel->reloc_count++ * sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count + <= srel->_cooked_size); + } + } + + /* Dynamic relocs are not propagated for SEC_DEBUGGING sections + because such sections are not SEC_ALLOC and thus ld.so will + not process them. */ + if (unresolved_reloc + && !((input_section->flags & SEC_DEBUGGING) != 0 + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0)) + (*_bfd_error_handler) + (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"), + bfd_archive_filename (input_bfd), + bfd_get_section_name (input_bfd, input_section), + (long) rel->r_offset, + h->root.root.string); + + /* There's no point in calling bfd_perform_relocation here. + Just go directly to our "special function". */ + r = elf_xtensa_do_reloc (howto, input_bfd, input_section, + relocation + rel->r_addend, + contents, rel->r_offset, is_weak_undef, + &error_message); + + if (r != bfd_reloc_ok) + { + const char *name; + + BFD_ASSERT (r == bfd_reloc_dangerous); + BFD_ASSERT (error_message != (char *) NULL); + + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section + (input_bfd, symtab_hdr->sh_link, sym->st_name); + if (name && *name == '\0') + name = bfd_section_name (input_bfd, sec); + } + if (name) + error_message = vsprint_msg (error_message, ": %s", + strlen (name), name); + if (!((*info->callbacks->reloc_dangerous) + (info, error_message, input_bfd, input_section, + rel->r_offset))) + return FALSE; + } + } + + return TRUE; +} + + +/* Finish up dynamic symbol handling. There's not much to do here since + the PLT and GOT entries are all set up by relocate_section. */ + +static bfd_boolean +elf_xtensa_finish_dynamic_symbol (output_bfd, info, h, sym) + bfd *output_bfd ATTRIBUTE_UNUSED; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0 + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + /* Mark the symbol as undefined, rather than as defined in + the .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; + } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + + return TRUE; +} + + +/* Combine adjacent literal table entries in the output. Adjacent + entries within each input section may have been removed during + relaxation, but we repeat the process here, even though it's too late + to shrink the output section, because it's important to minimize the + number of literal table entries to reduce the start-up work for the + runtime linker. Returns the number of remaining table entries or -1 + on error. */ + +static int +elf_xtensa_combine_prop_entries (output_bfd, secname) + bfd *output_bfd; + const char *secname; +{ + asection *sec; + bfd_byte *contents; + property_table_entry *table; + bfd_size_type section_size; + bfd_vma offset; + int n, m, num; + + sec = bfd_get_section_by_name (output_bfd, secname); + if (!sec) + return -1; + + section_size = (sec->_cooked_size != 0 ? sec->_cooked_size : sec->_raw_size); + BFD_ASSERT (section_size % 8 == 0); + num = section_size / 8; + + contents = (bfd_byte *) bfd_malloc (section_size); + table = (property_table_entry *) + bfd_malloc (num * sizeof (property_table_entry)); + if (contents == 0 || table == 0) + return -1; + + /* The ".xt.lit.plt" section has the SEC_IN_MEMORY flag set and this + propagates to the output section, where it doesn't really apply and + where it breaks the following call to bfd_get_section_contents. */ + sec->flags &= ~SEC_IN_MEMORY; + + if (! bfd_get_section_contents (output_bfd, sec, contents, 0, section_size)) + return -1; + + /* There should never be any relocations left at this point, so this + is quite a bit easier than what is done during relaxation. */ + + /* Copy the raw contents into a property table array and sort it. */ + offset = 0; + for (n = 0; n < num; n++) + { + table[n].address = bfd_get_32 (output_bfd, &contents[offset]); + table[n].size = bfd_get_32 (output_bfd, &contents[offset + 4]); + offset += 8; + } + qsort (table, num, sizeof (property_table_entry), property_table_compare); + + for (n = 0; n < num; n++) + { + bfd_boolean remove = FALSE; + + if (table[n].size == 0) + remove = TRUE; + else if (n > 0 && + (table[n-1].address + table[n-1].size == table[n].address)) + { + table[n-1].size += table[n].size; + remove = TRUE; + } + + if (remove) + { + for (m = n; m < num - 1; m++) + { + table[m].address = table[m+1].address; + table[m].size = table[m+1].size; + } + + n--; + num--; + } + } + + /* Copy the data back to the raw contents. */ + offset = 0; + for (n = 0; n < num; n++) + { + bfd_put_32 (output_bfd, table[n].address, &contents[offset]); + bfd_put_32 (output_bfd, table[n].size, &contents[offset + 4]); + offset += 8; + } + + /* Clear the removed bytes. */ + if ((bfd_size_type) (num * 8) < section_size) + { + memset (&contents[num * 8], 0, section_size - num * 8); + sec->_cooked_size = num * 8; + } + + if (! bfd_set_section_contents (output_bfd, sec, contents, 0, section_size)) + return -1; + + free (contents); + return num; +} + + +/* Finish up the dynamic sections. */ + +static bfd_boolean +elf_xtensa_finish_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *sdyn, *srelplt, *sgot; + Elf32_External_Dyn *dyncon, *dynconend; + int num_xtlit_entries; + + if (! elf_hash_table (info)->dynamic_sections_created) + return TRUE; + + dynobj = elf_hash_table (info)->dynobj; + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + BFD_ASSERT (sdyn != NULL); + + /* Set the first entry in the global offset table to the address of + the dynamic section. */ + sgot = bfd_get_section_by_name (dynobj, ".got"); + if (sgot) + { + BFD_ASSERT (sgot->_raw_size == 4); + if (sdyn == NULL) + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); + else + bfd_put_32 (output_bfd, + sdyn->output_section->vma + sdyn->output_offset, + sgot->contents); + } + + srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + if (srelplt != NULL && srelplt->_raw_size != 0) + { + asection *sgotplt, *srelgot, *spltlittbl; + int chunk, plt_chunks, plt_entries; + Elf_Internal_Rela irela; + bfd_byte *loc; + unsigned rtld_reloc; + + srelgot = bfd_get_section_by_name (dynobj, ".rela.got");; + BFD_ASSERT (srelgot != NULL); + + spltlittbl = bfd_get_section_by_name (dynobj, ".xt.lit.plt"); + BFD_ASSERT (spltlittbl != NULL); + + /* Find the first XTENSA_RTLD relocation. Presumably the rest + of them follow immediately after.... */ + for (rtld_reloc = 0; rtld_reloc < srelgot->reloc_count; rtld_reloc++) + { + loc = srelgot->contents + rtld_reloc * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_in (output_bfd, loc, &irela); + if (ELF32_R_TYPE (irela.r_info) == R_XTENSA_RTLD) + break; + } + BFD_ASSERT (rtld_reloc < srelgot->reloc_count); + + plt_entries = (srelplt->_raw_size / sizeof (Elf32_External_Rela)); + plt_chunks = + (plt_entries + PLT_ENTRIES_PER_CHUNK - 1) / PLT_ENTRIES_PER_CHUNK; + + for (chunk = 0; chunk < plt_chunks; chunk++) + { + int chunk_entries = 0; + + sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); + BFD_ASSERT (sgotplt != NULL); + + /* Emit special RTLD relocations for the first two entries in + each chunk of the .got.plt section. */ + + loc = srelgot->contents + rtld_reloc * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_in (output_bfd, loc, &irela); + BFD_ASSERT (ELF32_R_TYPE (irela.r_info) == R_XTENSA_RTLD); + irela.r_offset = (sgotplt->output_section->vma + + sgotplt->output_offset); + irela.r_addend = 1; /* tell rtld to set value to resolver function */ + bfd_elf32_swap_reloca_out (output_bfd, &irela, loc); + rtld_reloc += 1; + BFD_ASSERT (rtld_reloc <= srelgot->reloc_count); + + /* Next literal immediately follows the first. */ + loc += sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_in (output_bfd, loc, &irela); + BFD_ASSERT (ELF32_R_TYPE (irela.r_info) == R_XTENSA_RTLD); + irela.r_offset = (sgotplt->output_section->vma + + sgotplt->output_offset + 4); + /* Tell rtld to set value to object's link map. */ + irela.r_addend = 2; + bfd_elf32_swap_reloca_out (output_bfd, &irela, loc); + rtld_reloc += 1; + BFD_ASSERT (rtld_reloc <= srelgot->reloc_count); + + /* Fill in the literal table. */ + if (chunk < plt_chunks - 1) + chunk_entries = PLT_ENTRIES_PER_CHUNK; + else + chunk_entries = plt_entries - (chunk * PLT_ENTRIES_PER_CHUNK); + + BFD_ASSERT ((unsigned) (chunk + 1) * 8 <= spltlittbl->_cooked_size); + bfd_put_32 (output_bfd, + sgotplt->output_section->vma + sgotplt->output_offset, + spltlittbl->contents + (chunk * 8) + 0); + bfd_put_32 (output_bfd, + 8 + (chunk_entries * 4), + spltlittbl->contents + (chunk * 8) + 4); + } + + /* All the dynamic relocations have been emitted at this point. + Make sure the relocation sections are the correct size. */ + if (srelgot->_cooked_size != (sizeof (Elf32_External_Rela) + * srelgot->reloc_count) + || srelplt->_cooked_size != (sizeof (Elf32_External_Rela) + * srelplt->reloc_count)) + abort (); + + /* The .xt.lit.plt section has just been modified. This must + happen before the code below which combines adjacent literal + table entries, and the .xt.lit.plt contents have to be forced to + the output here. */ + if (! bfd_set_section_contents (output_bfd, + spltlittbl->output_section, + spltlittbl->contents, + spltlittbl->output_offset, + spltlittbl->_raw_size)) + return FALSE; + /* Clear SEC_HAS_CONTENTS so the contents won't be output again. */ + spltlittbl->flags &= ~SEC_HAS_CONTENTS; + } + + /* Combine adjacent literal table entries. */ + BFD_ASSERT (! info->relocateable); + num_xtlit_entries = elf_xtensa_combine_prop_entries (output_bfd, ".xt.lit"); + if (num_xtlit_entries < 0) + return FALSE; + + dyncon = (Elf32_External_Dyn *) sdyn->contents; + dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + const char *name; + asection *s; + + bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + default: + break; + + case DT_XTENSA_GOT_LOC_SZ: + s = bfd_get_section_by_name (output_bfd, ".xt.lit"); + BFD_ASSERT (s); + dyn.d_un.d_val = num_xtlit_entries; + break; + + case DT_XTENSA_GOT_LOC_OFF: + name = ".xt.lit"; + goto get_vma; + case DT_PLTGOT: + name = ".got"; + goto get_vma; + case DT_JMPREL: + name = ".rela.plt"; + get_vma: + s = bfd_get_section_by_name (output_bfd, name); + BFD_ASSERT (s); + dyn.d_un.d_ptr = s->vma; + break; + + case DT_PLTRELSZ: + s = bfd_get_section_by_name (output_bfd, ".rela.plt"); + BFD_ASSERT (s); + dyn.d_un.d_val = (s->_cooked_size ? s->_cooked_size : s->_raw_size); + break; + + case DT_RELASZ: + /* Adjust RELASZ to not include JMPREL. This matches what + glibc expects and what is done for several other ELF + targets (e.g., i386, alpha), but the "correct" behavior + seems to be unresolved. Since the linker script arranges + for .rela.plt to follow all other relocation sections, we + don't have to worry about changing the DT_RELA entry. */ + s = bfd_get_section_by_name (output_bfd, ".rela.plt"); + if (s) + { + dyn.d_un.d_val -= + (s->_cooked_size ? s->_cooked_size : s->_raw_size); + } + break; + } + + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + } + + return TRUE; +} + + +/* Functions for dealing with the e_flags field. */ + +/* Merge backend specific data from an object file to the output + object file when linking. */ + +static bfd_boolean +elf_xtensa_merge_private_bfd_data (ibfd, obfd) + bfd *ibfd; + bfd *obfd; +{ + unsigned out_mach, in_mach; + flagword out_flag, in_flag; + + /* Check if we have the same endianess. */ + if (!_bfd_generic_verify_endian_match (ibfd, obfd)) + return FALSE; + + /* Don't even pretend to support mixed-format linking. */ + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return FALSE; + + out_flag = elf_elfheader (obfd)->e_flags; + in_flag = elf_elfheader (ibfd)->e_flags; + + out_mach = out_flag & EF_XTENSA_MACH; + in_mach = in_flag & EF_XTENSA_MACH; + if (out_mach != in_mach) + { + (*_bfd_error_handler) + ("%s: incompatible machine type. Output is 0x%x. Input is 0x%x\n", + bfd_archive_filename (ibfd), out_mach, in_mach); + bfd_set_error (bfd_error_wrong_format); + return FALSE; + } + + if (! elf_flags_init (obfd)) + { + elf_flags_init (obfd) = TRUE; + elf_elfheader (obfd)->e_flags = in_flag; + + if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) + && bfd_get_arch_info (obfd)->the_default) + return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), + bfd_get_mach (ibfd)); + + return TRUE; + } + + if ((out_flag & EF_XTENSA_XT_INSN) != + (in_flag & EF_XTENSA_XT_INSN)) + elf_elfheader(obfd)->e_flags &= (~ EF_XTENSA_XT_INSN); + + if ((out_flag & EF_XTENSA_XT_LIT) != + (in_flag & EF_XTENSA_XT_LIT)) + elf_elfheader(obfd)->e_flags &= (~ EF_XTENSA_XT_LIT); + + return TRUE; +} + + +static bfd_boolean +elf_xtensa_set_private_flags (abfd, flags) + bfd *abfd; + flagword flags; +{ + BFD_ASSERT (!elf_flags_init (abfd) + || elf_elfheader (abfd)->e_flags == flags); + + elf_elfheader (abfd)->e_flags |= flags; + elf_flags_init (abfd) = TRUE; + + return TRUE; +} + + +extern flagword +elf_xtensa_get_private_bfd_flags (abfd) + bfd *abfd; +{ + return elf_elfheader (abfd)->e_flags; +} + + +static bfd_boolean +elf_xtensa_print_private_bfd_data (abfd, farg) + bfd *abfd; + PTR farg; +{ + FILE *f = (FILE *) farg; + flagword e_flags = elf_elfheader (abfd)->e_flags; + + fprintf (f, "\nXtensa header:\n"); + if ((e_flags & EF_XTENSA_MACH) == E_XTENSA_MACH) + fprintf (f, "\nMachine = Base\n"); + else + fprintf (f, "\nMachine Id = 0x%x\n", e_flags & EF_XTENSA_MACH); + + fprintf (f, "Insn tables = %s\n", + (e_flags & EF_XTENSA_XT_INSN) ? "true" : "false"); + + fprintf (f, "Literal tables = %s\n", + (e_flags & EF_XTENSA_XT_LIT) ? "true" : "false"); + + return _bfd_elf_print_private_bfd_data (abfd, farg); +} + + +/* Set the right machine number for an Xtensa ELF file. */ + +static bfd_boolean +elf_xtensa_object_p (abfd) + bfd *abfd; +{ + int mach; + unsigned long arch = elf_elfheader (abfd)->e_flags & EF_XTENSA_MACH; + + switch (arch) + { + case E_XTENSA_MACH: + mach = bfd_mach_xtensa; + break; + default: + return FALSE; + } + + (void) bfd_default_set_arch_mach (abfd, bfd_arch_xtensa, mach); + return TRUE; +} + + +/* The final processing done just before writing out an Xtensa ELF object + file. This gets the Xtensa architecture right based on the machine + number. */ + +static void +elf_xtensa_final_write_processing (abfd, linker) + bfd *abfd; + bfd_boolean linker ATTRIBUTE_UNUSED; +{ + int mach; + unsigned long val; + + switch (mach = bfd_get_mach (abfd)) + { + case bfd_mach_xtensa: + val = E_XTENSA_MACH; + break; + default: + return; + } + + elf_elfheader (abfd)->e_flags &= (~ EF_XTENSA_MACH); + elf_elfheader (abfd)->e_flags |= val; +} + + +static enum elf_reloc_type_class +elf_xtensa_reloc_type_class (rela) + const Elf_Internal_Rela *rela; +{ + switch ((int) ELF32_R_TYPE (rela->r_info)) + { + case R_XTENSA_RELATIVE: + return reloc_class_relative; + case R_XTENSA_JMP_SLOT: + return reloc_class_plt; + default: + return reloc_class_normal; + } +} + + +static bfd_boolean +elf_xtensa_discard_info_for_section (abfd, cookie, info, sec) + bfd *abfd; + struct elf_reloc_cookie *cookie; + struct bfd_link_info *info; + asection *sec; +{ + bfd_byte *contents; + bfd_vma section_size; + bfd_vma offset, actual_offset; + size_t removed_bytes = 0; + + section_size = (sec->_cooked_size ? sec->_cooked_size : sec->_raw_size); + if (section_size == 0 || section_size % 8 != 0) + return FALSE; + + if (sec->output_section + && bfd_is_abs_section (sec->output_section)) + return FALSE; + + contents = retrieve_contents (abfd, sec, info->keep_memory); + if (!contents) + return FALSE; + + cookie->rels = retrieve_internal_relocs (abfd, sec, info->keep_memory); + if (!cookie->rels) + { + release_contents (sec, contents); + return FALSE; + } + + cookie->rel = cookie->rels; + cookie->relend = cookie->rels + sec->reloc_count; + + for (offset = 0; offset < section_size; offset += 8) + { + actual_offset = offset - removed_bytes; + + /* The ...symbol_deleted_p function will skip over relocs but it + won't adjust their offsets, so do that here. */ + while (cookie->rel < cookie->relend + && cookie->rel->r_offset < offset) + { + cookie->rel->r_offset -= removed_bytes; + cookie->rel++; + } + + while (cookie->rel < cookie->relend + && cookie->rel->r_offset == offset) + { + if (_bfd_elf32_reloc_symbol_deleted_p (offset, cookie)) + { + /* Remove the table entry. (If the reloc type is NONE, then + the entry has already been merged with another and deleted + during relaxation.) */ + if (ELF32_R_TYPE (cookie->rel->r_info) != R_XTENSA_NONE) + { + /* Shift the contents up. */ + if (offset + 8 < section_size) + memmove (&contents[actual_offset], + &contents[actual_offset+8], + section_size - offset - 8); + removed_bytes += 8; + } + + /* Remove this relocation. */ + cookie->rel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + } + + /* Adjust the relocation offset for previous removals. This + should not be done before calling ...symbol_deleted_p + because it might mess up the offset comparisons there. + Make sure the offset doesn't underflow in the case where + the first entry is removed. */ + if (cookie->rel->r_offset >= removed_bytes) + cookie->rel->r_offset -= removed_bytes; + else + cookie->rel->r_offset = 0; + + cookie->rel++; + } + } + + if (removed_bytes != 0) + { + /* Adjust any remaining relocs (shouldn't be any). */ + for (; cookie->rel < cookie->relend; cookie->rel++) + { + if (cookie->rel->r_offset >= removed_bytes) + cookie->rel->r_offset -= removed_bytes; + else + cookie->rel->r_offset = 0; + } + + /* Clear the removed bytes. */ + memset (&contents[section_size - removed_bytes], 0, removed_bytes); + + pin_contents (sec, contents); + pin_internal_relocs (sec, cookie->rels); + + sec->_cooked_size = section_size - removed_bytes; + /* Also shrink _raw_size. See comments in relax_property_section. */ + sec->_raw_size = sec->_cooked_size; + } + else + { + release_contents (sec, contents); + release_internal_relocs (sec, cookie->rels); + } + + return (removed_bytes != 0); +} + + +static bfd_boolean +elf_xtensa_discard_info (abfd, cookie, info) + bfd *abfd; + struct elf_reloc_cookie *cookie; + struct bfd_link_info *info; +{ + asection *sec; + bfd_boolean changed = FALSE; + + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + if (xtensa_is_property_section (sec)) + { + if (elf_xtensa_discard_info_for_section (abfd, cookie, info, sec)) + changed = TRUE; + } + } + + return changed; +} + + +static bfd_boolean +elf_xtensa_ignore_discarded_relocs (sec) + asection *sec; +{ + return xtensa_is_property_section (sec); +} + + +/* Support for core dump NOTE sections. */ + +static bfd_boolean +elf_xtensa_grok_prstatus (abfd, note) + bfd *abfd; + Elf_Internal_Note *note; +{ + int offset; + unsigned int raw_size; + + /* The size for Xtensa is variable, so don't try to recognize the format + based on the size. Just assume this is GNU/Linux. */ + + /* pr_cursig */ + elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); + + /* pr_pid */ + elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24); + + /* pr_reg */ + offset = 72; + raw_size = note->descsz - offset - 4; + + /* Make a ".reg/999" section. */ + return _bfd_elfcore_make_pseudosection (abfd, ".reg", + raw_size, note->descpos + offset); +} + + +static bfd_boolean +elf_xtensa_grok_psinfo (abfd, note) + bfd *abfd; + Elf_Internal_Note *note; +{ + switch (note->descsz) + { + default: + return FALSE; + + case 128: /* GNU/Linux elf_prpsinfo */ + elf_tdata (abfd)->core_program + = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16); + elf_tdata (abfd)->core_command + = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80); + } + + /* Note that for some reason, a spurious space is tacked + onto the end of the args in some (at least one anyway) + implementations, so strip it off if it exists. */ + + { + char *command = elf_tdata (abfd)->core_command; + int n = strlen (command); + + if (0 < n && command[n - 1] == ' ') + command[n - 1] = '\0'; + } + + return TRUE; +} + + +/* Generic Xtensa configurability stuff. */ + +static xtensa_opcode callx0_op = XTENSA_UNDEFINED; +static xtensa_opcode callx4_op = XTENSA_UNDEFINED; +static xtensa_opcode callx8_op = XTENSA_UNDEFINED; +static xtensa_opcode callx12_op = XTENSA_UNDEFINED; +static xtensa_opcode call0_op = XTENSA_UNDEFINED; +static xtensa_opcode call4_op = XTENSA_UNDEFINED; +static xtensa_opcode call8_op = XTENSA_UNDEFINED; +static xtensa_opcode call12_op = XTENSA_UNDEFINED; + +static void +init_call_opcodes () +{ + if (callx0_op == XTENSA_UNDEFINED) + { + callx0_op = xtensa_opcode_lookup (xtensa_default_isa, "callx0"); + callx4_op = xtensa_opcode_lookup (xtensa_default_isa, "callx4"); + callx8_op = xtensa_opcode_lookup (xtensa_default_isa, "callx8"); + callx12_op = xtensa_opcode_lookup (xtensa_default_isa, "callx12"); + call0_op = xtensa_opcode_lookup (xtensa_default_isa, "call0"); + call4_op = xtensa_opcode_lookup (xtensa_default_isa, "call4"); + call8_op = xtensa_opcode_lookup (xtensa_default_isa, "call8"); + call12_op = xtensa_opcode_lookup (xtensa_default_isa, "call12"); + } +} + + +static bfd_boolean +is_indirect_call_opcode (opcode) + xtensa_opcode opcode; +{ + init_call_opcodes (); + return (opcode == callx0_op + || opcode == callx4_op + || opcode == callx8_op + || opcode == callx12_op); +} + + +static bfd_boolean +is_direct_call_opcode (opcode) + xtensa_opcode opcode; +{ + init_call_opcodes (); + return (opcode == call0_op + || opcode == call4_op + || opcode == call8_op + || opcode == call12_op); +} + + +static bfd_boolean +is_windowed_call_opcode (opcode) + xtensa_opcode opcode; +{ + init_call_opcodes (); + return (opcode == call4_op + || opcode == call8_op + || opcode == call12_op + || opcode == callx4_op + || opcode == callx8_op + || opcode == callx12_op); +} + + +static xtensa_opcode +get_l32r_opcode (void) +{ + static xtensa_opcode l32r_opcode = XTENSA_UNDEFINED; + if (l32r_opcode == XTENSA_UNDEFINED) + { + l32r_opcode = xtensa_opcode_lookup (xtensa_default_isa, "l32r"); + BFD_ASSERT (l32r_opcode != XTENSA_UNDEFINED); + } + return l32r_opcode; +} + + +static bfd_vma +l32r_offset (addr, pc) + bfd_vma addr; + bfd_vma pc; +{ + bfd_vma offset; + + offset = addr - ((pc+3) & -4); + BFD_ASSERT ((offset & ((1 << 2) - 1)) == 0); + offset = (signed int) offset >> 2; + BFD_ASSERT ((signed int) offset >> 16 == -1); + return offset; +} + + +/* Get the operand number for a PC-relative relocation. + If the relocation is not a PC-relative one, return (-1). */ + +static int +get_relocation_opnd (irel) + Elf_Internal_Rela *irel; +{ + if (ELF32_R_TYPE (irel->r_info) < R_XTENSA_OP0 + || ELF32_R_TYPE (irel->r_info) >= R_XTENSA_max) + return -1; + return ELF32_R_TYPE (irel->r_info) - R_XTENSA_OP0; +} + + +/* Get the opcode for a relocation. */ + +static xtensa_opcode +get_relocation_opcode (sec, contents, irel) + asection *sec; + bfd_byte *contents; + Elf_Internal_Rela *irel; +{ + static xtensa_insnbuf ibuff = NULL; + xtensa_isa isa = xtensa_default_isa; + + if (get_relocation_opnd (irel) == -1) + return XTENSA_UNDEFINED; + + if (contents == NULL) + return XTENSA_UNDEFINED; + + if (sec->_raw_size <= irel->r_offset) + return XTENSA_UNDEFINED; + + if (ibuff == NULL) + ibuff = xtensa_insnbuf_alloc (isa); + + /* Decode the instruction. */ + xtensa_insnbuf_from_chars (isa, ibuff, &contents[irel->r_offset]); + return xtensa_decode_insn (isa, ibuff); +} + + +bfd_boolean +is_l32r_relocation (sec, contents, irel) + asection *sec; + bfd_byte *contents; + Elf_Internal_Rela *irel; +{ + xtensa_opcode opcode; + + if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_OP1) + return FALSE; + + opcode = get_relocation_opcode (sec, contents, irel); + return (opcode == get_l32r_opcode ()); +} + + +/* Code for transforming CALLs at link-time. */ + +static bfd_reloc_status_type +elf_xtensa_do_asm_simplify (contents, address, content_length) + bfd_byte *contents; + bfd_vma address; + bfd_vma content_length; +{ + static xtensa_insnbuf insnbuf = NULL; + xtensa_opcode opcode; + xtensa_operand operand; + xtensa_opcode direct_call_opcode; + xtensa_isa isa = xtensa_default_isa; + bfd_byte *chbuf = contents + address; + int opn; + + if (insnbuf == NULL) + insnbuf = xtensa_insnbuf_alloc (isa); + + if (content_length < address) + { + (*_bfd_error_handler) + ("Attempt to convert L32R/CALLX to CALL failed\n"); + return bfd_reloc_other; + } + + opcode = get_expanded_call_opcode (chbuf, content_length - address); + direct_call_opcode = swap_callx_for_call_opcode (opcode); + if (direct_call_opcode == XTENSA_UNDEFINED) + { + (*_bfd_error_handler) + ("Attempt to convert L32R/CALLX to CALL failed\n"); + return bfd_reloc_other; + } + + /* Assemble a NOP ("or a1, a1, a1") into the 0 byte offset. */ + opcode = xtensa_opcode_lookup (isa, "or"); + xtensa_encode_insn (isa, opcode, insnbuf); + for (opn = 0; opn < 3; opn++) + { + operand = xtensa_get_operand (isa, opcode, opn); + xtensa_operand_set_field (operand, insnbuf, 1); + } + xtensa_insnbuf_to_chars (isa, insnbuf, chbuf); + + /* Assemble a CALL ("callN 0") into the 3 byte offset. */ + xtensa_encode_insn (isa, direct_call_opcode, insnbuf); + operand = xtensa_get_operand (isa, opcode, 0); + xtensa_operand_set_field (operand, insnbuf, 0); + xtensa_insnbuf_to_chars (isa, insnbuf, chbuf + 3); + + return bfd_reloc_ok; +} + + +static bfd_reloc_status_type +contract_asm_expansion (contents, content_length, irel) + bfd_byte *contents; + bfd_vma content_length; + Elf_Internal_Rela *irel; +{ + bfd_reloc_status_type retval = + elf_xtensa_do_asm_simplify (contents, irel->r_offset, content_length); + + if (retval != bfd_reloc_ok) + return retval; + + /* Update the irel->r_offset field so that the right immediate and + the right instruction are modified during the relocation. */ + irel->r_offset += 3; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_XTENSA_OP0); + return bfd_reloc_ok; +} + + +static xtensa_opcode +swap_callx_for_call_opcode (opcode) + xtensa_opcode opcode; +{ + init_call_opcodes (); + + if (opcode == callx0_op) return call0_op; + if (opcode == callx4_op) return call4_op; + if (opcode == callx8_op) return call8_op; + if (opcode == callx12_op) return call12_op; + + /* Return XTENSA_UNDEFINED if the opcode is not an indirect call. */ + return XTENSA_UNDEFINED; +} + + +/* Check if "buf" is pointing to a "L32R aN; CALLX aN" sequence, and + if so, return the CALLX opcode. If not, return XTENSA_UNDEFINED. */ + +#define L32R_TARGET_REG_OPERAND 0 +#define CALLN_SOURCE_OPERAND 0 + +static xtensa_opcode +get_expanded_call_opcode (buf, bufsize) + bfd_byte *buf; + int bufsize; +{ + static xtensa_insnbuf insnbuf = NULL; + xtensa_opcode opcode; + xtensa_operand operand; + xtensa_isa isa = xtensa_default_isa; + uint32 regno, call_regno; + + /* Buffer must be at least 6 bytes. */ + if (bufsize < 6) + return XTENSA_UNDEFINED; + + if (insnbuf == NULL) + insnbuf = xtensa_insnbuf_alloc (isa); + + xtensa_insnbuf_from_chars (isa, insnbuf, buf); + opcode = xtensa_decode_insn (isa, insnbuf); + + if (opcode != get_l32r_opcode ()) + return XTENSA_UNDEFINED; + + operand = xtensa_get_operand (isa, opcode, L32R_TARGET_REG_OPERAND); + regno = xtensa_operand_decode + (operand, xtensa_operand_get_field (operand, insnbuf)); + + /* Next instruction should be an CALLXn with operand 0 == regno. */ + xtensa_insnbuf_from_chars (isa, insnbuf, + buf + xtensa_insn_length (isa, opcode)); + opcode = xtensa_decode_insn (isa, insnbuf); + + if (!is_indirect_call_opcode (opcode)) + return XTENSA_UNDEFINED; + + operand = xtensa_get_operand (isa, opcode, CALLN_SOURCE_OPERAND); + call_regno = xtensa_operand_decode + (operand, xtensa_operand_get_field (operand, insnbuf)); + if (call_regno != regno) + return XTENSA_UNDEFINED; + + return opcode; +} + + +/* Data structures used during relaxation. */ + +/* r_reloc: relocation values. */ + +/* Through the relaxation process, we need to keep track of the values + that will result from evaluating relocations. The standard ELF + relocation structure is not sufficient for this purpose because we're + operating on multiple input files at once, so we need to know which + input file a relocation refers to. The r_reloc structure thus + records both the input file (bfd) and ELF relocation. + + For efficiency, an r_reloc also contains a "target_offset" field to + cache the target-section-relative offset value that is represented by + the relocation. */ + +typedef struct r_reloc_struct r_reloc; + +struct r_reloc_struct +{ + bfd *abfd; + Elf_Internal_Rela rela; + bfd_vma target_offset; +}; + +static bfd_boolean r_reloc_is_const + PARAMS ((const r_reloc *)); +static void r_reloc_init + PARAMS ((r_reloc *, bfd *, Elf_Internal_Rela *)); +static bfd_vma r_reloc_get_target_offset + PARAMS ((const r_reloc *)); +static asection *r_reloc_get_section + PARAMS ((const r_reloc *)); +static bfd_boolean r_reloc_is_defined + PARAMS ((const r_reloc *)); +static struct elf_link_hash_entry *r_reloc_get_hash_entry + PARAMS ((const r_reloc *)); + + +/* The r_reloc structure is included by value in literal_value, but not + every literal_value has an associated relocation -- some are simple + constants. In such cases, we set all the fields in the r_reloc + struct to zero. The r_reloc_is_const function should be used to + detect this case. */ + +static bfd_boolean +r_reloc_is_const (r_rel) + const r_reloc *r_rel; +{ + return (r_rel->abfd == NULL); +} + + +static void +r_reloc_init (r_rel, abfd, irel) + r_reloc *r_rel; + bfd *abfd; + Elf_Internal_Rela *irel; +{ + if (irel != NULL) + { + r_rel->rela = *irel; + r_rel->abfd = abfd; + r_rel->target_offset = r_reloc_get_target_offset (r_rel); + } + else + memset (r_rel, 0, sizeof (r_reloc)); +} + + +static bfd_vma +r_reloc_get_target_offset (r_rel) + const r_reloc *r_rel; +{ + bfd_vma target_offset; + unsigned long r_symndx; + + BFD_ASSERT (!r_reloc_is_const (r_rel)); + r_symndx = ELF32_R_SYM (r_rel->rela.r_info); + target_offset = get_elf_r_symndx_offset (r_rel->abfd, r_symndx); + return (target_offset + r_rel->rela.r_addend); +} + + +static struct elf_link_hash_entry * +r_reloc_get_hash_entry (r_rel) + const r_reloc *r_rel; +{ + unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info); + return get_elf_r_symndx_hash_entry (r_rel->abfd, r_symndx); +} + + +static asection * +r_reloc_get_section (r_rel) + const r_reloc *r_rel; +{ + unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info); + return get_elf_r_symndx_section (r_rel->abfd, r_symndx); +} + + +static bfd_boolean +r_reloc_is_defined (r_rel) + const r_reloc *r_rel; +{ + asection *sec = r_reloc_get_section (r_rel); + if (sec == bfd_abs_section_ptr + || sec == bfd_com_section_ptr + || sec == bfd_und_section_ptr) + return FALSE; + return TRUE; +} + + +/* source_reloc: relocations that reference literal sections. */ + +/* To determine whether literals can be coalesced, we need to first + record all the relocations that reference the literals. The + source_reloc structure below is used for this purpose. The + source_reloc entries are kept in a per-literal-section array, sorted + by offset within the literal section (i.e., target offset). + + The source_sec and r_rel.rela.r_offset fields identify the source of + the relocation. The r_rel field records the relocation value, i.e., + the offset of the literal being referenced. The opnd field is needed + to determine the range of the immediate field to which the relocation + applies, so we can determine whether another literal with the same + value is within range. The is_null field is true when the relocation + is being removed (e.g., when an L32R is being removed due to a CALLX + that is converted to a direct CALL). */ + +typedef struct source_reloc_struct source_reloc; + +struct source_reloc_struct +{ + asection *source_sec; + r_reloc r_rel; + xtensa_operand opnd; + bfd_boolean is_null; +}; + + +static void init_source_reloc + PARAMS ((source_reloc *, asection *, const r_reloc *, xtensa_operand)); +static source_reloc *find_source_reloc + PARAMS ((source_reloc *, int, asection *, Elf_Internal_Rela *)); +static int source_reloc_compare + PARAMS ((const PTR, const PTR)); + + +static void +init_source_reloc (reloc, source_sec, r_rel, opnd) + source_reloc *reloc; + asection *source_sec; + const r_reloc *r_rel; + xtensa_operand opnd; +{ + reloc->source_sec = source_sec; + reloc->r_rel = *r_rel; + reloc->opnd = opnd; + reloc->is_null = FALSE; +} + + +/* Find the source_reloc for a particular source offset and relocation + type. Note that the array is sorted by _target_ offset, so this is + just a linear search. */ + +static source_reloc * +find_source_reloc (src_relocs, src_count, sec, irel) + source_reloc *src_relocs; + int src_count; + asection *sec; + Elf_Internal_Rela *irel; +{ + int i; + + for (i = 0; i < src_count; i++) + { + if (src_relocs[i].source_sec == sec + && src_relocs[i].r_rel.rela.r_offset == irel->r_offset + && (ELF32_R_TYPE (src_relocs[i].r_rel.rela.r_info) + == ELF32_R_TYPE (irel->r_info))) + return &src_relocs[i]; + } + + return NULL; +} + + +static int +source_reloc_compare (ap, bp) + const PTR ap; + const PTR bp; +{ + const source_reloc *a = (const source_reloc *) ap; + const source_reloc *b = (const source_reloc *) bp; + + return (a->r_rel.target_offset - b->r_rel.target_offset); +} + + +/* Literal values and value hash tables. */ + +/* Literals with the same value can be coalesced. The literal_value + structure records the value of a literal: the "r_rel" field holds the + information from the relocation on the literal (if there is one) and + the "value" field holds the contents of the literal word itself. + + The value_map structure records a literal value along with the + location of a literal holding that value. The value_map hash table + is indexed by the literal value, so that we can quickly check if a + particular literal value has been seen before and is thus a candidate + for coalescing. */ + +typedef struct literal_value_struct literal_value; +typedef struct value_map_struct value_map; +typedef struct value_map_hash_table_struct value_map_hash_table; + +struct literal_value_struct +{ + r_reloc r_rel; + unsigned long value; +}; + +struct value_map_struct +{ + literal_value val; /* The literal value. */ + r_reloc loc; /* Location of the literal. */ + value_map *next; +}; + +struct value_map_hash_table_struct +{ + unsigned bucket_count; + value_map **buckets; + unsigned count; +}; + + +static bfd_boolean is_same_value + PARAMS ((const literal_value *, const literal_value *)); +static value_map_hash_table *value_map_hash_table_init + PARAMS ((void)); +static unsigned hash_literal_value + PARAMS ((const literal_value *)); +static unsigned hash_bfd_vma + PARAMS ((bfd_vma)); +static value_map *get_cached_value + PARAMS ((value_map_hash_table *, const literal_value *)); +static value_map *add_value_map + PARAMS ((value_map_hash_table *, const literal_value *, const r_reloc *)); + + +static bfd_boolean +is_same_value (src1, src2) + const literal_value *src1; + const literal_value *src2; +{ + if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) + return FALSE; + + if (r_reloc_is_const (&src1->r_rel)) + return (src1->value == src2->value); + + if (ELF32_R_TYPE (src1->r_rel.rela.r_info) + != ELF32_R_TYPE (src2->r_rel.rela.r_info)) + return FALSE; + + if (r_reloc_get_target_offset (&src1->r_rel) + != r_reloc_get_target_offset (&src2->r_rel)) + return FALSE; + + if (src1->value != src2->value) + return FALSE; + + /* Now check for the same section and the same elf_hash. */ + if (r_reloc_is_defined (&src1->r_rel)) + { + if (r_reloc_get_section (&src1->r_rel) + != r_reloc_get_section (&src2->r_rel)) + return FALSE; + } + else + { + if (r_reloc_get_hash_entry (&src1->r_rel) + != r_reloc_get_hash_entry (&src2->r_rel)) + return FALSE; + + if (r_reloc_get_hash_entry (&src1->r_rel) == 0) + return FALSE; + } + + return TRUE; +} + + +/* Must be power of 2. */ +#define INITIAL_HASH_RELOC_BUCKET_COUNT 1024 + +static value_map_hash_table * +value_map_hash_table_init () +{ + value_map_hash_table *values; + + values = (value_map_hash_table *) + bfd_malloc (sizeof (value_map_hash_table)); + + values->bucket_count = INITIAL_HASH_RELOC_BUCKET_COUNT; + values->count = 0; + values->buckets = (value_map **) + bfd_zmalloc (sizeof (value_map *) * values->bucket_count); + + return values; +} + + +static unsigned +hash_bfd_vma (val) + bfd_vma val; +{ + return (val >> 2) + (val >> 10); +} + + +static unsigned +hash_literal_value (src) + const literal_value *src; +{ + unsigned hash_val; + if (r_reloc_is_const (&src->r_rel)) + return hash_bfd_vma (src->value); + + hash_val = (hash_bfd_vma (r_reloc_get_target_offset (&src->r_rel)) + + hash_bfd_vma (src->value)); + + /* Now check for the same section and the same elf_hash. */ + if (r_reloc_is_defined (&src->r_rel)) + hash_val += hash_bfd_vma ((bfd_vma) r_reloc_get_section (&src->r_rel)); + else + hash_val += hash_bfd_vma ((bfd_vma) r_reloc_get_hash_entry (&src->r_rel)); + + return hash_val; +} + + +/* Check if the specified literal_value has been seen before. */ + +static value_map * +get_cached_value (map, val) + value_map_hash_table *map; + const literal_value *val; +{ + value_map *map_e; + value_map *bucket; + unsigned idx; + + idx = hash_literal_value (val); + idx = idx & (map->bucket_count - 1); + bucket = map->buckets[idx]; + for (map_e = bucket; map_e; map_e = map_e->next) + { + if (is_same_value (&map_e->val, val)) + return map_e; + } + return NULL; +} + + +/* Record a new literal value. It is illegal to call this if VALUE + already has an entry here. */ + +static value_map * +add_value_map (map, val, loc) + value_map_hash_table *map; + const literal_value *val; + const r_reloc *loc; +{ + value_map **bucket_p; + unsigned idx; + + value_map *val_e = (value_map *) bfd_zmalloc (sizeof (value_map)); + + BFD_ASSERT (get_cached_value (map, val) == NULL); + val_e->val = *val; + val_e->loc = *loc; + + idx = hash_literal_value (val); + idx = idx & (map->bucket_count - 1); + bucket_p = &map->buckets[idx]; + + val_e->next = *bucket_p; + *bucket_p = val_e; + map->count++; + /* FIXME: consider resizing the hash table if we get too many entries */ + + return val_e; +} + + +/* Lists of literals being coalesced or removed. */ + +/* In the usual case, the literal identified by "from" is being + coalesced with another literal identified by "to". If the literal is + unused and is being removed altogether, "to.abfd" will be NULL. + The removed_literal entries are kept on a per-section list, sorted + by the "from" offset field. */ + +typedef struct removed_literal_struct removed_literal; +typedef struct removed_literal_list_struct removed_literal_list; + +struct removed_literal_struct +{ + r_reloc from; + r_reloc to; + removed_literal *next; +}; + +struct removed_literal_list_struct +{ + removed_literal *head; + removed_literal *tail; +}; + + +static void add_removed_literal + PARAMS ((removed_literal_list *, const r_reloc *, const r_reloc *)); +static removed_literal *find_removed_literal + PARAMS ((removed_literal_list *, bfd_vma)); +static bfd_vma offset_with_removed_literals + PARAMS ((removed_literal_list *, bfd_vma)); + + +/* Record that the literal at "from" is being removed. If "to" is not + NULL, the "from" literal is being coalesced with the "to" literal. */ + +static void +add_removed_literal (removed_list, from, to) + removed_literal_list *removed_list; + const r_reloc *from; + const r_reloc *to; +{ + removed_literal *r, *new_r, *next_r; + + new_r = (removed_literal *) bfd_zmalloc (sizeof (removed_literal)); + + new_r->from = *from; + if (to) + new_r->to = *to; + else + new_r->to.abfd = NULL; + new_r->next = NULL; + + r = removed_list->head; + if (r == NULL) + { + removed_list->head = new_r; + removed_list->tail = new_r; + } + /* Special check for common case of append. */ + else if (removed_list->tail->from.target_offset < from->target_offset) + { + removed_list->tail->next = new_r; + removed_list->tail = new_r; + } + else + { + while (r->from.target_offset < from->target_offset + && r->next != NULL) + { + r = r->next; + } + next_r = r->next; + r->next = new_r; + new_r->next = next_r; + if (next_r == NULL) + removed_list->tail = new_r; + } +} + + +/* Check if the list of removed literals contains an entry for the + given address. Return the entry if found. */ + +static removed_literal * +find_removed_literal (removed_list, addr) + removed_literal_list *removed_list; + bfd_vma addr; +{ + removed_literal *r = removed_list->head; + while (r && r->from.target_offset < addr) + r = r->next; + if (r && r->from.target_offset == addr) + return r; + return NULL; +} + + +/* Adjust an offset in a section to compensate for literals that are + being removed. Search the list of removed literals and subtract + 4 bytes for every removed literal prior to the given address. */ + +static bfd_vma +offset_with_removed_literals (removed_list, addr) + removed_literal_list *removed_list; + bfd_vma addr; +{ + removed_literal *r = removed_list->head; + unsigned num_bytes = 0; + + if (r == NULL) + return addr; + + while (r && r->from.target_offset <= addr) + { + num_bytes += 4; + r = r->next; + } + if (num_bytes > addr) + return 0; + return (addr - num_bytes); +} + + +/* Coalescing literals may require a relocation to refer to a section in + a different input file, but the standard relocation information + cannot express that. Instead, the reloc_bfd_fix structures are used + to "fix" the relocations that refer to sections in other input files. + These structures are kept on per-section lists. The "src_type" field + records the relocation type in case there are multiple relocations on + the same location. FIXME: This is ugly; an alternative might be to + add new symbols with the "owner" field to some other input file. */ + +typedef struct reloc_bfd_fix_struct reloc_bfd_fix; + +struct reloc_bfd_fix_struct +{ + asection *src_sec; + bfd_vma src_offset; + unsigned src_type; /* Relocation type. */ + + bfd *target_abfd; + asection *target_sec; + bfd_vma target_offset; + + reloc_bfd_fix *next; +}; + + +static reloc_bfd_fix *reloc_bfd_fix_init + PARAMS ((asection *, bfd_vma, unsigned, bfd *, asection *, bfd_vma)); +static reloc_bfd_fix *get_bfd_fix + PARAMS ((reloc_bfd_fix *, asection *, bfd_vma, unsigned)); + + +static reloc_bfd_fix * +reloc_bfd_fix_init (src_sec, src_offset, src_type, + target_abfd, target_sec, target_offset) + asection *src_sec; + bfd_vma src_offset; + unsigned src_type; + bfd *target_abfd; + asection *target_sec; + bfd_vma target_offset; +{ + reloc_bfd_fix *fix; + + fix = (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix)); + fix->src_sec = src_sec; + fix->src_offset = src_offset; + fix->src_type = src_type; + fix->target_abfd = target_abfd; + fix->target_sec = target_sec; + fix->target_offset = target_offset; + + return fix; +} + + +static reloc_bfd_fix * +get_bfd_fix (fix_list, sec, offset, type) + reloc_bfd_fix *fix_list; + asection *sec; + bfd_vma offset; + unsigned type; +{ + reloc_bfd_fix *r; + + for (r = fix_list; r != NULL; r = r->next) + { + if (r->src_sec == sec + && r->src_offset == offset + && r->src_type == type) + return r; + } + return NULL; +} + + +/* Per-section data for relaxation. */ + +struct xtensa_relax_info_struct +{ + bfd_boolean is_relaxable_literal_section; + int visited; /* Number of times visited. */ + + source_reloc *src_relocs; /* Array[src_count]. */ + int src_count; + int src_next; /* Next src_relocs entry to assign. */ + + removed_literal_list removed_list; + + reloc_bfd_fix *fix_list; +}; + +struct elf_xtensa_section_data +{ + struct bfd_elf_section_data elf; + xtensa_relax_info relax_info; +}; + +static void init_xtensa_relax_info + PARAMS ((asection *)); +static xtensa_relax_info *get_xtensa_relax_info + PARAMS ((asection *)); +static void add_fix + PARAMS ((asection *, reloc_bfd_fix *)); + + +static bfd_boolean +elf_xtensa_new_section_hook (abfd, sec) + bfd *abfd; + asection *sec; +{ + struct elf_xtensa_section_data *sdata; + bfd_size_type amt = sizeof (*sdata); + + sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt); + if (sdata == NULL) + return FALSE; + sec->used_by_bfd = (PTR) sdata; + + return _bfd_elf_new_section_hook (abfd, sec); +} + + +static void +init_xtensa_relax_info (sec) + asection *sec; +{ + xtensa_relax_info *relax_info = get_xtensa_relax_info (sec); + + relax_info->is_relaxable_literal_section = FALSE; + relax_info->visited = 0; + + relax_info->src_relocs = NULL; + relax_info->src_count = 0; + relax_info->src_next = 0; + + relax_info->removed_list.head = NULL; + relax_info->removed_list.tail = NULL; + + relax_info->fix_list = NULL; +} + + +static xtensa_relax_info * +get_xtensa_relax_info (sec) + asection *sec; +{ + struct elf_xtensa_section_data *section_data; + + /* No info available if no section or if it is an output section. */ + if (!sec || sec == sec->output_section) + return NULL; + + section_data = (struct elf_xtensa_section_data *) elf_section_data (sec); + return §ion_data->relax_info; +} + + +static void +add_fix (src_sec, fix) + asection *src_sec; + reloc_bfd_fix *fix; +{ + xtensa_relax_info *relax_info; + + relax_info = get_xtensa_relax_info (src_sec); + fix->next = relax_info->fix_list; + relax_info->fix_list = fix; +} + + +/* Access to internal relocations, section contents and symbols. */ + +/* During relaxation, we need to modify relocations, section contents, + and symbol definitions, and we need to keep the original values from + being reloaded from the input files, i.e., we need to "pin" the + modified values in memory. We also want to continue to observe the + setting of the "keep-memory" flag. The following functions wrap the + standard BFD functions to take care of this for us. */ + +static Elf_Internal_Rela * +retrieve_internal_relocs (abfd, sec, keep_memory) + bfd *abfd; + asection *sec; + bfd_boolean keep_memory; +{ + Elf_Internal_Rela *internal_relocs; + + if ((sec->flags & SEC_LINKER_CREATED) != 0) + return NULL; + + internal_relocs = elf_section_data (sec)->relocs; + if (internal_relocs == NULL) + internal_relocs = (_bfd_elf32_link_read_relocs + (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, + keep_memory)); + return internal_relocs; +} + + +static void +pin_internal_relocs (sec, internal_relocs) + asection *sec; + Elf_Internal_Rela *internal_relocs; +{ + elf_section_data (sec)->relocs = internal_relocs; +} + + +static void +release_internal_relocs (sec, internal_relocs) + asection *sec; + Elf_Internal_Rela *internal_relocs; +{ + if (internal_relocs + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); +} + + +static bfd_byte * +retrieve_contents (abfd, sec, keep_memory) + bfd *abfd; + asection *sec; + bfd_boolean keep_memory; +{ + bfd_byte *contents; + + contents = elf_section_data (sec)->this_hdr.contents; + + if (contents == NULL && sec->_raw_size != 0) + { + contents = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (contents != NULL) + { + if (! bfd_get_section_contents (abfd, sec, contents, + (file_ptr) 0, sec->_raw_size)) + { + free (contents); + return NULL; + } + if (keep_memory) + elf_section_data (sec)->this_hdr.contents = contents; + } + } + return contents; +} + + +static void +pin_contents (sec, contents) + asection *sec; + bfd_byte *contents; +{ + elf_section_data (sec)->this_hdr.contents = contents; +} + + +static void +release_contents (sec, contents) + asection *sec; + bfd_byte *contents; +{ + if (contents && + elf_section_data (sec)->this_hdr.contents != contents) + free (contents); +} + + +static Elf_Internal_Sym * +retrieve_local_syms (input_bfd) + bfd *input_bfd; +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isymbuf; + size_t locsymcount; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + locsymcount = symtab_hdr->sh_info; + + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL && locsymcount != 0) + isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, + NULL, NULL, NULL); + + /* Save the symbols for this input file so they won't be read again. */ + if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents) + symtab_hdr->contents = (unsigned char *) isymbuf; + + return isymbuf; +} + + +/* Code for link-time relaxation. */ + +/* Local helper functions. */ +static bfd_boolean analyze_relocations + PARAMS ((struct bfd_link_info *)); +static bfd_boolean find_relaxable_sections + PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *)); +static bfd_boolean collect_source_relocs + PARAMS ((bfd *, asection *, struct bfd_link_info *)); +static bfd_boolean is_resolvable_asm_expansion + PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, + struct bfd_link_info *, bfd_boolean *)); +static bfd_boolean remove_literals + PARAMS ((bfd *, asection *, struct bfd_link_info *, value_map_hash_table *)); +static bfd_boolean relax_section + PARAMS ((bfd *, asection *, struct bfd_link_info *)); +static bfd_boolean relax_property_section + PARAMS ((bfd *, asection *, struct bfd_link_info *)); +static bfd_boolean relax_section_symbols + PARAMS ((bfd *, asection *)); +static bfd_boolean relocations_reach + PARAMS ((source_reloc *, int, const r_reloc *)); +static void translate_reloc + PARAMS ((const r_reloc *, r_reloc *)); +static Elf_Internal_Rela *get_irel_at_offset + PARAMS ((asection *, Elf_Internal_Rela *, bfd_vma)); +static Elf_Internal_Rela *find_associated_l32r_irel + PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *, + Elf_Internal_Rela *)); +static void shrink_dynamic_reloc_sections + PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *)); + + +static bfd_boolean +elf_xtensa_relax_section (abfd, sec, link_info, again) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + bfd_boolean *again; +{ + static value_map_hash_table *values = NULL; + xtensa_relax_info *relax_info; + + if (!values) + { + /* Do some overall initialization for relaxation. */ + values = value_map_hash_table_init (); + relaxing_section = TRUE; + if (!analyze_relocations (link_info)) + return FALSE; + } + *again = FALSE; + + /* Don't mess with linker-created sections. */ + if ((sec->flags & SEC_LINKER_CREATED) != 0) + return TRUE; + + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info != NULL); + + switch (relax_info->visited) + { + case 0: + /* Note: It would be nice to fold this pass into + analyze_relocations, but it is important for this step that the + sections be examined in link order. */ + if (!remove_literals (abfd, sec, link_info, values)) + return FALSE; + *again = TRUE; + break; + + case 1: + if (!relax_section (abfd, sec, link_info)) + return FALSE; + *again = TRUE; + break; + + case 2: + if (!relax_section_symbols (abfd, sec)) + return FALSE; + break; + } + + relax_info->visited++; + return TRUE; +} + +/* Initialization for relaxation. */ + +/* This function is called once at the start of relaxation. It scans + all the input sections and marks the ones that are relaxable (i.e., + literal sections with L32R relocations against them). It then + collect source_reloc information for all the relocations against + those relaxable sections. */ + +static bfd_boolean +analyze_relocations (link_info) + struct bfd_link_info *link_info; +{ + bfd *abfd; + asection *sec; + bfd_boolean is_relaxable = FALSE; + + /* Initialize the per-section relaxation info. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + init_xtensa_relax_info (sec); + } + + /* Mark relaxable sections (and count relocations against each one). */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable)) + return FALSE; + } + + /* Bail out if there are no relaxable sections. */ + if (!is_relaxable) + return TRUE; + + /* Allocate space for source_relocs. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + xtensa_relax_info *relax_info; + + relax_info = get_xtensa_relax_info (sec); + if (relax_info->is_relaxable_literal_section) + { + relax_info->src_relocs = (source_reloc *) + bfd_malloc (relax_info->src_count * sizeof (source_reloc)); + } + } + + /* Collect info on relocations against each relaxable section. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + if (!collect_source_relocs (abfd, sec, link_info)) + return FALSE; + } + + return TRUE; +} + + +/* Find all the literal sections that might be relaxed. The motivation + for this pass is that collect_source_relocs() needs to record _all_ + the relocations that target each relaxable section. That is + expensive and unnecessary unless the target section is actually going + to be relaxed. This pass identifies all such sections by checking if + they have L32Rs pointing to them. In the process, the total number + of relocations targetting each section is also counted so that we + know how much space to allocate for source_relocs against each + relaxable literal section. */ + +static bfd_boolean +find_relaxable_sections (abfd, sec, link_info, is_relaxable_p) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + bfd_boolean *is_relaxable_p; +{ + Elf_Internal_Rela *internal_relocs; + bfd_byte *contents; + bfd_boolean ok = TRUE; + unsigned i; + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + if (internal_relocs == NULL) + return ok; + + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->_raw_size != 0) + { + ok = FALSE; + goto error_return; + } + + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + r_reloc r_rel; + asection *target_sec; + xtensa_relax_info *target_relax_info; + + r_reloc_init (&r_rel, abfd, irel); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + if (!target_relax_info) + continue; + + /* Count relocations against the target section. */ + target_relax_info->src_count++; + + if (is_literal_section (target_sec) + && is_l32r_relocation (sec, contents, irel) + && r_reloc_is_defined (&r_rel)) + { + /* Mark the target section as relaxable. */ + target_relax_info->is_relaxable_literal_section = TRUE; + *is_relaxable_p = TRUE; + } + } + + error_return: + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + return ok; +} + + +/* Record _all_ the relocations that point to relaxable literal + sections, and get rid of ASM_EXPAND relocs by either converting them + to ASM_SIMPLIFY or by removing them. */ + +static bfd_boolean +collect_source_relocs (abfd, sec, link_info) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; +{ + Elf_Internal_Rela *internal_relocs; + bfd_byte *contents; + bfd_boolean ok = TRUE; + unsigned i; + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + if (internal_relocs == NULL) + return ok; + + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->_raw_size != 0) + { + ok = FALSE; + goto error_return; + } + + /* Record relocations against relaxable literal sections. */ + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + r_reloc r_rel; + asection *target_sec; + xtensa_relax_info *target_relax_info; + + r_reloc_init (&r_rel, abfd, irel); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && target_relax_info->is_relaxable_literal_section) + { + xtensa_opcode opcode; + xtensa_operand opnd; + source_reloc *s_reloc; + int src_next; + + src_next = target_relax_info->src_next++; + s_reloc = &target_relax_info->src_relocs[src_next]; + + opcode = get_relocation_opcode (sec, contents, irel); + if (opcode == XTENSA_UNDEFINED) + opnd = NULL; + else + opnd = xtensa_get_operand (xtensa_default_isa, opcode, + get_relocation_opnd (irel)); + + init_source_reloc (s_reloc, sec, &r_rel, opnd); + } + } + + /* Now get rid of ASM_EXPAND relocations. At this point, the + src_relocs array for the target literal section may still be + incomplete, but it must at least contain the entries for the L32R + relocations associated with ASM_EXPANDs because they were just + added in the preceding loop over the relocations. */ + + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + bfd_boolean is_reachable; + + if (!is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info, + &is_reachable)) + continue; + + if (is_reachable) + { + Elf_Internal_Rela *l32r_irel; + r_reloc r_rel; + asection *target_sec; + xtensa_relax_info *target_relax_info; + + /* Mark the source_reloc for the L32R so that it will be + removed in remove_literals(), along with the associated + literal. */ + l32r_irel = find_associated_l32r_irel (sec, contents, + irel, internal_relocs); + if (l32r_irel == NULL) + continue; + + r_reloc_init (&r_rel, abfd, l32r_irel); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && target_relax_info->is_relaxable_literal_section) + { + source_reloc *s_reloc; + + /* Search the source_relocs for the entry corresponding to + the l32r_irel. Note: The src_relocs array is not yet + sorted, but it wouldn't matter anyway because we're + searching by source offset instead of target offset. */ + s_reloc = find_source_reloc (target_relax_info->src_relocs, + target_relax_info->src_next, + sec, l32r_irel); + BFD_ASSERT (s_reloc); + s_reloc->is_null = TRUE; + } + + /* Convert this reloc to ASM_SIMPLIFY. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_XTENSA_ASM_SIMPLIFY); + l32r_irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + + pin_internal_relocs (sec, internal_relocs); + } + else + { + /* It is resolvable but doesn't reach. We resolve now + by eliminating the relocation -- the call will remain + expanded into L32R/CALLX. */ + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + pin_internal_relocs (sec, internal_relocs); + } + } + + error_return: + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + return ok; +} + + +/* Return TRUE if the asm expansion can be resolved. Generally it can + be resolved on a final link or when a partial link locates it in the + same section as the target. Set "is_reachable" flag if the target of + the call is within the range of a direct call, given the current VMA + for this section and the target section. */ + +bfd_boolean +is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info, + is_reachable_p) + bfd *abfd; + asection *sec; + bfd_byte *contents; + Elf_Internal_Rela *irel; + struct bfd_link_info *link_info; + bfd_boolean *is_reachable_p; +{ + asection *target_sec; + bfd_vma target_offset; + r_reloc r_rel; + xtensa_opcode opcode, direct_call_opcode; + bfd_vma self_address; + bfd_vma dest_address; + + *is_reachable_p = FALSE; + + if (contents == NULL) + return FALSE; + + if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) + return FALSE; + + opcode = get_expanded_call_opcode (contents + irel->r_offset, + sec->_raw_size - irel->r_offset); + + direct_call_opcode = swap_callx_for_call_opcode (opcode); + if (direct_call_opcode == XTENSA_UNDEFINED) + return FALSE; + + /* Check and see that the target resolves. */ + r_reloc_init (&r_rel, abfd, irel); + if (!r_reloc_is_defined (&r_rel)) + return FALSE; + + target_sec = r_reloc_get_section (&r_rel); + target_offset = r_reloc_get_target_offset (&r_rel); + + /* If the target is in a shared library, then it doesn't reach. This + isn't supposed to come up because the compiler should never generate + non-PIC calls on systems that use shared libraries, but the linker + shouldn't crash regardless. */ + if (!target_sec->output_section) + return FALSE; + + /* For relocateable sections, we can only simplify when the output + section of the target is the same as the output section of the + source. */ + if (link_info->relocateable + && (target_sec->output_section != sec->output_section)) + return FALSE; + + self_address = (sec->output_section->vma + + sec->output_offset + irel->r_offset + 3); + dest_address = (target_sec->output_section->vma + + target_sec->output_offset + target_offset); + + *is_reachable_p = pcrel_reloc_fits + (xtensa_get_operand (xtensa_default_isa, direct_call_opcode, 0), + self_address, dest_address); + + if ((self_address >> CALL_SEGMENT_BITS) != + (dest_address >> CALL_SEGMENT_BITS)) + return FALSE; + + return TRUE; +} + + +static Elf_Internal_Rela * +find_associated_l32r_irel (sec, contents, other_irel, internal_relocs) + asection *sec; + bfd_byte *contents; + Elf_Internal_Rela *other_irel; + Elf_Internal_Rela *internal_relocs; +{ + unsigned i; + + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + + if (irel == other_irel) + continue; + if (irel->r_offset != other_irel->r_offset) + continue; + if (is_l32r_relocation (sec, contents, irel)) + return irel; + } + + return NULL; +} + +/* First relaxation pass. */ + +/* If the section is relaxable (i.e., a literal section), check each + literal to see if it has the same value as another literal that has + already been seen, either in the current section or a previous one. + If so, add an entry to the per-section list of removed literals. The + actual changes are deferred until the next pass. */ + +static bfd_boolean +remove_literals (abfd, sec, link_info, values) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + value_map_hash_table *values; +{ + xtensa_relax_info *relax_info; + bfd_byte *contents; + Elf_Internal_Rela *internal_relocs; + source_reloc *src_relocs; + bfd_boolean ok = TRUE; + int i; + + /* Do nothing if it is not a relaxable literal section. */ + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info); + + if (!relax_info->is_relaxable_literal_section) + return ok; + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->_raw_size != 0) + { + ok = FALSE; + goto error_return; + } + + /* Sort the source_relocs by target offset. */ + src_relocs = relax_info->src_relocs; + qsort (src_relocs, relax_info->src_count, + sizeof (source_reloc), source_reloc_compare); + + for (i = 0; i < relax_info->src_count; i++) + { + source_reloc *rel; + Elf_Internal_Rela *irel = NULL; + literal_value val; + value_map *val_map; + + rel = &src_relocs[i]; + irel = get_irel_at_offset (sec, internal_relocs, + rel->r_rel.target_offset); + + /* If the target_offset for this relocation is the same as the + previous relocation, then we've already considered whether the + literal can be coalesced. Skip to the next one.... */ + if (i != 0 && (src_relocs[i-1].r_rel.target_offset + == rel->r_rel.target_offset)) + continue; + + /* Check if the relocation was from an L32R that is being removed + because a CALLX was converted to a direct CALL, and check if + there are no other relocations to the literal. */ + if (rel->is_null + && (i == relax_info->src_count - 1 + || (src_relocs[i+1].r_rel.target_offset + != rel->r_rel.target_offset))) + { + /* Mark the unused literal so that it will be removed. */ + add_removed_literal (&relax_info->removed_list, &rel->r_rel, NULL); + + /* Zero out the relocation on this literal location. */ + if (irel) + { + if (elf_hash_table (link_info)->dynamic_sections_created) + shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); + + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + } + + continue; + } + + /* Find the literal value. */ + r_reloc_init (&val.r_rel, abfd, irel); + BFD_ASSERT (rel->r_rel.target_offset < sec->_raw_size); + val.value = bfd_get_32 (abfd, contents + rel->r_rel.target_offset); + + /* Check if we've seen another literal with the same value. */ + val_map = get_cached_value (values, &val); + if (val_map != NULL) + { + /* First check that THIS and all the other relocs to this + literal will FIT if we move them to the new address. */ + + if (relocations_reach (rel, relax_info->src_count - i, + &val_map->loc)) + { + /* Mark that the literal will be coalesced. */ + add_removed_literal (&relax_info->removed_list, + &rel->r_rel, &val_map->loc); + } + else + { + /* Relocations do not reach -- do not remove this literal. */ + val_map->loc = rel->r_rel; + } + } + else + { + /* This is the first time we've seen this literal value. */ + BFD_ASSERT (sec == r_reloc_get_section (&rel->r_rel)); + add_value_map (values, &val, &rel->r_rel); + } + } + +error_return: + release_contents (sec, contents); + release_internal_relocs (sec, internal_relocs); + return ok; +} + + +/* Check if the original relocations (presumably on L32R instructions) + identified by reloc[0..N] can be changed to reference the literal + identified by r_rel. If r_rel is out of range for any of the + original relocations, then we don't want to coalesce the original + literal with the one at r_rel. We only check reloc[0..N], where the + offsets are all the same as for reloc[0] (i.e., they're all + referencing the same literal) and where N is also bounded by the + number of remaining entries in the "reloc" array. The "reloc" array + is sorted by target offset so we know all the entries for the same + literal will be contiguous. */ + +static bfd_boolean +relocations_reach (reloc, remaining_relocs, r_rel) + source_reloc *reloc; + int remaining_relocs; + const r_reloc *r_rel; +{ + bfd_vma from_offset, source_address, dest_address; + asection *sec; + int i; + + if (!r_reloc_is_defined (r_rel)) + return FALSE; + + sec = r_reloc_get_section (r_rel); + from_offset = reloc[0].r_rel.target_offset; + + for (i = 0; i < remaining_relocs; i++) + { + if (reloc[i].r_rel.target_offset != from_offset) + break; + + /* Ignore relocations that have been removed. */ + if (reloc[i].is_null) + continue; + + /* The original and new output section for these must be the same + in order to coalesce. */ + if (r_reloc_get_section (&reloc[i].r_rel)->output_section + != sec->output_section) + return FALSE; + + /* A NULL operand means it is not a PC-relative relocation, so + the literal can be moved anywhere. */ + if (reloc[i].opnd) + { + /* Otherwise, check to see that it fits. */ + source_address = (reloc[i].source_sec->output_section->vma + + reloc[i].source_sec->output_offset + + reloc[i].r_rel.rela.r_offset); + dest_address = (sec->output_section->vma + + sec->output_offset + + r_rel->target_offset); + + if (!pcrel_reloc_fits (reloc[i].opnd, source_address, dest_address)) + return FALSE; + } + } + + return TRUE; +} + + +/* WARNING: linear search here. If the relocation are in order by + address, we can use a faster binary search. ALSO, we assume that + there is only 1 non-NONE relocation per address. */ + +static Elf_Internal_Rela * +get_irel_at_offset (sec, internal_relocs, offset) + asection *sec; + Elf_Internal_Rela *internal_relocs; + bfd_vma offset; +{ + unsigned i; + if (!internal_relocs) + return NULL; + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + if (irel->r_offset == offset + && ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE) + return irel; + } + return NULL; +} + + +/* Second relaxation pass. */ + +/* Modify all of the relocations to point to the right spot, and if this + is a relaxable section, delete the unwanted literals and fix the + cooked_size. */ + +bfd_boolean +relax_section (abfd, sec, link_info) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; +{ + Elf_Internal_Rela *internal_relocs; + xtensa_relax_info *relax_info; + bfd_byte *contents; + bfd_boolean ok = TRUE; + unsigned i; + + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info); + + /* Handle property sections (e.g., literal tables) specially. */ + if (xtensa_is_property_section (sec)) + { + BFD_ASSERT (!relax_info->is_relaxable_literal_section); + return relax_property_section (abfd, sec, link_info); + } + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->_raw_size != 0) + { + ok = FALSE; + goto error_return; + } + + if (internal_relocs) + { + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel; + xtensa_relax_info *target_relax_info; + bfd_vma source_offset; + r_reloc r_rel; + unsigned r_type; + asection *target_sec; + + /* Locally change the source address. + Translate the target to the new target address. + If it points to this section and has been removed, + NULLify it. + Write it back. */ + + irel = &internal_relocs[i]; + source_offset = irel->r_offset; + + r_type = ELF32_R_TYPE (irel->r_info); + r_reloc_init (&r_rel, abfd, irel); + + if (relax_info->is_relaxable_literal_section) + { + if (r_type != R_XTENSA_NONE + && find_removed_literal (&relax_info->removed_list, + irel->r_offset)) + { + /* Remove this relocation. */ + if (elf_hash_table (link_info)->dynamic_sections_created) + shrink_dynamic_reloc_sections (link_info, abfd, sec, irel); + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + irel->r_offset = offset_with_removed_literals + (&relax_info->removed_list, irel->r_offset); + continue; + } + source_offset = + offset_with_removed_literals (&relax_info->removed_list, + irel->r_offset); + irel->r_offset = source_offset; + } + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && target_relax_info->is_relaxable_literal_section) + { + r_reloc new_rel; + reloc_bfd_fix *fix; + + translate_reloc (&r_rel, &new_rel); + + /* FIXME: If the relocation still references a section in + the same input file, the relocation should be modified + directly instead of adding a "fix" record. */ + + fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0, + r_reloc_get_section (&new_rel), + new_rel.target_offset); + add_fix (sec, fix); + } + + pin_internal_relocs (sec, internal_relocs); + } + } + + if (relax_info->is_relaxable_literal_section) + { + /* Walk through the contents and delete literals that are not needed + anymore. */ + + unsigned long size = sec->_cooked_size; + unsigned long removed = 0; + + removed_literal *reloc = relax_info->removed_list.head; + for (; reloc; reloc = reloc->next) + { + unsigned long upper = sec->_raw_size; + bfd_vma start = reloc->from.target_offset + 4; + if (reloc->next) + upper = reloc->next->from.target_offset; + if (upper - start != 0) + { + BFD_ASSERT (start <= upper); + memmove (contents + start - removed - 4, + contents + start, + upper - start ); + pin_contents (sec, contents); + } + removed += 4; + size -= 4; + } + + /* Change the section size. */ + sec->_cooked_size = size; + /* Also shrink _raw_size. (The code in relocate_section that + checks that relocations are within the section must use + _raw_size because of the way the stabs sections are relaxed; + shrinking _raw_size means that these checks will not be + unnecessarily lax.) */ + sec->_raw_size = size; + } + + error_return: + release_internal_relocs (sec, internal_relocs); + release_contents (sec, contents); + return ok; +} + + +/* Fix up a relocation to take account of removed literals. */ + +static void +translate_reloc (orig_rel, new_rel) + const r_reloc *orig_rel; + r_reloc *new_rel; +{ + asection *sec; + xtensa_relax_info *relax_info; + removed_literal *removed; + unsigned long new_offset; + + *new_rel = *orig_rel; + + if (!r_reloc_is_defined (orig_rel)) + return; + sec = r_reloc_get_section (orig_rel); + + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info); + + if (!relax_info->is_relaxable_literal_section) + return; + + /* Check if the original relocation is against a literal being removed. */ + removed = find_removed_literal (&relax_info->removed_list, + orig_rel->target_offset); + if (removed) + { + asection *new_sec; + + /* The fact that there is still a relocation to this literal indicates + that the literal is being coalesced, not simply removed. */ + BFD_ASSERT (removed->to.abfd != NULL); + + /* This was moved to some other address (possibly in another section). */ + *new_rel = removed->to; + new_sec = r_reloc_get_section (new_rel); + if (new_sec != sec) + { + sec = new_sec; + relax_info = get_xtensa_relax_info (sec); + if (!relax_info || !relax_info->is_relaxable_literal_section) + return; + } + } + + /* ...and the target address may have been moved within its section. */ + new_offset = offset_with_removed_literals (&relax_info->removed_list, + new_rel->target_offset); + + /* Modify the offset and addend. */ + new_rel->target_offset = new_offset; + new_rel->rela.r_addend += (new_offset - new_rel->target_offset); +} + + +/* For dynamic links, there may be a dynamic relocation for each + literal. The number of dynamic relocations must be computed in + size_dynamic_sections, which occurs before relaxation. When a + literal is removed, this function checks if there is a corresponding + dynamic relocation and shrinks the size of the appropriate dynamic + relocation section accordingly. At this point, the contents of the + dynamic relocation sections have not yet been filled in, so there's + nothing else that needs to be done. */ + +static void +shrink_dynamic_reloc_sections (info, abfd, input_section, rel) + struct bfd_link_info *info; + bfd *abfd; + asection *input_section; + Elf_Internal_Rela *rel; +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + unsigned long r_symndx; + int r_type; + struct elf_link_hash_entry *h; + bfd_boolean dynamic_symbol; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + + r_type = ELF32_R_TYPE (rel->r_info); + r_symndx = ELF32_R_SYM (rel->r_info); + + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + dynamic_symbol = xtensa_elf_dynamic_symbol_p (info, h); + + if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT) + && (input_section->flags & SEC_ALLOC) != 0 + && (dynamic_symbol || info->shared)) + { + bfd *dynobj; + const char *srel_name; + asection *srel; + bfd_boolean is_plt = FALSE; + + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + if (dynamic_symbol && r_type == R_XTENSA_PLT) + { + srel_name = ".rela.plt"; + is_plt = TRUE; + } + else + srel_name = ".rela.got"; + + /* Reduce size of the .rela.* section by one reloc. */ + srel = bfd_get_section_by_name (dynobj, srel_name); + BFD_ASSERT (srel != NULL); + BFD_ASSERT (srel->_cooked_size >= sizeof (Elf32_External_Rela)); + srel->_cooked_size -= sizeof (Elf32_External_Rela); + + /* Also shrink _raw_size. (This seems wrong but other bfd code seems + to assume that linker-created sections will never be relaxed and + hence _raw_size must always equal _cooked_size.) */ + srel->_raw_size = srel->_cooked_size; + + if (is_plt) + { + asection *splt, *sgotplt, *srelgot; + int reloc_index, chunk; + + /* Find the PLT reloc index of the entry being removed. This + is computed from the size of ".rela.plt". It is needed to + figure out which PLT chunk to resize. Usually "last index + = size - 1" since the index starts at zero, but in this + context, the size has just been decremented so there's no + need to subtract one. */ + reloc_index = srel->_cooked_size / sizeof (Elf32_External_Rela); + + chunk = reloc_index / PLT_ENTRIES_PER_CHUNK; + splt = elf_xtensa_get_plt_section (dynobj, chunk); + sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk); + BFD_ASSERT (splt != NULL && sgotplt != NULL); + + /* Check if an entire PLT chunk has just been eliminated. */ + if (reloc_index % PLT_ENTRIES_PER_CHUNK == 0) + { + /* The two magic GOT entries for that chunk can go away. */ + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (srelgot != NULL); + srelgot->reloc_count -= 2; + srelgot->_cooked_size -= 2 * sizeof (Elf32_External_Rela); + /* Shrink _raw_size (see comment above). */ + srelgot->_raw_size = srelgot->_cooked_size; + + sgotplt->_cooked_size -= 8; + + /* There should be only one entry left (and it will be + removed below). */ + BFD_ASSERT (sgotplt->_cooked_size == 4); + BFD_ASSERT (splt->_cooked_size == PLT_ENTRY_SIZE); + } + + BFD_ASSERT (sgotplt->_cooked_size >= 4); + BFD_ASSERT (splt->_cooked_size >= PLT_ENTRY_SIZE); + + sgotplt->_cooked_size -= 4; + splt->_cooked_size -= PLT_ENTRY_SIZE; + + /* Shrink _raw_sizes (see comment above). */ + sgotplt->_raw_size = sgotplt->_cooked_size; + splt->_raw_size = splt->_cooked_size; + } + } +} + + +/* This is similar to relax_section except that when a target is moved, + we shift addresses up. We also need to modify the size. This + algorithm does NOT allow for relocations into the middle of the + property sections. */ + +static bfd_boolean +relax_property_section (abfd, sec, link_info) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; +{ + Elf_Internal_Rela *internal_relocs; + bfd_byte *contents; + unsigned i, nexti; + bfd_boolean ok = TRUE; + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->_raw_size != 0) + { + ok = FALSE; + goto error_return; + } + + if (internal_relocs) + { + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel; + xtensa_relax_info *target_relax_info; + r_reloc r_rel; + unsigned r_type; + asection *target_sec; + + /* Locally change the source address. + Translate the target to the new target address. + If it points to this section and has been removed, MOVE IT. + Also, don't forget to modify the associated SIZE at + (offset + 4). */ + + irel = &internal_relocs[i]; + r_type = ELF32_R_TYPE (irel->r_info); + if (r_type == R_XTENSA_NONE) + continue; + + r_reloc_init (&r_rel, abfd, irel); + + target_sec = r_reloc_get_section (&r_rel); + target_relax_info = get_xtensa_relax_info (target_sec); + + if (target_relax_info + && target_relax_info->is_relaxable_literal_section) + { + /* Translate the relocation's destination. */ + bfd_vma new_offset; + bfd_vma new_end_offset; + bfd_byte *size_p; + long old_size, new_size; + + new_offset = + offset_with_removed_literals (&target_relax_info->removed_list, + r_rel.target_offset); + + /* Assert that we are not out of bounds. */ + size_p = &contents[irel->r_offset + 4]; + old_size = bfd_get_32 (abfd, &contents[irel->r_offset + 4]); + + new_end_offset = + offset_with_removed_literals (&target_relax_info->removed_list, + r_rel.target_offset + old_size); + + new_size = new_end_offset - new_offset; + if (new_size != old_size) + { + bfd_put_32 (abfd, new_size, size_p); + pin_contents (sec, contents); + } + + if (new_offset != r_rel.target_offset) + { + bfd_vma diff = new_offset - r_rel.target_offset; + irel->r_addend += diff; + pin_internal_relocs (sec, internal_relocs); + } + } + } + } + + /* Combine adjacent property table entries. This is also done in + finish_dynamic_sections() but at that point it's too late to + reclaim the space in the output section, so we do this twice. */ + + if (internal_relocs) + { + Elf_Internal_Rela *last_irel = NULL; + int removed_bytes = 0; + bfd_vma offset, last_irel_offset; + bfd_vma section_size; + + /* Walk over memory and irels at the same time. + This REQUIRES that the internal_relocs be sorted by offset. */ + qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela), + internal_reloc_compare); + nexti = 0; /* Index into internal_relocs. */ + + pin_internal_relocs (sec, internal_relocs); + pin_contents (sec, contents); + + last_irel_offset = (bfd_vma) -1; + section_size = (sec->_cooked_size ? sec->_cooked_size : sec->_raw_size); + BFD_ASSERT (section_size % 8 == 0); + + for (offset = 0; offset < section_size; offset += 8) + { + Elf_Internal_Rela *irel, *next_irel; + bfd_vma bytes_to_remove, size, actual_offset; + bfd_boolean remove_this_irel; + + irel = NULL; + next_irel = NULL; + + /* Find the next two relocations (if there are that many left), + skipping over any R_XTENSA_NONE relocs. On entry, "nexti" is + the starting reloc index. After these two loops, "i" + is the index of the first non-NONE reloc past that starting + index, and "nexti" is the index for the next non-NONE reloc + after "i". */ + + for (i = nexti; i < sec->reloc_count; i++) + { + if (ELF32_R_TYPE (internal_relocs[i].r_info) != R_XTENSA_NONE) + { + irel = &internal_relocs[i]; + break; + } + internal_relocs[i].r_offset -= removed_bytes; + } + + for (nexti = i + 1; nexti < sec->reloc_count; nexti++) + { + if (ELF32_R_TYPE (internal_relocs[nexti].r_info) + != R_XTENSA_NONE) + { + next_irel = &internal_relocs[nexti]; + break; + } + internal_relocs[nexti].r_offset -= removed_bytes; + } + + remove_this_irel = FALSE; + bytes_to_remove = 0; + actual_offset = offset - removed_bytes; + size = bfd_get_32 (abfd, &contents[actual_offset + 4]); + + /* Check that the irels are sorted by offset, + with only one per address. */ + BFD_ASSERT (!irel || (int) irel->r_offset > (int) last_irel_offset); + BFD_ASSERT (!next_irel || next_irel->r_offset > irel->r_offset); + + /* Make sure there isn't a reloc on the size field. */ + if (irel && irel->r_offset == offset + 4) + { + irel->r_offset -= removed_bytes; + last_irel_offset = irel->r_offset; + } + else if (next_irel && next_irel->r_offset == offset + 4) + { + nexti += 1; + irel->r_offset -= removed_bytes; + next_irel->r_offset -= removed_bytes; + last_irel_offset = next_irel->r_offset; + } + else if (size == 0) + { + /* Always remove entries with zero size. */ + bytes_to_remove = 8; + if (irel && irel->r_offset == offset) + { + remove_this_irel = TRUE; + + irel->r_offset -= removed_bytes; + last_irel_offset = irel->r_offset; + } + } + else if (irel && irel->r_offset == offset) + { + if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32) + { + if (last_irel) + { + bfd_vma old_size = + bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]); + bfd_vma old_address = + (last_irel->r_addend + + bfd_get_32 (abfd, &contents[last_irel->r_offset])); + bfd_vma new_address = + (irel->r_addend + + bfd_get_32 (abfd, &contents[actual_offset])); + + if ((ELF32_R_SYM (irel->r_info) == + ELF32_R_SYM (last_irel->r_info)) + && (old_address + old_size == new_address)) + { + /* fix the old size */ + bfd_put_32 (abfd, old_size + size, + &contents[last_irel->r_offset + 4]); + bytes_to_remove = 8; + remove_this_irel = TRUE; + } + else + last_irel = irel; + } + else + last_irel = irel; + } + + irel->r_offset -= removed_bytes; + last_irel_offset = irel->r_offset; + } + + if (remove_this_irel) + { + irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE); + irel->r_offset -= bytes_to_remove; + } + + if (bytes_to_remove != 0) + { + removed_bytes += bytes_to_remove; + if (offset + 8 < section_size) + memmove (&contents[actual_offset], + &contents[actual_offset+8], + section_size - offset - 8); + } + } + + if (removed_bytes) + { + /* Clear the removed bytes. */ + memset (&contents[section_size - removed_bytes], 0, removed_bytes); + + sec->_cooked_size = section_size - removed_bytes; + /* Also shrink _raw_size. (The code in relocate_section that + checks that relocations are within the section must use + _raw_size because of the way the stabs sections are + relaxed; shrinking _raw_size means that these checks will + not be unnecessarily lax.) */ + sec->_raw_size = sec->_cooked_size; + } + } + + error_return: + release_internal_relocs (sec, internal_relocs); + release_contents (sec, contents); + return ok; +} + + +/* Third relaxation pass. */ + +/* Change symbol values to account for removed literals. */ + +bfd_boolean +relax_section_symbols (abfd, sec) + bfd *abfd; + asection *sec; +{ + xtensa_relax_info *relax_info; + unsigned int sec_shndx; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isymbuf; + unsigned i, num_syms, num_locals; + + relax_info = get_xtensa_relax_info (sec); + BFD_ASSERT (relax_info); + + if (!relax_info->is_relaxable_literal_section) + return TRUE; + + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + isymbuf = retrieve_local_syms (abfd); + + num_syms = symtab_hdr->sh_size / sizeof (Elf32_External_Sym); + num_locals = symtab_hdr->sh_info; + + /* Adjust the local symbols defined in this section. */ + for (i = 0; i < num_locals; i++) + { + Elf_Internal_Sym *isym = &isymbuf[i]; + + if (isym->st_shndx == sec_shndx) + { + bfd_vma new_address = offset_with_removed_literals + (&relax_info->removed_list, isym->st_value); + if (new_address != isym->st_value) + isym->st_value = new_address; + } + } + + /* Now adjust the global symbols defined in this section. */ + for (i = 0; i < (num_syms - num_locals); i++) + { + struct elf_link_hash_entry *sym_hash; + + sym_hash = elf_sym_hashes (abfd)[i]; + + if (sym_hash->root.type == bfd_link_hash_warning) + sym_hash = (struct elf_link_hash_entry *) sym_hash->root.u.i.link; + + if ((sym_hash->root.type == bfd_link_hash_defined + || sym_hash->root.type == bfd_link_hash_defweak) + && sym_hash->root.u.def.section == sec) + { + bfd_vma new_address = offset_with_removed_literals + (&relax_info->removed_list, sym_hash->root.u.def.value); + if (new_address != sym_hash->root.u.def.value) + sym_hash->root.u.def.value = new_address; + } + } + + return TRUE; +} + + +/* "Fix" handling functions, called while performing relocations. */ + +static void +do_fix_for_relocateable_link (rel, input_bfd, input_section) + Elf_Internal_Rela *rel; + bfd *input_bfd; + asection *input_section; +{ + r_reloc r_rel; + asection *sec, *old_sec; + bfd_vma old_offset; + int r_type = ELF32_R_TYPE (rel->r_info); + reloc_bfd_fix *fix_list; + reloc_bfd_fix *fix; + + if (r_type == R_XTENSA_NONE) + return; + + fix_list = (get_xtensa_relax_info (input_section))->fix_list; + if (fix_list == NULL) + return; + + fix = get_bfd_fix (fix_list, input_section, rel->r_offset, r_type); + if (fix == NULL) + return; + + r_reloc_init (&r_rel, input_bfd, rel); + old_sec = r_reloc_get_section (&r_rel); + old_offset = r_reloc_get_target_offset (&r_rel); + + if (old_sec == NULL || !r_reloc_is_defined (&r_rel)) + { + BFD_ASSERT (r_type == R_XTENSA_ASM_EXPAND); + /* Leave it be. Resolution will happen in a later stage. */ + } + else + { + sec = fix->target_sec; + rel->r_addend += ((sec->output_offset + fix->target_offset) + - (old_sec->output_offset + old_offset)); + } +} + + +static void +do_fix_for_final_link (rel, input_section, relocationp) + Elf_Internal_Rela *rel; + asection *input_section; + bfd_vma *relocationp; +{ + asection *sec; + int r_type = ELF32_R_TYPE (rel->r_info); + reloc_bfd_fix *fix_list; + reloc_bfd_fix *fix; + + if (r_type == R_XTENSA_NONE) + return; + + fix_list = (get_xtensa_relax_info (input_section))->fix_list; + if (fix_list == NULL) + return; + + fix = get_bfd_fix (fix_list, input_section, rel->r_offset, r_type); + if (fix == NULL) + return; + + sec = fix->target_sec; + *relocationp = (sec->output_section->vma + + sec->output_offset + + fix->target_offset - rel->r_addend); +} + + +/* Miscellaneous utility functions.... */ + +static asection * +elf_xtensa_get_plt_section (dynobj, chunk) + bfd *dynobj; + int chunk; +{ + char plt_name[10]; + + if (chunk == 0) + return bfd_get_section_by_name (dynobj, ".plt"); + + sprintf (plt_name, ".plt.%u", chunk); + return bfd_get_section_by_name (dynobj, plt_name); +} + + +static asection * +elf_xtensa_get_gotplt_section (dynobj, chunk) + bfd *dynobj; + int chunk; +{ + char got_name[14]; + + if (chunk == 0) + return bfd_get_section_by_name (dynobj, ".got.plt"); + + sprintf (got_name, ".got.plt.%u", chunk); + return bfd_get_section_by_name (dynobj, got_name); +} + + +/* Get the input section for a given symbol index. + If the symbol is: + . a section symbol, return the section; + . a common symbol, return the common section; + . an undefined symbol, return the undefined section; + . an indirect symbol, follow the links; + . an absolute value, return the absolute section. */ + +static asection * +get_elf_r_symndx_section (abfd, r_symndx) + bfd *abfd; + unsigned long r_symndx; +{ + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + asection *target_sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + Elf_Internal_Sym *isymbuf; + unsigned int section_index; + + isymbuf = retrieve_local_syms (abfd); + section_index = isymbuf[r_symndx].st_shndx; + + if (section_index == SHN_UNDEF) + target_sec = bfd_und_section_ptr; + else if (section_index > 0 && section_index < SHN_LORESERVE) + target_sec = bfd_section_from_elf_index (abfd, section_index); + else if (section_index == SHN_ABS) + target_sec = bfd_abs_section_ptr; + else if (section_index == SHN_COMMON) + target_sec = bfd_com_section_ptr; + else + /* Who knows? */ + target_sec = NULL; + } + else + { + unsigned long indx = r_symndx - symtab_hdr->sh_info; + struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + switch (h->root.type) + { + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + target_sec = h->root.u.def.section; + break; + case bfd_link_hash_common: + target_sec = bfd_com_section_ptr; + break; + case bfd_link_hash_undefined: + case bfd_link_hash_undefweak: + target_sec = bfd_und_section_ptr; + break; + default: /* New indirect warning. */ + target_sec = bfd_und_section_ptr; + break; + } + } + return target_sec; +} + + +static struct elf_link_hash_entry * +get_elf_r_symndx_hash_entry (abfd, r_symndx) + bfd *abfd; + unsigned long r_symndx; +{ + unsigned long indx; + struct elf_link_hash_entry *h; + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + if (r_symndx < symtab_hdr->sh_info) + return NULL; + + indx = r_symndx - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + return h; +} + + +/* Get the section-relative offset for a symbol number. */ + +static bfd_vma +get_elf_r_symndx_offset (abfd, r_symndx) + bfd *abfd; + unsigned long r_symndx; +{ + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + bfd_vma offset = 0; + + if (r_symndx < symtab_hdr->sh_info) + { + Elf_Internal_Sym *isymbuf; + isymbuf = retrieve_local_syms (abfd); + offset = isymbuf[r_symndx].st_value; + } + else + { + unsigned long indx = r_symndx - symtab_hdr->sh_info; + struct elf_link_hash_entry *h = + elf_sym_hashes (abfd)[indx]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + offset = h->root.u.def.value; + } + return offset; +} + + +static bfd_boolean +pcrel_reloc_fits (opnd, self_address, dest_address) + xtensa_operand opnd; + bfd_vma self_address; + bfd_vma dest_address; +{ + uint32 new_address = + xtensa_operand_do_reloc (opnd, dest_address, self_address); + return (xtensa_operand_encode (opnd, &new_address) + == xtensa_encode_result_ok); +} + + +static bfd_boolean +xtensa_is_property_section (sec) + asection *sec; +{ + static int len = sizeof (".gnu.linkonce.t.") - 1; + + return (strcmp (".xt.insn", sec->name) == 0 + || strcmp (".xt.lit", sec->name) == 0 + || strncmp (".gnu.linkonce.x.", sec->name, len) == 0 + || strncmp (".gnu.linkonce.p.", sec->name, len) == 0); +} + + +static bfd_boolean +is_literal_section (sec) + asection *sec; +{ + /* FIXME: the current definition of this leaves a lot to be desired.... */ + if (sec == NULL || sec->name == NULL) + return FALSE; + return (strstr (sec->name, "literal") != NULL); +} + + +static int +internal_reloc_compare (ap, bp) + const PTR ap; + const PTR bp; +{ + const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap; + const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp; + + return (a->r_offset - b->r_offset); +} + + +static bfd_boolean +get_is_linkonce_section (abfd, sec) + bfd *abfd ATTRIBUTE_UNUSED; + asection *sec; +{ + flagword flags, link_once_flags; + bfd_boolean is_linkonce = FALSE;; + + flags = bfd_get_section_flags (abfd, sec); + link_once_flags = (flags & SEC_LINK_ONCE); + if (link_once_flags != 0) + is_linkonce = TRUE; + + /* In order for this to be useful to the assembler + before the linkonce flag is set we need to + check for the GNU extension name. */ + if (!is_linkonce && + strncmp (sec->name, ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) == 0) + is_linkonce = TRUE; + + return is_linkonce; +} + + +char * +xtensa_get_property_section_name (abfd, sec, base_name) + bfd *abfd; + asection *sec; + const char * base_name; +{ + char *table_sec_name = NULL; + bfd_boolean is_linkonce; + + is_linkonce = get_is_linkonce_section (abfd, sec); + + if (!is_linkonce) + { + table_sec_name = strdup (base_name); + } + else + { + static size_t prefix_len = sizeof (".gnu.linkonce.t.") - 1; + size_t len = strlen (sec->name) + 1; + char repl_char = '\0'; + const char *segname = sec->name; + + if (strncmp (segname, ".gnu.linkonce.t.", prefix_len) == 0) + { + if (strcmp (base_name, ".xt.insn") == 0) + repl_char = 'x'; + else if (strcmp (base_name, ".xt.lit") == 0) + repl_char = 'p'; + } + + if (repl_char != '\0') + { + char *name = (char *) bfd_malloc (len); + memcpy (name, sec->name, len); + name[prefix_len - 2] = repl_char; + table_sec_name = name; + } + else + { + size_t base_len = strlen (base_name) + 1; + char *name = (char *) bfd_malloc (len + base_len); + memcpy (name, sec->name, len - 1); + memcpy (name + len - 1, base_name, base_len); + table_sec_name = name; + } + } + + return table_sec_name; +} + + +/* Other functions called directly by the linker. */ + +bfd_boolean +xtensa_callback_required_dependence (abfd, sec, link_info, callback, closure) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + deps_callback_t callback; + PTR closure; +{ + Elf_Internal_Rela *internal_relocs; + bfd_byte *contents; + unsigned i; + bfd_boolean ok = TRUE; + + /* ".plt*" sections have no explicit relocations but they contain L32R + instructions that reference the corresponding ".got.plt*" sections. */ + if ((sec->flags & SEC_LINKER_CREATED) != 0 + && strncmp (sec->name, ".plt", 4) == 0) + { + asection *sgotplt; + + /* Find the corresponding ".got.plt*" section. */ + if (sec->name[4] == '\0') + sgotplt = bfd_get_section_by_name (sec->owner, ".got.plt"); + else + { + char got_name[14]; + int chunk = 0; + + BFD_ASSERT (sec->name[4] == '.'); + chunk = strtol (&sec->name[5], NULL, 10); + + sprintf (got_name, ".got.plt.%u", chunk); + sgotplt = bfd_get_section_by_name (sec->owner, got_name); + } + BFD_ASSERT (sgotplt); + + /* Assume worst-case offsets: L32R at the very end of the ".plt" + section referencing a literal at the very beginning of + ".got.plt". This is very close to the real dependence, anyway. */ + (*callback) (sec, sec->_raw_size, sgotplt, 0, closure); + } + + internal_relocs = retrieve_internal_relocs (abfd, sec, + link_info->keep_memory); + if (internal_relocs == NULL + || sec->reloc_count == 0) + return ok; + + /* Cache the contents for the duration of this scan. */ + contents = retrieve_contents (abfd, sec, link_info->keep_memory); + if (contents == NULL && sec->_raw_size != 0) + { + ok = FALSE; + goto error_return; + } + + if (xtensa_default_isa == NULL) + xtensa_isa_init (); + + for (i = 0; i < sec->reloc_count; i++) + { + Elf_Internal_Rela *irel = &internal_relocs[i]; + if (is_l32r_relocation (sec, contents, irel)) + { + r_reloc l32r_rel; + asection *target_sec; + bfd_vma target_offset; + + r_reloc_init (&l32r_rel, abfd, irel); + target_sec = NULL; + target_offset = 0; + /* L32Rs must be local to the input file. */ + if (r_reloc_is_defined (&l32r_rel)) + { + target_sec = r_reloc_get_section (&l32r_rel); + target_offset = r_reloc_get_target_offset (&l32r_rel); + } + (*callback) (sec, irel->r_offset, target_sec, target_offset, + closure); + } + } + + error_return: + release_internal_relocs (sec, internal_relocs); + release_contents (sec, contents); + return ok; +} + + +#ifndef ELF_ARCH +#define TARGET_LITTLE_SYM bfd_elf32_xtensa_le_vec +#define TARGET_LITTLE_NAME "elf32-xtensa-le" +#define TARGET_BIG_SYM bfd_elf32_xtensa_be_vec +#define TARGET_BIG_NAME "elf32-xtensa-be" +#define ELF_ARCH bfd_arch_xtensa + +/* The new EM_XTENSA value will be recognized beginning in the Xtensa T1040 + release. However, we still have to generate files with the EM_XTENSA_OLD + value so that pre-T1040 tools can read the files. As soon as we stop + caring about pre-T1040 tools, the following two values should be + swapped. At the same time, any other code that uses EM_XTENSA_OLD + (e.g., prep_headers() in elf.c) should be changed to use EM_XTENSA. */ +#define ELF_MACHINE_CODE EM_XTENSA_OLD +#define ELF_MACHINE_ALT1 EM_XTENSA + +#if XCHAL_HAVE_MMU +#define ELF_MAXPAGESIZE (1 << XCHAL_MMU_MIN_PTE_PAGE_SIZE) +#else /* !XCHAL_HAVE_MMU */ +#define ELF_MAXPAGESIZE 1 +#endif /* !XCHAL_HAVE_MMU */ +#endif /* ELF_ARCH */ + +#define elf_backend_can_gc_sections 1 +#define elf_backend_can_refcount 1 +#define elf_backend_plt_readonly 1 +#define elf_backend_got_header_size 4 +#define elf_backend_want_dynbss 0 +#define elf_backend_want_got_plt 1 + +#define elf_info_to_howto elf_xtensa_info_to_howto_rela + +#define bfd_elf32_bfd_final_link bfd_elf32_bfd_final_link +#define bfd_elf32_bfd_merge_private_bfd_data elf_xtensa_merge_private_bfd_data +#define bfd_elf32_new_section_hook elf_xtensa_new_section_hook +#define bfd_elf32_bfd_print_private_bfd_data elf_xtensa_print_private_bfd_data +#define bfd_elf32_bfd_relax_section elf_xtensa_relax_section +#define bfd_elf32_bfd_reloc_type_lookup elf_xtensa_reloc_type_lookup +#define bfd_elf32_bfd_set_private_flags elf_xtensa_set_private_flags + +#define elf_backend_adjust_dynamic_symbol elf_xtensa_adjust_dynamic_symbol +#define elf_backend_check_relocs elf_xtensa_check_relocs +#define elf_backend_copy_indirect_symbol elf_xtensa_copy_indirect_symbol +#define elf_backend_create_dynamic_sections elf_xtensa_create_dynamic_sections +#define elf_backend_discard_info elf_xtensa_discard_info +#define elf_backend_ignore_discarded_relocs elf_xtensa_ignore_discarded_relocs +#define elf_backend_final_write_processing elf_xtensa_final_write_processing +#define elf_backend_finish_dynamic_sections elf_xtensa_finish_dynamic_sections +#define elf_backend_finish_dynamic_symbol elf_xtensa_finish_dynamic_symbol +#define elf_backend_gc_mark_hook elf_xtensa_gc_mark_hook +#define elf_backend_gc_sweep_hook elf_xtensa_gc_sweep_hook +#define elf_backend_grok_prstatus elf_xtensa_grok_prstatus +#define elf_backend_grok_psinfo elf_xtensa_grok_psinfo +#define elf_backend_hide_symbol elf_xtensa_hide_symbol +#define elf_backend_modify_segment_map elf_xtensa_modify_segment_map +#define elf_backend_object_p elf_xtensa_object_p +#define elf_backend_reloc_type_class elf_xtensa_reloc_type_class +#define elf_backend_relocate_section elf_xtensa_relocate_section +#define elf_backend_size_dynamic_sections elf_xtensa_size_dynamic_sections + +#include "elf32-target.h" diff --git a/bfd/libbfd.h b/bfd/libbfd.h index df2ba3e3f97..9bbdebd2184 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -1475,6 +1475,16 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_IQ2000_OFFSET_16", "BFD_RELOC_IQ2000_OFFSET_21", "BFD_RELOC_IQ2000_UHI16", + "BFD_RELOC_XTENSA_RTLD", + "BFD_RELOC_XTENSA_GLOB_DAT", + "BFD_RELOC_XTENSA_JMP_SLOT", + "BFD_RELOC_XTENSA_RELATIVE", + "BFD_RELOC_XTENSA_PLT", + "BFD_RELOC_XTENSA_OP0", + "BFD_RELOC_XTENSA_OP1", + "BFD_RELOC_XTENSA_OP2", + "BFD_RELOC_XTENSA_ASM_EXPAND", + "BFD_RELOC_XTENSA_ASM_SIMPLIFY", "@@overflow: BFD_RELOC_UNUSED@@", }; #endif diff --git a/bfd/reloc.c b/bfd/reloc.c index f4a33218022..aa2321fef77 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -3850,6 +3850,49 @@ ENUMX ENUMDOC IQ2000 Relocations. +ENUM + BFD_RELOC_XTENSA_RTLD +ENUMDOC + Special Xtensa relocation used only by PLT entries in ELF shared + objects to indicate that the runtime linker should set the value + to one of its own internal functions or data structures. +ENUM + BFD_RELOC_XTENSA_GLOB_DAT +ENUMX + BFD_RELOC_XTENSA_JMP_SLOT +ENUMX + BFD_RELOC_XTENSA_RELATIVE +ENUMDOC + Xtensa relocations for ELF shared objects. +ENUM + BFD_RELOC_XTENSA_PLT +ENUMDOC + Xtensa relocation used in ELF object files for symbols that may require + PLT entries. Otherwise, this is just a generic 32-bit relocation. +ENUM + BFD_RELOC_XTENSA_OP0 +ENUMX + BFD_RELOC_XTENSA_OP1 +ENUMX + BFD_RELOC_XTENSA_OP2 +ENUMDOC + Generic Xtensa relocations. Only the operand number is encoded + in the relocation. The details are determined by extracting the + instruction opcode. +ENUM + BFD_RELOC_XTENSA_ASM_EXPAND +ENUMDOC + Xtensa relocation to mark that the assembler expanded the + instructions from an original target. The expansion size is + encoded in the reloc size. +ENUM + BFD_RELOC_XTENSA_ASM_SIMPLIFY +ENUMDOC + Xtensa relocation to mark that the linker should simplify + assembler-expanded instructions. This is commonly used + internally by the linker after analysis of a + BFD_RELOC_XTENSA_ASM_EXPAND. + ENDSENUM BFD_RELOC_UNUSED CODE_FRAGMENT diff --git a/bfd/targets.c b/bfd/targets.c index 2211e124a7f..5ccae2a9928 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -579,6 +579,8 @@ extern const bfd_target bfd_elf32_us_cris_vec; extern const bfd_target bfd_elf32_v850_vec; extern const bfd_target bfd_elf32_vax_vec; extern const bfd_target bfd_elf32_xstormy16_vec; +extern const bfd_target bfd_elf32_xtensa_be_vec; +extern const bfd_target bfd_elf32_xtensa_le_vec; extern const bfd_target bfd_elf64_alpha_freebsd_vec; extern const bfd_target bfd_elf64_alpha_vec; extern const bfd_target bfd_elf64_big_generic_vec; @@ -871,6 +873,8 @@ static const bfd_target * const _bfd_target_vector[] = { &bfd_elf32_v850_vec, &bfd_elf32_vax_vec, &bfd_elf32_xstormy16_vec, + &bfd_elf32_xtensa_be_vec, + &bfd_elf32_xtensa_le_vec, #ifdef BFD64 &bfd_elf64_alpha_freebsd_vec, &bfd_elf64_alpha_vec, diff --git a/bfd/xtensa-isa.c b/bfd/xtensa-isa.c new file mode 100644 index 00000000000..ffbef53dfd4 --- /dev/null +++ b/bfd/xtensa-isa.c @@ -0,0 +1,593 @@ +/* Configurable Xtensa ISA support. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include + +#include "xtensa-isa.h" +#include "xtensa-isa-internal.h" + +xtensa_isa xtensa_default_isa = NULL; + +static int +opname_lookup_compare (const void *v1, const void *v2) +{ + opname_lookup_entry *e1 = (opname_lookup_entry *)v1; + opname_lookup_entry *e2 = (opname_lookup_entry *)v2; + + return strcmp (e1->key, e2->key); +} + + +xtensa_isa +xtensa_isa_init (void) +{ + xtensa_isa isa; + int mod; + + isa = xtensa_load_isa (0); + if (isa == 0) + { + fprintf (stderr, "Failed to initialize Xtensa base ISA module\n"); + return NULL; + } + + for (mod = 1; xtensa_isa_modules[mod].get_num_opcodes_fn; mod++) + { + if (!xtensa_extend_isa (isa, mod)) + { + fprintf (stderr, "Failed to initialize Xtensa TIE ISA module\n"); + return NULL; + } + } + + return isa; +} + +/* ISA information. */ + +static int +xtensa_check_isa_config (xtensa_isa_internal *isa, + struct config_struct *config_table) +{ + int i, j; + + if (!config_table) + { + fprintf (stderr, "Error: Empty configuration table in ISA DLL\n"); + return 0; + } + + /* For the first module, save a pointer to the table and record the + specified endianness and availability of the density option. */ + + if (isa->num_modules == 0) + { + int found_memory_order = 0; + + isa->config = config_table; + isa->has_density = 1; /* Default to have density option. */ + + for (i = 0; config_table[i].param_name; i++) + { + if (!strcmp (config_table[i].param_name, "IsaMemoryOrder")) + { + isa->is_big_endian = + (strcmp (config_table[i].param_value, "BigEndian") == 0); + found_memory_order = 1; + } + if (!strcmp (config_table[i].param_name, "IsaUseDensityInstruction")) + { + isa->has_density = atoi (config_table[i].param_value); + } + } + if (!found_memory_order) + { + fprintf (stderr, "Error: \"IsaMemoryOrder\" missing from " + "configuration table in ISA DLL\n"); + return 0; + } + + return 1; + } + + /* For subsequent modules, check that the parameters match. Note: This + code is sufficient to handle the current model where there are never + more than 2 modules; we might at some point want to handle cases where + module N > 0 specifies some parameters not included in the base table, + and we would then add those to isa->config so that subsequent modules + would check against them. */ + + for (i = 0; config_table[i].param_name; i++) + { + for (j = 0; isa->config[j].param_name; j++) + { + if (!strcmp (config_table[i].param_name, isa->config[j].param_name)) + { + int mismatch; + if (!strcmp (config_table[i].param_name, "IsaCoprocessorCount")) + { + /* Only require the coprocessor count to be <= the base. */ + int tiecnt = atoi (config_table[i].param_value); + int basecnt = atoi (isa->config[j].param_value); + mismatch = (tiecnt > basecnt); + } + else + mismatch = strcmp (config_table[i].param_value, + isa->config[j].param_value); + if (mismatch) + { +#define MISMATCH_MESSAGE \ +"Error: Configuration mismatch in the \"%s\" parameter:\n\ +the configuration used when the TIE file was compiled had a value of\n\ +\"%s\", while the current configuration has a value of\n\ +\"%s\". Please rerun the TIE compiler with a matching\n\ +configuration.\n" + fprintf (stderr, MISMATCH_MESSAGE, + config_table[i].param_name, + config_table[i].param_value, + isa->config[j].param_value); + return 0; + } + break; + } + } + } + + return 1; +} + + +static int +xtensa_add_isa (xtensa_isa_internal *isa, libisa_module_specifier libisa) +{ + const int (*get_num_opcodes_fn) (void); + struct config_struct *(*get_config_table_fn) (void); + xtensa_opcode_internal **(*get_opcodes_fn) (void); + int (*decode_insn_fn) (const xtensa_insnbuf); + xtensa_opcode_internal **opcodes; + int opc, insn_size, prev_num_opcodes, new_num_opcodes, this_module; + + get_num_opcodes_fn = xtensa_isa_modules[libisa].get_num_opcodes_fn; + get_opcodes_fn = xtensa_isa_modules[libisa].get_opcodes_fn; + decode_insn_fn = xtensa_isa_modules[libisa].decode_insn_fn; + get_config_table_fn = xtensa_isa_modules[libisa].get_config_table_fn; + + if (!get_num_opcodes_fn || !get_opcodes_fn || !decode_insn_fn + || (!get_config_table_fn && isa->num_modules == 0)) + return 0; + + if (get_config_table_fn + && !xtensa_check_isa_config (isa, get_config_table_fn ())) + return 0; + + prev_num_opcodes = isa->num_opcodes; + new_num_opcodes = (*get_num_opcodes_fn) (); + + isa->num_opcodes += new_num_opcodes; + isa->opcode_table = (xtensa_opcode_internal **) + realloc (isa->opcode_table, isa->num_opcodes * + sizeof (xtensa_opcode_internal *)); + isa->opname_lookup_table = (opname_lookup_entry *) + realloc (isa->opname_lookup_table, isa->num_opcodes * + sizeof (opname_lookup_entry)); + + opcodes = (*get_opcodes_fn) (); + + insn_size = isa->insn_size; + for (opc = 0; opc < new_num_opcodes; opc++) + { + xtensa_opcode_internal *intopc = opcodes[opc]; + int newopc = prev_num_opcodes + opc; + isa->opcode_table[newopc] = intopc; + isa->opname_lookup_table[newopc].key = intopc->name; + isa->opname_lookup_table[newopc].opcode = newopc; + if (intopc->length > insn_size) + insn_size = intopc->length; + } + + isa->insn_size = insn_size; + isa->insnbuf_size = ((isa->insn_size + sizeof (xtensa_insnbuf_word) - 1) / + sizeof (xtensa_insnbuf_word)); + + qsort (isa->opname_lookup_table, isa->num_opcodes, + sizeof (opname_lookup_entry), opname_lookup_compare); + + /* Check for duplicate opcode names. */ + for (opc = 1; opc < isa->num_opcodes; opc++) + { + if (!opname_lookup_compare (&isa->opname_lookup_table[opc-1], + &isa->opname_lookup_table[opc])) + { + fprintf (stderr, "Error: Duplicate TIE opcode \"%s\"\n", + isa->opname_lookup_table[opc].key); + return 0; + } + } + + this_module = isa->num_modules; + isa->num_modules += 1; + + isa->module_opcode_base = (int *) realloc (isa->module_opcode_base, + isa->num_modules * sizeof (int)); + isa->module_decode_fn = (xtensa_insn_decode_fn *) + realloc (isa->module_decode_fn, isa->num_modules * + sizeof (xtensa_insn_decode_fn)); + + isa->module_opcode_base[this_module] = prev_num_opcodes; + isa->module_decode_fn[this_module] = decode_insn_fn; + + xtensa_default_isa = isa; + + return 1; /* Library was successfully added. */ +} + + +xtensa_isa +xtensa_load_isa (libisa_module_specifier libisa) +{ + xtensa_isa_internal *isa; + + isa = (xtensa_isa_internal *) malloc (sizeof (xtensa_isa_internal)); + memset (isa, 0, sizeof (xtensa_isa_internal)); + if (!xtensa_add_isa (isa, libisa)) + { + xtensa_isa_free (isa); + return NULL; + } + return (xtensa_isa) isa; +} + + +int +xtensa_extend_isa (xtensa_isa isa, libisa_module_specifier libisa) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + return xtensa_add_isa (intisa, libisa); +} + + +void +xtensa_isa_free (xtensa_isa isa) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + if (intisa->opcode_table) + free (intisa->opcode_table); + if (intisa->opname_lookup_table) + free (intisa->opname_lookup_table); + if (intisa->module_opcode_base) + free (intisa->module_opcode_base); + if (intisa->module_decode_fn) + free (intisa->module_decode_fn); + free (intisa); +} + + +int +xtensa_insn_maxlength (xtensa_isa isa) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + return intisa->insn_size; +} + + +int +xtensa_insnbuf_size (xtensa_isa isa) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *)isa; + return intisa->insnbuf_size; +} + + +int +xtensa_num_opcodes (xtensa_isa isa) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + return intisa->num_opcodes; +} + + +xtensa_opcode +xtensa_opcode_lookup (xtensa_isa isa, const char *opname) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + opname_lookup_entry entry, *result; + + entry.key = opname; + result = bsearch (&entry, intisa->opname_lookup_table, intisa->num_opcodes, + sizeof (opname_lookup_entry), opname_lookup_compare); + if (!result) return XTENSA_UNDEFINED; + return result->opcode; +} + + +xtensa_opcode +xtensa_decode_insn (xtensa_isa isa, const xtensa_insnbuf insn) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + int n, opc; + for (n = 0; n < intisa->num_modules; n++) { + opc = (intisa->module_decode_fn[n]) (insn); + if (opc != XTENSA_UNDEFINED) + return intisa->module_opcode_base[n] + opc; + } + return XTENSA_UNDEFINED; +} + + +/* Opcode information. */ + +void +xtensa_encode_insn (xtensa_isa isa, xtensa_opcode opc, xtensa_insnbuf insn) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + xtensa_insnbuf template = intisa->opcode_table[opc]->template(); + int len = intisa->opcode_table[opc]->length; + int n; + + /* Convert length to 32-bit words. */ + len = (len + 3) / 4; + + /* Copy the template. */ + for (n = 0; n < len; n++) + insn[n] = template[n]; + + /* Fill any unused buffer space with zeros. */ + for ( ; n < intisa->insnbuf_size; n++) + insn[n] = 0; +} + + +const char * +xtensa_opcode_name (xtensa_isa isa, xtensa_opcode opc) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + return intisa->opcode_table[opc]->name; +} + + +int +xtensa_insn_length (xtensa_isa isa, xtensa_opcode opc) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + return intisa->opcode_table[opc]->length; +} + + +int +xtensa_insn_length_from_first_byte (xtensa_isa isa, char first_byte) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + int is_density = (first_byte & (intisa->is_big_endian ? 0x80 : 0x08)) != 0; + return (intisa->has_density && is_density ? 2 : 3); +} + + +int +xtensa_num_operands (xtensa_isa isa, xtensa_opcode opc) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + return intisa->opcode_table[opc]->iclass->num_operands; +} + + +xtensa_operand +xtensa_get_operand (xtensa_isa isa, xtensa_opcode opc, int opnd) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + xtensa_iclass_internal *iclass = intisa->opcode_table[opc]->iclass; + if (opnd >= iclass->num_operands) + return NULL; + return (xtensa_operand) iclass->operands[opnd]; +} + + +/* Operand information. */ + +char * +xtensa_operand_kind (xtensa_operand opnd) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return intop->operand_kind; +} + + +char +xtensa_operand_inout (xtensa_operand opnd) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return intop->inout; +} + + +uint32 +xtensa_operand_get_field (xtensa_operand opnd, const xtensa_insnbuf insn) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return (*intop->get_field) (insn); +} + + +void +xtensa_operand_set_field (xtensa_operand opnd, xtensa_insnbuf insn, uint32 val) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return (*intop->set_field) (insn, val); +} + + +xtensa_encode_result +xtensa_operand_encode (xtensa_operand opnd, uint32 *valp) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return (*intop->encode) (valp); +} + + +uint32 +xtensa_operand_decode (xtensa_operand opnd, uint32 val) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return (*intop->decode) (val); +} + + +int +xtensa_operand_isPCRelative (xtensa_operand opnd) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + return intop->isPCRelative; +} + + +uint32 +xtensa_operand_do_reloc (xtensa_operand opnd, uint32 addr, uint32 pc) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + if (!intop->isPCRelative) + return addr; + return (*intop->do_reloc) (addr, pc); +} + + +uint32 +xtensa_operand_undo_reloc (xtensa_operand opnd, uint32 offset, uint32 pc) +{ + xtensa_operand_internal *intop = (xtensa_operand_internal *) opnd; + if (!intop->isPCRelative) + return offset; + return (*intop->undo_reloc) (offset, pc); +} + + +/* Instruction buffers. */ + +xtensa_insnbuf +xtensa_insnbuf_alloc (xtensa_isa isa) +{ + return (xtensa_insnbuf) malloc (xtensa_insnbuf_size (isa) * + sizeof (xtensa_insnbuf_word)); +} + + +void +xtensa_insnbuf_free (xtensa_insnbuf buf) +{ + free( buf ); +} + + +/* Given , the index of a byte in a xtensa_insnbuf, our + internal representation of a xtensa instruction word, return the index of + its word and the bit index of its low order byte in the xtensa_insnbuf. */ + +static inline int +byte_to_word_index (int byte_index) +{ + return byte_index / sizeof (xtensa_insnbuf_word); +} + + +static inline int +byte_to_bit_index (int byte_index) +{ + return (byte_index & 0x3) * 8; +} + + +/* Copy an instruction in the 32 bit words pointed at by to characters + pointed at by . This is more complicated than you might think because + we want 16 bit instructions in bytes 2,3 for big endian. This function + allows us to specify which byte in to start with and which way to + increment, allowing trivial implementation for both big and little endian. + And it seems to make pretty good code for both. */ + +void +xtensa_insnbuf_to_chars (xtensa_isa isa, const xtensa_insnbuf insn, char *cp) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + int insn_size = xtensa_insn_maxlength (intisa); + int fence_post, start, increment, i, byte_count; + xtensa_opcode opc; + + if (intisa->is_big_endian) + { + start = insn_size - 1; + increment = -1; + } + else + { + start = 0; + increment = 1; + } + + /* Find the opcode; do nothing if the buffer does not contain a valid + instruction since we need to know how many bytes to copy. */ + opc = xtensa_decode_insn (isa, insn); + if (opc == XTENSA_UNDEFINED) + return; + + byte_count = xtensa_insn_length (isa, opc); + fence_post = start + (byte_count * increment); + + for (i = start; i != fence_post; i += increment, ++cp) + { + int word_inx = byte_to_word_index (i); + int bit_inx = byte_to_bit_index (i); + + *cp = (insn[word_inx] >> bit_inx) & 0xff; + } +} + +/* Inward conversion from byte stream to xtensa_insnbuf. See + xtensa_insnbuf_to_chars for a discussion of why this is + complicated by endianness. */ + +void +xtensa_insnbuf_from_chars (xtensa_isa isa, xtensa_insnbuf insn, const char* cp) +{ + xtensa_isa_internal *intisa = (xtensa_isa_internal *) isa; + int insn_size = xtensa_insn_maxlength (intisa); + int fence_post, start, increment, i; + + if (intisa->is_big_endian) + { + start = insn_size - 1; + increment = -1; + } + else + { + start = 0; + increment = 1; + } + + fence_post = start + (insn_size * increment); + memset (insn, 0, xtensa_insnbuf_size (isa) * sizeof (xtensa_insnbuf_word)); + + for ( i = start; i != fence_post; i += increment, ++cp ) + { + int word_inx = byte_to_word_index (i); + int bit_inx = byte_to_bit_index (i); + + insn[word_inx] |= (*cp & 0xff) << bit_inx; + } +} + diff --git a/bfd/xtensa-modules.c b/bfd/xtensa-modules.c new file mode 100644 index 00000000000..662f72860e1 --- /dev/null +++ b/bfd/xtensa-modules.c @@ -0,0 +1,6090 @@ +/* Xtensa configuration-specific ISA information. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Automatically generated by gen-opcode-code - DO NOT EDIT! */ + +#include +#include "xtensa-isa-internal.h" +#include "ansidecl.h" + +#define BPW 32 +#define WINDEX(_n) ((_n) / BPW) +#define BINDEX(_n) ((_n) %% BPW) + +static uint32 tie_do_reloc_l (uint32, uint32) ATTRIBUTE_UNUSED; +static uint32 tie_undo_reloc_l (uint32, uint32) ATTRIBUTE_UNUSED; + +static uint32 +tie_do_reloc_l (uint32 addr, uint32 pc) +{ + return (addr - pc); +} + +static uint32 +tie_undo_reloc_l (uint32 offset, uint32 pc) +{ + return (pc + offset); +} + +xtensa_opcode_internal** get_opcodes (void); +const int get_num_opcodes (void); +int decode_insn (const xtensa_insnbuf); +int interface_version (void); + +uint32 get_bbi_field (const xtensa_insnbuf); +void set_bbi_field (xtensa_insnbuf, uint32); +uint32 get_bbi4_field (const xtensa_insnbuf); +void set_bbi4_field (xtensa_insnbuf, uint32); +uint32 get_i_field (const xtensa_insnbuf); +void set_i_field (xtensa_insnbuf, uint32); +uint32 get_imm12_field (const xtensa_insnbuf); +void set_imm12_field (xtensa_insnbuf, uint32); +uint32 get_imm12b_field (const xtensa_insnbuf); +void set_imm12b_field (xtensa_insnbuf, uint32); +uint32 get_imm16_field (const xtensa_insnbuf); +void set_imm16_field (xtensa_insnbuf, uint32); +uint32 get_imm4_field (const xtensa_insnbuf); +void set_imm4_field (xtensa_insnbuf, uint32); +uint32 get_imm6_field (const xtensa_insnbuf); +void set_imm6_field (xtensa_insnbuf, uint32); +uint32 get_imm6hi_field (const xtensa_insnbuf); +void set_imm6hi_field (xtensa_insnbuf, uint32); +uint32 get_imm6lo_field (const xtensa_insnbuf); +void set_imm6lo_field (xtensa_insnbuf, uint32); +uint32 get_imm7_field (const xtensa_insnbuf); +void set_imm7_field (xtensa_insnbuf, uint32); +uint32 get_imm7hi_field (const xtensa_insnbuf); +void set_imm7hi_field (xtensa_insnbuf, uint32); +uint32 get_imm7lo_field (const xtensa_insnbuf); +void set_imm7lo_field (xtensa_insnbuf, uint32); +uint32 get_imm8_field (const xtensa_insnbuf); +void set_imm8_field (xtensa_insnbuf, uint32); +uint32 get_m_field (const xtensa_insnbuf); +void set_m_field (xtensa_insnbuf, uint32); +uint32 get_mn_field (const xtensa_insnbuf); +void set_mn_field (xtensa_insnbuf, uint32); +uint32 get_n_field (const xtensa_insnbuf); +void set_n_field (xtensa_insnbuf, uint32); +uint32 get_none_field (const xtensa_insnbuf); +void set_none_field (xtensa_insnbuf, uint32); +uint32 get_offset_field (const xtensa_insnbuf); +void set_offset_field (xtensa_insnbuf, uint32); +uint32 get_op0_field (const xtensa_insnbuf); +void set_op0_field (xtensa_insnbuf, uint32); +uint32 get_op1_field (const xtensa_insnbuf); +void set_op1_field (xtensa_insnbuf, uint32); +uint32 get_op2_field (const xtensa_insnbuf); +void set_op2_field (xtensa_insnbuf, uint32); +uint32 get_r_field (const xtensa_insnbuf); +void set_r_field (xtensa_insnbuf, uint32); +uint32 get_s_field (const xtensa_insnbuf); +void set_s_field (xtensa_insnbuf, uint32); +uint32 get_sa4_field (const xtensa_insnbuf); +void set_sa4_field (xtensa_insnbuf, uint32); +uint32 get_sae_field (const xtensa_insnbuf); +void set_sae_field (xtensa_insnbuf, uint32); +uint32 get_sae4_field (const xtensa_insnbuf); +void set_sae4_field (xtensa_insnbuf, uint32); +uint32 get_sal_field (const xtensa_insnbuf); +void set_sal_field (xtensa_insnbuf, uint32); +uint32 get_sar_field (const xtensa_insnbuf); +void set_sar_field (xtensa_insnbuf, uint32); +uint32 get_sas_field (const xtensa_insnbuf); +void set_sas_field (xtensa_insnbuf, uint32); +uint32 get_sas4_field (const xtensa_insnbuf); +void set_sas4_field (xtensa_insnbuf, uint32); +uint32 get_sr_field (const xtensa_insnbuf); +void set_sr_field (xtensa_insnbuf, uint32); +uint32 get_t_field (const xtensa_insnbuf); +void set_t_field (xtensa_insnbuf, uint32); +uint32 get_thi3_field (const xtensa_insnbuf); +void set_thi3_field (xtensa_insnbuf, uint32); +uint32 get_z_field (const xtensa_insnbuf); +void set_z_field (xtensa_insnbuf, uint32); + + +uint32 +get_bbi_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf0000) >> 16) | + ((insn[0] & 0x100) >> 4); +} + +void +set_bbi_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff0ffff) | ((val << 16) & 0xf0000); + insn[0] = (insn[0] & 0xfffffeff) | ((val << 4) & 0x100); +} + +uint32 +get_bbi4_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x100) >> 8); +} + +void +set_bbi4_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffffeff) | ((val << 8) & 0x100); +} + +uint32 +get_i_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x80000) >> 19); +} + +void +set_i_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff7ffff) | ((val << 19) & 0x80000); +} + +uint32 +get_imm12_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xfff)); +} + +void +set_imm12_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff000) | (val & 0xfff); +} + +uint32 +get_imm12b_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xff)) | + ((insn[0] & 0xf000) >> 4); +} + +void +set_imm12b_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffffff00) | (val & 0xff); + insn[0] = (insn[0] & 0xffff0fff) | ((val << 4) & 0xf000); +} + +uint32 +get_imm16_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xffff)); +} + +void +set_imm16_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffff0000) | (val & 0xffff); +} + +uint32 +get_imm4_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8); +} + +void +set_imm4_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); +} + +uint32 +get_imm6_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8) | + ((insn[0] & 0x30000) >> 12); +} + +void +set_imm6_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); + insn[0] = (insn[0] & 0xfffcffff) | ((val << 12) & 0x30000); +} + +uint32 +get_imm6hi_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x30000) >> 16); +} + +void +set_imm6hi_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffcffff) | ((val << 16) & 0x30000); +} + +uint32 +get_imm6lo_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8); +} + +void +set_imm6lo_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); +} + +uint32 +get_imm7_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8) | + ((insn[0] & 0x70000) >> 12); +} + +void +set_imm7_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); + insn[0] = (insn[0] & 0xfff8ffff) | ((val << 12) & 0x70000); +} + +uint32 +get_imm7hi_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x70000) >> 16); +} + +void +set_imm7hi_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff8ffff) | ((val << 16) & 0x70000); +} + +uint32 +get_imm7lo_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8); +} + +void +set_imm7lo_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); +} + +uint32 +get_imm8_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xff)); +} + +void +set_imm8_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffffff00) | (val & 0xff); +} + +uint32 +get_m_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x30000) >> 16); +} + +void +set_m_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffcffff) | ((val << 16) & 0x30000); +} + +uint32 +get_mn_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x30000) >> 16) | + ((insn[0] & 0xc0000) >> 16); +} + +void +set_mn_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffcffff) | ((val << 16) & 0x30000); + insn[0] = (insn[0] & 0xfff3ffff) | ((val << 16) & 0xc0000); +} + +uint32 +get_n_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xc0000) >> 18); +} + +void +set_n_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff3ffff) | ((val << 18) & 0xc0000); +} + +uint32 +get_none_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x0)); +} + +void +set_none_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffffffff) | (val & 0x0); +} + +uint32 +get_offset_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x3ffff)); +} + +void +set_offset_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffc0000) | (val & 0x3ffff); +} + +uint32 +get_op0_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00000) >> 20); +} + +void +set_op0_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xff0fffff) | ((val << 20) & 0xf00000); +} + +uint32 +get_op1_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf0) >> 4); +} + +void +set_op1_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffffff0f) | ((val << 4) & 0xf0); +} + +uint32 +get_op2_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf)); +} + +void +set_op2_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffffff0) | (val & 0xf); +} + +uint32 +get_r_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8); +} + +void +set_r_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); +} + +uint32 +get_s_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf000) >> 12); +} + +void +set_s_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffff0fff) | ((val << 12) & 0xf000); +} + +uint32 +get_sa4_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x1)); +} + +void +set_sa4_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffffffe) | (val & 0x1); +} + +uint32 +get_sae_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf000) >> 12) | + ((insn[0] & 0x10)); +} + +void +set_sae_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffff0fff) | ((val << 12) & 0xf000); + insn[0] = (insn[0] & 0xffffffef) | (val & 0x10); +} + +uint32 +get_sae4_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x10) >> 4); +} + +void +set_sae4_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffffffef) | ((val << 4) & 0x10); +} + +uint32 +get_sal_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf0000) >> 16) | + ((insn[0] & 0x1) << 4); +} + +void +set_sal_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff0ffff) | ((val << 16) & 0xf0000); + insn[0] = (insn[0] & 0xfffffffe) | ((val >> 4) & 0x1); +} + +uint32 +get_sar_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf000) >> 12) | + ((insn[0] & 0x1) << 4); +} + +void +set_sar_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffff0fff) | ((val << 12) & 0xf000); + insn[0] = (insn[0] & 0xfffffffe) | ((val >> 4) & 0x1); +} + +uint32 +get_sas_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf000) >> 12) | + ((insn[0] & 0x10000) >> 12); +} + +void +set_sas_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xffff0fff) | ((val << 12) & 0xf000); + insn[0] = (insn[0] & 0xfffeffff) | ((val << 12) & 0x10000); +} + +uint32 +get_sas4_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x10000) >> 16); +} + +void +set_sas4_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffeffff) | ((val << 16) & 0x10000); +} + +uint32 +get_sr_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf00) >> 8) | + ((insn[0] & 0xf000) >> 8); +} + +void +set_sr_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffff0ff) | ((val << 8) & 0xf00); + insn[0] = (insn[0] & 0xffff0fff) | ((val << 8) & 0xf000); +} + +uint32 +get_t_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xf0000) >> 16); +} + +void +set_t_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff0ffff) | ((val << 16) & 0xf0000); +} + +uint32 +get_thi3_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0xe0000) >> 17); +} + +void +set_thi3_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfff1ffff) | ((val << 17) & 0xe0000); +} + +uint32 +get_z_field (const xtensa_insnbuf insn) +{ + return ((insn[0] & 0x40000) >> 18); +} + +void +set_z_field (xtensa_insnbuf insn, uint32 val) +{ + insn[0] = (insn[0] & 0xfffbffff) | ((val << 18) & 0x40000); +} + +uint32 decode_b4constu (uint32); +xtensa_encode_result encode_b4constu (uint32 *); +uint32 decode_simm8x256 (uint32); +xtensa_encode_result encode_simm8x256 (uint32 *); +uint32 decode_soffset (uint32); +xtensa_encode_result encode_soffset (uint32 *); +uint32 decode_imm4 (uint32); +xtensa_encode_result encode_imm4 (uint32 *); +uint32 decode_op0 (uint32); +xtensa_encode_result encode_op0 (uint32 *); +uint32 decode_op1 (uint32); +xtensa_encode_result encode_op1 (uint32 *); +uint32 decode_imm6 (uint32); +xtensa_encode_result encode_imm6 (uint32 *); +uint32 decode_op2 (uint32); +xtensa_encode_result encode_op2 (uint32 *); +uint32 decode_imm7 (uint32); +xtensa_encode_result encode_imm7 (uint32 *); +uint32 decode_simm4 (uint32); +xtensa_encode_result encode_simm4 (uint32 *); +uint32 decode_ai4const (uint32); +xtensa_encode_result encode_ai4const (uint32 *); +uint32 decode_imm8 (uint32); +xtensa_encode_result encode_imm8 (uint32 *); +uint32 decode_sae (uint32); +xtensa_encode_result encode_sae (uint32 *); +uint32 decode_imm7lo (uint32); +xtensa_encode_result encode_imm7lo (uint32 *); +uint32 decode_simm7 (uint32); +xtensa_encode_result encode_simm7 (uint32 *); +uint32 decode_simm8 (uint32); +xtensa_encode_result encode_simm8 (uint32 *); +uint32 decode_uimm12x8 (uint32); +xtensa_encode_result encode_uimm12x8 (uint32 *); +uint32 decode_sal (uint32); +xtensa_encode_result encode_sal (uint32 *); +uint32 decode_uimm6 (uint32); +xtensa_encode_result encode_uimm6 (uint32 *); +uint32 decode_sas4 (uint32); +xtensa_encode_result encode_sas4 (uint32 *); +uint32 decode_uimm8 (uint32); +xtensa_encode_result encode_uimm8 (uint32 *); +uint32 decode_uimm16x4 (uint32); +xtensa_encode_result encode_uimm16x4 (uint32 *); +uint32 decode_sar (uint32); +xtensa_encode_result encode_sar (uint32 *); +uint32 decode_sa4 (uint32); +xtensa_encode_result encode_sa4 (uint32 *); +uint32 decode_sas (uint32); +xtensa_encode_result encode_sas (uint32 *); +uint32 decode_imm6hi (uint32); +xtensa_encode_result encode_imm6hi (uint32 *); +uint32 decode_bbi (uint32); +xtensa_encode_result encode_bbi (uint32 *); +uint32 decode_uimm8x2 (uint32); +xtensa_encode_result encode_uimm8x2 (uint32 *); +uint32 decode_uimm8x4 (uint32); +xtensa_encode_result encode_uimm8x4 (uint32 *); +uint32 decode_msalp32 (uint32); +xtensa_encode_result encode_msalp32 (uint32 *); +uint32 decode_bbi4 (uint32); +xtensa_encode_result encode_bbi4 (uint32 *); +uint32 decode_op2p1 (uint32); +xtensa_encode_result encode_op2p1 (uint32 *); +uint32 decode_soffsetx4 (uint32); +xtensa_encode_result encode_soffsetx4 (uint32 *); +uint32 decode_imm6lo (uint32); +xtensa_encode_result encode_imm6lo (uint32 *); +uint32 decode_imm12 (uint32); +xtensa_encode_result encode_imm12 (uint32 *); +uint32 decode_b4const (uint32); +xtensa_encode_result encode_b4const (uint32 *); +uint32 decode_i (uint32); +xtensa_encode_result encode_i (uint32 *); +uint32 decode_imm16 (uint32); +xtensa_encode_result encode_imm16 (uint32 *); +uint32 decode_mn (uint32); +xtensa_encode_result encode_mn (uint32 *); +uint32 decode_m (uint32); +xtensa_encode_result encode_m (uint32 *); +uint32 decode_n (uint32); +xtensa_encode_result encode_n (uint32 *); +uint32 decode_none (uint32); +xtensa_encode_result encode_none (uint32 *); +uint32 decode_imm12b (uint32); +xtensa_encode_result encode_imm12b (uint32 *); +uint32 decode_r (uint32); +xtensa_encode_result encode_r (uint32 *); +uint32 decode_s (uint32); +xtensa_encode_result encode_s (uint32 *); +uint32 decode_t (uint32); +xtensa_encode_result encode_t (uint32 *); +uint32 decode_thi3 (uint32); +xtensa_encode_result encode_thi3 (uint32 *); +uint32 decode_sae4 (uint32); +xtensa_encode_result encode_sae4 (uint32 *); +uint32 decode_offset (uint32); +xtensa_encode_result encode_offset (uint32 *); +uint32 decode_imm7hi (uint32); +xtensa_encode_result encode_imm7hi (uint32 *); +uint32 decode_uimm4x16 (uint32); +xtensa_encode_result encode_uimm4x16 (uint32 *); +uint32 decode_simm12b (uint32); +xtensa_encode_result encode_simm12b (uint32 *); +uint32 decode_lsi4x4 (uint32); +xtensa_encode_result encode_lsi4x4 (uint32 *); +uint32 decode_z (uint32); +xtensa_encode_result encode_z (uint32 *); +uint32 decode_simm12 (uint32); +xtensa_encode_result encode_simm12 (uint32 *); +uint32 decode_sr (uint32); +xtensa_encode_result encode_sr (uint32 *); +uint32 decode_nimm4x2 (uint32); +xtensa_encode_result encode_nimm4x2 (uint32 *); + + +static const uint32 b4constu_table[] = { + 32768, + 65536, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 10, + 12, + 16, + 32, + 64, + 128, + 256 +}; + +uint32 +decode_b4constu (uint32 val) +{ + val = b4constu_table[val]; + return val; +} + +xtensa_encode_result +encode_b4constu (uint32 *valp) +{ + uint32 val = *valp; + unsigned i; + for (i = 0; i < (1 << 4); i += 1) + if (b4constu_table[i] == val) goto found; + return xtensa_encode_result_not_in_table; + found: + val = i; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_simm8x256 (uint32 val) +{ + val = (val ^ 0x80) - 0x80; + val <<= 8; + return val; +} + +xtensa_encode_result +encode_simm8x256 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 8) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 8; + if (((val + (1 << 7)) >> 8) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_soffset (uint32 val) +{ + val = (val ^ 0x20000) - 0x20000; + return val; +} + +xtensa_encode_result +encode_soffset (uint32 *valp) +{ + uint32 val = *valp; + if (((val + (1 << 17)) >> 18) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm4 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_op0 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_op0 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_op1 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_op1 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm6 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm6 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 6) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_op2 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_op2 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm7 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm7 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 7) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_simm4 (uint32 val) +{ + val = (val ^ 0x8) - 0x8; + return val; +} + +xtensa_encode_result +encode_simm4 (uint32 *valp) +{ + uint32 val = *valp; + if (((val + (1 << 3)) >> 4) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +static const uint32 ai4const_table[] = { + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15 +}; + +uint32 +decode_ai4const (uint32 val) +{ + val = ai4const_table[val]; + return val; +} + +xtensa_encode_result +encode_ai4const (uint32 *valp) +{ + uint32 val = *valp; + unsigned i; + for (i = 0; i < (1 << 4); i += 1) + if (ai4const_table[i] == val) goto found; + return xtensa_encode_result_not_in_table; + found: + val = i; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm8 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm8 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 8) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sae (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sae (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 5) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm7lo (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm7lo (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_simm7 (uint32 val) +{ + if (val > 95) + val |= -32; + return val; +} + +xtensa_encode_result +encode_simm7 (uint32 *valp) +{ + uint32 val = *valp; + if ((signed int) val < -32) + return xtensa_encode_result_too_low; + if ((signed int) val > 95) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_simm8 (uint32 val) +{ + val = (val ^ 0x80) - 0x80; + return val; +} + +xtensa_encode_result +encode_simm8 (uint32 *valp) +{ + uint32 val = *valp; + if (((val + (1 << 7)) >> 8) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm12x8 (uint32 val) +{ + val <<= 3; + return val; +} + +xtensa_encode_result +encode_uimm12x8 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 3) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 3; + if ((val >> 12) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sal (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sal (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 5) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm6 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_uimm6 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 6) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sas4 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sas4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 1) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm8 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_uimm8 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 8) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm16x4 (uint32 val) +{ + val |= -1 << 16; + val <<= 2; + return val; +} + +xtensa_encode_result +encode_uimm16x4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 2) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 2; + if ((signed int) val >> 16 != -1) + { + if ((signed int) val >= 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sar (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sar (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 5) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sa4 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sa4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 1) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sas (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sas (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 5) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm6hi (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm6hi (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 2) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_bbi (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_bbi (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 5) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm8x2 (uint32 val) +{ + val <<= 1; + return val; +} + +xtensa_encode_result +encode_uimm8x2 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 1) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 1; + if ((val >> 8) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm8x4 (uint32 val) +{ + val <<= 2; + return val; +} + +xtensa_encode_result +encode_uimm8x4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 2) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 2; + if ((val >> 8) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +static const uint32 mip32const_table[] = { + 32, + 31, + 30, + 29, + 28, + 27, + 26, + 25, + 24, + 23, + 22, + 21, + 20, + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 10, + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1 +}; + +uint32 +decode_msalp32 (uint32 val) +{ + val = mip32const_table[val]; + return val; +} + +xtensa_encode_result +encode_msalp32 (uint32 *valp) +{ + uint32 val = *valp; + unsigned i; + for (i = 0; i < (1 << 5); i += 1) + if (mip32const_table[i] == val) goto found; + return xtensa_encode_result_not_in_table; + found: + val = i; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_bbi4 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_bbi4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 1) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +static const uint32 i4p1const_table[] = { + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 +}; + +uint32 +decode_op2p1 (uint32 val) +{ + val = i4p1const_table[val]; + return val; +} + +xtensa_encode_result +encode_op2p1 (uint32 *valp) +{ + uint32 val = *valp; + unsigned i; + for (i = 0; i < (1 << 4); i += 1) + if (i4p1const_table[i] == val) goto found; + return xtensa_encode_result_not_in_table; + found: + val = i; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_soffsetx4 (uint32 val) +{ + val = (val ^ 0x20000) - 0x20000; + val <<= 2; + return val; +} + +xtensa_encode_result +encode_soffsetx4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 2) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 2; + if (((val + (1 << 17)) >> 18) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm6lo (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm6lo (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm12 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm12 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 12) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +static const uint32 b4const_table[] = { + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 10, + 12, + 16, + 32, + 64, + 128, + 256 +}; + +uint32 +decode_b4const (uint32 val) +{ + val = b4const_table[val]; + return val; +} + +xtensa_encode_result +encode_b4const (uint32 *valp) +{ + uint32 val = *valp; + unsigned i; + for (i = 0; i < (1 << 4); i += 1) + if (b4const_table[i] == val) goto found; + return xtensa_encode_result_not_in_table; + found: + val = i; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_i (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_i (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 1) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm16 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm16 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 16) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_mn (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_mn (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_m (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_m (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 2) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_n (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_n (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 2) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_none (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_none (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 0) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm12b (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm12b (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 12) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_r (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_r (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_s (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_s (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_t (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_t (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_thi3 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_thi3 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 3) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sae4 (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sae4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 1) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_offset (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_offset (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 18) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_imm7hi (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_imm7hi (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 3) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_uimm4x16 (uint32 val) +{ + val <<= 4; + return val; +} + +xtensa_encode_result +encode_uimm4x16 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 4) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 4; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_simm12b (uint32 val) +{ + val = (val ^ 0x800) - 0x800; + return val; +} + +xtensa_encode_result +encode_simm12b (uint32 *valp) +{ + uint32 val = *valp; + if (((val + (1 << 11)) >> 12) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_lsi4x4 (uint32 val) +{ + val <<= 2; + return val; +} + +xtensa_encode_result +encode_lsi4x4 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 2) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 2; + if ((val >> 4) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_z (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_z (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 1) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_simm12 (uint32 val) +{ + val = (val ^ 0x800) - 0x800; + return val; +} + +xtensa_encode_result +encode_simm12 (uint32 *valp) +{ + uint32 val = *valp; + if (((val + (1 << 11)) >> 12) != 0) + { + if ((signed int) val > 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_sr (uint32 val) +{ + return val; +} + +xtensa_encode_result +encode_sr (uint32 *valp) +{ + uint32 val = *valp; + if ((val >> 8) != 0) + return xtensa_encode_result_too_high; + *valp = val; + return xtensa_encode_result_ok; +} + +uint32 +decode_nimm4x2 (uint32 val) +{ + val |= -1 << 4; + val <<= 2; + return val; +} + +xtensa_encode_result +encode_nimm4x2 (uint32 *valp) +{ + uint32 val = *valp; + if ((val & ((1 << 2) - 1)) != 0) + return xtensa_encode_result_align; + val = (signed int) val >> 2; + if ((signed int) val >> 4 != -1) + { + if ((signed int) val >= 0) + return xtensa_encode_result_too_high; + else + return xtensa_encode_result_too_low; + } + *valp = val; + return xtensa_encode_result_ok; +} + + + +uint32 do_reloc_l (uint32, uint32); +uint32 undo_reloc_l (uint32, uint32); +uint32 do_reloc_L (uint32, uint32); +uint32 undo_reloc_L (uint32, uint32); +uint32 do_reloc_r (uint32, uint32); +uint32 undo_reloc_r (uint32, uint32); + + +uint32 +do_reloc_l (uint32 addr, uint32 pc) +{ + return addr - pc - 4; +} + +uint32 +undo_reloc_l (uint32 offset, uint32 pc) +{ + return pc + offset + 4; +} + +uint32 +do_reloc_L (uint32 addr, uint32 pc) +{ + return addr - (pc & -4) - 4; +} + +uint32 +undo_reloc_L (uint32 offset, uint32 pc) +{ + return (pc & -4) + offset + 4; +} + +uint32 +do_reloc_r (uint32 addr, uint32 pc) +{ + return addr - ((pc+3) & -4); +} + +uint32 +undo_reloc_r (uint32 offset, uint32 pc) +{ + return ((pc+3) & -4) + offset; +} + +static xtensa_operand_internal iib4const_operand = { + "i", + '<', + 0, + get_r_field, + set_r_field, + encode_b4const, + decode_b4const, + 0, + 0 +}; + +static xtensa_operand_internal iiuimm8_operand = { + "i", + '<', + 0, + get_imm8_field, + set_imm8_field, + encode_uimm8, + decode_uimm8, + 0, + 0 +}; + +static xtensa_operand_internal lisoffsetx4_operand = { + "L", + '<', + 1, + get_offset_field, + set_offset_field, + encode_soffsetx4, + decode_soffsetx4, + do_reloc_L, + undo_reloc_L, +}; + +static xtensa_operand_internal iisimm8x256_operand = { + "i", + '<', + 0, + get_imm8_field, + set_imm8_field, + encode_simm8x256, + decode_simm8x256, + 0, + 0 +}; + +static xtensa_operand_internal lisimm12_operand = { + "l", + '<', + 1, + get_imm12_field, + set_imm12_field, + encode_simm12, + decode_simm12, + do_reloc_l, + undo_reloc_l, +}; + +static xtensa_operand_internal iiop2p1_operand = { + "i", + '<', + 0, + get_op2_field, + set_op2_field, + encode_op2p1, + decode_op2p1, + 0, + 0 +}; + +static xtensa_operand_internal iisae_operand = { + "i", + '<', + 0, + get_sae_field, + set_sae_field, + encode_sae, + decode_sae, + 0, + 0 +}; + +static xtensa_operand_internal iis_operand = { + "i", + '<', + 0, + get_s_field, + set_s_field, + encode_s, + decode_s, + 0, + 0 +}; + +static xtensa_operand_internal iit_operand = { + "i", + '<', + 0, + get_t_field, + set_t_field, + encode_t, + decode_t, + 0, + 0 +}; + +static xtensa_operand_internal iisimm12b_operand = { + "i", + '<', + 0, + get_imm12b_field, + set_imm12b_field, + encode_simm12b, + decode_simm12b, + 0, + 0 +}; + +static xtensa_operand_internal iinimm4x2_operand = { + "i", + '<', + 0, + get_imm4_field, + set_imm4_field, + encode_nimm4x2, + decode_nimm4x2, + 0, + 0 +}; + +static xtensa_operand_internal iiuimm4x16_operand = { + "i", + '<', + 0, + get_op2_field, + set_op2_field, + encode_uimm4x16, + decode_uimm4x16, + 0, + 0 +}; + +static xtensa_operand_internal abs_operand = { + "a", + '=', + 0, + get_s_field, + set_s_field, + encode_s, + decode_s, + 0, + 0 +}; + +static xtensa_operand_internal iisar_operand = { + "i", + '<', + 0, + get_sar_field, + set_sar_field, + encode_sar, + decode_sar, + 0, + 0 +}; + +static xtensa_operand_internal abt_operand = { + "a", + '=', + 0, + get_t_field, + set_t_field, + encode_t, + decode_t, + 0, + 0 +}; + +static xtensa_operand_internal iisas_operand = { + "i", + '<', + 0, + get_sas_field, + set_sas_field, + encode_sas, + decode_sas, + 0, + 0 +}; + +static xtensa_operand_internal amr_operand = { + "a", + '=', + 0, + get_r_field, + set_r_field, + encode_r, + decode_r, + 0, + 0 +}; + +static xtensa_operand_internal iib4constu_operand = { + "i", + '<', + 0, + get_r_field, + set_r_field, + encode_b4constu, + decode_b4constu, + 0, + 0 +}; + +static xtensa_operand_internal iisr_operand = { + "i", + '<', + 0, + get_sr_field, + set_sr_field, + encode_sr, + decode_sr, + 0, + 0 +}; + +static xtensa_operand_internal iibbi_operand = { + "i", + '<', + 0, + get_bbi_field, + set_bbi_field, + encode_bbi, + decode_bbi, + 0, + 0 +}; + +static xtensa_operand_internal iiai4const_operand = { + "i", + '<', + 0, + get_t_field, + set_t_field, + encode_ai4const, + decode_ai4const, + 0, + 0 +}; + +static xtensa_operand_internal iiuimm12x8_operand = { + "i", + '<', + 0, + get_imm12_field, + set_imm12_field, + encode_uimm12x8, + decode_uimm12x8, + 0, + 0 +}; + +static xtensa_operand_internal riuimm16x4_operand = { + "r", + '<', + 1, + get_imm16_field, + set_imm16_field, + encode_uimm16x4, + decode_uimm16x4, + do_reloc_r, + undo_reloc_r, +}; + +static xtensa_operand_internal lisimm8_operand = { + "l", + '<', + 1, + get_imm8_field, + set_imm8_field, + encode_simm8, + decode_simm8, + do_reloc_l, + undo_reloc_l, +}; + +static xtensa_operand_internal iilsi4x4_operand = { + "i", + '<', + 0, + get_r_field, + set_r_field, + encode_lsi4x4, + decode_lsi4x4, + 0, + 0 +}; + +static xtensa_operand_internal iiuimm8x2_operand = { + "i", + '<', + 0, + get_imm8_field, + set_imm8_field, + encode_uimm8x2, + decode_uimm8x2, + 0, + 0 +}; + +static xtensa_operand_internal iisimm4_operand = { + "i", + '<', + 0, + get_mn_field, + set_mn_field, + encode_simm4, + decode_simm4, + 0, + 0 +}; + +static xtensa_operand_internal iimsalp32_operand = { + "i", + '<', + 0, + get_sal_field, + set_sal_field, + encode_msalp32, + decode_msalp32, + 0, + 0 +}; + +static xtensa_operand_internal liuimm6_operand = { + "l", + '<', + 1, + get_imm6_field, + set_imm6_field, + encode_uimm6, + decode_uimm6, + do_reloc_l, + undo_reloc_l, +}; + +static xtensa_operand_internal iiuimm8x4_operand = { + "i", + '<', + 0, + get_imm8_field, + set_imm8_field, + encode_uimm8x4, + decode_uimm8x4, + 0, + 0 +}; + +static xtensa_operand_internal lisoffset_operand = { + "l", + '<', + 1, + get_offset_field, + set_offset_field, + encode_soffset, + decode_soffset, + do_reloc_l, + undo_reloc_l, +}; + +static xtensa_operand_internal iisimm7_operand = { + "i", + '<', + 0, + get_imm7_field, + set_imm7_field, + encode_simm7, + decode_simm7, + 0, + 0 +}; + +static xtensa_operand_internal ais_operand = { + "a", + '<', + 0, + get_s_field, + set_s_field, + encode_s, + decode_s, + 0, + 0 +}; + +static xtensa_operand_internal liuimm8_operand = { + "l", + '<', + 1, + get_imm8_field, + set_imm8_field, + encode_uimm8, + decode_uimm8, + do_reloc_l, + undo_reloc_l, +}; + +static xtensa_operand_internal ait_operand = { + "a", + '<', + 0, + get_t_field, + set_t_field, + encode_t, + decode_t, + 0, + 0 +}; + +static xtensa_operand_internal iisimm8_operand = { + "i", + '<', + 0, + get_imm8_field, + set_imm8_field, + encode_simm8, + decode_simm8, + 0, + 0 +}; + +static xtensa_operand_internal aor_operand = { + "a", + '>', + 0, + get_r_field, + set_r_field, + encode_r, + decode_r, + 0, + 0 +}; + +static xtensa_operand_internal aos_operand = { + "a", + '>', + 0, + get_s_field, + set_s_field, + encode_s, + decode_s, + 0, + 0 +}; + +static xtensa_operand_internal aot_operand = { + "a", + '>', + 0, + get_t_field, + set_t_field, + encode_t, + decode_t, + 0, + 0 +}; + +static xtensa_iclass_internal nopn_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *movi_operand_list[] = { + &aot_operand, + &iisimm12b_operand +}; + +static xtensa_iclass_internal movi_iclass = { + 2, + &movi_operand_list[0] +}; + +static xtensa_operand_internal *bsi8u_operand_list[] = { + &ais_operand, + &iib4constu_operand, + &lisimm8_operand +}; + +static xtensa_iclass_internal bsi8u_iclass = { + 3, + &bsi8u_operand_list[0] +}; + +static xtensa_operand_internal *itlb_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal itlb_iclass = { + 1, + &itlb_operand_list[0] +}; + +static xtensa_operand_internal *shiftst_operand_list[] = { + &aor_operand, + &ais_operand, + &ait_operand +}; + +static xtensa_iclass_internal shiftst_iclass = { + 3, + &shiftst_operand_list[0] +}; + +static xtensa_operand_internal *l32r_operand_list[] = { + &aot_operand, + &riuimm16x4_operand +}; + +static xtensa_iclass_internal l32r_iclass = { + 2, + &l32r_operand_list[0] +}; + +static xtensa_iclass_internal rfe_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *wait_operand_list[] = { + &iis_operand +}; + +static xtensa_iclass_internal wait_iclass = { + 1, + &wait_operand_list[0] +}; + +static xtensa_operand_internal *rfi_operand_list[] = { + &iis_operand +}; + +static xtensa_iclass_internal rfi_iclass = { + 1, + &rfi_operand_list[0] +}; + +static xtensa_operand_internal *movz_operand_list[] = { + &amr_operand, + &ais_operand, + &ait_operand +}; + +static xtensa_iclass_internal movz_iclass = { + 3, + &movz_operand_list[0] +}; + +static xtensa_operand_internal *callx_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal callx_iclass = { + 1, + &callx_operand_list[0] +}; + +static xtensa_operand_internal *mov_n_operand_list[] = { + &aot_operand, + &ais_operand +}; + +static xtensa_iclass_internal mov_n_iclass = { + 2, + &mov_n_operand_list[0] +}; + +static xtensa_operand_internal *loadi4_operand_list[] = { + &aot_operand, + &ais_operand, + &iilsi4x4_operand +}; + +static xtensa_iclass_internal loadi4_iclass = { + 3, + &loadi4_operand_list[0] +}; + +static xtensa_operand_internal *exti_operand_list[] = { + &aor_operand, + &ait_operand, + &iisae_operand, + &iiop2p1_operand +}; + +static xtensa_iclass_internal exti_iclass = { + 4, + &exti_operand_list[0] +}; + +static xtensa_operand_internal *break_operand_list[] = { + &iis_operand, + &iit_operand +}; + +static xtensa_iclass_internal break_iclass = { + 2, + &break_operand_list[0] +}; + +static xtensa_operand_internal *slli_operand_list[] = { + &aor_operand, + &ais_operand, + &iimsalp32_operand +}; + +static xtensa_iclass_internal slli_iclass = { + 3, + &slli_operand_list[0] +}; + +static xtensa_operand_internal *s16i_operand_list[] = { + &ait_operand, + &ais_operand, + &iiuimm8x2_operand +}; + +static xtensa_iclass_internal s16i_iclass = { + 3, + &s16i_operand_list[0] +}; + +static xtensa_operand_internal *call_operand_list[] = { + &lisoffsetx4_operand +}; + +static xtensa_iclass_internal call_iclass = { + 1, + &call_operand_list[0] +}; + +static xtensa_operand_internal *shifts_operand_list[] = { + &aor_operand, + &ais_operand +}; + +static xtensa_iclass_internal shifts_iclass = { + 2, + &shifts_operand_list[0] +}; + +static xtensa_operand_internal *shiftt_operand_list[] = { + &aor_operand, + &ait_operand +}; + +static xtensa_iclass_internal shiftt_iclass = { + 2, + &shiftt_operand_list[0] +}; + +static xtensa_operand_internal *rotw_operand_list[] = { + &iisimm4_operand +}; + +static xtensa_iclass_internal rotw_iclass = { + 1, + &rotw_operand_list[0] +}; + +static xtensa_operand_internal *addsub_operand_list[] = { + &aor_operand, + &ais_operand, + &ait_operand +}; + +static xtensa_iclass_internal addsub_iclass = { + 3, + &addsub_operand_list[0] +}; + +static xtensa_operand_internal *l8i_operand_list[] = { + &aot_operand, + &ais_operand, + &iiuimm8_operand +}; + +static xtensa_iclass_internal l8i_iclass = { + 3, + &l8i_operand_list[0] +}; + +static xtensa_operand_internal *sari_operand_list[] = { + &iisas_operand +}; + +static xtensa_iclass_internal sari_iclass = { + 1, + &sari_operand_list[0] +}; + +static xtensa_operand_internal *xsr_operand_list[] = { + &abt_operand, + &iisr_operand +}; + +static xtensa_iclass_internal xsr_iclass = { + 2, + &xsr_operand_list[0] +}; + +static xtensa_operand_internal *rsil_operand_list[] = { + &aot_operand, + &iis_operand +}; + +static xtensa_iclass_internal rsil_iclass = { + 2, + &rsil_operand_list[0] +}; + +static xtensa_operand_internal *bst8_operand_list[] = { + &ais_operand, + &ait_operand, + &lisimm8_operand +}; + +static xtensa_iclass_internal bst8_iclass = { + 3, + &bst8_operand_list[0] +}; + +static xtensa_operand_internal *addi_operand_list[] = { + &aot_operand, + &ais_operand, + &iisimm8_operand +}; + +static xtensa_iclass_internal addi_iclass = { + 3, + &addi_operand_list[0] +}; + +static xtensa_operand_internal *callx12_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal callx12_iclass = { + 1, + &callx12_operand_list[0] +}; + +static xtensa_operand_internal *bsi8_operand_list[] = { + &ais_operand, + &iib4const_operand, + &lisimm8_operand +}; + +static xtensa_iclass_internal bsi8_iclass = { + 3, + &bsi8_operand_list[0] +}; + +static xtensa_operand_internal *jumpx_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal jumpx_iclass = { + 1, + &jumpx_operand_list[0] +}; + +static xtensa_iclass_internal retn_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *nsa_operand_list[] = { + &aot_operand, + &ais_operand +}; + +static xtensa_iclass_internal nsa_iclass = { + 2, + &nsa_operand_list[0] +}; + +static xtensa_operand_internal *storei4_operand_list[] = { + &ait_operand, + &ais_operand, + &iilsi4x4_operand +}; + +static xtensa_iclass_internal storei4_iclass = { + 3, + &storei4_operand_list[0] +}; + +static xtensa_operand_internal *wtlb_operand_list[] = { + &ait_operand, + &ais_operand +}; + +static xtensa_iclass_internal wtlb_iclass = { + 2, + &wtlb_operand_list[0] +}; + +static xtensa_operand_internal *dce_operand_list[] = { + &ais_operand, + &iiuimm4x16_operand +}; + +static xtensa_iclass_internal dce_iclass = { + 2, + &dce_operand_list[0] +}; + +static xtensa_operand_internal *l16i_operand_list[] = { + &aot_operand, + &ais_operand, + &iiuimm8x2_operand +}; + +static xtensa_iclass_internal l16i_iclass = { + 3, + &l16i_operand_list[0] +}; + +static xtensa_operand_internal *callx4_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal callx4_iclass = { + 1, + &callx4_operand_list[0] +}; + +static xtensa_operand_internal *callx8_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal callx8_iclass = { + 1, + &callx8_operand_list[0] +}; + +static xtensa_operand_internal *movsp_operand_list[] = { + &aot_operand, + &ais_operand +}; + +static xtensa_iclass_internal movsp_iclass = { + 2, + &movsp_operand_list[0] +}; + +static xtensa_operand_internal *wsr_operand_list[] = { + &ait_operand, + &iisr_operand +}; + +static xtensa_iclass_internal wsr_iclass = { + 2, + &wsr_operand_list[0] +}; + +static xtensa_operand_internal *call12_operand_list[] = { + &lisoffsetx4_operand +}; + +static xtensa_iclass_internal call12_iclass = { + 1, + &call12_operand_list[0] +}; + +static xtensa_operand_internal *call4_operand_list[] = { + &lisoffsetx4_operand +}; + +static xtensa_iclass_internal call4_iclass = { + 1, + &call4_operand_list[0] +}; + +static xtensa_operand_internal *addmi_operand_list[] = { + &aot_operand, + &ais_operand, + &iisimm8x256_operand +}; + +static xtensa_iclass_internal addmi_iclass = { + 3, + &addmi_operand_list[0] +}; + +static xtensa_operand_internal *bit_operand_list[] = { + &aor_operand, + &ais_operand, + &ait_operand +}; + +static xtensa_iclass_internal bit_iclass = { + 3, + &bit_operand_list[0] +}; + +static xtensa_operand_internal *call8_operand_list[] = { + &lisoffsetx4_operand +}; + +static xtensa_iclass_internal call8_iclass = { + 1, + &call8_operand_list[0] +}; + +static xtensa_iclass_internal itlba_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *break_n_operand_list[] = { + &iis_operand +}; + +static xtensa_iclass_internal break_n_iclass = { + 1, + &break_n_operand_list[0] +}; + +static xtensa_operand_internal *sar_operand_list[] = { + &ais_operand +}; + +static xtensa_iclass_internal sar_iclass = { + 1, + &sar_operand_list[0] +}; + +static xtensa_operand_internal *s32e_operand_list[] = { + &ait_operand, + &ais_operand, + &iinimm4x2_operand +}; + +static xtensa_iclass_internal s32e_iclass = { + 3, + &s32e_operand_list[0] +}; + +static xtensa_operand_internal *bz6_operand_list[] = { + &ais_operand, + &liuimm6_operand +}; + +static xtensa_iclass_internal bz6_iclass = { + 2, + &bz6_operand_list[0] +}; + +static xtensa_operand_internal *loop_operand_list[] = { + &ais_operand, + &liuimm8_operand +}; + +static xtensa_iclass_internal loop_iclass = { + 2, + &loop_operand_list[0] +}; + +static xtensa_operand_internal *rsr_operand_list[] = { + &aot_operand, + &iisr_operand +}; + +static xtensa_iclass_internal rsr_iclass = { + 2, + &rsr_operand_list[0] +}; + +static xtensa_operand_internal *icache_operand_list[] = { + &ais_operand, + &iiuimm8x4_operand +}; + +static xtensa_iclass_internal icache_iclass = { + 2, + &icache_operand_list[0] +}; + +static xtensa_operand_internal *s8i_operand_list[] = { + &ait_operand, + &ais_operand, + &iiuimm8_operand +}; + +static xtensa_iclass_internal s8i_iclass = { + 3, + &s8i_operand_list[0] +}; + +static xtensa_iclass_internal return_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *dcache_operand_list[] = { + &ais_operand, + &iiuimm8x4_operand +}; + +static xtensa_iclass_internal dcache_iclass = { + 2, + &dcache_operand_list[0] +}; + +static xtensa_operand_internal *s32i_operand_list[] = { + &ait_operand, + &ais_operand, + &iiuimm8x4_operand +}; + +static xtensa_iclass_internal s32i_iclass = { + 3, + &s32i_operand_list[0] +}; + +static xtensa_operand_internal *jump_operand_list[] = { + &lisoffset_operand +}; + +static xtensa_iclass_internal jump_iclass = { + 1, + &jump_operand_list[0] +}; + +static xtensa_operand_internal *addi_n_operand_list[] = { + &aor_operand, + &ais_operand, + &iiai4const_operand +}; + +static xtensa_iclass_internal addi_n_iclass = { + 3, + &addi_n_operand_list[0] +}; + +static xtensa_iclass_internal sync_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *neg_operand_list[] = { + &aor_operand, + &ait_operand +}; + +static xtensa_iclass_internal neg_iclass = { + 2, + &neg_operand_list[0] +}; + +static xtensa_iclass_internal syscall_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *bsz12_operand_list[] = { + &ais_operand, + &lisimm12_operand +}; + +static xtensa_iclass_internal bsz12_iclass = { + 2, + &bsz12_operand_list[0] +}; + +static xtensa_iclass_internal excw_iclass = { + 0, + 0 +}; + +static xtensa_operand_internal *movi_n_operand_list[] = { + &aos_operand, + &iisimm7_operand +}; + +static xtensa_iclass_internal movi_n_iclass = { + 2, + &movi_n_operand_list[0] +}; + +static xtensa_operand_internal *rtlb_operand_list[] = { + &aot_operand, + &ais_operand +}; + +static xtensa_iclass_internal rtlb_iclass = { + 2, + &rtlb_operand_list[0] +}; + +static xtensa_operand_internal *actl_operand_list[] = { + &aot_operand, + &ais_operand +}; + +static xtensa_iclass_internal actl_iclass = { + 2, + &actl_operand_list[0] +}; + +static xtensa_operand_internal *srli_operand_list[] = { + &aor_operand, + &ait_operand, + &iis_operand +}; + +static xtensa_iclass_internal srli_iclass = { + 3, + &srli_operand_list[0] +}; + +static xtensa_operand_internal *bsi8b_operand_list[] = { + &ais_operand, + &iibbi_operand, + &lisimm8_operand +}; + +static xtensa_iclass_internal bsi8b_iclass = { + 3, + &bsi8b_operand_list[0] +}; + +static xtensa_operand_internal *acts_operand_list[] = { + &ait_operand, + &ais_operand +}; + +static xtensa_iclass_internal acts_iclass = { + 2, + &acts_operand_list[0] +}; + +static xtensa_operand_internal *add_n_operand_list[] = { + &aor_operand, + &ais_operand, + &ait_operand +}; + +static xtensa_iclass_internal add_n_iclass = { + 3, + &add_n_operand_list[0] +}; + +static xtensa_operand_internal *srai_operand_list[] = { + &aor_operand, + &ait_operand, + &iisar_operand +}; + +static xtensa_iclass_internal srai_iclass = { + 3, + &srai_operand_list[0] +}; + +static xtensa_operand_internal *entry_operand_list[] = { + &abs_operand, + &iiuimm12x8_operand +}; + +static xtensa_iclass_internal entry_iclass = { + 2, + &entry_operand_list[0] +}; + +static xtensa_operand_internal *l32e_operand_list[] = { + &aot_operand, + &ais_operand, + &iinimm4x2_operand +}; + +static xtensa_iclass_internal l32e_iclass = { + 3, + &l32e_operand_list[0] +}; + +static xtensa_operand_internal *dpf_operand_list[] = { + &ais_operand, + &iiuimm8x4_operand +}; + +static xtensa_iclass_internal dpf_iclass = { + 2, + &dpf_operand_list[0] +}; + +static xtensa_operand_internal *l32i_operand_list[] = { + &aot_operand, + &ais_operand, + &iiuimm8x4_operand +}; + +static xtensa_iclass_internal l32i_iclass = { + 3, + &l32i_operand_list[0] +}; + +static xtensa_insnbuf abs_template (void); +static xtensa_insnbuf add_template (void); +static xtensa_insnbuf add_n_template (void); +static xtensa_insnbuf addi_template (void); +static xtensa_insnbuf addi_n_template (void); +static xtensa_insnbuf addmi_template (void); +static xtensa_insnbuf addx2_template (void); +static xtensa_insnbuf addx4_template (void); +static xtensa_insnbuf addx8_template (void); +static xtensa_insnbuf and_template (void); +static xtensa_insnbuf ball_template (void); +static xtensa_insnbuf bany_template (void); +static xtensa_insnbuf bbc_template (void); +static xtensa_insnbuf bbci_template (void); +static xtensa_insnbuf bbs_template (void); +static xtensa_insnbuf bbsi_template (void); +static xtensa_insnbuf beq_template (void); +static xtensa_insnbuf beqi_template (void); +static xtensa_insnbuf beqz_template (void); +static xtensa_insnbuf beqz_n_template (void); +static xtensa_insnbuf bge_template (void); +static xtensa_insnbuf bgei_template (void); +static xtensa_insnbuf bgeu_template (void); +static xtensa_insnbuf bgeui_template (void); +static xtensa_insnbuf bgez_template (void); +static xtensa_insnbuf blt_template (void); +static xtensa_insnbuf blti_template (void); +static xtensa_insnbuf bltu_template (void); +static xtensa_insnbuf bltui_template (void); +static xtensa_insnbuf bltz_template (void); +static xtensa_insnbuf bnall_template (void); +static xtensa_insnbuf bne_template (void); +static xtensa_insnbuf bnei_template (void); +static xtensa_insnbuf bnez_template (void); +static xtensa_insnbuf bnez_n_template (void); +static xtensa_insnbuf bnone_template (void); +static xtensa_insnbuf break_template (void); +static xtensa_insnbuf break_n_template (void); +static xtensa_insnbuf call0_template (void); +static xtensa_insnbuf call12_template (void); +static xtensa_insnbuf call4_template (void); +static xtensa_insnbuf call8_template (void); +static xtensa_insnbuf callx0_template (void); +static xtensa_insnbuf callx12_template (void); +static xtensa_insnbuf callx4_template (void); +static xtensa_insnbuf callx8_template (void); +static xtensa_insnbuf dhi_template (void); +static xtensa_insnbuf dhwb_template (void); +static xtensa_insnbuf dhwbi_template (void); +static xtensa_insnbuf dii_template (void); +static xtensa_insnbuf diwb_template (void); +static xtensa_insnbuf diwbi_template (void); +static xtensa_insnbuf dpfr_template (void); +static xtensa_insnbuf dpfro_template (void); +static xtensa_insnbuf dpfw_template (void); +static xtensa_insnbuf dpfwo_template (void); +static xtensa_insnbuf dsync_template (void); +static xtensa_insnbuf entry_template (void); +static xtensa_insnbuf esync_template (void); +static xtensa_insnbuf excw_template (void); +static xtensa_insnbuf extui_template (void); +static xtensa_insnbuf idtlb_template (void); +static xtensa_insnbuf idtlba_template (void); +static xtensa_insnbuf ihi_template (void); +static xtensa_insnbuf iii_template (void); +static xtensa_insnbuf iitlb_template (void); +static xtensa_insnbuf iitlba_template (void); +static xtensa_insnbuf ipf_template (void); +static xtensa_insnbuf isync_template (void); +static xtensa_insnbuf j_template (void); +static xtensa_insnbuf jx_template (void); +static xtensa_insnbuf l16si_template (void); +static xtensa_insnbuf l16ui_template (void); +static xtensa_insnbuf l32e_template (void); +static xtensa_insnbuf l32i_template (void); +static xtensa_insnbuf l32i_n_template (void); +static xtensa_insnbuf l32r_template (void); +static xtensa_insnbuf l8ui_template (void); +static xtensa_insnbuf ldct_template (void); +static xtensa_insnbuf lict_template (void); +static xtensa_insnbuf licw_template (void); +static xtensa_insnbuf loop_template (void); +static xtensa_insnbuf loopgtz_template (void); +static xtensa_insnbuf loopnez_template (void); +static xtensa_insnbuf memw_template (void); +static xtensa_insnbuf mov_n_template (void); +static xtensa_insnbuf moveqz_template (void); +static xtensa_insnbuf movgez_template (void); +static xtensa_insnbuf movi_template (void); +static xtensa_insnbuf movi_n_template (void); +static xtensa_insnbuf movltz_template (void); +static xtensa_insnbuf movnez_template (void); +static xtensa_insnbuf movsp_template (void); +static xtensa_insnbuf neg_template (void); +static xtensa_insnbuf nop_n_template (void); +static xtensa_insnbuf nsa_template (void); +static xtensa_insnbuf nsau_template (void); +static xtensa_insnbuf or_template (void); +static xtensa_insnbuf pdtlb_template (void); +static xtensa_insnbuf pitlb_template (void); +static xtensa_insnbuf rdtlb0_template (void); +static xtensa_insnbuf rdtlb1_template (void); +static xtensa_insnbuf ret_template (void); +static xtensa_insnbuf ret_n_template (void); +static xtensa_insnbuf retw_template (void); +static xtensa_insnbuf retw_n_template (void); +static xtensa_insnbuf rfde_template (void); +static xtensa_insnbuf rfe_template (void); +static xtensa_insnbuf rfi_template (void); +static xtensa_insnbuf rfwo_template (void); +static xtensa_insnbuf rfwu_template (void); +static xtensa_insnbuf ritlb0_template (void); +static xtensa_insnbuf ritlb1_template (void); +static xtensa_insnbuf rotw_template (void); +static xtensa_insnbuf rsil_template (void); +static xtensa_insnbuf rsr_template (void); +static xtensa_insnbuf rsync_template (void); +static xtensa_insnbuf s16i_template (void); +static xtensa_insnbuf s32e_template (void); +static xtensa_insnbuf s32i_template (void); +static xtensa_insnbuf s32i_n_template (void); +static xtensa_insnbuf s8i_template (void); +static xtensa_insnbuf sdct_template (void); +static xtensa_insnbuf sict_template (void); +static xtensa_insnbuf sicw_template (void); +static xtensa_insnbuf simcall_template (void); +static xtensa_insnbuf sll_template (void); +static xtensa_insnbuf slli_template (void); +static xtensa_insnbuf sra_template (void); +static xtensa_insnbuf srai_template (void); +static xtensa_insnbuf src_template (void); +static xtensa_insnbuf srl_template (void); +static xtensa_insnbuf srli_template (void); +static xtensa_insnbuf ssa8b_template (void); +static xtensa_insnbuf ssa8l_template (void); +static xtensa_insnbuf ssai_template (void); +static xtensa_insnbuf ssl_template (void); +static xtensa_insnbuf ssr_template (void); +static xtensa_insnbuf sub_template (void); +static xtensa_insnbuf subx2_template (void); +static xtensa_insnbuf subx4_template (void); +static xtensa_insnbuf subx8_template (void); +static xtensa_insnbuf syscall_template (void); +static xtensa_insnbuf waiti_template (void); +static xtensa_insnbuf wdtlb_template (void); +static xtensa_insnbuf witlb_template (void); +static xtensa_insnbuf wsr_template (void); +static xtensa_insnbuf xor_template (void); +static xtensa_insnbuf xsr_template (void); + +static xtensa_insnbuf +abs_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00001006 }; + return &template[0]; +} + +static xtensa_insnbuf +add_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000008 }; + return &template[0]; +} + +static xtensa_insnbuf +add_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00a00000 }; + return &template[0]; +} + +static xtensa_insnbuf +addi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200c00 }; + return &template[0]; +} + +static xtensa_insnbuf +addi_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00b00000 }; + return &template[0]; +} + +static xtensa_insnbuf +addmi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200d00 }; + return &template[0]; +} + +static xtensa_insnbuf +addx2_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000009 }; + return &template[0]; +} + +static xtensa_insnbuf +addx4_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000000a }; + return &template[0]; +} + +static xtensa_insnbuf +addx8_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000000b }; + return &template[0]; +} + +static xtensa_insnbuf +and_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000001 }; + return &template[0]; +} + +static xtensa_insnbuf +ball_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700400 }; + return &template[0]; +} + +static xtensa_insnbuf +bany_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700800 }; + return &template[0]; +} + +static xtensa_insnbuf +bbc_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700500 }; + return &template[0]; +} + +static xtensa_insnbuf +bbci_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700600 }; + return &template[0]; +} + +static xtensa_insnbuf +bbs_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700d00 }; + return &template[0]; +} + +static xtensa_insnbuf +bbsi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700e00 }; + return &template[0]; +} + +static xtensa_insnbuf +beq_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700100 }; + return &template[0]; +} + +static xtensa_insnbuf +beqi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00680000 }; + return &template[0]; +} + +static xtensa_insnbuf +beqz_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00640000 }; + return &template[0]; +} + +static xtensa_insnbuf +beqz_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00c80000 }; + return &template[0]; +} + +static xtensa_insnbuf +bge_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700a00 }; + return &template[0]; +} + +static xtensa_insnbuf +bgei_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006b0000 }; + return &template[0]; +} + +static xtensa_insnbuf +bgeu_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700b00 }; + return &template[0]; +} + +static xtensa_insnbuf +bgeui_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006f0000 }; + return &template[0]; +} + +static xtensa_insnbuf +bgez_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00670000 }; + return &template[0]; +} + +static xtensa_insnbuf +blt_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700200 }; + return &template[0]; +} + +static xtensa_insnbuf +blti_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006a0000 }; + return &template[0]; +} + +static xtensa_insnbuf +bltu_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700300 }; + return &template[0]; +} + +static xtensa_insnbuf +bltui_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006e0000 }; + return &template[0]; +} + +static xtensa_insnbuf +bltz_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00660000 }; + return &template[0]; +} + +static xtensa_insnbuf +bnall_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700c00 }; + return &template[0]; +} + +static xtensa_insnbuf +bne_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700900 }; + return &template[0]; +} + +static xtensa_insnbuf +bnei_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00690000 }; + return &template[0]; +} + +static xtensa_insnbuf +bnez_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00650000 }; + return &template[0]; +} + +static xtensa_insnbuf +bnez_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00cc0000 }; + return &template[0]; +} + +static xtensa_insnbuf +bnone_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00700000 }; + return &template[0]; +} + +static xtensa_insnbuf +break_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000400 }; + return &template[0]; +} + +static xtensa_insnbuf +break_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00d20f00 }; + return &template[0]; +} + +static xtensa_insnbuf +call0_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00500000 }; + return &template[0]; +} + +static xtensa_insnbuf +call12_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x005c0000 }; + return &template[0]; +} + +static xtensa_insnbuf +call4_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00540000 }; + return &template[0]; +} + +static xtensa_insnbuf +call8_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00580000 }; + return &template[0]; +} + +static xtensa_insnbuf +callx0_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00030000 }; + return &template[0]; +} + +static xtensa_insnbuf +callx12_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x000f0000 }; + return &template[0]; +} + +static xtensa_insnbuf +callx4_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00070000 }; + return &template[0]; +} + +static xtensa_insnbuf +callx8_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x000b0000 }; + return &template[0]; +} + +static xtensa_insnbuf +dhi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00260700 }; + return &template[0]; +} + +static xtensa_insnbuf +dhwb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00240700 }; + return &template[0]; +} + +static xtensa_insnbuf +dhwbi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00250700 }; + return &template[0]; +} + +static xtensa_insnbuf +dii_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00270700 }; + return &template[0]; +} + +static xtensa_insnbuf +diwb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00280740 }; + return &template[0]; +} + +static xtensa_insnbuf +diwbi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00280750 }; + return &template[0]; +} + +static xtensa_insnbuf +dpfr_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200700 }; + return &template[0]; +} + +static xtensa_insnbuf +dpfro_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00220700 }; + return &template[0]; +} + +static xtensa_insnbuf +dpfw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00210700 }; + return &template[0]; +} + +static xtensa_insnbuf +dpfwo_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00230700 }; + return &template[0]; +} + +static xtensa_insnbuf +dsync_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00030200 }; + return &template[0]; +} + +static xtensa_insnbuf +entry_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006c0000 }; + return &template[0]; +} + +static xtensa_insnbuf +esync_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00020200 }; + return &template[0]; +} + +static xtensa_insnbuf +excw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00080200 }; + return &template[0]; +} + +static xtensa_insnbuf +extui_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000040 }; + return &template[0]; +} + +static xtensa_insnbuf +idtlb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000c05 }; + return &template[0]; +} + +static xtensa_insnbuf +idtlba_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000805 }; + return &template[0]; +} + +static xtensa_insnbuf +ihi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x002e0700 }; + return &template[0]; +} + +static xtensa_insnbuf +iii_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x002f0700 }; + return &template[0]; +} + +static xtensa_insnbuf +iitlb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000405 }; + return &template[0]; +} + +static xtensa_insnbuf +iitlba_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000005 }; + return &template[0]; +} + +static xtensa_insnbuf +ipf_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x002c0700 }; + return &template[0]; +} + +static xtensa_insnbuf +isync_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000200 }; + return &template[0]; +} + +static xtensa_insnbuf +j_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00600000 }; + return &template[0]; +} + +static xtensa_insnbuf +jx_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x000a0000 }; + return &template[0]; +} + +static xtensa_insnbuf +l16si_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200900 }; + return &template[0]; +} + +static xtensa_insnbuf +l16ui_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200100 }; + return &template[0]; +} + +static xtensa_insnbuf +l32e_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000090 }; + return &template[0]; +} + +static xtensa_insnbuf +l32i_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200200 }; + return &template[0]; +} + +static xtensa_insnbuf +l32i_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00800000 }; + return &template[0]; +} + +static xtensa_insnbuf +l32r_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00100000 }; + return &template[0]; +} + +static xtensa_insnbuf +l8ui_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200000 }; + return &template[0]; +} + +static xtensa_insnbuf +ldct_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000081f }; + return &template[0]; +} + +static xtensa_insnbuf +lict_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000001f }; + return &template[0]; +} + +static xtensa_insnbuf +licw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000021f }; + return &template[0]; +} + +static xtensa_insnbuf +loop_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006d0800 }; + return &template[0]; +} + +static xtensa_insnbuf +loopgtz_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006d0a00 }; + return &template[0]; +} + +static xtensa_insnbuf +loopnez_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x006d0900 }; + return &template[0]; +} + +static xtensa_insnbuf +memw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x000c0200 }; + return &template[0]; +} + +static xtensa_insnbuf +mov_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00d00000 }; + return &template[0]; +} + +static xtensa_insnbuf +moveqz_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000038 }; + return &template[0]; +} + +static xtensa_insnbuf +movgez_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000003b }; + return &template[0]; +} + +static xtensa_insnbuf +movi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200a00 }; + return &template[0]; +} + +static xtensa_insnbuf +movi_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00c00000 }; + return &template[0]; +} + +static xtensa_insnbuf +movltz_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000003a }; + return &template[0]; +} + +static xtensa_insnbuf +movnez_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000039 }; + return &template[0]; +} + +static xtensa_insnbuf +movsp_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000100 }; + return &template[0]; +} + +static xtensa_insnbuf +neg_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000006 }; + return &template[0]; +} + +static xtensa_insnbuf +nop_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00d30f00 }; + return &template[0]; +} + +static xtensa_insnbuf +nsa_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000e04 }; + return &template[0]; +} + +static xtensa_insnbuf +nsau_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000f04 }; + return &template[0]; +} + +static xtensa_insnbuf +or_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000002 }; + return &template[0]; +} + +static xtensa_insnbuf +pdtlb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000d05 }; + return &template[0]; +} + +static xtensa_insnbuf +pitlb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000505 }; + return &template[0]; +} + +static xtensa_insnbuf +rdtlb0_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000b05 }; + return &template[0]; +} + +static xtensa_insnbuf +rdtlb1_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000f05 }; + return &template[0]; +} + +static xtensa_insnbuf +ret_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00020000 }; + return &template[0]; +} + +static xtensa_insnbuf +ret_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00d00f00 }; + return &template[0]; +} + +static xtensa_insnbuf +retw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00060000 }; + return &template[0]; +} + +static xtensa_insnbuf +retw_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00d10f00 }; + return &template[0]; +} + +static xtensa_insnbuf +rfde_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00002300 }; + return &template[0]; +} + +static xtensa_insnbuf +rfe_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000300 }; + return &template[0]; +} + +static xtensa_insnbuf +rfi_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00010300 }; + return &template[0]; +} + +static xtensa_insnbuf +rfwo_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00004300 }; + return &template[0]; +} + +static xtensa_insnbuf +rfwu_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00005300 }; + return &template[0]; +} + +static xtensa_insnbuf +ritlb0_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000305 }; + return &template[0]; +} + +static xtensa_insnbuf +ritlb1_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000705 }; + return &template[0]; +} + +static xtensa_insnbuf +rotw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000804 }; + return &template[0]; +} + +static xtensa_insnbuf +rsil_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000600 }; + return &template[0]; +} + +static xtensa_insnbuf +rsr_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000030 }; + return &template[0]; +} + +static xtensa_insnbuf +rsync_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00010200 }; + return &template[0]; +} + +static xtensa_insnbuf +s16i_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200500 }; + return &template[0]; +} + +static xtensa_insnbuf +s32e_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000094 }; + return &template[0]; +} + +static xtensa_insnbuf +s32i_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200600 }; + return &template[0]; +} + +static xtensa_insnbuf +s32i_n_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00900000 }; + return &template[0]; +} + +static xtensa_insnbuf +s8i_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00200400 }; + return &template[0]; +} + +static xtensa_insnbuf +sdct_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000091f }; + return &template[0]; +} + +static xtensa_insnbuf +sict_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000011f }; + return &template[0]; +} + +static xtensa_insnbuf +sicw_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000031f }; + return &template[0]; +} + +static xtensa_insnbuf +simcall_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00001500 }; + return &template[0]; +} + +static xtensa_insnbuf +sll_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000001a }; + return &template[0]; +} + +static xtensa_insnbuf +slli_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000010 }; + return &template[0]; +} + +static xtensa_insnbuf +sra_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000001b }; + return &template[0]; +} + +static xtensa_insnbuf +srai_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000012 }; + return &template[0]; +} + +static xtensa_insnbuf +src_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000018 }; + return &template[0]; +} + +static xtensa_insnbuf +srl_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000019 }; + return &template[0]; +} + +static xtensa_insnbuf +srli_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000014 }; + return &template[0]; +} + +static xtensa_insnbuf +ssa8b_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000304 }; + return &template[0]; +} + +static xtensa_insnbuf +ssa8l_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000204 }; + return &template[0]; +} + +static xtensa_insnbuf +ssai_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000404 }; + return &template[0]; +} + +static xtensa_insnbuf +ssl_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000104 }; + return &template[0]; +} + +static xtensa_insnbuf +ssr_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000004 }; + return &template[0]; +} + +static xtensa_insnbuf +sub_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000000c }; + return &template[0]; +} + +static xtensa_insnbuf +subx2_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000000d }; + return &template[0]; +} + +static xtensa_insnbuf +subx4_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000000e }; + return &template[0]; +} + +static xtensa_insnbuf +subx8_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x0000000f }; + return &template[0]; +} + +static xtensa_insnbuf +syscall_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000500 }; + return &template[0]; +} + +static xtensa_insnbuf +waiti_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000700 }; + return &template[0]; +} + +static xtensa_insnbuf +wdtlb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000e05 }; + return &template[0]; +} + +static xtensa_insnbuf +witlb_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000605 }; + return &template[0]; +} + +static xtensa_insnbuf +wsr_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000031 }; + return &template[0]; +} + +static xtensa_insnbuf +xor_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000003 }; + return &template[0]; +} + +static xtensa_insnbuf +xsr_template (void) +{ + static xtensa_insnbuf_word template[] = { 0x00000016 }; + return &template[0]; +} + +static xtensa_opcode_internal abs_opcode = { + "abs", + 3, + abs_template, + &neg_iclass +}; + +static xtensa_opcode_internal add_opcode = { + "add", + 3, + add_template, + &addsub_iclass +}; + +static xtensa_opcode_internal add_n_opcode = { + "add.n", + 2, + add_n_template, + &add_n_iclass +}; + +static xtensa_opcode_internal addi_opcode = { + "addi", + 3, + addi_template, + &addi_iclass +}; + +static xtensa_opcode_internal addi_n_opcode = { + "addi.n", + 2, + addi_n_template, + &addi_n_iclass +}; + +static xtensa_opcode_internal addmi_opcode = { + "addmi", + 3, + addmi_template, + &addmi_iclass +}; + +static xtensa_opcode_internal addx2_opcode = { + "addx2", + 3, + addx2_template, + &addsub_iclass +}; + +static xtensa_opcode_internal addx4_opcode = { + "addx4", + 3, + addx4_template, + &addsub_iclass +}; + +static xtensa_opcode_internal addx8_opcode = { + "addx8", + 3, + addx8_template, + &addsub_iclass +}; + +static xtensa_opcode_internal and_opcode = { + "and", + 3, + and_template, + &bit_iclass +}; + +static xtensa_opcode_internal ball_opcode = { + "ball", + 3, + ball_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bany_opcode = { + "bany", + 3, + bany_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bbc_opcode = { + "bbc", + 3, + bbc_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bbci_opcode = { + "bbci", + 3, + bbci_template, + &bsi8b_iclass +}; + +static xtensa_opcode_internal bbs_opcode = { + "bbs", + 3, + bbs_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bbsi_opcode = { + "bbsi", + 3, + bbsi_template, + &bsi8b_iclass +}; + +static xtensa_opcode_internal beq_opcode = { + "beq", + 3, + beq_template, + &bst8_iclass +}; + +static xtensa_opcode_internal beqi_opcode = { + "beqi", + 3, + beqi_template, + &bsi8_iclass +}; + +static xtensa_opcode_internal beqz_opcode = { + "beqz", + 3, + beqz_template, + &bsz12_iclass +}; + +static xtensa_opcode_internal beqz_n_opcode = { + "beqz.n", + 2, + beqz_n_template, + &bz6_iclass +}; + +static xtensa_opcode_internal bge_opcode = { + "bge", + 3, + bge_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bgei_opcode = { + "bgei", + 3, + bgei_template, + &bsi8_iclass +}; + +static xtensa_opcode_internal bgeu_opcode = { + "bgeu", + 3, + bgeu_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bgeui_opcode = { + "bgeui", + 3, + bgeui_template, + &bsi8u_iclass +}; + +static xtensa_opcode_internal bgez_opcode = { + "bgez", + 3, + bgez_template, + &bsz12_iclass +}; + +static xtensa_opcode_internal blt_opcode = { + "blt", + 3, + blt_template, + &bst8_iclass +}; + +static xtensa_opcode_internal blti_opcode = { + "blti", + 3, + blti_template, + &bsi8_iclass +}; + +static xtensa_opcode_internal bltu_opcode = { + "bltu", + 3, + bltu_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bltui_opcode = { + "bltui", + 3, + bltui_template, + &bsi8u_iclass +}; + +static xtensa_opcode_internal bltz_opcode = { + "bltz", + 3, + bltz_template, + &bsz12_iclass +}; + +static xtensa_opcode_internal bnall_opcode = { + "bnall", + 3, + bnall_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bne_opcode = { + "bne", + 3, + bne_template, + &bst8_iclass +}; + +static xtensa_opcode_internal bnei_opcode = { + "bnei", + 3, + bnei_template, + &bsi8_iclass +}; + +static xtensa_opcode_internal bnez_opcode = { + "bnez", + 3, + bnez_template, + &bsz12_iclass +}; + +static xtensa_opcode_internal bnez_n_opcode = { + "bnez.n", + 2, + bnez_n_template, + &bz6_iclass +}; + +static xtensa_opcode_internal bnone_opcode = { + "bnone", + 3, + bnone_template, + &bst8_iclass +}; + +static xtensa_opcode_internal break_opcode = { + "break", + 3, + break_template, + &break_iclass +}; + +static xtensa_opcode_internal break_n_opcode = { + "break.n", + 2, + break_n_template, + &break_n_iclass +}; + +static xtensa_opcode_internal call0_opcode = { + "call0", + 3, + call0_template, + &call_iclass +}; + +static xtensa_opcode_internal call12_opcode = { + "call12", + 3, + call12_template, + &call12_iclass +}; + +static xtensa_opcode_internal call4_opcode = { + "call4", + 3, + call4_template, + &call4_iclass +}; + +static xtensa_opcode_internal call8_opcode = { + "call8", + 3, + call8_template, + &call8_iclass +}; + +static xtensa_opcode_internal callx0_opcode = { + "callx0", + 3, + callx0_template, + &callx_iclass +}; + +static xtensa_opcode_internal callx12_opcode = { + "callx12", + 3, + callx12_template, + &callx12_iclass +}; + +static xtensa_opcode_internal callx4_opcode = { + "callx4", + 3, + callx4_template, + &callx4_iclass +}; + +static xtensa_opcode_internal callx8_opcode = { + "callx8", + 3, + callx8_template, + &callx8_iclass +}; + +static xtensa_opcode_internal dhi_opcode = { + "dhi", + 3, + dhi_template, + &dcache_iclass +}; + +static xtensa_opcode_internal dhwb_opcode = { + "dhwb", + 3, + dhwb_template, + &dcache_iclass +}; + +static xtensa_opcode_internal dhwbi_opcode = { + "dhwbi", + 3, + dhwbi_template, + &dcache_iclass +}; + +static xtensa_opcode_internal dii_opcode = { + "dii", + 3, + dii_template, + &dcache_iclass +}; + +static xtensa_opcode_internal diwb_opcode = { + "diwb", + 3, + diwb_template, + &dce_iclass +}; + +static xtensa_opcode_internal diwbi_opcode = { + "diwbi", + 3, + diwbi_template, + &dce_iclass +}; + +static xtensa_opcode_internal dpfr_opcode = { + "dpfr", + 3, + dpfr_template, + &dpf_iclass +}; + +static xtensa_opcode_internal dpfro_opcode = { + "dpfro", + 3, + dpfro_template, + &dpf_iclass +}; + +static xtensa_opcode_internal dpfw_opcode = { + "dpfw", + 3, + dpfw_template, + &dpf_iclass +}; + +static xtensa_opcode_internal dpfwo_opcode = { + "dpfwo", + 3, + dpfwo_template, + &dpf_iclass +}; + +static xtensa_opcode_internal dsync_opcode = { + "dsync", + 3, + dsync_template, + &sync_iclass +}; + +static xtensa_opcode_internal entry_opcode = { + "entry", + 3, + entry_template, + &entry_iclass +}; + +static xtensa_opcode_internal esync_opcode = { + "esync", + 3, + esync_template, + &sync_iclass +}; + +static xtensa_opcode_internal excw_opcode = { + "excw", + 3, + excw_template, + &excw_iclass +}; + +static xtensa_opcode_internal extui_opcode = { + "extui", + 3, + extui_template, + &exti_iclass +}; + +static xtensa_opcode_internal idtlb_opcode = { + "idtlb", + 3, + idtlb_template, + &itlb_iclass +}; + +static xtensa_opcode_internal idtlba_opcode = { + "idtlba", + 3, + idtlba_template, + &itlba_iclass +}; + +static xtensa_opcode_internal ihi_opcode = { + "ihi", + 3, + ihi_template, + &icache_iclass +}; + +static xtensa_opcode_internal iii_opcode = { + "iii", + 3, + iii_template, + &icache_iclass +}; + +static xtensa_opcode_internal iitlb_opcode = { + "iitlb", + 3, + iitlb_template, + &itlb_iclass +}; + +static xtensa_opcode_internal iitlba_opcode = { + "iitlba", + 3, + iitlba_template, + &itlba_iclass +}; + +static xtensa_opcode_internal ipf_opcode = { + "ipf", + 3, + ipf_template, + &icache_iclass +}; + +static xtensa_opcode_internal isync_opcode = { + "isync", + 3, + isync_template, + &sync_iclass +}; + +static xtensa_opcode_internal j_opcode = { + "j", + 3, + j_template, + &jump_iclass +}; + +static xtensa_opcode_internal jx_opcode = { + "jx", + 3, + jx_template, + &jumpx_iclass +}; + +static xtensa_opcode_internal l16si_opcode = { + "l16si", + 3, + l16si_template, + &l16i_iclass +}; + +static xtensa_opcode_internal l16ui_opcode = { + "l16ui", + 3, + l16ui_template, + &l16i_iclass +}; + +static xtensa_opcode_internal l32e_opcode = { + "l32e", + 3, + l32e_template, + &l32e_iclass +}; + +static xtensa_opcode_internal l32i_opcode = { + "l32i", + 3, + l32i_template, + &l32i_iclass +}; + +static xtensa_opcode_internal l32i_n_opcode = { + "l32i.n", + 2, + l32i_n_template, + &loadi4_iclass +}; + +static xtensa_opcode_internal l32r_opcode = { + "l32r", + 3, + l32r_template, + &l32r_iclass +}; + +static xtensa_opcode_internal l8ui_opcode = { + "l8ui", + 3, + l8ui_template, + &l8i_iclass +}; + +static xtensa_opcode_internal ldct_opcode = { + "ldct", + 3, + ldct_template, + &actl_iclass +}; + +static xtensa_opcode_internal lict_opcode = { + "lict", + 3, + lict_template, + &actl_iclass +}; + +static xtensa_opcode_internal licw_opcode = { + "licw", + 3, + licw_template, + &actl_iclass +}; + +static xtensa_opcode_internal loop_opcode = { + "loop", + 3, + loop_template, + &loop_iclass +}; + +static xtensa_opcode_internal loopgtz_opcode = { + "loopgtz", + 3, + loopgtz_template, + &loop_iclass +}; + +static xtensa_opcode_internal loopnez_opcode = { + "loopnez", + 3, + loopnez_template, + &loop_iclass +}; + +static xtensa_opcode_internal memw_opcode = { + "memw", + 3, + memw_template, + &sync_iclass +}; + +static xtensa_opcode_internal mov_n_opcode = { + "mov.n", + 2, + mov_n_template, + &mov_n_iclass +}; + +static xtensa_opcode_internal moveqz_opcode = { + "moveqz", + 3, + moveqz_template, + &movz_iclass +}; + +static xtensa_opcode_internal movgez_opcode = { + "movgez", + 3, + movgez_template, + &movz_iclass +}; + +static xtensa_opcode_internal movi_opcode = { + "movi", + 3, + movi_template, + &movi_iclass +}; + +static xtensa_opcode_internal movi_n_opcode = { + "movi.n", + 2, + movi_n_template, + &movi_n_iclass +}; + +static xtensa_opcode_internal movltz_opcode = { + "movltz", + 3, + movltz_template, + &movz_iclass +}; + +static xtensa_opcode_internal movnez_opcode = { + "movnez", + 3, + movnez_template, + &movz_iclass +}; + +static xtensa_opcode_internal movsp_opcode = { + "movsp", + 3, + movsp_template, + &movsp_iclass +}; + +static xtensa_opcode_internal neg_opcode = { + "neg", + 3, + neg_template, + &neg_iclass +}; + +static xtensa_opcode_internal nop_n_opcode = { + "nop.n", + 2, + nop_n_template, + &nopn_iclass +}; + +static xtensa_opcode_internal nsa_opcode = { + "nsa", + 3, + nsa_template, + &nsa_iclass +}; + +static xtensa_opcode_internal nsau_opcode = { + "nsau", + 3, + nsau_template, + &nsa_iclass +}; + +static xtensa_opcode_internal or_opcode = { + "or", + 3, + or_template, + &bit_iclass +}; + +static xtensa_opcode_internal pdtlb_opcode = { + "pdtlb", + 3, + pdtlb_template, + &rtlb_iclass +}; + +static xtensa_opcode_internal pitlb_opcode = { + "pitlb", + 3, + pitlb_template, + &rtlb_iclass +}; + +static xtensa_opcode_internal rdtlb0_opcode = { + "rdtlb0", + 3, + rdtlb0_template, + &rtlb_iclass +}; + +static xtensa_opcode_internal rdtlb1_opcode = { + "rdtlb1", + 3, + rdtlb1_template, + &rtlb_iclass +}; + +static xtensa_opcode_internal ret_opcode = { + "ret", + 3, + ret_template, + &return_iclass +}; + +static xtensa_opcode_internal ret_n_opcode = { + "ret.n", + 2, + ret_n_template, + &retn_iclass +}; + +static xtensa_opcode_internal retw_opcode = { + "retw", + 3, + retw_template, + &return_iclass +}; + +static xtensa_opcode_internal retw_n_opcode = { + "retw.n", + 2, + retw_n_template, + &retn_iclass +}; + +static xtensa_opcode_internal rfde_opcode = { + "rfde", + 3, + rfde_template, + &rfe_iclass +}; + +static xtensa_opcode_internal rfe_opcode = { + "rfe", + 3, + rfe_template, + &rfe_iclass +}; + +static xtensa_opcode_internal rfi_opcode = { + "rfi", + 3, + rfi_template, + &rfi_iclass +}; + +static xtensa_opcode_internal rfwo_opcode = { + "rfwo", + 3, + rfwo_template, + &rfe_iclass +}; + +static xtensa_opcode_internal rfwu_opcode = { + "rfwu", + 3, + rfwu_template, + &rfe_iclass +}; + +static xtensa_opcode_internal ritlb0_opcode = { + "ritlb0", + 3, + ritlb0_template, + &rtlb_iclass +}; + +static xtensa_opcode_internal ritlb1_opcode = { + "ritlb1", + 3, + ritlb1_template, + &rtlb_iclass +}; + +static xtensa_opcode_internal rotw_opcode = { + "rotw", + 3, + rotw_template, + &rotw_iclass +}; + +static xtensa_opcode_internal rsil_opcode = { + "rsil", + 3, + rsil_template, + &rsil_iclass +}; + +static xtensa_opcode_internal rsr_opcode = { + "rsr", + 3, + rsr_template, + &rsr_iclass +}; + +static xtensa_opcode_internal rsync_opcode = { + "rsync", + 3, + rsync_template, + &sync_iclass +}; + +static xtensa_opcode_internal s16i_opcode = { + "s16i", + 3, + s16i_template, + &s16i_iclass +}; + +static xtensa_opcode_internal s32e_opcode = { + "s32e", + 3, + s32e_template, + &s32e_iclass +}; + +static xtensa_opcode_internal s32i_opcode = { + "s32i", + 3, + s32i_template, + &s32i_iclass +}; + +static xtensa_opcode_internal s32i_n_opcode = { + "s32i.n", + 2, + s32i_n_template, + &storei4_iclass +}; + +static xtensa_opcode_internal s8i_opcode = { + "s8i", + 3, + s8i_template, + &s8i_iclass +}; + +static xtensa_opcode_internal sdct_opcode = { + "sdct", + 3, + sdct_template, + &acts_iclass +}; + +static xtensa_opcode_internal sict_opcode = { + "sict", + 3, + sict_template, + &acts_iclass +}; + +static xtensa_opcode_internal sicw_opcode = { + "sicw", + 3, + sicw_template, + &acts_iclass +}; + +static xtensa_opcode_internal simcall_opcode = { + "simcall", + 3, + simcall_template, + &syscall_iclass +}; + +static xtensa_opcode_internal sll_opcode = { + "sll", + 3, + sll_template, + &shifts_iclass +}; + +static xtensa_opcode_internal slli_opcode = { + "slli", + 3, + slli_template, + &slli_iclass +}; + +static xtensa_opcode_internal sra_opcode = { + "sra", + 3, + sra_template, + &shiftt_iclass +}; + +static xtensa_opcode_internal srai_opcode = { + "srai", + 3, + srai_template, + &srai_iclass +}; + +static xtensa_opcode_internal src_opcode = { + "src", + 3, + src_template, + &shiftst_iclass +}; + +static xtensa_opcode_internal srl_opcode = { + "srl", + 3, + srl_template, + &shiftt_iclass +}; + +static xtensa_opcode_internal srli_opcode = { + "srli", + 3, + srli_template, + &srli_iclass +}; + +static xtensa_opcode_internal ssa8b_opcode = { + "ssa8b", + 3, + ssa8b_template, + &sar_iclass +}; + +static xtensa_opcode_internal ssa8l_opcode = { + "ssa8l", + 3, + ssa8l_template, + &sar_iclass +}; + +static xtensa_opcode_internal ssai_opcode = { + "ssai", + 3, + ssai_template, + &sari_iclass +}; + +static xtensa_opcode_internal ssl_opcode = { + "ssl", + 3, + ssl_template, + &sar_iclass +}; + +static xtensa_opcode_internal ssr_opcode = { + "ssr", + 3, + ssr_template, + &sar_iclass +}; + +static xtensa_opcode_internal sub_opcode = { + "sub", + 3, + sub_template, + &addsub_iclass +}; + +static xtensa_opcode_internal subx2_opcode = { + "subx2", + 3, + subx2_template, + &addsub_iclass +}; + +static xtensa_opcode_internal subx4_opcode = { + "subx4", + 3, + subx4_template, + &addsub_iclass +}; + +static xtensa_opcode_internal subx8_opcode = { + "subx8", + 3, + subx8_template, + &addsub_iclass +}; + +static xtensa_opcode_internal syscall_opcode = { + "syscall", + 3, + syscall_template, + &syscall_iclass +}; + +static xtensa_opcode_internal waiti_opcode = { + "waiti", + 3, + waiti_template, + &wait_iclass +}; + +static xtensa_opcode_internal wdtlb_opcode = { + "wdtlb", + 3, + wdtlb_template, + &wtlb_iclass +}; + +static xtensa_opcode_internal witlb_opcode = { + "witlb", + 3, + witlb_template, + &wtlb_iclass +}; + +static xtensa_opcode_internal wsr_opcode = { + "wsr", + 3, + wsr_template, + &wsr_iclass +}; + +static xtensa_opcode_internal xor_opcode = { + "xor", + 3, + xor_template, + &bit_iclass +}; + +static xtensa_opcode_internal xsr_opcode = { + "xsr", + 3, + xsr_template, + &xsr_iclass +}; + +static xtensa_opcode_internal * opcodes[149] = { + &abs_opcode, + &add_opcode, + &add_n_opcode, + &addi_opcode, + &addi_n_opcode, + &addmi_opcode, + &addx2_opcode, + &addx4_opcode, + &addx8_opcode, + &and_opcode, + &ball_opcode, + &bany_opcode, + &bbc_opcode, + &bbci_opcode, + &bbs_opcode, + &bbsi_opcode, + &beq_opcode, + &beqi_opcode, + &beqz_opcode, + &beqz_n_opcode, + &bge_opcode, + &bgei_opcode, + &bgeu_opcode, + &bgeui_opcode, + &bgez_opcode, + &blt_opcode, + &blti_opcode, + &bltu_opcode, + &bltui_opcode, + &bltz_opcode, + &bnall_opcode, + &bne_opcode, + &bnei_opcode, + &bnez_opcode, + &bnez_n_opcode, + &bnone_opcode, + &break_opcode, + &break_n_opcode, + &call0_opcode, + &call12_opcode, + &call4_opcode, + &call8_opcode, + &callx0_opcode, + &callx12_opcode, + &callx4_opcode, + &callx8_opcode, + &dhi_opcode, + &dhwb_opcode, + &dhwbi_opcode, + &dii_opcode, + &diwb_opcode, + &diwbi_opcode, + &dpfr_opcode, + &dpfro_opcode, + &dpfw_opcode, + &dpfwo_opcode, + &dsync_opcode, + &entry_opcode, + &esync_opcode, + &excw_opcode, + &extui_opcode, + &idtlb_opcode, + &idtlba_opcode, + &ihi_opcode, + &iii_opcode, + &iitlb_opcode, + &iitlba_opcode, + &ipf_opcode, + &isync_opcode, + &j_opcode, + &jx_opcode, + &l16si_opcode, + &l16ui_opcode, + &l32e_opcode, + &l32i_opcode, + &l32i_n_opcode, + &l32r_opcode, + &l8ui_opcode, + &ldct_opcode, + &lict_opcode, + &licw_opcode, + &loop_opcode, + &loopgtz_opcode, + &loopnez_opcode, + &memw_opcode, + &mov_n_opcode, + &moveqz_opcode, + &movgez_opcode, + &movi_opcode, + &movi_n_opcode, + &movltz_opcode, + &movnez_opcode, + &movsp_opcode, + &neg_opcode, + &nop_n_opcode, + &nsa_opcode, + &nsau_opcode, + &or_opcode, + &pdtlb_opcode, + &pitlb_opcode, + &rdtlb0_opcode, + &rdtlb1_opcode, + &ret_opcode, + &ret_n_opcode, + &retw_opcode, + &retw_n_opcode, + &rfde_opcode, + &rfe_opcode, + &rfi_opcode, + &rfwo_opcode, + &rfwu_opcode, + &ritlb0_opcode, + &ritlb1_opcode, + &rotw_opcode, + &rsil_opcode, + &rsr_opcode, + &rsync_opcode, + &s16i_opcode, + &s32e_opcode, + &s32i_opcode, + &s32i_n_opcode, + &s8i_opcode, + &sdct_opcode, + &sict_opcode, + &sicw_opcode, + &simcall_opcode, + &sll_opcode, + &slli_opcode, + &sra_opcode, + &srai_opcode, + &src_opcode, + &srl_opcode, + &srli_opcode, + &ssa8b_opcode, + &ssa8l_opcode, + &ssai_opcode, + &ssl_opcode, + &ssr_opcode, + &sub_opcode, + &subx2_opcode, + &subx4_opcode, + &subx8_opcode, + &syscall_opcode, + &waiti_opcode, + &wdtlb_opcode, + &witlb_opcode, + &wsr_opcode, + &xor_opcode, + &xsr_opcode +}; + +xtensa_opcode_internal ** +get_opcodes (void) +{ + return &opcodes[0]; +} + +const int +get_num_opcodes (void) +{ + return 149; +} + +#define xtensa_abs_op 0 +#define xtensa_add_op 1 +#define xtensa_add_n_op 2 +#define xtensa_addi_op 3 +#define xtensa_addi_n_op 4 +#define xtensa_addmi_op 5 +#define xtensa_addx2_op 6 +#define xtensa_addx4_op 7 +#define xtensa_addx8_op 8 +#define xtensa_and_op 9 +#define xtensa_ball_op 10 +#define xtensa_bany_op 11 +#define xtensa_bbc_op 12 +#define xtensa_bbci_op 13 +#define xtensa_bbs_op 14 +#define xtensa_bbsi_op 15 +#define xtensa_beq_op 16 +#define xtensa_beqi_op 17 +#define xtensa_beqz_op 18 +#define xtensa_beqz_n_op 19 +#define xtensa_bge_op 20 +#define xtensa_bgei_op 21 +#define xtensa_bgeu_op 22 +#define xtensa_bgeui_op 23 +#define xtensa_bgez_op 24 +#define xtensa_blt_op 25 +#define xtensa_blti_op 26 +#define xtensa_bltu_op 27 +#define xtensa_bltui_op 28 +#define xtensa_bltz_op 29 +#define xtensa_bnall_op 30 +#define xtensa_bne_op 31 +#define xtensa_bnei_op 32 +#define xtensa_bnez_op 33 +#define xtensa_bnez_n_op 34 +#define xtensa_bnone_op 35 +#define xtensa_break_op 36 +#define xtensa_break_n_op 37 +#define xtensa_call0_op 38 +#define xtensa_call12_op 39 +#define xtensa_call4_op 40 +#define xtensa_call8_op 41 +#define xtensa_callx0_op 42 +#define xtensa_callx12_op 43 +#define xtensa_callx4_op 44 +#define xtensa_callx8_op 45 +#define xtensa_dhi_op 46 +#define xtensa_dhwb_op 47 +#define xtensa_dhwbi_op 48 +#define xtensa_dii_op 49 +#define xtensa_diwb_op 50 +#define xtensa_diwbi_op 51 +#define xtensa_dpfr_op 52 +#define xtensa_dpfro_op 53 +#define xtensa_dpfw_op 54 +#define xtensa_dpfwo_op 55 +#define xtensa_dsync_op 56 +#define xtensa_entry_op 57 +#define xtensa_esync_op 58 +#define xtensa_excw_op 59 +#define xtensa_extui_op 60 +#define xtensa_idtlb_op 61 +#define xtensa_idtlba_op 62 +#define xtensa_ihi_op 63 +#define xtensa_iii_op 64 +#define xtensa_iitlb_op 65 +#define xtensa_iitlba_op 66 +#define xtensa_ipf_op 67 +#define xtensa_isync_op 68 +#define xtensa_j_op 69 +#define xtensa_jx_op 70 +#define xtensa_l16si_op 71 +#define xtensa_l16ui_op 72 +#define xtensa_l32e_op 73 +#define xtensa_l32i_op 74 +#define xtensa_l32i_n_op 75 +#define xtensa_l32r_op 76 +#define xtensa_l8ui_op 77 +#define xtensa_ldct_op 78 +#define xtensa_lict_op 79 +#define xtensa_licw_op 80 +#define xtensa_loop_op 81 +#define xtensa_loopgtz_op 82 +#define xtensa_loopnez_op 83 +#define xtensa_memw_op 84 +#define xtensa_mov_n_op 85 +#define xtensa_moveqz_op 86 +#define xtensa_movgez_op 87 +#define xtensa_movi_op 88 +#define xtensa_movi_n_op 89 +#define xtensa_movltz_op 90 +#define xtensa_movnez_op 91 +#define xtensa_movsp_op 92 +#define xtensa_neg_op 93 +#define xtensa_nop_n_op 94 +#define xtensa_nsa_op 95 +#define xtensa_nsau_op 96 +#define xtensa_or_op 97 +#define xtensa_pdtlb_op 98 +#define xtensa_pitlb_op 99 +#define xtensa_rdtlb0_op 100 +#define xtensa_rdtlb1_op 101 +#define xtensa_ret_op 102 +#define xtensa_ret_n_op 103 +#define xtensa_retw_op 104 +#define xtensa_retw_n_op 105 +#define xtensa_rfde_op 106 +#define xtensa_rfe_op 107 +#define xtensa_rfi_op 108 +#define xtensa_rfwo_op 109 +#define xtensa_rfwu_op 110 +#define xtensa_ritlb0_op 111 +#define xtensa_ritlb1_op 112 +#define xtensa_rotw_op 113 +#define xtensa_rsil_op 114 +#define xtensa_rsr_op 115 +#define xtensa_rsync_op 116 +#define xtensa_s16i_op 117 +#define xtensa_s32e_op 118 +#define xtensa_s32i_op 119 +#define xtensa_s32i_n_op 120 +#define xtensa_s8i_op 121 +#define xtensa_sdct_op 122 +#define xtensa_sict_op 123 +#define xtensa_sicw_op 124 +#define xtensa_simcall_op 125 +#define xtensa_sll_op 126 +#define xtensa_slli_op 127 +#define xtensa_sra_op 128 +#define xtensa_srai_op 129 +#define xtensa_src_op 130 +#define xtensa_srl_op 131 +#define xtensa_srli_op 132 +#define xtensa_ssa8b_op 133 +#define xtensa_ssa8l_op 134 +#define xtensa_ssai_op 135 +#define xtensa_ssl_op 136 +#define xtensa_ssr_op 137 +#define xtensa_sub_op 138 +#define xtensa_subx2_op 139 +#define xtensa_subx4_op 140 +#define xtensa_subx8_op 141 +#define xtensa_syscall_op 142 +#define xtensa_waiti_op 143 +#define xtensa_wdtlb_op 144 +#define xtensa_witlb_op 145 +#define xtensa_wsr_op 146 +#define xtensa_xor_op 147 +#define xtensa_xsr_op 148 + +int +decode_insn (const xtensa_insnbuf insn) +{ + switch (get_op0_field (insn)) { + case 0: /* QRST: op0=0000 */ + switch (get_op1_field (insn)) { + case 3: /* RST3: op1=0011 */ + switch (get_op2_field (insn)) { + case 8: /* MOVEQZ: op2=1000 */ + return xtensa_moveqz_op; + case 9: /* MOVNEZ: op2=1001 */ + return xtensa_movnez_op; + case 10: /* MOVLTZ: op2=1010 */ + return xtensa_movltz_op; + case 11: /* MOVGEZ: op2=1011 */ + return xtensa_movgez_op; + case 0: /* RSR: op2=0000 */ + return xtensa_rsr_op; + case 1: /* WSR: op2=0001 */ + return xtensa_wsr_op; + } + break; + case 9: /* LSI4: op1=1001 */ + switch (get_op2_field (insn)) { + case 4: /* S32E: op2=0100 */ + return xtensa_s32e_op; + case 0: /* L32E: op2=0000 */ + return xtensa_l32e_op; + } + break; + case 4: /* EXTUI: op1=010x */ + case 5: /* EXTUI: op1=010x */ + return xtensa_extui_op; + case 0: /* RST0: op1=0000 */ + switch (get_op2_field (insn)) { + case 15: /* SUBX8: op2=1111 */ + return xtensa_subx8_op; + case 0: /* ST0: op2=0000 */ + switch (get_r_field (insn)) { + case 0: /* SNM0: r=0000 */ + switch (get_m_field (insn)) { + case 2: /* JR: m=10 */ + switch (get_n_field (insn)) { + case 0: /* RET: n=00 */ + return xtensa_ret_op; + case 1: /* RETW: n=01 */ + return xtensa_retw_op; + case 2: /* JX: n=10 */ + return xtensa_jx_op; + } + break; + case 3: /* CALLX: m=11 */ + switch (get_n_field (insn)) { + case 0: /* CALLX0: n=00 */ + return xtensa_callx0_op; + case 1: /* CALLX4: n=01 */ + return xtensa_callx4_op; + case 2: /* CALLX8: n=10 */ + return xtensa_callx8_op; + case 3: /* CALLX12: n=11 */ + return xtensa_callx12_op; + } + break; + } + break; + case 1: /* MOVSP: r=0001 */ + return xtensa_movsp_op; + case 2: /* SYNC: r=0010 */ + switch (get_s_field (insn)) { + case 0: /* SYNCT: s=0000 */ + switch (get_t_field (insn)) { + case 2: /* ESYNC: t=0010 */ + return xtensa_esync_op; + case 3: /* DSYNC: t=0011 */ + return xtensa_dsync_op; + case 8: /* EXCW: t=1000 */ + return xtensa_excw_op; + case 12: /* MEMW: t=1100 */ + return xtensa_memw_op; + case 0: /* ISYNC: t=0000 */ + return xtensa_isync_op; + case 1: /* RSYNC: t=0001 */ + return xtensa_rsync_op; + } + break; + } + break; + case 4: /* BREAK: r=0100 */ + return xtensa_break_op; + case 3: /* RFEI: r=0011 */ + switch (get_t_field (insn)) { + case 0: /* RFET: t=0000 */ + switch (get_s_field (insn)) { + case 2: /* RFDE: s=0010 */ + return xtensa_rfde_op; + case 4: /* RFWO: s=0100 */ + return xtensa_rfwo_op; + case 5: /* RFWU: s=0101 */ + return xtensa_rfwu_op; + case 0: /* RFE: s=0000 */ + return xtensa_rfe_op; + } + break; + case 1: /* RFI: t=0001 */ + return xtensa_rfi_op; + } + break; + case 5: /* SCALL: r=0101 */ + switch (get_s_field (insn)) { + case 0: /* SYSCALL: s=0000 */ + return xtensa_syscall_op; + case 1: /* SIMCALL: s=0001 */ + return xtensa_simcall_op; + } + break; + case 6: /* RSIL: r=0110 */ + return xtensa_rsil_op; + case 7: /* WAITI: r=0111 */ + return xtensa_waiti_op; + } + break; + case 1: /* AND: op2=0001 */ + return xtensa_and_op; + case 2: /* OR: op2=0010 */ + return xtensa_or_op; + case 3: /* XOR: op2=0011 */ + return xtensa_xor_op; + case 4: /* ST1: op2=0100 */ + switch (get_r_field (insn)) { + case 15: /* NSAU: r=1111 */ + return xtensa_nsau_op; + case 0: /* SSR: r=0000 */ + return xtensa_ssr_op; + case 1: /* SSL: r=0001 */ + return xtensa_ssl_op; + case 2: /* SSA8L: r=0010 */ + return xtensa_ssa8l_op; + case 3: /* SSA8B: r=0011 */ + return xtensa_ssa8b_op; + case 4: /* SSAI: r=0100 */ + return xtensa_ssai_op; + case 8: /* ROTW: r=1000 */ + return xtensa_rotw_op; + case 14: /* NSA: r=1110 */ + return xtensa_nsa_op; + } + break; + case 8: /* ADD: op2=1000 */ + return xtensa_add_op; + case 5: /* ST4: op2=0101 */ + switch (get_r_field (insn)) { + case 15: /* RDTLB1: r=1111 */ + return xtensa_rdtlb1_op; + case 0: /* IITLBA: r=0000 */ + return xtensa_iitlba_op; + case 3: /* RITLB0: r=0011 */ + return xtensa_ritlb0_op; + case 4: /* IITLB: r=0100 */ + return xtensa_iitlb_op; + case 8: /* IDTLBA: r=1000 */ + return xtensa_idtlba_op; + case 5: /* PITLB: r=0101 */ + return xtensa_pitlb_op; + case 6: /* WITLB: r=0110 */ + return xtensa_witlb_op; + case 7: /* RITLB1: r=0111 */ + return xtensa_ritlb1_op; + case 11: /* RDTLB0: r=1011 */ + return xtensa_rdtlb0_op; + case 12: /* IDTLB: r=1100 */ + return xtensa_idtlb_op; + case 13: /* PDTLB: r=1101 */ + return xtensa_pdtlb_op; + case 14: /* WDTLB: r=1110 */ + return xtensa_wdtlb_op; + } + break; + case 6: /* RT0: op2=0110 */ + switch (get_s_field (insn)) { + case 0: /* NEG: s=0000 */ + return xtensa_neg_op; + case 1: /* ABS: s=0001 */ + return xtensa_abs_op; + } + break; + case 9: /* ADDX2: op2=1001 */ + return xtensa_addx2_op; + case 10: /* ADDX4: op2=1010 */ + return xtensa_addx4_op; + case 11: /* ADDX8: op2=1011 */ + return xtensa_addx8_op; + case 12: /* SUB: op2=1100 */ + return xtensa_sub_op; + case 13: /* SUBX2: op2=1101 */ + return xtensa_subx2_op; + case 14: /* SUBX4: op2=1110 */ + return xtensa_subx4_op; + } + break; + case 1: /* RST1: op1=0001 */ + switch (get_op2_field (insn)) { + case 15: /* IMP: op2=1111 */ + switch (get_r_field (insn)) { + case 0: /* LICT: r=0000 */ + return xtensa_lict_op; + case 1: /* SICT: r=0001 */ + return xtensa_sict_op; + case 2: /* LICW: r=0010 */ + return xtensa_licw_op; + case 3: /* SICW: r=0011 */ + return xtensa_sicw_op; + case 8: /* LDCT: r=1000 */ + return xtensa_ldct_op; + case 9: /* SDCT: r=1001 */ + return xtensa_sdct_op; + } + break; + case 0: /* SLLI: op2=000x */ + case 1: /* SLLI: op2=000x */ + return xtensa_slli_op; + case 2: /* SRAI: op2=001x */ + case 3: /* SRAI: op2=001x */ + return xtensa_srai_op; + case 4: /* SRLI: op2=0100 */ + return xtensa_srli_op; + case 8: /* SRC: op2=1000 */ + return xtensa_src_op; + case 9: /* SRL: op2=1001 */ + return xtensa_srl_op; + case 6: /* XSR: op2=0110 */ + return xtensa_xsr_op; + case 10: /* SLL: op2=1010 */ + return xtensa_sll_op; + case 11: /* SRA: op2=1011 */ + return xtensa_sra_op; + } + break; + } + break; + case 1: /* L32R: op0=0001 */ + return xtensa_l32r_op; + case 2: /* LSAI: op0=0010 */ + switch (get_r_field (insn)) { + case 0: /* L8UI: r=0000 */ + return xtensa_l8ui_op; + case 1: /* L16UI: r=0001 */ + return xtensa_l16ui_op; + case 2: /* L32I: r=0010 */ + return xtensa_l32i_op; + case 4: /* S8I: r=0100 */ + return xtensa_s8i_op; + case 5: /* S16I: r=0101 */ + return xtensa_s16i_op; + case 9: /* L16SI: r=1001 */ + return xtensa_l16si_op; + case 6: /* S32I: r=0110 */ + return xtensa_s32i_op; + case 7: /* CACHE: r=0111 */ + switch (get_t_field (insn)) { + case 15: /* III: t=1111 */ + return xtensa_iii_op; + case 0: /* DPFR: t=0000 */ + return xtensa_dpfr_op; + case 1: /* DPFW: t=0001 */ + return xtensa_dpfw_op; + case 2: /* DPFRO: t=0010 */ + return xtensa_dpfro_op; + case 4: /* DHWB: t=0100 */ + return xtensa_dhwb_op; + case 3: /* DPFWO: t=0011 */ + return xtensa_dpfwo_op; + case 8: /* DCE: t=1000 */ + switch (get_op1_field (insn)) { + case 4: /* DIWB: op1=0100 */ + return xtensa_diwb_op; + case 5: /* DIWBI: op1=0101 */ + return xtensa_diwbi_op; + } + break; + case 5: /* DHWBI: t=0101 */ + return xtensa_dhwbi_op; + case 6: /* DHI: t=0110 */ + return xtensa_dhi_op; + case 7: /* DII: t=0111 */ + return xtensa_dii_op; + case 12: /* IPF: t=1100 */ + return xtensa_ipf_op; + case 14: /* IHI: t=1110 */ + return xtensa_ihi_op; + } + break; + case 10: /* MOVI: r=1010 */ + return xtensa_movi_op; + case 12: /* ADDI: r=1100 */ + return xtensa_addi_op; + case 13: /* ADDMI: r=1101 */ + return xtensa_addmi_op; + } + break; + case 8: /* L32I.N: op0=1000 */ + return xtensa_l32i_n_op; + case 5: /* CALL: op0=0101 */ + switch (get_n_field (insn)) { + case 0: /* CALL0: n=00 */ + return xtensa_call0_op; + case 1: /* CALL4: n=01 */ + return xtensa_call4_op; + case 2: /* CALL8: n=10 */ + return xtensa_call8_op; + case 3: /* CALL12: n=11 */ + return xtensa_call12_op; + } + break; + case 6: /* SI: op0=0110 */ + switch (get_n_field (insn)) { + case 0: /* J: n=00 */ + return xtensa_j_op; + case 1: /* BZ: n=01 */ + switch (get_m_field (insn)) { + case 0: /* BEQZ: m=00 */ + return xtensa_beqz_op; + case 1: /* BNEZ: m=01 */ + return xtensa_bnez_op; + case 2: /* BLTZ: m=10 */ + return xtensa_bltz_op; + case 3: /* BGEZ: m=11 */ + return xtensa_bgez_op; + } + break; + case 2: /* BI0: n=10 */ + switch (get_m_field (insn)) { + case 0: /* BEQI: m=00 */ + return xtensa_beqi_op; + case 1: /* BNEI: m=01 */ + return xtensa_bnei_op; + case 2: /* BLTI: m=10 */ + return xtensa_blti_op; + case 3: /* BGEI: m=11 */ + return xtensa_bgei_op; + } + break; + case 3: /* BI1: n=11 */ + switch (get_m_field (insn)) { + case 0: /* ENTRY: m=00 */ + return xtensa_entry_op; + case 1: /* B1: m=01 */ + switch (get_r_field (insn)) { + case 8: /* LOOP: r=1000 */ + return xtensa_loop_op; + case 9: /* LOOPNEZ: r=1001 */ + return xtensa_loopnez_op; + case 10: /* LOOPGTZ: r=1010 */ + return xtensa_loopgtz_op; + } + break; + case 2: /* BLTUI: m=10 */ + return xtensa_bltui_op; + case 3: /* BGEUI: m=11 */ + return xtensa_bgeui_op; + } + break; + } + break; + case 9: /* S32I.N: op0=1001 */ + return xtensa_s32i_n_op; + case 10: /* ADD.N: op0=1010 */ + return xtensa_add_n_op; + case 7: /* B: op0=0111 */ + switch (get_r_field (insn)) { + case 6: /* BBCI: r=011x */ + case 7: /* BBCI: r=011x */ + return xtensa_bbci_op; + case 0: /* BNONE: r=0000 */ + return xtensa_bnone_op; + case 1: /* BEQ: r=0001 */ + return xtensa_beq_op; + case 2: /* BLT: r=0010 */ + return xtensa_blt_op; + case 4: /* BALL: r=0100 */ + return xtensa_ball_op; + case 14: /* BBSI: r=111x */ + case 15: /* BBSI: r=111x */ + return xtensa_bbsi_op; + case 3: /* BLTU: r=0011 */ + return xtensa_bltu_op; + case 5: /* BBC: r=0101 */ + return xtensa_bbc_op; + case 8: /* BANY: r=1000 */ + return xtensa_bany_op; + case 9: /* BNE: r=1001 */ + return xtensa_bne_op; + case 10: /* BGE: r=1010 */ + return xtensa_bge_op; + case 11: /* BGEU: r=1011 */ + return xtensa_bgeu_op; + case 12: /* BNALL: r=1100 */ + return xtensa_bnall_op; + case 13: /* BBS: r=1101 */ + return xtensa_bbs_op; + } + break; + case 11: /* ADDI.N: op0=1011 */ + return xtensa_addi_n_op; + case 12: /* ST2: op0=1100 */ + switch (get_i_field (insn)) { + case 0: /* MOVI.N: i=0 */ + return xtensa_movi_n_op; + case 1: /* BZ6: i=1 */ + switch (get_z_field (insn)) { + case 0: /* BEQZ.N: z=0 */ + return xtensa_beqz_n_op; + case 1: /* BNEZ.N: z=1 */ + return xtensa_bnez_n_op; + } + break; + } + break; + case 13: /* ST3: op0=1101 */ + switch (get_r_field (insn)) { + case 15: /* S3: r=1111 */ + switch (get_t_field (insn)) { + case 0: /* RET.N: t=0000 */ + return xtensa_ret_n_op; + case 1: /* RETW.N: t=0001 */ + return xtensa_retw_n_op; + case 2: /* BREAK.N: t=0010 */ + return xtensa_break_n_op; + case 3: /* NOP.N: t=0011 */ + return xtensa_nop_n_op; + } + break; + case 0: /* MOV.N: r=0000 */ + return xtensa_mov_n_op; + } + break; + } + return XTENSA_UNDEFINED; +} + +int +interface_version (void) +{ + return 3; +} + +static struct config_struct config_table[] = { + {"IsaMemoryOrder", "BigEndian"}, + {"PIFReadDataBits", "128"}, + {"PIFWriteDataBits", "128"}, + {"IsaCoprocessorCount", "0"}, + {"IsaUseBooleans", "0"}, + {"IsaUseDensityInstruction", "1"}, + {0, 0} +}; + +struct config_struct * get_config_table (void); + +struct config_struct * +get_config_table (void) +{ + return config_table; +} + +xtensa_isa_module xtensa_isa_modules[] = { + { get_num_opcodes, get_opcodes, decode_insn, get_config_table }, + { 0, 0, 0, 0 } +}; diff --git a/gas/ChangeLog b/gas/ChangeLog index 132ad34eb9b..5462dd59b28 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,32 @@ +2003-04-01 Bob Wilson + + * Makefile.am (CPU_TYPES): Add xtensa. + (TARGET_CPU_CFILES): Add config/tc-xtensa.c. + (TARGET_CPU_HFILES): Add config/tc-xtensa.h. + (xtensa-relax.o): New target. + Run "make dep-am". + * Makefile.in: Regenerate. + * configure.in: Handle xtensa-*-*. Add xtensa-relax.o to + extra_objects for xtensa targets. + * configure: Regenerate. + * write.c (write_object_file): Add new md_post_relax_hook. + * config/tc-xtensa.c: New file. + * config/tc-xtensa.h: Likewise. + * config/xtensa-istack.h: Likewise. + * config/xtensa-relax.c: Likewise. + * config/xtensa-relax.h: Likewise. + * doc/Makefile.am (CPU_DOCS): Add c-xtensa.texi. + * doc/Makefile.in: Regenerate. + * doc/all.texi: Set new XTENSA variable. + * doc/as.texinfo: Set new Xtensa variable. Describe + Xtensa-specific options. Define line comment character for + Xtensa. Add Xtensa processors to list of ELF targets where + alignment is specified in bytes. Add new Xtensa-Dependent node. + Add acknowledgements for those contributing to the Xtensa port. + * doc/internals.texi: Describe new md_post_relax_hook. + * doc/c-xtensa.texi: New file. + + 2003-04-01 Nick Clifton Richard Earnshaw diff --git a/gas/Makefile.am b/gas/Makefile.am index 15f8f188013..76665bdb820 100644 --- a/gas/Makefile.am +++ b/gas/Makefile.am @@ -86,6 +86,7 @@ CPU_TYPES = \ w65 \ v850 \ xstormy16 \ + xtensa \ z8k # Object format types. This is only used for dependency information. @@ -278,6 +279,7 @@ TARGET_CPU_CFILES = \ config/tc-w65.c \ config/tc-v850.c \ config/tc-xstormy16.c \ + config/tc-xtensa.c \ config/tc-z8k.c TARGET_CPU_HFILES = \ @@ -329,6 +331,7 @@ TARGET_CPU_HFILES = \ config/tc-w65.h \ config/tc-v850.h \ config/tc-xstormy16.h \ + config/tc-xtensa.h \ config/tc-z8k.h # OBJ files in config @@ -601,6 +604,10 @@ e-crisaout.o: $(srcdir)/config/e-crisaout.c e-criself.o: $(srcdir)/config/e-criself.c $(COMPILE) -c $(srcdir)/config/e-criself.c +xtensa-relax.o: $(srcdir)/config/xtensa-relax.c + $(COMPILE) -c $(srcdir)/config/xtensa-relax.c + + # The m68k operand parser. EXTRA_as_new_SOURCES = config/m68k-parse.y @@ -1517,6 +1524,13 @@ DEPTC_xstormy16_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ subsegs.h $(INCDIR)/obstack.h $(srcdir)/../opcodes/xstormy16-desc.h \ $(INCDIR)/opcode/cgen.h $(srcdir)/../opcodes/xstormy16-opc.h \ cgen.h +DEPTC_xtensa_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xtensa.h \ + $(INCDIR)/xtensa-config.h sb.h $(INCDIR)/safe-ctype.h \ + subsegs.h $(INCDIR)/obstack.h $(srcdir)/config/xtensa-relax.h \ + $(INCDIR)/xtensa-isa.h $(srcdir)/config/xtensa-istack.h \ + dwarf2dbg.h struc-symbol.h DEPTC_z8k_coff = $(INCDIR)/symcat.h $(srcdir)/config/obj-coff.h \ $(srcdir)/config/tc-z8k.h $(INCDIR)/coff/internal.h \ $(INCDIR)/coff/z8k.h $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h \ @@ -1915,8 +1929,8 @@ DEPOBJ_sh64_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-sh64.h \ $(srcdir)/config/tc-sh.h $(INCDIR)/elf/sh.h $(INCDIR)/elf/reloc-macros.h \ - $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \ - struc-symbol.h $(INCDIR)/aout/aout64.h + $(BFDDIR)/elf32-sh64.h $(INCDIR)/safe-ctype.h subsegs.h \ + $(INCDIR)/obstack.h struc-symbol.h $(INCDIR)/aout/aout64.h DEPOBJ_sparc_aout = $(INCDIR)/symcat.h $(srcdir)/config/obj-aout.h \ $(srcdir)/config/tc-sparc.h $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h \ $(INCDIR)/aout/aout64.h $(INCDIR)/obstack.h @@ -2023,6 +2037,11 @@ DEPOBJ_xstormy16_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xstormy16.h \ $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \ struc-symbol.h $(INCDIR)/aout/aout64.h +DEPOBJ_xtensa_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xtensa.h \ + $(INCDIR)/xtensa-config.h $(INCDIR)/safe-ctype.h subsegs.h \ + $(INCDIR)/obstack.h struc-symbol.h $(INCDIR)/aout/aout64.h DEPOBJ_z8k_coff = $(INCDIR)/symcat.h $(srcdir)/config/obj-coff.h \ $(srcdir)/config/tc-z8k.h $(INCDIR)/coff/internal.h \ $(INCDIR)/coff/z8k.h $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h \ @@ -2363,6 +2382,10 @@ DEP_xstormy16_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-xstormy16.h DEP_xstormy16_elf = $(srcdir)/config/obj-elf.h $(INCDIR)/symcat.h \ $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xstormy16.h +DEP_xtensa_elf = $(srcdir)/config/obj-elf.h $(INCDIR)/symcat.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xtensa.h \ + $(INCDIR)/xtensa-config.h DEP_z8k_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-z8k.h \ $(INCDIR)/symcat.h $(INCDIR)/coff/internal.h $(INCDIR)/coff/z8k.h \ $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h $(INCDIR)/bfdlink.h diff --git a/gas/Makefile.in b/gas/Makefile.in index 75e8767e54f..9f18f7fbdf8 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation @@ -197,6 +197,7 @@ CPU_TYPES = \ w65 \ v850 \ xstormy16 \ + xtensa \ z8k @@ -395,6 +396,7 @@ TARGET_CPU_CFILES = \ config/tc-w65.c \ config/tc-v850.c \ config/tc-xstormy16.c \ + config/tc-xtensa.c \ config/tc-z8k.c @@ -447,6 +449,7 @@ TARGET_CPU_HFILES = \ config/tc-w65.h \ config/tc-v850.h \ config/tc-xstormy16.h \ + config/tc-xtensa.h \ config/tc-z8k.h @@ -1333,6 +1336,14 @@ DEPTC_xstormy16_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ $(INCDIR)/opcode/cgen.h $(srcdir)/../opcodes/xstormy16-opc.h \ cgen.h +DEPTC_xtensa_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xtensa.h \ + $(INCDIR)/xtensa-config.h sb.h $(INCDIR)/safe-ctype.h \ + subsegs.h $(INCDIR)/obstack.h $(srcdir)/config/xtensa-relax.h \ + $(INCDIR)/xtensa-isa.h $(srcdir)/config/xtensa-istack.h \ + dwarf2dbg.h struc-symbol.h + DEPTC_z8k_coff = $(INCDIR)/symcat.h $(srcdir)/config/obj-coff.h \ $(srcdir)/config/tc-z8k.h $(INCDIR)/coff/internal.h \ $(INCDIR)/coff/z8k.h $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h \ @@ -1822,8 +1833,8 @@ DEPOBJ_sh64_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-sh64.h \ $(srcdir)/config/tc-sh.h $(INCDIR)/elf/sh.h $(INCDIR)/elf/reloc-macros.h \ - $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \ - struc-symbol.h $(INCDIR)/aout/aout64.h + $(BFDDIR)/elf32-sh64.h $(INCDIR)/safe-ctype.h subsegs.h \ + $(INCDIR)/obstack.h struc-symbol.h $(INCDIR)/aout/aout64.h DEPOBJ_sparc_aout = $(INCDIR)/symcat.h $(srcdir)/config/obj-aout.h \ $(srcdir)/config/tc-sparc.h $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h \ @@ -1956,6 +1967,12 @@ DEPOBJ_xstormy16_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \ struc-symbol.h $(INCDIR)/aout/aout64.h +DEPOBJ_xtensa_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xtensa.h \ + $(INCDIR)/xtensa-config.h $(INCDIR)/safe-ctype.h subsegs.h \ + $(INCDIR)/obstack.h struc-symbol.h $(INCDIR)/aout/aout64.h + DEPOBJ_z8k_coff = $(INCDIR)/symcat.h $(srcdir)/config/obj-coff.h \ $(srcdir)/config/tc-z8k.h $(INCDIR)/coff/internal.h \ $(INCDIR)/coff/z8k.h $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h \ @@ -2411,6 +2428,11 @@ DEP_xstormy16_elf = $(srcdir)/config/obj-elf.h $(INCDIR)/symcat.h \ $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xstormy16.h +DEP_xtensa_elf = $(srcdir)/config/obj-elf.h $(INCDIR)/symcat.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-xtensa.h \ + $(INCDIR)/xtensa-config.h + DEP_z8k_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-z8k.h \ $(INCDIR)/symcat.h $(INCDIR)/coff/internal.h $(INCDIR)/coff/z8k.h \ $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h $(INCDIR)/bfdlink.h @@ -2471,7 +2493,7 @@ configure configure.in gdbinit.in itbl-lex.c itbl-parse.c DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) -TAR = tar +TAR = gtar GZIP_ENV = --best SOURCES = $(itbl_test_SOURCES) $(as_new_SOURCES) $(EXTRA_as_new_SOURCES) OBJECTS = $(itbl_test_OBJECTS) $(as_new_OBJECTS) @@ -2807,7 +2829,7 @@ distclean-generic: -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: - -test -z "itbl-lex.cconfig/m68k-parse.hconfig/m68k-parse.citbl-parse.hitbl-parse.c" || rm -f itbl-lex.c config/m68k-parse.h config/m68k-parse.c itbl-parse.h itbl-parse.c + -test -z "itbl-lexlconfig/m68k-parsehconfig/m68k-parsecitbl-parsehitbl-parsec" || rm -f itbl-lexl config/m68k-parseh config/m68k-parsec itbl-parseh itbl-parsec mostlyclean-am: mostlyclean-hdr mostlyclean-noinstPROGRAMS \ mostlyclean-compile mostlyclean-libtool \ mostlyclean-tags mostlyclean-generic @@ -2955,6 +2977,9 @@ e-crisaout.o: $(srcdir)/config/e-crisaout.c e-criself.o: $(srcdir)/config/e-criself.c $(COMPILE) -c $(srcdir)/config/e-criself.c +xtensa-relax.o: $(srcdir)/config/xtensa-relax.c + $(COMPILE) -c $(srcdir)/config/xtensa-relax.c + # If m68k-parse.y is in a different directory, then ylwrap will use an # absolute path when it invokes yacc, which will cause yacc to put the # absolute path into the generated file. That's a pain when it comes diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c new file mode 100644 index 00000000000..32a04beb3d3 --- /dev/null +++ b/gas/config/tc-xtensa.c @@ -0,0 +1,9014 @@ +/* tc-xtensa.c -- Assemble Xtensa instructions. + Copyright 2003 Free Software Foundation, Inc. + + 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 2, 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, 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. */ + +#include +#include "as.h" +#include "sb.h" +#include "safe-ctype.h" +#include "tc-xtensa.h" +#include "frags.h" +#include "subsegs.h" +#include "xtensa-relax.h" +#include "xtensa-istack.h" +#include "dwarf2dbg.h" +#include "struc-symbol.h" +#include "xtensa-config.h" + +#ifndef uint32 +#define uint32 unsigned int +#endif +#ifndef int32 +#define int32 signed int +#endif + +/* Notes: + + There are 3 forms for instructions, + 1) the MEMORY format -- this is the encoding 2 or 3 byte instruction + 2) the TInsn -- handles instructions/labels and literals; + all operands are assumed to be expressions + 3) the IStack -- a stack of TInsn. this allows us to + reason about the generated expansion instructions + + Naming conventions (used somewhat inconsistently): + The xtensa_ functions are exported + The xg_ functions are internal + + We also have a couple of different extensibility mechanisms. + 1) The idiom replacement: + This is used when a line is first parsed to + replace an instruction pattern with another instruction + It is currently limited to replacements of instructions + with constant operands. + 2) The xtensa-relax.c mechanism that has stronger instruction + replacement patterns. When an instruction's immediate field + does not fit the next instruction sequence is attempted. + In addition, "narrow" opcodes are supported this way. */ + + +/* Define characters with special meanings to GAS. */ +const char comment_chars[] = "#"; +const char line_comment_chars[] = "#"; +const char line_separator_chars[] = ";"; +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + + +/* Flag to indicate whether the hardware supports the density option. + If not, enabling density instructions (via directives or --density flag) + is illegal. */ + +#if STATIC_LIBISA +bfd_boolean density_supported = XCHAL_HAVE_DENSITY; +#else +bfd_boolean density_supported = TRUE; +#endif + +#define XTENSA_FETCH_WIDTH 4 + +/* Flags for properties of the last instruction in a segment. */ +#define FLAG_IS_A0_WRITER 0x1 +#define FLAG_IS_BAD_LOOPEND 0x2 + + +/* We define a special segment names ".literal" to place literals + into. The .fini and .init sections are special because they + contain code that is moved together by the linker. We give them + their own special .fini.literal and .init.literal sections. */ + +#define LITERAL_SECTION_NAME xtensa_section_rename (".literal") +#define FINI_SECTION_NAME xtensa_section_rename (".fini") +#define INIT_SECTION_NAME xtensa_section_rename (".init") +#define FINI_LITERAL_SECTION_NAME xtensa_section_rename (".fini.literal") +#define INIT_LITERAL_SECTION_NAME xtensa_section_rename (".init.literal") + + +/* This type is used for the directive_stack to keep track of the + state of the literal collection pools. */ + +typedef struct lit_state_struct +{ + const char *lit_seg_name; + const char *init_lit_seg_name; + const char *fini_lit_seg_name; + segT lit_seg; + segT init_lit_seg; + segT fini_lit_seg; +} lit_state; + +static lit_state default_lit_sections; + + +/* We keep lists of literal segments. The seg_list type is the node + for such a list. The *_literal_head locals are the heads of the + various lists. All of these lists have a dummy node at the start. */ + +typedef struct seg_list_struct +{ + struct seg_list_struct *next; + segT seg; +} seg_list; + +static seg_list literal_head_h; +static seg_list *literal_head = &literal_head_h; +static seg_list init_literal_head_h; +static seg_list *init_literal_head = &init_literal_head_h; +static seg_list fini_literal_head_h; +static seg_list *fini_literal_head = &fini_literal_head_h; + + +/* Global flag to indicate when we are emitting literals. */ +int generating_literals = 0; + + +/* Structure for saving the current state before emitting literals. */ +typedef struct emit_state_struct +{ + const char *name; + segT now_seg; + subsegT now_subseg; + int generating_literals; +} emit_state; + + +/* Directives. */ + +typedef enum +{ + directive_none = 0, + directive_literal, + directive_density, + directive_generics, + directive_relax, + directive_freeregs, + directive_longcalls, + directive_literal_prefix +} directiveE; + +typedef struct +{ + const char *name; + bfd_boolean can_be_negated; +} directive_infoS; + +const directive_infoS directive_info[] = +{ + {"none", FALSE}, + {"literal", FALSE}, + {"density", TRUE}, + {"generics", TRUE}, + {"relax", TRUE}, + {"freeregs", FALSE}, + {"longcalls", TRUE}, + {"literal_prefix", FALSE} +}; + +bfd_boolean directive_state[] = +{ + FALSE, /* none */ + FALSE, /* literal */ +#if STATIC_LIBISA && !XCHAL_HAVE_DENSITY + FALSE, /* density */ +#else + TRUE, /* density */ +#endif + TRUE, /* generics */ + TRUE, /* relax */ + FALSE, /* freeregs */ + FALSE, /* longcalls */ + FALSE /* literal_prefix */ +}; + + +enum xtensa_relax_statesE +{ + RELAX_ALIGN_NEXT_OPCODE, + /* Use the first opcode of the next fragment to determine the + alignment requirements. This is ONLY used for LOOPS + currently. */ + + RELAX_DESIRE_ALIGN_IF_TARGET, + /* These are placed in front of labels. They will all be converted + to RELAX_DESIRE_ALIGN / RELAX_LOOP_END or rs_fill of 0 before + relaxation begins. */ + + RELAX_ADD_NOP_IF_A0_B_RETW, + /* These are placed in front of conditional branches. It will be + turned into a NOP (using a1) if the branch is immediately + followed by a RETW or RETW.N. Otherwise it will be turned into + an rs_fill of 0 before relaxation begins. */ + + RELAX_ADD_NOP_IF_PRE_LOOP_END, + /* These are placed after JX instructions. It will be turned into a + NOP if there is one instruction before a loop end label. + Otherwise it will be turned into an rs_fill of 0 before + relaxation begins. This is used to avoid a hardware TIE + interlock issue prior to T1040. */ + + RELAX_ADD_NOP_IF_SHORT_LOOP, + /* These are placed after LOOP instructions. It will be turned into + a NOP when: (1) there are less than 3 instructions in the loop; + we place 2 of these in a row to add up to 2 NOPS in short loops; + or (2) The instructions in the loop do not include a branch or + jump. Otherwise it will be turned into an rs_fill of 0 before + relaxation begins. This is used to avoid hardware bug + PR3830. */ + + RELAX_ADD_NOP_IF_CLOSE_LOOP_END, + /* These are placed after LOOP instructions. It will be turned into + a NOP if there are less than 12 bytes to the end of some other + loop's end. Otherwise it will be turned into an rs_fill of 0 + before relaxation begins. This is used to avoid hardware bug + PR3830. */ + + RELAX_DESIRE_ALIGN, + /* The next fragment like its first instruction to NOT cross a + 4-byte boundary. */ + + RELAX_LOOP_END, + /* This will be turned into a NOP or NOP.N if the previous + instruction is expanded to negate a loop. */ + + RELAX_LOOP_END_ADD_NOP, + /* When the code density option is available, this will generate a + NOP.N marked RELAX_NARROW. Otherwise, it will create an rs_fill + fragment with a NOP in it. */ + + RELAX_LITERAL, + /* Another fragment could generate an expansion here but has not yet. */ + + RELAX_LITERAL_NR, + /* Expansion has been generated by an instruction that generates a + literal. However, the stretch has NOT been reported yet in this + fragment. */ + + RELAX_LITERAL_FINAL, + /* Expansion has been generated by an instruction that generates a + literal. */ + + RELAX_LITERAL_POOL_BEGIN, + RELAX_LITERAL_POOL_END, + /* Technically these are not relaxations at all, but mark a location + to store literals later. Note that fr_var stores the frchain for + BEGIN frags and fr_var stores now_seg for END frags. */ + + RELAX_NARROW, + /* The last instruction in this fragment (at->fr_opcode) can be + freely replaced with a single wider instruction if a future + alignment desires or needs it. */ + + RELAX_IMMED, + /* The last instruction in this fragment (at->fr_opcode) contains + the value defined by fr_symbol (fr_offset = 0). If the value + does not fit, use the specified expansion. This is similar to + "NARROW", except that these may not be expanded in order to align + code. */ + + RELAX_IMMED_STEP1, + /* The last instruction in this fragment (at->fr_opcode) contains a + literal. It has already been expanded at least 1 step. */ + + RELAX_IMMED_STEP2 + /* The last instruction in this fragment (at->fr_opcode) contains a + literal. It has already been expanded at least 2 steps. */ +}; + +/* This is used as a stopper to bound the number of steps that + can be taken. */ +#define RELAX_IMMED_MAXSTEPS (RELAX_IMMED_STEP2 - RELAX_IMMED) + + +typedef bfd_boolean (*frag_predicate) (const fragS *); + + +/* Directive functions. */ + +static bfd_boolean use_generics + PARAMS ((void)); +static bfd_boolean use_longcalls + PARAMS ((void)); +static bfd_boolean code_density_available + PARAMS ((void)); +static bfd_boolean can_relax + PARAMS ((void)); +static void directive_push + PARAMS ((directiveE, bfd_boolean, const void *)); +static void directive_pop + PARAMS ((directiveE *, bfd_boolean *, const char **, + unsigned int *, const void **)); +static void directive_balance + PARAMS ((void)); +static bfd_boolean inside_directive + PARAMS ((directiveE)); +static void get_directive + PARAMS ((directiveE *, bfd_boolean *)); +static void xtensa_begin_directive + PARAMS ((int)); +static void xtensa_end_directive + PARAMS ((int)); +static void xtensa_literal_prefix + PARAMS ((char const *, int)); +static void xtensa_literal_position + PARAMS ((int)); +static void xtensa_literal_pseudo + PARAMS ((int)); + +/* Parsing and Idiom Translation Functions. */ + +static const char *expression_end + PARAMS ((const char *)); +static unsigned tc_get_register + PARAMS ((const char *)); +static void expression_maybe_register + PARAMS ((xtensa_operand, expressionS *)); +static int tokenize_arguments + PARAMS ((char **, char *)); +static bfd_boolean parse_arguments + PARAMS ((TInsn *, int, char **)); +static int xg_translate_idioms + PARAMS ((char **, int *, char **)); +static int xg_translate_sysreg_op + PARAMS ((char **, int *, char **)); +static void xg_reverse_shift_count + PARAMS ((char **)); +static int xg_arg_is_constant + PARAMS ((char *, offsetT *)); +static void xg_replace_opname + PARAMS ((char **, char *)); +static int xg_check_num_args + PARAMS ((int *, int, char *, char **)); + +/* Functions for dealing with the Xtensa ISA. */ + +static bfd_boolean operand_is_immed + PARAMS ((xtensa_operand)); +static bfd_boolean operand_is_pcrel_label + PARAMS ((xtensa_operand)); +static int get_relaxable_immed + PARAMS ((xtensa_opcode)); +static xtensa_opcode get_opcode_from_buf + PARAMS ((const char *)); +static bfd_boolean is_direct_call_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_call_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_entry_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_loop_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_the_loop_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_jx_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_windowed_return_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_conditional_branch_opcode + PARAMS ((xtensa_opcode)); +static bfd_boolean is_branch_or_jump_opcode + PARAMS ((xtensa_opcode)); +static bfd_reloc_code_real_type opnum_to_reloc + PARAMS ((int)); +static int reloc_to_opnum + PARAMS ((bfd_reloc_code_real_type)); +static void xtensa_insnbuf_set_operand + PARAMS ((xtensa_insnbuf, xtensa_opcode, xtensa_operand, int32, + const char *, unsigned int)); +static uint32 xtensa_insnbuf_get_operand + PARAMS ((xtensa_insnbuf, xtensa_opcode, int)); +static void xtensa_insnbuf_set_immediate_field + PARAMS ((xtensa_opcode, xtensa_insnbuf, int32, const char *, + unsigned int)); +static bfd_boolean is_negatable_branch + PARAMS ((TInsn *)); + +/* Functions for Internal Lists of Symbols. */ +static void xtensa_define_label + PARAMS ((symbolS *)); +static void add_target_symbol + PARAMS ((symbolS *, bfd_boolean)); +static symbolS *xtensa_find_label + PARAMS ((fragS *, offsetT, bfd_boolean)); +static void map_over_defined_symbols + PARAMS ((void (*fn) (symbolS *))); +static bfd_boolean is_loop_target_label + PARAMS ((symbolS *)); +static void xtensa_mark_target_fragments + PARAMS ((void)); + +/* Various Other Internal Functions. */ + +static bfd_boolean is_unique_insn_expansion + PARAMS ((TransitionRule *)); +static int xg_get_insn_size + PARAMS ((TInsn *)); +static int xg_get_build_instr_size + PARAMS ((BuildInstr *)); +static bfd_boolean xg_is_narrow_insn + PARAMS ((TInsn *)); +static bfd_boolean xg_is_single_relaxable_insn + PARAMS ((TInsn *)); +static int xg_get_max_narrow_insn_size + PARAMS ((xtensa_opcode)); +static int xg_get_max_insn_widen_size + PARAMS ((xtensa_opcode)); +static int xg_get_max_insn_widen_literal_size + PARAMS ((xtensa_opcode)); +static bfd_boolean xg_is_relaxable_insn + PARAMS ((TInsn *, int)); +static symbolS *get_special_literal_symbol + PARAMS ((void)); +static symbolS *get_special_label_symbol + PARAMS ((void)); +static bfd_boolean xg_build_to_insn + PARAMS ((TInsn *, TInsn *, BuildInstr *)); +static bfd_boolean xg_build_to_stack + PARAMS ((IStack *, TInsn *, BuildInstr *)); +static bfd_boolean xg_expand_to_stack + PARAMS ((IStack *, TInsn *, int)); +static bfd_boolean xg_expand_narrow + PARAMS ((TInsn *, TInsn *)); +static bfd_boolean xg_immeds_fit + PARAMS ((const TInsn *)); +static bfd_boolean xg_symbolic_immeds_fit + PARAMS ((const TInsn *, segT, fragS *, offsetT, long)); +static bfd_boolean xg_check_operand + PARAMS ((int32, xtensa_operand)); +static int is_dnrange + PARAMS ((fragS *, symbolS *, long)); +static int xg_assembly_relax + PARAMS ((IStack *, TInsn *, segT, fragS *, offsetT, int, long)); +static void xg_force_frag_space + PARAMS ((int)); +static void xg_finish_frag + PARAMS ((char *, enum xtensa_relax_statesE, int, bfd_boolean)); +static bfd_boolean is_branch_jmp_to_next + PARAMS ((TInsn *, fragS *)); +static void xg_add_branch_and_loop_targets + PARAMS ((TInsn *)); +static bfd_boolean xg_instruction_matches_rule + PARAMS ((TInsn *, TransitionRule *)); +static TransitionRule *xg_instruction_match + PARAMS ((TInsn *)); +static bfd_boolean xg_build_token_insn + PARAMS ((BuildInstr *, TInsn *, TInsn *)); +static bfd_boolean xg_simplify_insn + PARAMS ((TInsn *, TInsn *)); +static bfd_boolean xg_expand_assembly_insn + PARAMS ((IStack *, TInsn *)); +static symbolS *xg_assemble_literal + PARAMS ((TInsn *)); +static void xg_assemble_literal_space + PARAMS ((int)); +static symbolS *xtensa_create_literal_symbol + PARAMS ((segT, fragS *)); +static symbolS *xtensa_create_local_symbol + PARAMS ((bfd *, const char *, segT, valueT, fragS *)); +static bfd_boolean get_is_linkonce_section + PARAMS ((bfd *, segT)); +static bfd_boolean xg_emit_insn + PARAMS ((TInsn *, bfd_boolean)); +static bfd_boolean xg_emit_insn_to_buf + PARAMS ((TInsn *, char *, fragS *, offsetT, bfd_boolean)); +static bfd_boolean xg_add_opcode_fix + PARAMS ((xtensa_opcode, int, expressionS *, fragS *, offsetT)); +static void xg_resolve_literals + PARAMS ((TInsn *, symbolS *)); +static void xg_resolve_labels + PARAMS ((TInsn *, symbolS *)); +static void xg_assemble_tokens + PARAMS ((TInsn *)); +static bfd_boolean is_register_writer + PARAMS ((const TInsn *, const char *, int)); +static bfd_boolean is_bad_loopend_opcode + PARAMS ((const TInsn *)); +static bfd_boolean is_unaligned_label + PARAMS ((symbolS *)); +static fragS *next_non_empty_frag + PARAMS ((const fragS *)); +static xtensa_opcode next_frag_opcode + PARAMS ((const fragS *)); +static void update_next_frag_nop_state + PARAMS ((fragS *)); +static bfd_boolean next_frag_is_branch_target + PARAMS ((const fragS *)); +static bfd_boolean next_frag_is_loop_target + PARAMS ((const fragS *)); +static addressT next_frag_pre_opcode_bytes + PARAMS ((const fragS *)); +static bfd_boolean is_next_frag_target + PARAMS ((const fragS *, const fragS *)); +static void xtensa_mark_literal_pool_location + PARAMS ((bfd_boolean)); +static void xtensa_move_labels + PARAMS ((fragS *, valueT, fragS *, valueT)); +static void assemble_nop + PARAMS ((size_t, char *)); +static addressT get_expanded_loop_offset + PARAMS ((xtensa_opcode)); +static fragS *get_literal_pool_location + PARAMS ((segT)); +static void set_literal_pool_location + PARAMS ((segT, fragS *)); + +/* Helpers for xtensa_end(). */ + +static void xtensa_cleanup_align_frags + PARAMS ((void)); +static void xtensa_fix_target_frags + PARAMS ((void)); +static bfd_boolean frag_can_negate_branch + PARAMS ((fragS *)); +static void xtensa_fix_a0_b_retw_frags + PARAMS ((void)); +static bfd_boolean next_instrs_are_b_retw + PARAMS ((fragS *)); +static void xtensa_fix_b_j_loop_end_frags + PARAMS ((void)); +static bfd_boolean next_instr_is_loop_end + PARAMS ((fragS *)); +static void xtensa_fix_close_loop_end_frags + PARAMS ((void)); +static size_t min_bytes_to_other_loop_end + PARAMS ((fragS *, fragS *, offsetT, size_t)); +static size_t unrelaxed_frag_min_size + PARAMS ((fragS *)); +static void xtensa_fix_short_loop_frags + PARAMS ((void)); +static size_t count_insns_to_loop_end + PARAMS ((fragS *, bfd_boolean, size_t)); +static size_t unrelaxed_frag_min_insn_count + PARAMS ((fragS *)); +static bfd_boolean branch_before_loop_end + PARAMS ((fragS *)); +static bfd_boolean unrelaxed_frag_has_b_j + PARAMS ((fragS *)); +static void xtensa_sanity_check + PARAMS ((void)); +static bfd_boolean is_empty_loop + PARAMS ((const TInsn *, fragS *)); +static bfd_boolean is_local_forward_loop + PARAMS ((const TInsn *, fragS *)); + +/* Alignment Functions. */ + +static size_t get_text_align_power + PARAMS ((int)); +static addressT get_text_align_max_fill_size + PARAMS ((int, bfd_boolean, bfd_boolean)); +static addressT get_text_align_fill_size + PARAMS ((addressT, int, int, bfd_boolean, bfd_boolean)); +static size_t get_text_align_nop_count + PARAMS ((size_t, bfd_boolean)); +static size_t get_text_align_nth_nop_size + PARAMS ((size_t, size_t, bfd_boolean)); +static addressT get_noop_aligned_address + PARAMS ((fragS *, addressT)); +static addressT get_widen_aligned_address + PARAMS ((fragS *, addressT)); + +/* Helpers for xtensa_relax_frag(). */ + +static long relax_frag_text_align + PARAMS ((fragS *, long)); +static long relax_frag_add_nop + PARAMS ((fragS *)); +static long relax_frag_narrow + PARAMS ((fragS *, long)); +static bfd_boolean future_alignment_required + PARAMS ((fragS *, long)); +static long relax_frag_immed + PARAMS ((segT, fragS *, long, int, int *)); + +/* Helpers for md_convert_frag(). */ + +static void convert_frag_align_next_opcode + PARAMS ((fragS *)); +static void convert_frag_narrow + PARAMS ((fragS *)); +static void convert_frag_immed + PARAMS ((segT, fragS *, int)); +static fixS *fix_new_exp_in_seg + PARAMS ((segT, subsegT, fragS *, int, int, expressionS *, int, + bfd_reloc_code_real_type)); +static void convert_frag_immed_finish_loop + PARAMS ((segT, fragS *, TInsn *)); +static offsetT get_expression_value + PARAMS ((segT, expressionS *)); + +/* Flags for the Last Instruction in Each Subsegment. */ + +static unsigned get_last_insn_flags + PARAMS ((segT, subsegT)); +static void set_last_insn_flags + PARAMS ((segT, subsegT, unsigned, bfd_boolean)); + +/* Segment list functions. */ + +static void xtensa_remove_section + PARAMS ((segT)); +static void xtensa_insert_section + PARAMS ((segT, segT)); +static void xtensa_move_seg_list_to_beginning + PARAMS ((seg_list *)); +static void xtensa_move_literals + PARAMS ((void)); +static void xtensa_move_frag_symbol + PARAMS ((symbolS *)); +static void xtensa_move_frag_symbols + PARAMS ((void)); +static void xtensa_reorder_seg_list + PARAMS ((seg_list *, segT)); +static void xtensa_reorder_segments + PARAMS ((void)); +static segT get_last_sec + PARAMS ((void)); +static void xtensa_switch_to_literal_fragment + PARAMS ((emit_state *)); +static void xtensa_switch_section_emit_state + PARAMS ((emit_state *, segT, subsegT)); +static void xtensa_restore_emit_state + PARAMS ((emit_state *)); +static void cache_literal_section + PARAMS ((seg_list *, const char *, segT *)); +static segT retrieve_literal_seg + PARAMS ((seg_list *, const char *)); +static segT seg_present + PARAMS ((const char *)); +static void add_seg_list + PARAMS ((seg_list *, segT)); + +/* Property Table (e.g., ".xt.insn" and ".xt.lit") Functions. */ + +static void xtensa_create_property_segments + PARAMS ((frag_predicate, const char *, xt_section_type)); +static segment_info_type *retrieve_segment_info + PARAMS ((segT)); +static segT retrieve_xtensa_section + PARAMS ((char *)); +static bfd_boolean section_has_property + PARAMS ((segT sec, frag_predicate)); +static void add_xt_block_frags + PARAMS ((segT, segT, xtensa_block_info **, frag_predicate)); +static bfd_boolean get_frag_is_literal + PARAMS ((const fragS *)); +static bfd_boolean get_frag_is_insn + PARAMS ((const fragS *)); + +/* Import from elf32-xtensa.c in BFD library. */ +extern char *xtensa_get_property_section_name + PARAMS ((bfd *, asection *, const char *)); + +/* TInsn and IStack functions. */ +static bfd_boolean tinsn_has_symbolic_operands + PARAMS ((const TInsn *)); +static bfd_boolean tinsn_has_invalid_symbolic_operands + PARAMS ((const TInsn *)); +static bfd_boolean tinsn_has_complex_operands + PARAMS ((const TInsn *)); +static bfd_boolean tinsn_to_insnbuf + PARAMS ((TInsn *, xtensa_insnbuf)); +static bfd_boolean tinsn_check_arguments + PARAMS ((const TInsn *)); +static void tinsn_from_chars + PARAMS ((TInsn *, char *)); +static void tinsn_immed_from_frag + PARAMS ((TInsn *, fragS *)); +static int get_num_stack_text_bytes + PARAMS ((IStack *)); +static int get_num_stack_literal_bytes + PARAMS ((IStack *)); + +/* Expression Utilities. */ +bfd_boolean expr_is_const + PARAMS ((const expressionS *)); +offsetT get_expr_const + PARAMS ((const expressionS *)); +void set_expr_const + PARAMS ((expressionS *, offsetT)); +void set_expr_symbol_offset + PARAMS ((expressionS *, symbolS *, offsetT)); +bfd_boolean expr_is_equal + PARAMS ((expressionS *, expressionS *)); +static void copy_expr + PARAMS ((expressionS *, const expressionS *)); + +#ifdef XTENSA_SECTION_RENAME +static void build_section_rename + PARAMS ((const char *)); +static void add_section_rename + PARAMS ((char *, char *)); +#endif + +#ifdef XTENSA_COMBINE_LITERALS +static void find_lit_sym_translation + PARAMS ((expressionS *)); +static void add_lit_sym_translation + PARAMS ((char *, offsetT, symbolS *)); +#endif + + +/* ISA imported from bfd. */ +extern xtensa_isa xtensa_default_isa; + +extern int target_big_endian; + +static xtensa_opcode xtensa_addi_opcode; +static xtensa_opcode xtensa_addmi_opcode; +static xtensa_opcode xtensa_call0_opcode; +static xtensa_opcode xtensa_call4_opcode; +static xtensa_opcode xtensa_call8_opcode; +static xtensa_opcode xtensa_call12_opcode; +static xtensa_opcode xtensa_callx0_opcode; +static xtensa_opcode xtensa_callx4_opcode; +static xtensa_opcode xtensa_callx8_opcode; +static xtensa_opcode xtensa_callx12_opcode; +static xtensa_opcode xtensa_entry_opcode; +static xtensa_opcode xtensa_isync_opcode; +static xtensa_opcode xtensa_j_opcode; +static xtensa_opcode xtensa_jx_opcode; +static xtensa_opcode xtensa_loop_opcode; +static xtensa_opcode xtensa_loopnez_opcode; +static xtensa_opcode xtensa_loopgtz_opcode; +static xtensa_opcode xtensa_nop_n_opcode; +static xtensa_opcode xtensa_or_opcode; +static xtensa_opcode xtensa_ret_opcode; +static xtensa_opcode xtensa_ret_n_opcode; +static xtensa_opcode xtensa_retw_opcode; +static xtensa_opcode xtensa_retw_n_opcode; +static xtensa_opcode xtensa_rsr_opcode; +static xtensa_opcode xtensa_waiti_opcode; + + +/* Command-line Options. */ + +bfd_boolean use_literal_section = TRUE; +static bfd_boolean align_targets = TRUE; +static bfd_boolean align_only_targets = FALSE; +static bfd_boolean software_a0_b_retw_interlock = TRUE; +static bfd_boolean has_a0_b_retw = FALSE; +static bfd_boolean workaround_a0_b_retw = TRUE; + +static bfd_boolean software_avoid_b_j_loop_end = TRUE; +static bfd_boolean workaround_b_j_loop_end = TRUE; +static bfd_boolean maybe_has_b_j_loop_end = FALSE; + +static bfd_boolean software_avoid_short_loop = TRUE; +static bfd_boolean workaround_short_loop = TRUE; +static bfd_boolean maybe_has_short_loop = FALSE; + +static bfd_boolean software_avoid_close_loop_end = TRUE; +static bfd_boolean workaround_close_loop_end = TRUE; +static bfd_boolean maybe_has_close_loop_end = FALSE; + +/* When avoid_short_loops is true, all loops with early exits must + have at least 3 instructions. avoid_all_short_loops is a modifier + to the avoid_short_loop flag. In addition to the avoid_short_loop + actions, all straightline loopgtz and loopnez must have at least 3 + instructions. */ + +static bfd_boolean software_avoid_all_short_loops = TRUE; +static bfd_boolean workaround_all_short_loops = TRUE; + +/* This is on a per-instruction basis. */ +static bfd_boolean specific_opcode = FALSE; + +enum +{ + option_density = OPTION_MD_BASE, + option_no_density, + + option_relax, + option_no_relax, + + option_generics, + option_no_generics, + + option_text_section_literals, + option_no_text_section_literals, + + option_align_targets, + option_no_align_targets, + + option_align_only_targets, + option_no_align_only_targets, + + option_longcalls, + option_no_longcalls, + + option_workaround_a0_b_retw, + option_no_workaround_a0_b_retw, + + option_workaround_b_j_loop_end, + option_no_workaround_b_j_loop_end, + + option_workaround_short_loop, + option_no_workaround_short_loop, + + option_workaround_all_short_loops, + option_no_workaround_all_short_loops, + + option_workaround_close_loop_end, + option_no_workaround_close_loop_end, + + option_no_workarounds, + +#ifdef XTENSA_SECTION_RENAME + option_literal_section_name, + option_text_section_name, + option_data_section_name, + option_bss_section_name, + option_rename_section_name, +#endif + + option_eb, + option_el +}; + +const char *md_shortopts = ""; + +struct option md_longopts[] = +{ + {"density", no_argument, NULL, option_density}, + {"no-density", no_argument, NULL, option_no_density}, + /* At least as early as alameda, --[no-]relax didn't work as + documented, so as of albany, --[no-]relax is equivalent to + --[no-]generics. Both of these will be deprecated in + BearValley. */ + {"relax", no_argument, NULL, option_generics}, + {"no-relax", no_argument, NULL, option_no_generics}, + {"generics", no_argument, NULL, option_generics}, + {"no-generics", no_argument, NULL, option_no_generics}, + {"text-section-literals", no_argument, NULL, option_text_section_literals}, + {"no-text-section-literals", no_argument, NULL, + option_no_text_section_literals}, + /* This option was changed from -align-target to -target-align + because it conflicted with the "-al" option. */ + {"target-align", no_argument, NULL, option_align_targets}, + {"no-target-align", no_argument, NULL, + option_no_align_targets}, +#if 0 + /* This option should do a better job aligning targets because + it will only attempt to align targets that are the target of a + branch. */ + { "target-align-only", no_argument, NULL, option_align_only_targets }, + { "no-target-align-only", no_argument, NULL, option_no_align_only_targets }, +#endif /* 0 */ + {"longcalls", no_argument, NULL, option_longcalls}, + {"no-longcalls", no_argument, NULL, option_no_longcalls}, + + {"no-workaround-a0-b-retw", no_argument, NULL, + option_no_workaround_a0_b_retw}, + {"workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw}, + + {"no-workaround-b-j-loop-end", no_argument, NULL, + option_no_workaround_b_j_loop_end}, + {"workaround-b-j-loop-end", no_argument, NULL, + option_workaround_b_j_loop_end}, + + {"no-workaround-short-loops", no_argument, NULL, + option_no_workaround_short_loop}, + {"workaround-short-loops", no_argument, NULL, option_workaround_short_loop}, + + {"no-workaround-all-short-loops", no_argument, NULL, + option_no_workaround_all_short_loops}, + {"workaround-all-short-loop", no_argument, NULL, + option_workaround_all_short_loops}, + + {"no-workaround-close-loop-end", no_argument, NULL, + option_no_workaround_close_loop_end}, + {"workaround-close-loop-end", no_argument, NULL, + option_workaround_close_loop_end}, + + {"no-workarounds", no_argument, NULL, option_no_workarounds}, + +#ifdef XTENSA_SECTION_RENAME + {"literal-section-name", required_argument, NULL, + option_literal_section_name}, + {"text-section-name", required_argument, NULL, + option_text_section_name}, + {"data-section-name", required_argument, NULL, + option_data_section_name}, + {"rename-section", required_argument, NULL, + option_rename_section_name}, + {"bss-section-name", required_argument, NULL, + option_bss_section_name}, +#endif /* XTENSA_SECTION_RENAME */ + + {NULL, no_argument, NULL, 0} +}; + +size_t md_longopts_size = sizeof md_longopts; + + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + switch (c) + { + case option_density: + if (!density_supported) + { + as_bad (_("'--density' option not supported in this Xtensa " + "configuration")); + return 0; + } + directive_state[directive_density] = TRUE; + return 1; + case option_no_density: + directive_state[directive_density] = FALSE; + return 1; + case option_generics: + directive_state[directive_generics] = TRUE; + return 1; + case option_no_generics: + directive_state[directive_generics] = FALSE; + return 1; + case option_longcalls: + directive_state[directive_longcalls] = TRUE; + return 1; + case option_no_longcalls: + directive_state[directive_longcalls] = FALSE; + return 1; + case option_text_section_literals: + use_literal_section = FALSE; + return 1; + case option_no_text_section_literals: + use_literal_section = TRUE; + return 1; + case option_workaround_a0_b_retw: + workaround_a0_b_retw = TRUE; + software_a0_b_retw_interlock = TRUE; + return 1; + case option_no_workaround_a0_b_retw: + workaround_a0_b_retw = FALSE; + software_a0_b_retw_interlock = FALSE; + return 1; + case option_workaround_b_j_loop_end: + workaround_b_j_loop_end = TRUE; + software_avoid_b_j_loop_end = TRUE; + return 1; + case option_no_workaround_b_j_loop_end: + workaround_b_j_loop_end = FALSE; + software_avoid_b_j_loop_end = FALSE; + return 1; + + case option_workaround_short_loop: + workaround_short_loop = TRUE; + software_avoid_short_loop = TRUE; + return 1; + case option_no_workaround_short_loop: + workaround_short_loop = FALSE; + software_avoid_short_loop = FALSE; + return 1; + + case option_workaround_all_short_loops: + workaround_all_short_loops = TRUE; + software_avoid_all_short_loops = TRUE; + return 1; + case option_no_workaround_all_short_loops: + workaround_all_short_loops = FALSE; + software_avoid_all_short_loops = FALSE; + return 1; + + case option_workaround_close_loop_end: + workaround_close_loop_end = TRUE; + software_avoid_close_loop_end = TRUE; + return 1; + case option_no_workaround_close_loop_end: + workaround_close_loop_end = FALSE; + software_avoid_close_loop_end = FALSE; + return 1; + + case option_no_workarounds: + workaround_a0_b_retw = FALSE; + software_a0_b_retw_interlock = FALSE; + workaround_b_j_loop_end = FALSE; + software_avoid_b_j_loop_end = FALSE; + workaround_short_loop = FALSE; + software_avoid_short_loop = FALSE; + workaround_all_short_loops = FALSE; + software_avoid_all_short_loops = FALSE; + workaround_close_loop_end = FALSE; + software_avoid_close_loop_end = FALSE; + return 1; + + case option_align_targets: + align_targets = TRUE; + return 1; + case option_no_align_targets: + align_targets = FALSE; + return 1; + + case option_align_only_targets: + align_only_targets = TRUE; + return 1; + case option_no_align_only_targets: + align_only_targets = FALSE; + return 1; + +#ifdef XTENSA_SECTION_RENAME + case option_literal_section_name: + add_section_rename (".literal", arg); + as_warn (_("'--literal-section-name' is deprecated; " + "use '--rename-section .literal=NEWNAME'")); + return 1; + + case option_text_section_name: + add_section_rename (".text", arg); + as_warn (_("'--text-section-name' is deprecated; " + "use '--rename-section .text=NEWNAME'")); + return 1; + + case option_data_section_name: + add_section_rename (".data", arg); + as_warn (_("'--data-section-name' is deprecated; " + "use '--rename-section .data=NEWNAME'")); + return 1; + + case option_bss_section_name: + add_section_rename (".bss", arg); + as_warn (_("'--bss-section-name' is deprecated; " + "use '--rename-section .bss=NEWNAME'")); + return 1; + + case option_rename_section_name: + build_section_rename (arg); + return 1; +#endif /* XTENSA_SECTION_RENAME */ + + case 'Q': + /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section + should be emitted or not. FIXME: Not implemented. */ + return 1; + + default: + return 0; + } +} + + +void +md_show_usage (stream) + FILE *stream; +{ + fputs ("\nXtensa options:\n" + "--[no-]density [Do not] emit density instructions\n" + "--[no-]relax [Do not] perform branch relaxation\n" + "--[no-]generics [Do not] transform instructions\n" + "--[no-]longcalls [Do not] emit 32-bit call sequences\n" + "--[no-]target-align [Do not] try to align branch targets\n" + "--[no-]text-section-literals\n" + " [Do not] put literals in the text section\n" + "--no-workarounds Do not use any Xtensa workarounds\n" +#ifdef XTENSA_SECTION_RENAME + "--rename-section old=new(:old1=new1)*\n" + " Rename section 'old' to 'new'\n" + "\nThe following Xtensa options are deprecated\n" + "--literal-section-name Name of literal section (default .literal)\n" + "--text-section-name Name of text section (default .text)\n" + "--data-section-name Name of data section (default .data)\n" + "--bss-section-name Name of bss section (default .bss)\n" +#endif + , stream); +} + + +/* Directive data and functions. */ + +typedef struct state_stackS_struct +{ + directiveE directive; + bfd_boolean negated; + bfd_boolean old_state; + const char *file; + unsigned int line; + const void *datum; + struct state_stackS_struct *prev; +} state_stackS; + +state_stackS *directive_state_stack; + +const pseudo_typeS md_pseudo_table[] = +{ + {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */ + {"literal_position", xtensa_literal_position, 0}, + {"frame", s_ignore, 0}, /* formerly used for STABS debugging */ + {"word", cons, 4}, + {"begin", xtensa_begin_directive, 0}, + {"end", xtensa_end_directive, 0}, + {"file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0}, + {"loc", dwarf2_directive_loc, 0}, + {"literal", xtensa_literal_pseudo, 0}, + {NULL, 0, 0}, +}; + + +bfd_boolean +use_generics () +{ + return directive_state[directive_generics]; +} + + +bfd_boolean +use_longcalls () +{ + return directive_state[directive_longcalls]; +} + + +bfd_boolean +code_density_available () +{ + return directive_state[directive_density]; +} + + +bfd_boolean +can_relax () +{ + return use_generics (); +} + + +static void +directive_push (directive, negated, datum) + directiveE directive; + bfd_boolean negated; + const void *datum; +{ + char *file; + unsigned int line; + state_stackS *stack = (state_stackS *) xmalloc (sizeof (state_stackS)); + + as_where (&file, &line); + + stack->directive = directive; + stack->negated = negated; + stack->old_state = directive_state[directive]; + stack->file = file; + stack->line = line; + stack->datum = datum; + stack->prev = directive_state_stack; + directive_state_stack = stack; + + directive_state[directive] = !negated; +} + +static void +directive_pop (directive, negated, file, line, datum) + directiveE *directive; + bfd_boolean *negated; + const char **file; + unsigned int *line; + const void **datum; +{ + state_stackS *top = directive_state_stack; + + if (!directive_state_stack) + { + as_bad (_("unmatched end directive")); + *directive = directive_none; + return; + } + + directive_state[directive_state_stack->directive] = top->old_state; + *directive = top->directive; + *negated = top->negated; + *file = top->file; + *line = top->line; + *datum = top->datum; + directive_state_stack = top->prev; + free (top); +} + + +static void +directive_balance () +{ + while (directive_state_stack) + { + directiveE directive; + bfd_boolean negated; + const char *file; + unsigned int line; + const void *datum; + + directive_pop (&directive, &negated, &file, &line, &datum); + as_warn_where ((char *) file, line, + _(".begin directive with no matching .end directive")); + } +} + + +static bfd_boolean +inside_directive (dir) + directiveE dir; +{ + state_stackS *top = directive_state_stack; + + while (top && top->directive != dir) + top = top->prev; + + return (top != NULL); +} + + +static void +get_directive (directive, negated) + directiveE *directive; + bfd_boolean *negated; +{ + int len; + unsigned i; + + if (strncmp (input_line_pointer, "no-", 3) != 0) + *negated = FALSE; + else + { + *negated = TRUE; + input_line_pointer += 3; + } + + len = strspn (input_line_pointer, + "abcdefghijklmnopqrstuvwxyz_/0123456789."); + + for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i) + { + if (strncmp (input_line_pointer, directive_info[i].name, len) == 0) + { + input_line_pointer += len; + *directive = (directiveE) i; + if (*negated && !directive_info[i].can_be_negated) + as_bad (_("directive %s can't be negated"), + directive_info[i].name); + return; + } + } + + as_bad (_("unknown directive")); + *directive = (directiveE) XTENSA_UNDEFINED; +} + + +static void +xtensa_begin_directive (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + directiveE directive; + bfd_boolean negated; + emit_state *state; + int len; + lit_state *ls; + + get_directive (&directive, &negated); + if (directive == (directiveE) XTENSA_UNDEFINED) + { + discard_rest_of_line (); + return; + } + + switch (directive) + { + case directive_literal: + state = (emit_state *) xmalloc (sizeof (emit_state)); + xtensa_switch_to_literal_fragment (state); + directive_push (directive_literal, negated, state); + break; + + case directive_literal_prefix: + /* Check to see if the current fragment is a literal + fragment. If it is, then this operation is not allowed. */ + if (frag_now->tc_frag_data.is_literal) + { + as_bad (_("cannot set literal_prefix inside literal fragment")); + return; + } + + /* Allocate the literal state for this section and push + onto the directive stack. */ + ls = xmalloc (sizeof (lit_state)); + assert (ls); + + *ls = default_lit_sections; + + directive_push (directive_literal_prefix, negated, ls); + + /* Parse the new prefix from the input_line_pointer. */ + SKIP_WHITESPACE (); + len = strspn (input_line_pointer, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz_/0123456789.$"); + + /* Process the new prefix. */ + xtensa_literal_prefix (input_line_pointer, len); + + /* Skip the name in the input line. */ + input_line_pointer += len; + break; + + case directive_freeregs: + /* This information is currently unused, but we'll accept the statement + and just discard the rest of the line. This won't check the syntax, + but it will accept every correct freeregs directive. */ + input_line_pointer += strcspn (input_line_pointer, "\n"); + directive_push (directive_freeregs, negated, 0); + break; + + case directive_density: + if (!density_supported && !negated) + { + as_warn (_("Xtensa density option not supported; ignored")); + break; + } + /* fall through */ + + default: + directive_push (directive, negated, 0); + break; + } + + demand_empty_rest_of_line (); +} + + +static void +xtensa_end_directive (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + directiveE begin_directive, end_directive; + bfd_boolean begin_negated, end_negated; + const char *file; + unsigned int line; + emit_state *state; + lit_state *s; + + get_directive (&end_directive, &end_negated); + if (end_directive == (directiveE) XTENSA_UNDEFINED) + { + discard_rest_of_line (); + return; + } + + if (end_directive == directive_density && !density_supported && !end_negated) + { + as_warn (_("Xtensa density option not supported; ignored")); + demand_empty_rest_of_line (); + return; + } + + directive_pop (&begin_directive, &begin_negated, &file, &line, + (const void **) &state); + + if (begin_directive != directive_none) + { + if (begin_directive != end_directive || begin_negated != end_negated) + { + as_bad (_("does not match begin %s%s at %s:%d"), + begin_negated ? "no-" : "", + directive_info[begin_directive].name, file, line); + } + else + { + switch (end_directive) + { + case directive_literal: + frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL); + xtensa_restore_emit_state (state); + free (state); + break; + + case directive_freeregs: + break; + + case directive_literal_prefix: + /* Restore the default collection sections from saved state. */ + s = (lit_state *) state; + assert (s); + + if (use_literal_section) + default_lit_sections = *s; + + /* free the state storage */ + free (s); + break; + + default: + break; + } + } + } + + demand_empty_rest_of_line (); +} + + +/* Place an aligned literal fragment at the current location. */ + +static void +xtensa_literal_position (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + if (inside_directive (directive_literal)) + as_warn (_(".literal_position inside literal directive; ignoring")); + else if (!use_literal_section) + xtensa_mark_literal_pool_location (FALSE); + + demand_empty_rest_of_line (); +} + + +/* Support .literal label, value@plt + offset. */ + +static void +xtensa_literal_pseudo (ignored) + int ignored ATTRIBUTE_UNUSED; +{ + emit_state state; + char *base_name; +#ifdef XTENSA_COMBINE_LITERALS + char *next_name; + symbolS *duplicate; + bfd_boolean used_name = FALSE; + int offset = 0; +#endif + char c; + char *p; + expressionS expP; + segT dest_seg; + + /* If we are using text-section literals, then this is the right value... */ + dest_seg = now_seg; + + base_name = input_line_pointer; + + xtensa_switch_to_literal_fragment (&state); + + /* ...but if we aren't using text-section-literals, then we + need to put them in the section we just switched to. */ + if (use_literal_section) + dest_seg = now_seg; + + /* All literals are aligned to four-byte boundaries + which is handled by switch to literal fragment. */ + /* frag_align (2, 0, 0); */ + + c = get_symbol_end (); + /* Just after name is now '\0'. */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + + if (*input_line_pointer != ',' && *input_line_pointer != ':') + { + as_bad (_("expected comma or colon after symbol name; " + "rest of line ignored")); + ignore_rest_of_line (); + xtensa_restore_emit_state (&state); + return; + } + *p = 0; + +#ifdef XTENSA_COMBINE_LITERALS + /* We need next name to start out equal to base_name, + but we modify it later to refer to a symbol and an offset. */ + next_name = xmalloc (strlen (base_name) + 1); + strcpy (next_name, base_name); + + /* We need a copy of base_name because we refer to it in the + lit_sym_translations and the source is somewhere in the input stream. */ + base_name = xmalloc (strlen (base_name) + 1); + strcpy (base_name, next_name); + +#else + + colon (base_name); +#endif + + do + { + input_line_pointer++; /* skip ',' or ':' */ + + expr (0, &expP); + +#ifdef XTENSA_COMBINE_LITERALS + duplicate = is_duplicate_literal (&expP, dest_seg); + if (duplicate) + { + add_lit_sym_translation (base_name, offset, duplicate); + used_name = TRUE; + continue; + } + colon (next_name); +#endif + + /* We only support 4-byte literals with .literal. */ + emit_expr (&expP, 4); + +#ifdef XTENSA_COMBINE_LITERALS + cache_literal (next_name, &expP, dest_seg); + free (next_name); + + if (*input_line_pointer == ',') + { + offset += 4; + next_name = xmalloc (strlen (base_name) + + strlen (XTENSA_LIT_PLUS_OFFSET) + 10); + sprintf (next_name, "%s%s%d", + XTENSA_LIT_PLUS_OFFSET, base_name, offset); + } +#endif + } + while (*input_line_pointer == ','); + + *p = c; +#ifdef XTENSA_COMBINE_LITERALS + if (!used_name) + free (base_name); +#endif + + demand_empty_rest_of_line (); + + xtensa_restore_emit_state (&state); +} + + +static void +xtensa_literal_prefix (start, len) + char const *start; + int len; +{ + segT s_now; /* Storage for the current seg and subseg. */ + subsegT ss_now; + char *name; /* Pointer to the name itself. */ + char *newname; + + if (!use_literal_section) + return; + + /* Store away the current section and subsection. */ + s_now = now_seg; + ss_now = now_subseg; + + /* Get a null-terminated copy of the name. */ + name = xmalloc (len + 1); + assert (name); + + strncpy (name, start, len); + name[len] = 0; + + /* Allocate the sections (interesting note: the memory pointing to + the name is actually used for the name by the new section). */ + newname = xmalloc (len + strlen (".literal") + 1); + strcpy (newname, name); + strcpy (newname + len, ".literal"); + + /* Note that retrieve_literal_seg does not create a segment if + it already exists. */ + default_lit_sections.lit_seg = NULL; /* retrieved on demand */ + + /* Canonicalizing section names allows renaming literal + sections to occur correctly. */ + default_lit_sections.lit_seg_name = + tc_canonicalize_symbol_name (newname); + + free (name); + + /* Restore the current section and subsection and set the + generation into the old segment. */ + subseg_set (s_now, ss_now); +} + + +/* Parsing and Idiom Translation. */ + +static const char * +expression_end (name) + const char *name; +{ + while (1) + { + switch (*name) + { + case ';': + case '\0': + case ',': + return name; + case ' ': + case '\t': + ++name; + continue; + default: + return 0; + } + } +} + + +#define ERROR_REG_NUM ((unsigned) -1) + +static unsigned +tc_get_register (prefix) + const char *prefix; +{ + unsigned reg; + const char *next_expr; + const char *old_line_pointer; + + SKIP_WHITESPACE (); + old_line_pointer = input_line_pointer; + + if (*input_line_pointer == '$') + ++input_line_pointer; + + /* Accept "sp" as a synonym for "a1". */ + if (input_line_pointer[0] == 's' && input_line_pointer[1] == 'p' + && expression_end (input_line_pointer + 2)) + { + input_line_pointer += 2; + return 1; /* AR[1] */ + } + + while (*input_line_pointer++ == *prefix++) + ; + --input_line_pointer; + --prefix; + + if (*prefix) + { + as_bad (_("bad register name: %s"), old_line_pointer); + return ERROR_REG_NUM; + } + + if (!ISDIGIT ((unsigned char) *input_line_pointer)) + { + as_bad (_("bad register number: %s"), input_line_pointer); + return ERROR_REG_NUM; + } + + reg = 0; + + while (ISDIGIT ((int) *input_line_pointer)) + reg = reg * 10 + *input_line_pointer++ - '0'; + + if (!(next_expr = expression_end (input_line_pointer))) + { + as_bad (_("bad register name: %s"), old_line_pointer); + return ERROR_REG_NUM; + } + + input_line_pointer = (char *) next_expr; + + return reg; +} + + +#define PLT_SUFFIX "@PLT" +#define plt_suffix "@plt" + +static void +expression_maybe_register (opnd, tok) + xtensa_operand opnd; + expressionS *tok; +{ + char *kind = xtensa_operand_kind (opnd); + + if ((strlen (kind) == 1) + && (*kind == 'l' || *kind == 'L' || *kind == 'i' || *kind == 'r')) + { + segT t = expression (tok); + if (t == absolute_section && operand_is_pcrel_label (opnd)) + { + assert (tok->X_op == O_constant); + tok->X_op = O_symbol; + tok->X_add_symbol = &abs_symbol; + } + if (tok->X_op == O_symbol + && (!strncmp (input_line_pointer, PLT_SUFFIX, + strlen (PLT_SUFFIX) - 1) + || !strncmp (input_line_pointer, plt_suffix, + strlen (plt_suffix) - 1))) + { + tok->X_add_symbol->sy_tc.plt = 1; + input_line_pointer += strlen (plt_suffix); + } +#ifdef XTENSA_COMBINE_LITERALS + find_lit_sym_translation (tok); +#endif + } + else + { + unsigned reg = tc_get_register (kind); + + if (reg != ERROR_REG_NUM) /* Already errored */ + { + uint32 buf = reg; + if ((xtensa_operand_encode (opnd, &buf) != xtensa_encode_result_ok) + || (reg != xtensa_operand_decode (opnd, buf))) + as_bad (_("register number out of range")); + } + + tok->X_op = O_register; + tok->X_add_symbol = 0; + tok->X_add_number = reg; + } +} + + +/* Split up the arguments for an opcode or pseudo-op. */ + +static int +tokenize_arguments (args, str) + char **args; + char *str; +{ + char *old_input_line_pointer; + bfd_boolean saw_comma = FALSE; + bfd_boolean saw_arg = FALSE; + int num_args = 0; + char *arg_end, *arg; + int arg_len; + + /* Save and restore input_line_pointer around this function. */ + old_input_line_pointer = input_line_pointer; + input_line_pointer = str; + + while (*input_line_pointer) + { + SKIP_WHITESPACE (); + switch (*input_line_pointer) + { + case '\0': + goto fini; + + case ',': + input_line_pointer++; + if (saw_comma || !saw_arg) + goto err; + saw_comma = TRUE; + break; + + default: + if (!saw_comma && saw_arg) + goto err; + + arg_end = input_line_pointer + 1; + while (!expression_end (arg_end)) + arg_end += 1; + + arg_len = arg_end - input_line_pointer; + arg = (char *) xmalloc (arg_len + 1); + args[num_args] = arg; + + strncpy (arg, input_line_pointer, arg_len); + arg[arg_len] = '\0'; + + input_line_pointer = arg_end; + num_args += 1; + saw_comma = FALSE; + saw_arg = TRUE; + break; + } + } + +fini: + if (saw_comma) + goto err; + input_line_pointer = old_input_line_pointer; + return num_args; + +err: + input_line_pointer = old_input_line_pointer; + return -1; +} + + +/* Parse the arguments to an opcode. Return true on error. */ + +static bfd_boolean +parse_arguments (insn, num_args, arg_strings) + TInsn *insn; + int num_args; + char **arg_strings; +{ + expressionS *tok = insn->tok; + xtensa_opcode opcode = insn->opcode; + bfd_boolean had_error = TRUE; + xtensa_isa isa = xtensa_default_isa; + int n; + int opcode_operand_count; + int actual_operand_count = 0; + xtensa_operand opnd = NULL; + char *old_input_line_pointer; + + if (insn->insn_type == ITYPE_LITERAL) + opcode_operand_count = 1; + else + opcode_operand_count = xtensa_num_operands (isa, opcode); + + memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS); + + /* Save and restore input_line_pointer around this function. */ + old_input_line_pointer = input_line_pointer; + + for (n = 0; n < num_args; n++) + { + input_line_pointer = arg_strings[n]; + + if (actual_operand_count >= opcode_operand_count) + { + as_warn (_("too many arguments")); + goto err; + } + assert (actual_operand_count < MAX_INSN_ARGS); + + opnd = xtensa_get_operand (isa, opcode, actual_operand_count); + expression_maybe_register (opnd, tok); + + if (tok->X_op == O_illegal || tok->X_op == O_absent) + goto err; + actual_operand_count++; + tok++; + } + + insn->ntok = tok - insn->tok; + had_error = FALSE; + + err: + input_line_pointer = old_input_line_pointer; + return had_error; +} + + +static void +xg_reverse_shift_count (cnt_argp) + char **cnt_argp; +{ + char *cnt_arg, *new_arg; + cnt_arg = *cnt_argp; + + /* replace the argument with "31-(argument)" */ + new_arg = (char *) xmalloc (strlen (cnt_arg) + 6); + sprintf (new_arg, "31-(%s)", cnt_arg); + + free (cnt_arg); + *cnt_argp = new_arg; +} + + +/* If "arg" is a constant expression, return non-zero with the value + in *valp. */ + +static int +xg_arg_is_constant (arg, valp) + char *arg; + offsetT *valp; +{ + expressionS exp; + char *save_ptr = input_line_pointer; + + input_line_pointer = arg; + expression (&exp); + input_line_pointer = save_ptr; + + if (exp.X_op == O_constant) + { + *valp = exp.X_add_number; + return 1; + } + + return 0; +} + + +static void +xg_replace_opname (popname, newop) + char **popname; + char *newop; +{ + free (*popname); + *popname = (char *) xmalloc (strlen (newop) + 1); + strcpy (*popname, newop); +} + + +static int +xg_check_num_args (pnum_args, expected_num, opname, arg_strings) + int *pnum_args; + int expected_num; + char *opname; + char **arg_strings; +{ + int num_args = *pnum_args; + + if (num_args < expected_num) + { + as_bad (_("not enough operands (%d) for '%s'; expected %d"), + num_args, opname, expected_num); + return -1; + } + + if (num_args > expected_num) + { + as_warn (_("too many operands (%d) for '%s'; expected %d"), + num_args, opname, expected_num); + while (num_args-- > expected_num) + { + free (arg_strings[num_args]); + arg_strings[num_args] = 0; + } + *pnum_args = expected_num; + return -1; + } + + return 0; +} + + +static int +xg_translate_sysreg_op (popname, pnum_args, arg_strings) + char **popname; + int *pnum_args; + char **arg_strings; +{ + char *opname, *new_opname; + offsetT val; + bfd_boolean has_underbar = FALSE; + + opname = *popname; + if (*opname == '_') + { + has_underbar = TRUE; + opname += 1; + } + + /* Opname == [rw]ur... */ + + if (opname[3] == '\0') + { + /* If the register is not specified as part of the opcode, + then get it from the operand and move it to the opcode. */ + + if (xg_check_num_args (pnum_args, 2, opname, arg_strings)) + return -1; + + if (!xg_arg_is_constant (arg_strings[1], &val)) + { + as_bad (_("register number for `%s' is not a constant"), opname); + return -1; + } + if ((unsigned) val > 255) + { + as_bad (_("register number (%ld) for `%s' is out of range"), + val, opname); + return -1; + } + + /* Remove the last argument, which is now part of the opcode. */ + free (arg_strings[1]); + arg_strings[1] = 0; + *pnum_args = 1; + + /* Translate the opcode. */ + new_opname = (char *) xmalloc (8); + sprintf (new_opname, "%s%cur%u", (has_underbar ? "_" : ""), + opname[0], (unsigned) val); + free (*popname); + *popname = new_opname; + } + + return 0; +} + + +/* If the instruction is an idiom (i.e., a built-in macro), translate it. + Returns non-zero if an error was found. */ + +static int +xg_translate_idioms (popname, pnum_args, arg_strings) + char **popname; + int *pnum_args; + char **arg_strings; +{ + char *opname = *popname; + bfd_boolean has_underbar = FALSE; + + if (*opname == '_') + { + has_underbar = TRUE; + opname += 1; + } + + if (strcmp (opname, "mov") == 0) + { + if (!has_underbar && code_density_available ()) + xg_replace_opname (popname, "mov.n"); + else + { + if (xg_check_num_args (pnum_args, 2, opname, arg_strings)) + return -1; + xg_replace_opname (popname, (has_underbar ? "_or" : "or")); + arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1); + strcpy (arg_strings[2], arg_strings[1]); + *pnum_args = 3; + } + return 0; + } + + if (strcmp (opname, "bbsi.l") == 0) + { + if (xg_check_num_args (pnum_args, 3, opname, arg_strings)) + return -1; + xg_replace_opname (popname, (has_underbar ? "_bbsi" : "bbsi")); + if (target_big_endian) + xg_reverse_shift_count (&arg_strings[1]); + return 0; + } + + if (strcmp (opname, "bbci.l") == 0) + { + if (xg_check_num_args (pnum_args, 3, opname, arg_strings)) + return -1; + xg_replace_opname (popname, (has_underbar ? "_bbci" : "bbci")); + if (target_big_endian) + xg_reverse_shift_count (&arg_strings[1]); + return 0; + } + + if (strcmp (opname, "nop") == 0) + { + if (!has_underbar && code_density_available ()) + xg_replace_opname (popname, "nop.n"); + else + { + if (xg_check_num_args (pnum_args, 0, opname, arg_strings)) + return -1; + xg_replace_opname (popname, (has_underbar ? "_or" : "or")); + arg_strings[0] = (char *) xmalloc (3); + arg_strings[1] = (char *) xmalloc (3); + arg_strings[2] = (char *) xmalloc (3); + strcpy (arg_strings[0], "a1"); + strcpy (arg_strings[1], "a1"); + strcpy (arg_strings[2], "a1"); + *pnum_args = 3; + } + return 0; + } + + if ((opname[0] == 'r' || opname[0] == 'w') + && opname[1] == 'u' + && opname[2] == 'r') + return xg_translate_sysreg_op (popname, pnum_args, arg_strings); + + + /* WIDENING DENSITY OPCODES + + questionable relaxations (widening) from old "tai" idioms: + + ADD.N --> ADD + BEQZ.N --> BEQZ + RET.N --> RET + RETW.N --> RETW + MOVI.N --> MOVI + MOV.N --> MOV + NOP.N --> NOP + + Note: this incomplete list was imported to match the "tai" + behavior; other density opcodes are not handled. + + The xtensa-relax code may know how to do these but it doesn't do + anything when these density opcodes appear inside a no-density + region. Somehow GAS should either print an error when that happens + or do the widening. The old "tai" behavior was to do the widening. + For now, I'll make it widen but print a warning. + + FIXME: GAS needs to detect density opcodes inside no-density + regions and treat them as errors. This code should be removed + when that is done. */ + + if (use_generics () + && !has_underbar + && density_supported + && !code_density_available ()) + { + if (strcmp (opname, "add.n") == 0) + xg_replace_opname (popname, "add"); + + else if (strcmp (opname, "beqz.n") == 0) + xg_replace_opname (popname, "beqz"); + + else if (strcmp (opname, "ret.n") == 0) + xg_replace_opname (popname, "ret"); + + else if (strcmp (opname, "retw.n") == 0) + xg_replace_opname (popname, "retw"); + + else if (strcmp (opname, "movi.n") == 0) + xg_replace_opname (popname, "movi"); + + else if (strcmp (opname, "mov.n") == 0) + { + if (xg_check_num_args (pnum_args, 2, opname, arg_strings)) + return -1; + xg_replace_opname (popname, "or"); + arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1); + strcpy (arg_strings[2], arg_strings[1]); + *pnum_args = 3; + } + + else if (strcmp (opname, "nop.n") == 0) + { + if (xg_check_num_args (pnum_args, 0, opname, arg_strings)) + return -1; + xg_replace_opname (popname, "or"); + arg_strings[0] = (char *) xmalloc (3); + arg_strings[1] = (char *) xmalloc (3); + arg_strings[2] = (char *) xmalloc (3); + strcpy (arg_strings[0], "a1"); + strcpy (arg_strings[1], "a1"); + strcpy (arg_strings[2], "a1"); + *pnum_args = 3; + } + } + + return 0; +} + + +/* Functions for dealing with the Xtensa ISA. */ + +/* Return true if the given operand is an immed or target instruction, + i.e., has a reloc associated with it. Currently, this is only true + if the operand kind is "i, "l" or "L". */ + +static bfd_boolean +operand_is_immed (opnd) + xtensa_operand opnd; +{ + const char *opkind = xtensa_operand_kind (opnd); + if (opkind[0] == '\0' || opkind[1] != '\0') + return FALSE; + switch (opkind[0]) + { + case 'i': + case 'l': + case 'L': + return TRUE; + } + return FALSE; +} + + +/* Return true if the given operand is a pc-relative label. This is + true for "l", "L", and "r" operand kinds. */ + +bfd_boolean +operand_is_pcrel_label (opnd) + xtensa_operand opnd; +{ + const char *opkind = xtensa_operand_kind (opnd); + if (opkind[0] == '\0' || opkind[1] != '\0') + return FALSE; + switch (opkind[0]) + { + case 'r': + case 'l': + case 'L': + return TRUE; + } + return FALSE; +} + + +/* Currently the assembler only allows us to use a single target per + fragment. Because of this, only one operand for a given + instruction may be symbolic. If there is an operand of kind "lrL", + the last one is chosen. Otherwise, the result is the number of the + last operand of type "i", and if there are none of those, we fail + and return -1. */ + +int +get_relaxable_immed (opcode) + xtensa_opcode opcode; +{ + int last_immed = -1; + int noperands, opi; + xtensa_operand operand; + + if (opcode == XTENSA_UNDEFINED) + return -1; + + noperands = xtensa_num_operands (xtensa_default_isa, opcode); + for (opi = noperands - 1; opi >= 0; opi--) + { + operand = xtensa_get_operand (xtensa_default_isa, opcode, opi); + if (operand_is_pcrel_label (operand)) + return opi; + if (last_immed == -1 && operand_is_immed (operand)) + last_immed = opi; + } + return last_immed; +} + + +xtensa_opcode +get_opcode_from_buf (buf) + const char *buf; +{ + static xtensa_insnbuf insnbuf = NULL; + xtensa_opcode opcode; + xtensa_isa isa = xtensa_default_isa; + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + xtensa_insnbuf_from_chars (isa, insnbuf, buf); + opcode = xtensa_decode_insn (isa, insnbuf); + return opcode; +} + + +static bfd_boolean +is_direct_call_opcode (opcode) + xtensa_opcode opcode; +{ + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_call0_opcode + || opcode == xtensa_call4_opcode + || opcode == xtensa_call8_opcode + || opcode == xtensa_call12_opcode); +} + + +static bfd_boolean +is_call_opcode (opcode) + xtensa_opcode opcode; +{ + if (is_direct_call_opcode (opcode)) + return TRUE; + + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_callx0_opcode + || opcode == xtensa_callx4_opcode + || opcode == xtensa_callx8_opcode + || opcode == xtensa_callx12_opcode); +} + + +/* Return true if the opcode is an entry opcode. This is used because + "entry" adds an implicit ".align 4" and also the entry instruction + has an extra check for an operand value. */ + +static bfd_boolean +is_entry_opcode (opcode) + xtensa_opcode opcode; +{ + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_entry_opcode); +} + + +/* Return true if it is one of the loop opcodes. Loops are special + because they need automatic alignment and they have a relaxation so + complex that we hard-coded it. */ + +static bfd_boolean +is_loop_opcode (opcode) + xtensa_opcode opcode; +{ + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_loop_opcode + || opcode == xtensa_loopnez_opcode + || opcode == xtensa_loopgtz_opcode); +} + + +static bfd_boolean +is_the_loop_opcode (opcode) + xtensa_opcode opcode; +{ + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_loop_opcode); +} + + +static bfd_boolean +is_jx_opcode (opcode) + xtensa_opcode opcode; +{ + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_jx_opcode); +} + + +/* Return true if the opcode is a retw or retw.n. + Needed to add nops to avoid a hardware interlock issue. */ + +static bfd_boolean +is_windowed_return_opcode (opcode) + xtensa_opcode opcode; +{ + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + return (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode); +} + + +/* Return true if the opcode type is "l" and the opcode is NOT a jump. */ + +static bfd_boolean +is_conditional_branch_opcode (opcode) + xtensa_opcode opcode; +{ + xtensa_isa isa = xtensa_default_isa; + int num_ops, i; + + if (opcode == xtensa_j_opcode && opcode != XTENSA_UNDEFINED) + return FALSE; + + num_ops = xtensa_num_operands (isa, opcode); + for (i = 0; i < num_ops; i++) + { + xtensa_operand operand = xtensa_get_operand (isa, opcode, i); + if (strcmp (xtensa_operand_kind (operand), "l") == 0) + return TRUE; + } + return FALSE; +} + + +/* Return true if the given opcode is a conditional branch + instruction, i.e., currently this is true if the instruction + is a jx or has an operand with 'l' type and is not a loop. */ + +bfd_boolean +is_branch_or_jump_opcode (opcode) + xtensa_opcode opcode; +{ + int opn, op_count; + + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + if (is_loop_opcode (opcode)) + return FALSE; + + if (is_jx_opcode (opcode)) + return TRUE; + + op_count = xtensa_num_operands (xtensa_default_isa, opcode); + for (opn = 0; opn < op_count; opn++) + { + xtensa_operand opnd = + xtensa_get_operand (xtensa_default_isa, opcode, opn); + const char *opkind = xtensa_operand_kind (opnd); + if (opkind && opkind[0] == 'l' && opkind[1] == '\0') + return TRUE; + } + return FALSE; +} + + +/* Convert from operand numbers to BFD relocation type code. + Return BFD_RELOC_NONE on failure. */ + +bfd_reloc_code_real_type +opnum_to_reloc (opnum) + int opnum; +{ + switch (opnum) + { + case 0: + return BFD_RELOC_XTENSA_OP0; + case 1: + return BFD_RELOC_XTENSA_OP1; + case 2: + return BFD_RELOC_XTENSA_OP2; + default: + break; + } + return BFD_RELOC_NONE; +} + + +/* Convert from BFD relocation type code to operand number. + Return -1 on failure. */ + +int +reloc_to_opnum (reloc) + bfd_reloc_code_real_type reloc; +{ + switch (reloc) + { + case BFD_RELOC_XTENSA_OP0: + return 0; + case BFD_RELOC_XTENSA_OP1: + return 1; + case BFD_RELOC_XTENSA_OP2: + return 2; + default: + break; + } + return -1; +} + + +static void +xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line) + xtensa_insnbuf insnbuf; + xtensa_opcode opcode; + xtensa_operand operand; + int32 value; + const char *file; + unsigned int line; +{ + xtensa_encode_result encode_result; + uint32 valbuf = value; + + encode_result = xtensa_operand_encode (operand, &valbuf); + + switch (encode_result) + { + case xtensa_encode_result_ok: + break; + case xtensa_encode_result_align: + as_bad_where ((char *) file, line, + _("operand %d not properly aligned for '%s'"), + value, xtensa_opcode_name (xtensa_default_isa, opcode)); + break; + case xtensa_encode_result_not_in_table: + as_bad_where ((char *) file, line, + _("operand %d not in immediate table for '%s'"), + value, xtensa_opcode_name (xtensa_default_isa, opcode)); + break; + case xtensa_encode_result_too_high: + as_bad_where ((char *) file, line, + _("operand %d too large for '%s'"), value, + xtensa_opcode_name (xtensa_default_isa, opcode)); + break; + case xtensa_encode_result_too_low: + as_bad_where ((char *) file, line, + _("operand %d too small for '%s'"), value, + xtensa_opcode_name (xtensa_default_isa, opcode)); + break; + case xtensa_encode_result_not_ok: + as_bad_where ((char *) file, line, + _("operand %d is invalid for '%s'"), value, + xtensa_opcode_name (xtensa_default_isa, opcode)); + break; + default: + abort (); + } + + xtensa_operand_set_field (operand, insnbuf, valbuf); +} + + +static uint32 +xtensa_insnbuf_get_operand (insnbuf, opcode, opnum) + xtensa_insnbuf insnbuf; + xtensa_opcode opcode; + int opnum; +{ + xtensa_operand op = xtensa_get_operand (xtensa_default_isa, opcode, opnum); + return xtensa_operand_decode (op, xtensa_operand_get_field (op, insnbuf)); +} + + +static void +xtensa_insnbuf_set_immediate_field (opcode, insnbuf, value, file, line) + xtensa_opcode opcode; + xtensa_insnbuf insnbuf; + int32 value; + const char *file; + unsigned int line; +{ + xtensa_isa isa = xtensa_default_isa; + int last_opnd = xtensa_num_operands (isa, opcode) - 1; + xtensa_operand operand = xtensa_get_operand (isa, opcode, last_opnd); + xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line); +} + + +static bfd_boolean +is_negatable_branch (insn) + TInsn *insn; +{ + xtensa_isa isa = xtensa_default_isa; + int i; + int num_ops = xtensa_num_operands (isa, insn->opcode); + + for (i = 0; i < num_ops; i++) + { + xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i); + char *kind = xtensa_operand_kind (opnd); + if (strlen (kind) == 1 && *kind == 'l') + return TRUE; + } + return FALSE; +} + + +/* Lists for recording various properties of symbols. */ + +typedef struct symbol_consS_struct +{ + symbolS *first; + /* These are used for the target taken. */ + int is_loop_target:1; + int is_branch_target:1; + int is_literal:1; + int is_moved:1; + struct symbol_consS_struct *rest; +} symbol_consS; + +symbol_consS *defined_symbols = 0; +symbol_consS *branch_targets = 0; + + +static void +xtensa_define_label (sym) + symbolS *sym; +{ + symbol_consS *cons = (symbol_consS *) xmalloc (sizeof (symbol_consS)); + + cons->first = sym; + cons->is_branch_target = 0; + cons->is_loop_target = 0; + cons->is_literal = generating_literals ? 1 : 0; + cons->is_moved = 0; + cons->rest = defined_symbols; + defined_symbols = cons; +} + + +void +add_target_symbol (sym, is_loop) + symbolS *sym; + bfd_boolean is_loop; +{ + symbol_consS *cons, *sym_e; + + for (sym_e = branch_targets; sym_e; sym_e = sym_e->rest) + { + if (sym_e->first == sym) + { + if (is_loop) + sym_e->is_loop_target = 1; + else + sym_e->is_branch_target = 1; + return; + } + } + + cons = (symbol_consS *) xmalloc (sizeof (symbol_consS)); + cons->first = sym; + cons->is_branch_target = (is_loop ? 0 : 1); + cons->is_loop_target = (is_loop ? 1 : 0); + cons->rest = branch_targets; + branch_targets = cons; +} + + +/* Find the symbol at a given position. (Note: the "loops_ok" + argument is provided to allow ignoring labels that define loop + ends. This fixes a bug where the NOPs to align a loop opcode were + included in a previous zero-cost loop: + + loop a0, loopend + + loopend: + + loop a2, loopend2 + + + would become: + + loop a0, loopend + + nop.n <===== bad! + loopend: + + loop a2, loopend2 + + + This argument is used to prevent moving the NOP to before the + loop-end label, which is what you want in this special case.) */ + +static symbolS * +xtensa_find_label (fragP, offset, loops_ok) + fragS *fragP; + offsetT offset; + bfd_boolean loops_ok; +{ + symbol_consS *consP; + + for (consP = defined_symbols; consP; consP = consP->rest) + { + symbolS *symP = consP->first; + + if (S_GET_SEGMENT (symP) == now_seg + && symbol_get_frag (symP) == fragP + && symbol_constant_p (symP) + && S_GET_VALUE (symP) == fragP->fr_address + (unsigned) offset + && (loops_ok || !is_loop_target_label (symP))) + return symP; + } + return NULL; +} + + +static void +map_over_defined_symbols (fn) + void (*fn) PARAMS ((symbolS *)); +{ + symbol_consS *sym_cons; + + for (sym_cons = defined_symbols; sym_cons; sym_cons = sym_cons->rest) + fn (sym_cons->first); +} + + +static bfd_boolean +is_loop_target_label (sym) + symbolS *sym; +{ + symbol_consS *sym_e; + + for (sym_e = branch_targets; sym_e; sym_e = sym_e->rest) + { + if (sym_e->first == sym) + return sym_e->is_loop_target; + } + return FALSE; +} + + +/* Walk over all of the symbols that are branch target labels and + loop target labels. Mark the associated fragments for these with + the appropriate flags. */ + +static void +xtensa_mark_target_fragments () +{ + symbol_consS *sym_e; + + for (sym_e = branch_targets; sym_e; sym_e = sym_e->rest) + { + symbolS *sym = sym_e->first; + + if (symbol_get_frag (sym) + && symbol_constant_p (sym) + && S_GET_VALUE (sym) == 0) + { + if (sym_e->is_branch_target) + symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE; + if (sym_e->is_loop_target) + symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE; + } + } +} + + +/* Various Other Internal Functions. */ + +static bfd_boolean +is_unique_insn_expansion (r) + TransitionRule *r; +{ + if (!r->to_instr || r->to_instr->next != NULL) + return FALSE; + if (r->to_instr->typ != INSTR_INSTR) + return FALSE; + return TRUE; +} + + +static int +xg_get_insn_size (insn) + TInsn *insn; +{ + assert (insn->insn_type == ITYPE_INSN); + return xtensa_insn_length (xtensa_default_isa, insn->opcode); +} + + +static int +xg_get_build_instr_size (insn) + BuildInstr *insn; +{ + assert (insn->typ == INSTR_INSTR); + return xtensa_insn_length (xtensa_default_isa, insn->opcode); +} + + +bfd_boolean +xg_is_narrow_insn (insn) + TInsn *insn; +{ + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + int num_match = 0; + assert (insn->insn_type == ITYPE_INSN); + assert (insn->opcode < table->num_opcodes); + + for (l = table->table[insn->opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + + if (xg_instruction_matches_rule (insn, rule) + && is_unique_insn_expansion (rule)) + { + /* It only generates one instruction... */ + assert (insn->insn_type == ITYPE_INSN); + /* ...and it is a larger instruction. */ + if (xg_get_insn_size (insn) + < xg_get_build_instr_size (rule->to_instr)) + { + num_match++; + if (num_match > 1) + return FALSE; + } + } + } + return (num_match == 1); +} + + +bfd_boolean +xg_is_single_relaxable_insn (insn) + TInsn *insn; +{ + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + int num_match = 0; + assert (insn->insn_type == ITYPE_INSN); + assert (insn->opcode < table->num_opcodes); + + for (l = table->table[insn->opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + + if (xg_instruction_matches_rule (insn, rule) + && is_unique_insn_expansion (rule)) + { + assert (insn->insn_type == ITYPE_INSN); + /* ... and it is a larger instruction. */ + if (xg_get_insn_size (insn) + <= xg_get_build_instr_size (rule->to_instr)) + { + num_match++; + if (num_match > 1) + return FALSE; + } + } + } + return (num_match == 1); +} + + +/* Return the largest size instruction that this instruction can + expand to. Currently, in all cases, this is 3 bytes. Of course we + could just calculate this once and generate a table. */ + +int +xg_get_max_narrow_insn_size (opcode) + xtensa_opcode opcode; +{ + /* Go ahead and compute it, but it better be 3. */ + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + int old_size = xtensa_insn_length (xtensa_default_isa, opcode); + assert (opcode < table->num_opcodes); + + /* Actually we can do better. Check to see of Only one applies. */ + for (l = table->table[opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + + /* If it only generates one instruction. */ + if (is_unique_insn_expansion (rule)) + { + int new_size = xtensa_insn_length (xtensa_default_isa, + rule->to_instr->opcode); + if (new_size > old_size) + { + assert (new_size == 3); + return 3; + } + } + } + return old_size; +} + + +/* Return the maximum number of bytes this opcode can expand to. */ + +int +xg_get_max_insn_widen_size (opcode) + xtensa_opcode opcode; +{ + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + int max_size = xtensa_insn_length (xtensa_default_isa, opcode); + + assert (opcode < table->num_opcodes); + + for (l = table->table[opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + BuildInstr *build_list; + int this_size = 0; + + if (!rule) + continue; + build_list = rule->to_instr; + if (is_unique_insn_expansion (rule)) + { + assert (build_list->typ == INSTR_INSTR); + this_size = xg_get_max_insn_widen_size (build_list->opcode); + } + else + for (; build_list != NULL; build_list = build_list->next) + { + switch (build_list->typ) + { + case INSTR_INSTR: + this_size += xtensa_insn_length (xtensa_default_isa, + build_list->opcode); + + break; + case INSTR_LITERAL_DEF: + case INSTR_LABEL_DEF: + default: + break; + } + } + if (this_size > max_size) + max_size = this_size; + } + return max_size; +} + + +/* Return the maximum number of literal bytes this opcode can generate. */ + +int +xg_get_max_insn_widen_literal_size (opcode) + xtensa_opcode opcode; +{ + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + int max_size = 0; + + assert (opcode < table->num_opcodes); + + for (l = table->table[opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + BuildInstr *build_list; + int this_size = 0; + + if (!rule) + continue; + build_list = rule->to_instr; + if (is_unique_insn_expansion (rule)) + { + assert (build_list->typ == INSTR_INSTR); + this_size = xg_get_max_insn_widen_literal_size (build_list->opcode); + } + else + for (; build_list != NULL; build_list = build_list->next) + { + switch (build_list->typ) + { + case INSTR_LITERAL_DEF: + /* hard coded 4-byte literal. */ + this_size += 4; + break; + case INSTR_INSTR: + case INSTR_LABEL_DEF: + default: + break; + } + } + if (this_size > max_size) + max_size = this_size; + } + return max_size; +} + + +bfd_boolean +xg_is_relaxable_insn (insn, lateral_steps) + TInsn *insn; + int lateral_steps; +{ + int steps_taken = 0; + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + + assert (insn->insn_type == ITYPE_INSN); + assert (insn->opcode < table->num_opcodes); + + for (l = table->table[insn->opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + + if (xg_instruction_matches_rule (insn, rule)) + { + if (steps_taken == lateral_steps) + return TRUE; + steps_taken++; + } + } + return FALSE; +} + + +static symbolS * +get_special_literal_symbol () +{ + static symbolS *sym = NULL; + + if (sym == NULL) + sym = symbol_find_or_make ("SPECIAL_LITERAL0\001"); + return sym; +} + + +static symbolS * +get_special_label_symbol () +{ + static symbolS *sym = NULL; + + if (sym == NULL) + sym = symbol_find_or_make ("SPECIAL_LABEL0\001"); + return sym; +} + + +/* Return true on success. */ + +bfd_boolean +xg_build_to_insn (targ, insn, bi) + TInsn *targ; + TInsn *insn; + BuildInstr *bi; +{ + BuildOp *op; + symbolS *sym; + + memset (targ, 0, sizeof (TInsn)); + switch (bi->typ) + { + case INSTR_INSTR: + op = bi->ops; + targ->opcode = bi->opcode; + targ->insn_type = ITYPE_INSN; + targ->is_specific_opcode = FALSE; + + for (; op != NULL; op = op->next) + { + int op_num = op->op_num; + int op_data = op->op_data; + + assert (op->op_num < MAX_INSN_ARGS); + + if (targ->ntok <= op_num) + targ->ntok = op_num + 1; + + switch (op->typ) + { + case OP_CONSTANT: + set_expr_const (&targ->tok[op_num], op_data); + break; + case OP_OPERAND: + assert (op_data < insn->ntok); + copy_expr (&targ->tok[op_num], &insn->tok[op_data]); + break; + case OP_LITERAL: + sym = get_special_literal_symbol (); + set_expr_symbol_offset (&targ->tok[op_num], sym, 0); + break; + case OP_LABEL: + sym = get_special_label_symbol (); + set_expr_symbol_offset (&targ->tok[op_num], sym, 0); + break; + default: + /* currently handles: + OP_OPERAND_LOW8 + OP_OPERAND_HI24S + OP_OPERAND_F32MINUS */ + if (xg_has_userdef_op_fn (op->typ)) + { + assert (op_data < insn->ntok); + if (expr_is_const (&insn->tok[op_data])) + { + long val; + copy_expr (&targ->tok[op_num], &insn->tok[op_data]); + val = xg_apply_userdef_op_fn (op->typ, + targ->tok[op_num]. + X_add_number); + targ->tok[op_num].X_add_number = val; + } + else + return FALSE; /* We cannot use a relocation for this. */ + break; + } + assert (0); + break; + } + } + break; + + case INSTR_LITERAL_DEF: + op = bi->ops; + targ->opcode = XTENSA_UNDEFINED; + targ->insn_type = ITYPE_LITERAL; + targ->is_specific_opcode = FALSE; + for (; op != NULL; op = op->next) + { + int op_num = op->op_num; + int op_data = op->op_data; + assert (op->op_num < MAX_INSN_ARGS); + + if (targ->ntok <= op_num) + targ->ntok = op_num + 1; + + switch (op->typ) + { + case OP_OPERAND: + assert (op_data < insn->ntok); + copy_expr (&targ->tok[op_num], &insn->tok[op_data]); + break; + case OP_LITERAL: + case OP_CONSTANT: + case OP_LABEL: + default: + assert (0); + break; + } + } + break; + + case INSTR_LABEL_DEF: + op = bi->ops; + targ->opcode = XTENSA_UNDEFINED; + targ->insn_type = ITYPE_LABEL; + targ->is_specific_opcode = FALSE; + /* Literal with no ops. is a label? */ + assert (op == NULL); + break; + + default: + assert (0); + } + + return TRUE; +} + + +/* Return true on success. */ + +bfd_boolean +xg_build_to_stack (istack, insn, bi) + IStack *istack; + TInsn *insn; + BuildInstr *bi; +{ + for (; bi != NULL; bi = bi->next) + { + TInsn *next_insn = istack_push_space (istack); + + if (!xg_build_to_insn (next_insn, insn, bi)) + return FALSE; + } + return TRUE; +} + + +/* Return true on valid expansion. */ + +bfd_boolean +xg_expand_to_stack (istack, insn, lateral_steps) + IStack *istack; + TInsn *insn; + int lateral_steps; +{ + int stack_size = istack->ninsn; + int steps_taken = 0; + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + + assert (insn->insn_type == ITYPE_INSN); + assert (insn->opcode < table->num_opcodes); + + for (l = table->table[insn->opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + + if (xg_instruction_matches_rule (insn, rule)) + { + if (lateral_steps == steps_taken) + { + int i; + + /* This is it. Expand the rule to the stack. */ + if (!xg_build_to_stack (istack, insn, rule->to_instr)) + return FALSE; + + /* Check to see if it fits. */ + for (i = stack_size; i < istack->ninsn; i++) + { + TInsn *insn = &istack->insn[i]; + + if (insn->insn_type == ITYPE_INSN + && !tinsn_has_symbolic_operands (insn) + && !xg_immeds_fit (insn)) + { + istack->ninsn = stack_size; + return FALSE; + } + } + return TRUE; + } + steps_taken++; + } + } + return FALSE; +} + + +bfd_boolean +xg_expand_narrow (targ, insn) + TInsn *targ; + TInsn *insn; +{ + TransitionTable *table = xg_build_widen_table (); + TransitionList *l; + + assert (insn->insn_type == ITYPE_INSN); + assert (insn->opcode < table->num_opcodes); + + for (l = table->table[insn->opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + if (xg_instruction_matches_rule (insn, rule) + && is_unique_insn_expansion (rule)) + { + /* Is it a larger instruction? */ + if (xg_get_insn_size (insn) + <= xg_get_build_instr_size (rule->to_instr)) + { + xg_build_to_insn (targ, insn, rule->to_instr); + return FALSE; + } + } + } + return TRUE; +} + + +/* Assumes: All immeds are constants. Check that all constants fit + into their immeds; return false if not. */ + +static bfd_boolean +xg_immeds_fit (insn) + const TInsn *insn; +{ + int i; + + int n = insn->ntok; + assert (insn->insn_type == ITYPE_INSN); + for (i = 0; i < n; ++i) + { + const expressionS *expr = &insn->tok[i]; + xtensa_operand opnd = xtensa_get_operand (xtensa_default_isa, + insn->opcode, i); + if (!operand_is_immed (opnd)) + continue; + + switch (expr->X_op) + { + case O_register: + case O_constant: + { + if (xg_check_operand (expr->X_add_number, opnd)) + return FALSE; + } + break; + default: + /* The symbol should have a fixup associated with it. */ + assert (FALSE); + break; + } + } + return TRUE; +} + + +/* This should only be called after we have an initial + estimate of the addresses. */ + +static bfd_boolean +xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch) + const TInsn *insn; + segT pc_seg; + fragS *pc_frag; + offsetT pc_offset; + long stretch; +{ + symbolS *symbolP; + offsetT target, pc, new_offset; + int i; + int n = insn->ntok; + + assert (insn->insn_type == ITYPE_INSN); + + for (i = 0; i < n; ++i) + { + const expressionS *expr = &insn->tok[i]; + xtensa_operand opnd = xtensa_get_operand (xtensa_default_isa, + insn->opcode, i); + if (!operand_is_immed (opnd)) + continue; + + switch (expr->X_op) + { + case O_register: + case O_constant: + if (xg_check_operand (expr->X_add_number, opnd)) + return FALSE; + break; + + case O_symbol: + /* We only allow symbols for pc-relative stuff. + If pc_frag == 0, then we don't have frag locations yet. */ + if (pc_frag == 0) + return FALSE; + + /* If it is PC-relative and the symbol is in the same segment as + the PC.... */ + if (!xtensa_operand_isPCRelative (opnd) + || S_GET_SEGMENT (expr->X_add_symbol) != pc_seg) + return FALSE; + + symbolP = expr->X_add_symbol; + target = S_GET_VALUE (symbolP) + expr->X_add_number; + pc = pc_frag->fr_address + pc_offset; + + /* If frag has yet to be reached on this pass, assume it + will move by STRETCH just as we did. If this is not so, + it will be because some frag between grows, and that will + force another pass. Beware zero-length frags. There + should be a faster way to do this. */ + + if (stretch && is_dnrange (pc_frag, symbolP, stretch)) + target += stretch; + + new_offset = xtensa_operand_do_reloc (opnd, target, pc); + if (xg_check_operand (new_offset, opnd)) + return FALSE; + break; + + default: + /* The symbol should have a fixup associated with it. */ + return FALSE; + } + } + + return TRUE; +} + + +/* This will check to see if the value can be converted into the + operand type. It will return true if it does not fit. */ + +static bfd_boolean +xg_check_operand (value, operand) + int32 value; + xtensa_operand operand; +{ + uint32 valbuf = value; + return (xtensa_operand_encode (operand, &valbuf) != xtensa_encode_result_ok); +} + + +/* Check if a symbol is pointing to somewhere after + the start frag, given that the segment has stretched + by stretch during relaxation. + + This is more complicated than it might appear at first blush + because of the stretching that goes on. Here is how the check + works: + + If the symbol and the frag are in the same segment, then + the symbol could be down range. Note that this function + assumes that start_frag is in now_seg. + + If the symbol is pointing to a frag with an address greater than + than the start_frag's address, then it _could_ be down range. + + The problem comes because target_frag may or may not have had + stretch bytes added to its address already, depending on if it is + before or after start frag. (And if we knew that, then we wouldn't + need this function.) start_frag has definitely already had stretch + bytes added to its address. + + If target_frag's address hasn't been adjusted yet, then to + determine if it comes after start_frag, we need to subtract + stretch from start_frag's address. + + If target_frag's address has been adjusted, then it might have + been adjusted such that it comes after start_frag's address minus + stretch bytes. + + So, in that case, we scan for it down stream to within + stretch bytes. We could search to the end of the fr_chain, but + that ends up taking too much time (over a minute on some gnu + tests). */ + +int +is_dnrange (start_frag, sym, stretch) + fragS *start_frag; + symbolS *sym; + long stretch; +{ + if (S_GET_SEGMENT (sym) == now_seg) + { + fragS *cur_frag = symbol_get_frag (sym); + + if (cur_frag->fr_address >= start_frag->fr_address - stretch) + { + int distance = stretch; + + while (cur_frag && distance >= 0) + { + distance -= cur_frag->fr_fix; + if (cur_frag == start_frag) + return 0; + cur_frag = cur_frag->fr_next; + } + return 1; + } + } + return 0; +} + + +/* Relax the assembly instruction at least "min_steps". + Return the number of steps taken. */ + +int +xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps, + stretch) + IStack *istack; + TInsn *insn; + segT pc_seg; + fragS *pc_frag; /* If pc_frag == 0, then no pc-relative. */ + offsetT pc_offset; /* Offset in fragment. */ + int min_steps; /* Minimum number of conversion steps. */ + long stretch; /* Number of bytes stretched so far. */ +{ + int steps_taken = 0; + + /* assert (has no symbolic operands) + Some of its immeds don't fit. + Try to build a relaxed version. + This may go through a couple of stages + of single instruction transformations before + we get there. */ + + TInsn single_target; + TInsn current_insn; + int lateral_steps = 0; + int istack_size = istack->ninsn; + + if (xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch) + && steps_taken >= min_steps) + { + istack_push (istack, insn); + return steps_taken; + } + tinsn_copy (¤t_insn, insn); + + /* Walk through all of the single instruction expansions. */ + while (xg_is_single_relaxable_insn (¤t_insn)) + { + int error_val = xg_expand_narrow (&single_target, ¤t_insn); + + assert (!error_val); + + if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset, + stretch)) + { + steps_taken++; + if (steps_taken >= min_steps) + { + istack_push (istack, &single_target); + return steps_taken; + } + } + tinsn_copy (¤t_insn, &single_target); + } + + /* Now check for a multi-instruction expansion. */ + while (xg_is_relaxable_insn (¤t_insn, lateral_steps)) + { + if (xg_symbolic_immeds_fit (¤t_insn, pc_seg, pc_frag, pc_offset, + stretch)) + { + if (steps_taken >= min_steps) + { + istack_push (istack, ¤t_insn); + return steps_taken; + } + } + steps_taken++; + if (xg_expand_to_stack (istack, ¤t_insn, lateral_steps)) + { + if (steps_taken >= min_steps) + return steps_taken; + } + lateral_steps++; + istack->ninsn = istack_size; + } + + /* It's not going to work -- use the original. */ + istack_push (istack, insn); + return steps_taken; +} + + +static void +xg_force_frag_space (size) + int size; +{ + /* This may have the side effect of creating a new fragment for the + space to go into. I just do not like the name of the "frag" + functions. */ + frag_grow (size); +} + + +void +xg_finish_frag (last_insn, state, max_growth, is_insn) + char *last_insn; + enum xtensa_relax_statesE state; + int max_growth; + bfd_boolean is_insn; +{ + /* Finish off this fragment so that it has at LEAST the desired + max_growth. If it doesn't fit in this fragment, close this one + and start a new one. In either case, return a pointer to the + beginning of the growth area. */ + + fragS *old_frag; + xg_force_frag_space (max_growth); + + old_frag = frag_now; + + frag_now->fr_opcode = last_insn; + if (is_insn) + frag_now->tc_frag_data.is_insn = TRUE; + + frag_var (rs_machine_dependent, max_growth, max_growth, + state, frag_now->fr_symbol, frag_now->fr_offset, last_insn); + + /* Just to make sure that we did not split it up. */ + assert (old_frag->fr_next == frag_now); +} + + +static bfd_boolean +is_branch_jmp_to_next (insn, fragP) + TInsn *insn; + fragS *fragP; +{ + xtensa_isa isa = xtensa_default_isa; + int i; + int num_ops = xtensa_num_operands (isa, insn->opcode); + int target_op = -1; + symbolS *sym; + fragS *target_frag; + + if (is_loop_opcode (insn->opcode)) + return FALSE; + + for (i = 0; i < num_ops; i++) + { + xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i); + char *kind = xtensa_operand_kind (opnd); + if (strlen (kind) == 1 && *kind == 'l') + { + target_op = i; + break; + } + } + if (target_op == -1) + return FALSE; + + if (insn->ntok <= target_op) + return FALSE; + + if (insn->tok[target_op].X_op != O_symbol) + return FALSE; + + sym = insn->tok[target_op].X_add_symbol; + if (sym == NULL) + return FALSE; + + if (insn->tok[target_op].X_add_number != 0) + return FALSE; + + target_frag = symbol_get_frag (sym); + if (target_frag == NULL) + return FALSE; + + if (is_next_frag_target (fragP->fr_next, target_frag) + && S_GET_VALUE (sym) == target_frag->fr_address) + return TRUE; + + return FALSE; +} + + +static void +xg_add_branch_and_loop_targets (insn) + TInsn *insn; +{ + xtensa_isa isa = xtensa_default_isa; + int num_ops = xtensa_num_operands (isa, insn->opcode); + + if (is_loop_opcode (insn->opcode)) + { + int i = 1; + xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i); + char *kind = xtensa_operand_kind (opnd); + if (strlen (kind) == 1 && *kind == 'l') + if (insn->tok[i].X_op == O_symbol) + add_target_symbol (insn->tok[i].X_add_symbol, TRUE); + return; + } + + /* Currently, we do not add branch targets. This is an optimization + for later that tries to align only branch targets, not just any + label in a text section. */ + + if (align_only_targets) + { + int i; + + for (i = 0; i < insn->ntok && i < num_ops; i++) + { + xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i); + char *kind = xtensa_operand_kind (opnd); + if (strlen (kind) == 1 && *kind == 'l' + && insn->tok[i].X_op == O_symbol) + add_target_symbol (insn->tok[i].X_add_symbol, FALSE); + } + } +} + + +/* Return the transition rule that matches or NULL if none matches. */ + +bfd_boolean +xg_instruction_matches_rule (insn, rule) + TInsn *insn; + TransitionRule *rule; +{ + PreconditionList *condition_l; + + if (rule->opcode != insn->opcode) + return FALSE; + + for (condition_l = rule->conditions; + condition_l != NULL; + condition_l = condition_l->next) + { + expressionS *exp1; + expressionS *exp2; + Precondition *cond = condition_l->precond; + + switch (cond->typ) + { + case OP_CONSTANT: + /* The expression must be the constant. */ + assert (cond->op_num < insn->ntok); + exp1 = &insn->tok[cond->op_num]; + if (!expr_is_const (exp1)) + return FALSE; + switch (cond->cmp) + { + case OP_EQUAL: + if (get_expr_const (exp1) != cond->op_data) + return FALSE; + break; + case OP_NOTEQUAL: + if (get_expr_const (exp1) == cond->op_data) + return FALSE; + break; + } + break; + + case OP_OPERAND: + assert (cond->op_num < insn->ntok); + assert (cond->op_data < insn->ntok); + exp1 = &insn->tok[cond->op_num]; + exp2 = &insn->tok[cond->op_data]; + + switch (cond->cmp) + { + case OP_EQUAL: + if (!expr_is_equal (exp1, exp2)) + return FALSE; + break; + case OP_NOTEQUAL: + if (expr_is_equal (exp1, exp2)) + return FALSE; + break; + } + break; + + case OP_LITERAL: + case OP_LABEL: + default: + return FALSE; + } + } + return TRUE; +} + + +TransitionRule * +xg_instruction_match (insn) + TInsn *insn; +{ + TransitionTable *table = xg_build_simplify_table (); + TransitionList *l; + assert (insn->opcode < table->num_opcodes); + + /* Walk through all of the possible transitions. */ + for (l = table->table[insn->opcode]; l != NULL; l = l->next) + { + TransitionRule *rule = l->rule; + if (xg_instruction_matches_rule (insn, rule)) + return rule; + } + return NULL; +} + + +/* Return false if no error. */ + +bfd_boolean +xg_build_token_insn (instr_spec, old_insn, new_insn) + BuildInstr *instr_spec; + TInsn *old_insn; + TInsn *new_insn; +{ + int num_ops = 0; + BuildOp *b_op; + + switch (instr_spec->typ) + { + case INSTR_INSTR: + new_insn->insn_type = ITYPE_INSN; + new_insn->opcode = instr_spec->opcode; + new_insn->is_specific_opcode = FALSE; + break; + case INSTR_LITERAL_DEF: + new_insn->insn_type = ITYPE_LITERAL; + new_insn->opcode = XTENSA_UNDEFINED; + new_insn->is_specific_opcode = FALSE; + break; + case INSTR_LABEL_DEF: + as_bad (_("INSTR_LABEL_DEF not supported yet")); + break; + } + + for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next) + { + expressionS *exp; + const expressionS *src_exp; + + num_ops++; + switch (b_op->typ) + { + case OP_CONSTANT: + /* The expression must be the constant. */ + assert (b_op->op_num < MAX_INSN_ARGS); + exp = &new_insn->tok[b_op->op_num]; + set_expr_const (exp, b_op->op_data); + break; + + case OP_OPERAND: + assert (b_op->op_num < MAX_INSN_ARGS); + assert (b_op->op_data < (unsigned) old_insn->ntok); + src_exp = &old_insn->tok[b_op->op_data]; + exp = &new_insn->tok[b_op->op_num]; + copy_expr (exp, src_exp); + break; + + case OP_LITERAL: + case OP_LABEL: + as_bad (_("can't handle generation of literal/labels yet")); + assert (0); + + default: + as_bad (_("can't handle undefined OP TYPE")); + assert (0); + } + } + + new_insn->ntok = num_ops; + return FALSE; +} + + +/* Return true if it was simplified. */ + +bfd_boolean +xg_simplify_insn (old_insn, new_insn) + TInsn *old_insn; + TInsn *new_insn; +{ + TransitionRule *rule = xg_instruction_match (old_insn); + BuildInstr *insn_spec; + if (rule == NULL) + return FALSE; + + insn_spec = rule->to_instr; + /* There should only be one. */ + assert (insn_spec != NULL); + assert (insn_spec->next == NULL); + if (insn_spec->next != NULL) + return FALSE; + + xg_build_token_insn (insn_spec, old_insn, new_insn); + + return TRUE; +} + + +/* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i -> + l32i.n. (2) Check the number of operands. (3) Place the instruction + tokens into the stack or if we can relax it at assembly time, place + multiple instructions/literals onto the stack. Return false if no + error. */ + +static bfd_boolean +xg_expand_assembly_insn (istack, orig_insn) + IStack *istack; + TInsn *orig_insn; +{ + int noperands; + TInsn new_insn; + memset (&new_insn, 0, sizeof (TInsn)); + + /* On return, we will be using the "use_tokens" with "use_ntok". + This will reduce things like addi to addi.n. */ + if (code_density_available () && !orig_insn->is_specific_opcode) + { + if (xg_simplify_insn (orig_insn, &new_insn)) + orig_insn = &new_insn; + } + + noperands = xtensa_num_operands (xtensa_default_isa, orig_insn->opcode); + if (orig_insn->ntok < noperands) + { + as_bad (_("found %d operands for '%s': Expected %d"), + orig_insn->ntok, + xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode), + noperands); + return TRUE; + } + if (orig_insn->ntok > noperands) + as_warn (_("found too many (%d) operands for '%s': Expected %d"), + orig_insn->ntok, + xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode), + noperands); + + /* If there are not enough operands, we will assert above. If there + are too many, just cut out the extras here. */ + + orig_insn->ntok = noperands; + + /* Cases: + + Instructions with all constant immeds: + Assemble them and relax the instruction if possible. + Give error if not possible; no fixup needed. + + Instructions with symbolic immeds: + Assemble them with a Fix up (that may cause instruction expansion). + Also close out the fragment if the fixup may cause instruction expansion. + + There are some other special cases where we need alignment. + 1) before certain instructions with required alignment (OPCODE_ALIGN) + 2) before labels that have jumps (LABEL_ALIGN) + 3) after call instructions (RETURN_ALIGN) + Multiple of these may be possible on the same fragment. + If so, make sure to satisfy the required alignment. + Then try to get the desired alignment. */ + + if (tinsn_has_invalid_symbolic_operands (orig_insn)) + return TRUE; + + if (orig_insn->is_specific_opcode || !can_relax ()) + { + istack_push (istack, orig_insn); + return FALSE; + } + + if (tinsn_has_symbolic_operands (orig_insn)) + { + if (tinsn_has_complex_operands (orig_insn)) + xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0); + else + istack_push (istack, orig_insn); + } + else + { + if (xg_immeds_fit (orig_insn)) + istack_push (istack, orig_insn); + else + xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0); + } + +#if 0 + for (i = 0; i < istack->ninsn; i++) + { + if (xg_simplify_insn (&new_insn, &istack->insn[i])) + istack->insn[i] = new_insn; + } +#endif + + return FALSE; +} + + +/* Currently all literals that are generated here are 32-bit L32R targets. */ + +symbolS * +xg_assemble_literal (insn) + /* const */ TInsn *insn; +{ + emit_state state; + symbolS *lit_sym = NULL; + + /* size = 4 for L32R. It could easily be larger when we move to + larger constants. Add a parameter later. */ + offsetT litsize = 4; + offsetT litalign = 2; /* 2^2 = 4 */ + expressionS saved_loc; + set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ()); + + assert (insn->insn_type == ITYPE_LITERAL); + assert (insn->ntok = 1); /* must be only one token here */ + + xtensa_switch_to_literal_fragment (&state); + + /* Force a 4-byte align here. Note that this opens a new frag, so all + literals done with this function have a frag to themselves. That's + important for the way text section literals work. */ + frag_align (litalign, 0, 0); + + emit_expr (&insn->tok[0], litsize); + + assert (frag_now->tc_frag_data.literal_frag == NULL); + frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg); + frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now); + lit_sym = frag_now->fr_symbol; + frag_now->tc_frag_data.is_literal = TRUE; + + /* Go back. */ + xtensa_restore_emit_state (&state); + return lit_sym; +} + + +static void +xg_assemble_literal_space (size) + /* const */ int size; +{ + emit_state state; + /* We might have to do something about this alignment. It only + takes effect if something is placed here. */ + offsetT litalign = 2; /* 2^2 = 4 */ + fragS *lit_saved_frag; + + expressionS saved_loc; + + assert (size % 4 == 0); + set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ()); + + xtensa_switch_to_literal_fragment (&state); + + /* Force a 4-byte align here. */ + frag_align (litalign, 0, 0); + + xg_force_frag_space (size); + + lit_saved_frag = frag_now; + frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg); + frag_now->tc_frag_data.is_literal = TRUE; + frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now); + xg_finish_frag (0, RELAX_LITERAL, size, FALSE); + + /* Go back. */ + xtensa_restore_emit_state (&state); + frag_now->tc_frag_data.literal_frag = lit_saved_frag; +} + + +symbolS * +xtensa_create_literal_symbol (sec, frag) + segT sec; + fragS *frag; +{ + static int lit_num = 0; + static char name[256]; + symbolS *fragSym; + + sprintf (name, ".L_lit_sym%d", lit_num); + fragSym = xtensa_create_local_symbol (stdoutput, name, sec, 0, frag_now); + + frag->tc_frag_data.is_literal = TRUE; + lit_num++; + return fragSym; +} + + +/* Create a local symbol. If it is in a linkonce section, we have to + be careful to make sure that if it is used in a relocation that the + symbol will be in the output file. */ + +symbolS * +xtensa_create_local_symbol (abfd, name, sec, value, frag) + bfd *abfd; + const char *name; + segT sec; + valueT value; + fragS *frag; +{ + symbolS *symbolP; + + if (get_is_linkonce_section (abfd, sec)) + { + symbolP = symbol_new (name, sec, value, frag); + S_CLEAR_EXTERNAL (symbolP); + /* symbolP->local = 1; */ + } + else + symbolP = symbol_new (name, sec, value, frag); + + return symbolP; +} + + +/* Return true if the section flags are marked linkonce + or the name is .gnu.linkonce*. */ + +bfd_boolean +get_is_linkonce_section (abfd, sec) + bfd *abfd ATTRIBUTE_UNUSED; + segT sec; +{ + flagword flags, link_once_flags; + + flags = bfd_get_section_flags (abfd, sec); + link_once_flags = (flags & SEC_LINK_ONCE); + + /* Flags might not be set yet. */ + if (!link_once_flags) + { + static size_t len = sizeof ".gnu.linkonce.t."; + + if (strncmp (segment_name (sec), ".gnu.linkonce.t.", len - 1) == 0) + link_once_flags = SEC_LINK_ONCE; + } + return (link_once_flags != 0); +} + + +/* Emit an instruction to the current fragment. If record_fix is true, + then this instruction will not change and we can go ahead and record + the fixup. If record_fix is false, then the instruction may change + and we are going to close out this fragment. Go ahead and set the + fr_symbol and fr_offset instead of adding a fixup. */ + +static bfd_boolean +xg_emit_insn (t_insn, record_fix) + TInsn *t_insn; + bfd_boolean record_fix; +{ + bfd_boolean ok = TRUE; + xtensa_isa isa = xtensa_default_isa; + xtensa_opcode opcode = t_insn->opcode; + bfd_boolean has_fixup = FALSE; + int noperands; + int i, byte_count; + fragS *oldfrag; + size_t old_size; + char *f; + static xtensa_insnbuf insnbuf = NULL; + + /* Use a static pointer to the insn buffer so we don't have to call + malloc each time through. */ + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + has_fixup = tinsn_to_insnbuf (t_insn, insnbuf); + + noperands = xtensa_num_operands (isa, opcode); + assert (noperands == t_insn->ntok); + + byte_count = xtensa_insn_length (isa, opcode); + oldfrag = frag_now; + /* This should NEVER cause us to jump into a new frag; + we've already reserved space. */ + old_size = frag_now_fix (); + f = frag_more (byte_count); + assert (oldfrag == frag_now); + + /* This needs to generate a record that lists the parts that are + instructions. */ + if (!frag_now->tc_frag_data.is_insn) + { + /* If we are at the beginning of a fragment, switch this + fragment to an instruction fragment. */ + if (now_seg != absolute_section && old_size != 0) + as_warn (_("instruction fragment may contain data")); + frag_now->tc_frag_data.is_insn = TRUE; + } + + xtensa_insnbuf_to_chars (isa, insnbuf, f); + + /* dwarf2_emit_insn (byte_count); */ + + /* Now spit out the opcode fixup.... */ + if (!has_fixup) + return !ok; + + for (i = 0; i < noperands; ++i) + { + expressionS *expr = &t_insn->tok[i]; + switch (expr->X_op) + { + case O_symbol: + if (get_relaxable_immed (opcode) == i) + { + if (record_fix) + { + if (!xg_add_opcode_fix (opcode, i, expr, frag_now, + f - frag_now->fr_literal)) + ok = FALSE; + } + else + { + /* Write it to the fr_offset, fr_symbol. */ + frag_now->fr_symbol = expr->X_add_symbol; + frag_now->fr_offset = expr->X_add_number; + } + } + else + { + as_bad (_("invalid operand %d on '%s'"), + i, xtensa_opcode_name (isa, opcode)); + ok = FALSE; + } + break; + + case O_constant: + case O_register: + break; + + default: + as_bad (_("invalid expression for operand %d on '%s'"), + i, xtensa_opcode_name (isa, opcode)); + ok = FALSE; + break; + } + } + + return !ok; +} + + +static bfd_boolean +xg_emit_insn_to_buf (t_insn, buf, fragP, offset, build_fix) + TInsn *t_insn; + char *buf; + fragS *fragP; + offsetT offset; + bfd_boolean build_fix; +{ + static xtensa_insnbuf insnbuf = NULL; + bfd_boolean has_symbolic_immed = FALSE; + bfd_boolean ok = TRUE; + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + has_symbolic_immed = tinsn_to_insnbuf (t_insn, insnbuf); + if (has_symbolic_immed && build_fix) + { + /* Add a fixup. */ + int opnum = get_relaxable_immed (t_insn->opcode); + expressionS *exp = &t_insn->tok[opnum]; + + if (!xg_add_opcode_fix (t_insn->opcode, + opnum, exp, fragP, offset)) + ok = FALSE; + } + fragP->tc_frag_data.is_insn = TRUE; + xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf); + return ok; +} + + +/* Put in a fixup record based on the opcode. + Return true on success. */ + +bfd_boolean +xg_add_opcode_fix (opcode, opnum, expr, fragP, offset) + xtensa_opcode opcode; + int opnum; + expressionS *expr; + fragS *fragP; + offsetT offset; +{ + bfd_reloc_code_real_type reloc; + reloc_howto_type *howto; + int insn_length; + fixS *the_fix; + + reloc = opnum_to_reloc (opnum); + if (reloc == BFD_RELOC_NONE) + { + as_bad (_("invalid relocation operand %i on '%s'"), + opnum, xtensa_opcode_name (xtensa_default_isa, opcode)); + return FALSE; + } + + howto = bfd_reloc_type_lookup (stdoutput, reloc); + + if (!howto) + { + as_bad (_("undefined symbol for opcode \"%s\"."), + xtensa_opcode_name (xtensa_default_isa, opcode)); + return FALSE; + } + + insn_length = xtensa_insn_length (xtensa_default_isa, opcode); + the_fix = fix_new_exp (fragP, offset, insn_length, expr, + howto->pc_relative, reloc); + + if (expr->X_add_symbol && + (S_IS_EXTERNAL (expr->X_add_symbol) || S_IS_WEAK (expr->X_add_symbol))) + the_fix->fx_plt = TRUE; + + return TRUE; +} + + +void +xg_resolve_literals (insn, lit_sym) + TInsn *insn; + symbolS *lit_sym; +{ + symbolS *sym = get_special_literal_symbol (); + int i; + if (lit_sym == 0) + return; + assert (insn->insn_type == ITYPE_INSN); + for (i = 0; i < insn->ntok; i++) + if (insn->tok[i].X_add_symbol == sym) + insn->tok[i].X_add_symbol = lit_sym; + +} + + +void +xg_resolve_labels (insn, label_sym) + TInsn *insn; + symbolS *label_sym; +{ + symbolS *sym = get_special_label_symbol (); + int i; + /* assert(!insn->is_literal); */ + for (i = 0; i < insn->ntok; i++) + if (insn->tok[i].X_add_symbol == sym) + insn->tok[i].X_add_symbol = label_sym; + +} + + +static void +xg_assemble_tokens (insn) + /*const */ TInsn *insn; +{ + /* By the time we get here, there's not too much left to do. + 1) Check our assumptions. + 2) Check if the current instruction is "narrow". + If so, then finish the frag, create another one. + We could also go back to change some previous + "narrow" frags into no-change ones if we have more than + MAX_NARROW_ALIGNMENT of them without alignment restrictions + between them. + + Cases: + 1) It has constant operands and doesn't fit. + Go ahead and assemble it so it will fail. + 2) It has constant operands that fit. + If narrow and !is_specific_opcode, + assemble it and put in a relocation + else + assemble it. + 3) It has a symbolic immediate operand + a) Find the worst-case relaxation required + b) Find the worst-case literal pool space required. + Insert appropriate alignment & space in the literal. + Assemble it. + Add the relocation. */ + + assert (insn->insn_type == ITYPE_INSN); + + if (!tinsn_has_symbolic_operands (insn)) + { + if (xg_is_narrow_insn (insn) && !insn->is_specific_opcode) + { + /* assemble it but add max required space */ + int max_size = xg_get_max_narrow_insn_size (insn->opcode); + int min_size = xg_get_insn_size (insn); + char *last_insn; + assert (max_size == 3); + /* make sure we have enough space to widen it */ + xg_force_frag_space (max_size); + /* Output the instruction. It may cause an error if some + operands do not fit. */ + last_insn = frag_more (0); + if (xg_emit_insn (insn, TRUE)) + as_warn (_("instruction with constant operands does not fit")); + xg_finish_frag (last_insn, RELAX_NARROW, max_size - min_size, TRUE); + } + else + { + /* Assemble it. No relocation needed. */ + int max_size = xg_get_insn_size (insn); + xg_force_frag_space (max_size); + if (xg_emit_insn (insn, FALSE)) + as_warn (_("instruction with constant operands does not " + "fit without widening")); + /* frag_more (max_size); */ + + /* Special case for jx. If the jx is the next to last + instruction in a loop, we will add a NOP after it. This + avoids a hardware issue that could occur if the jx jumped + to the next instruction. */ + if (software_avoid_b_j_loop_end + && is_jx_opcode (insn->opcode)) + { + maybe_has_b_j_loop_end = TRUE; + /* add 2 of these */ + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 4, 4, + RELAX_ADD_NOP_IF_PRE_LOOP_END, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + } + } + } + else + { + /* Need to assemble it with space for the relocation. */ + if (!insn->is_specific_opcode) + { + /* Assemble it but add max required space. */ + char *last_insn; + int min_size = xg_get_insn_size (insn); + int max_size = xg_get_max_insn_widen_size (insn->opcode); + int max_literal_size = + xg_get_max_insn_widen_literal_size (insn->opcode); + +#if 0 + symbolS *immed_sym = xg_get_insn_immed_symbol (insn); + set_frag_segment (frag_now, now_seg); +#endif /* 0 */ + + /* Make sure we have enough space to widen the instruction. + This may open a new fragment. */ + xg_force_frag_space (max_size); + if (max_literal_size != 0) + xg_assemble_literal_space (max_literal_size); + + /* Output the instruction. It may cause an error if some + operands do not fit. Emit the incomplete instruction. */ + last_insn = frag_more (0); + xg_emit_insn (insn, FALSE); + + xg_finish_frag (last_insn, RELAX_IMMED, max_size - min_size, TRUE); + + /* Special cases for loops: + close_loop_end should be inserted AFTER short_loop. + Make sure that CLOSE loops are processed BEFORE short_loops + when converting them. */ + + /* "short_loop": add a NOP if the loop is < 4 bytes. */ + if (software_avoid_short_loop + && is_loop_opcode (insn->opcode)) + { + maybe_has_short_loop = TRUE; + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 4, 4, + RELAX_ADD_NOP_IF_SHORT_LOOP, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 4, 4, + RELAX_ADD_NOP_IF_SHORT_LOOP, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + } + + /* "close_loop_end": Add up to 12 bytes of NOPs to keep a + loop at least 12 bytes away from another loop's loop + end. */ + if (software_avoid_close_loop_end + && is_loop_opcode (insn->opcode)) + { + maybe_has_close_loop_end = TRUE; + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 12, 12, + RELAX_ADD_NOP_IF_CLOSE_LOOP_END, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + } + } + else + { + /* Assemble it in place. No expansion will be required, + but we'll still need a relocation record. */ + int max_size = xg_get_insn_size (insn); + xg_force_frag_space (max_size); + if (xg_emit_insn (insn, TRUE)) + as_warn (_("instruction's constant operands do not fit")); + } + } +} + + +/* Return true if the instruction can write to the specified + integer register. */ + +static bfd_boolean +is_register_writer (insn, regset, regnum) + const TInsn *insn; + const char *regset; + int regnum; +{ + int i; + int num_ops; + xtensa_isa isa = xtensa_default_isa; + + num_ops = xtensa_num_operands (isa, insn->opcode); + + for (i = 0; i < num_ops; i++) + { + xtensa_operand operand = xtensa_get_operand (isa, insn->opcode, i); + char inout = xtensa_operand_inout (operand); + + if (inout == '>' || inout == '=') + { + if (strcmp (xtensa_operand_kind (operand), regset) == 0) + { + if ((insn->tok[i].X_op == O_register) + && (insn->tok[i].X_add_number == regnum)) + return TRUE; + } + } + } + return FALSE; +} + + +static bfd_boolean +is_bad_loopend_opcode (tinsn) + const TInsn * tinsn; +{ + xtensa_opcode opcode = tinsn->opcode; + + if (opcode == XTENSA_UNDEFINED) + return FALSE; + + if (opcode == xtensa_call0_opcode + || opcode == xtensa_callx0_opcode + || opcode == xtensa_call4_opcode + || opcode == xtensa_callx4_opcode + || opcode == xtensa_call8_opcode + || opcode == xtensa_callx8_opcode + || opcode == xtensa_call12_opcode + || opcode == xtensa_callx12_opcode + || opcode == xtensa_isync_opcode + || opcode == xtensa_ret_opcode + || opcode == xtensa_ret_n_opcode + || opcode == xtensa_retw_opcode + || opcode == xtensa_retw_n_opcode + || opcode == xtensa_waiti_opcode) + return TRUE; + + /* An RSR of LCOUNT is illegal as the last opcode in a loop. */ + if (opcode == xtensa_rsr_opcode + && tinsn->ntok >= 2 + && tinsn->tok[1].X_op == O_constant + && tinsn->tok[1].X_add_number == 2) + return TRUE; + + return FALSE; +} + + +/* Labels that begin with ".Ln" or ".LM" are unaligned. + This allows the debugger to add unaligned labels. + Also, the assembler generates stabs labels that need + not be aligned: FAKE_LABEL_NAME . {"F", "L", "endfunc"}. */ + +bfd_boolean +is_unaligned_label (sym) + symbolS *sym; +{ + const char *name = S_GET_NAME (sym); + static size_t fake_size = 0; + + if (name + && name[0] == '.' + && name[1] == 'L' && (name[2] == 'n' || name[2] == 'M')) + return TRUE; + + /* FAKE_LABEL_NAME followed by "F", "L" or "endfunc" */ + if (fake_size == 0) + fake_size = strlen (FAKE_LABEL_NAME); + + if (name + && strncmp (FAKE_LABEL_NAME, name, fake_size) == 0 + && (name[fake_size] == 'F' + || name[fake_size] == 'L' + || (name[fake_size] == 'e' + && strncmp ("endfunc", name+fake_size, 7) == 0))) + return TRUE; + + return FALSE; +} + + +fragS * +next_non_empty_frag (fragP) + const fragS *fragP; +{ + fragS *next_fragP = fragP->fr_next; + + /* Sometimes an empty will end up here due storage allocation issues. + So we have to skip until we find something legit. */ + while (next_fragP && next_fragP->fr_fix == 0) + next_fragP = next_fragP->fr_next; + + if (next_fragP == NULL || next_fragP->fr_fix == 0) + return NULL; + + return next_fragP; +} + + +xtensa_opcode +next_frag_opcode (fragP) + const fragS * fragP; +{ + const fragS *next_fragP = next_non_empty_frag (fragP); + static xtensa_insnbuf insnbuf = NULL; + xtensa_isa isa = xtensa_default_isa; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + if (next_fragP == NULL) + return XTENSA_UNDEFINED; + + xtensa_insnbuf_from_chars (isa, insnbuf, next_fragP->fr_literal); + return xtensa_decode_insn (isa, insnbuf); +} + + +/* Return true if the target frag is one of the next non-empty frags. */ + +bfd_boolean +is_next_frag_target (fragP, target) + const fragS *fragP; + const fragS *target; +{ + if (fragP == NULL) + return FALSE; + + for (; fragP; fragP = fragP->fr_next) + { + if (fragP == target) + return TRUE; + if (fragP->fr_fix != 0) + return FALSE; + if (fragP->fr_type == rs_fill && fragP->fr_offset != 0) + return FALSE; + if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code) + && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0)) + return FALSE; + if (fragP->fr_type == rs_space) + return FALSE; + } + return FALSE; +} + + +/* If the next legit fragment is an end-of-loop marker, + switch its state so it will instantiate a NOP. */ + +static void +update_next_frag_nop_state (fragP) + fragS *fragP; +{ + fragS *next_fragP = fragP->fr_next; + + while (next_fragP && next_fragP->fr_fix == 0) + { + if (next_fragP->fr_type == rs_machine_dependent + && next_fragP->fr_subtype == RELAX_LOOP_END) + { + next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP; + return; + } + next_fragP = next_fragP->fr_next; + } +} + + +static bfd_boolean +next_frag_is_branch_target (fragP) + const fragS *fragP; +{ + /* Sometimes an empty will end up here due storage allocation issues, + so we have to skip until we find something legit. */ + for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next) + { + if (fragP->tc_frag_data.is_branch_target) + return TRUE; + if (fragP->fr_fix != 0) + break; + } + return FALSE; +} + + +static bfd_boolean +next_frag_is_loop_target (fragP) + const fragS *fragP; +{ + /* Sometimes an empty will end up here due storage allocation issues. + So we have to skip until we find something legit. */ + for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next) + { + if (fragP->tc_frag_data.is_loop_target) + return TRUE; + if (fragP->fr_fix != 0) + break; + } + return FALSE; +} + + +static addressT +next_frag_pre_opcode_bytes (fragp) + const fragS *fragp; +{ + const fragS *next_fragp = fragp->fr_next; + + xtensa_opcode next_opcode = next_frag_opcode (fragp); + if (!is_loop_opcode (next_opcode)) + return 0; + + /* Sometimes an empty will end up here due storage allocation issues. + So we have to skip until we find something legit. */ + while (next_fragp->fr_fix == 0) + next_fragp = next_fragp->fr_next; + + if (next_fragp->fr_type != rs_machine_dependent) + return 0; + + /* There is some implicit knowledge encoded in here. + The LOOP instructions that are NOT RELAX_IMMED have + been relaxed. */ + if (next_fragp->fr_subtype > RELAX_IMMED) + return get_expanded_loop_offset (next_opcode); + + return 0; +} + + +/* Mark a location where we can later insert literal frags. Update + the section's literal_pool_loc, so subsequent literals can be + placed nearest to their use. */ + +static void +xtensa_mark_literal_pool_location (move_labels) + bfd_boolean move_labels; +{ + /* Any labels pointing to the current location need + to be adjusted to after the literal pool. */ + emit_state s; + fragS *label_target = frag_now; + fragS *pool_location; + offsetT label_offset = frag_now_fix (); + + frag_align (2, 0, 0); + + /* We stash info in the fr_var of these frags + so we can later move the literal's fixes into this + frchain's fix list. We can use fr_var because fr_var's + interpretation depends solely on the fr_type and subtype. */ + pool_location = frag_now; + frag_variant (rs_machine_dependent, 0, (int) frchain_now, + RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL); + frag_variant (rs_machine_dependent, 0, (int) now_seg, + RELAX_LITERAL_POOL_END, NULL, 0, NULL); + + /* Now put a frag into the literal pool that points to this location. */ + set_literal_pool_location (now_seg, pool_location); + xtensa_switch_to_literal_fragment (&s); + + /* Close whatever frag is there. */ + frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL); + frag_now->tc_frag_data.literal_frag = pool_location; + frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL); + xtensa_restore_emit_state (&s); + if (move_labels) + xtensa_move_labels (label_target, label_offset, frag_now, 0); +} + + +static void +xtensa_move_labels (old_frag, old_offset, new_frag, new_offset) + fragS *old_frag; + valueT old_offset; + fragS *new_frag ATTRIBUTE_UNUSED; + valueT new_offset; +{ + symbolS *old_sym; + + /* Repeat until there are no more.... */ + for (old_sym = xtensa_find_label (old_frag, old_offset, TRUE); + old_sym; + old_sym = xtensa_find_label (old_frag, old_offset, TRUE)) + { + S_SET_VALUE (old_sym, (valueT) new_offset); + symbol_set_frag (old_sym, frag_now); + } +} + + +/* Assemble a NOP of the requested size in the buffer. User must have + allocated "buf" with at least "size" bytes. */ + +void +assemble_nop (size, buf) + size_t size; + char *buf; +{ + static xtensa_insnbuf insnbuf = NULL; + TInsn t_insn; + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + tinsn_init (&t_insn); + switch (size) + { + case 2: + t_insn.opcode = xtensa_nop_n_opcode; + t_insn.ntok = 0; + if (t_insn.opcode == XTENSA_UNDEFINED) + as_fatal (_("opcode 'NOP.N' unavailable in this configuration")); + tinsn_to_insnbuf (&t_insn, insnbuf); + xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf); + break; + + case 3: + t_insn.opcode = xtensa_or_opcode; + assert (t_insn.opcode != XTENSA_UNDEFINED); + if (t_insn.opcode == XTENSA_UNDEFINED) + as_fatal (_("opcode 'OR' unavailable in this configuration")); + set_expr_const (&t_insn.tok[0], 1); + set_expr_const (&t_insn.tok[1], 1); + set_expr_const (&t_insn.tok[2], 1); + t_insn.ntok = 3; + tinsn_to_insnbuf (&t_insn, insnbuf); + xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf); + break; + + default: + as_fatal (_("invalid %d-byte NOP requested"), size); + } +} + + +/* Return the number of bytes for the offset of the expanded loop + instruction. This should be incorporated into the relaxation + specification but is hard-coded here. This is used to auto-align + the loop instruction. It is invalid to call this function if the + configuration does not have loops or if the opcode is not a loop + opcode. */ + +static addressT +get_expanded_loop_offset (opcode) + xtensa_opcode opcode; +{ + /* This is the OFFSET of the loop instruction in the expanded loop. + This MUST correspond directly to the specification of the loop + expansion. It will be validated on fragment conversion. */ + if (opcode == XTENSA_UNDEFINED) + as_fatal (_("get_expanded_loop_offset: undefined opcode")); + if (opcode == xtensa_loop_opcode) + return 0; + if (opcode == xtensa_loopnez_opcode) + return 3; + if (opcode == xtensa_loopgtz_opcode) + return 6; + as_fatal (_("get_expanded_loop_offset: invalid opcode")); + return 0; +} + + +fragS * +get_literal_pool_location (seg) + segT seg; +{ + return seg_info (seg)->tc_segment_info_data.literal_pool_loc; +} + + +static void +set_literal_pool_location (seg, literal_pool_loc) + segT seg; + fragS *literal_pool_loc; +{ + seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc; +} + + +/* External Functions and Other GAS Hooks. */ + +const char * +xtensa_target_format () +{ + return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le"); +} + + +void +xtensa_file_arch_init (abfd) + bfd *abfd; +{ + bfd_set_private_flags (abfd, 0x100 | 0x200); +} + + +void +md_number_to_chars (buf, val, n) + char *buf; + valueT val; + int n; +{ + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, 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 () +{ + segT current_section = now_seg; + int current_subsec = now_subseg; + xtensa_isa isa; + +#if STATIC_LIBISA + isa = xtensa_isa_init (); +#else + /* ISA was already initialized by xtensa_init(). */ + isa = xtensa_default_isa; +#endif + + /* Set up the .literal, .fini.literal and .init.literal sections. */ + memset (&default_lit_sections, 0, sizeof (default_lit_sections)); + default_lit_sections.init_lit_seg_name = INIT_LITERAL_SECTION_NAME; + default_lit_sections.fini_lit_seg_name = FINI_LITERAL_SECTION_NAME; + default_lit_sections.lit_seg_name = LITERAL_SECTION_NAME; + + subseg_set (current_section, current_subsec); + + xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi"); + xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi"); + xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0"); + xtensa_call4_opcode = xtensa_opcode_lookup (isa, "call4"); + xtensa_call8_opcode = xtensa_opcode_lookup (isa, "call8"); + xtensa_call12_opcode = xtensa_opcode_lookup (isa, "call12"); + xtensa_callx0_opcode = xtensa_opcode_lookup (isa, "callx0"); + xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4"); + xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8"); + xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12"); + xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry"); + xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync"); + xtensa_j_opcode = xtensa_opcode_lookup (isa, "j"); + xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx"); + xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop"); + xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez"); + xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz"); + xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n"); + xtensa_or_opcode = xtensa_opcode_lookup (isa, "or"); + xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret"); + xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n"); + xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw"); + xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n"); + xtensa_rsr_opcode = xtensa_opcode_lookup (isa, "rsr"); + xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti"); +} + + +/* tc_frob_label hook */ + +void +xtensa_frob_label (sym) + symbolS *sym; +{ + xtensa_define_label (sym); + if (is_loop_target_label (sym) + && (get_last_insn_flags (now_seg, now_subseg) + & FLAG_IS_BAD_LOOPEND) != 0) + as_bad (_("invalid last instruction for a zero-overhead loop")); + + /* No target aligning in the absolute section. */ + if (now_seg != absolute_section && align_targets + && !is_unaligned_label (sym)) + { + fragS *old_frag = frag_now; + offsetT old_offset = frag_now_fix (); + if (frag_now->tc_frag_data.is_literal) + return; + /* frag_now->tc_frag_data.is_insn = TRUE; */ + frag_var (rs_machine_dependent, 4, 4, + RELAX_DESIRE_ALIGN_IF_TARGET, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + xtensa_move_labels (old_frag, old_offset, frag_now, 0); + /* Once we know whether or not the label is a branch target + We will suppress some of these alignments. */ + } +} + + +/* md_flush_pending_output hook */ + +void +xtensa_flush_pending_output () +{ + /* If there is a non-zero instruction fragment, close it. */ + if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn) + { + frag_wane (frag_now); + frag_new (0); + } + frag_now->tc_frag_data.is_insn = FALSE; +} + + +void +md_assemble (str) + char *str; +{ + xtensa_isa isa = xtensa_default_isa; + char *opname; + unsigned opnamelen; + bfd_boolean has_underbar = FALSE; + char *arg_strings[MAX_INSN_ARGS]; + int num_args; + IStack istack; /* Put instructions into here. */ + TInsn orig_insn; /* Original instruction from the input. */ + int i; + symbolS *lit_sym = NULL; + + if (frag_now->tc_frag_data.is_literal) + { + static bfd_boolean reported = 0; + if (reported < 4) + as_bad (_("cannot assemble '%s' into a literal fragment"), str); + if (reported == 3) + as_bad (_("...")); + reported++; + return; + } + + istack_init (&istack); + tinsn_init (&orig_insn); + + /* Split off the opcode. */ + opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/0123456789."); + opname = xmalloc (opnamelen + 1); + memcpy (opname, str, opnamelen); + opname[opnamelen] = '\0'; + + num_args = tokenize_arguments (arg_strings, str + opnamelen); + if (num_args == -1) + { + as_bad (_("syntax error")); + return; + } + + if (xg_translate_idioms (&opname, &num_args, arg_strings)) + return; + + /* Check for an underbar prefix. */ + if (*opname == '_') + { + has_underbar = TRUE; + opname += 1; + } + + orig_insn.insn_type = ITYPE_INSN; + orig_insn.ntok = 0; + orig_insn.is_specific_opcode = (has_underbar || !use_generics ()); + specific_opcode = orig_insn.is_specific_opcode; + + orig_insn.opcode = xtensa_opcode_lookup (isa, opname); + if (orig_insn.opcode == XTENSA_UNDEFINED) + { + as_bad (_("unknown opcode %s"), opname); + return; + } + + if (frag_now_fix () != 0 && !frag_now->tc_frag_data.is_insn) + { + frag_wane (frag_now); + frag_new (0); + } + + if (software_a0_b_retw_interlock) + { + if ((get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0 + && is_conditional_branch_opcode (orig_insn.opcode)) + { + has_a0_b_retw = TRUE; + + /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW. + After the first assembly pass we will check all of them and + add a nop if needed. */ + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 4, 4, + RELAX_ADD_NOP_IF_A0_B_RETW, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 4, 4, + RELAX_ADD_NOP_IF_A0_B_RETW, + frag_now->fr_symbol, frag_now->fr_offset, NULL); + } + } + + /* Special case: The call instructions should be marked "specific opcode" + to keep them from expanding. */ + if (!use_longcalls () && is_direct_call_opcode (orig_insn.opcode)) + orig_insn.is_specific_opcode = TRUE; + + /* Parse the arguments. */ + if (parse_arguments (&orig_insn, num_args, arg_strings)) + { + as_bad (_("syntax error")); + return; + } + + /* Free the opcode and argument strings, now that they've been parsed. */ + free (has_underbar ? opname - 1 : opname); + opname = 0; + while (num_args-- > 0) + free (arg_strings[num_args]); + + /* Check for the right number and type of arguments. */ + if (tinsn_check_arguments (&orig_insn)) + return; + + /* See if the instruction implies an aligned section. */ + if (is_entry_opcode (orig_insn.opcode) || is_loop_opcode (orig_insn.opcode)) + record_alignment (now_seg, 2); + + xg_add_branch_and_loop_targets (&orig_insn); + + /* Special cases for instructions that force an alignment... */ + if (!orig_insn.is_specific_opcode && is_loop_opcode (orig_insn.opcode)) + { + fragS *old_frag = frag_now; + offsetT old_offset = frag_now_fix (); + symbolS *old_sym = NULL; + size_t max_fill; + + frag_now->tc_frag_data.is_insn = TRUE; + frag_now->tc_frag_data.is_no_density = !code_density_available (); + max_fill = get_text_align_max_fill_size + (get_text_align_power (XTENSA_FETCH_WIDTH), + TRUE, frag_now->tc_frag_data.is_no_density); + frag_var (rs_machine_dependent, max_fill, max_fill, + RELAX_ALIGN_NEXT_OPCODE, frag_now->fr_symbol, + frag_now->fr_offset, NULL); + + /* Repeat until there are no more. */ + while ((old_sym = xtensa_find_label (old_frag, old_offset, FALSE))) + { + S_SET_VALUE (old_sym, (valueT) 0); + symbol_set_frag (old_sym, frag_now); + } + } + + /* Special count for "entry" instruction. */ + if (is_entry_opcode (orig_insn.opcode)) + { + /* Check that the second opcode (#1) is >= 16. */ + if (orig_insn.ntok >= 2) + { + expressionS *exp = &orig_insn.tok[1]; + switch (exp->X_op) + { + case O_constant: + if (exp->X_add_number < 16) + as_warn (_("entry instruction with stack decrement < 16")); + break; + + default: + as_warn (_("entry instruction with non-constant decrement")); + } + } + } + + if (!orig_insn.is_specific_opcode && is_entry_opcode (orig_insn.opcode)) + { + xtensa_mark_literal_pool_location (TRUE); + + /* Automatically align ENTRY instructions. */ + frag_align (2, 0, 0); + } + + if (software_a0_b_retw_interlock) + set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER, + is_register_writer (&orig_insn, "a", 0)); + + set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, + is_bad_loopend_opcode (&orig_insn)); + + /* Finish it off: + assemble_tokens (opcode, tok, ntok); + expand the tokens from the orig_insn into the + stack of instructions that will not expand + unless required at relaxation time. */ + if (xg_expand_assembly_insn (&istack, &orig_insn)) + return; + + for (i = 0; i < istack.ninsn; i++) + { + TInsn *insn = &istack.insn[i]; + if (insn->insn_type == ITYPE_LITERAL) + { + assert (lit_sym == NULL); + lit_sym = xg_assemble_literal (insn); + } + else + { + if (lit_sym) + xg_resolve_literals (insn, lit_sym); + xg_assemble_tokens (insn); + } + } + + /* Now, if the original opcode was a call... */ + if (align_targets && is_call_opcode (orig_insn.opcode)) + { + frag_now->tc_frag_data.is_insn = TRUE; + frag_var (rs_machine_dependent, 4, 4, + RELAX_DESIRE_ALIGN, + frag_now->fr_symbol, + frag_now->fr_offset, + NULL); + } +} + + +/* TC_CONS_FIX_NEW hook: Check for "@PLT" suffix on symbol references. + If found, use an XTENSA_PLT reloc for 4-byte values. Otherwise, this + is the same as the standard code in read.c. */ + +void +xtensa_cons_fix_new (frag, where, size, exp) + fragS *frag; + int where; + int size; + expressionS *exp; +{ + bfd_reloc_code_real_type r; + bfd_boolean plt = FALSE; + + if (*input_line_pointer == '@') + { + if (!strncmp (input_line_pointer, PLT_SUFFIX, strlen (PLT_SUFFIX) - 1) + && !strncmp (input_line_pointer, plt_suffix, + strlen (plt_suffix) - 1)) + { + as_bad (_("undefined @ suffix '%s', expected '%s'"), + input_line_pointer, plt_suffix); + ignore_rest_of_line (); + return; + } + + input_line_pointer += strlen (plt_suffix); + plt = TRUE; + } + + switch (size) + { + case 1: + r = BFD_RELOC_8; + break; + case 2: + r = BFD_RELOC_16; + break; + case 4: + r = plt ? BFD_RELOC_XTENSA_PLT : BFD_RELOC_32; + break; + case 8: + r = BFD_RELOC_64; + break; + default: + as_bad (_("unsupported BFD relocation size %u"), size); + r = BFD_RELOC_32; + break; + } + fix_new_exp (frag, where, size, exp, 0, r); +} + + +/* TC_FRAG_INIT hook */ + +void +xtensa_frag_init (frag) + fragS *frag; +{ + frag->tc_frag_data.is_no_density = !code_density_available (); +} + + +symbolS * +md_undefined_symbol (name) + char *name ATTRIBUTE_UNUSED; +{ + return NULL; +} + + +/* Round up a section size to the appropriate boundary. */ + +valueT +md_section_align (segment, size) + segT segment ATTRIBUTE_UNUSED; + valueT size; +{ + return size; /* Byte alignment is fine. */ +} + + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + char *insn_p; + static xtensa_insnbuf insnbuf = NULL; + int opnum; + xtensa_operand operand; + xtensa_opcode opcode; + xtensa_isa isa = xtensa_default_isa; + valueT addr = fixP->fx_where + fixP->fx_frag->fr_address; + + if (fixP->fx_done) + return addr; + + if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND) + return addr; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where]; + xtensa_insnbuf_from_chars (isa, insnbuf, insn_p); + opcode = xtensa_decode_insn (isa, insnbuf); + + opnum = reloc_to_opnum (fixP->fx_r_type); + + if (opnum < 0) + as_fatal (_("invalid operand relocation for '%s' instruction"), + xtensa_opcode_name (isa, opcode)); + if (opnum >= xtensa_num_operands (isa, opcode)) + as_fatal (_("invalid relocation for operand %d in '%s' instruction"), + opnum, xtensa_opcode_name (isa, opcode)); + operand = xtensa_get_operand (isa, opcode, opnum); + if (!operand) + { + as_warn_where (fixP->fx_file, + fixP->fx_line, + _("invalid relocation type %d for %s instruction"), + fixP->fx_r_type, xtensa_opcode_name (isa, opcode)); + return addr; + } + + if (!operand_is_pcrel_label (operand)) + { + as_bad_where (fixP->fx_file, + fixP->fx_line, + _("invalid relocation for operand %d of '%s'"), + opnum, xtensa_opcode_name (isa, opcode)); + return addr; + } + if (!xtensa_operand_isPCRelative (operand)) + { + as_warn_where (fixP->fx_file, + fixP->fx_line, + _("non-PCREL relocation operand %d for '%s': %s"), + opnum, xtensa_opcode_name (isa, opcode), + bfd_get_reloc_code_name (fixP->fx_r_type)); + return addr; + } + + return 0 - xtensa_operand_do_reloc (operand, 0, addr); +} + + +/* tc_symbol_new_hook */ + +void +xtensa_symbol_new_hook (symbolP) + symbolS *symbolP; +{ + symbolP->sy_tc.plt = 0; +} + + +/* tc_fix_adjustable hook */ + +bfd_boolean +xtensa_fix_adjustable (fixP) + fixS *fixP; +{ + /* We need the symbol name for the VTABLE entries. */ + if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 0; + + return 1; +} + + +void +md_apply_fix3 (fixP, valP, seg) + fixS *fixP; + valueT *valP; + segT seg ATTRIBUTE_UNUSED; +{ + if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0) + { + /* This happens when the relocation is within the current section. + It seems this implies a PCREL operation. We'll catch it and error + if not. */ + + char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where; + static xtensa_insnbuf insnbuf = NULL; + xtensa_opcode opcode; + xtensa_isa isa; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_XTENSA_ASM_EXPAND: + fixP->fx_done = 1; + break; + + case BFD_RELOC_XTENSA_ASM_SIMPLIFY: + as_bad (_("unhandled local relocation fix %s"), + bfd_get_reloc_code_name (fixP->fx_r_type)); + break; + + case BFD_RELOC_32: + case BFD_RELOC_16: + case BFD_RELOC_8: + /* The only one we support that isn't an instruction field. */ + md_number_to_chars (fixpos, *valP, fixP->fx_size); + fixP->fx_done = 1; + break; + + case BFD_RELOC_XTENSA_OP0: + case BFD_RELOC_XTENSA_OP1: + case BFD_RELOC_XTENSA_OP2: + isa = xtensa_default_isa; + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + xtensa_insnbuf_from_chars (isa, insnbuf, fixpos); + opcode = xtensa_decode_insn (isa, insnbuf); + if (opcode == XTENSA_UNDEFINED) + as_fatal (_("undecodable FIX")); + + xtensa_insnbuf_set_immediate_field (opcode, insnbuf, *valP, + fixP->fx_file, fixP->fx_line); + + fixP->fx_frag->tc_frag_data.is_insn = TRUE; + xtensa_insnbuf_to_chars (isa, insnbuf, fixpos); + fixP->fx_done = 1; + break; + + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + break; + + default: + as_bad (_("unhandled local relocation fix %s"), + bfd_get_reloc_code_name (fixP->fx_r_type)); + } + } +} + + +char * +md_atof (type, litP, sizeP) + int type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[4]; + char *t; + int i; + + switch (type) + { + case 'f': + prec = 2; + break; + + case 'd': + prec = 4; + break; + + default: + *sizeP = 0; + return "bad call to md_atof"; + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * 2; + + for (i = prec - 1; i >= 0; i--) + { + int idx = i; + if (target_big_endian) + idx = (prec - 1 - i); + + md_number_to_chars (litP, (valueT) words[idx], 2); + litP += 2; + } + + return NULL; +} + + +int +md_estimate_size_before_relax (fragP, seg) + fragS *fragP; + segT seg ATTRIBUTE_UNUSED; +{ + return fragP->tc_frag_data.text_expansion; +} + + +/* Translate internal representation of relocation info to BFD target + format. */ + +arelent * +tc_gen_reloc (section, fixp) + asection *section ATTRIBUTE_UNUSED; + fixS *fixp; +{ + arelent *reloc; + + reloc = (arelent *) xmalloc (sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + + /* Make sure none of our internal relocations make it this far. + They'd better have been fully resolved by this point. */ + assert ((int) fixp->fx_r_type > 0); + + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent `%s' relocation in object file"), + bfd_get_reloc_code_name (fixp->fx_r_type)); + return NULL; + } + + if (!fixp->fx_pcrel != !reloc->howto->pc_relative) + { + as_fatal (_("internal error? cannot generate `%s' relocation"), + bfd_get_reloc_code_name (fixp->fx_r_type)); + } + assert (!fixp->fx_pcrel == !reloc->howto->pc_relative); + + reloc->addend = fixp->fx_offset; + + switch (fixp->fx_r_type) + { + case BFD_RELOC_XTENSA_OP0: + case BFD_RELOC_XTENSA_OP1: + case BFD_RELOC_XTENSA_OP2: + case BFD_RELOC_XTENSA_ASM_EXPAND: + case BFD_RELOC_32: + case BFD_RELOC_XTENSA_PLT: + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + break; + + case BFD_RELOC_XTENSA_ASM_SIMPLIFY: + as_warn (_("emitting simplification relocation")); + break; + + default: + as_warn (_("emitting unknown relocation")); + } + + return reloc; +} + + +void +xtensa_end () +{ + directive_balance (); + xtensa_move_literals (); + + xtensa_reorder_segments (); + xtensa_mark_target_fragments (); + xtensa_cleanup_align_frags (); + xtensa_fix_target_frags (); + if (software_a0_b_retw_interlock && has_a0_b_retw) + xtensa_fix_a0_b_retw_frags (); + if (software_avoid_b_j_loop_end && maybe_has_b_j_loop_end) + xtensa_fix_b_j_loop_end_frags (); + + /* "close_loop_end" should be processed BEFORE "short_loop". */ + if (software_avoid_close_loop_end && maybe_has_close_loop_end) + xtensa_fix_close_loop_end_frags (); + + if (software_avoid_short_loop && maybe_has_short_loop) + xtensa_fix_short_loop_frags (); + + xtensa_sanity_check (); +} + + +static void +xtensa_cleanup_align_frags () +{ + frchainS *frchP; + + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + fragS *fragP; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + if ((fragP->fr_type == rs_align + || fragP->fr_type == rs_align_code + || (fragP->fr_type == rs_machine_dependent + && (fragP->fr_subtype == RELAX_DESIRE_ALIGN + || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET))) + && fragP->fr_fix == 0) + { + fragS * next = fragP->fr_next; + + while (next + && next->fr_type == rs_machine_dependent + && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET) + { + frag_wane (next); + next = next->fr_next; + } + } + } + } +} + + +/* Re-process all of the fragments looking to convert all of the + RELAX_DESIRE_ALIGN_IF_TARGET fragments. If there is a branch + target in the next fragment, convert this to RELAX_DESIRE_ALIGN. + If the next fragment starts with a loop target, AND the previous + fragment can be expanded to negate the branch, convert this to a + RELAX_LOOP_END. Otherwise, convert to a .fill 0. */ + +static void +xtensa_fix_target_frags () +{ + frchainS *frchP; + + /* When this routine is called, all of the subsections are still intact + so we walk over subsections instead of sections. */ + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + bfd_boolean prev_frag_can_negate_branch = FALSE; + fragS *fragP; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET) + { + if (next_frag_is_loop_target (fragP)) + { + if (prev_frag_can_negate_branch) + fragP->fr_subtype = RELAX_LOOP_END; + else + { + if (!align_only_targets || + next_frag_is_branch_target (fragP)) + fragP->fr_subtype = RELAX_DESIRE_ALIGN; + else + frag_wane (fragP); + } + } + else if (!align_only_targets + || next_frag_is_branch_target (fragP)) + fragP->fr_subtype = RELAX_DESIRE_ALIGN; + else + frag_wane (fragP); + } + if (fragP->fr_fix != 0) + prev_frag_can_negate_branch = FALSE; + if (frag_can_negate_branch (fragP)) + prev_frag_can_negate_branch = TRUE; + } + } +} + + +static bfd_boolean +frag_can_negate_branch (fragP) + fragS *fragP; +{ + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_IMMED) + { + TInsn t_insn; + tinsn_from_chars (&t_insn, fragP->fr_opcode); + if (is_negatable_branch (&t_insn)) + return TRUE; + } + return FALSE; +} + + +/* Re-process all of the fragments looking to convert all of the + RELAX_ADD_NOP_IF_A0_B_RETW. If the next instruction is a + conditional branch or a retw/retw.n, convert this frag to one that + will generate a NOP. In any case close it off with a .fill 0. */ + +static void +xtensa_fix_a0_b_retw_frags () +{ + frchainS *frchP; + + /* When this routine is called, all of the subsections are still intact + so we walk over subsections instead of sections. */ + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + fragS *fragP; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW) + { + if (next_instrs_are_b_retw (fragP)) + relax_frag_add_nop (fragP); + else + frag_wane (fragP); + } + } + } +} + + +bfd_boolean +next_instrs_are_b_retw (fragP) + fragS * fragP; +{ + xtensa_opcode opcode; + const fragS *next_fragP = next_non_empty_frag (fragP); + static xtensa_insnbuf insnbuf = NULL; + xtensa_isa isa = xtensa_default_isa; + int offset = 0; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + if (next_fragP == NULL) + return FALSE; + + /* Check for the conditional branch. */ + xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]); + opcode = xtensa_decode_insn (isa, insnbuf); + + if (!is_conditional_branch_opcode (opcode)) + return FALSE; + + offset += xtensa_insn_length (isa, opcode); + if (offset == next_fragP->fr_fix) + { + next_fragP = next_non_empty_frag (next_fragP); + offset = 0; + } + if (next_fragP == NULL) + return FALSE; + + /* Check for the retw/retw.n. */ + xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]); + opcode = xtensa_decode_insn (isa, insnbuf); + + if (is_windowed_return_opcode (opcode)) + return TRUE; + return FALSE; +} + + +/* Re-process all of the fragments looking to convert all of the + RELAX_ADD_NOP_IF_PRE_LOOP_END. If there is one instruction and a + loop end label, convert this frag to one that will generate a NOP. + In any case close it off with a .fill 0. */ + +static void +xtensa_fix_b_j_loop_end_frags () +{ + frchainS *frchP; + + /* When this routine is called, all of the subsections are still intact + so we walk over subsections instead of sections. */ + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + fragS *fragP; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END) + { + if (next_instr_is_loop_end (fragP)) + relax_frag_add_nop (fragP); + else + frag_wane (fragP); + } + } + } +} + + +bfd_boolean +next_instr_is_loop_end (fragP) + fragS * fragP; +{ + const fragS *next_fragP; + + if (next_frag_is_loop_target (fragP)) + return FALSE; + + next_fragP = next_non_empty_frag (fragP); + if (next_fragP == NULL) + return FALSE; + + if (!next_frag_is_loop_target (next_fragP)) + return FALSE; + + /* If the size is >= 3 then there is more than one instruction here. + The hardware bug will not fire. */ + if (next_fragP->fr_fix > 3) + return FALSE; + + return TRUE; +} + + +/* Re-process all of the fragments looking to convert all of the + RELAX_ADD_NOP_IF_CLOSE_LOOP_END. If there is an loop end that is + not MY loop's loop end within 12 bytes, add enough nops here to + make it at least 12 bytes away. In any case close it off with a + .fill 0. */ + +static void +xtensa_fix_close_loop_end_frags () +{ + frchainS *frchP; + + /* When this routine is called, all of the subsections are still intact + so we walk over subsections instead of sections. */ + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + fragS *fragP; + + fragS *current_target = NULL; + offsetT current_offset = 0; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_IMMED) + { + /* Read it. If the instruction is a loop, get the target. */ + xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode); + if (is_loop_opcode (opcode)) + { + TInsn t_insn; + + tinsn_from_chars (&t_insn, fragP->fr_opcode); + tinsn_immed_from_frag (&t_insn, fragP); + + /* Get the current fragment target. */ + if (fragP->fr_symbol) + { + current_target = symbol_get_frag (fragP->fr_symbol); + current_offset = fragP->fr_offset; + } + } + } + + if (current_target + && fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END) + { + size_t min_bytes; + size_t bytes_added = 0; + +#define REQUIRED_LOOP_DIVIDING_BYTES 12 + /* Max out at 12. */ + min_bytes = min_bytes_to_other_loop_end + (fragP->fr_next, current_target, current_offset, + REQUIRED_LOOP_DIVIDING_BYTES); + + if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES) + { + while (min_bytes + bytes_added + < REQUIRED_LOOP_DIVIDING_BYTES) + { + int length = 3; + + if (fragP->fr_var < length) + as_warn (_("fr_var %lu < length %d; ignoring"), + fragP->fr_var, length); + else + { + assemble_nop (length, + fragP->fr_literal + fragP->fr_fix); + fragP->fr_fix += length; + fragP->fr_var -= length; + } + bytes_added += length; + } + } + frag_wane (fragP); + } + } + } +} + + +size_t +min_bytes_to_other_loop_end (fragP, current_target, current_offset, max_size) + fragS *fragP; + fragS *current_target; + offsetT current_offset; + size_t max_size; +{ + size_t offset = 0; + fragS *current_fragP; + + for (current_fragP = fragP; + current_fragP; + current_fragP = current_fragP->fr_next) + { + if (current_fragP->tc_frag_data.is_loop_target + && current_fragP != current_target) + return offset + current_offset; + + offset += unrelaxed_frag_min_size (current_fragP); + + if (offset + current_offset >= max_size) + return max_size; + } + return max_size; +} + + +size_t +unrelaxed_frag_min_size (fragP) + fragS * fragP; +{ + size_t size = fragP->fr_fix; + + /* add fill size */ + if (fragP->fr_type == rs_fill) + size += fragP->fr_offset; + + return size; +} + + +/* Re-process all of the fragments looking to convert all + of the RELAX_ADD_NOP_IF_SHORT_LOOP. If: + + A) + 1) the instruction size count to the loop end label + is too short (<= 2 instructions), + 2) loop has a jump or branch in it + + or B) + 1) software_avoid_all_short_loops is true + 2) The generating loop was a 'loopgtz' or 'loopnez' + 3) the instruction size count to the loop end label is too short + (<= 2 instructions) + then convert this frag (and maybe the next one) to generate a NOP. + In any case close it off with a .fill 0. */ + +static void +xtensa_fix_short_loop_frags () +{ + frchainS *frchP; + + /* When this routine is called, all of the subsections are still intact + so we walk over subsections instead of sections. */ + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + fragS *fragP; + fragS *current_target = NULL; + offsetT current_offset = 0; + xtensa_opcode current_opcode = XTENSA_UNDEFINED; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + /* check on the current loop */ + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_IMMED) + { + /* Read it. If the instruction is a loop, get the target. */ + xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode); + if (is_loop_opcode (opcode)) + { + TInsn t_insn; + + tinsn_from_chars (&t_insn, fragP->fr_opcode); + tinsn_immed_from_frag (&t_insn, fragP); + + /* Get the current fragment target. */ + if (fragP->fr_symbol) + { + current_target = symbol_get_frag (fragP->fr_symbol); + current_offset = fragP->fr_offset; + current_opcode = opcode; + } + } + } + + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP) + { + size_t insn_count = + count_insns_to_loop_end (fragP->fr_next, TRUE, 3); + if (insn_count < 3 + && (branch_before_loop_end (fragP->fr_next) + || (software_avoid_all_short_loops + && current_opcode != XTENSA_UNDEFINED + && !is_the_loop_opcode (current_opcode)))) + relax_frag_add_nop (fragP); + else + frag_wane (fragP); + } + } + } +} + + +size_t +count_insns_to_loop_end (base_fragP, count_relax_add, max_count) + fragS *base_fragP; + bfd_boolean count_relax_add; + size_t max_count; +{ + fragS *fragP = NULL; + size_t insn_count = 0; + + fragP = base_fragP; + + for (; fragP && !fragP->tc_frag_data.is_loop_target; fragP = fragP->fr_next) + { + insn_count += unrelaxed_frag_min_insn_count (fragP); + if (insn_count >= max_count) + return max_count; + + if (count_relax_add) + { + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP) + { + /* In order to add the appropriate number of + NOPs, we count an instruction for downstream + occurrences. */ + insn_count++; + if (insn_count >= max_count) + return max_count; + } + } + } + return insn_count; +} + + +size_t +unrelaxed_frag_min_insn_count (fragP) + fragS *fragP; +{ + size_t insn_count = 0; + int offset = 0; + + if (!fragP->tc_frag_data.is_insn) + return insn_count; + + /* Decode the fixed instructions. */ + while (offset < fragP->fr_fix) + { + xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset); + if (opcode == XTENSA_UNDEFINED) + { + as_fatal (_("undecodable instruction in instruction frag")); + return insn_count; + } + offset += xtensa_insn_length (xtensa_default_isa, opcode); + insn_count++; + } + + return insn_count; +} + + +bfd_boolean +branch_before_loop_end (base_fragP) + fragS *base_fragP; +{ + fragS *fragP; + + for (fragP = base_fragP; + fragP && !fragP->tc_frag_data.is_loop_target; + fragP = fragP->fr_next) + { + if (unrelaxed_frag_has_b_j (fragP)) + return TRUE; + } + return FALSE; +} + + +bfd_boolean +unrelaxed_frag_has_b_j (fragP) + fragS *fragP; +{ + size_t insn_count = 0; + int offset = 0; + + if (!fragP->tc_frag_data.is_insn) + return FALSE; + + /* Decode the fixed instructions. */ + while (offset < fragP->fr_fix) + { + xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset); + if (opcode == XTENSA_UNDEFINED) + { + as_fatal (_("undecodable instruction in instruction frag")); + return insn_count; + } + if (is_branch_or_jump_opcode (opcode)) + return TRUE; + offset += xtensa_insn_length (xtensa_default_isa, opcode); + } + return FALSE; +} + + +/* Checks to be made after initial assembly but before relaxation. */ + +static void +xtensa_sanity_check () +{ + char *file_name; + int line; + + frchainS *frchP; + + as_where (&file_name, &line); + for (frchP = frchain_root; frchP; frchP = frchP->frch_next) + { + fragS *fragP; + + /* Walk over all of the fragments in a subsection. */ + for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next) + { + /* Currently we only check for empty loops here. */ + if (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_IMMED) + { + static xtensa_insnbuf insnbuf = NULL; + TInsn t_insn; + + if (fragP->fr_opcode != NULL) + { + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + tinsn_from_chars (&t_insn, fragP->fr_opcode); + tinsn_immed_from_frag (&t_insn, fragP); + + if (is_loop_opcode (t_insn.opcode)) + { + if (is_empty_loop (&t_insn, fragP)) + { + new_logical_line (fragP->fr_file, fragP->fr_line); + as_bad (_("invalid empty loop")); + } + if (!is_local_forward_loop (&t_insn, fragP)) + { + new_logical_line (fragP->fr_file, fragP->fr_line); + as_bad (_("loop target does not follow " + "loop instruction in section")); + } + } + } + } + } + } + new_logical_line (file_name, line); +} + + +#define LOOP_IMMED_OPN 1 + +/* Return true if the loop target is the next non-zero fragment. */ + +bfd_boolean +is_empty_loop (insn, fragP) + const TInsn *insn; + fragS *fragP; +{ + const expressionS *expr; + symbolS *symbolP; + fragS *next_fragP; + + if (insn->insn_type != ITYPE_INSN) + return FALSE; + + if (!is_loop_opcode (insn->opcode)) + return FALSE; + + if (insn->ntok <= LOOP_IMMED_OPN) + return FALSE; + + expr = &insn->tok[LOOP_IMMED_OPN]; + + if (expr->X_op != O_symbol) + return FALSE; + + symbolP = expr->X_add_symbol; + if (!symbolP) + return FALSE; + + if (symbol_get_frag (symbolP) == NULL) + return FALSE; + + if (S_GET_VALUE (symbolP) != 0) + return FALSE; + + /* Walk through the zero-size fragments from this one. If we find + the target fragment, then this is a zero-size loop. */ + for (next_fragP = fragP->fr_next; + next_fragP != NULL; + next_fragP = next_fragP->fr_next) + { + if (next_fragP == symbol_get_frag (symbolP)) + return TRUE; + if (next_fragP->fr_fix != 0) + return FALSE; + } + return FALSE; +} + + +bfd_boolean +is_local_forward_loop (insn, fragP) + const TInsn *insn; + fragS *fragP; +{ + const expressionS *expr; + symbolS *symbolP; + fragS *next_fragP; + + if (insn->insn_type != ITYPE_INSN) + return FALSE; + + if (!is_loop_opcode (insn->opcode)) + return FALSE; + + if (insn->ntok <= LOOP_IMMED_OPN) + return FALSE; + + expr = &insn->tok[LOOP_IMMED_OPN]; + + if (expr->X_op != O_symbol) + return FALSE; + + symbolP = expr->X_add_symbol; + if (!symbolP) + return FALSE; + + if (symbol_get_frag (symbolP) == NULL) + return FALSE; + + /* Walk through fragments until we find the target. + If we do not find the target, then this is an invalid loop. */ + for (next_fragP = fragP->fr_next; + next_fragP != NULL; + next_fragP = next_fragP->fr_next) + if (next_fragP == symbol_get_frag (symbolP)) + return TRUE; + + return FALSE; +} + + +/* Alignment Functions. */ + +size_t +get_text_align_power (target_size) + int target_size; +{ + size_t i = 0; + for (i = 0; i < sizeof (size_t); i++) + { + if (target_size <= (1 << i)) + return i; + } + as_fatal (_("get_text_align_power: argument too large")); + return 0; +} + + +addressT +get_text_align_max_fill_size (align_pow, use_nops, use_no_density) + int align_pow; + bfd_boolean use_nops; + bfd_boolean use_no_density; +{ + if (!use_nops) + return (1 << align_pow); + if (use_no_density) + return 3 * (1 << align_pow); + + return 1 + (1 << align_pow); +} + + +/* get_text_align_fill_size () + + Desired alignments: + give the address + target_size = size of next instruction + align_pow = get_text_align_power (target_size). + use_nops = 0 + use_no_density = 0; + Loop alignments: + address = current address + loop instruction size; + target_size = 3 (for 2 or 3 byte target) + = 8 (for 8 byte target) + align_pow = get_text_align_power (target_size); + use_nops = 1 + use_no_density = set appropriately + Text alignments: + address = current address + loop instruction size; + target_size = 0 + align_pow = get_text_align_power (target_size); + use_nops = 0 + use_no_density = 0. */ + +addressT +get_text_align_fill_size (address, align_pow, target_size, + use_nops, use_no_density) + addressT address; + int align_pow; + int target_size; + bfd_boolean use_nops; + bfd_boolean use_no_density; +{ + /* Input arguments: + + align_pow: log2 (required alignment). + + target_size: alignment must allow the new_address and + new_address+target_size-1. + + use_nops: if true, then we can only use 2 or 3 byte nops. + + use_no_density: if use_nops and use_no_density, we can only use + 3-byte nops. + + Usually, for non-zero target_size, the align_pow is the power of 2 + that is greater than or equal to the target_size. This handles the + 2-byte, 3-byte and 8-byte instructions. */ + + size_t alignment = (1 << align_pow); + if (!use_nops) + { + /* This is the easy case. */ + size_t mod; + mod = address % alignment; + if (mod != 0) + mod = alignment - mod; + assert ((address + mod) % alignment == 0); + return mod; + } + + /* This is the slightly harder case. */ + assert ((int) alignment >= target_size); + assert (target_size > 0); + if (!use_no_density) + { + size_t i; + for (i = 0; i < alignment * 2; i++) + { + if (i == 1) + continue; + if ((address + i) >> align_pow == + (address + i + target_size - 1) >> align_pow) + return i; + } + } + else + { + size_t i; + + /* Can only fill multiples of 3. */ + for (i = 0; i <= alignment * 3; i += 3) + { + if ((address + i) >> align_pow == + (address + i + target_size - 1) >> align_pow) + return i; + } + } + assert (0); + return 0; +} + + +/* This will assert if it is not possible. */ + +size_t +get_text_align_nop_count (fill_size, use_no_density) + size_t fill_size; + bfd_boolean use_no_density; +{ + size_t count = 0; + if (use_no_density) + { + assert (fill_size % 3 == 0); + return (fill_size / 3); + } + + assert (fill_size != 1); /* Bad argument. */ + + while (fill_size > 1) + { + size_t insn_size = 3; + if (fill_size == 2 || fill_size == 4) + insn_size = 2; + fill_size -= insn_size; + count++; + } + assert (fill_size != 1); /* Bad algorithm. */ + return count; +} + + +size_t +get_text_align_nth_nop_size (fill_size, n, use_no_density) + size_t fill_size; + size_t n; + bfd_boolean use_no_density; +{ + size_t count = 0; + + assert (get_text_align_nop_count (fill_size, use_no_density) > n); + + if (use_no_density) + return 3; + + while (fill_size > 1) + { + size_t insn_size = 3; + if (fill_size == 2 || fill_size == 4) + insn_size = 2; + fill_size -= insn_size; + count++; + if (n + 1 == count) + return insn_size; + } + assert (0); + return 0; +} + + +/* For the given fragment, find the appropriate address + for it to begin at if we are using NOPs to align it. */ + +static addressT +get_noop_aligned_address (fragP, address) + fragS *fragP; + addressT address; +{ + static xtensa_insnbuf insnbuf = NULL; + size_t fill_size = 0; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + switch (fragP->fr_type) + { + case rs_machine_dependent: + if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE) + { + /* The rule is: get next fragment's FIRST instruction. Find + the smallest number of bytes that need to be added to + ensure that the next fragment's FIRST instruction will fit + in a single word. + + E.G., 2 bytes : 0, 1, 2 mod 4 + 3 bytes: 0, 1 mod 4 + + If the FIRST instruction MIGHT be relaxed, + assume that it will become a 3 byte instruction. */ + + int target_insn_size; + xtensa_opcode opcode = next_frag_opcode (fragP); + addressT pre_opcode_bytes; + + if (opcode == XTENSA_UNDEFINED) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE")); + as_fatal (_("cannot continue")); + } + + target_insn_size = xtensa_insn_length (xtensa_default_isa, opcode); + + pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP); + + if (is_loop_opcode (opcode)) + { + /* next_fragP should be the loop. */ + const fragS *next_fragP = next_non_empty_frag (fragP); + xtensa_opcode next_opcode = next_frag_opcode (next_fragP); + size_t alignment; + + pre_opcode_bytes += target_insn_size; + + /* For loops, the alignment depends on the size of the + instruction following the loop, not the loop instruction. */ + if (next_opcode == XTENSA_UNDEFINED) + target_insn_size = 3; + else + { + target_insn_size = + xtensa_insn_length (xtensa_default_isa, next_opcode); + + if (target_insn_size == 2) + target_insn_size = 3; /* ISA specifies this. */ + } + + /* If it was 8, then we'll need a larger alignment + for the section. */ + alignment = get_text_align_power (target_insn_size); + + /* Is Now_seg valid */ + record_alignment (now_seg, alignment); + } + else + as_fatal (_("expected loop opcode in relax align next target")); + + fill_size = get_text_align_fill_size + (address + pre_opcode_bytes, + get_text_align_power (target_insn_size), + target_insn_size, TRUE, fragP->tc_frag_data.is_no_density); + } + break; +#if 0 + case rs_align: + case rs_align_code: + fill_size = get_text_align_fill_size + (address, fragP->fr_offset, 1, TRUE, + fragP->tc_frag_data.is_no_density); + break; +#endif + default: + as_fatal (_("expected align_code or RELAX_ALIGN_NEXT_OPCODE")); + } + + return address + fill_size; +} + + +/* 3 mechanisms for relaxing an alignment: + + Align to a power of 2. + Align so the next fragment's instruction does not cross a word boundary. + Align the current instruction so that if the next instruction + were 3 bytes, it would not cross a word boundary. + + We can align with: + + zeros - This is easy; always insert zeros. + nops - 3 and 2 byte instructions + 2 - 2 byte nop + 3 - 3 byte nop + 4 - 2, 2-byte nops + >=5 : 3 byte instruction + fn(n-3) + widening - widen previous instructions. */ + +static addressT +get_widen_aligned_address (fragP, address) + fragS *fragP; + addressT address; +{ + addressT align_pow, new_address, loop_insn_offset; + fragS *next_frag; + int insn_size; + xtensa_opcode opcode, next_opcode; + static xtensa_insnbuf insnbuf = NULL; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + if (fragP->fr_type == rs_align || fragP->fr_type == rs_align_code) + { + align_pow = fragP->fr_offset; + new_address = ((address + ((1 << align_pow) - 1)) + << align_pow) >> align_pow; + return new_address; + } + + if (fragP->fr_type == rs_machine_dependent) + { + switch (fragP->fr_subtype) + { + case RELAX_DESIRE_ALIGN: + + /* The rule is: get the next fragment's FIRST instruction. + Find the smallest number of bytes needed to be added + in order to ensure that the next fragment is FIRST + instruction will fit in a single word. + i.e. 2 bytes : 0, 1, 2. mod 4 + 3 bytes: 0, 1 mod 4 + If the FIRST instruction MIGHT be relaxed, + assume that it will become a 3-byte instruction. */ + + insn_size = 3; + /* Check to see if it might be 2 bytes. */ + next_opcode = next_frag_opcode (fragP); + if (next_opcode != XTENSA_UNDEFINED + && xtensa_insn_length (xtensa_default_isa, next_opcode) == 2) + insn_size = 2; + + assert (insn_size <= 4); + for (new_address = address; new_address < address + 4; new_address++) + { + if (new_address >> 2 == (new_address + insn_size - 1) >> 2) + return new_address; + } + as_bad (_("internal error aligning")); + return address; + + case RELAX_ALIGN_NEXT_OPCODE: + /* The rule is: get next fragment's FIRST instruction. + Find the smallest number of bytes needed to be added + in order to ensure that the next fragment's FIRST + instruction will fit in a single word. + i.e. 2 bytes : 0, 1, 2. mod 4 + 3 bytes: 0, 1 mod 4 + If the FIRST instruction MIGHT be relaxed, + assume that it will become a 3 byte instruction. */ + + opcode = next_frag_opcode (fragP); + if (opcode == XTENSA_UNDEFINED) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE")); + as_fatal (_("cannot continue")); + } + insn_size = xtensa_insn_length (xtensa_default_isa, opcode); + assert (insn_size <= 4); + assert (is_loop_opcode (opcode)); + + loop_insn_offset = 0; + next_frag = next_non_empty_frag (fragP); + + /* If the loop has been expanded then the loop + instruction could be at an offset from this fragment. */ + if (next_frag->fr_subtype != RELAX_IMMED) + loop_insn_offset = get_expanded_loop_offset (opcode); + + for (new_address = address; new_address < address + 4; new_address++) + { + if ((new_address + loop_insn_offset + insn_size) >> 2 == + (new_address + loop_insn_offset + insn_size + 2) >> 2) + return new_address; + } + as_bad (_("internal error aligning")); + return address; + + default: + as_bad (_("internal error aligning")); + return address; + } + } + as_bad (_("internal error aligning")); + return address; +} + + +/* md_relax_frag Hook and Helper Functions. */ + +/* Return the number of bytes added to this fragment, given that the + input has been stretched already by "stretch". */ + +long +xtensa_relax_frag (fragP, stretch, stretched_p) + fragS *fragP; + long stretch; + int *stretched_p; +{ + int unreported = fragP->tc_frag_data.unreported_expansion; + long new_stretch = 0; + char *file_name; + int line, lit_size; + + as_where (&file_name, &line); + new_logical_line (fragP->fr_file, fragP->fr_line); + + fragP->tc_frag_data.unreported_expansion = 0; + + switch (fragP->fr_subtype) + { + case RELAX_ALIGN_NEXT_OPCODE: + /* Always convert. */ + new_stretch = relax_frag_text_align (fragP, stretch); + break; + + case RELAX_LOOP_END: + /* Do nothing. */ + break; + + case RELAX_LOOP_END_ADD_NOP: + /* Add a NOP and switch to .fill 0. */ + new_stretch = relax_frag_add_nop (fragP); + break; + + case RELAX_DESIRE_ALIGN: + /* We REALLY want to change the relaxation order here. This + should do NOTHING. The narrowing before it will either align + it or not. */ + break; + + case RELAX_LITERAL: + case RELAX_LITERAL_FINAL: + return 0; + + case RELAX_LITERAL_NR: + lit_size = 4; + fragP->fr_subtype = RELAX_LITERAL_FINAL; + assert (unreported == lit_size); + memset (&fragP->fr_literal[fragP->fr_fix], 0, 4); + fragP->fr_var -= lit_size; + fragP->fr_fix += lit_size; + new_stretch = 4; + break; + + case RELAX_NARROW: + new_stretch = relax_frag_narrow (fragP, stretch); + break; + + case RELAX_IMMED: + case RELAX_IMMED_STEP1: + case RELAX_IMMED_STEP2: + /* Place the immediate. */ + new_stretch = relax_frag_immed (now_seg, fragP, stretch, + fragP->fr_subtype - RELAX_IMMED, + stretched_p); + break; + + case RELAX_LITERAL_POOL_BEGIN: + case RELAX_LITERAL_POOL_END: + /* No relaxation required. */ + break; + + default: + as_bad (_("bad relaxation state")); + } + + new_logical_line (file_name, line); + return new_stretch; +} + + +static long +relax_frag_text_align (fragP, stretch) + fragS *fragP; + long stretch; +{ + addressT old_address, old_next_address, old_size; + addressT new_address, new_next_address, new_size; + addressT growth; + + /* Overview of the relaxation procedure for alignment + inside an executable section: + + The old size is stored in the tc_frag_data.text_expansion field. + + Calculate the new address, fix up the text_expansion and + return the growth. */ + + /* Calculate the old address of this fragment and the next fragment. */ + old_address = fragP->fr_address - stretch; + old_next_address = (fragP->fr_address - stretch + fragP->fr_fix + + fragP->tc_frag_data.text_expansion); + old_size = old_next_address - old_address; + + /* Calculate the new address of this fragment and the next fragment. */ + new_address = fragP->fr_address; + new_next_address = + get_noop_aligned_address (fragP, fragP->fr_address + fragP->fr_fix); + new_size = new_next_address - new_address; + + growth = new_size - old_size; + + /* Fix up the text_expansion field and return the new growth. */ + fragP->tc_frag_data.text_expansion += growth; + return growth; +} + + +/* Add a NOP (i.e., "or a1, a1, a1"). Use the 3-byte one because we + don't know about the availability of density yet. TODO: When the + flags are stored per fragment, use NOP.N when possible. */ + +static long +relax_frag_add_nop (fragP) + fragS *fragP; +{ + static xtensa_insnbuf insnbuf = NULL; + TInsn t_insn; + char *nop_buf = fragP->fr_literal + fragP->fr_fix; + int length; + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + tinsn_init (&t_insn); + t_insn.opcode = xtensa_or_opcode; + assert (t_insn.opcode != XTENSA_UNDEFINED); + + t_insn.ntok = 3; + set_expr_const (&t_insn.tok[0], 1); + set_expr_const (&t_insn.tok[1], 1); + set_expr_const (&t_insn.tok[2], 1); + + tinsn_to_insnbuf (&t_insn, insnbuf); + fragP->tc_frag_data.is_insn = TRUE; + xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, nop_buf); + + length = xtensa_insn_length (xtensa_default_isa, t_insn.opcode); + if (fragP->fr_var < length) + { + as_warn (_("fr_var (%ld) < length (%d); ignoring"), + fragP->fr_var, length); + frag_wane (fragP); + return 0; + } + + fragP->fr_fix += length; + fragP->fr_var -= length; + frag_wane (fragP); + return length; +} + + +static long +relax_frag_narrow (fragP, stretch) + fragS *fragP; + long stretch; +{ + /* Overview of the relaxation procedure for alignment inside an + executable section: Find the number of widenings required and the + number of nop bytes required. Store the number of bytes ALREADY + widened. If there are enough instructions to widen (must go back + ONLY through NARROW fragments), mark each of the fragments as TO BE + widened, recalculate the fragment addresses. */ + + assert (fragP->fr_type == rs_machine_dependent + && fragP->fr_subtype == RELAX_NARROW); + + if (!future_alignment_required (fragP, 0)) + { + /* If already expanded but no longer needed because of a prior + stretch, it is SAFE to unexpand because the next fragment will + NEVER start at an address > the previous time through the + relaxation. */ + if (fragP->tc_frag_data.text_expansion) + { + if (stretch > 0) + { + fragP->tc_frag_data.text_expansion = 0; + return -1; + } + /* Otherwise we have to live with this bad choice. */ + return 0; + } + return 0; + } + + if (fragP->tc_frag_data.text_expansion == 0) + { + fragP->tc_frag_data.text_expansion = 1; + return 1; + } + + return 0; +} + + +static bfd_boolean +future_alignment_required (fragP, stretch) + fragS *fragP; + long stretch; +{ + long address = fragP->fr_address + stretch; + int num_widens = 0; + addressT aligned_address; + offsetT desired_diff; + + while (fragP) + { + /* Limit this to a small search. */ + if (num_widens > 8) + return FALSE; + address += fragP->fr_fix; + + switch (fragP->fr_type) + { + case rs_fill: + address += fragP->fr_offset * fragP->fr_var; + break; + + case rs_machine_dependent: + switch (fragP->fr_subtype) + { + case RELAX_NARROW: + /* address += fragP->fr_fix; */ + num_widens++; + break; + + case RELAX_IMMED: + address += (/* fragP->fr_fix + */ + fragP->tc_frag_data.text_expansion); + break; + + case RELAX_ALIGN_NEXT_OPCODE: + case RELAX_DESIRE_ALIGN: + /* address += fragP->fr_fix; */ + aligned_address = get_widen_aligned_address (fragP, address); + desired_diff = aligned_address - address; + assert (desired_diff >= 0); + /* If there are enough wideners in between do it. */ + /* return (num_widens == desired_diff); */ + if (num_widens == desired_diff) + return TRUE; + if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE) + return FALSE; + break; + + default: + return FALSE; + } + break; + + default: + return FALSE; + } + fragP = fragP->fr_next; + } + + return FALSE; +} + + +static long +relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p) + segT segP; + fragS *fragP; + long stretch; + int min_steps; + int *stretched_p; +{ + static xtensa_insnbuf insnbuf = NULL; + TInsn t_insn; + int old_size; + bfd_boolean negatable_branch = FALSE; + bfd_boolean branch_jmp_to_next = FALSE; + IStack istack; + offsetT frag_offset; + int num_steps; + fragS *lit_fragP; + int num_text_bytes, num_literal_bytes; + int literal_diff, text_diff; + + assert (fragP->fr_opcode != NULL); + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + tinsn_from_chars (&t_insn, fragP->fr_opcode); + tinsn_immed_from_frag (&t_insn, fragP); + + negatable_branch = is_negatable_branch (&t_insn); + + old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode); + + if (software_avoid_b_j_loop_end) + branch_jmp_to_next = is_branch_jmp_to_next (&t_insn, fragP); + + /* Special case: replace a branch to the next instruction with a NOP. + This is required to work around a hardware bug in T1040.0 and also + serves as an optimization. */ + + if (branch_jmp_to_next + && ((old_size == 2) || (old_size == 3)) + && !next_frag_is_loop_target (fragP)) + return 0; + + /* Here is the fun stuff: Get the immediate field from this + instruction. If it fits, we are done. If not, find the next + instruction sequence that fits. */ + + frag_offset = fragP->fr_opcode - fragP->fr_literal; + istack_init (&istack); + num_steps = xg_assembly_relax (&istack, &t_insn, segP, fragP, frag_offset, + min_steps, stretch); + if (num_steps < min_steps) + { + as_fatal (_("internal error: relaxation failed")); + return 0; + } + + if (num_steps > RELAX_IMMED_MAXSTEPS) + { + as_fatal (_("internal error: relaxation requires too many steps")); + return 0; + } + + fragP->fr_subtype = (int) RELAX_IMMED + num_steps; + + /* Figure out the number of bytes needed. */ + lit_fragP = 0; + num_text_bytes = get_num_stack_text_bytes (&istack) - old_size; + num_literal_bytes = get_num_stack_literal_bytes (&istack); + literal_diff = num_literal_bytes - fragP->tc_frag_data.literal_expansion; + text_diff = num_text_bytes - fragP->tc_frag_data.text_expansion; + + /* It MUST get larger. If not, we could get an infinite loop. */ + know (num_text_bytes >= 0); + know (literal_diff >= 0 && text_diff >= 0); + + fragP->tc_frag_data.text_expansion = num_text_bytes; + fragP->tc_frag_data.literal_expansion = num_literal_bytes; + + /* Find the associated expandable literal for this. */ + if (literal_diff != 0) + { + lit_fragP = fragP->tc_frag_data.literal_frag; + if (lit_fragP) + { + assert (literal_diff == 4); + lit_fragP->tc_frag_data.unreported_expansion += literal_diff; + + /* We expect that the literal section state has NOT been + modified yet. */ + assert (lit_fragP->fr_type == rs_machine_dependent + && lit_fragP->fr_subtype == RELAX_LITERAL); + lit_fragP->fr_subtype = RELAX_LITERAL_NR; + + /* We need to mark this section for another iteration + of relaxation. */ + (*stretched_p)++; + } + } + + /* This implicitly uses the assumption that a branch is negated + when the size of the output increases by at least 2 bytes. */ + + if (negatable_branch && num_text_bytes >= 2) + { + /* If next frag is a loop end, then switch it to add a NOP. */ + update_next_frag_nop_state (fragP); + } + + return text_diff; +} + + +/* md_convert_frag Hook and Helper Functions. */ + +void +md_convert_frag (abfd, sec, fragp) + bfd *abfd ATTRIBUTE_UNUSED; + segT sec; + fragS *fragp; +{ + char *file_name; + int line; + + as_where (&file_name, &line); + new_logical_line (fragp->fr_file, fragp->fr_line); + + switch (fragp->fr_subtype) + { + case RELAX_ALIGN_NEXT_OPCODE: + /* Always convert. */ + convert_frag_align_next_opcode (fragp); + break; + + case RELAX_DESIRE_ALIGN: + /* Do nothing. If not aligned already, too bad. */ + break; + + case RELAX_LITERAL: + case RELAX_LITERAL_FINAL: + break; + + case RELAX_NARROW: + /* No conversion. */ + convert_frag_narrow (fragp); + break; + + case RELAX_IMMED: + case RELAX_IMMED_STEP1: + case RELAX_IMMED_STEP2: + /* Place the immediate. */ + convert_frag_immed (sec, fragp, fragp->fr_subtype - RELAX_IMMED); + break; + + case RELAX_LITERAL_NR: + if (use_literal_section) + { + /* This should have been handled during relaxation. When + relaxing a code segment, literals sometimes need to be + added to the corresponding literal segment. If that + literal segment has already been relaxed, then we end up + in this situation. Marking the literal segments as data + would make this happen less often (since GAS always relaxes + code before data), but we could still get into trouble if + there are instructions in a segment that is not marked as + containing code. Until we can implement a better solution, + cheat and adjust the addresses of all the following frags. + This could break subsequent alignments, but the linker's + literal coalescing will do that anyway. */ + + fragS *f; + fragp->fr_subtype = RELAX_LITERAL_FINAL; + assert (fragp->tc_frag_data.unreported_expansion == 4); + memset (&fragp->fr_literal[fragp->fr_fix], 0, 4); + fragp->fr_var -= 4; + fragp->fr_fix += 4; + for (f = fragp->fr_next; f; f = f->fr_next) + f->fr_address += 4; + } + else + as_bad (_("invalid relaxation fragment result")); + break; + } + + fragp->fr_var = 0; + new_logical_line (file_name, line); +} + + +void +convert_frag_align_next_opcode (fragp) + fragS *fragp; +{ + char *nop_buf; /* Location for Writing. */ + size_t i; + + bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density; + addressT aligned_address; + size_t fill_size, nop_count; + + aligned_address = get_noop_aligned_address (fragp, fragp->fr_address + + fragp->fr_fix); + fill_size = aligned_address - (fragp->fr_address + fragp->fr_fix); + nop_count = get_text_align_nop_count (fill_size, use_no_density); + nop_buf = fragp->fr_literal + fragp->fr_fix; + + for (i = 0; i < nop_count; i++) + { + size_t nop_size; + nop_size = get_text_align_nth_nop_size (fill_size, i, use_no_density); + + assemble_nop (nop_size, nop_buf); + nop_buf += nop_size; + } + + fragp->fr_fix += fill_size; + fragp->fr_var -= fill_size; +} + + +static void +convert_frag_narrow (fragP) + fragS *fragP; +{ + static xtensa_insnbuf insnbuf = NULL; + TInsn t_insn, single_target; + int size, old_size, diff, error_val; + offsetT frag_offset; + + if (fragP->tc_frag_data.text_expansion == 0) + { + /* No conversion. */ + fragP->fr_var = 0; + return; + } + + assert (fragP->fr_opcode != NULL); + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + tinsn_from_chars (&t_insn, fragP->fr_opcode); + tinsn_immed_from_frag (&t_insn, fragP); + + /* Just convert it to a wide form.... */ + size = 0; + old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode); + + tinsn_init (&single_target); + frag_offset = fragP->fr_opcode - fragP->fr_literal; + + error_val = xg_expand_narrow (&single_target, &t_insn); + if (error_val) + as_bad (_("unable to widen instruction")); + + size = xtensa_insn_length (xtensa_default_isa, single_target.opcode); + xg_emit_insn_to_buf (&single_target, fragP->fr_opcode, + fragP, frag_offset, TRUE); + + diff = size - old_size; + assert (diff >= 0); + assert (diff <= fragP->fr_var); + fragP->fr_var -= diff; + fragP->fr_fix += diff; + + /* clean it up */ + fragP->fr_var = 0; +} + + +static void +convert_frag_immed (segP, fragP, min_steps) + segT segP; + fragS *fragP; + int min_steps; +{ + char *immed_instr = fragP->fr_opcode; + static xtensa_insnbuf insnbuf = NULL; + TInsn orig_t_insn; + bfd_boolean expanded = FALSE; + char *fr_opcode = fragP->fr_opcode; + bfd_boolean branch_jmp_to_next = FALSE; + int size; + + assert (fragP->fr_opcode != NULL); + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa); + + tinsn_from_chars (&orig_t_insn, fragP->fr_opcode); + tinsn_immed_from_frag (&orig_t_insn, fragP); + + /* Here is the fun stuff: Get the immediate field from this + instruction. If it fits, we're done. If not, find the next + instruction sequence that fits. */ + + if (software_avoid_b_j_loop_end) + branch_jmp_to_next = is_branch_jmp_to_next (&orig_t_insn, fragP); + + if (branch_jmp_to_next && !next_frag_is_loop_target (fragP)) + { + /* Conversion just inserts a NOP and marks the fix as completed. */ + size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode); + assemble_nop (size, fragP->fr_opcode); + fragP->fr_var = 0; + } + else + { + IStack istack; + int i; + symbolS *lit_sym = NULL; + int total_size = 0; + int old_size; + int diff; + symbolS *gen_label = NULL; + offsetT frag_offset; + + /* It does not fit. Find something that does and + convert immediately. */ + frag_offset = fragP->fr_opcode - fragP->fr_literal; + istack_init (&istack); + xg_assembly_relax (&istack, &orig_t_insn, + segP, fragP, frag_offset, min_steps, 0); + + old_size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode); + + /* Assemble this right inline. */ + + /* First, create the mapping from a label name to the REAL label. */ + total_size = 0; + for (i = 0; i < istack.ninsn; i++) + { + TInsn *t_insn = &istack.insn[i]; + int size = 0; + fragS *lit_frag; + + switch (t_insn->insn_type) + { + case ITYPE_LITERAL: + if (lit_sym != NULL) + as_bad (_("multiple literals in expansion")); + /* First find the appropriate space in the literal pool. */ + lit_frag = fragP->tc_frag_data.literal_frag; + if (lit_frag == NULL) + as_bad (_("no registered fragment for literal")); + if (t_insn->ntok != 1) + as_bad (_("number of literal tokens != 1")); + + /* Set the literal symbol and add a fixup. */ + lit_sym = lit_frag->fr_symbol; + break; + + case ITYPE_LABEL: + assert (gen_label == NULL); + gen_label = symbol_new (FAKE_LABEL_NAME, now_seg, + fragP->fr_opcode - fragP->fr_literal + + total_size, fragP); + break; + + case ITYPE_INSN: + size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode); + total_size += size; + break; + } + } + + total_size = 0; + for (i = 0; i < istack.ninsn; i++) + { + TInsn *t_insn = &istack.insn[i]; + fragS *lit_frag; + int size; + segT target_seg; + + switch (t_insn->insn_type) + { + case ITYPE_LITERAL: + lit_frag = fragP->tc_frag_data.literal_frag; + /* already checked */ + assert (lit_frag != NULL); + assert (lit_sym != NULL); + assert (t_insn->ntok == 1); + /* add a fixup */ + target_seg = S_GET_SEGMENT (lit_sym); + assert (target_seg); + fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4, + &t_insn->tok[0], FALSE, BFD_RELOC_32); + break; + + case ITYPE_LABEL: + break; + + case ITYPE_INSN: + xg_resolve_labels (t_insn, gen_label); + xg_resolve_literals (t_insn, lit_sym); + size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode); + total_size += size; + xg_emit_insn_to_buf (t_insn, immed_instr, fragP, + immed_instr - fragP->fr_literal, TRUE); + immed_instr += size; + break; + } + } + + diff = total_size - old_size; + assert (diff >= 0); + if (diff != 0) + expanded = TRUE; + assert (diff <= fragP->fr_var); + fragP->fr_var -= diff; + fragP->fr_fix += diff; + } + + /* Clean it up. */ + fragP->fr_var = 0; + + /* Check for undefined immediates in LOOP instructions. */ + if (is_loop_opcode (orig_t_insn.opcode)) + { + symbolS *sym; + sym = orig_t_insn.tok[1].X_add_symbol; + if (sym != NULL && !S_IS_DEFINED (sym)) + { + as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym)); + return; + } + sym = orig_t_insn.tok[1].X_op_symbol; + if (sym != NULL && !S_IS_DEFINED (sym)) + { + as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym)); + return; + } + } + + if (expanded && is_loop_opcode (orig_t_insn.opcode)) + convert_frag_immed_finish_loop (segP, fragP, &orig_t_insn); + + if (expanded && is_direct_call_opcode (orig_t_insn.opcode)) + { + /* Add an expansion note on the expanded instruction. */ + fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4, + &orig_t_insn.tok[0], TRUE, + BFD_RELOC_XTENSA_ASM_EXPAND); + + } +} + + +/* Add a new fix expression into the desired segment. We have to + switch to that segment to do this. */ + +static fixS * +fix_new_exp_in_seg (new_seg, new_subseg, + frag, where, size, exp, pcrel, r_type) + segT new_seg; + subsegT new_subseg; + fragS *frag; + int where; + int size; + expressionS *exp; + int pcrel; + bfd_reloc_code_real_type r_type; +{ + fixS *new_fix; + segT seg = now_seg; + subsegT subseg = now_subseg; + assert (new_seg != 0); + subseg_set (new_seg, new_subseg); + + if (r_type == BFD_RELOC_32 + && exp->X_add_symbol + && exp->X_add_symbol->sy_tc.plt == 1) + { + r_type = BFD_RELOC_XTENSA_PLT; + } + + new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type); + subseg_set (seg, subseg); + return new_fix; +} + + +/* Relax a loop instruction so that it can span loop >256 bytes. */ +/* + loop as, .L1 + .L0: + rsr as, LEND + wsr as, LBEG + addi as, as, lo8(label-.L1) + addmi as, as, mid8(label-.L1) + wsr as, LEND + isync + rsr as, LCOUNT + addi as, as, 1 + .L1: + <> + label: */ + +static void +convert_frag_immed_finish_loop (segP, fragP, t_insn) + segT segP; + fragS *fragP; + TInsn *t_insn; +{ + TInsn loop_insn; + TInsn addi_insn; + TInsn addmi_insn; + unsigned long target; + static xtensa_insnbuf insnbuf = NULL; + unsigned int loop_length, loop_length_hi, loop_length_lo; + xtensa_isa isa = xtensa_default_isa; + addressT loop_offset; + addressT addi_offset = 9; + addressT addmi_offset = 12; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + /* Get the loop offset. */ + loop_offset = get_expanded_loop_offset (t_insn->opcode); + /* Validate that there really is a LOOP at the loop_offset. */ + tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset); + + if (!is_loop_opcode (loop_insn.opcode)) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _("loop relaxation specification does not correspond")); + assert (0); + } + addi_offset += loop_offset; + addmi_offset += loop_offset; + + assert (t_insn->ntok == 2); + target = get_expression_value (segP, &t_insn->tok[1]); + + know (symbolP); + know (symbolP->sy_frag); + know (!(S_GET_SEGMENT (symbolP) == absolute_section) + || symbol_get_frag (symbolP) == &zero_address_frag); + + loop_length = target - (fragP->fr_address + fragP->fr_fix); + loop_length_hi = loop_length & ~0x0ff; + loop_length_lo = loop_length & 0x0ff; + if (loop_length_lo >= 128) + { + loop_length_lo -= 256; + loop_length_hi += 256; + } + + /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most + 32512. If the loop is larger than that, then we just fail. */ + if (loop_length_hi > 32512) + as_bad_where (fragP->fr_file, fragP->fr_line, + _("loop too long for LOOP instruction")); + + tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset); + assert (addi_insn.opcode == xtensa_addi_opcode); + + tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset); + assert (addmi_insn.opcode == xtensa_addmi_opcode); + + set_expr_const (&addi_insn.tok[2], loop_length_lo); + tinsn_to_insnbuf (&addi_insn, insnbuf); + + fragP->tc_frag_data.is_insn = TRUE; + xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addi_offset); + + set_expr_const (&addmi_insn.tok[2], loop_length_hi); + tinsn_to_insnbuf (&addmi_insn, insnbuf); + xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addmi_offset); +} + + +static offsetT +get_expression_value (segP, exp) + segT segP; + expressionS *exp; +{ + if (exp->X_op == O_constant) + return exp->X_add_number; + if (exp->X_op == O_symbol) + { + /* Find the fragment. */ + symbolS *sym = exp->X_add_symbol; + + assert (S_GET_SEGMENT (sym) == segP + || S_GET_SEGMENT (sym) == absolute_section); + + return (S_GET_VALUE (sym) + exp->X_add_number); + } + as_bad (_("invalid expression evaluation type %d"), exp->X_op); + return 0; +} + + +/* A map that keeps information on a per-subsegment basis. This is + maintained during initial assembly, but is invalid once the + subsegments are smashed together. I.E., it cannot be used during + the relaxation. */ + +typedef struct subseg_map_struct +{ + /* the key */ + segT seg; + subsegT subseg; + + /* the data */ + unsigned flags; + + struct subseg_map_struct *next; +} subseg_map; + +static subseg_map *sseg_map = NULL; + + +static unsigned +get_last_insn_flags (seg, subseg) + segT seg; + subsegT subseg; +{ + subseg_map *subseg_e; + + for (subseg_e = sseg_map; subseg_e != NULL; subseg_e = subseg_e->next) + if (seg == subseg_e->seg && subseg == subseg_e->subseg) + return subseg_e->flags; + + return 0; +} + + +static void +set_last_insn_flags (seg, subseg, fl, val) + segT seg; + subsegT subseg; + unsigned fl; + bfd_boolean val; +{ + subseg_map *subseg_e; + + for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next) + if (seg == subseg_e->seg && subseg == subseg_e->subseg) + break; + + if (!subseg_e) + { + subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map)); + memset (subseg_e, 0, sizeof (subseg_map)); + subseg_e->seg = seg; + subseg_e->subseg = subseg; + subseg_e->flags = 0; + subseg_e->next = sseg_map; + sseg_map = subseg_e; + } + + if (val) + subseg_e->flags |= fl; + else + subseg_e->flags &= ~fl; +} + + +/* Segment Lists and emit_state Stuff. */ + +/* Remove the segment from the global sections list. */ + +static void +xtensa_remove_section (sec) + segT sec; +{ + /* Handle brain-dead bfd_section_list_remove macro, which + expect the address of the prior section's "next" field, not + just the address of the section to remove. */ + + segT *ps_next_ptr = &stdoutput->sections; + while (*ps_next_ptr != sec && *ps_next_ptr != NULL) + ps_next_ptr = &(*ps_next_ptr)->next; + + assert (*ps_next_ptr != NULL); + + bfd_section_list_remove (stdoutput, ps_next_ptr); +} + + +static void +xtensa_insert_section (after_sec, sec) + segT after_sec; + segT sec; +{ + segT *after_sec_next; + if (after_sec == NULL) + after_sec_next = &stdoutput->sections; + else + after_sec_next = &after_sec->next; + + bfd_section_list_insert (stdoutput, after_sec_next, sec); +} + + +static void +xtensa_move_seg_list_to_beginning (head) + seg_list *head; +{ + head = head->next; + while (head) + { + segT literal_section = head->seg; + + /* Move the literal section to the front of the section list. */ + assert (literal_section); + xtensa_remove_section (literal_section); + xtensa_insert_section (NULL, literal_section); + + head = head->next; + } +} + + +void +xtensa_move_literals () +{ + seg_list *segment; + frchainS *frchain_from, *frchain_to; + fragS *search_frag, *next_frag, *last_frag, *literal_pool, *insert_after; + fragS **frag_splice; + emit_state state; + segT dest_seg; + fixS *fix, *next_fix, **fix_splice; + + /* As clunky as this is, we can't rely on frag_var + and frag_variant to get called in all situations. */ + + segment = literal_head->next; + while (segment) + { + frchain_from = seg_info (segment->seg)->frchainP; + search_frag = frchain_from->frch_root; + while (search_frag) + { + search_frag->tc_frag_data.is_literal = TRUE; + search_frag = search_frag->fr_next; + } + segment = segment->next; + } + + if (use_literal_section) + return; + + segment = literal_head->next; + while (segment) + { + frchain_from = seg_info (segment->seg)->frchainP; + search_frag = frchain_from->frch_root; + literal_pool = NULL; + frchain_to = NULL; + frag_splice = &(frchain_from->frch_root); + + while (!search_frag->tc_frag_data.literal_frag) + { + assert (search_frag->fr_fix == 0 + || search_frag->fr_type == rs_align); + search_frag = search_frag->fr_next; + } + + assert (search_frag->tc_frag_data.literal_frag->fr_subtype + == RELAX_LITERAL_POOL_BEGIN); + xtensa_switch_section_emit_state (&state, segment->seg, 0); + + /* Make sure that all the frags in this series are closed, and + that there is at least one left over of zero-size. This + prevents us from making a segment with an frchain without any + frags in it. */ + frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL); + last_frag = frag_now; + frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL); + + while (search_frag != frag_now) + { + next_frag = search_frag->fr_next; + + /* First, move the frag out of the literal section and + to the appropriate place. */ + if (search_frag->tc_frag_data.literal_frag) + { + literal_pool = search_frag->tc_frag_data.literal_frag; + assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN); + /* Note that we set this fr_var to be a fix + chain when we created the literal pool location + as RELAX_LITERAL_POOL_BEGIN. */ + frchain_to = (frchainS *) literal_pool->fr_var; + } + insert_after = literal_pool; + + while (insert_after->fr_next->fr_subtype != RELAX_LITERAL_POOL_END) + insert_after = insert_after->fr_next; + + dest_seg = (segT) insert_after->fr_next->fr_var; + + *frag_splice = next_frag; + search_frag->fr_next = insert_after->fr_next; + insert_after->fr_next = search_frag; + search_frag->tc_frag_data.lit_seg = dest_seg; + + /* Now move any fixups associated with this frag to the + right section. */ + fix = frchain_from->fix_root; + fix_splice = &(frchain_from->fix_root); + while (fix) + { + next_fix = fix->fx_next; + if (fix->fx_frag == search_frag) + { + *fix_splice = next_fix; + fix->fx_next = frchain_to->fix_root; + frchain_to->fix_root = fix; + if (frchain_to->fix_tail == NULL) + frchain_to->fix_tail = fix; + } + else + fix_splice = &(fix->fx_next); + fix = next_fix; + } + search_frag = next_frag; + } + + if (frchain_from->fix_root != NULL) + { + frchain_from = seg_info (segment->seg)->frchainP; + as_warn (_("fixes not all moved from %s"), segment->seg->name); + + assert (frchain_from->fix_root == NULL); + } + frchain_from->fix_tail = NULL; + xtensa_restore_emit_state (&state); + segment = segment->next; + } + + xtensa_move_frag_symbols (); +} + + +static void +xtensa_move_frag_symbol (sym) + symbolS *sym; +{ + fragS *frag = symbol_get_frag (sym); + + if (frag->tc_frag_data.lit_seg != (segT) 0) + S_SET_SEGMENT (sym, frag->tc_frag_data.lit_seg); +} + + +static void +xtensa_move_frag_symbols () +{ + symbolS *symbolP; + + /* Although you might think that only one of these lists should be + searched, it turns out that the difference of the two sets + (either way) is not empty. They do overlap quite a bit, + however. */ + + for (symbolP = symbol_rootP; symbolP; symbolP = symbolP->sy_next) + xtensa_move_frag_symbol (symbolP); + + map_over_defined_symbols (xtensa_move_frag_symbol); +} + + +static void +xtensa_reorder_seg_list (head, after) + seg_list *head; + segT after; +{ + /* Move all of the sections in the section list to come + after "after" in the gnu segment list. */ + + head = head->next; + while (head) + { + segT literal_section = head->seg; + + /* Move the literal section after "after". */ + assert (literal_section); + if (literal_section != after) + { + xtensa_remove_section (literal_section); + xtensa_insert_section (after, literal_section); + } + + head = head->next; + } +} + + +/* Push all the literal segments to the end of the gnu list. */ + +void +xtensa_reorder_segments () +{ + segT sec; + segT last_sec; + int old_count = 0; + int new_count = 0; + + for (sec = stdoutput->sections; sec != NULL; sec = sec->next) + old_count++; + + /* Now that we have the last section, push all the literal + sections to the end. */ + last_sec = get_last_sec (); + xtensa_reorder_seg_list (literal_head, last_sec); + xtensa_reorder_seg_list (init_literal_head, last_sec); + xtensa_reorder_seg_list (fini_literal_head, last_sec); + + /* Now perform the final error check. */ + for (sec = stdoutput->sections; sec != NULL; sec = sec->next) + new_count++; + assert (new_count == old_count); +} + + +segT +get_last_sec () +{ + segT last_sec = stdoutput->sections; + while (last_sec->next != NULL) + last_sec = last_sec->next; + + return last_sec; +} + + +/* Change the emit state (seg, subseg, and frag related stuff) to the + correct location. Return a emit_state which can be passed to + xtensa_restore_emit_state to return to current fragment. */ + +void +xtensa_switch_to_literal_fragment (result) + emit_state *result; +{ + /* When we mark a literal pool location, we want to put a frag in + the literal pool that points to it. But to do that, we want to + switch_to_literal_fragment. But literal sections don't have + literal pools, so their location is always null, so we would + recurse forever. This is kind of hacky, but it works. */ + + static bfd_boolean recursive = FALSE; + fragS *pool_location = get_literal_pool_location (now_seg); + bfd_boolean is_init = + (now_seg && !strcmp (segment_name (now_seg), INIT_SECTION_NAME)); + + bfd_boolean is_fini = + (now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME)); + + + if (pool_location == NULL + && !use_literal_section + && !recursive + && !is_init && ! is_fini) + { + as_warn (_("inlining literal pool; " + "specify location with .literal_position.")); + recursive = TRUE; + xtensa_mark_literal_pool_location (FALSE); + recursive = FALSE; + } + + /* Special case: If we are in the ".fini" or ".init" section, then + we will ALWAYS be generating to the ".fini.literal" and + ".init.literal" sections. */ + + if (is_init) + { + cache_literal_section (init_literal_head, + default_lit_sections.init_lit_seg_name, + &default_lit_sections.init_lit_seg); + xtensa_switch_section_emit_state (result, + default_lit_sections.init_lit_seg, 0); + } + else if (is_fini) + { + cache_literal_section (fini_literal_head, + default_lit_sections.fini_lit_seg_name, + &default_lit_sections.fini_lit_seg); + xtensa_switch_section_emit_state (result, + default_lit_sections.fini_lit_seg, 0); + } + else + { + cache_literal_section (literal_head, + default_lit_sections.lit_seg_name, + &default_lit_sections.lit_seg); + xtensa_switch_section_emit_state (result, + default_lit_sections.lit_seg, 0); + } + + if (!use_literal_section && + !is_init && !is_fini && + get_literal_pool_location (now_seg) != pool_location) + { + /* Close whatever frag is there. */ + frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL); + frag_now->tc_frag_data.literal_frag = pool_location; + frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL); + } + + /* Do a 4 byte align here. */ + frag_align (2, 0, 0); +} + + +/* Call this function before emitting data into the literal section. + This is a helper function for xtensa_switch_to_literal_fragment. + This is similar to a .section new_now_seg subseg. */ + +void +xtensa_switch_section_emit_state (state, new_now_seg, new_now_subseg) + emit_state *state; + segT new_now_seg; + subsegT new_now_subseg; +{ + state->name = now_seg->name; + state->now_seg = now_seg; + state->now_subseg = now_subseg; + state->generating_literals = generating_literals; + generating_literals++; + subseg_new (segment_name (new_now_seg), new_now_subseg); +} + + +/* Use to restore the emitting into the normal place. */ + +void +xtensa_restore_emit_state (state) + emit_state *state; +{ + generating_literals = state->generating_literals; + subseg_new (state->name, state->now_subseg); +} + + +/* Get a segment of a given name. If the segment is already + present, return it; otherwise, create a new one. */ + +static void +cache_literal_section (head, name, seg) + seg_list *head; + const char *name; + segT *seg; +{ + segT current_section = now_seg; + int current_subsec = now_subseg; + + if (*seg != 0) + return; + *seg = retrieve_literal_seg (head, name); + subseg_set (current_section, current_subsec); +} + + +/* Get a segment of a given name. If the segment is already + present, return it; otherwise, create a new one. */ + +static segT +retrieve_literal_seg (head, name) + seg_list *head; + const char *name; +{ + segT ret = 0; + + assert (head); + + ret = seg_present (name); + if (!ret) + { + ret = subseg_new (name, (subsegT) 0); + add_seg_list (head, ret); + bfd_set_section_flags (stdoutput, ret, SEC_HAS_CONTENTS | + SEC_READONLY | SEC_ALLOC | SEC_LOAD | SEC_CODE); + bfd_set_section_alignment (stdoutput, ret, 2); + } + + return ret; +} + + +/* Return a segment of a given name if it is present. */ + +static segT +seg_present (name) + const char *name; +{ + segT seg; + seg = stdoutput->sections; + + while (seg) + { + if (!strcmp (segment_name (seg), name)) + return seg; + seg = seg->next; + } + + return 0; +} + + +/* Add a segment to a segment list. */ + +static void +add_seg_list (head, seg) + seg_list *head; + segT seg; +{ + seg_list *n; + n = (seg_list *) xmalloc (sizeof (seg_list)); + assert (n); + + n->seg = seg; + n->next = head->next; + head->next = n; +} + + +/* Set up Property Tables after Relaxation. */ + +#define XTENSA_INSN_SEC_NAME ".xt.insn" +#define XTENSA_LIT_SEC_NAME ".xt.lit" + +void +xtensa_post_relax_hook () +{ + xtensa_move_seg_list_to_beginning (literal_head); + xtensa_move_seg_list_to_beginning (init_literal_head); + xtensa_move_seg_list_to_beginning (fini_literal_head); + + xtensa_create_property_segments (get_frag_is_insn, + XTENSA_INSN_SEC_NAME, + xt_literal_sec); + if (use_literal_section) + xtensa_create_property_segments (get_frag_is_literal, + XTENSA_LIT_SEC_NAME, + xt_insn_sec); +} + + +static bfd_boolean +get_frag_is_literal (fragP) + const fragS *fragP; +{ + assert (fragP != NULL); + return (fragP->tc_frag_data.is_literal); +} + + +static bfd_boolean +get_frag_is_insn (fragP) + const fragS *fragP; +{ + assert (fragP != NULL); + return (fragP->tc_frag_data.is_insn); +} + + +static void +xtensa_create_property_segments (property_function, section_name_base, + sec_type) + frag_predicate property_function; + const char * section_name_base; + xt_section_type sec_type; +{ + segT *seclist; + + /* Walk over all of the current segments. + Walk over each fragment + For each fragment that has instructions + Build an instruction record (append where possible). */ + + for (seclist = &stdoutput->sections; + seclist && *seclist; + seclist = &(*seclist)->next) + { + segT sec = *seclist; + if (section_has_property (sec, property_function)) + { + char * property_section_name = + xtensa_get_property_section_name (stdoutput, sec, + section_name_base); + segT insn_sec = retrieve_xtensa_section (property_section_name); + segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec); + xtensa_block_info ** xt_blocks = + &xt_seg_info->tc_segment_info_data.blocks[sec_type]; + /* Walk over all of the frchains here and add new sections. */ + add_xt_block_frags (sec, insn_sec, xt_blocks, property_function); + } + } + + /* Now we fill them out.... */ + + for (seclist = &stdoutput->sections; + seclist && *seclist; + seclist = &(*seclist)->next) + { + segment_info_type *seginfo; + xtensa_block_info *block; + segT sec = *seclist; + seginfo = seg_info (sec); + block = seginfo->tc_segment_info_data.blocks[sec_type]; + + if (block) + { + xtensa_block_info *cur_block; + /* This is a section with some data. */ + size_t num_recs = 0; + size_t rec_size; + + for (cur_block = block; cur_block; cur_block = cur_block->next) + num_recs++; + + rec_size = num_recs * 8; + bfd_set_section_size (stdoutput, sec, rec_size); + + /* In order to make this work with the assembler, we have to + build some frags and then build the "fixups" for it. It + would be easier to just set the contents then set the + arlents. */ + + if (num_recs) + { + /* Allocate a fragment and leak it. */ + fragS *fragP; + size_t frag_size; + fixS *fixes; + frchainS *frchainP; + size_t i; + char *frag_data; + + frag_size = sizeof (fragS) + rec_size; + fragP = (fragS *) xmalloc (frag_size); + + memset (fragP, 0, frag_size); + fragP->fr_address = 0; + fragP->fr_next = NULL; + fragP->fr_fix = rec_size; + fragP->fr_var = 0; + fragP->fr_type = rs_fill; + /* the rest are zeros */ + + frchainP = seginfo->frchainP; + frchainP->frch_root = fragP; + frchainP->frch_last = fragP; + + fixes = (fixS *) xmalloc (sizeof (fixS) * num_recs); + memset (fixes, 0, sizeof (fixS) * num_recs); + + seginfo->fix_root = fixes; + seginfo->fix_tail = &fixes[num_recs - 1]; + cur_block = block; + frag_data = &fragP->fr_literal[0]; + for (i = 0; i < num_recs; i++) + { + fixS *fix = &fixes[i]; + assert (cur_block); + + /* Write the fixup. */ + if (i != num_recs - 1) + fix->fx_next = &fixes[i + 1]; + else + fix->fx_next = NULL; + fix->fx_size = 4; + fix->fx_done = 0; + fix->fx_frag = fragP; + fix->fx_where = i * 8; + fix->fx_addsy = section_symbol (cur_block->sec); + fix->fx_offset = cur_block->offset; + fix->fx_r_type = BFD_RELOC_32; + fix->fx_file = "Internal Assembly"; + fix->fx_line = 0; + + /* Write the length. */ + md_number_to_chars (&frag_data[4 + 8 * i], + cur_block->size, 4); + cur_block = cur_block->next; + } + } + } + } +} + + +segment_info_type * +retrieve_segment_info (seg) + segT seg; +{ + segment_info_type *seginfo; + seginfo = (segment_info_type *) bfd_get_section_userdata (stdoutput, seg); + if (!seginfo) + { + frchainS *frchainP; + + seginfo = (segment_info_type *) xmalloc (sizeof (*seginfo)); + memset ((PTR) seginfo, 0, sizeof (*seginfo)); + seginfo->fix_root = NULL; + seginfo->fix_tail = NULL; + seginfo->bfd_section = seg; + seginfo->sym = 0; + /* We will not be dealing with these, only our special ones. */ +#if 0 + if (seg == bfd_abs_section_ptr) + abs_seg_info = seginfo; + else if (seg == bfd_und_section_ptr) + und_seg_info = seginfo; + else +#endif + bfd_set_section_userdata (stdoutput, seg, (PTR) seginfo); +#if 0 + seg_fix_rootP = &segment_info[seg].fix_root; + seg_fix_tailP = &segment_info[seg].fix_tail; +#endif + + frchainP = (frchainS *) xmalloc (sizeof (frchainS)); + frchainP->frch_root = NULL; + frchainP->frch_last = NULL; + frchainP->frch_next = NULL; + frchainP->frch_seg = seg; + frchainP->frch_subseg = 0; + frchainP->fix_root = NULL; + frchainP->fix_tail = NULL; + /* Do not init the objstack. */ + /* obstack_begin (&frchainP->frch_obstack, chunksize); */ + /* frchainP->frch_frag_now = fragP; */ + frchainP->frch_frag_now = NULL; + + seginfo->frchainP = frchainP; + } + + return seginfo; +} + + +segT +retrieve_xtensa_section (sec_name) + char *sec_name; +{ + bfd *abfd = stdoutput; + flagword flags, out_flags, link_once_flags; + segT s; + + flags = bfd_get_section_flags (abfd, now_seg); + link_once_flags = (flags & SEC_LINK_ONCE); + if (link_once_flags) + link_once_flags |= (flags & SEC_LINK_DUPLICATES); + out_flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY | link_once_flags); + + s = bfd_make_section_old_way (abfd, sec_name); + if (s == NULL) + as_bad (_("could not create section %s"), sec_name); + if (!bfd_set_section_flags (abfd, s, out_flags)) + as_bad (_("invalid flag combination on section %s"), sec_name); + + return s; +} + + +bfd_boolean +section_has_property (sec, property_function) + segT sec; + frag_predicate property_function; +{ + segment_info_type *seginfo = seg_info (sec); + fragS *fragP; + + if (seginfo && seginfo->frchainP) + { + for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next) + { + if (property_function (fragP) + && (fragP->fr_type != rs_fill || fragP->fr_fix != 0)) + return TRUE; + } + } + return FALSE; +} + + +/* Two types of block sections exist right now: literal and insns. */ + +void +add_xt_block_frags (sec, xt_block_sec, xt_block, property_function) + segT sec; + segT xt_block_sec; + xtensa_block_info **xt_block; + frag_predicate property_function; +{ + segment_info_type *seg_info; + segment_info_type *xt_seg_info; + bfd_vma seg_offset; + fragS *fragP; + + xt_seg_info = retrieve_segment_info (xt_block_sec); + seg_info = retrieve_segment_info (sec); + + /* Build it if needed. */ + while (*xt_block != NULL) + xt_block = &(*xt_block)->next; + /* We are either at NULL at the beginning or at the end. */ + + /* Walk through the frags. */ + seg_offset = 0; + + if (seg_info->frchainP) + { + for (fragP = seg_info->frchainP->frch_root; + fragP; + fragP = fragP->fr_next) + { + if (property_function (fragP) + && (fragP->fr_type != rs_fill || fragP->fr_fix != 0)) + { + if (*xt_block != NULL) + { + if ((*xt_block)->offset + (*xt_block)->size + == fragP->fr_address) + (*xt_block)->size += fragP->fr_fix; + else + xt_block = &((*xt_block)->next); + } + if (*xt_block == NULL) + { + xtensa_block_info *new_block = (xtensa_block_info *) + xmalloc (sizeof (xtensa_block_info)); + new_block->sec = sec; + new_block->offset = fragP->fr_address; + new_block->size = fragP->fr_fix; + new_block->next = NULL; + *xt_block = new_block; + } + } + } + } +} + + +/* Instruction Stack Functions (from "xtensa-istack.h"). */ + +void +istack_init (stack) + IStack *stack; +{ + memset (stack, 0, sizeof (IStack)); + stack->ninsn = 0; +} + + +bfd_boolean +istack_empty (stack) + IStack *stack; +{ + return (stack->ninsn == 0); +} + + +bfd_boolean +istack_full (stack) + IStack *stack; +{ + return (stack->ninsn == MAX_ISTACK); +} + + +/* Return a pointer to the top IStack entry. + It is an error to call this if istack_empty () is true. */ + +TInsn * +istack_top (stack) + IStack *stack; +{ + int rec = stack->ninsn - 1; + assert (!istack_empty (stack)); + return &stack->insn[rec]; +} + + +/* Add a new TInsn to an IStack. + It is an error to call this if istack_full () is true. */ + +void +istack_push (stack, insn) + IStack *stack; + TInsn *insn; +{ + int rec = stack->ninsn; + assert (!istack_full (stack)); + tinsn_copy (&stack->insn[rec], insn); + stack->ninsn++; +} + + +/* Clear space for the next TInsn on the IStack and return a pointer + to it. It is an error to call this if istack_full () is true. */ + +TInsn * +istack_push_space (stack) + IStack *stack; +{ + int rec = stack->ninsn; + TInsn *insn; + assert (!istack_full (stack)); + insn = &stack->insn[rec]; + memset (insn, 0, sizeof (TInsn)); + stack->ninsn++; + return insn; +} + + +/* Remove the last pushed instruction. It is an error to call this if + istack_empty () returns true. */ + +void +istack_pop (stack) + IStack *stack; +{ + int rec = stack->ninsn - 1; + assert (!istack_empty (stack)); + stack->ninsn--; + memset (&stack->insn[rec], 0, sizeof (TInsn)); +} + + +/* TInsn functions. */ + +void +tinsn_init (dst) + TInsn *dst; +{ + memset (dst, 0, sizeof (TInsn)); +} + + +void +tinsn_copy (dst, src) + TInsn *dst; + const TInsn *src; +{ + tinsn_init (dst); + memcpy (dst, src, sizeof (TInsn)); +} + + +/* Get the ``num''th token of the TInsn. + It is illegal to call this if num > insn->ntoks. */ + +expressionS * +tinsn_get_tok (insn, num) + TInsn *insn; + int num; +{ + assert (num < insn->ntok); + return &insn->tok[num]; +} + + +/* Return true if ANY of the operands in the insn are symbolic. */ + +static bfd_boolean +tinsn_has_symbolic_operands (insn) + const TInsn *insn; +{ + int i; + int n = insn->ntok; + + assert (insn->insn_type == ITYPE_INSN); + + for (i = 0; i < n; ++i) + { + switch (insn->tok[i].X_op) + { + case O_register: + case O_constant: + break; + default: + return TRUE; + } + } + return FALSE; +} + + +bfd_boolean +tinsn_has_invalid_symbolic_operands (insn) + const TInsn *insn; +{ + int i; + int n = insn->ntok; + + assert (insn->insn_type == ITYPE_INSN); + + for (i = 0; i < n; ++i) + { + switch (insn->tok[i].X_op) + { + case O_register: + case O_constant: + break; + default: + if (i == get_relaxable_immed (insn->opcode)) + break; + as_bad (_("invalid symbolic operand %d on '%s'"), + i, xtensa_opcode_name (xtensa_default_isa, insn->opcode)); + return TRUE; + } + } + return FALSE; +} + + +/* For assembly code with complex expressions (e.g. subtraction), + we have to build them in the literal pool so that + their results are calculated correctly after relaxation. + The relaxation only handles expressions that + boil down to SYMBOL + OFFSET. */ + +static bfd_boolean +tinsn_has_complex_operands (insn) + const TInsn *insn; +{ + int i; + int n = insn->ntok; + assert (insn->insn_type == ITYPE_INSN); + for (i = 0; i < n; ++i) + { + switch (insn->tok[i].X_op) + { + case O_register: + case O_constant: + case O_symbol: + break; + default: + return TRUE; + } + } + return FALSE; +} + + +/* Convert the constant operands in the t_insn to insnbuf. + Return true if there is a symbol in the immediate field. + + Before this is called, + 1) the number of operands are correct + 2) the t_insn is a ITYPE_INSN + 3) ONLY the relaxable_ is built + 4) All operands are O_constant, O_symbol. All constants fit + The return value tells whether there are any remaining O_symbols. */ + +static bfd_boolean +tinsn_to_insnbuf (t_insn, insnbuf) + TInsn *t_insn; + xtensa_insnbuf insnbuf; +{ + xtensa_isa isa = xtensa_default_isa; + xtensa_opcode opcode = t_insn->opcode; + bfd_boolean has_fixup = FALSE; + int noperands = xtensa_num_operands (isa, opcode); + int i; + uint32 opnd_value; + char *file_name; + int line; + + assert (t_insn->insn_type == ITYPE_INSN); + if (noperands != t_insn->ntok) + as_fatal (_("operand number mismatch")); + + xtensa_encode_insn (isa, opcode, insnbuf); + + for (i = 0; i < noperands; ++i) + { + expressionS *expr = &t_insn->tok[i]; + xtensa_operand operand = xtensa_get_operand (isa, opcode, i); + switch (expr->X_op) + { + case O_register: + /* The register number has already been checked in + expression_maybe_register, so we don't need to check here. */ + opnd_value = expr->X_add_number; + (void) xtensa_operand_encode (operand, &opnd_value); + xtensa_operand_set_field (operand, insnbuf, opnd_value); + break; + + case O_constant: + as_where (&file_name, &line); + /* It is a constant and we called this function, + then we have to try to fit it. */ + xtensa_insnbuf_set_operand (insnbuf, opcode, operand, + expr->X_add_number, file_name, line); + break; + + case O_symbol: + default: + has_fixup = TRUE; + break; + } + } + return has_fixup; +} + + +/* Check the instruction arguments. Return true on failure. */ + +bfd_boolean +tinsn_check_arguments (insn) + const TInsn *insn; +{ + xtensa_isa isa = xtensa_default_isa; + xtensa_opcode opcode = insn->opcode; + + if (opcode == XTENSA_UNDEFINED) + { + as_bad (_("invalid opcode")); + return TRUE; + } + + if (xtensa_num_operands (isa, opcode) > insn->ntok) + { + as_bad (_("too few operands")); + return TRUE; + } + + if (xtensa_num_operands (isa, opcode) < insn->ntok) + { + as_bad (_("too many operands")); + return TRUE; + } + return FALSE; +} + + +/* Load an instruction from its encoded form. */ + +static void +tinsn_from_chars (t_insn, f) + TInsn *t_insn; + char *f; +{ + static xtensa_insnbuf insnbuf = NULL; + int i; + xtensa_opcode opcode; + xtensa_isa isa = xtensa_default_isa; + + if (!insnbuf) + insnbuf = xtensa_insnbuf_alloc (isa); + + xtensa_insnbuf_from_chars (isa, insnbuf, f); + opcode = xtensa_decode_insn (isa, insnbuf); + + /* Find the immed. */ + tinsn_init (t_insn); + t_insn->insn_type = ITYPE_INSN; + t_insn->is_specific_opcode = FALSE; /* Must not be specific. */ + t_insn->opcode = opcode; + t_insn->ntok = xtensa_num_operands (isa, opcode); + for (i = 0; i < t_insn->ntok; i++) + { + set_expr_const (&t_insn->tok[i], + xtensa_insnbuf_get_operand (insnbuf, opcode, i)); + } +} + + +/* Read the value of the relaxable immed from the fr_symbol and fr_offset. */ + +static void +tinsn_immed_from_frag (t_insn, fragP) + TInsn *t_insn; + fragS *fragP; +{ + xtensa_opcode opcode = t_insn->opcode; + int opnum; + + if (fragP->fr_symbol) + { + opnum = get_relaxable_immed (opcode); + set_expr_symbol_offset (&t_insn->tok[opnum], + fragP->fr_symbol, fragP->fr_offset); + } +} + + +static int +get_num_stack_text_bytes (istack) + IStack *istack; +{ + int i; + int text_bytes = 0; + + for (i = 0; i < istack->ninsn; i++) + { + TInsn *t_insn = &istack->insn[i]; + if (t_insn->insn_type == ITYPE_INSN) + text_bytes += xg_get_insn_size (t_insn); + } + return text_bytes; +} + + +static int +get_num_stack_literal_bytes (istack) + IStack *istack; +{ + int i; + int lit_bytes = 0; + + for (i = 0; i < istack->ninsn; i++) + { + TInsn *t_insn = &istack->insn[i]; + + if (t_insn->insn_type == ITYPE_LITERAL && t_insn->ntok == 1) + lit_bytes += 4; + } + return lit_bytes; +} + + +/* Expression utilities. */ + +/* Return true if the expression is an integer constant. */ + +bfd_boolean +expr_is_const (s) + const expressionS *s; +{ + return (s->X_op == O_constant); +} + + +/* Get the expression constant. + Calling this is illegal if expr_is_const () returns true. */ + +offsetT +get_expr_const (s) + const expressionS *s; +{ + assert (expr_is_const (s)); + return s->X_add_number; +} + + +/* Set the expression to a constant value. */ + +void +set_expr_const (s, val) + expressionS *s; + offsetT val; +{ + s->X_op = O_constant; + s->X_add_number = val; + s->X_add_symbol = NULL; + s->X_op_symbol = NULL; +} + + +/* Set the expression to a symbol + constant offset. */ + +void +set_expr_symbol_offset (s, sym, offset) + expressionS *s; + symbolS *sym; + offsetT offset; +{ + s->X_op = O_symbol; + s->X_add_symbol = sym; + s->X_op_symbol = NULL; /* unused */ + s->X_add_number = offset; +} + + +bfd_boolean +expr_is_equal (s1, s2) + expressionS *s1; + expressionS *s2; +{ + if (s1->X_op != s2->X_op) + return FALSE; + if (s1->X_add_symbol != s2->X_add_symbol) + return FALSE; + if (s1->X_op_symbol != s2->X_op_symbol) + return FALSE; + if (s1->X_add_number != s2->X_add_number) + return FALSE; + return TRUE; +} + + +static void +copy_expr (dst, src) + expressionS *dst; + const expressionS *src; +{ + memcpy (dst, src, sizeof (expressionS)); +} + + +/* Support for Tensilica's "--rename-section" option. */ + +#ifdef XTENSA_SECTION_RENAME + +struct rename_section_struct +{ + char *old_name; + char *new_name; + struct rename_section_struct *next; +}; + +static struct rename_section_struct *section_rename; + + +/* Parse the string oldname=new_name:oldname2=new_name2 + and call add_section_rename. */ + +void +build_section_rename (arg) + const char *arg; +{ + char *this_arg = NULL; + char *next_arg = NULL; + + for (this_arg = strdup (arg); this_arg != NULL; this_arg = next_arg) + { + if (this_arg) + { + next_arg = strchr (this_arg, ':'); + if (next_arg) + { + *next_arg = '\0'; + next_arg++; + } + } + { + char *old_name = this_arg; + char *new_name = strchr (this_arg, '='); + + if (*old_name == '\0') + { + as_warn (_("ignoring extra '-rename-section' delimiter ':'")); + continue; + } + if (!new_name || new_name[1] == '\0') + { + as_warn (_("ignoring invalid '-rename-section' " + "specification: '%s'"), old_name); + continue; + } + *new_name = '\0'; + new_name++; + add_section_rename (old_name, new_name); + } + } +} + + +static void +add_section_rename (old_name, new_name) + char *old_name; + char *new_name; +{ + struct rename_section_struct *r = section_rename; + + /* Check for invalid section renaming. */ + for (r = section_rename; r != NULL; r = r->next) + { + if (strcmp (r->old_name, old_name) == 0) + as_bad (_("section %s renamed multiple times"), old_name); + if (strcmp (r->new_name, new_name) == 0) + as_bad (_("multiple sections remapped to output section %s"), + new_name); + } + + /* Now add it. */ + r = (struct rename_section_struct *) + xmalloc (sizeof (struct rename_section_struct)); + r->old_name = strdup (old_name); + r->new_name = strdup (new_name); + r->next = section_rename; + section_rename = r; +} + + +const char * +xtensa_section_rename (name) + const char *name; +{ + struct rename_section_struct *r = section_rename; + + for (r = section_rename; r != NULL; r = r->next) + if (strcmp (r->old_name, name) == 0) + return r->new_name; + + return name; +} + +#endif /* XTENSA_SECTION_RENAME */ + + +/* Combining identical literals. */ + +#ifdef XTENSA_COMBINE_LITERALS + +/* This code records all the .literal values that are ever seen and + detects duplicates so that identical values can be combined. This + is currently disabled because it's only half-baked. */ + +#define XTENSA_LIT_PLUS_OFFSET ".xtensa_litsym_offset_" + +/* TODO: make this into a more efficient data structure. */ +typedef struct literal_list_elem +{ + symbolS *sym; /* The symbol that points to this literal. */ + expressionS expr; /* The expression. */ + segT seg; + struct literal_list_elem *next; /* Next in the list. */ +} literal_list_elem; + +literal_list_elem *lit_cache = NULL; + +typedef struct lit_sym_translation +{ + char *name; /* This name. */ + offsetT offset; /* Plus this offset. */ + symbolS *sym; /* Should really mean this symbol. */ + struct lit_sym_translation *next; +} lit_sym_translation; + +lit_sym_translation *translations = NULL; + +static bfd_boolean is_duplicate_expression + PARAMS ((expressionS *, expressionS *)); +static void cache_literal + PARAMS ((char *sym_name, expressionS *, segT)); +static symbolS *is_duplicate_literal + PARAMS ((expressionS *, segT)); + + +static bfd_boolean +is_duplicate_expression (e1, e2) + expressionS *e1; + expressionS *e2; +{ + if (e1->X_op != e2->X_op) + return FALSE; + if (e1->X_add_symbol != e2->X_add_symbol) + return FALSE; + if (e1->X_op_symbol != e2->X_op_symbol) + return FALSE; + if (e1->X_add_number != e2->X_add_number) + return FALSE; + if (e1->X_unsigned != e2->X_unsigned) + return FALSE; + if (e1->X_md != e2->X_md) + return FALSE; + return TRUE; +} + + +static void +cache_literal (sym_name, expP, seg) + char *sym_name; + expressionS *expP; + segT seg; +{ + literal_list_elem *lit = xmalloc (sizeof (literal_list_elem)); + + lit->sym = symbol_find (sym_name); + lit->expr = *expP; + lit->seg = seg; + lit->next = lit_cache; + lit_cache = lit; +} + + +static symbolS * +is_duplicate_literal (expr, seg) + expressionS *expr; + segT seg; +{ + literal_list_elem *lit = lit_cache; + + while (lit != NULL) + { + if (is_duplicate_expression (&lit->expr, expr) && seg == lit->seg) + return lit->sym; + lit = lit->next; + } + + return NULL; +} + + +static void +add_lit_sym_translation (name, offset, target) + char * name; + offsetT offset; + symbolS * target; +{ + lit_sym_translation *lit_trans = xmalloc (sizeof (lit_sym_translation)); + + lit_trans->name = name; + lit_trans->offset = offset; + lit_trans->sym = target; + lit_trans->next = translations; + translations = lit_trans; +} + + +static void +find_lit_sym_translation (expr) + expressionS *expr; +{ + lit_sym_translation *lit_trans = translations; + + if (expr->X_op != O_symbol) + return; + + while (lit_trans != NULL) + { + if (lit_trans->offset == expr->X_add_number + && strcmp (lit_trans->name, S_GET_NAME (expr->X_add_symbol)) == 0) + { + expr->X_add_symbol = lit_trans->sym; + expr->X_add_number = 0; + return; + } + lit_trans = lit_trans->next; + } +} + +#endif /* XTENSA_COMBINE_LITERALS */ diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h new file mode 100644 index 00000000000..c6bc09dc234 --- /dev/null +++ b/gas/config/tc-xtensa.h @@ -0,0 +1,200 @@ +/* tc-xtensa.h -- Header file for tc-xtensa.c. + Copyright (C) 2003 Free Software Foundation, Inc. + + 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 2, 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, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef TC_XTENSA +#define TC_XTENSA 1 + +#ifdef ANSI_PROTOTYPES +struct fix; +#endif + +#ifndef BFD_ASSEMBLER +#error Xtensa support requires BFD_ASSEMBLER +#endif + +#ifndef OBJ_ELF +#error Xtensa support requires ELF object format +#endif + +#include "xtensa-config.h" + +#define TARGET_BYTES_BIG_ENDIAN XCHAL_HAVE_BE + + +struct xtensa_frag_type +{ + unsigned is_literal:1; + unsigned is_text:1; + unsigned is_loop_target:1; + unsigned is_branch_target:1; + unsigned is_insn:1; + + /* Info about the current state of assembly, i.e., density, relax, + generics, freeregs, longcalls. These need to be passed to the + backend and then to the linking file. */ + + unsigned is_no_density:1; + unsigned is_relax:1; + unsigned is_generics:1; + unsigned is_longcalls:1; + + /* For text fragments that can generate literals at relax time, this + variable points to the frag where the literal will be stored. For + literal frags, this variable points to the nearest literal pool + location frag. This literal frag will be moved to after this + location. */ + + fragS *literal_frag; + + /* The destination segment for literal frags. (Note that this is only + valid after xtensa_move_literals. */ + + segT lit_seg; + + /* For the relaxation scheme, some literal fragments can have their + expansions modified by an instruction that relaxes. */ + + unsigned text_expansion; + unsigned literal_expansion; + unsigned unreported_expansion; +}; + +typedef struct xtensa_block_info_struct +{ + segT sec; + bfd_vma offset; + size_t size; + struct xtensa_block_info_struct *next; +} xtensa_block_info; + +typedef enum +{ + xt_insn_sec, + xt_literal_sec, + max_xt_sec +} xt_section_type; + +typedef struct xtensa_segment_info_struct +{ + fragS *literal_pool_loc; + xtensa_block_info *blocks[max_xt_sec]; +} xtensa_segment_info; + +typedef struct xtensa_symfield_type_struct +{ + unsigned int plt : 1; +} xtensa_symfield_type; + + +/* Section renaming is only supported in Tensilica's version of GAS. */ +#define XTENSA_SECTION_RENAME 1 +#ifdef XTENSA_SECTION_RENAME +extern const char *xtensa_section_rename + PARAMS ((const char *)); +#else +/* Tensilica's section renaming feature is not included here. */ +#define xtensa_section_rename(name) (name) +#endif /* XTENSA_SECTION_RENAME */ + + +extern const char *xtensa_target_format + PARAMS ((void)); +extern void xtensa_frag_init + PARAMS ((fragS *)); +extern void xtensa_cons_fix_new + PARAMS ((fragS *, int, int, expressionS *)); +extern void xtensa_frob_label + PARAMS ((struct symbol *)); +extern void xtensa_end + PARAMS ((void)); +extern void xtensa_post_relax_hook + PARAMS ((void)); +extern void xtensa_file_arch_init + PARAMS ((bfd *)); +extern void xtensa_flush_pending_output + PARAMS ((void)); +extern bfd_boolean xtensa_fix_adjustable + PARAMS ((struct fix *)); +extern void xtensa_symbol_new_hook + PARAMS ((symbolS *)); +extern long xtensa_relax_frag + PARAMS ((fragS *, long, int *)); + +#define TARGET_FORMAT xtensa_target_format () +#define TARGET_ARCH bfd_arch_xtensa +#define TC_SEGMENT_INFO_TYPE xtensa_segment_info +#define TC_SYMFIELD_TYPE xtensa_symfield_type +#define TC_FRAG_TYPE struct xtensa_frag_type +#define TC_FRAG_INIT(frag) xtensa_frag_init (frag) +#define TC_CONS_FIX_NEW xtensa_cons_fix_new +#define tc_canonicalize_symbol_name(s) xtensa_section_rename (s) +#define tc_init_after_args() xtensa_file_arch_init (stdoutput) +#define tc_fix_adjustable(fix) xtensa_fix_adjustable (fix) +#define tc_frob_label(sym) xtensa_frob_label (sym) +#define tc_symbol_new_hook(s) xtensa_symbol_new_hook (s) +#define md_elf_section_rename(name) xtensa_section_rename (name) +#define md_end xtensa_end +#define md_flush_pending_output() xtensa_flush_pending_output () +#define md_operand(x) +#define TEXT_SECTION_NAME xtensa_section_rename (".text") +#define DATA_SECTION_NAME xtensa_section_rename (".data") +#define BSS_SECTION_NAME xtensa_section_rename (".bss") + + +/* The renumber_section function must be mapped over all the sections + after calling xtensa_post_relax_hook. That function is static in + write.c so it cannot be called from xtensa_post_relax_hook itself. */ + +#define md_post_relax_hook \ + do \ + { \ + int i = 0; \ + xtensa_post_relax_hook (); \ + bfd_map_over_sections (stdoutput, renumber_sections, &i); \ + } \ + while (0) + + +/* Because xtensa relaxation can insert a new literal into the middle of + fragment and thus require re-running the relaxation pass on the + section, we need an explicit flag here. We explicitly use the name + "stretched" here to avoid changing the source code in write.c. */ + +#define md_relax_frag(segment, fragP, stretch) \ + xtensa_relax_frag (fragP, stretch, &stretched) + + +#define LOCAL_LABELS_FB 1 +#define WORKING_DOT_WORD 1 +#define DOUBLESLASH_LINE_COMMENTS +#define TC_HANDLES_FX_DONE +#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 0 + +#define MD_APPLY_SYM_VALUE(FIX) 0 + +/* The default literal sections should always be marked as "code" (i.e., + SHF_EXECINSTR). This is particularly important for the Linux kernel + module loader so that the literals are not placed after the text. */ +#define ELF_TC_SPECIAL_SECTIONS \ + { ".literal", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, \ + { ".init.literal", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, \ + { ".fini.literal", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, + +#endif /* TC_XTENSA */ diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c new file mode 100644 index 00000000000..47aa03c60ac --- /dev/null +++ b/gas/config/xtensa-relax.c @@ -0,0 +1,1766 @@ +/* Table of relaxations for Xtensa assembly. + Copyright 2003 Free Software Foundation, Inc. + + 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 2, 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, 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. */ + +/* This file contains the code for generating runtime data structures + for relaxation pattern matching from statically specified strings. + Each action contains an instruction pattern to match and + preconditions for the match as well as an expansion if the pattern + matches. The preconditions can specify that two operands are the + same or an operand is a specific constant. The expansion uses the + bound variables from the pattern to specify that specific operands + from the pattern should be used in the result. + + The patterns match a language like: + + INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )* + INSN_TEMPL ::= OPCODE ' ' [ OPERAND (',' OPERAND)* ] + OPCODE ::= id + OPERAND ::= CONSTANT | VARIABLE | SPECIALFN '(' VARIABLE ')' + SPECIALFN ::= 'HI24S' | 'F32MINUS' | 'LOW8' + VARIABLE ::= '%' id + PRECOND ::= OPERAND CMPOP OPERAND + CMPOP ::= '==' | '!=' + + The replacement language + INSN_REPL ::= INSN_LABEL_LIT ( ';' INSN_LABEL_LIT )* + INSN_LABEL_LIT ::= INSN_TEMPL + | 'LABEL' num + | 'LITERAL' num ' ' VARIABLE + + The operands in a PRECOND must be constants or variables bound by + the INSN_PATTERN. + + The operands in the INSN_REPL must be constants, variables bound in + the associated INSN_PATTERN, special variables that are bound in + the INSN_REPL by LABEL or LITERAL definitions, or special value + manipulation functions. + + A simple example of a replacement pattern: + {"movi.n %as,%imm", "movi %as,%imm"} would convert the narrow + movi.n instruction to the wide movi instruction. + + A more complex example of a branch around: + {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"} + would convert a branch to a negated branch to the following instruction + with a jump to the original label. + + An Xtensa-specific example that generates a literal: + {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"} + will convert a movi instruction to an l32r of a literal + literal defined in the literal pool. + + Even more complex is a conversion of a load with immediate offset + to a load of a freshly generated literal, an explicit add and + a load with 0 offset. This transformation is only valid, though + when the first and second operands are not the same as specified + by the "| %at!=%as" precondition clause. + {"l32i %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"} + + There is special case for loop instructions here, but because we do + not currently have the ability to represent the difference of two + symbols, the conversion requires special code in the assembler to + write the operands of the addi/addmi pair representing the + difference of the old and new loop end label. */ + +#include "as.h" +#include "xtensa-isa.h" +#include "xtensa-relax.h" +#include + +/* Imported from bfd. */ +extern xtensa_isa xtensa_default_isa; + + +/* The opname_list is a small list of names that we use for opcode and + operand variable names to simplify ownership of these commonly used + strings. Strings entered in the table can be compared by pointer + equality. */ + +typedef struct opname_list_struct opname_list; +typedef opname_list opname_e; + +struct opname_list_struct +{ + char *opname; + opname_list *next; +}; + +static opname_list *local_opnames = NULL; + + +/* The "opname_map" and its element structure "opname_map_e" are used + for binding an operand number to a name or a constant. */ + +typedef struct opname_map_e_struct opname_map_e; +typedef struct opname_map_struct opname_map; + +struct opname_map_e_struct +{ + const char *operand_name; /* If null, then use constant_value. */ + size_t operand_num; + unsigned constant_value; + opname_map_e *next; +}; + +struct opname_map_struct +{ + opname_map_e *head; + opname_map_e **tail; +}; + +/* The "precond_list" and its element structure "precond_e" represents + explicit preconditions comparing operand variables and constants. + In the "precond_e" structure, a variable is identified by the name + in the "opname" field. If that field is NULL, then the operand + is the constant in field "opval". */ + +typedef struct precond_e_struct precond_e; +typedef struct precond_list_struct precond_list; + +struct precond_e_struct +{ + const char *opname1; + unsigned opval1; + CmpOp cmpop; + const char *opname2; + unsigned opval2; + precond_e *next; +}; + +struct precond_list_struct +{ + precond_e *head; + precond_e **tail; +}; + + +/* The insn_templ represents the INSN_TEMPL instruction template. It + is an opcode name with a list of operands. These are used for + instruction patterns and replacement patterns. */ + +typedef struct insn_templ_struct insn_templ; +struct insn_templ_struct +{ + const char *opcode_name; + opname_map operand_map; +}; + + +/* The insn_pattern represents an INSN_PATTERN instruction pattern. + It is an instruction template with preconditions that specify when + it actually matches a given instruction. */ + +typedef struct insn_pattern_struct insn_pattern; +struct insn_pattern_struct +{ + insn_templ t; + precond_list preconds; +}; + + +/* The "insn_repl" and associated element structure "insn_repl_e" + instruction replacement list is a list of + instructions/LITERALS/LABELS with constant operands or operands + with names bound to the operand names in the associated pattern. */ + +typedef struct insn_repl_e_struct insn_repl_e; +struct insn_repl_e_struct +{ + insn_templ t; + insn_repl_e *next; +}; + +typedef struct insn_repl_struct insn_repl; +struct insn_repl_struct +{ + insn_repl_e *head; + insn_repl_e **tail; +}; + + +/* The split_rec is a vector of allocated char * pointers. */ + +typedef struct split_rec_struct split_rec; +struct split_rec_struct +{ + char **vec; + size_t count; +}; + +/* The "string_pattern_pair" is a set of pairs containing instruction + patterns and replacement strings. */ + +typedef struct string_pattern_pair_struct string_pattern_pair; +struct string_pattern_pair_struct +{ + const char *pattern; + const char *replacement; +}; + + +/* The widen_spec_list is a list of valid substitutions that generate + wider representations. These are generally used to specify + replacements for instructions whose immediates do not fit their + encodings. A valid transition may require mutiple steps of + one-to-one instruction replacements with a final multiple + instruction replacement. As an example, here are the transitions + required to replace an 'addi.n' with an 'addi', 'addmi'. + + addi.n a4, 0x1010 + => addi a4, 0x1010 + => addmi a4, 0x1010 + => addmi a4, 0x1000, addi a4, 0x10. */ + +static string_pattern_pair widen_spec_list[] = +{ + {"add.n %ar,%as,%at", "add %ar,%as,%at"}, + {"addi.n %ar,%as,%imm", "addi %ar,%as,%imm"}, + {"beqz.n %as,%label", "beqz %as,%label"}, + {"bnez.n %as,%label", "bnez %as,%label"}, + {"l32i.n %at,%as,%imm", "l32i %at,%as,%imm"}, + {"mov.n %at,%as", "or %at,%as,%as"}, + {"movi.n %as,%imm", "movi %as,%imm"}, + {"nop.n", "or 1,1,1"}, + {"ret.n", "ret"}, + {"retw.n", "retw"}, + {"s32i.n %at,%as,%imm", "s32i %at,%as,%imm"}, + {"srli %at,%as,%imm", "extui %at,%as,%imm,F32MINUS(%imm)"}, + {"slli %ar,%as,0", "or %ar,%as,%as"}, + /* Widening with literals */ + {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"}, + {"addi %ar,%as,%imm", "addmi %ar,%as,%imm"}, + /* LOW8 is the low 8 bits of the Immed + MID8S is the middle 8 bits of the Immed */ + {"addmi %ar,%as,%imm", "addmi %ar,%as,HI24S(%imm); addi %ar,%ar,LOW8(%imm)"}, + {"addmi %ar,%as,%imm | %ar!=%as", + "LITERAL0 %imm; l32r %ar,%LITERAL0; add %ar,%as,%ar"}, + + /* Widening the load instructions with too-large immediates */ + {"l8ui %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l8ui %at,%at,0"}, + {"l16si %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16si %at,%at,0"}, + {"l16ui %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16ui %at,%at,0"}, +#if 0 /* Xtensa Synchronization Option not yet available */ + {"l32ai %at,%as,%imm", + "LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32ai %at,%at,0"}, +#endif +#if 0 /* Xtensa Speculation Option not yet available */ + {"l32is %at,%as,%imm", + "LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32is %at,%at,0"}, +#endif + {"l32i %at,%as,%imm | %at!=%as", + "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"}, + + /* This is only PART of the loop instruction. In addition, hard + coded into it's use is a modification of the final operand in the + instruction in bytes 9 and 12. */ + {"loop %as,%label", + "loop %as,%LABEL0;" + "rsr %as, 1;" /* LEND */ + "wsr %as, 0;" /* LBEG */ + "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */ + "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */ + "wsr %as, 1;" + "isync;" + "rsr %as, 2;" /* LCOUNT */ + "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */ + "LABEL0"}, + {"loopgtz %as,%label", + "beqz %as,%label;" + "bltz %as,%label;" + "loopgtz %as,%LABEL0;" + "rsr %as, 1;" /* LEND */ + "wsr %as, 0;" /* LBEG */ + "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */ + "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */ + "wsr %as, 1;" + "isync;" + "rsr %as, 2;" /* LCOUNT */ + "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */ + "LABEL0"}, + {"loopnez %as,%label", + "beqz %as,%label;" + "loopnez %as,%LABEL0;" + "rsr %as, 1;" /* LEND */ + "wsr %as, 0;" /* LBEG */ + "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */ + "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */ + "wsr %as, 1;" + "isync;" + "rsr %as, 2;" /* LCOUNT */ + "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */ + "LABEL0"}, + +#if 0 /* no mechanism here to determine if Density Option is available */ + {"beqz %as,%label", "bnez.n %as,%LABEL0;j %label;LABEL0"}, + {"bnez %as,%label", "beqz.n %as,%LABEL0;j %label;LABEL0"}, +#else + {"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"}, + {"bnez %as,%label", "beqz %as,%LABEL0;j %label;LABEL0"}, +#endif + + {"bgez %as,%label", "bltz %as,%LABEL0;j %label;LABEL0"}, + {"bltz %as,%label", "bgez %as,%LABEL0;j %label;LABEL0"}, + {"beqi %as,%imm,%label", "bnei %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bnei %as,%imm,%label", "beqi %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bgei %as,%imm,%label", "blti %as,%imm,%LABEL0;j %label;LABEL0"}, + {"blti %as,%imm,%label", "bgei %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bgeui %as,%imm,%label", "bltui %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bltui %as,%imm,%label", "bgeui %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bbci %as,%imm,%label", "bbsi %as,%imm,%LABEL0;j %label;LABEL0"}, + {"bbsi %as,%imm,%label", "bbci %as,%imm,%LABEL0;j %label;LABEL0"}, + {"beq %as,%at,%label", "bne %as,%at,%LABEL0;j %label;LABEL0"}, + {"bne %as,%at,%label", "beq %as,%at,%LABEL0;j %label;LABEL0"}, + {"bge %as,%at,%label", "blt %as,%at,%LABEL0;j %label;LABEL0"}, + {"blt %as,%at,%label", "bge %as,%at,%LABEL0;j %label;LABEL0"}, + {"bgeu %as,%at,%label", "bltu %as,%at,%LABEL0;j %label;LABEL0"}, + {"bltu %as,%at,%label", "bgeu %as,%at,%LABEL0;j %label;LABEL0"}, + {"bany %as,%at,%label", "bnone %as,%at,%LABEL0;j %label;LABEL0"}, +#if 1 /* provide relaxations for Boolean Option */ + {"bt %bs,%label", "bf %bs,%LABEL0;j %label;LABEL0"}, + {"bf %bs,%label", "bt %bs,%LABEL0;j %label;LABEL0"}, +#endif + {"bnone %as,%at,%label", "bany %as,%at,%LABEL0;j %label;LABEL0"}, + {"ball %as,%at,%label", "bnall %as,%at,%LABEL0;j %label;LABEL0"}, + {"bnall %as,%at,%label", "ball %as,%at,%LABEL0;j %label;LABEL0"}, + {"bbc %as,%at,%label", "bbs %as,%at,%LABEL0;j %label;LABEL0"}, + {"bbs %as,%at,%label", "bbc %as,%at,%LABEL0;j %label;LABEL0"}, + {"call0 %label", "LITERAL0 %label; l32r a0,%LITERAL0; callx0 a0"}, + {"call4 %label", "LITERAL0 %label; l32r a4,%LITERAL0; callx4 a4"}, + {"call8 %label", "LITERAL0 %label; l32r a8,%LITERAL0; callx8 a8"}, + {"call12 %label", "LITERAL0 %label; l32r a12,%LITERAL0; callx12 a12"} +}; + +#define WIDEN_COUNT (sizeof (widen_spec_list) / sizeof (string_pattern_pair)) + + +/* The simplify_spec_list specifies simplifying transformations that + will reduce the instruction width or otherwise simplify an + instruction. These are usually applied before relaxation in the + assembler. It is always legal to simplify. Even for "addi as, 0", + the "addi.n as, 0" will eventually be widened back to an "addi 0" + after the widening table is applied. Note: The usage of this table + has changed somewhat so that it is entirely specific to "narrowing" + instructions to use the density option. This table is not used at + all when the density option is not available. */ + +string_pattern_pair simplify_spec_list[] = +{ + {"add %ar,%as,%at", "add.n %ar,%as,%at"}, + {"addi.n %ar,%as,0", "mov.n %ar,%as"}, + {"addi %ar,%as,0", "mov.n %ar,%as"}, + {"addi %ar,%as,%imm", "addi.n %ar,%as,%imm"}, + {"addmi %ar,%as,%imm", "addi.n %ar,%as,%imm"}, + {"beqz %as,%label", "beqz.n %as,%label"}, + {"bnez %as,%label", "bnez.n %as,%label"}, + {"l32i %at,%as,%imm", "l32i.n %at,%as,%imm"}, + {"movi %as,%imm", "movi.n %as,%imm"}, + {"or %ar,%as,%at | %as==%at", "mov.n %ar,%as"}, + {"ret", "ret.n"}, + {"retw", "retw.n"}, + {"s32i %at,%as,%imm", "s32i.n %at,%as,%imm"}, + {"slli %ar,%as,0", "mov.n %ar,%as"} +}; + +#define SIMPLIFY_COUNT \ + (sizeof (simplify_spec_list) / sizeof (string_pattern_pair)) + + +/* Transition generation helpers. */ + +static void append_transition + PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *)); +static void append_condition + PARAMS ((TransitionRule *, Precondition *)); +static void append_value_condition + PARAMS ((TransitionRule *, CmpOp, unsigned, unsigned)); +static void append_constant_value_condition + PARAMS ((TransitionRule *, CmpOp, unsigned, unsigned)); +static void append_build_insn + PARAMS ((TransitionRule *, BuildInstr *)); +static void append_op + PARAMS ((BuildInstr *, BuildOp *)); +static void append_literal_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_label_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_constant_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_field_op + PARAMS ((BuildInstr *, unsigned, unsigned)); +static void append_user_fn_field_op + PARAMS ((BuildInstr *, unsigned, OpType, unsigned)); +static long operand_function_HI24S + PARAMS ((long)); +static long operand_function_F32MINUS + PARAMS ((long)); +static long operand_function_LOW8 + PARAMS ((long)); + +/* Externally visible functions. */ + +extern bfd_boolean xg_has_userdef_op_fn + PARAMS ((OpType)); +extern long xg_apply_userdef_op_fn + PARAMS ((OpType, long)); + +/* Parsing helpers. */ + +static const char *enter_opname_n + PARAMS ((const char *, size_t)); +static const char *enter_opname + PARAMS ((const char *)); + +/* Construction and destruction. */ + +static void init_opname_map + PARAMS ((opname_map *)); +static void clear_opname_map + PARAMS ((opname_map *)); +static void init_precond_list + PARAMS ((precond_list *)); +static void clear_precond_list + PARAMS ((precond_list *)); +static void init_insn_templ + PARAMS ((insn_templ *)); +static void clear_insn_templ + PARAMS ((insn_templ *)); +static void init_insn_pattern + PARAMS ((insn_pattern *)); +static void clear_insn_pattern + PARAMS ((insn_pattern *)); +static void init_insn_repl + PARAMS ((insn_repl *)); +static void clear_insn_repl + PARAMS ((insn_repl *)); +static void init_split_rec + PARAMS ((split_rec *)); +static void clear_split_rec + PARAMS ((split_rec *)); + +/* Operand and insn_templ helpers. */ + +static bfd_boolean same_operand_name + PARAMS ((const opname_map_e *, const opname_map_e *)); +static opname_map_e *get_opmatch + PARAMS ((opname_map *, const char *)); +static bfd_boolean op_is_constant + PARAMS ((const opname_map_e *)); +static unsigned op_get_constant + PARAMS ((const opname_map_e *)); +static size_t insn_templ_operand_count + PARAMS ((const insn_templ *)); + +/* parsing helpers. */ + +static const char *skip_white + PARAMS ((const char *)); +static void trim_whitespace + PARAMS ((char *)); +static void split_string + PARAMS ((split_rec *, const char *, char, bfd_boolean)); + +/* Language parsing. */ + +static bfd_boolean parse_insn_pattern + PARAMS ((const char *, insn_pattern *)); +static bfd_boolean parse_insn_repl + PARAMS ((const char *, insn_repl *)); +static bfd_boolean parse_insn_templ + PARAMS ((const char *, insn_templ *)); +static bfd_boolean parse_special_fn + PARAMS ((const char *, const char **, const char **)); +static bfd_boolean parse_precond + PARAMS ((const char *, precond_e *)); +static bfd_boolean parse_constant + PARAMS ((const char *, unsigned *)); +static bfd_boolean parse_id_constant + PARAMS ((const char *, const char *, unsigned *)); + +/* Transition table building code. */ + +static TransitionRule *build_transition + PARAMS ((insn_pattern *, insn_repl *, const char *, const char *)); +static TransitionTable *build_transition_table + PARAMS ((const string_pattern_pair *, size_t)); + + +void +append_transition (tt, opcode, t) + TransitionTable *tt; + xtensa_opcode opcode; + TransitionRule *t; +{ + TransitionList *tl = (TransitionList *) xmalloc (sizeof (TransitionList)); + TransitionList *prev; + TransitionList *nxt; + assert (tt != NULL); + assert (opcode < tt->num_opcodes); + + prev = tt->table[opcode]; + tl->rule = t; + tl->next = NULL; + if (prev == NULL) + { + tt->table[opcode] = tl; + return; + } + nxt = prev->next; + while (nxt != NULL) + { + prev = nxt; + nxt = nxt->next; + } + prev->next = tl; + return; +} + + +void +append_condition (tr, cond) + TransitionRule *tr; + Precondition *cond; +{ + PreconditionList *pl = + (PreconditionList *) xmalloc (sizeof (PreconditionList)); + PreconditionList *prev = tr->conditions; + PreconditionList *nxt; + + pl->precond = cond; + pl->next = NULL; + if (prev == NULL) + { + tr->conditions = pl; + return; + } + nxt = prev->next; + while (nxt != NULL) + { + prev = nxt; + nxt = nxt->next; + } + prev->next = pl; +} + + +void +append_value_condition (tr, cmp, op1, op2) + TransitionRule *tr; + CmpOp cmp; + unsigned op1; + unsigned op2; +{ + Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition)); + + cond->cmp = cmp; + cond->op_num = op1; + cond->typ = OP_OPERAND; + cond->op_data = op2; + append_condition (tr, cond); +} + + +void +append_constant_value_condition (tr, cmp, op1, cnst) + TransitionRule *tr; + CmpOp cmp; + unsigned op1; + unsigned cnst; +{ + Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition)); + + cond->cmp = cmp; + cond->op_num = op1; + cond->typ = OP_CONSTANT; + cond->op_data = cnst; + append_condition (tr, cond); +} + + +void +append_build_insn (tr, bi) + TransitionRule *tr; + BuildInstr *bi; +{ + BuildInstr *prev = tr->to_instr; + BuildInstr *nxt; + + bi->next = NULL; + if (prev == NULL) + { + tr->to_instr = bi; + return; + } + nxt = prev->next; + while (nxt != 0) + { + prev = nxt; + nxt = prev->next; + } + prev->next = bi; +} + + +void +append_op (bi, b_op) + BuildInstr *bi; + BuildOp *b_op; +{ + BuildOp *prev = bi->ops; + BuildOp *nxt; + + if (prev == NULL) + { + bi->ops = b_op; + return; + } + nxt = prev->next; + while (nxt != NULL) + { + prev = nxt; + nxt = nxt->next; + } + prev->next = b_op; +} + + +void +append_literal_op (bi, op1, litnum) + BuildInstr *bi; + unsigned op1; + unsigned litnum; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_LITERAL; + b_op->op_data = litnum; + b_op->next = NULL; + append_op (bi, b_op); +} + + +void +append_label_op (bi, op1, labnum) + BuildInstr *bi; + unsigned op1; + unsigned labnum; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_LABEL; + b_op->op_data = labnum; + b_op->next = NULL; + append_op (bi, b_op); +} + + +void +append_constant_op (bi, op1, cnst) + BuildInstr *bi; + unsigned op1; + unsigned cnst; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_CONSTANT; + b_op->op_data = cnst; + b_op->next = NULL; + append_op (bi, b_op); +} + + +void +append_field_op (bi, op1, src_op) + BuildInstr *bi; + unsigned op1; + unsigned src_op; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = OP_OPERAND; + b_op->op_data = src_op; + b_op->next = NULL; + append_op (bi, b_op); +} + + +/* These could be generated but are not currently. */ + +void +append_user_fn_field_op (bi, op1, typ, src_op) + BuildInstr *bi; + unsigned op1; + OpType typ; + unsigned src_op; +{ + BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp)); + + b_op->op_num = op1; + b_op->typ = typ; + b_op->op_data = src_op; + b_op->next = NULL; + append_op (bi, b_op); +} + + +/* These operand functions are the semantics of user-defined + operand functions. */ + +long +operand_function_HI24S (a) + long a; +{ + if (a & 0x80) + return (a & (~0xff)) + 0x100; + else + return (a & (~0xff)); +} + + +long +operand_function_F32MINUS (a) + long a; +{ + return (32 - a); +} + + +long +operand_function_LOW8 (a) + long a; +{ + if (a & 0x80) + return (a & 0xff) | ~0xff; + else + return (a & 0xff); +} + + +bfd_boolean +xg_has_userdef_op_fn (op) + OpType op; +{ + switch (op) + { + case OP_OPERAND_F32MINUS: + case OP_OPERAND_LOW8: + case OP_OPERAND_HI24S: + return TRUE; + default: + break; + } + return FALSE; +} + + +long +xg_apply_userdef_op_fn (op, a) + OpType op; + long a; +{ + switch (op) + { + case OP_OPERAND_F32MINUS: + return operand_function_F32MINUS (a); + case OP_OPERAND_LOW8: + return operand_function_LOW8 (a); + case OP_OPERAND_HI24S: + return operand_function_HI24S (a); + default: + break; + } + return FALSE; +} + + +/* Generate a transition table. */ + +const char * +enter_opname_n (name, len) + const char *name; + size_t len; +{ + opname_e *op; + + for (op = local_opnames; op != NULL; op = op->next) + { + if (strlen (op->opname) == len && strncmp (op->opname, name, len) == 0) + return op->opname; + } + op = (opname_e *) xmalloc (sizeof (opname_e)); + op->opname = (char *) xmalloc (len + 1); + strncpy (op->opname, name, len); + op->opname[len] = '\0'; + return op->opname; +} + + +static const char * +enter_opname (name) + const char *name; +{ + opname_e *op; + + for (op = local_opnames; op != NULL; op = op->next) + { + if (strcmp (op->opname, name) == 0) + return op->opname; + } + op = (opname_e *) xmalloc (sizeof (opname_e)); + op->opname = strdup (name); + return op->opname; +} + + +void +init_opname_map (m) + opname_map *m; +{ + m->head = NULL; + m->tail = &m->head; +} + + +void +clear_opname_map (m) + opname_map *m; +{ + opname_map_e *e; + + while (m->head != NULL) + { + e = m->head; + m->head = e->next; + free (e); + } + m->tail = &m->head; +} + + +static bfd_boolean +same_operand_name (m1, m2) + const opname_map_e *m1; + const opname_map_e *m2; +{ + if (m1->operand_name == NULL || m1->operand_name == NULL) + return FALSE; + return (m1->operand_name == m2->operand_name); +} + + +opname_map_e * +get_opmatch (map, operand_name) + opname_map *map; + const char *operand_name; +{ + opname_map_e *m; + + for (m = map->head; m != NULL; m = m->next) + { + if (strcmp (m->operand_name, operand_name) == 0) + return m; + } + return NULL; +} + + +bfd_boolean +op_is_constant (m1) + const opname_map_e *m1; +{ + return (m1->operand_name == NULL); +} + + +static unsigned +op_get_constant (m1) + const opname_map_e *m1; +{ + assert (m1->operand_name == NULL); + return m1->constant_value; +} + + +void +init_precond_list (l) + precond_list *l; +{ + l->head = NULL; + l->tail = &l->head; +} + + +void +clear_precond_list (l) + precond_list *l; +{ + precond_e *e; + + while (l->head != NULL) + { + e = l->head; + l->head = e->next; + free (e); + } + l->tail = &l->head; +} + + +void +init_insn_templ (t) + insn_templ *t; +{ + t->opcode_name = NULL; + init_opname_map (&t->operand_map); +} + + +void +clear_insn_templ (t) + insn_templ *t; +{ + clear_opname_map (&t->operand_map); +} + + +void +init_insn_pattern (p) + insn_pattern *p; +{ + init_insn_templ (&p->t); + init_precond_list (&p->preconds); +} + + +void +clear_insn_pattern (p) + insn_pattern *p; +{ + clear_insn_templ (&p->t); + clear_precond_list (&p->preconds); +} + + +void +init_insn_repl (r) + insn_repl *r; +{ + r->head = NULL; + r->tail = &r->head; +} + + +void +clear_insn_repl (r) + insn_repl *r; +{ + insn_repl_e *e; + + while (r->head != NULL) + { + e = r->head; + r->head = e->next; + clear_insn_templ (&e->t); + } + r->tail = &r->head; +} + + +static size_t +insn_templ_operand_count (t) + const insn_templ *t; +{ + size_t i = 0; + const opname_map_e *op; + + for (op = t->operand_map.head; op != NULL; op = op->next, ++i) + ; + return i; +} + + +/* Convert a string to a number. E.G.: parse_constant("10", &num) */ + +bfd_boolean +parse_constant (in, val_p) + const char *in; + unsigned *val_p; +{ + unsigned val = 0; + const char *p; + + if (in == NULL) + return FALSE; + p = in; + + while (*p != '\0') + { + if (*p >= '0' && *p <= '9') + val = val * 10 + (*p - '0'); + else + return FALSE; + ++p; + } + *val_p = val; + return TRUE; +} + + +/* Match a pattern like "foo1" with + parse_id_constant("foo1", "foo", &num). + This may also be used to just match a number. */ + +bfd_boolean +parse_id_constant (in, name, val_p) + const char *in; + const char *name; + unsigned *val_p; +{ + unsigned namelen = 0; + const char *p; + + if (in == NULL) + return FALSE; + + if (name != NULL) + namelen = strlen (name); + + if (name != NULL && strncmp (in, name, namelen) != 0) + return FALSE; + + p = &in[namelen]; + return parse_constant (p, val_p); +} + + +static bfd_boolean +parse_special_fn (name, fn_name_p, arg_name_p) + const char *name; + const char **fn_name_p; + const char **arg_name_p; +{ + char *p_start; + const char *p_end; + + p_start = strchr (name, '('); + if (p_start == NULL) + return FALSE; + + p_end = strchr (p_start, ')'); + + if (p_end == NULL) + return FALSE; + + if (p_end[1] != '\0') + return FALSE; + + *fn_name_p = enter_opname_n (name, p_start - name); + *arg_name_p = enter_opname_n (p_start + 1, p_end - p_start - 1); + return TRUE; +} + + +const char * +skip_white (p) + const char *p; +{ + if (p == NULL) + return p; + while (*p == ' ') + ++p; + return p; +} + + +void +trim_whitespace (in) + char *in; +{ + char *last_white = NULL; + char *p = in; + + while (p && *p != '\0') + { + while (*p == ' ') + { + if (last_white == NULL) + last_white = p; + p++; + } + if (*p != '\0') + { + last_white = NULL; + p++; + } + } + if (last_white) + *last_white = '\0'; +} + + +/* Split a string into component strings where "c" is the + delimiter. Place the result in the split_rec. */ + +void +split_string (rec, in, c, elide_whitespace) + split_rec *rec; + const char *in; + char c; + bfd_boolean elide_whitespace; +{ + size_t cnt = 0; + size_t i; + const char *p = in; + + while (p != NULL && *p != '\0') + { + cnt++; + p = strchr (p, c); + if (p) + p++; + } + rec->count = cnt; + rec->vec = NULL; + + if (rec->count == 0) + return; + + rec->vec = (char **) xmalloc (sizeof (char *) * cnt); + for (i = 0; i < cnt; i++) + rec->vec[i] = 0; + + p = in; + for (i = 0; i < cnt; i++) + { + const char *q; + size_t len; + + q = p; + if (elide_whitespace) + q = skip_white (q); + + p = strchr (q, c); + if (p == NULL) + rec->vec[i] = strdup (q); + else + { + len = p - q; + rec->vec[i] = (char *) xmalloc (sizeof (char) * (len + 1)); + strncpy (rec->vec[i], q, len); + rec->vec[i][len] = '\0'; + p++; + } + + if (elide_whitespace) + trim_whitespace (rec->vec[i]); + } +} + + +void +clear_split_rec (rec) + split_rec *rec; +{ + size_t i; + + for (i = 0; i < rec->count; ++i) + free (rec->vec[i]); + + if (rec->count > 0) + free (rec->vec); +} + + +void +init_split_rec (rec) + split_rec *rec; +{ + rec->vec = NULL; + rec->count = 0; +} + + +/* Parse an instruction template like "insn op1, op2, op3". */ + +bfd_boolean +parse_insn_templ (s, t) + const char *s; + insn_templ *t; +{ + const char *p = s; + /* First find the first whitespace. */ + size_t insn_name_len; + split_rec oprec; + size_t i; + + init_split_rec (&oprec); + + p = skip_white (p); + insn_name_len = strcspn (s, " "); + if (insn_name_len == 0) + return FALSE; + + init_insn_templ (t); + t->opcode_name = enter_opname_n (p, insn_name_len); + + p = p + insn_name_len; + + /* Split by ',' and skip beginning and trailing whitespace. */ + split_string (&oprec, p, ',', TRUE); + + for (i = 0; i < oprec.count; i++) + { + const char *opname = oprec.vec[i]; + opname_map_e *e = (opname_map_e *) xmalloc (sizeof (opname_map_e)); + e->next = NULL; + e->operand_name = NULL; + e->constant_value = 0; + e->operand_num = i; + + /* If it begins with a number, assume that it is a number. */ + if (opname && opname[0] >= '0' && opname[0] <= '9') + { + unsigned val; + + if (parse_constant (opname, &val)) + e->constant_value = val; + else + { + free (e); + clear_split_rec (&oprec); + clear_insn_templ (t); + return FALSE; + } + } + else + e->operand_name = enter_opname (oprec.vec[i]); + + *t->operand_map.tail = e; + t->operand_map.tail = &e->next; + } + clear_split_rec (&oprec); + return TRUE; +} + + +bfd_boolean +parse_precond (s, precond) + const char *s; + precond_e *precond; +{ + /* All preconditions are currently of the form: + a == b or a != b or a == k (where k is a constant). + Later we may use some special functions like DENSITY == 1 + to identify when density is available. */ + + const char *p = s; + size_t len; + precond->opname1 = NULL; + precond->opval1 = 0; + precond->cmpop = OP_EQUAL; + precond->opname2 = NULL; + precond->opval2 = 0; + precond->next = NULL; + + p = skip_white (p); + + len = strcspn (p, " !="); + + if (len == 0) + return FALSE; + + precond->opname1 = enter_opname_n (p, len); + p = p + len; + p = skip_white (p); + + /* Check for "==" and "!=". */ + if (strncmp (p, "==", 2) == 0) + precond->cmpop = OP_EQUAL; + else if (strncmp (p, "!=", 2) == 0) + precond->cmpop = OP_NOTEQUAL; + else + return FALSE; + + p = p + 2; + p = skip_white (p); + + /* No trailing whitespace from earlier parsing. */ + if (p[0] >= '0' && p[0] <= '9') + { + unsigned val; + if (parse_constant (p, &val)) + precond->opval2 = val; + else + return FALSE; + } + else + precond->opname2 = enter_opname (p); + return TRUE; +} + + +/* Parse a string like: + "insn op1, op2, op3, op4 | op1 != op2 | op2 == op3 | op4 == 1". + I.E., instruction "insn" with 4 operands where operand 1 and 2 are not + the same and operand 2 and 3 are the same and operand 4 is 1. */ + +bfd_boolean +parse_insn_pattern (in, insn) + const char *in; + insn_pattern *insn; +{ + + split_rec rec; + size_t i; + + init_split_rec (&rec); + init_insn_pattern (insn); + + split_string (&rec, in, '|', TRUE); + + if (rec.count == 0) + { + clear_split_rec (&rec); + return FALSE; + } + + if (!parse_insn_templ (rec.vec[0], &insn->t)) + { + clear_split_rec (&rec); + return FALSE; + } + + for (i = 1; i < rec.count; i++) + { + precond_e *cond = (precond_e *) xmalloc (sizeof (precond_e)); + + if (!parse_precond (rec.vec[i], cond)) + { + clear_split_rec (&rec); + clear_insn_pattern (insn); + return FALSE; + } + + /* Append the condition. */ + *insn->preconds.tail = cond; + insn->preconds.tail = &cond->next; + } + + clear_split_rec (&rec); + return TRUE; +} + + +bfd_boolean +parse_insn_repl (in, r_p) + const char *in; + insn_repl *r_p; +{ + /* This is a list of instruction templates separated by ';'. */ + split_rec rec; + size_t i; + + split_string (&rec, in, ';', TRUE); + + for (i = 0; i < rec.count; i++) + { + insn_repl_e *e = (insn_repl_e *) xmalloc (sizeof (insn_repl_e)); + + e->next = NULL; + + if (!parse_insn_templ (rec.vec[i], &e->t)) + { + free (e); + clear_insn_repl (r_p); + return FALSE; + } + *r_p->tail = e; + r_p->tail = &e->next; + } + return TRUE; +} + + +TransitionRule * +build_transition (initial_insn, replace_insns, from_string, to_string) + insn_pattern *initial_insn; + insn_repl *replace_insns; + const char *from_string; + const char *to_string; +{ + TransitionRule *tr = NULL; + xtensa_opcode opcode; + xtensa_isa isa = xtensa_default_isa; + + opname_map_e *op1; + opname_map_e *op2; + + precond_e *precond; + insn_repl_e *r; + unsigned label_count = 0; + unsigned max_label_count = 0; + bfd_boolean has_label = FALSE; + unsigned literal_count = 0; + + opcode = xtensa_opcode_lookup (isa, initial_insn->t.opcode_name); + if (opcode == XTENSA_UNDEFINED) + { + /* It is OK to not be able to translate some of these opcodes. */ +#if 0 + as_warn (_("Invalid opcode '%s' in transition rule '%s'\n"), + initial_insn->t.opcode_name, to_string); +#endif + return NULL; + } + + + if (xtensa_num_operands (isa, opcode) + != (int) insn_templ_operand_count (&initial_insn->t)) + { + /* This is also OK because there are opcodes that + have different numbers of operands on different + architecture variations. */ +#if 0 + as_fatal (_("opcode %s mismatched operand count %d != expected %d"), + xtensa_opcode_name (isa, opcode), + xtensa_num_operands (isa, opcode), + insn_templ_operand_count (&initial_insn->t)); +#endif + return NULL; + } + + tr = (TransitionRule *) xmalloc (sizeof (TransitionRule)); + tr->opcode = opcode; + tr->conditions = NULL; + tr->to_instr = NULL; + + /* Build the conditions. First, equivalent operand condition.... */ + for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next) + { + for (op2 = op1->next; op2 != NULL; op2 = op2->next) + { + if (same_operand_name (op1, op2)) + { + append_value_condition (tr, OP_EQUAL, + op1->operand_num, op2->operand_num); + } + } + } + + /* Now the condition that an operand value must be a constant.... */ + for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next) + { + if (op_is_constant (op1)) + { + append_constant_value_condition (tr, + OP_EQUAL, + op1->operand_num, + op_get_constant (op1)); + } + } + + + /* Now add the explicit preconditions listed after the "|" in the spec. + These are currently very limited, so we do a special case + parse for them. We expect spaces, opname != opname. */ + for (precond = initial_insn->preconds.head; + precond != NULL; + precond = precond->next) + { + op1 = NULL; + op2 = NULL; + + if (precond->opname1) + { + op1 = get_opmatch (&initial_insn->t.operand_map, precond->opname1); + if (op1 == NULL) + { + as_fatal (_("opcode '%s': no bound opname '%s' " + "for precondition in '%s'"), + xtensa_opcode_name (isa, opcode), + precond->opname1, from_string); + return NULL; + } + } + + if (precond->opname2) + { + op2 = get_opmatch (&initial_insn->t.operand_map, precond->opname2); + if (op2 == NULL) + { + as_fatal (_("opcode '%s': no bound opname '%s' " + "for precondition in %s"), + xtensa_opcode_name (isa, opcode), + precond->opname2, from_string); + return NULL; + } + } + + if (op1 == NULL && op2 == NULL) + { + as_fatal (_("opcode '%s': precondition only contains " + "constants in '%s'"), + xtensa_opcode_name (isa, opcode), from_string); + return NULL; + } + else if (op1 != NULL && op2 != NULL) + append_value_condition (tr, precond->cmpop, + op1->operand_num, op2->operand_num); + else if (op2 == NULL) + append_constant_value_condition (tr, precond->cmpop, + op1->operand_num, precond->opval1); + else + append_constant_value_condition (tr, precond->cmpop, + op2->operand_num, precond->opval2); + } + + /* Generate the replacement instructions. Some of these + "instructions" are actually labels and literals. The literals + must be defined in order 0..n and a literal must be defined + (e.g., "LITERAL0 %imm") before use (e.g., "%LITERAL0"). The + labels must be defined in order, but they can be used before they + are defined. Also there are a number of special operands (e.g., + HI24S). */ + + for (r = replace_insns->head; r != NULL; r = r->next) + { + BuildInstr *bi; + const char *opcode_name; + size_t operand_count; + opname_map_e *op; + unsigned idnum = 0; + const char *fn_name; + const char *operand_arg_name; + + bi = (BuildInstr *) xmalloc (sizeof (BuildInstr)); + append_build_insn (tr, bi); + + bi->id = 0; + bi->opcode = XTENSA_UNDEFINED; + bi->ops = NULL; + bi->next = NULL; + + opcode_name = r->t.opcode_name; + operand_count = insn_templ_operand_count (&r->t); + + if (parse_id_constant (opcode_name, "LITERAL", &idnum)) + { + bi->typ = INSTR_LITERAL_DEF; + bi->id = idnum; + if (idnum != literal_count) + as_fatal (_("generated literals must be numbered consecutively")); + ++literal_count; + if (operand_count != 1) + as_fatal (_("expected one operand for generated literal")); + + } + else if (parse_id_constant (opcode_name, "LABEL", &idnum)) + { + bi->typ = INSTR_LABEL_DEF; + bi->id = idnum; + if (idnum != label_count) + as_fatal (_("generated labels must be numbered consecutively")); + ++label_count; + if (operand_count != 0) + as_fatal (_("expected 0 operands for generated label")); + } + else + { + bi->typ = INSTR_INSTR; + bi->opcode = xtensa_opcode_lookup (isa, r->t.opcode_name); + if (bi->opcode == XTENSA_UNDEFINED) + return NULL; + /* Check for the right number of ops. */ + if (xtensa_num_operands (isa, bi->opcode) + != (int) operand_count) + as_fatal (_("opcode '%s': replacement does not have %d ops"), + opcode_name, xtensa_num_operands (isa, bi->opcode)); + } + + for (op = r->t.operand_map.head; op != NULL; op = op->next) + { + unsigned idnum; + + if (op_is_constant (op)) + append_constant_op (bi, op->operand_num, op_get_constant (op)); + else if (parse_id_constant (op->operand_name, "%LITERAL", &idnum)) + { + if (idnum >= literal_count) + as_fatal (_("opcode %s: replacement " + "literal %d >= literal_count(%d)"), + opcode_name, idnum, literal_count); + append_literal_op (bi, op->operand_num, idnum); + } + else if (parse_id_constant (op->operand_name, "%LABEL", &idnum)) + { + has_label = TRUE; + if (idnum > max_label_count) + max_label_count = idnum; + append_label_op (bi, op->operand_num, idnum); + } + else if (parse_id_constant (op->operand_name, "a", &idnum)) + append_constant_op (bi, op->operand_num, idnum); + else if (op->operand_name[0] == '%') + { + opname_map_e *orig_op; + orig_op = get_opmatch (&initial_insn->t.operand_map, + op->operand_name); + if (orig_op == NULL) + { + as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"), + opcode_name, op->operand_name, to_string); + + append_constant_op (bi, op->operand_num, 0); + } + else + append_field_op (bi, op->operand_num, orig_op->operand_num); + } + else if (parse_special_fn (op->operand_name, + &fn_name, &operand_arg_name)) + { + opname_map_e *orig_op; + OpType typ = OP_CONSTANT; + + if (strcmp (fn_name, "LOW8") == 0) + typ = OP_OPERAND_LOW8; + else if (strcmp (fn_name, "HI24S") == 0) + typ = OP_OPERAND_HI24S; + else if (strcmp (fn_name, "F32MINUS") == 0) + typ = OP_OPERAND_F32MINUS; + else + as_fatal (_("unknown user defined function %s"), fn_name); + + orig_op = get_opmatch (&initial_insn->t.operand_map, + operand_arg_name); + if (orig_op == NULL) + { + as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"), + opcode_name, op->operand_name, to_string); + append_constant_op (bi, op->operand_num, 0); + } + else + append_user_fn_field_op (bi, op->operand_num, + typ, orig_op->operand_num); + } + else + { + as_fatal (_("opcode %s: could not parse operand '%s' in '%s'"), + opcode_name, op->operand_name, to_string); + append_constant_op (bi, op->operand_num, 0); + } + } + } + if (has_label && max_label_count >= label_count) + { + as_fatal (_("opcode %s: replacement label %d >= label_count(%d)"), + xtensa_opcode_name (isa, opcode), + max_label_count, label_count); + return NULL; + } + + return tr; +} + + +TransitionTable * +build_transition_table (transitions, transition_count) + const string_pattern_pair *transitions; + size_t transition_count; +{ + TransitionTable *table = NULL; + int num_opcodes = xtensa_num_opcodes (xtensa_default_isa); + int i; + size_t tnum; + + if (table != NULL) + return table; + + /* Otherwise, build it now. */ + table = (TransitionTable *) xmalloc (sizeof (TransitionTable)); + table->num_opcodes = num_opcodes; + table->table = + (TransitionList **) xmalloc (sizeof (TransitionTable *) * num_opcodes); + + for (i = 0; i < num_opcodes; i++) + table->table[i] = NULL; + + for (tnum = 0; tnum < transition_count; tnum++) + { + const char *from_string = transitions[tnum].pattern; + const char *to_string = transitions[tnum].replacement; + + insn_pattern initial_insn; + insn_repl replace_insns; + TransitionRule *tr; + + init_insn_pattern (&initial_insn); + if (!parse_insn_pattern (from_string, &initial_insn)) + { + as_fatal (_("could not parse INSN_PATTERN '%s'"), from_string); + clear_insn_pattern (&initial_insn); + continue; + } + + init_insn_repl (&replace_insns); + if (!parse_insn_repl (to_string, &replace_insns)) + { + as_fatal (_("could not parse INSN_REPL '%s'"), to_string); + clear_insn_pattern (&initial_insn); + clear_insn_repl (&replace_insns); + continue; + } + + tr = build_transition (&initial_insn, &replace_insns, + from_string, to_string); + if (tr) + append_transition (table, tr->opcode, tr); + + clear_insn_repl (&replace_insns); + clear_insn_pattern (&initial_insn); + } + return table; +} + + +extern TransitionTable * +xg_build_widen_table () +{ + static TransitionTable *table = NULL; + if (table == NULL) + table = build_transition_table (widen_spec_list, WIDEN_COUNT); + return table; +} + + +extern TransitionTable * +xg_build_simplify_table () +{ + static TransitionTable *table = NULL; + if (table == NULL) + table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT); + return table; +} diff --git a/gas/config/xtensa-relax.h b/gas/config/xtensa-relax.h new file mode 100644 index 00000000000..99bf77bfd8e --- /dev/null +++ b/gas/config/xtensa-relax.h @@ -0,0 +1,142 @@ +/* Table of relaxations for Xtensa assembly. + Copyright 2003 Free Software Foundation, Inc. + + 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 2, 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, 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. */ + +#ifndef XTENSA_RELAX_H +#define XTENSA_RELAX_H + +#include "xtensa-isa.h" + + +/* Data structures for the table-driven relaxations for Xtensa processors. + See xtensa-relax.c for details. */ + +typedef struct transition_list TransitionList; +typedef struct transition_table TransitionTable; +typedef struct transition_rule TransitionRule; +typedef struct precondition_list PreconditionList; +typedef struct precondition Precondition; + +struct transition_table +{ + int num_opcodes; + TransitionList **table; /* Possible transitions for each opcode. */ +}; + +struct transition_list +{ + TransitionRule *rule; + TransitionList *next; +}; + +struct precondition_list +{ + Precondition *precond; + PreconditionList *next; +}; + + +/* Operand types and constraints on operands: */ + +typedef enum op_type OpType; +typedef enum cmp_op CmpOp; + +enum op_type +{ + OP_CONSTANT, + OP_OPERAND, + OP_OPERAND_LOW8, /* Sign-extended low 8 bits of immed. */ + OP_OPERAND_HI24S, /* high 24 bits of immed, + plus 0x100 if low 8 bits are signed. */ + OP_OPERAND_F32MINUS, /* 32 - immed. */ + OP_LITERAL, + OP_LABEL +}; + +enum cmp_op +{ + OP_EQUAL, + OP_NOTEQUAL, +}; + +struct precondition +{ + CmpOp cmp; + int op_num; + OpType typ; /* CONSTANT: op_data is a constant. + OPERAND: operand op_num must equal op_data. + Cannot be LITERAL or LABEL. */ + int op_data; +}; + +typedef struct build_op BuildOp; + +struct build_op +{ + int op_num; + OpType typ; + unsigned op_data; /* CONSTANT: op_data is the value to encode. + OPERAND: op_data is the field in the + source instruction to take the value from + and encode in the op_num field here. + LITERAL or LABEL: op_data is the ordinal + that identifies the appropriate one, i.e., + there can be more than one literal or + label in an expansion. */ + BuildOp *next; +}; + +typedef struct build_instr BuildInstr; +typedef enum instr_type InstrType; + +enum instr_type +{ + INSTR_INSTR, + INSTR_LITERAL_DEF, + INSTR_LABEL_DEF +}; + +struct build_instr +{ + InstrType typ; + unsigned id; /* LITERAL_DEF or LABEL_DEF: an ordinal to + identify which one. */ + xtensa_opcode opcode; /* unused for LITERAL_DEF or LABEL_DEF. */ + BuildOp *ops; + BuildInstr *next; +}; + +struct transition_rule +{ + xtensa_opcode opcode; + PreconditionList *conditions; + BuildInstr *to_instr; +}; + +extern TransitionTable *xg_build_simplify_table + PARAMS ((void)); +extern TransitionTable *xg_build_widen_table + PARAMS ((void)); + +extern bfd_boolean xg_has_userdef_op_fn + PARAMS ((OpType)); +extern long xg_apply_userdef_op_fn + PARAMS ((OpType, long)); + +#endif /* !XTENSA_RELAX_H */ diff --git a/gas/configure b/gas/configure index 3ea803b923c..10989f733f6 100755 --- a/gas/configure +++ b/gas/configure @@ -1989,6 +1989,19 @@ case $host in # Find out which ABI we are using. echo '#line 1991 "configure"' > conftest.$ac_ext if { (eval echo configure:1992: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" @@ -2000,6 +2013,7 @@ case $host in LD="${LD-ld} -64" ;; esac + fi fi rm -rf conftest* ;; @@ -2007,7 +2021,7 @@ case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext - if { (eval echo configure:2011: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + if { (eval echo configure:2025: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then case "`/usr/bin/file conftest.o`" in *ELF-32*) HPUX_IA64_MODE="32" @@ -2025,7 +2039,7 @@ ia64-*-hpux*) SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" echo $ac_n "checking whether the C compiler needs -belf""... $ac_c" 1>&6 -echo "configure:2029: checking whether the C compiler needs -belf" >&5 +echo "configure:2043: checking whether the C compiler needs -belf" >&5 if eval "test \"`echo '$''{'lt_cv_cc_needs_belf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2038,14 +2052,14 @@ ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$a cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2063: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* lt_cv_cc_needs_belf=yes else @@ -2314,6 +2328,7 @@ for this_target in $target $canon_targets ; do sparc86x*) cpu_type=sparc arch=sparc86x ;; sparc*) cpu_type=sparc arch=sparclite ;; # ??? See tc-sparc.c. v850*) cpu_type=v850 ;; + xtensa*) cpu_type=xtensa arch=xtensa ;; *) cpu_type=${cpu} ;; esac @@ -2649,6 +2664,8 @@ EOF xstormy16-*-*) fmt=elf ;; + xtensa-*-*) fmt=elf ;; + z8k-*-coff | z8k-*-sim) fmt=coff ;; *-*-aout | *-*-scout) fmt=aout ;; @@ -2822,6 +2839,13 @@ EOF using_cgen=yes ;; + xtensa) + echo ${extra_objects} | grep -s "xtensa-relax.o" + if test $? -ne 0 ; then + extra_objects="$extra_objects xtensa-relax.o" + fi + ;; + *) ;; esac @@ -3201,7 +3225,7 @@ EOF # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3205: checking for $ac_word" >&5 +echo "configure:3229: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3231,7 +3255,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3235: checking for $ac_word" >&5 +echo "configure:3259: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3282,7 +3306,7 @@ fi # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3286: checking for $ac_word" >&5 +echo "configure:3310: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3314,7 +3338,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:3318: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:3342: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -3325,12 +3349,12 @@ cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF -#line 3329 "configure" +#line 3353 "configure" #include "confdefs.h" main(){return(0);} EOF -if { (eval echo configure:3334: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3358: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -3356,12 +3380,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:3360: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:3384: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:3365: checking whether we are using GNU C" >&5 +echo "configure:3389: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3370,7 +3394,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:3374: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:3398: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -3389,7 +3413,7 @@ ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:3393: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:3417: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3426,7 +3450,7 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3430: checking for $ac_word" >&5 +echo "configure:3454: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_YACC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3457,7 +3481,7 @@ done test -n "$YACC" || YACC="yacc" echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:3461: checking how to run the C preprocessor" >&5 +echo "configure:3485: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -3472,13 +3496,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3482: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3506: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -3489,13 +3513,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3499: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3523: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -3506,13 +3530,13 @@ else rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3516: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3540: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -3542,7 +3566,7 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3546: checking for $ac_word" >&5 +echo "configure:3570: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LEX'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3575,7 +3599,7 @@ test -n "$LEX" || LEX="$missing_dir/missing flex" # Extract the first word of "flex", so it can be a program name with args. set dummy flex; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3579: checking for $ac_word" >&5 +echo "configure:3603: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LEX'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3609,7 +3633,7 @@ then *) ac_lib=l ;; esac echo $ac_n "checking for yywrap in -l$ac_lib""... $ac_c" 1>&6 -echo "configure:3613: checking for yywrap in -l$ac_lib" >&5 +echo "configure:3637: checking for yywrap in -l$ac_lib" >&5 ac_lib_var=`echo $ac_lib'_'yywrap | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3617,7 +3641,7 @@ else ac_save_LIBS="$LIBS" LIBS="-l$ac_lib $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3656: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3651,7 +3675,7 @@ fi fi echo $ac_n "checking lex output file root""... $ac_c" 1>&6 -echo "configure:3655: checking lex output file root" >&5 +echo "configure:3679: checking lex output file root" >&5 if eval "test \"`echo '$''{'ac_cv_prog_lex_root'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3672,7 +3696,7 @@ echo "$ac_t""$ac_cv_prog_lex_root" 1>&6 LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root echo $ac_n "checking whether yytext is a pointer""... $ac_c" 1>&6 -echo "configure:3676: checking whether yytext is a pointer" >&5 +echo "configure:3700: checking whether yytext is a pointer" >&5 if eval "test \"`echo '$''{'ac_cv_prog_lex_yytext_pointer'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3684,14 +3708,14 @@ echo 'extern char *yytext;' >>$LEX_OUTPUT_ROOT.c ac_save_LIBS="$LIBS" LIBS="$LIBS $LEXLIB" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3719: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_prog_lex_yytext_pointer=yes else @@ -3717,7 +3741,7 @@ ALL_LINGUAS="fr tr es" # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3721: checking for $ac_word" >&5 +echo "configure:3745: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3745,12 +3769,12 @@ else fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:3749: checking for ANSI C header files" >&5 +echo "configure:3773: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -3758,7 +3782,7 @@ else #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3762: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3786: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -3775,7 +3799,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -3793,7 +3817,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -3814,7 +3838,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -3825,7 +3849,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:3829: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3853: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then : else @@ -3849,12 +3873,12 @@ EOF fi echo $ac_n "checking for working const""... $ac_c" 1>&6 -echo "configure:3853: checking for working const" >&5 +echo "configure:3877: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:3931: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -3924,21 +3948,21 @@ EOF fi echo $ac_n "checking for inline""... $ac_c" 1>&6 -echo "configure:3928: checking for inline" >&5 +echo "configure:3952: checking for inline" >&5 if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:3966: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_inline=$ac_kw; break else @@ -3964,12 +3988,12 @@ EOF esac echo $ac_n "checking for off_t""... $ac_c" 1>&6 -echo "configure:3968: checking for off_t" >&5 +echo "configure:3992: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3997,12 +4021,12 @@ EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 -echo "configure:4001: checking for size_t" >&5 +echo "configure:4025: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -4032,19 +4056,19 @@ fi # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:4036: checking for working alloca.h" >&5 +echo "configure:4060: checking for working alloca.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *p = alloca(2 * sizeof(int)); ; return 0; } EOF -if { (eval echo configure:4048: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4072: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_header_alloca_h=yes else @@ -4065,12 +4089,12 @@ EOF fi echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:4069: checking for alloca" >&5 +echo "configure:4093: checking for alloca" >&5 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4126: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_alloca_works=yes else @@ -4130,12 +4154,12 @@ EOF echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:4134: checking whether alloca needs Cray hooks" >&5 +echo "configure:4158: checking whether alloca needs Cray hooks" >&5 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&6 if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4164: checking for $ac_func" >&5 +echo "configure:4188: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4216: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4215,7 +4239,7 @@ done fi echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:4219: checking stack direction for C alloca" >&5 +echo "configure:4243: checking stack direction for C alloca" >&5 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4223,7 +4247,7 @@ else ac_cv_c_stack_direction=0 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4270: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_c_stack_direction=1 else @@ -4267,17 +4291,17 @@ for ac_hdr in unistd.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:4271: checking for $ac_hdr" >&5 +echo "configure:4295: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:4281: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:4305: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -4306,12 +4330,12 @@ done for ac_func in getpagesize do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4310: checking for $ac_func" >&5 +echo "configure:4334: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4362: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4359,7 +4383,7 @@ fi done echo $ac_n "checking for working mmap""... $ac_c" 1>&6 -echo "configure:4363: checking for working mmap" >&5 +echo "configure:4387: checking for working mmap" >&5 if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4367,7 +4391,7 @@ else ac_cv_func_mmap_fixed_mapped=no else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:4535: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_func_mmap_fixed_mapped=yes else @@ -4535,17 +4559,17 @@ unistd.h values.h sys/param.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:4539: checking for $ac_hdr" >&5 +echo "configure:4563: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:4549: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:4573: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -4575,12 +4599,12 @@ done __argz_count __argz_stringify __argz_next do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4579: checking for $ac_func" >&5 +echo "configure:4603: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4631: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4632,12 +4656,12 @@ done for ac_func in stpcpy do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4636: checking for $ac_func" >&5 +echo "configure:4660: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4688: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4694,19 +4718,19 @@ EOF if test $ac_cv_header_locale_h = yes; then echo $ac_n "checking for LC_MESSAGES""... $ac_c" 1>&6 -echo "configure:4698: checking for LC_MESSAGES" >&5 +echo "configure:4722: checking for LC_MESSAGES" >&5 if eval "test \"`echo '$''{'am_cv_val_LC_MESSAGES'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { return LC_MESSAGES ; return 0; } EOF -if { (eval echo configure:4710: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4734: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* am_cv_val_LC_MESSAGES=yes else @@ -4727,7 +4751,7 @@ EOF fi fi echo $ac_n "checking whether NLS is requested""... $ac_c" 1>&6 -echo "configure:4731: checking whether NLS is requested" >&5 +echo "configure:4755: checking whether NLS is requested" >&5 # Check whether --enable-nls or --disable-nls was given. if test "${enable_nls+set}" = set; then enableval="$enable_nls" @@ -4747,7 +4771,7 @@ fi EOF echo $ac_n "checking whether included gettext is requested""... $ac_c" 1>&6 -echo "configure:4751: checking whether included gettext is requested" >&5 +echo "configure:4775: checking whether included gettext is requested" >&5 # Check whether --with-included-gettext or --without-included-gettext was given. if test "${with_included_gettext+set}" = set; then withval="$with_included_gettext" @@ -4766,17 +4790,17 @@ fi ac_safe=`echo "libintl.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for libintl.h""... $ac_c" 1>&6 -echo "configure:4770: checking for libintl.h" >&5 +echo "configure:4794: checking for libintl.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:4780: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:4804: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -4793,19 +4817,19 @@ fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for gettext in libc""... $ac_c" 1>&6 -echo "configure:4797: checking for gettext in libc" >&5 +echo "configure:4821: checking for gettext in libc" >&5 if eval "test \"`echo '$''{'gt_cv_func_gettext_libc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { return (int) gettext ("") ; return 0; } EOF -if { (eval echo configure:4809: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4833: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gt_cv_func_gettext_libc=yes else @@ -4821,7 +4845,7 @@ echo "$ac_t""$gt_cv_func_gettext_libc" 1>&6 if test "$gt_cv_func_gettext_libc" != "yes"; then echo $ac_n "checking for bindtextdomain in -lintl""... $ac_c" 1>&6 -echo "configure:4825: checking for bindtextdomain in -lintl" >&5 +echo "configure:4849: checking for bindtextdomain in -lintl" >&5 ac_lib_var=`echo intl'_'bindtextdomain | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4829,7 +4853,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lintl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4868: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4856,19 +4880,19 @@ fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for gettext in libintl""... $ac_c" 1>&6 -echo "configure:4860: checking for gettext in libintl" >&5 +echo "configure:4884: checking for gettext in libintl" >&5 if eval "test \"`echo '$''{'gt_cv_func_gettext_libintl'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4896: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gt_cv_func_gettext_libintl=yes else @@ -4896,7 +4920,7 @@ EOF # Extract the first word of "msgfmt", so it can be a program name with args. set dummy msgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4900: checking for $ac_word" >&5 +echo "configure:4924: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4930,12 +4954,12 @@ fi for ac_func in dcgettext do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:4934: checking for $ac_func" >&5 +echo "configure:4958: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4986: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -4985,7 +5009,7 @@ done # Extract the first word of "gmsgfmt", so it can be a program name with args. set dummy gmsgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4989: checking for $ac_word" >&5 +echo "configure:5013: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_GMSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5021,7 +5045,7 @@ fi # Extract the first word of "xgettext", so it can be a program name with args. set dummy xgettext; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:5025: checking for $ac_word" >&5 +echo "configure:5049: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_XGETTEXT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5053,7 +5077,7 @@ else fi cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5089: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* CATOBJEXT=.gmo DATADIRNAME=share @@ -5093,7 +5117,7 @@ fi # Extract the first word of "msgfmt", so it can be a program name with args. set dummy msgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:5097: checking for $ac_word" >&5 +echo "configure:5121: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5127,7 +5151,7 @@ fi # Extract the first word of "gmsgfmt", so it can be a program name with args. set dummy gmsgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:5131: checking for $ac_word" >&5 +echo "configure:5155: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_GMSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5163,7 +5187,7 @@ fi # Extract the first word of "xgettext", so it can be a program name with args. set dummy xgettext; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:5167: checking for $ac_word" >&5 +echo "configure:5191: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_XGETTEXT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5253,7 +5277,7 @@ fi LINGUAS= else echo $ac_n "checking for catalogs to be installed""... $ac_c" 1>&6 -echo "configure:5257: checking for catalogs to be installed" >&5 +echo "configure:5281: checking for catalogs to be installed" >&5 NEW_LINGUAS= for lang in ${LINGUAS=$ALL_LINGUAS}; do case "$ALL_LINGUAS" in @@ -5281,17 +5305,17 @@ echo "configure:5257: checking for catalogs to be installed" >&5 if test "$CATOBJEXT" = ".cat"; then ac_safe=`echo "linux/version.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for linux/version.h""... $ac_c" 1>&6 -echo "configure:5285: checking for linux/version.h" >&5 +echo "configure:5309: checking for linux/version.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:5295: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:5319: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -5354,7 +5378,7 @@ fi echo $ac_n "checking whether to enable maintainer-specific portions of Makefiles""... $ac_c" 1>&6 -echo "configure:5358: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo "configure:5382: checking whether to enable maintainer-specific portions of Makefiles" >&5 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" @@ -5379,7 +5403,7 @@ fi echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 -echo "configure:5383: checking for executable suffix" >&5 +echo "configure:5407: checking for executable suffix" >&5 if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5389,7 +5413,7 @@ else rm -f conftest* echo 'int main () { return 0; }' > conftest.$ac_ext ac_cv_exeext= - if { (eval echo configure:5393: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + if { (eval echo configure:5417: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then for file in conftest.*; do case $file in *.c | *.o | *.obj | *.ilk | *.pdb) ;; @@ -5414,17 +5438,17 @@ for ac_hdr in string.h stdlib.h memory.h strings.h unistd.h stdarg.h varargs.h e do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:5418: checking for $ac_hdr" >&5 +echo "configure:5442: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:5428: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:5452: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -5454,7 +5478,7 @@ done # Put this here so that autoconf's "cross-compiling" message doesn't confuse # people who are not cross-compiling but are compiling cross-assemblers. echo $ac_n "checking whether compiling a cross-assembler""... $ac_c" 1>&6 -echo "configure:5458: checking whether compiling a cross-assembler" >&5 +echo "configure:5482: checking whether compiling a cross-assembler" >&5 if test "${host}" = "${target}"; then cross_gas=no else @@ -5469,19 +5493,19 @@ echo "$ac_t""$cross_gas" 1>&6 # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:5473: checking for working alloca.h" >&5 +echo "configure:5497: checking for working alloca.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *p = alloca(2 * sizeof(int)); ; return 0; } EOF -if { (eval echo configure:5485: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5509: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_header_alloca_h=yes else @@ -5502,12 +5526,12 @@ EOF fi echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:5506: checking for alloca" >&5 +echo "configure:5530: checking for alloca" >&5 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5563: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_alloca_works=yes else @@ -5567,12 +5591,12 @@ EOF echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:5571: checking whether alloca needs Cray hooks" >&5 +echo "configure:5595: checking whether alloca needs Cray hooks" >&5 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&6 if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:5601: checking for $ac_func" >&5 +echo "configure:5625: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5653: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -5652,7 +5676,7 @@ done fi echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:5656: checking stack direction for C alloca" >&5 +echo "configure:5680: checking stack direction for C alloca" >&5 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -5660,7 +5684,7 @@ else ac_cv_c_stack_direction=0 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:5707: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_c_stack_direction=1 else @@ -5701,21 +5725,21 @@ EOF fi echo $ac_n "checking for inline""... $ac_c" 1>&6 -echo "configure:5705: checking for inline" >&5 +echo "configure:5729: checking for inline" >&5 if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:5743: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_inline=$ac_kw; break else @@ -5745,12 +5769,12 @@ esac for ac_func in unlink remove do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:5749: checking for $ac_func" >&5 +echo "configure:5773: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5801: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -5802,12 +5826,12 @@ done for ac_func in sbrk do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:5806: checking for $ac_func" >&5 +echo "configure:5830: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5858: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -5865,7 +5889,7 @@ case $host in ;; *-ncr-sysv4.3*) echo $ac_n "checking for _mwvalidcheckl in -lmw""... $ac_c" 1>&6 -echo "configure:5869: checking for _mwvalidcheckl in -lmw" >&5 +echo "configure:5893: checking for _mwvalidcheckl in -lmw" >&5 ac_lib_var=`echo mw'_'_mwvalidcheckl | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -5873,7 +5897,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lmw $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5912: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -5905,7 +5929,7 @@ else fi echo $ac_n "checking for main in -lm""... $ac_c" 1>&6 -echo "configure:5909: checking for main in -lm" >&5 +echo "configure:5933: checking for main in -lm" >&5 ac_lib_var=`echo m'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -5913,14 +5937,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lm $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5948: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -5943,7 +5967,7 @@ fi ;; *) echo $ac_n "checking for main in -lm""... $ac_c" 1>&6 -echo "configure:5947: checking for main in -lm" >&5 +echo "configure:5971: checking for main in -lm" >&5 ac_lib_var=`echo m'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -5951,14 +5975,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lm $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:5986: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -5989,12 +6013,12 @@ esac # enough, but on some of those systems, the assert macro relies on requoting # working properly! echo $ac_n "checking for working assert macro""... $ac_c" 1>&6 -echo "configure:5993: checking for working assert macro" >&5 +echo "configure:6017: checking for working assert macro" >&5 if eval "test \"`echo '$''{'gas_cv_assert_ok'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -6010,7 +6034,7 @@ assert (a == b ; return 0; } EOF -if { (eval echo configure:6014: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6038: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_assert_ok=yes else @@ -6051,12 +6075,12 @@ gas_test_headers=" " echo $ac_n "checking whether declaration is required for strstr""... $ac_c" 1>&6 -echo "configure:6055: checking whether declaration is required for strstr" >&5 +echo "configure:6079: checking whether declaration is required for strstr" >&5 if eval "test \"`echo '$''{'gas_cv_decl_needed_strstr'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6095: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_decl_needed_strstr=no else @@ -6088,12 +6112,12 @@ fi echo $ac_n "checking whether declaration is required for malloc""... $ac_c" 1>&6 -echo "configure:6092: checking whether declaration is required for malloc" >&5 +echo "configure:6116: checking whether declaration is required for malloc" >&5 if eval "test \"`echo '$''{'gas_cv_decl_needed_malloc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6132: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_decl_needed_malloc=no else @@ -6125,12 +6149,12 @@ fi echo $ac_n "checking whether declaration is required for free""... $ac_c" 1>&6 -echo "configure:6129: checking whether declaration is required for free" >&5 +echo "configure:6153: checking whether declaration is required for free" >&5 if eval "test \"`echo '$''{'gas_cv_decl_needed_free'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6169: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_decl_needed_free=no else @@ -6162,12 +6186,12 @@ fi echo $ac_n "checking whether declaration is required for sbrk""... $ac_c" 1>&6 -echo "configure:6166: checking whether declaration is required for sbrk" >&5 +echo "configure:6190: checking whether declaration is required for sbrk" >&5 if eval "test \"`echo '$''{'gas_cv_decl_needed_sbrk'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6206: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_decl_needed_sbrk=no else @@ -6199,12 +6223,12 @@ fi echo $ac_n "checking whether declaration is required for environ""... $ac_c" 1>&6 -echo "configure:6203: checking whether declaration is required for environ" >&5 +echo "configure:6227: checking whether declaration is required for environ" >&5 if eval "test \"`echo '$''{'gas_cv_decl_needed_environ'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6243: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_decl_needed_environ=no else @@ -6239,12 +6263,12 @@ fi # for it? echo $ac_n "checking whether declaration is required for errno""... $ac_c" 1>&6 -echo "configure:6243: checking whether declaration is required for errno" >&5 +echo "configure:6267: checking whether declaration is required for errno" >&5 if eval "test \"`echo '$''{'gas_cv_decl_needed_errno'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:6287: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gas_cv_decl_needed_errno=no else diff --git a/gas/configure.in b/gas/configure.in index 146822db9e0..cb3e2820f07 100644 --- a/gas/configure.in +++ b/gas/configure.in @@ -153,6 +153,7 @@ changequote([,])dnl sparc86x*) cpu_type=sparc arch=sparc86x ;; sparc*) cpu_type=sparc arch=sparclite ;; # ??? See tc-sparc.c. v850*) cpu_type=v850 ;; + xtensa*) cpu_type=xtensa arch=xtensa ;; *) cpu_type=${cpu} ;; esac @@ -482,6 +483,8 @@ changequote([,])dnl xstormy16-*-*) fmt=elf ;; + xtensa-*-*) fmt=elf ;; + z8k-*-coff | z8k-*-sim) fmt=coff ;; *-*-aout | *-*-scout) fmt=aout ;; @@ -648,6 +651,13 @@ changequote([,])dnl using_cgen=yes ;; + xtensa) + echo ${extra_objects} | grep -s "xtensa-relax.o" + if test $? -ne 0 ; then + extra_objects="$extra_objects xtensa-relax.o" + fi + ;; + *) ;; esac diff --git a/gas/doc/Makefile.am b/gas/doc/Makefile.am index 5dbab7af383..1e8acbccb80 100644 --- a/gas/doc/Makefile.am +++ b/gas/doc/Makefile.am @@ -55,6 +55,7 @@ CPU_DOCS = \ c-tic54x.texi \ c-vax.texi \ c-v850.texi \ + c-xtensa.texi \ c-z8k.texi gasver.texi: Makefile diff --git a/gas/doc/Makefile.in b/gas/doc/Makefile.in index 576c5e60e4a..6b8a4e4da9b 100644 --- a/gas/doc/Makefile.in +++ b/gas/doc/Makefile.in @@ -167,6 +167,7 @@ CPU_DOCS = \ c-tic54x.texi \ c-vax.texi \ c-v850.texi \ + c-xtensa.texi \ c-z8k.texi diff --git a/gas/doc/all.texi b/gas/doc/all.texi index 30f02db86e2..4e302cea1f6 100644 --- a/gas/doc/all.texi +++ b/gas/doc/all.texi @@ -58,6 +58,7 @@ @set V850 @set VAX @set VXWORKS +@set XTENSA @set Z8000 @c Does this version of the assembler use the difference-table kluge? diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index 41867f6b10b..5f95c966e86 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -59,6 +59,7 @@ @set TIC54X @set V850 @set VAX +@set XTENSA @end ifset @c man end @c common OR combinations of conditions @@ -449,6 +450,13 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}. @ifset Z8000 @c Z8000 has no machine-dependent assembler options @end ifset +@ifset XTENSA + +@emph{Target Xtensa options:} + [@b{--[no-]density}] [@b{--[no-]relax}] [@b{--[no-]generics}] + [@b{--[no-]text-section-literals}] + [@b{--[no-]target-align}] [@b{--[no-]longcalls}] +@end ifset @c man end @end smallexample @@ -1057,6 +1065,45 @@ Assemble for a little endian target. See the info pages for documentation of the MMIX-specific options. @end ifset +@ifset XTENSA +The following options are available when @value{AS} is configured for +an Xtensa processor. + +@table @gcctabopt +@item --density | --no-density +Enable or disable use of instructions from the Xtensa code density +option. This is enabled by default when the Xtensa processor supports +the code density option. + +@item --relax | --no-relax +Enable or disable instruction relaxation. This is enabled by default. +Note: In the current implementation, these options also control whether +assembler optimizations are performed, making these options equivalent +to @option{--generics} and @option{--no-generics}. + +@item --generics | --no-generics +Enable or disable all assembler transformations of Xtensa instructions. +The default is @option{--generics}; +@option{--no-generics} should be used only in the rare cases when the +instructions must be exactly as specified in the assembly source. + +@item --text-section-literals | --no-text-section-literals +With @option{--text-@-section-@-literals}, literal pools are interspersed +in the text section. The default is +@option{--no-@-text-@-section-@-literals}, which places literals in a +separate section in the output file. + +@item --target-align | --no-target-align +Enable or disable automatic alignment to reduce branch penalties at the +expense of some code density. The default is @option{--target-@-align}. + +@item --longcalls | --no-longcalls +Enable or disable transformation of call instructions to allow calls +across a greater range of addresses. The default is +@option{--no-@-longcalls}. +@end table +@end ifset + @c man end @menu @@ -2068,6 +2115,9 @@ is considered a comment and is ignored. The line comment character is @ifset V850 @samp{#} on the V850; @end ifset +@ifset XTENSA +@samp{#} for Xtensa systems; +@end ifset see @ref{Machine Dependencies}. @refill @c FIXME What about i860? @@ -3834,7 +3884,7 @@ required alignment; this can be useful if you want the alignment to be filled with no-op instructions when appropriate. The way the required alignment is specified varies from system to system. -For the a29k, hppa, m68k, m88k, w65, sparc, and Hitachi SH, and i386 using ELF +For the a29k, hppa, m68k, m88k, w65, sparc, Xtensa, and Hitachi SH, and i386 using ELF format, the first expression is the alignment request in bytes. For example @samp{.align 8} advances @@ -5865,6 +5915,9 @@ subject, see the hardware manufacturer's manual. @ifset V850 * V850-Dependent:: V850 Dependent Features @end ifset +@ifset XTENSA +* Xtensa-Dependent:: Xtensa Dependent Features +@end ifset @ifset Z8000 * Z8000-Dependent:: Z8000 Dependent Features @end ifset @@ -6036,6 +6089,10 @@ family. @include c-v850.texi @end ifset +@ifset XTENSA +@include c-xtensa.texi +@end ifset + @ifset GENERIC @c reverse effect of @down at top of generic Machine-Dep chapter @raisesections @@ -6330,6 +6387,9 @@ support for openVMS/Alpha. Timothy Wall, Michael Hayes, and Greg Smart contributed to the various tic* flavors. +David Heine, Sterling Augustine, Bob Wilson and John Ruttenberg from Tensilica, +Inc. added support for Xtensa processors. + Several engineers at Cygnus Support have also provided many small bug fixes and configuration enhancements. diff --git a/gas/doc/c-xtensa.texi b/gas/doc/c-xtensa.texi new file mode 100644 index 00000000000..69a3d819af8 --- /dev/null +++ b/gas/doc/c-xtensa.texi @@ -0,0 +1,740 @@ +@c Copyright (C) 2002 Free Software Foundation, Inc. +@c This is part of the GAS manual. +@c For copying conditions, see the file as.texinfo. +@c +@ifset GENERIC +@page +@node Xtensa-Dependent +@chapter Xtensa Dependent Features +@end ifset +@ifclear GENERIC +@node Machine Dependencies +@chapter Xtensa Dependent Features +@end ifclear + +@cindex Xtensa architecture +This chapter covers features of the @sc{gnu} assembler that are specific +to the Xtensa architecture. For details about the Xtensa instruction +set, please consult the @cite{Xtensa Instruction Set Architecture (ISA) +Reference Manual}. + +@menu +* Xtensa Options:: Command-line Options. +* Xtensa Syntax:: Assembler Syntax for Xtensa Processors. +* Xtensa Optimizations:: Assembler Optimizations. +* Xtensa Relaxation:: Other Automatic Transformations. +* Xtensa Directives:: Directives for Xtensa Processors. +@end menu + +@node Xtensa Options +@section Command Line Options + +The Xtensa version of the @sc{gnu} assembler supports these +special options: + +@table @code +@item --density | --no-density +@kindex --density +@kindex --no-density +@cindex Xtensa density option +@cindex density option, Xtensa +Enable or disable use of the Xtensa code density option (16-bit +instructions). @xref{Density Instructions, ,Using Density +Instructions}. If the processor is configured with the density option, +this is enabled by default; otherwise, it is always disabled. + +@item --relax | --no-relax +@kindex --relax +@kindex --no-relax +Enable or disable relaxation of instructions with immediate operands +that are outside the legal range for the instructions. @xref{Xtensa +Relaxation, ,Xtensa Relaxation}. The default is @samp{--relax} and this +default should almost always be used. If relaxation is disabled with +@samp{--no-relax}, instruction operands that are out of range will cause +errors. Note: In the current implementation, these options also control +whether assembler optimizations are performed, making these options +equivalent to @samp{--generics} and @samp{--no-generics}. + +@item --generics | --no-generics +@kindex --generics +@kindex --no-generics +Enable or disable all assembler transformations of Xtensa instructions, +including both relaxation and optimization. The default is +@samp{--generics}; @samp{--no-generics} should only be used in the rare +cases when the instructions must be exactly as specified in the assembly +source. +@c The @samp{--no-generics} option is like @samp{--no-relax} +@c except that it also disables assembler optimizations (@pxref{Xtensa +@c Optimizations}). +As with @samp{--no-relax}, using @samp{--no-generics} +causes out of range instruction operands to be errors. + +@item --text-section-literals | --no-text-section-literals +@kindex --text-section-literals +@kindex --no-text-section-literals +Control the treatment of literal pools. The default is +@samp{--no-@-text-@-section-@-literals}, which places literals in a +separate section in the output file. This allows the literal pool to be +placed in a data RAM/ROM, and it also allows the linker to combine literal +pools from separate object files to remove redundant literals and +improve code size. With @samp{--text-@-section-@-literals}, the +literals are interspersed in the text section in order to keep them as +close as possible to their references. This may be necessary for large +assembly files. + +@item --target-align | --no-target-align +@kindex --target-align +@kindex --no-target-align +Enable or disable automatic alignment to reduce branch penalties at some +expense in code size. @xref{Xtensa Automatic Alignment, ,Automatic +Instruction Alignment}. This optimization is enabled by default. Note +that the assembler will always align instructions like @code{LOOP} that +have fixed alignment requirements. + +@item --longcalls | --no-longcalls +@kindex --longcalls +@kindex --no-longcalls +Enable or disable transformation of call instructions to allow calls +across a greater range of addresses. @xref{Xtensa Call Relaxation, +,Function Call Relaxation}. This option should be used when call +targets can potentially be out of range, but it degrades both code size +and performance. The default is @samp{--no-@-longcalls}. +@end table + +@node Xtensa Syntax +@section Assembler Syntax +@cindex syntax, Xtensa assembler +@cindex Xtensa assembler syntax + +Block comments are delimited by @samp{/*} and @samp{*/}. End of line +comments may be introduced with either @samp{#} or @samp{//}. + +Instructions consist of a leading opcode or macro name followed by +whitespace and an optional comma-separated list of operands: + +@smallexample +@var{opcode} [@var{operand},@dots{}] +@end smallexample + +Instructions must be separated by a newline or semicolon. + +@menu +* Xtensa Opcodes:: Opcode Naming Conventions. +* Xtensa Registers:: Register Naming. +@end menu + +@node Xtensa Opcodes +@subsection Opcode Names +@cindex Xtensa opcode names +@cindex opcode names, Xtenxa + +See the @cite{Xtensa Instruction Set Architecture (ISA) Reference +Manual} for a complete list of opcodes and descriptions of their +semantics. + +@cindex generic opcodes +@cindex specific opcodes +@cindex _ opcode prefix +The Xtensa assembler distinguishes between @dfn{generic} and +@dfn{specific} opcodes. Specific opcodes correspond directly to Xtensa +machine instructions. Prefixing an opcode with an underscore character +(@samp{_}) identifies it as a specific opcode. Opcodes without a +leading underscore are generic, which means the assembler is required to +preserve their semantics but may not translate them directly to the +specific opcodes with the same names. Instead, the assembler may +optimize a generic opcode and select a better instruction to use in its +place (@pxref{Xtensa Optimizations, ,Xtensa Optimizations}), or the +assembler may relax the instruction to handle operands that are out of +range for the corresponding specific opcode (@pxref{Xtensa Relaxation, +,Xtensa Relaxation}). + +Only use specific opcodes when it is essential to select +the exact machine instructions produced by the assembler. +Using specific opcodes unnecessarily only makes the code less +efficient, by disabling assembler optimization, and less flexible, by +disabling relaxation. + +Note that this special handling of underscore prefixes only applies to +Xtensa opcodes, not to either built-in macros or user-defined macros. +When an underscore prefix is used with a macro (e.g., @code{_NOP}), it +refers to a different macro. The assembler generally provides built-in +macros both with and without the underscore prefix, where the underscore +versions behave as if the underscore carries through to the instructions +in the macros. For example, @code{_NOP} expands to @code{_OR a1,a1,a1}. + +The underscore prefix only applies to individual instructions, not to +series of instructions. For example, if a series of instructions have +underscore prefixes, the assembler will not transform the individual +instructions, but it may insert other instructions between them (e.g., +to align a @code{LOOP} instruction). To prevent the assembler from +modifying a series of instructions as a whole, use the +@code{no-generics} directive. @xref{Generics Directive, ,generics}. + +@node Xtensa Registers +@subsection Register Names +@cindex Xtensa register names +@cindex register names, Xtensa +@cindex sp register + +An initial @samp{$} character is optional in all register names. +General purpose registers are named @samp{a0}@dots{}@samp{a15}. Additional +registers may be added by processor configuration options. In +particular, the @sc{mac16} option adds a @sc{mr} register bank. Its +registers are named @samp{m0}@dots{}@samp{m3}. + +As a special feature, @samp{sp} is also supported as a synonym for +@samp{a1}. + +@node Xtensa Optimizations +@section Xtensa Optimizations +@cindex optimizations + +The optimizations currently supported by @code{@value{AS}} are +generation of density instructions where appropriate and automatic +branch target alignment. + +@menu +* Density Instructions:: Using Density Instructions. +* Xtensa Automatic Alignment:: Automatic Instruction Alignment. +@end menu + +@node Density Instructions +@subsection Using Density Instructions +@cindex density instructions + +The Xtensa instruction set has a code density option that provides +16-bit versions of some of the most commonly used opcodes. Use of these +opcodes can significantly reduce code size. When possible, the +assembler automatically translates generic instructions from the core +Xtensa instruction set into equivalent instructions from the Xtensa code +density option. This translation can be disabled by using specific +opcodes (@pxref{Xtensa Opcodes, ,Opcode Names}), by using the +@samp{--no-density} command-line option (@pxref{Xtensa Options, ,Command +Line Options}), or by using the @code{no-density} directive +(@pxref{Density Directive, ,density}). + +It is a good idea @emph{not} to use the density instuctions directly. +The assembler will automatically select dense instructions where +possible. If you later need to avoid using the code density option, you +can disable it in the assembler without having to modify the code. + +@node Xtensa Automatic Alignment +@subsection Automatic Instruction Alignment +@cindex alignment of @code{LOOP} instructions +@cindex alignment of @code{ENTRY} instructions +@cindex alignment of branch targets +@cindex @code{LOOP} instructions, alignment +@cindex @code{ENTRY} instructions, alignment +@cindex branch target alignment + +The Xtensa assembler will automatically align certain instructions, both +to optimize performance and to satisfy architectural requirements. + +When the @code{--target-@-align} command-line option is enabled +(@pxref{Xtensa Options, ,Command Line Options}), the assembler attempts +to widen density instructions preceding a branch target so that the +target instruction does not cross a 4-byte boundary. Similarly, the +assembler also attempts to align each instruction following a call +instruction. If there are not enough preceding safe density +instructions to align a target, no widening will be performed. This +alignment has the potential to reduce branch penalties at some expense +in code size. The assembler will not attempt to align labels with the +prefixes @code{.Ln} and @code{.LM}, since these labels are used for +debugging information and are not typically branch targets. + +The @code{LOOP} family of instructions must be aligned on either a 1 or +2 mod 4 byte boundary. The assembler knows about this restriction and +inserts the minimal number of 2 or 3 byte no-op instructions +to satisfy it. When no-op instructions are added, any label immediately +preceding the original loop will be moved in order to refer to the loop +instruction, not the newly generated no-op instruction. + +Similarly, the @code{ENTRY} instruction must be aligned on a 0 mod 4 +byte boundary. The assembler satisfies this requirement by inserting +zero bytes when required. In addition, labels immediately preceding the +@code{ENTRY} instruction will be moved to the newly aligned instruction +location. + +@node Xtensa Relaxation +@section Xtensa Relaxation +@cindex relaxation + +When an instruction operand is outside the range allowed for that +particular instruction field, @code{@value{AS}} can transform the code +to use a functionally-equivalent instruction or sequence of +instructions. This process is known as @dfn{relaxation}. This is +typically done for branch instructions because the distance of the +branch targets is not known until assembly-time. The Xtensa assembler +offers branch relaxation and also extends this concept to function +calls, @code{MOVI} instructions and other instructions with immediate +fields. + +@menu +* Xtensa Branch Relaxation:: Relaxation of Branches. +* Xtensa Call Relaxation:: Relaxation of Function Calls. +* Xtensa Immediate Relaxation:: Relaxation of other Immediate Fields. +@end menu + +@node Xtensa Branch Relaxation +@subsection Conditional Branch Relaxation +@cindex relaxation of branch instructions +@cindex branch instructions, relaxation + +When the target of a branch is too far away from the branch itself, +i.e., when the offset from the branch to the target is too large to fit +in the immediate field of the branch instruction, it may be necessary to +replace the branch with a branch around a jump. For example, + +@smallexample + beqz a2, L +@end smallexample + +may result in: + +@smallexample + bnez.n a2, M + j L +M: +@end smallexample + +(The @code{BNEZ.N} instruction would be used in this example only if the +density option is available. Otherwise, @code{BNEZ} would be used.) + +@node Xtensa Call Relaxation +@subsection Function Call Relaxation +@cindex relaxation of call instructions +@cindex call instructions, relaxation + +Function calls may require relaxation because the Xtensa immediate call +instructions (@code{CALL0}, @code{CALL4}, @code{CALL8} and +@code{CALL12}) provide a PC-relative offset of only 512 Kbytes in either +direction. For larger programs, it may be necessary to use indirect +calls (@code{CALLX0}, @code{CALLX4}, @code{CALLX8} and @code{CALLX12}) +where the target address is specified in a register. The Xtensa +assembler can automatically relax immediate call instructions into +indirect call instructions. This relaxation is done by loading the +address of the called function into the callee's return address register +and then using a @code{CALLX} instruction. So, for example: + +@smallexample + call8 func +@end smallexample + +might be relaxed to: + +@smallexample + .literal .L1, func + l32r a8, .L1 + callx8 a8 +@end smallexample + +Because the addresses of targets of function calls are not generally +known until link-time, the assembler must assume the worst and relax all +the calls to functions in other source files, not just those that really +will be out of range. The linker can recognize calls that were +unnecessarily relaxed, but it can only partially remove the overhead +introduced by the assembler. + +Call relaxation has a negative effect +on both code size and performance, so this relaxation is disabled by +default. If a program is too large and some of the calls are out of +range, function call relaxation can be enabled using the +@samp{--longcalls} command-line option or the @code{longcalls} directive +(@pxref{Longcalls Directive, ,longcalls}). + +@node Xtensa Immediate Relaxation +@subsection Other Immediate Field Relaxation +@cindex immediate fields, relaxation +@cindex relaxation of immediate fields + +@cindex @code{MOVI} instructions, relaxation +@cindex relaxation of @code{MOVI} instructions +The @code{MOVI} machine instruction can only materialize values in the +range from -2048 to 2047. Values outside this range are best +materalized with @code{L32R} instructions. Thus: + +@smallexample + movi a0, 100000 +@end smallexample + +is assembled into the following machine code: + +@smallexample + .literal .L1, 100000 + l32r a0, .L1 +@end smallexample + +@cindex @code{L8UI} instructions, relaxation +@cindex @code{L16SI} instructions, relaxation +@cindex @code{L16UI} instructions, relaxation +@cindex @code{L32I} instructions, relaxation +@cindex relaxation of @code{L8UI} instructions +@cindex relaxation of @code{L16SI} instructions +@cindex relaxation of @code{L16UI} instructions +@cindex relaxation of @code{L32I} instructions +The @code{L8UI} machine instruction can only be used with immediate +offsets in the range from 0 to 255. The @code{L16SI} and @code{L16UI} +machine instructions can only be used with offsets from 0 to 510. The +@code{L32I} machine instruction can only be used with offsets from 0 to +1020. A load offset outside these ranges can be materalized with +an @code{L32R} instruction if the destination register of the load +is different than the source address register. For example: + +@smallexample + l32i a1, a0, 2040 +@end smallexample + +is translated to: + +@smallexample + .literal .L1, 2040 + l32r a1, .L1 + addi a1, a0, a1 + l32i a1, a1, 0 +@end smallexample + +@noindent +If the load destination and source address register are the same, an +out-of-range offset causes an error. + +@cindex @code{ADDI} instructions, relaxation +@cindex relaxation of @code{ADDI} instructions +The Xtensa @code{ADDI} instruction only allows immediate operands in the +range from -128 to 127. There are a number of alternate instruction +sequences for the generic @code{ADDI} operation. First, if the +immediate is 0, the @code{ADDI} will be turned into a @code{MOV.N} +instruction (or the equivalent @code{OR} instruction if the code density +option is not available). If the @code{ADDI} immediate is outside of +the range -128 to 127, but inside the range -32896 to 32639, an +@code{ADDMI} instruction or @code{ADDMI}/@code{ADDI} sequence will be +used. Finally, if the immediate is outside of this range and a free +register is available, an @code{L32R}/@code{ADD} sequence will be used +with a literal allocated from the literal pool. + +For example: + +@smallexample + addi a5, a6, 0 + addi a5, a6, 512 + addi a5, a6, 513 + addi a5, a6, 50000 +@end smallexample + +is assembled into the following: + +@smallexample + .literal .L1, 50000 + mov.n a5, a6 + addmi a5, a6, 0x200 + addmi a5, a6, 0x200 + addi a5, a5, 1 + l32r a5, .L1 + add a5, a6, a5 +@end smallexample + +@node Xtensa Directives +@section Directives +@cindex Xtensa directives +@cindex directives, Xtensa + +The Xtensa assember supports a region-based directive syntax: + +@smallexample + .begin @var{directive} [@var{options}] + @dots{} + .end @var{directive} +@end smallexample + +All the Xtensa-specific directives that apply to a region of code use +this syntax. + +The directive applies to code between the @code{.begin} and the +@code{.end}. The state of the option after the @code{.end} reverts to +what it was before the @code{.begin}. +A nested @code{.begin}/@code{.end} region can further +change the state of the directive without having to be aware of its +outer state. For example, consider: + +@smallexample + .begin no-density +L: add a0, a1, a2 + .begin density +M: add a0, a1, a2 + .end density +N: add a0, a1, a2 + .end no-density +@end smallexample + +The generic @code{ADD} opcodes at @code{L} and @code{N} in the outer +@code{no-density} region both result in @code{ADD} machine instructions, +but the assembler selects an @code{ADD.N} instruction for the generic +@code{ADD} at @code{M} in the inner @code{density} region. + +The advantage of this style is that it works well inside macros which can +preserve the context of their callers. + +@cindex precedence of directives +@cindex directives, precedence +When command-line options and assembler directives are used at the same +time and conflict, the one that overrides a default behavior takes +precedence over one that is the same as the default. For example, if +the code density option is available, the default is to select density +instructions whenever possible. So, if the above is assembled with the +@samp{--no-density} flag, which overrides the default, all the generic +@code{ADD} instructions result in @code{ADD} machine instructions. If +assembled with the @samp{--density} flag, which is already the default, +the @code{no-density} directive takes precedence and only one of +the generic @code{ADD} instructions is optimized to be a @code{ADD.N} +machine instruction. An underscore prefix identifying a specific opcode +always takes precedence over directives and command-line flags. + +The following directives are available: +@menu +* Density Directive:: Disable Use of Density Instructions. +* Relax Directive:: Disable Assembler Relaxation. +* Longcalls Directive:: Use Indirect Calls for Greater Range. +* Generics Directive:: Disable All Assembler Transformations. +* Literal Directive:: Intermix Literals with Instructions. +* Literal Position Directive:: Specify Inline Literal Pool Locations. +* Literal Prefix Directive:: Specify Literal Section Name Prefix. +* Freeregs Directive:: List Registers Available for Assembler Use. +* Frame Directive:: Describe a stack frame. +@end menu + +@node Density Directive +@subsection density +@cindex @code{density} directive +@cindex @code{no-density} directive + +The @code{density} and @code{no-density} directives enable or disable +optimization of generic instructions into density instructions within +the region. @xref{Density Instructions, ,Using Density Instructions}. + +@smallexample + .begin [no-]density + .end [no-]density +@end smallexample + +This optimization is enabled by default unless the Xtensa configuration +does not support the code density option or the @samp{--no-density} +command-line option was specified. + +@node Relax Directive +@subsection relax +@cindex @code{relax} directive +@cindex @code{no-relax} directive + +The @code{relax} directive enables or disables relaxation +within the region. @xref{Xtensa Relaxation, ,Xtensa Relaxation}. +Note: In the current implementation, these directives also control +whether assembler optimizations are performed, making them equivalent to +the @code{generics} and @code{no-generics} directives. + +@smallexample + .begin [no-]relax + .end [no-]relax +@end smallexample + +Relaxation is enabled by default unless the @samp{--no-relax} +command-line option was specified. + +@node Longcalls Directive +@subsection longcalls +@cindex @code{longcalls} directive +@cindex @code{no-longcalls} directive + +The @code{longcalls} directive enables or disables function call +relaxation. @xref{Xtensa Call Relaxation, ,Function Call Relaxation}. + +@smallexample + .begin [no-]longcalls + .end [no-]longcalls +@end smallexample + +Call relaxation is disabled by default unless the @samp{--longcalls} +command-line option is specified. + +@node Generics Directive +@subsection generics +@cindex @code{generics} directive +@cindex @code{no-generics} directive + +This directive enables or disables all assembler transformation, +including relaxation (@pxref{Xtensa Relaxation, ,Xtensa Relaxation}) and +optimization (@pxref{Xtensa Optimizations, ,Xtensa Optimizations}). + +@smallexample + .begin [no-]generics + .end [no-]generics +@end smallexample + +Disabling generics is roughly equivalent to adding an underscore prefix +to every opcode within the region, so that every opcode is treated as a +specific opcode. @xref{Xtensa Opcodes, ,Opcode Names}. In the current +implementation of @code{@value{AS}}, built-in macros are also disabled +within a @code{no-generics} region. + +@node Literal Directive +@subsection literal +@cindex @code{literal} directive + +The @code{.literal} directive is used to define literal pool data, i.e., +read-only 32-bit data accessed via @code{L32R} instructions. + +@smallexample + .literal @var{label}, @var{value}[, @var{value}@dots{}] +@end smallexample + +This directive is similar to the standard @code{.word} directive, except +that the actual location of the literal data is determined by the +assembler and linker, not by the position of the @code{.literal} +directive. Using this directive gives the assembler freedom to locate +the literal data in the most appropriate place and possibly to combine +identical literals. For example, the code: + +@smallexample + entry sp, 40 + .literal .L1, sym + l32r a4, .L1 +@end smallexample + +can be used to load a pointer to the symbol @code{sym} into register +@code{a4}. The value of @code{sym} will not be placed between the +@code{ENTRY} and @code{L32R} instructions; instead, the assembler puts +the data in a literal pool. + +By default literal pools are placed in a separate section; however, when +using the @samp{--text-@-section-@-literals} option (@pxref{Xtensa +Options, ,Command Line Options}), the literal pools are placed in the +current section. These text section literal pools are created +automatically before @code{ENTRY} instructions and manually after +@samp{.literal_position} directives (@pxref{Literal Position Directive, +,literal_position}). If there are no preceding @code{ENTRY} +instructions or @code{.literal_position} directives, the assembler will +print a warning and place the literal pool at the beginning of the +current section. In such cases, explicit @code{.literal_position} +directives should be used to place the literal pools. + +@node Literal Position Directive +@subsection literal_position +@cindex @code{literal_position} directive + +When using @samp{--text-@-section-@-literals} to place literals inline +in the section being assembled, the @code{.literal_position} directive +can be used to mark a potential location for a literal pool. + +@smallexample + .literal_position +@end smallexample + +The @code{.literal_position} directive is ignored when the +@samp{--text-@-section-@-literals} option is not used. + +The assembler will automatically place text section literal pools +before @code{ENTRY} instructions, so the @code{.literal_position} +directive is only needed to specify some other location for a literal +pool. You may need to add an explicit jump instruction to skip over an +inline literal pool. + +For example, an interrupt vector does not begin with an @code{ENTRY} +instruction so the assembler will be unable to automatically find a good +place to put a literal pool. Moreover, the code for the interrupt +vector must be at a specific starting address, so the literal pool +cannot come before the start of the code. The literal pool for the +vector must be explicitly positioned in the middle of the vector (before +any uses of the literals, of course). The @code{.literal_position} +directive can be used to do this. In the following code, the literal +for @samp{M} will automatically be aligned correctly and is placed after +the unconditional jump. + +@smallexample + .global M +code_start: + j continue + .literal_position + .align 4 +continue: + movi a4, M +@end smallexample + +@node Literal Prefix Directive +@subsection literal_prefix +@cindex @code{literal_prefix} directive + +The @code{literal_prefix} directive allows you to specify different +sections to hold literals from different portions of an assembly file. +With this directive, a single assembly file can be used to generate code +into multiple sections, including literals generated by the assembler. + +@smallexample + .begin literal_prefix [@var{name}] + .end literal_prefix +@end smallexample + +For the code inside the delimited region, the assembler puts literals in +the section @code{@var{name}.literal}. If this section does not yet +exist, the assembler creates it. The @var{name} parameter is +optional. If @var{name} is not specified, the literal prefix is set to +the ``default'' for the file. This default is usually @code{.literal} +but can be changed with the @samp{--rename-section} command-line +argument. + +@node Freeregs Directive +@subsection freeregs +@cindex @code{freeregs} directive + +This directive tells the assembler that the given registers are unused +in the region. + +@smallexample + .begin freeregs @var{ri}[,@var{ri}@dots{}] + .end freeregs +@end smallexample + +This allows the assembler to use these registers for relaxations or +optimizations. (They are actually only for relaxations at present, but +the possibility of optimizations exists in the future.) + +Nested @code{freeregs} directives can be used to add additional registers +to the list of those available to the assembler. For example: + +@smallexample + .begin freeregs a3, a4 + .begin freeregs a5 +@end smallexample + +has the effect of declaring @code{a3}, @code{a4}, and @code{a5} all free. + +@node Frame Directive +@subsection frame +@cindex @code{frame} directive + +This directive tells the assembler to emit information to allow the +debugger to locate a function's stack frame. The syntax is: + +@smallexample + .frame @var{reg}, @var{size} +@end smallexample + +where @var{reg} is the register used to hold the frame pointer (usually +the same as the stack pointer) and @var{size} is the size in bytes of +the stack frame. The @code{.frame} directive is typically placed +immediately after the @code{ENTRY} instruction for a function. + +In almost all circumstances, this information just duplicates the +information given in the function's @code{ENTRY} instruction; however, +there are two cases where this is not true: + +@enumerate +@item +The size of the stack frame is too big to fit in the immediate field +of the @code{ENTRY} instruction. + +@item +The frame pointer is different than the stack pointer, as with functions +that call @code{alloca}. +@end enumerate + +@c Local Variables: +@c fill-column: 72 +@c End: diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi index afff9f93d77..4e8a9d1bef3 100644 --- a/gas/doc/internals.texi +++ b/gas/doc/internals.texi @@ -1450,6 +1450,10 @@ completed, but before the relocations have been generated. If you define this macro, GAS will call it after the relocs have been generated. +@item md_post_relax_hook +If you define this macro, GAS will call it after relaxing and sizing the +segments. + @item LISTING_HEADER A string to use on the header line of a listing. The default value is simply @code{"GAS LISTING"}. diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index 6f1f3201afd..5582d168c16 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2003-04-01 Bob Wilson + + * gas/xtensa/all.exp: New file. + * gas/xtensa/entry_align.s: Likewise. + * gas/xtensa/entry_misalign2.s: Likewise. + * gas/xtensa/entry_misalign.s: Likewise. + * gas/xtensa/j_too_far.s: Likewise. + * gas/xtensa/loop_align.s: Likewise. + * gas/xtensa/loop_misalign.s: Likewise. + 2003-03-25 Stan Cox Nick Clifton diff --git a/gas/testsuite/gas/xtensa/all.exp b/gas/testsuite/gas/xtensa/all.exp new file mode 100644 index 00000000000..e01c4a395f0 --- /dev/null +++ b/gas/testsuite/gas/xtensa/all.exp @@ -0,0 +1,98 @@ +# +# Some generic xtensa tests +# +if [istarget xtensa*-*-*] then { + gas_test_error "j_too_far.s" "" "Check for jump out of range error" + + set testname "j_too_far.s: error line number reporting" + gas_start "j_too_far.s" "" + set x1 0 + while 1 { + expect { + -re ":4: Error:.*too large" { set x1 1 } + timeout { perror "timeout\n"; break } + eof { break } + } + } + gas_finish + if [all_ones $x1] then { pass $testname } else { fail $testname } + + + gas_test "entry_misalign.s" "" "" "Xtensa Entry misalignment" + set testname "entry_misalign.s: Force entry misalignment" + objdump_start_no_subdir "a.out" "-d -j .text" + set x1 0 + while 1 { + expect { + -re "^.*2:.*entry" { set x1 1 } + timeout { perror "timeout\n"; break } + eof { break } + } + } + objdump_finish + if [all_ones $x1] then { pass $testname } else { fail $testname } + + + gas_test "entry_misalign2.s" "" "" "Xtensa Entry misalignment(2)" + set testname "entry_misalign2.s: Force entry misalignment(2)" + objdump_start_no_subdir "a.out" "-d -j .text" + set x1 0 + while 1 { + expect { + -re "^.*2:.*entry" { set x1 1 } + timeout { perror "timeout\n"; break } + eof { break } + } + } + objdump_finish + if [all_ones $x1] then { pass $testname } else { fail $testname } + + gas_test "entry_align.s" "" "" "Xtensa autoalign entry" + set testname "entry_align.s: autoalign entry" + objdump_start_no_subdir "a.out" "-d -j .text" + set x1 0 + while 1 { + expect { + -re "^.*4:.*entry" { set x1 1 } + timeout { perror "timeout\n"; break } + eof { break } + } + } + objdump_finish + if [all_ones $x1] then { pass $testname } else { fail $testname } + + gas_test "loop_misalign.s" "" "" "Xtensa Loop misalignment" + set testname "loop_misalign.s: Force loop misalignment" + objdump_start_no_subdir "a.out" "-d -j .text" + set x1 0 + while 1 { + expect { + -re "^.*0:.*loop" { set x1 1 } + timeout { perror "timeout\n"; break } + eof { break } + } + } + objdump_finish + if [all_ones $x1] then { pass $testname } else { fail $testname } + + + gas_test "loop_align.s" "" "" "Xtensa autoalign loop" + set testname "loop_align.s: autoalign loop" + objdump_start_no_subdir "a.out" "-d -j .text" + set x1 0 + while 1 { + expect { + -re "^.*2:.*loop" { set x1 1 } + timeout { perror "timeout\n"; break } + eof { break } + } + } + objdump_finish + if [all_ones $x1] then { pass $testname } else { fail $testname } + + +} + +if [info exists errorInfo] then { + unset errorInfo + } diff --git a/gas/testsuite/gas/xtensa/entry_align.s b/gas/testsuite/gas/xtensa/entry_align.s new file mode 100644 index 00000000000..df8d1deb1dd --- /dev/null +++ b/gas/testsuite/gas/xtensa/entry_align.s @@ -0,0 +1,4 @@ + _nop.n +l4: + entry a5,16 + _mov.n a4,a5 diff --git a/gas/testsuite/gas/xtensa/entry_misalign.s b/gas/testsuite/gas/xtensa/entry_misalign.s new file mode 100644 index 00000000000..c3e243a6060 --- /dev/null +++ b/gas/testsuite/gas/xtensa/entry_misalign.s @@ -0,0 +1,4 @@ + _nop.n +l4: + _entry a5,16 + _mov.n a4,a5 diff --git a/gas/testsuite/gas/xtensa/entry_misalign2.s b/gas/testsuite/gas/xtensa/entry_misalign2.s new file mode 100644 index 00000000000..5d48b6c59b9 --- /dev/null +++ b/gas/testsuite/gas/xtensa/entry_misalign2.s @@ -0,0 +1,6 @@ + .begin no-generics + nop.n +l4: + entry a5,16 + mov.n a4,a5 + .end no-generics diff --git a/gas/testsuite/gas/xtensa/j_too_far.s b/gas/testsuite/gas/xtensa/j_too_far.s new file mode 100644 index 00000000000..3348d856bc6 --- /dev/null +++ b/gas/testsuite/gas/xtensa/j_too_far.s @@ -0,0 +1,8 @@ + .text + .align 4 + entry a5,16 + j too_far + .fill 150000 +too_far: + nop + nop diff --git a/gas/testsuite/gas/xtensa/loop_align.s b/gas/testsuite/gas/xtensa/loop_align.s new file mode 100644 index 00000000000..25ebc122e43 --- /dev/null +++ b/gas/testsuite/gas/xtensa/loop_align.s @@ -0,0 +1,5 @@ +l4: + loop a5,l5 + _mov.n a4,a5 +l5: + _nop.n diff --git a/gas/testsuite/gas/xtensa/loop_misalign.s b/gas/testsuite/gas/xtensa/loop_misalign.s new file mode 100644 index 00000000000..bb4c6e32fe4 --- /dev/null +++ b/gas/testsuite/gas/xtensa/loop_misalign.s @@ -0,0 +1,5 @@ +l4: + _loop a5,l5 + _mov.n a4,a5 +l5: + _nop.n diff --git a/gas/write.c b/gas/write.c index f5db82e298d..e4ce91cb921 100644 --- a/gas/write.c +++ b/gas/write.c @@ -1586,6 +1586,10 @@ write_object_file () /* Relaxation has completed. Freeze all syms. */ finalize_syms = 1; +#ifdef md_post_relax_hook + md_post_relax_hook; +#endif + #ifndef BFD_ASSEMBLER /* Crawl the symbol chain. diff --git a/include/ChangeLog b/include/ChangeLog index 0e558d3c184..0c3a4df1177 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,10 @@ +2003-04-01 Bob Wilson + + * dis-asm.h (print_insn_xtensa): Declare. + * xtensa-config.h: New file. + * xtensa-isa-internal.h: Likewise. + * xtensa-isa.h: Likewise. + 2003-03-17 Kaveh R. Ghazi * ansidecl.h (ATTRIBUTE_NONNULL, ATTRIBUTE_NULL_PRINTF, diff --git a/include/dis-asm.h b/include/dis-asm.h index 5e6bdc3ad3b..392cbf9b46d 100644 --- a/include/dis-asm.h +++ b/include/dis-asm.h @@ -237,6 +237,7 @@ extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_vax PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_xstormy16 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_xtensa PARAMS ((bfd_vma, disassemble_info*)); extern int print_insn_sh64 PARAMS ((bfd_vma, disassemble_info *)); extern int print_insn_sh64x_media PARAMS ((bfd_vma, disassemble_info *)); extern int print_insn_frv PARAMS ((bfd_vma, disassemble_info *)); diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index 6055c2287da..9d5ea67c1f3 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,8 @@ +2003-04-01 Bob Wilson + + * elf/common.h (EM_XTENSA_OLD): Define. + * elf/xtensa.h: New file. + 2003-04-01 Nick Clifton * arm.h (ARM_NOTE_SECTION): Include .gnu in the string. diff --git a/include/elf/common.h b/include/elf/common.h index 2315fa8d7ce..a515817a509 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -261,6 +261,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Vitesse IQ2000. */ #define EM_IQ2000 0xFEBA + +/* Old, unofficial value for Xtensa. */ +#define EM_XTENSA_OLD 0xabc7 + /* See the above comment before you add a new EM_* value here. */ /* Values for e_version. */ diff --git a/include/elf/xtensa.h b/include/elf/xtensa.h new file mode 100644 index 00000000000..394ee41381e --- /dev/null +++ b/include/elf/xtensa.h @@ -0,0 +1,87 @@ +/* Xtensa ELF support for BFD. + Copyright 2003 Free Software Foundation, Inc. + Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This file holds definitions specific to the Xtensa ELF ABI. */ + +#ifndef _ELF_XTENSA_H +#define _ELF_XTENSA_H + +#include "elf/reloc-macros.h" + +/* Relocations. */ +START_RELOC_NUMBERS (elf_xtensa_reloc_type) + RELOC_NUMBER (R_XTENSA_NONE, 0) + RELOC_NUMBER (R_XTENSA_32, 1) + RELOC_NUMBER (R_XTENSA_RTLD, 2) + RELOC_NUMBER (R_XTENSA_GLOB_DAT, 3) + RELOC_NUMBER (R_XTENSA_JMP_SLOT, 4) + RELOC_NUMBER (R_XTENSA_RELATIVE, 5) + RELOC_NUMBER (R_XTENSA_PLT, 6) + RELOC_NUMBER (R_XTENSA_OP0, 8) + RELOC_NUMBER (R_XTENSA_OP1, 9) + RELOC_NUMBER (R_XTENSA_OP2, 10) + RELOC_NUMBER (R_XTENSA_ASM_EXPAND, 11) + RELOC_NUMBER (R_XTENSA_ASM_SIMPLIFY, 12) + RELOC_NUMBER (R_XTENSA_GNU_VTINHERIT, 15) + RELOC_NUMBER (R_XTENSA_GNU_VTENTRY, 16) +END_RELOC_NUMBERS (R_XTENSA_max) + +/* Processor-specific flags for the ELF header e_flags field. */ + +/* Four-bit Xtensa machine type field. */ +#define EF_XTENSA_MACH 0x0000000f + +/* Various CPU types. */ +#define E_XTENSA_MACH 0x00000000 + +/* Leave bits 0xf0 alone in case we ever have more than 16 cpu types. + Highly unlikely, but what the heck. */ + +#define EF_XTENSA_XT_INSN 0x00000100 +#define EF_XTENSA_XT_LIT 0x00000200 + + +/* Processor-specific dynamic array tags. */ + +/* Offset of the table that records the GOT location(s). */ +#define DT_XTENSA_GOT_LOC_OFF 0x70000000 + +/* Number of entries in the GOT location table. */ +#define DT_XTENSA_GOT_LOC_SZ 0x70000001 + + +/* Definitions for instruction and literal property tables. The + instruction tables for ".gnu.linkonce.t.*" sections are placed in + the following sections: + + instruction tables: .gnu.linkonce.x.* + literal tables: .gnu.linkonce.p.* +*/ + +#define XTENSA_INSN_SEC_NAME ".xt.insn" +#define XTENSA_LIT_SEC_NAME ".xt.lit" + +typedef struct property_table_entry_t +{ + bfd_vma address; + bfd_vma size; +} property_table_entry; + +#endif /* _ELF_XTENSA_H */ diff --git a/include/xtensa-config.h b/include/xtensa-config.h new file mode 100644 index 00000000000..18a63ebe075 --- /dev/null +++ b/include/xtensa-config.h @@ -0,0 +1,70 @@ +/* Xtensa configuration settings. + Copyright (C) 2003 Free Software Foundation, Inc. + Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica. + +** NOTE: This file was automatically generated by the Xtensa Processor +** Generator. Changes made here will be lost when this file is +** updated or replaced with the settings for a different Xtensa +** processor configuration. DO NOT EDIT! + +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 2, 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef XTENSA_CONFIG_H +#define XTENSA_CONFIG_H + +/* The macros defined here match those with the same names in the Xtensa + compile-time HAL (Hardware Abstraction Layer). Please refer to the + Xtensa System Software Reference Manual for documentation of these + macros. */ + +#define XCHAL_HAVE_BE 1 +#define XCHAL_HAVE_DENSITY 1 +#define XCHAL_HAVE_MAC16 0 +#define XCHAL_HAVE_MUL16 0 +#define XCHAL_HAVE_MUL32 0 +#define XCHAL_HAVE_DIV32 0 +#define XCHAL_HAVE_NSA 1 +#define XCHAL_HAVE_MINMAX 0 +#define XCHAL_HAVE_SEXT 0 +#define XCHAL_HAVE_LOOPS 1 +#define XCHAL_HAVE_BOOLEANS 0 +#define XCHAL_HAVE_FP 0 +#define XCHAL_HAVE_FP_DIV 0 +#define XCHAL_HAVE_FP_RECIP 0 +#define XCHAL_HAVE_FP_SQRT 0 +#define XCHAL_HAVE_FP_RSQRT 0 +#define XCHAL_HAVE_WINDOWED 1 + +#define XCHAL_ICACHE_SIZE 8192 +#define XCHAL_DCACHE_SIZE 8192 +#define XCHAL_ICACHE_LINESIZE 16 +#define XCHAL_DCACHE_LINESIZE 16 +#define XCHAL_ICACHE_LINEWIDTH 4 +#define XCHAL_DCACHE_LINEWIDTH 4 +#define XCHAL_DCACHE_IS_WRITEBACK 0 + +#define XCHAL_HAVE_MMU 1 +#define XCHAL_MMU_MIN_PTE_PAGE_SIZE 12 + +#define XCHAL_HAVE_DEBUG 1 +#define XCHAL_NUM_IBREAK 2 +#define XCHAL_NUM_DBREAK 2 +#define XCHAL_DEBUGLEVEL 4 + +#define XCHAL_EXTRA_SA_SIZE 0 +#define XCHAL_EXTRA_SA_ALIGN 1 + +#endif /* !XTENSA_CONFIG_H */ diff --git a/include/xtensa-isa-internal.h b/include/xtensa-isa-internal.h new file mode 100644 index 00000000000..d2244c510a5 --- /dev/null +++ b/include/xtensa-isa-internal.h @@ -0,0 +1,114 @@ +/* Internal definitions for configurable Xtensa ISA support. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Use the statically-linked version for the GNU tools. */ +#define STATIC_LIBISA 1 + +#define ISA_INTERFACE_VERSION 3 + +struct config_struct +{ + char *param_name; + char *param_value; +}; + +/* Encode/decode function types for immediate operands. */ +typedef uint32 (*xtensa_immed_decode_fn) (uint32); +typedef xtensa_encode_result (*xtensa_immed_encode_fn) (uint32 *); + +/* Field accessor function types. */ +typedef uint32 (*xtensa_get_field_fn) (const xtensa_insnbuf); +typedef void (*xtensa_set_field_fn) (xtensa_insnbuf, uint32); + +/* PC-relative relocation function types. */ +typedef uint32 (*xtensa_do_reloc_fn) (uint32, uint32); +typedef uint32 (*xtensa_undo_reloc_fn) (uint32, uint32); + +/* Instruction decode function type. */ +typedef int (*xtensa_insn_decode_fn) (const xtensa_insnbuf); + +/* Instruction encoding template function type (each of these functions + returns a constant template; they exist only to make it easier for the + TIE compiler to generate endian-independent DLLs). */ +typedef xtensa_insnbuf (*xtensa_encoding_template_fn) (void); + + +typedef struct xtensa_operand_internal_struct +{ + char *operand_kind; /* e.g., "a", "f", "i", "l".... */ + char inout; /* '<', '>', or '='. */ + char isPCRelative; /* Is this a PC-relative offset? */ + xtensa_get_field_fn get_field; /* Get encoded value of the field. */ + xtensa_set_field_fn set_field; /* Set field with an encoded value. */ + xtensa_immed_encode_fn encode; /* Encode the operand value. */ + xtensa_immed_decode_fn decode; /* Decode the value from the field. */ + xtensa_do_reloc_fn do_reloc; /* Perform a PC-relative relocation. */ + xtensa_undo_reloc_fn undo_reloc; /* Undo a PC-relative relocation. */ +} xtensa_operand_internal; + + +typedef struct xtensa_iclass_internal_struct +{ + int num_operands; /* Size of "operands" array. */ + xtensa_operand_internal **operands; /* Array of operand structures. */ +} xtensa_iclass_internal; + + +typedef struct xtensa_opcode_internal_struct +{ + const char *name; /* Opcode mnemonic. */ + int length; /* Length in bytes of the insn. */ + xtensa_encoding_template_fn template; /* Fn returning encoding template. */ + xtensa_iclass_internal *iclass; /* Iclass for this opcode. */ +} xtensa_opcode_internal; + + +typedef struct opname_lookup_entry_struct +{ + const char *key; /* Opcode mnemonic. */ + xtensa_opcode opcode; /* Internal opcode number. */ +} opname_lookup_entry; + + +typedef struct xtensa_isa_internal_struct +{ + int is_big_endian; /* Endianness. */ + int insn_size; /* Maximum length in bytes. */ + int insnbuf_size; /* Number of insnbuf_words. */ + int num_opcodes; /* Total number for all modules. */ + xtensa_opcode_internal **opcode_table;/* Indexed by internal opcode #. */ + int num_modules; /* Number of modules (DLLs) loaded. */ + int *module_opcode_base; /* Starting opcode # for each module. */ + xtensa_insn_decode_fn *module_decode_fn; /* Decode fn for each module. */ + opname_lookup_entry *opname_lookup_table; /* Lookup table for each module. */ + struct config_struct *config; /* Table of configuration parameters. */ + int has_density; /* Is density option available? */ +} xtensa_isa_internal; + + +typedef struct xtensa_isa_module_struct +{ + const int (*get_num_opcodes_fn) (void); + xtensa_opcode_internal **(*get_opcodes_fn) (void); + int (*decode_insn_fn) (const xtensa_insnbuf); + struct config_struct *(*get_config_table_fn) (void); +} xtensa_isa_module; + +extern xtensa_isa_module xtensa_isa_modules[]; + diff --git a/include/xtensa-isa.h b/include/xtensa-isa.h new file mode 100644 index 00000000000..54f750c9a1a --- /dev/null +++ b/include/xtensa-isa.h @@ -0,0 +1,230 @@ +/* Interface definition for configurable Xtensa ISA support. + Copyright 2003 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef XTENSA_LIBISA_H +#define XTENSA_LIBISA_H + +/* Use the statically-linked version for the GNU tools. */ +#define STATIC_LIBISA 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef uint32 +#define uint32 unsigned int +#endif + +/* This file defines the interface to the Xtensa ISA library. This library + contains most of the ISA-specific information for a particular Xtensa + processor. For example, the set of valid instructions, their opcode + encodings and operand fields are all included here. To support Xtensa's + configurability and user-defined instruction extensions (i.e., TIE), the + library is initialized by loading one or more dynamic libraries; only a + small set of interface code is present in the statically-linked portion + of the library. + + This interface basically defines four abstract data types. + + . an instruction buffer - for holding the raw instruction bits + . ISA info - information about the ISA as a whole + . opcode info - information about individual instructions + . operand info - information about specific instruction operands + + It would be nice to implement these as classes in C++, but the library is + implemented in C to match the expectations of the GNU tools. + Instead, the interface defines a set of functions to access each data + type. With the exception of the instruction buffer, the internal + representations of the data structures are hidden. All accesses must be + made through the functions defined here. */ + +typedef void* xtensa_isa; +typedef void* xtensa_operand; + + +/* Opcodes are represented here using sequential integers beginning with 0. + The specific value used for a particular opcode is only fixed for a + particular instantiation of an xtensa_isa structure, so these values + should only be used internally. */ +typedef int xtensa_opcode; + +/* Define a unique value for undefined opcodes ("static const int" doesn't + seem to work for this because EGCS 1.0.3 on i686-Linux without -O won't + allow it to be used as an initializer). */ +#define XTENSA_UNDEFINED -1 + + +typedef int libisa_module_specifier; + +extern xtensa_isa xtensa_isa_init (void); + + +/* Instruction buffers. */ + +typedef uint32 xtensa_insnbuf_word; +typedef xtensa_insnbuf_word *xtensa_insnbuf; + +/* Get the size in words of the xtensa_insnbuf array. */ +extern int xtensa_insnbuf_size (xtensa_isa); + +/* Allocate (with malloc) an xtensa_insnbuf of the right size. */ +extern xtensa_insnbuf xtensa_insnbuf_alloc (xtensa_isa); + +/* Release (with free) an xtensa_insnbuf of the right size. */ +extern void xtensa_insnbuf_free (xtensa_insnbuf); + +/* Inward and outward conversion from memory images (byte streams) to our + internal instruction representation. */ +extern void xtensa_insnbuf_to_chars (xtensa_isa, const xtensa_insnbuf, + char *); + +extern void xtensa_insnbuf_from_chars (xtensa_isa, xtensa_insnbuf, + const char *); + + +/* ISA information. */ + +/* Load the ISA information from a shared library. If successful, this returns + a value which identifies the ISA for use in subsequent calls to the ISA + library; otherwise, it returns NULL. Multiple ISAs can be loaded to support + heterogeneous multiprocessor systems. */ +extern xtensa_isa xtensa_load_isa (libisa_module_specifier); + +/* Extend an existing set of ISA information by loading an additional shared + library of ISA information. This is primarily intended for loading TIE + extensions. If successful, the return value is non-zero. */ +extern int xtensa_extend_isa (xtensa_isa, libisa_module_specifier); + +/* The default ISA. This variable is set automatically to the ISA most + recently loaded and is provided as a convenience. An exception is the GNU + opcodes library, where there is a fixed interface that does not allow + passing the ISA as a parameter and the ISA must be taken from this global + variable. (Note: Since this variable is just a convenience, it is not + exported when libisa is built as a DLL, due to the hassle of dealing with + declspecs.) */ +extern xtensa_isa xtensa_default_isa; + + +/* Deallocate an xtensa_isa structure. */ +extern void xtensa_isa_free (xtensa_isa); + +/* Get the maximum instruction size in bytes. */ +extern int xtensa_insn_maxlength (xtensa_isa); + +/* Get the total number of opcodes for this processor. */ +extern int xtensa_num_opcodes (xtensa_isa); + +/* Translate a mnemonic name to an opcode. Returns XTENSA_UNDEFINED if + the name is not a valid opcode mnemonic. */ +extern xtensa_opcode xtensa_opcode_lookup (xtensa_isa, const char *); + +/* Decode a binary instruction buffer. Returns the opcode or + XTENSA_UNDEFINED if the instruction is illegal. */ +extern xtensa_opcode xtensa_decode_insn (xtensa_isa, const xtensa_insnbuf); + + +/* Opcode information. */ + +/* Set the opcode field(s) in a binary instruction buffer. The operand + fields are set to zero. */ +extern void xtensa_encode_insn (xtensa_isa, xtensa_opcode, xtensa_insnbuf); + +/* Get the mnemonic name for an opcode. */ +extern const char * xtensa_opcode_name (xtensa_isa, xtensa_opcode); + +/* Find the length (in bytes) of an instruction. */ +extern int xtensa_insn_length (xtensa_isa, xtensa_opcode); + +/* Find the length of an instruction by looking only at the first byte. */ +extern int xtensa_insn_length_from_first_byte (xtensa_isa, char); + +/* Find the number of operands for an instruction. */ +extern int xtensa_num_operands (xtensa_isa, xtensa_opcode); + +/* Get the information about operand number "opnd" of a particular opcode. */ +extern xtensa_operand xtensa_get_operand (xtensa_isa, xtensa_opcode, int); + +/* Operand information. */ + +/* Find the kind of operand. There are three possibilities: + 1) PC-relative immediates (e.g., "l", "L"). These can be identified with + the xtensa_operand_isPCRelative function. + 2) non-PC-relative immediates ("i"). + 3) register-file short names (e.g., "a", "b", "m" and others defined + via TIE). */ +extern char * xtensa_operand_kind (xtensa_operand); + +/* Check if an operand is an input ('<'), output ('>'), or inout ('=') + operand. Note: The output operand of a conditional assignment + (e.g., movnez) appears here as an inout ('=') even if it is declared + in the TIE code as an output ('>'); this allows the compiler to + properly handle register allocation for conditional assignments. */ +extern char xtensa_operand_inout (xtensa_operand); + +/* Get and set the raw (encoded) value of the field for the specified + operand. The "set" function does not check if the value fits in the + field; that is done by the "encode" function below. */ +extern uint32 xtensa_operand_get_field (xtensa_operand, const xtensa_insnbuf); + +extern void xtensa_operand_set_field (xtensa_operand, xtensa_insnbuf, uint32); + + +/* Encode and decode operands. The raw bits in the operand field + may be encoded in a variety of different ways. These functions hide the + details of that encoding. The encode function has a special return type + (xtensa_encode_result) to indicate success or the reason for failure; the + encoded value is returned through the argument pointer. The decode function + has no possibility of failure and returns the decoded value. */ + +typedef enum +{ + xtensa_encode_result_ok, + xtensa_encode_result_align, + xtensa_encode_result_not_in_table, + xtensa_encode_result_too_low, + xtensa_encode_result_too_high, + xtensa_encode_result_not_ok, + xtensa_encode_result_max = xtensa_encode_result_not_ok +} xtensa_encode_result; + +extern xtensa_encode_result xtensa_operand_encode (xtensa_operand, uint32 *); + +extern uint32 xtensa_operand_decode (xtensa_operand, uint32); + + +/* For PC-relative offset operands, the interpretation of the offset may vary + between opcodes, e.g., is it relative to the current PC or that of the next + instruction? The following functions are defined to perform PC-relative + relocations and to undo them (as in the disassembler). The first function + takes the desired address and the PC of the current instruction and returns + the unencoded value to be stored in the offset field. The second function + takes the unencoded offset value and the current PC and returns the address. + Note that these functions do not replace the encode/decode functions; the + operands must be encoded/decoded separately. */ + +extern int xtensa_operand_isPCRelative (xtensa_operand); + +extern uint32 xtensa_operand_do_reloc (xtensa_operand, uint32, uint32); + +extern uint32 xtensa_operand_undo_reloc (xtensa_operand, uint32, uint32); + +#ifdef __cplusplus +} +#endif +#endif /* XTENSA_LIBISA_H */ diff --git a/ld/ChangeLog b/ld/ChangeLog index 9836da4001f..2ea63d692b2 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,16 @@ +2003-04-01 Bob Wilson + + * Makefile.am (ALL_EMULATIONS): Add eelf32xtensa.o. + (eelf32xtensa.c): New target. + * Makefile.in: Regenerate. + * configure.tgt: Handle xtensa-*-*. + * gen-doc.texi: Set XTENSA variable. + * ld.texinfo: Set XTENSA variable. Add new Xtensa node. + * emulparams/elf32xtensa.sh: New file. + * emulparams/xtensa-config.sh: Likewise. + * emultempl/xtensaelf.em: Likewise. + * scripttempl/elfxtensa.sc: Likewise. + 2003-04-01 Jakub Jelinek * configure.tgt (powerpc*-*-linux*): Add elf32ppc to ppc64 diff --git a/ld/Makefile.am b/ld/Makefile.am index 41af307a074..6e35d38ee6e 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -183,6 +183,7 @@ ALL_EMULATIONS = \ eelf32ppcwindiss.o \ eelf32vax.o \ eelf32xstormy16.o \ + eelf32xtensa.o \ eelf_i386.o \ eelf_i386_be.o \ eelf_i386_chaos.o \ @@ -589,6 +590,11 @@ eelf32xstormy16.c: $(srcdir)/emulparams/elf32xstormy16.sh \ eelf32vax.c: $(srcdir)/emulparams/elf32vax.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32vax "$(tdir_elf32vax)" +eelf32xtensa.c: $(srcdir)/emulparams/elf32xtensa.sh \ + $(srcdir)/emulparams/xtensa-config.sh \ + $(srcdir)/emultempl/elf32.em $(srcdir)/emultempl/xtensaelf.em \ + $(srcdir)/scripttempl/elfxtensa.sc ${GEN_DEPENDS} + ${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)" eelf32fr30.c: $(srcdir)/emulparams/elf32fr30.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32fr30 "$(tdir_fr30)" diff --git a/ld/Makefile.in b/ld/Makefile.in index 23b7d5b5c00..dc226af5574 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -297,6 +297,7 @@ ALL_EMULATIONS = \ eelf32ppcwindiss.o \ eelf32vax.o \ eelf32xstormy16.o \ + eelf32xtensa.o \ eelf_i386.o \ eelf_i386_be.o \ eelf_i386_chaos.o \ @@ -1315,6 +1316,11 @@ eelf32xstormy16.c: $(srcdir)/emulparams/elf32xstormy16.sh \ eelf32vax.c: $(srcdir)/emulparams/elf32vax.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32vax "$(tdir_elf32vax)" +eelf32xtensa.c: $(srcdir)/emulparams/elf32xtensa.sh \ + $(srcdir)/emulparams/xtensa-config.sh \ + $(srcdir)/emultempl/elf32.em $(srcdir)/emultempl/xtensaelf.em \ + $(srcdir)/scripttempl/elfxtensa.sc ${GEN_DEPENDS} + ${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)" eelf32fr30.c: $(srcdir)/emulparams/elf32fr30.sh \ $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} ${GENSCRIPTS} elf32fr30 "$(tdir_fr30)" diff --git a/ld/configure.tgt b/ld/configure.tgt index 3e4fa2567bd..8c10d4274cc 100644 --- a/ld/configure.tgt +++ b/ld/configure.tgt @@ -525,6 +525,7 @@ iq2000-*-elf) targ_emul=elf32iq2000 ; targ_extra_emuls="elf32iq10" ;; frv-*-*) targ_emul=elf32frv ;; w65-*-*) targ_emul=w65 ;; xstormy16-*-*) targ_emul=elf32xstormy16 ;; +xtensa-*-*) targ_emul=elf32xtensa;; fr30-*-*) targ_emul=elf32fr30 ;; mcore-*-pe) targ_emul=mcorepe ; targ_extra_ofiles="deffilep.o pe-dll.o" ;; diff --git a/ld/emulparams/elf32xtensa.sh b/ld/emulparams/elf32xtensa.sh new file mode 100644 index 00000000000..12e28820b3f --- /dev/null +++ b/ld/emulparams/elf32xtensa.sh @@ -0,0 +1,32 @@ +# First set some configuration-specific variables +. ${srcdir}/emulparams/xtensa-config.sh + +# See genscripts.sh and ../scripttempl/elfxtensa.sc for the meaning of these. +SCRIPT_NAME=elfxtensa +TEMPLATE_NAME=elf32 +EXTRA_EM_FILE=xtensaelf +OUTPUT_FORMAT=undefined +BIG_OUTPUT_FORMAT="elf32-xtensa-be" +LITTLE_OUTPUT_FORMAT="elf32-xtensa-le" +TEXT_START_ADDR=0x400000 +NONPAGED_TEXT_START_ADDR=0x400000 +ARCH=xtensa +MACHINE= +GENERATE_SHLIB_SCRIPT=yes +GENERATE_COMBRELOC_SCRIPT=yes +NO_SMALL_DATA=yes +OTHER_READONLY_SECTIONS=' + .xt_except_table : { KEEP (*(.xt_except_table)) } + .xt.lit : { *(.xt.lit*) *(.gnu.linkonce.p*) } +' +OTHER_READWRITE_SECTIONS=' + .xt_except_desc : + { + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + *(.xt_except_desc_end) + } +' +OTHER_SECTIONS=' + .xt.insn : { *(.xt.insn) *(.gnu.linkonce.x*) } +' diff --git a/ld/emulparams/xtensa-config.sh b/ld/emulparams/xtensa-config.sh new file mode 100644 index 00000000000..b273df5b434 --- /dev/null +++ b/ld/emulparams/xtensa-config.sh @@ -0,0 +1,8 @@ +# Xtensa configuration settings. + +## NOTE: This file was automatically generated by the Xtensa Processor +## Generator. Changes made here will be lost when this file is +## updated or replaced with the settings for a different Xtensa +## processor configuration. DO NOT EDIT! + +MAXPAGESIZE=0x1000 diff --git a/ld/emultempl/xtensaelf.em b/ld/emultempl/xtensaelf.em new file mode 100644 index 00000000000..b0700758f69 --- /dev/null +++ b/ld/emultempl/xtensaelf.em @@ -0,0 +1,1586 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2003 +# Free Software Foundation, Inc. +# +# This file is part of GLD, the Gnu Linker. +# +# 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# This file is sourced from elf32.em, and defines extra xtensa-elf +# specific routines. +# +cat >>e${EMULATION_NAME}.c < + +static char *elf_xtensa_choose_target + PARAMS ((int, char **)); +static bfd_boolean elf_xtensa_place_orphan + PARAMS ((lang_input_statement_type *, asection *)); +static void elf_xtensa_before_parse + PARAMS ((void)); +static void elf_xtensa_before_allocation + PARAMS ((void)); +static void xtensa_wild_group_interleave + PARAMS ((lang_statement_union_type *)); +static void xtensa_wild_group_interleave_callback + PARAMS ((lang_statement_union_type *)); +static void xtensa_colocate_output_literals + PARAMS ((lang_statement_union_type *)); +static void xtensa_colocate_output_literals_callback + PARAMS ((lang_statement_union_type *)); + + +/* Flag for the emulation-specific "--no-relax" option. */ +static bfd_boolean disable_relaxation = FALSE; + +/* This number is irrelevant until we turn on use_literal_pages */ +static bfd_vma xtensa_page_power = 12; /* 4K pages. */ + +/* To force a page break between literals and text, change + xtensa_use_literal_pages to "true". */ +static bfd_boolean xtensa_use_literal_pages = FALSE; + +#define EXTRA_VALIDATION 0 + + +static char * +elf_xtensa_choose_target (argc, argv) + int argc ATTRIBUTE_UNUSED; + char **argv ATTRIBUTE_UNUSED; +{ + if (XCHAL_HAVE_BE) + return "${BIG_OUTPUT_FORMAT}"; + else + return "${LITTLE_OUTPUT_FORMAT}"; +} + + +static bfd_boolean +elf_xtensa_place_orphan (file, s) + lang_input_statement_type *file; + asection *s; +{ + /* Early exit for relocatable links. */ + if (link_info.relocateable) + return FALSE; + + return gld${EMULATION_NAME}_place_orphan (file, s); +} + + +static void +elf_xtensa_before_parse () +{ + /* Just call the default hook.... Tensilica's version of this function + does some other work that isn't relevant here. */ + gld${EMULATION_NAME}_before_parse (); +} + + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +void +elf_xtensa_before_allocation () +{ + bfd *in_bfd; + bfd_boolean is_big_endian = XCHAL_HAVE_BE; + + /* Check that the output endianness matches the Xtensa + configuration. The BFD library always includes both big and + little endian target vectors for Xtensa, but it only supports the + detailed instruction encode/decode operations (such as are + required to process relocations) for the selected Xtensa + configuration. */ + + if (is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) + { + einfo (_("%F%P: little endian output does not match " + "Xtensa configuration\n")); + } + if (!is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_BIG) + { + einfo (_("%F%P: big endian output does not match " + "Xtensa configuration\n")); + } + + /* Check that the endianness for each input file matches the output. + The merge_private_bfd_data hook has already reported any mismatches + as errors, but those errors are not fatal. At this point, we + cannot go any further if there are any mismatches. */ + + for (in_bfd = link_info.input_bfds; + in_bfd != NULL; + in_bfd = in_bfd->link_next) + { + if ((is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) + || (!is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_BIG)) + einfo (_("%F%P: cross-endian linking not supported\n")); + } + + /* Enable relaxation by default if the "--no-relax" option was not + specified. This is done here instead of in the before_parse hook + because there is a check in main() to prohibit use of --relax and + -r together and that combination should be allowed for Xtensa. */ + + if (!disable_relaxation) + command_line.relax = TRUE; + + gld${EMULATION_NAME}_before_allocation (); + + xtensa_wild_group_interleave (stat_ptr->head); + if (command_line.relax) + xtensa_colocate_output_literals (stat_ptr->head); + + /* TBD: We need to force the page alignments to here and only do + them as needed for the entire output section. Finally, if this + is a relocateable link then we need to add alignment notes so + that the literals can be separated later. */ +} + + +typedef struct wildcard_list section_name_list; + +typedef struct reloc_deps_e_t reloc_deps_e; +typedef struct reloc_deps_section_t reloc_deps_section; +typedef struct reloc_deps_graph_t reloc_deps_graph; + + +struct reloc_deps_e_t +{ + asection *src; /* Contains l32rs. */ + asection *tgt; /* Contains literals. */ + reloc_deps_e *next; +}; + +/* Place these in the userdata field. */ +struct reloc_deps_section_t +{ + reloc_deps_e *preds; + reloc_deps_e *succs; + bfd_boolean is_only_literal; +}; + + +struct reloc_deps_graph_t +{ + size_t count; + size_t size; + asection **sections; +}; + +static void xtensa_layout_wild + PARAMS ((const reloc_deps_graph *, lang_wild_statement_type *)); + +typedef void (*deps_callback_t) + PARAMS ((asection *, /* src_sec */ + bfd_vma, /* src_offset */ + asection *, /* target_sec */ + bfd_vma, /* target_offset */ + PTR)); /* closure */ + +static void build_deps_graph_callback + PARAMS ((asection *, bfd_vma, asection *, bfd_vma, PTR)); +extern bfd_boolean xtensa_callback_required_dependence + PARAMS ((bfd *, asection *, struct bfd_link_info *, + deps_callback_t, PTR)); +static void xtensa_ldlang_clear_addresses + PARAMS ((lang_statement_union_type *)); +static bfd_boolean ld_local_file_relocations_fit + PARAMS ((lang_statement_union_type *, const reloc_deps_graph *)); +static bfd_vma ld_assign_relative_paged_dot + PARAMS ((bfd_vma, lang_statement_union_type *, + const reloc_deps_graph *, bfd_boolean)); +static bfd_vma ld_xtensa_insert_page_offsets + PARAMS ((bfd_vma, lang_statement_union_type *, reloc_deps_graph *, + bfd_boolean)); +static void lang_for_each_statement_worker + PARAMS ((void (*) (lang_statement_union_type *), + lang_statement_union_type *)); +static void xtensa_move_dependencies_to_front + PARAMS ((reloc_deps_graph *, lang_wild_statement_type *)); +static reloc_deps_graph *ld_build_required_section_dependence + PARAMS ((lang_statement_union_type *)); +static bfd_boolean section_is_source + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *)); +static bfd_boolean section_is_target + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *)); +static bfd_boolean section_is_source_or_target + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *)); +static bfd_boolean deps_has_sec_edge + PARAMS ((const reloc_deps_graph *, asection *, asection *)); +static bfd_boolean deps_has_edge + PARAMS ((const reloc_deps_graph *, lang_statement_union_type *, + lang_statement_union_type *)); +static void add_deps_edge + PARAMS ((reloc_deps_graph *, asection *, asection *)); +#if EXTRA_VALIDATION +static size_t ld_count_children + PARAMS ((lang_statement_union_type *)); +#endif +static void free_reloc_deps_graph + PARAMS ((reloc_deps_graph *)); +static void xtensa_colocate_literals + PARAMS ((reloc_deps_graph *, lang_statement_union_type *)); +static reloc_deps_section *xtensa_get_section_deps + PARAMS ((const reloc_deps_graph *, asection *)); +static void xtensa_set_section_deps + PARAMS ((const reloc_deps_graph *, asection *, reloc_deps_section *)); +static void xtensa_append_section_deps + PARAMS ((reloc_deps_graph *, asection *)); + +extern lang_statement_list_type constructor_list; + +/* Begin verbatim code from ldlang.c: + the following are copied from ldlang.c because they are defined + there statically. */ + +static void +lang_for_each_statement_worker (func, s) + void (*func) PARAMS ((lang_statement_union_type *)); + lang_statement_union_type *s; +{ + for (; s != (lang_statement_union_type *) NULL; s = s->header.next) + { + func (s); + + switch (s->header.type) + { + case lang_constructors_statement_enum: + lang_for_each_statement_worker (func, constructor_list.head); + break; + case lang_output_section_statement_enum: + lang_for_each_statement_worker + (func, + s->output_section_statement.children.head); + break; + case lang_wild_statement_enum: + lang_for_each_statement_worker + (func, + s->wild_statement.children.head); + break; + case lang_group_statement_enum: + lang_for_each_statement_worker (func, + s->group_statement.children.head); + break; + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_section_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + default: + FAIL (); + break; + } + } +} + +/* End of verbatim code from ldlang.c. */ + + +reloc_deps_section * +xtensa_get_section_deps (deps, sec) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + asection *sec; +{ + /* We have a separate function for this so that + we could in the future keep a completely independent + structure that maps a section to its dependence edges. + For now, we place these in the sec->userdata field. */ + reloc_deps_section *sec_deps = (reloc_deps_section *) sec->userdata; + return sec_deps; +} + +void +xtensa_set_section_deps (deps, sec, deps_section) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + asection *sec; + reloc_deps_section *deps_section; +{ + sec->userdata = (void *) deps_section; +} + + +/* This is used to keep a list of all of the sections participating in + the graph so we can clean them up quickly. */ + +static void +xtensa_append_section_deps (deps, sec) + reloc_deps_graph *deps; + asection *sec; +{ + if (deps->size <= deps->count) + { + asection **new_sections; + size_t i; + size_t new_size; + + new_size = deps->size * 2; + if (new_size == 0) + new_size = 20; + + new_sections = (asection**) xmalloc (sizeof (asection*) * new_size); + memset (new_sections, 0, sizeof (asection*) * new_size); + for (i = 0; i < deps->count; i++) + { + new_sections[i] = deps->sections[i]; + } + if (deps->sections != NULL) + free (deps->sections); + deps->sections = new_sections; + deps->size = new_size; + } + deps->sections[deps->count] = sec; + deps->count++; +} + + +static void +free_reloc_deps_graph (deps) + reloc_deps_graph *deps; +{ + size_t i; + for (i = 0; i < deps->count; i++) + { + asection *sec = deps->sections[i]; + reloc_deps_section *sec_deps; + sec_deps = xtensa_get_section_deps (deps, sec); + if (sec_deps) + { + reloc_deps_e *next; + while (sec_deps->succs != NULL) + { + next = sec_deps->succs->next; + free (sec_deps->succs); + sec_deps->succs = next; + } + + while (sec_deps->preds != NULL) + { + next = sec_deps->preds->next; + free (sec_deps->preds); + sec_deps->preds = next; + } + free (sec_deps); + } + xtensa_set_section_deps (deps, sec, NULL); + } + if (deps->sections) + free (deps->sections); + + free (deps); +} + + +bfd_boolean +section_is_source (deps, s) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + lang_statement_union_type *s; +{ + asection *sec; + const reloc_deps_section *sec_deps; + + if (s->header.type != lang_input_section_enum) + return FALSE; + sec = s->input_section.section; + + sec_deps = xtensa_get_section_deps (deps, sec); + return (sec_deps && sec_deps->succs != NULL); +} + + +bfd_boolean +section_is_target (deps, s) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + lang_statement_union_type *s; +{ + asection *sec; + const reloc_deps_section *sec_deps; + + if (s->header.type != lang_input_section_enum) + return FALSE; + sec = s->input_section.section; + + sec_deps = xtensa_get_section_deps (deps, sec); + return (sec_deps && sec_deps->preds != NULL); +} + +bfd_boolean +section_is_source_or_target (deps, s) + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + lang_statement_union_type *s; +{ + return (section_is_source (deps, s) + || section_is_target (deps, s)); +} + + +typedef struct xtensa_ld_iter_stack_t xtensa_ld_iter_stack; +typedef struct xtensa_ld_iter_t xtensa_ld_iter; + +struct xtensa_ld_iter_t +{ + lang_statement_union_type *parent; /* Parent of the list. */ + lang_statement_list_type *l; /* List that holds it. */ + lang_statement_union_type **loc; /* Place in the list. */ +}; + +struct xtensa_ld_iter_stack_t +{ + xtensa_ld_iter iterloc; /* List that hold it. */ + + xtensa_ld_iter_stack *next; /* Next in the stack. */ + xtensa_ld_iter_stack *prev; /* Back pointer for stack. */ +}; + +static void ld_xtensa_move_section_after + PARAMS ((xtensa_ld_iter *, xtensa_ld_iter *)); + + +void +ld_xtensa_move_section_after (to, current) + xtensa_ld_iter *to; + xtensa_ld_iter *current; +{ + lang_statement_union_type *to_next; + lang_statement_union_type *current_next; + lang_statement_union_type **e; + +#if EXTRA_VALIDATION + size_t old_to_count, new_to_count; + size_t old_current_count, new_current_count; +#endif + + if (to == current) + return; + +#if EXTRA_VALIDATION + old_to_count = ld_count_children (to->parent); + old_current_count = ld_count_children (current->parent); +#endif + + to_next = *(to->loc); + current_next = (*current->loc)->header.next; + + *(to->loc) = *(current->loc); + + *(current->loc) = current_next; + (*(to->loc))->header.next = to_next; + + /* reset "to" list tail */ + for (e = &to->l->head; *e != NULL; e = &(*e)->header.next) + ; + to->l->tail = e; + + /* reset "current" list tail */ + for (e = ¤t->l->head; *e != NULL; e = &(*e)->header.next) + ; + current->l->tail = e; + +#if EXTRA_VALIDATION + new_to_count = ld_count_children (to->parent); + new_current_count = ld_count_children (current->parent); + + ASSERT ((old_to_count + old_current_count) + == (new_to_count + new_current_count)); +#endif +} + + +/* Can only be called with lang_statements that have lists. Returns + false if the list is empty. */ + +static bfd_boolean iter_stack_empty + PARAMS ((xtensa_ld_iter_stack **)); +static bfd_boolean iter_stack_push + PARAMS ((xtensa_ld_iter_stack **, lang_statement_union_type *)); +static void iter_stack_pop + PARAMS ((xtensa_ld_iter_stack **)); +static void iter_stack_update + PARAMS ((xtensa_ld_iter_stack **)); +static void iter_stack_next + PARAMS ((xtensa_ld_iter_stack **)); +static lang_statement_union_type *iter_stack_current + PARAMS ((xtensa_ld_iter_stack **)); +static void iter_stack_create + PARAMS ((xtensa_ld_iter_stack **, lang_statement_union_type *)); +static void iter_stack_copy_current + PARAMS ((xtensa_ld_iter_stack **, xtensa_ld_iter *)); + + +static bfd_boolean +iter_stack_empty (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + return (*stack_p == NULL); +} + + +static bfd_boolean +iter_stack_push (stack_p, parent) + xtensa_ld_iter_stack **stack_p; + lang_statement_union_type *parent; +{ + xtensa_ld_iter_stack *stack; + lang_statement_list_type *l = NULL; + + switch (parent->header.type) + { + case lang_output_section_statement_enum: + l = &parent->output_section_statement.children; + break; + case lang_wild_statement_enum: + l = &parent->wild_statement.children; + break; + case lang_group_statement_enum: + l = &parent->group_statement.children; + break; + default: + ASSERT (0); + return FALSE; + } + + /* Empty. do not push. */ + if (l->tail == &l->head) + return FALSE; + + stack = (xtensa_ld_iter_stack *) xmalloc (sizeof (xtensa_ld_iter_stack)); + memset (stack, 0, sizeof (xtensa_ld_iter_stack)); + stack->iterloc.parent = parent; + stack->iterloc.l = l; + stack->iterloc.loc = &l->head; + + stack->next = *stack_p; + stack->prev = NULL; + if (*stack_p != NULL) + (*stack_p)->prev = stack; + *stack_p = stack; + return TRUE; +} + + +static void +iter_stack_pop (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + xtensa_ld_iter_stack *stack; + + stack = *stack_p; + + if (stack == NULL) + { + ASSERT (stack != NULL); + return; + } + + if (stack->next != NULL) + stack->next->prev = NULL; + + *stack_p = stack->next; + free (stack); +} + + +/* This MUST be called if, during iteration, the user changes the + underlying structure. It will check for a NULL current and advance + accordingly. */ + +static void +iter_stack_update (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + if (!iter_stack_empty (stack_p) + && (*(*stack_p)->iterloc.loc) == NULL) + { + iter_stack_pop (stack_p); + + while (!iter_stack_empty (stack_p) + && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) + { + iter_stack_pop (stack_p); + } + if (!iter_stack_empty (stack_p)) + (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; + } +} + + +static void +iter_stack_next (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + xtensa_ld_iter_stack *stack; + lang_statement_union_type *current; + stack = *stack_p; + + current = *stack->iterloc.loc; + /* If we are on the first element. */ + if (current != NULL) + { + switch (current->header.type) + { + case lang_output_section_statement_enum: + case lang_wild_statement_enum: + case lang_group_statement_enum: + /* If the list if not empty, we are done. */ + if (iter_stack_push (stack_p, *stack->iterloc.loc)) + return; + /* Otherwise increment the pointer as normal. */ + break; + default: + break; + } + } + + while (!iter_stack_empty (stack_p) + && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) + { + iter_stack_pop (stack_p); + } + if (!iter_stack_empty (stack_p)) + (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; +} + + +static lang_statement_union_type * +iter_stack_current (stack_p) + xtensa_ld_iter_stack **stack_p; +{ + return *((*stack_p)->iterloc.loc); +} + + +/* The iter stack is a preorder. */ + +static void +iter_stack_create (stack_p, parent) + xtensa_ld_iter_stack **stack_p; + lang_statement_union_type *parent; +{ + iter_stack_push (stack_p, parent); +} + + +static void +iter_stack_copy_current (stack_p, front) + xtensa_ld_iter_stack **stack_p; + xtensa_ld_iter *front; +{ + *front = (*stack_p)->iterloc; +} + + +void +xtensa_colocate_literals (deps, statement) + reloc_deps_graph *deps; + lang_statement_union_type *statement; +{ + /* Keep a stack of pointers to control iteration through the contours. */ + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + xtensa_ld_iter front; /* Location where new insertion should occur. */ + xtensa_ld_iter *front_p = NULL; + + xtensa_ld_iter current; /* Location we are checking. */ + xtensa_ld_iter *current_p = NULL; + bfd_boolean in_literals = FALSE; + + if (deps->count == 0) + return; + +#if 0 + ld_assign_relative_paged_dot (0x100000, statement, deps, + xtensa_use_literal_pages); + + if (!ld_local_file_relocations_fit (statement, deps)) + fprintf (stderr, "initial relocation placement does not fit\n"); + + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement); +#endif + + iter_stack_create (stack_p, statement); + + while (!iter_stack_empty (stack_p)) + { + bfd_boolean skip_increment = FALSE; + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_assignment_statement_enum: + /* Any assignment statement should block reordering across it. */ + front_p = NULL; + in_literals = FALSE; + break; + + case lang_input_section_enum: + if (front_p == NULL) + { + in_literals = (section_is_target (deps, l) + && !section_is_source (deps, l)); + if (in_literals) + { + front_p = &front; + iter_stack_copy_current (stack_p, front_p); + } + } + else + { + bfd_boolean is_target; + current_p = ¤t; + iter_stack_copy_current (stack_p, current_p); + is_target = (section_is_target (deps, l) + && !section_is_source (deps, l)); + + if (in_literals) + { + iter_stack_copy_current (stack_p, front_p); + if (!is_target) + in_literals = FALSE; + } + else + { + if (is_target) + { + /* Try to insert in place. */ + ld_xtensa_move_section_after (front_p, current_p); + ld_assign_relative_paged_dot (0x100000, + statement, + deps, + xtensa_use_literal_pages); + + /* We use this code because it's already written. */ + if (!ld_local_file_relocations_fit (statement, deps)) + { + /* Move it back. */ + ld_xtensa_move_section_after (current_p, front_p); + /* Reset the literal placement. */ + iter_stack_copy_current (stack_p, front_p); + } + else + { + /* Move front pointer up by one. */ + front_p->loc = &(*front_p->loc)->header.next; + + /* Do not increment the current pointer. */ + skip_increment = TRUE; + } + } + } + } + break; + default: + break; + } + + if (!skip_increment) + iter_stack_next (stack_p); + else + /* Be careful to update the stack_p if it now is a null. */ + iter_stack_update (stack_p); + } + + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement); +} + + +void +xtensa_move_dependencies_to_front (deps, w) + reloc_deps_graph *deps; + lang_wild_statement_type *w; +{ + /* Keep a front pointer and a current pointer. */ + lang_statement_union_type **front; + lang_statement_union_type **current; + + /* Walk to the end of the targets. */ + for (front = &w->children.head; + (*front != NULL) && section_is_source_or_target (deps, *front); + front = &(*front)->header.next) + ; + + if (*front == NULL) + return; + + current = &(*front)->header.next; + while (*current != NULL) + { + if (section_is_source_or_target (deps, *current)) + { + /* Insert in place. */ + xtensa_ld_iter front_iter; + xtensa_ld_iter current_iter; + + front_iter.parent = (lang_statement_union_type *) w; + front_iter.l = &w->children; + front_iter.loc = front; + + current_iter.parent = (lang_statement_union_type *) w; + current_iter.l = &w->children; + current_iter.loc = current; + + ld_xtensa_move_section_after (&front_iter, ¤t_iter); + front = &(*front)->header.next; + } + else + { + current = &(*current)->header.next; + } + } +} + + +static bfd_boolean +deps_has_sec_edge (deps, src, tgt) + const reloc_deps_graph *deps; + asection *src; + asection *tgt; +{ + const reloc_deps_section *sec_deps; + const reloc_deps_e *sec_deps_e; + + sec_deps = xtensa_get_section_deps (deps, src); + if (sec_deps == NULL) + return FALSE; + + for (sec_deps_e = sec_deps->succs; + sec_deps_e != NULL; + sec_deps_e = sec_deps_e->next) + { + ASSERT (sec_deps_e->src == src); + if (sec_deps_e->tgt == tgt) + return TRUE; + } + return FALSE; +} + + +static bfd_boolean +deps_has_edge (deps, src, tgt) + const reloc_deps_graph *deps; + lang_statement_union_type *src; + lang_statement_union_type *tgt; +{ + if (!section_is_source (deps, src)) + return FALSE; + if (!section_is_target (deps, tgt)) + return FALSE; + + if (src->header.type != lang_input_section_enum) + return FALSE; + if (tgt->header.type != lang_input_section_enum) + return FALSE; + + return deps_has_sec_edge (deps, src->input_section.section, + tgt->input_section.section); +} + + +static void +add_deps_edge (deps, src_sec, tgt_sec) + reloc_deps_graph *deps; + asection *src_sec; + asection *tgt_sec; +{ + reloc_deps_section *src_sec_deps; + reloc_deps_section *tgt_sec_deps; + + reloc_deps_e *src_edge; + reloc_deps_e *tgt_edge; + + if (deps_has_sec_edge (deps, src_sec, tgt_sec)) + return; + + src_sec_deps = xtensa_get_section_deps (deps, src_sec); + if (src_sec_deps == NULL) + { + /* Add a section. */ + src_sec_deps = (reloc_deps_section *) + xmalloc (sizeof (reloc_deps_section)); + memset (src_sec_deps, 0, sizeof (reloc_deps_section)); + src_sec_deps->is_only_literal = 0; + src_sec_deps->preds = NULL; + src_sec_deps->succs = NULL; + xtensa_set_section_deps (deps, src_sec, src_sec_deps); + xtensa_append_section_deps (deps, src_sec); + } + + tgt_sec_deps = xtensa_get_section_deps (deps, tgt_sec); + if (tgt_sec_deps == NULL) + { + /* Add a section. */ + tgt_sec_deps = (reloc_deps_section *) + xmalloc (sizeof (reloc_deps_section)); + memset (tgt_sec_deps, 0, sizeof (reloc_deps_section)); + tgt_sec_deps->is_only_literal = 0; + tgt_sec_deps->preds = NULL; + tgt_sec_deps->succs = NULL; + xtensa_set_section_deps (deps, tgt_sec, tgt_sec_deps); + xtensa_append_section_deps (deps, tgt_sec); + } + + /* Add the edges. */ + src_edge = (reloc_deps_e *) xmalloc (sizeof (reloc_deps_e)); + memset (src_edge, 0, sizeof (reloc_deps_e)); + src_edge->src = src_sec; + src_edge->tgt = tgt_sec; + src_edge->next = src_sec_deps->succs; + src_sec_deps->succs = src_edge; + + tgt_edge = (reloc_deps_e *) xmalloc (sizeof (reloc_deps_e)); + memset (tgt_edge, 0, sizeof (reloc_deps_e)); + tgt_edge->src = src_sec; + tgt_edge->tgt = tgt_sec; + tgt_edge->next = tgt_sec_deps->preds; + tgt_sec_deps->preds = tgt_edge; +} + + +void +build_deps_graph_callback (src_sec, src_offset, + target_sec, target_offset, closure) + asection *src_sec; + bfd_vma src_offset ATTRIBUTE_UNUSED; + asection *target_sec; + bfd_vma target_offset ATTRIBUTE_UNUSED; + PTR closure; +{ + reloc_deps_graph *deps; + deps = (reloc_deps_graph*) closure; + + /* If the target is defined. */ + if (target_sec != NULL) + add_deps_edge (deps, src_sec, target_sec); +} + + +reloc_deps_graph * +ld_build_required_section_dependence (s) + lang_statement_union_type *s; +{ + reloc_deps_graph *deps; + xtensa_ld_iter_stack *stack = NULL; + + deps = (reloc_deps_graph*) xmalloc (sizeof (reloc_deps_graph)); + deps->sections = NULL; + deps->count = 0; + deps->size = 0; + + for (iter_stack_create (&stack, s); + !iter_stack_empty (&stack); + iter_stack_next (&stack)) + { + lang_statement_union_type *l = iter_stack_current (&stack); + + if (l->header.type == lang_input_section_enum) + { + lang_input_section_type *input; + input = &l->input_section; + xtensa_callback_required_dependence (input->ifile->the_bfd, + input->section, + &link_info, + /* Use the same closure. */ + build_deps_graph_callback, + (PTR) deps); + } + } + return deps; +} + + +#if EXTRA_VALIDATION +size_t +ld_count_children (s) + lang_statement_union_type *s; +{ + size_t count = 0; + xtensa_ld_iter_stack *stack = NULL; + for (iter_stack_create (&stack, s); + !iter_stack_empty (&stack); + iter_stack_next (&stack)) + { + lang_statement_union_type *l = iter_stack_current (&stack); + ASSERT (l != NULL); + count++; + } + return count; +} +#endif /* EXTRA_VALIDATION */ + + +void +xtensa_wild_group_interleave_callback (statement) + lang_statement_union_type * statement; +{ + lang_wild_statement_type *w; + reloc_deps_graph *deps; + if (statement->header.type == lang_wild_statement_enum) + { +#if EXTRA_VALIDATION + size_t old_child_count; + size_t new_child_count; +#endif + bfd_boolean no_reorder; + + w = &statement->wild_statement; + + no_reorder = FALSE; + + /* If it has 0 or 1 section bound, then do not reorder. */ + if (w->children.head == NULL + || (w->children.head->header.type == lang_input_section_enum + && w->children.head->header.next == NULL)) + no_reorder = TRUE; + + if (w->filenames_sorted) + no_reorder = TRUE; + + /* Check for sorting in a section list wildcard spec as well. */ + if (!no_reorder) + { + struct wildcard_list *l; + for (l = w->section_list; l != NULL; l = l->next) + { + if (l->spec.sorted == TRUE) + { + no_reorder = TRUE; + break; + } + } + } + + /* Special case until the NOREORDER linker directive is supported: + *(.init) output sections and *(.fini) specs may NOT be reordered. */ + + /* Check for sorting in a section list wildcard spec as well. */ + if (!no_reorder) + { + struct wildcard_list *l; + for (l = w->section_list; l != NULL; l = l->next) + { + if (l->spec.name + && ((strcmp (".init", l->spec.name) == 0) + || (strcmp (".fini", l->spec.name) == 0))) + { + no_reorder = TRUE; + break; + } + } + } + +#if EXTRA_VALIDATION + old_child_count = ld_count_children (statement); +#endif + + /* It is now officially a target. Build the graph of source + section -> target section (kept as a list of edges). */ + deps = ld_build_required_section_dependence (statement); + + /* If this wildcard does not reorder.... */ + if (!no_reorder && deps->count != 0) + { + /* First check for reverse dependences. Fix if possible. */ + xtensa_layout_wild (deps, w); + + xtensa_move_dependencies_to_front (deps, w); +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + + xtensa_colocate_literals (deps, statement); + +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + } + + /* Clean up. */ + free_reloc_deps_graph (deps); + } +} + + +void +xtensa_wild_group_interleave (s) + lang_statement_union_type *s; +{ + lang_for_each_statement_worker (xtensa_wild_group_interleave_callback, s); +} + + +void +xtensa_layout_wild (deps, w) + const reloc_deps_graph *deps; + lang_wild_statement_type *w; +{ + /* If it does not fit initially, we need to do this step. Move all + of the wild literal sections to a new list, then move each of + them back in just before the first section they depend on. */ + lang_statement_union_type **s_p; +#if EXTRA_VALIDATION + size_t old_count, new_count; + size_t ct1, ct2; +#endif + + lang_wild_statement_type literal_wild; + literal_wild.header.next = NULL; + literal_wild.header.type = lang_wild_statement_enum; + literal_wild.filename = NULL; + literal_wild.filenames_sorted = FALSE; + literal_wild.section_list = NULL; + literal_wild.keep_sections = FALSE; + literal_wild.children.head = NULL; + literal_wild.children.tail = &literal_wild.children.head; + +#if EXTRA_VALIDATION + old_count = ld_count_children ((lang_statement_union_type*) w); +#endif + + s_p = &w->children.head; + while (*s_p != NULL) + { + lang_statement_union_type *l = *s_p; + if (l->header.type == lang_input_section_enum) + { + if (section_is_target (deps, l) + && ! section_is_source (deps, l)) + { + /* Detach. */ + *s_p = l->header.next; + if (*s_p == NULL) + w->children.tail = s_p; + l->header.next = NULL; + + /* Append. */ + *literal_wild.children.tail = l; + literal_wild.children.tail = &l->header.next; + continue; + } + } + s_p = &(*s_p)->header.next; + } + +#if EXTRA_VALIDATION + ct1 = ld_count_children ((lang_statement_union_type*) w); + ct2 = ld_count_children ((lang_statement_union_type*) &literal_wild); + + ASSERT (old_count == (ct1 + ct2)); +#endif + + /* Now place them back in front of their dependent sections. */ + + while (literal_wild.children.head != NULL) + { + lang_statement_union_type *lit = literal_wild.children.head; + bfd_boolean placed = FALSE; + +#if EXTRA_VALIDATION + ASSERT (ct2 > 0); + ct2--; +#endif + + /* Detach. */ + literal_wild.children.head = lit->header.next; + if (literal_wild.children.head == NULL) + literal_wild.children.tail = &literal_wild.children.head; + lit->header.next = NULL; + + /* Find a spot to place it. */ + for (s_p = &w->children.head; *s_p != NULL; s_p = &(*s_p)->header.next) + { + lang_statement_union_type *src = *s_p; + if (deps_has_edge (deps, src, lit)) + { + /* Place it here. */ + lit->header.next = *s_p; + *s_p = lit; + placed = TRUE; + break; + } + } + + if (!placed) + { + /* Put it at the end. */ + *w->children.tail = lit; + w->children.tail = &lit->header.next; + } + } + +#if EXTRA_VALIDATION + new_count = ld_count_children ((lang_statement_union_type*) w); + ASSERT (new_count == old_count); +#endif +} + + +void +xtensa_colocate_output_literals_callback (statement) + lang_statement_union_type * statement; +{ + lang_output_section_statement_type *os; + reloc_deps_graph *deps; + if (statement->header.type == lang_output_section_statement_enum) + { + /* Now, we walk over the contours of the output section statement. + + First we build the literal section dependences as before. + + At the first uniquely_literal section, we mark it as a good + spot to place other literals. Continue walking (and counting + sizes) until we find the next literal section. If this + section can be moved to the first one, then we move it. If + we every find a modification of ".", start over. If we find + a labeling of the current location, start over. Finally, at + the end, if we require page alignment, add page alignments. */ + +#if EXTRA_VALIDATION + size_t old_child_count; + size_t new_child_count; +#endif + bfd_boolean no_reorder = FALSE; + + os = &statement->output_section_statement; + +#if EXTRA_VALIDATION + old_child_count = ld_count_children (statement); +#endif + + /* It is now officially a target. Build the graph of source + section -> target section (kept as a list of edges). */ + + deps = ld_build_required_section_dependence (statement); + + /* If this wildcard does not reorder.... */ + if (!no_reorder) + { + /* First check for reverse dependences. Fix if possible. */ + xtensa_colocate_literals (deps, statement); + +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + } + + /* Insert align/offset assignment statement. */ + if (xtensa_use_literal_pages) + { + ld_xtensa_insert_page_offsets ((bfd_vma) 0, statement, deps, + xtensa_use_literal_pages); + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, + statement); + } + + /* Clean up. */ + free_reloc_deps_graph (deps); + } +} + + +void +xtensa_colocate_output_literals (s) + lang_statement_union_type *s; +{ + lang_for_each_statement_worker (xtensa_colocate_output_literals_callback, s); +} + + +void +xtensa_ldlang_clear_addresses (statement) + lang_statement_union_type * statement; +{ + switch (statement->header.type) + { + case lang_input_section_enum: + { + asection *bfd_section = statement->input_section.section; + bfd_section->output_offset = 0; + } + break; + default: + break; + } +} + + +bfd_vma +ld_assign_relative_paged_dot (dot, s, deps, lit_align) + bfd_vma dot; + lang_statement_union_type *s; + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; + bfd_boolean lit_align; +{ + /* Walk through all of the input statements in this wild statement + assign dot to all of them. */ + + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + bfd_boolean first_section = FALSE; + bfd_boolean in_literals = FALSE; + + for (iter_stack_create (stack_p, s); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_input_section_enum: + { + asection *section = l->input_section.section; + size_t align_pow = section->alignment_power; + bfd_boolean do_xtensa_alignment = FALSE; + + if (lit_align) + { + bfd_boolean sec_is_target = section_is_target (deps, l); + bfd_boolean sec_is_source = section_is_source (deps, l); + + if (section->_raw_size != 0 + && (first_section + || (in_literals && !sec_is_target) + || (!in_literals && sec_is_target))) + { + do_xtensa_alignment = TRUE; + } + first_section = FALSE; + if (section->_raw_size != 0) + in_literals = (sec_is_target && !sec_is_source); + } + + if (do_xtensa_alignment && xtensa_page_power != 0) + dot += (1 << xtensa_page_power); + + dot = align_power (dot, align_pow); + section->output_offset = dot; + dot += section->_raw_size; + } + break; + case lang_fill_statement_enum: + dot += l->fill_statement.size; + break; + case lang_padding_statement_enum: + dot += l->padding_statement.size; + break; + default: + break; + } + } + return dot; +} + + +bfd_boolean +ld_local_file_relocations_fit (statement, deps) + lang_statement_union_type *statement; + const reloc_deps_graph *deps ATTRIBUTE_UNUSED; +{ + /* Walk over all of the dependencies that we identified and make + sure that IF the source and target are here (addr != 0): + 1) target addr < source addr + 2) (roundup(source + source_size, 4) - rounddown(target, 4)) + < (256K - (1 << bad align)) + Need a worst-case proof.... */ + + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + size_t max_align_power = 0; + size_t align_penalty = 256; + reloc_deps_e *e; + size_t i; + + /* Find the worst-case alignment requirement for this set of statements. */ + for (iter_stack_create (stack_p, statement); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + if (l->header.type == lang_input_section_enum) + { + lang_input_section_type *input = &l->input_section; + asection *section = input->section; + if (section->alignment_power > max_align_power) + max_align_power = section->alignment_power; + } + } + + /* Now check that everything fits. */ + for (i = 0; i < deps->count; i++) + { + asection *sec = deps->sections[i]; + const reloc_deps_section *deps_section = + xtensa_get_section_deps (deps, sec); + if (deps_section) + { + /* We choose to walk through the successors. */ + for (e = deps_section->succs; e != NULL; e = e->next) + { + if ((e->src != e->tgt) + && e->src->output_section == e->tgt->output_section + && e->src->output_offset != 0 + && e->tgt->output_offset != 0) + { + bfd_vma l32r_addr = + align_power (e->src->output_offset + e->src->_raw_size, 2); + bfd_vma target_addr = e->tgt->output_offset & (~3); + if (l32r_addr < target_addr) + { + fprintf (stderr, "Warning: " + "l32r target section before l32r\n"); + return FALSE; + } + + if ((l32r_addr - target_addr) > (256*1024 - align_penalty)) + return FALSE; + } + } + } + } + + return TRUE; +} + + +bfd_vma +ld_xtensa_insert_page_offsets (dot, s, deps, lit_align) + bfd_vma dot; + lang_statement_union_type *s; + reloc_deps_graph *deps; + bfd_boolean lit_align; +{ + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + bfd_boolean first_section = FALSE; + bfd_boolean in_literals = FALSE; + + if (!lit_align) + return FALSE; + + for (iter_stack_create (stack_p, s); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_input_section_enum: + { + asection *section = l->input_section.section; + bfd_boolean do_xtensa_alignment = FALSE; + + if (lit_align) + { + if (section->_raw_size != 0 + && (first_section + || (in_literals && !section_is_target (deps, l)) + || (!in_literals && section_is_target (deps, l)))) + { + do_xtensa_alignment = TRUE; + } + first_section = FALSE; + if (section->_raw_size != 0) + { + in_literals = (section_is_target (deps, l) + && !section_is_source (deps, l)); + } + } + + if (do_xtensa_alignment && xtensa_page_power != 0) + { + /* Create an expression that increments the current address, + i.e., "dot", by (1 << xtensa_align_power). */ + etree_type *name_op = exp_nameop (NAME, "."); + etree_type *addend_op = exp_intop (1 << xtensa_page_power); + etree_type *add_op = exp_binop ('+', name_op, addend_op); + etree_type *assign_op = exp_assop ('=', ".", add_op); + + lang_assignment_statement_type *assign_stmt; + lang_statement_union_type *assign_union; + lang_statement_list_type tmplist; + lang_statement_list_type *old_stat_ptr = stat_ptr; + + /* There is hidden state in "lang_add_assignment". It + appends the new assignment statement to the stat_ptr + list. Thus, we swap it before and after the call. */ + + tmplist.head = NULL; + tmplist.tail = &tmplist.head; + + stat_ptr = &tmplist; + /* Warning: side effect; statement appended to stat_ptr. */ + assign_stmt = lang_add_assignment (assign_op); + assign_union = (lang_statement_union_type *) assign_stmt; + stat_ptr = old_stat_ptr; + + assign_union->header.next = l; + *(*stack_p)->iterloc.loc = assign_union; + iter_stack_next (stack_p); + } + } + break; + default: + break; + } + } + return dot; +} + +EOF + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_NO_RELAX 301 +' + +PARSE_AND_LIST_LONGOPTS=' + { "no-relax", no_argument, NULL, OPTION_NO_RELAX}, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _(" --no-relax\t\tDo not relax branches or coalesce literals\n")); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_NO_RELAX: + disable_relaxation = TRUE; + break; +' + +# Replace some of the standard ELF functions with our own versions. +# +LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse +LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target +LDEMUL_PLACE_ORPHAN=elf_xtensa_place_orphan +LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation + diff --git a/ld/gen-doc.texi b/ld/gen-doc.texi index 0a5acb25d02..5add195d2f9 100644 --- a/ld/gen-doc.texi +++ b/ld/gen-doc.texi @@ -11,6 +11,7 @@ @set MSP430 @set TICOFF @set WIN32 +@set XTENSA @c 3. Properties of this configuration @clear SingleFormat diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 96a87f4173f..ce9bf9097e8 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -45,6 +45,7 @@ @set V850 @set VAX @set WIN32 +@set XTENSA @end ifset @c man end @@ -157,6 +158,9 @@ section entitled ``GNU Free Documentation License''. @ifset WIN32 * Win32:: ld and WIN32 (cygwin/mingw) @end ifset +@ifset XTENSA +* Xtensa:: ld and Xtensa Processors +@end ifset @end ifclear @ifclear SingleFormat * BFD:: BFD @@ -1227,7 +1231,9 @@ This option is only supported on a few targets. @ifset I960 @xref{i960,, @command{ld} and the Intel 960 family}. @end ifset - +@ifset XTENSA +@xref{Xtensa,, @command{ld} and Xtensa Processors}. +@end ifset On some platforms, the @samp{--relax} option performs global optimizations that become possible when the linker resolves addressing @@ -4446,6 +4452,9 @@ functionality are not listed. @ifset WIN32 * WIN32:: @command{ld} and WIN32 (cygwin/mingw) @end ifset +@ifset XTENSA +* Xtensa:: @command{ld} and Xtensa Processors +@end ifset @end menu @end ifset @@ -5077,6 +5086,72 @@ which is probably not what you wanted. @end ifclear @end ifset +@ifset XTENSA +@ifclear GENERIC +@raisesections +@end ifclear + +@node Xtensa +@section @code{ld} and Xtensa Processors + +@cindex Xtensa processors +The default @command{ld} behavior for Xtensa processors is to interpret +@code{SECTIONS} commands so that lists of explicitly named sections in a +specification with a wildcard file will be interleaved when necessary to +keep literal pools within the range of PC-relative load offsets. For +example, with the command: + +@smallexample +SECTIONS +@{ + .text : @{ + *(.literal .text) + @} +@} +@end smallexample + +@noindent +@command{ld} may interleave some of the @code{.literal} +and @code{.text} sections from different object files to ensure that the +literal pools are within the range of PC-relative load offsets. A valid +interleaving might place the @code{.literal} sections from an initial +group of files followed by the @code{.text} sections of that group of +files. Then, the @code{.literal} sections from the rest of the files +and the @code{.text} sections from the rest of the files would follow. +The non-interleaved order can still be specified as: + +@smallexample +SECTIONS +@{ + .text : @{ + *(.literal) *(.text) + @} +@} +@end smallexample + +@cindex @code{--relax} on Xtensa +@cindex relaxing on Xtensa +@kindex --no-relax +The Xtensa version of @command{ld} enables the @option{--relax} option by +default to attempt to reduce space in the output image by combining +literals with identical values. It also provides the +@option{--no-relax} option to disable this optimization. When enabled, +the relaxation algorithm ensures that a literal will only be merged with +another literal when the new merged literal location is within the +offset range of all of its uses. + +The relaxation mechanism will also attempt to optimize +assembler-generated ``longcall'' sequences of +@code{L32R}/@code{CALLX@var{n}} when the target is known to fit into a +@code{CALL@var{n}} instruction encoding. The current optimization +converts the sequence into @code{NOP}/@code{CALL@var{n}} and removes the +literal referenced by the @code{L32R} instruction. + +@ifclear GENERIC +@lowersections +@end ifclear +@end ifset + @ifclear SingleFormat @node BFD @chapter BFD diff --git a/ld/scripttempl/elfxtensa.sc b/ld/scripttempl/elfxtensa.sc new file mode 100644 index 00000000000..a4ba5c8188c --- /dev/null +++ b/ld/scripttempl/elfxtensa.sc @@ -0,0 +1,397 @@ +# +# Unusual variables checked by this code: +# NOP - four byte opcode for no-op (defaults to 0) +# NO_SMALL_DATA - no .sbss/.sbss2/.sdata/.sdata2 sections if not +# empty. +# DATA_ADDR - if end-of-text-plus-one-page isn't right for data start +# INITIAL_READONLY_SECTIONS - at start of text segment +# OTHER_READONLY_SECTIONS - other than .text .init .rodata ... +# (e.g., .PARISC.milli) +# OTHER_TEXT_SECTIONS - these get put in .text when relocating +# OTHER_READWRITE_SECTIONS - other than .data .bss .ctors .sdata ... +# (e.g., .PARISC.global) +# OTHER_BSS_SECTIONS - other than .bss .sbss ... +# OTHER_SECTIONS - at the end +# EXECUTABLE_SYMBOLS - symbols that must be defined for an +# executable (e.g., _DYNAMIC_LINK) +# TEXT_START_SYMBOLS - symbols that appear at the start of the +# .text section. +# DATA_START_SYMBOLS - symbols that appear at the start of the +# .data section. +# OTHER_GOT_SYMBOLS - symbols defined just before .got. +# OTHER_GOT_SECTIONS - sections just after .got. +# OTHER_SDATA_SECTIONS - sections just after .sdata. +# OTHER_BSS_SYMBOLS - symbols that appear at the start of the +# .bss section besides __bss_start. +# TEXT_DYNAMIC - .dynamic in text segment, not data segment. +# EMBEDDED - whether this is for an embedded system. +# SHLIB_TEXT_START_ADDR - if set, add to SIZEOF_HEADERS to set +# start address of shared library. +# INPUT_FILES - INPUT command of files to always include +# WRITABLE_RODATA - if set, the .rodata section should be writable +# INIT_START, INIT_END - statements just before and just after +# combination of .init sections. +# FINI_START, FINI_END - statements just before and just after +# combination of .fini sections. +# STACK_ADDR - start of a .stack section. +# OTHER_END_SYMBOLS - symbols to place right at the end of the script. +# +# When adding sections, do note that the names of some sections are used +# when specifying the start address of the next. +# + +# Many sections come in three flavours. There is the 'real' section, +# like ".data". Then there are the per-procedure or per-variable +# sections, generated by -ffunction-sections and -fdata-sections in GCC, +# and useful for --gc-sections, which for a variable "foo" might be +# ".data.foo". Then there are the linkonce sections, for which the linker +# eliminates duplicates, which are named like ".gnu.linkonce.d.foo". +# The exact correspondences are: +# +# Section Linkonce section +# .text .gnu.linkonce.t.foo +# .rodata .gnu.linkonce.r.foo +# .data .gnu.linkonce.d.foo +# .bss .gnu.linkonce.b.foo +# .sdata .gnu.linkonce.s.foo +# .sbss .gnu.linkonce.sb.foo +# .sdata2 .gnu.linkonce.s2.foo +# .sbss2 .gnu.linkonce.sb2.foo +# .debug_info .gnu.linkonce.wi.foo +# .tdata .gnu.linkonce.td.foo +# .tbss .gnu.linkonce.tb.foo +# +# Each of these can also have corresponding .rel.* and .rela.* sections. + +test -z "$ENTRY" && ENTRY=_start +test -z "${ELFSIZE}" && ELFSIZE=32 +test -z "${ALIGNMENT}" && ALIGNMENT="${ELFSIZE} / 8" +test "$LD_FLAG" = "N" && DATA_ADDR=. +test -n "$CREATE_SHLIB" && test -n "$SHLIB_DATA_ADDR" && COMMONPAGESIZE="" +test -z "$CREATE_SHLIB" && test -n "$DATA_ADDR" && COMMONPAGESIZE="" +DATA_SEGMENT_ALIGN="ALIGN(${SEGMENT_SIZE}) + (. & (${MAXPAGESIZE} - 1))" +DATA_SEGMENT_END="" +if test -n "${COMMONPAGESIZE}"; then + DATA_SEGMENT_ALIGN="ALIGN (${SEGMENT_SIZE}) - ((${MAXPAGESIZE} - .) & (${MAXPAGESIZE} - 1)); . = DATA_SEGMENT_ALIGN (${MAXPAGESIZE}, ${COMMONPAGESIZE})" + DATA_SEGMENT_END=". = DATA_SEGMENT_END (.);" +fi +INTERP=".interp ${RELOCATING-0} : { *(.interp) }" +DYNAMIC=".dynamic ${RELOCATING-0} : { *(.dynamic) }" +RODATA=".rodata ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }" +INIT_LIT=".init.literal 0 : { *(.init.literal) }" +INIT=".init 0 : { *(.init) }" +FINI_LIT=".fini.literal 0 : { *(.fini.literal) }" +FINI=".fini 0 : { *(.fini) }" +if test -z "${NO_SMALL_DATA}"; then + SBSS=".sbss ${RELOCATING-0} : + { + ${RELOCATING+PROVIDE (__sbss_start = .);} + ${RELOCATING+PROVIDE (___sbss_start = .);} + *(.dynsbss) + *(.sbss${RELOCATING+ .sbss.* .gnu.linkonce.sb.*}) + *(.scommon) + ${RELOCATING+PROVIDE (__sbss_end = .);} + ${RELOCATING+PROVIDE (___sbss_end = .);} + }" + SBSS2=".sbss2 ${RELOCATING-0} : { *(.sbss2${RELOCATING+ .sbss2.* .gnu.linkonce.sb2.*}) }" + SDATA="/* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata ${RELOCATING-0} : + { + ${RELOCATING+${SDATA_START_SYMBOLS}} + *(.sdata${RELOCATING+ .sdata.* .gnu.linkonce.s.*}) + }" + SDATA2=".sdata2 ${RELOCATING-0} : { *(.sdata2${RELOCATING+ .sdata2.* .gnu.linkonce.s2.*}) }" + REL_SDATA=".rel.sdata ${RELOCATING-0} : { *(.rel.sdata${RELOCATING+ .rel.sdata.* .rel.gnu.linkonce.s.*}) } + .rela.sdata ${RELOCATING-0} : { *(.rela.sdata${RELOCATING+ .rela.sdata.* .rela.gnu.linkonce.s.*}) }" + REL_SBSS=".rel.sbss ${RELOCATING-0} : { *(.rel.sbss${RELOCATING+ .rel.sbss.* .rel.gnu.linkonce.sb.*}) } + .rela.sbss ${RELOCATING-0} : { *(.rela.sbss${RELOCATING+ .rela.sbss.* .rela.gnu.linkonce.sb.*}) }" + REL_SDATA2=".rel.sdata2 ${RELOCATING-0} : { *(.rel.sdata2${RELOCATING+ .rel.sdata2.* .rel.gnu.linkonce.s2.*}) } + .rela.sdata2 ${RELOCATING-0} : { *(.rela.sdata2${RELOCATING+ .rela.sdata2.* .rela.gnu.linkonce.s2.*}) }" + REL_SBSS2=".rel.sbss2 ${RELOCATING-0} : { *(.rel.sbss2${RELOCATING+ .rel.sbss2.* .rel.gnu.linkonce.sb2.*}) } + .rela.sbss2 ${RELOCATING-0} : { *(.rela.sbss2${RELOCATING+ .rela.sbss2.* .rela.gnu.linkonce.sb2.*}) }" +fi +CTOR=".ctors ${CONSTRUCTING-0} : + { + ${CONSTRUCTING+${CTOR_START}} + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + + KEEP (*crtbegin.o(.ctors)) + + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + + KEEP (*(EXCLUDE_FILE (*crtend.o $OTHER_EXCLUDE_FILES) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + ${CONSTRUCTING+${CTOR_END}} + }" +DTOR=".dtors ${CONSTRUCTING-0} : + { + ${CONSTRUCTING+${DTOR_START}} + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o $OTHER_EXCLUDE_FILES) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + ${CONSTRUCTING+${DTOR_END}} + }" +STACK=" .stack ${RELOCATING-0}${RELOCATING+${STACK_ADDR}} : + { + ${RELOCATING+_stack = .;} + *(.stack) + }" + +# if this is for an embedded system, don't add SIZEOF_HEADERS. +if [ -z "$EMBEDDED" ]; then + test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR} + SIZEOF_HEADERS" +else + test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR}" +fi + +cat < + + * ld-elf/merge.d: xfail xtensa-*-*. + * ld-scripts/crossref.exp: Add -mtext-section-literals to CFLAGS + for Xtensa targets. + * ld-srec/srec.exp: Add -no-relax flag for Xtensa targets. + * ld-xtensa/coalesce1.s: New file. + * ld-xtensa/coalesce2.s: Likewise. + * ld-xtensa/coalesce.exp: Likewise. + * ld-xtensa/coalesce.t: Likewise. + * ld-xtensa/lcall1.s: Likewise. + * ld-xtensa/lcall2.s: Likewise. + * ld-xtensa/lcall.exp: Likewise. + * ld-xtensa/lcall.t: Likewise. + 2003-03-25 Alexandre Oliva * ld-mips-elf/mips-elf.exp: Added... diff --git a/ld/testsuite/ld-elf/merge.d b/ld/testsuite/ld-elf/merge.d index cbefcaf7606..63319278385 100644 --- a/ld/testsuite/ld-elf/merge.d +++ b/ld/testsuite/ld-elf/merge.d @@ -3,7 +3,7 @@ #objdump: -s #xfail: "arc-*-*" "avr-*-*" "cris-*-*" "dlx-*-*" "fr30-*-*" "frv-*-*" #xfail: "hppa*-*-*" "h8300-*-*" "i960-*-*" "ip2k-*-*" "m32r-*-*" "mcore-*-*" -#xfail: "mn10*-*-*" "openrisc-*-*" "pj-*-*" "sparc*-*-*" +#xfail: "mn10*-*-*" "openrisc-*-*" "pj-*-*" "sparc*-*-*" "xtensa-*-*" .*: file format .*elf.* diff --git a/ld/testsuite/ld-scripts/crossref.exp b/ld/testsuite/ld-scripts/crossref.exp index fbc31513991..4fd81b3de89 100644 --- a/ld/testsuite/ld-scripts/crossref.exp +++ b/ld/testsuite/ld-scripts/crossref.exp @@ -26,6 +26,13 @@ if { [which $CC] == 0 } { return } +# Xtensa targets currently default to putting literal values in a separate +# section and that requires linker script support, so put literals in text. +global CFLAGS +if [istarget xtensa*-*-*] { + set CFLAGS "$CFLAGS -mtext-section-literals" +} + if { ![ld_compile $CC "$srcdir/$subdir/cross1.c" tmpdir/cross1.o] \ || ![ld_compile $CC "$srcdir/$subdir/cross2.c" tmpdir/cross2.o] } { unresolved $test1 diff --git a/ld/testsuite/ld-srec/srec.exp b/ld/testsuite/ld-srec/srec.exp index a965c22bbd4..979cfda2907 100644 --- a/ld/testsuite/ld-srec/srec.exp +++ b/ld/testsuite/ld-srec/srec.exp @@ -288,6 +288,11 @@ proc run_srec_test { test objs } { if [istarget v850*-*-elf] { set objs "$objs -L ../gcc -lgcc" } + + # Xtensa ELF targets relax by default; S-Record linker does not + if [istarget xtensa*-*-*] { + set flags "$flags -no-relax" + } if { ![ld_simple_link $ld tmpdir/sr1 "$flags $objs"] \ || ![ld_simple_link $ld tmpdir/sr2.sr "$flags --oformat srec $objs"] } { diff --git a/ld/testsuite/ld-xtensa/coalesce.exp b/ld/testsuite/ld-xtensa/coalesce.exp new file mode 100644 index 00000000000..ef51d6f7e9b --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce.exp @@ -0,0 +1,93 @@ +# Test literal coaslescing for Xtensa targets. +# By David Heine, Tensilica, Inc. +# Copyright 2002, 2003 +# Free Software Foundation, Inc. +# +# This file 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +set testname "COALESCE" + +set OBJDUMPFLAGS "-dr" + +# +# default_ld_objdump +# run objdump on a file +# +proc default_ld_objdump { objdump object outputfile } { + global OBJDUMPFLAGS + global objdump_output + global host_triplet + + if {[which $objdump] == 0} then { + perror "$objdump does not exist" + return 0 + } + + if ![info exists OBJDUMPFLAGS] { set OBJDUMPFLAGS "" } + + verbose -log "$objdump $OBJDUMPFLAGS $object >$outputfile" + + catch "exec $objdump $OBJDUMPFLAGS $object >$outputfile" exec_output + set exec_output [prune_warnings $exec_output] + if [string match "" $exec_output] then { + return 1 + } else { + verbose -log "$exec_output" + perror "$object: objdump failed" + return 0 + } +} + + +if ![ld_assemble $as $srcdir/$subdir/coalesce1.s tmpdir/coalesce1.o] { + unresolved $testname + return +} +if ![ld_assemble $as $srcdir/$subdir/coalesce2.s tmpdir/coalesce2.o] { + unresolved $testname + return +} + +set object "tmpdir/coalesce" +set outputfile "$object.txt" + +if ![ld_simple_link $ld $object "-T $srcdir/$subdir/coalesce.t tmpdir/coalesce1.o tmpdir/coalesce2.o"] { + verbose -log "failure in ld" + fail $testname + return +} + +if ![default_ld_objdump $objdump $object $outputfile ] { + verbose -log "failure in objdump" + fail $testname + return +} + +set file [open $outputfile r] +set found 0 + +while { [gets $file line] != -1 } { + # verbose "$line" 2 + if [regexp "^0000000c
:" $line] { + set found 1 + } +} +close $file +if $found { + pass $testname +} else { + fail $testname +} + diff --git a/ld/testsuite/ld-xtensa/coalesce.t b/ld/testsuite/ld-xtensa/coalesce.t new file mode 100644 index 00000000000..7bff69fc861 --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce.t @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x00000000 : { + *(.literal .text) + } +} diff --git a/ld/testsuite/ld-xtensa/coalesce1.s b/ld/testsuite/ld-xtensa/coalesce1.s new file mode 100644 index 00000000000..437446370c7 --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce1.s @@ -0,0 +1,15 @@ + .global foo + .data + .global g_name + .align 4 +g_name: + .word 0xffffffff + .text + .global main + .align 4 +main: + entry a5,16 + movi a5,20000 + movi a6,g_name + call8 foo + ret diff --git a/ld/testsuite/ld-xtensa/coalesce2.s b/ld/testsuite/ld-xtensa/coalesce2.s new file mode 100644 index 00000000000..962915c8a8c --- /dev/null +++ b/ld/testsuite/ld-xtensa/coalesce2.s @@ -0,0 +1,9 @@ + .text + .global foo + .global g_name +foo: + entry a5,16 + movi a5,20000 + movi a6,g_name + movi a7,50000 + ret diff --git a/ld/testsuite/ld-xtensa/lcall.exp b/ld/testsuite/ld-xtensa/lcall.exp new file mode 100644 index 00000000000..9879a556f88 --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall.exp @@ -0,0 +1,107 @@ +# Test Xtensa longcall optimization. +# By David Heine, Tensilica, Inc. +# Copyright 2002, 2003 +# Free Software Foundation, Inc. +# +# This file 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +set testname "LCALL" + +set OBJDUMPFLAGS "-dr" + +# +# default_ld_objdump +# run objdump on a file +# +proc default_ld_objdump { objdump object outputfile } { + global OBJDUMPFLAGS + global objdump_output + global host_triplet + + if {[which $objdump] == 0} then { + perror "$objdump does not exist" + return 0 + } + + if ![info exists OBJDUMPFLAGS] { set OBJDUMPFLAGS "" } + + verbose -log "$objdump $OBJDUMPFLAGS $object >$outputfile" + + catch "exec $objdump $OBJDUMPFLAGS $object >$outputfile" exec_output + set exec_output [prune_warnings $exec_output] + if [string match "" $exec_output] then { + return 1 + } else { + verbose -log "$exec_output" + perror "$object: objdump failed" + return 0 + } +} + + +if ![ld_assemble $as $srcdir/$subdir/lcall1.s tmpdir/lcall1.o] { + unresolved $testname + return +} +if ![ld_assemble $as $srcdir/$subdir/lcall2.s tmpdir/lcall2.o] { + unresolved $testname + return +} + +set object "tmpdir/lcall" +set outputfile "$object.txt" + +if ![ld_simple_link $ld $object "-T $srcdir/$subdir/lcall.t tmpdir/lcall1.o tmpdir/lcall2.o"] { + verbose -log "failure in ld" + fail $testname + return +} + +if ![default_ld_objdump $objdump $object $outputfile ] { + verbose -log "failure in objdump" + fail $testname + return +} + +set file [open $outputfile r] +while { [gets $file line] != -1 } { + # verbose "$line" 2 + if [regexp "l32r" $line] { + verbose -log "Found an l32r in the linked object" + verbose -log "$line" + fail $testname + } +} +close $file +pass $testname + + +set testname "LCALL2" +set file [open $outputfile r] +set found 0 + +while { [gets $file line] != -1 } { + # verbose "$line" 2 + if [regexp "^00000004 :" $line] { + set found 1 + } +} +close $file +if $found { + pass $testname +} else { + fail $testname +} + diff --git a/ld/testsuite/ld-xtensa/lcall.t b/ld/testsuite/ld-xtensa/lcall.t new file mode 100644 index 00000000000..7bff69fc861 --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall.t @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x00000000 : { + *(.literal .text) + } +} diff --git a/ld/testsuite/ld-xtensa/lcall1.s b/ld/testsuite/ld-xtensa/lcall1.s new file mode 100644 index 00000000000..1056c6a0c15 --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall1.s @@ -0,0 +1,12 @@ +.global foo +.text + .align 4 +label1: + .begin literal + .word 0xffffffff + .end literal + entry a5,16 +.begin longcalls + call4 foo +.end longcalls + nop diff --git a/ld/testsuite/ld-xtensa/lcall2.s b/ld/testsuite/ld-xtensa/lcall2.s new file mode 100644 index 00000000000..f4784f04221 --- /dev/null +++ b/ld/testsuite/ld-xtensa/lcall2.s @@ -0,0 +1,5 @@ +.global foo +foo: + entry a5,16 + nop + ret diff --git a/opcodes/Makefile.am b/opcodes/Makefile.am index d3ecd6fe579..df208325baa 100644 --- a/opcodes/Makefile.am +++ b/opcodes/Makefile.am @@ -160,6 +160,7 @@ CFILES = \ xstormy16-dis.c \ xstormy16-ibld.c \ xstormy16-opc.c \ + xtensa-dis.c \ z8k-dis.c \ z8kgen.c @@ -270,6 +271,7 @@ ALL_MACHINES = \ xstormy16-dis.lo \ xstormy16-ibld.lo \ xstormy16-opc.lo \ + xtensa-dis.lo \ z8k-dis.lo OFILES = @BFD_MACHINES@ @@ -817,6 +819,9 @@ xstormy16-ibld.lo: xstormy16-ibld.c sysdep.h config.h \ xstormy16-opc.lo: xstormy16-opc.c sysdep.h config.h \ $(INCDIR)/ansidecl.h $(BFD_H) $(INCDIR)/symcat.h xstormy16-desc.h \ $(INCDIR)/opcode/cgen.h xstormy16-opc.h $(INCDIR)/libiberty.h +xtensa-dis.lo: xtensa-dis.c $(INCDIR)/xtensa-isa.h \ + $(INCDIR)/ansidecl.h sysdep.h config.h $(INCDIR)/dis-asm.h \ + $(BFD_H) $(INCDIR)/symcat.h z8k-dis.lo: z8k-dis.c sysdep.h config.h $(INCDIR)/ansidecl.h \ $(INCDIR)/dis-asm.h $(BFD_H) $(INCDIR)/symcat.h z8k-opc.h z8kgen.lo: z8kgen.c sysdep.h config.h $(INCDIR)/ansidecl.h \ diff --git a/opcodes/Makefile.in b/opcodes/Makefile.in index 4939324e7a6..0220191ccbe 100644 --- a/opcodes/Makefile.in +++ b/opcodes/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation @@ -271,6 +271,7 @@ CFILES = \ xstormy16-dis.c \ xstormy16-ibld.c \ xstormy16-opc.c \ + xtensa-dis.c \ z8k-dis.c \ z8kgen.c @@ -382,6 +383,7 @@ ALL_MACHINES = \ xstormy16-dis.lo \ xstormy16-ibld.lo \ xstormy16-opc.lo \ + xtensa-dis.lo \ z8k-dis.lo @@ -465,7 +467,7 @@ acinclude.m4 aclocal.m4 config.in configure configure.in DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) -TAR = tar +TAR = gtar GZIP_ENV = --best SOURCES = libopcodes.a.c $(libopcodes_la_SOURCES) OBJECTS = libopcodes.a.$(OBJEXT) $(libopcodes_la_OBJECTS) @@ -1313,6 +1315,9 @@ xstormy16-ibld.lo: xstormy16-ibld.c sysdep.h config.h \ xstormy16-opc.lo: xstormy16-opc.c sysdep.h config.h \ $(INCDIR)/ansidecl.h $(BFD_H) $(INCDIR)/symcat.h xstormy16-desc.h \ $(INCDIR)/opcode/cgen.h xstormy16-opc.h $(INCDIR)/libiberty.h +xtensa-dis.lo: xtensa-dis.c $(INCDIR)/xtensa-isa.h \ + $(INCDIR)/ansidecl.h sysdep.h config.h $(INCDIR)/dis-asm.h \ + $(BFD_H) $(INCDIR)/symcat.h z8k-dis.lo: z8k-dis.c sysdep.h config.h $(INCDIR)/ansidecl.h \ $(INCDIR)/dis-asm.h $(BFD_H) $(INCDIR)/symcat.h z8k-opc.h z8kgen.lo: z8kgen.c sysdep.h config.h $(INCDIR)/ansidecl.h \ diff --git a/opcodes/configure b/opcodes/configure index 171fadde4aa..be6e1b80fec 100755 --- a/opcodes/configure +++ b/opcodes/configure @@ -57,6 +57,7 @@ program_suffix=NONE program_transform_name=s,x,x, silent= site= +sitefile= srcdir= target=NONE verbose= @@ -171,6 +172,7 @@ Configuration: --help print this message --no-create do not create output files --quiet, --silent do not print \`checking...' messages + --site-file=FILE use FILE as the site file --version print the version of autoconf that created configure Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX @@ -341,6 +343,11 @@ EOF -site=* | --site=* | --sit=*) site="$ac_optarg" ;; + -site-file | --site-file | --site-fil | --site-fi | --site-f) + ac_prev=sitefile ;; + -site-file=* | --site-file=* | --site-fil=* | --site-fi=* | --site-f=*) + sitefile="$ac_optarg" ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) @@ -506,12 +513,16 @@ fi srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` # Prefer explicitly selected file to automatically selected ones. -if test -z "$CONFIG_SITE"; then - if test "x$prefix" != xNONE; then - CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" - else - CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" +if test -z "$sitefile"; then + if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi fi +else + CONFIG_SITE="$sitefile" fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then @@ -550,12 +561,12 @@ else fi echo $ac_n "checking for Cygwin environment""... $ac_c" 1>&6 -echo "configure:554: checking for Cygwin environment" >&5 +echo "configure:565: checking for Cygwin environment" >&5 if eval "test \"`echo '$''{'ac_cv_cygwin'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:581: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_cygwin=yes else @@ -583,19 +594,19 @@ echo "$ac_t""$ac_cv_cygwin" 1>&6 CYGWIN= test "$ac_cv_cygwin" = yes && CYGWIN=yes echo $ac_n "checking for mingw32 environment""... $ac_c" 1>&6 -echo "configure:587: checking for mingw32 environment" >&5 +echo "configure:598: checking for mingw32 environment" >&5 if eval "test \"`echo '$''{'ac_cv_mingw32'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:610: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_mingw32=yes else @@ -660,7 +671,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } fi echo $ac_n "checking host system type""... $ac_c" 1>&6 -echo "configure:664: checking host system type" >&5 +echo "configure:675: checking host system type" >&5 host_alias=$host case "$host_alias" in @@ -681,7 +692,7 @@ host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$host" 1>&6 echo $ac_n "checking target system type""... $ac_c" 1>&6 -echo "configure:685: checking target system type" >&5 +echo "configure:696: checking target system type" >&5 target_alias=$target case "$target_alias" in @@ -699,7 +710,7 @@ target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$target" 1>&6 echo $ac_n "checking build system type""... $ac_c" 1>&6 -echo "configure:703: checking build system type" >&5 +echo "configure:714: checking build system type" >&5 build_alias=$build case "$build_alias" in @@ -724,7 +735,7 @@ test "$host_alias" != "$target_alias" && # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:728: checking for $ac_word" >&5 +echo "configure:739: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -754,7 +765,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:758: checking for $ac_word" >&5 +echo "configure:769: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -805,7 +816,7 @@ fi # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:809: checking for $ac_word" >&5 +echo "configure:820: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -837,7 +848,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:841: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:852: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -848,12 +859,12 @@ cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF -#line 852 "configure" +#line 863 "configure" #include "confdefs.h" main(){return(0);} EOF -if { (eval echo configure:857: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:868: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -879,12 +890,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:883: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:894: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:888: checking whether we are using GNU C" >&5 +echo "configure:899: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -893,7 +904,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:897: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:908: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -912,7 +923,7 @@ ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:916: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:927: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -944,7 +955,7 @@ else fi echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 -echo "configure:948: checking for POSIXized ISC" >&5 +echo "configure:959: checking for POSIXized ISC" >&5 if test -d /etc/conf/kconfig.d && grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 then @@ -982,7 +993,7 @@ BFD_VERSION=`sed -n -e 's/^.._INIT_AUTOMAKE.*,[ ]*\([^ ]*\)[ ]*).*/\1/p' < ${ # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:986: checking for a BSD compatible install" >&5 +echo "configure:997: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1035,7 +1046,7 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 -echo "configure:1039: checking whether build environment is sane" >&5 +echo "configure:1050: checking whether build environment is sane" >&5 # Just in case sleep 1 echo timestamp > conftestfile @@ -1092,7 +1103,7 @@ test "$program_suffix" != NONE && test "$program_transform_name" = "" && program_transform_name="s,x,x," echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -echo "configure:1096: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo "configure:1107: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1138,7 +1149,7 @@ EOF missing_dir=`cd $ac_aux_dir && pwd` echo $ac_n "checking for working aclocal""... $ac_c" 1>&6 -echo "configure:1142: checking for working aclocal" >&5 +echo "configure:1153: checking for working aclocal" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1151,7 +1162,7 @@ else fi echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 -echo "configure:1155: checking for working autoconf" >&5 +echo "configure:1166: checking for working autoconf" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1164,7 +1175,7 @@ else fi echo $ac_n "checking for working automake""... $ac_c" 1>&6 -echo "configure:1168: checking for working automake" >&5 +echo "configure:1179: checking for working automake" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1177,7 +1188,7 @@ else fi echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 -echo "configure:1181: checking for working autoheader" >&5 +echo "configure:1192: checking for working autoheader" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1190,7 +1201,7 @@ else fi echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 -echo "configure:1194: checking for working makeinfo" >&5 +echo "configure:1205: checking for working makeinfo" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1213,7 +1224,7 @@ fi # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1217: checking for $ac_word" >&5 +echo "configure:1228: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1245,7 +1256,7 @@ fi # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1249: checking for $ac_word" >&5 +echo "configure:1260: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1277,7 +1288,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1281: checking for $ac_word" >&5 +echo "configure:1292: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1392,7 +1403,7 @@ ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. echo $ac_n "checking for ld used by GCC""... $ac_c" 1>&6 -echo "configure:1396: checking for ld used by GCC" >&5 +echo "configure:1407: checking for ld used by GCC" >&5 case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw @@ -1422,10 +1433,10 @@ echo "configure:1396: checking for ld used by GCC" >&5 esac elif test "$with_gnu_ld" = yes; then echo $ac_n "checking for GNU ld""... $ac_c" 1>&6 -echo "configure:1426: checking for GNU ld" >&5 +echo "configure:1437: checking for GNU ld" >&5 else echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6 -echo "configure:1429: checking for non-GNU ld" >&5 +echo "configure:1440: checking for non-GNU ld" >&5 fi if eval "test \"`echo '$''{'lt_cv_path_LD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1460,7 +1471,7 @@ else fi test -z "$LD" && { echo "configure: error: no acceptable ld found in \$PATH" 1>&2; exit 1; } echo $ac_n "checking if the linker ($LD) is GNU ld""... $ac_c" 1>&6 -echo "configure:1464: checking if the linker ($LD) is GNU ld" >&5 +echo "configure:1475: checking if the linker ($LD) is GNU ld" >&5 if eval "test \"`echo '$''{'lt_cv_prog_gnu_ld'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1477,7 +1488,7 @@ with_gnu_ld=$lt_cv_prog_gnu_ld echo $ac_n "checking for $LD option to reload object files""... $ac_c" 1>&6 -echo "configure:1481: checking for $LD option to reload object files" >&5 +echo "configure:1492: checking for $LD option to reload object files" >&5 if eval "test \"`echo '$''{'lt_cv_ld_reload_flag'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1489,7 +1500,7 @@ reload_flag=$lt_cv_ld_reload_flag test -n "$reload_flag" && reload_flag=" $reload_flag" echo $ac_n "checking for BSD-compatible nm""... $ac_c" 1>&6 -echo "configure:1493: checking for BSD-compatible nm" >&5 +echo "configure:1504: checking for BSD-compatible nm" >&5 if eval "test \"`echo '$''{'lt_cv_path_NM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1527,7 +1538,7 @@ NM="$lt_cv_path_NM" echo "$ac_t""$NM" 1>&6 echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:1531: checking whether ln -s works" >&5 +echo "configure:1542: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1548,7 +1559,7 @@ else fi echo $ac_n "checking how to recognise dependant libraries""... $ac_c" 1>&6 -echo "configure:1552: checking how to recognise dependant libraries" >&5 +echo "configure:1563: checking how to recognise dependant libraries" >&5 if eval "test \"`echo '$''{'lt_cv_deplibs_check_method'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1721,13 +1732,13 @@ file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method echo $ac_n "checking for object suffix""... $ac_c" 1>&6 -echo "configure:1725: checking for object suffix" >&5 +echo "configure:1736: checking for object suffix" >&5 if eval "test \"`echo '$''{'ac_cv_objext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else rm -f conftest* echo 'int i = 1;' > conftest.$ac_ext -if { (eval echo configure:1731: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1742: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then for ac_file in conftest.*; do case $ac_file in *.c) ;; @@ -1747,7 +1758,7 @@ ac_objext=$ac_cv_objext echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 -echo "configure:1751: checking for executable suffix" >&5 +echo "configure:1762: checking for executable suffix" >&5 if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1757,10 +1768,10 @@ else rm -f conftest* echo 'int main () { return 0; }' > conftest.$ac_ext ac_cv_exeext= - if { (eval echo configure:1761: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + if { (eval echo configure:1772: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then for file in conftest.*; do case $file in - *.c | *.o | *.obj) ;; + *.c | *.o | *.obj | *.ilk | *.pdb) ;; *) ac_cv_exeext=`echo $file | sed -e s/conftest//` ;; esac done @@ -1784,7 +1795,7 @@ case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then echo $ac_n "checking for ${ac_tool_prefix}file""... $ac_c" 1>&6 -echo "configure:1788: checking for ${ac_tool_prefix}file" >&5 +echo "configure:1799: checking for ${ac_tool_prefix}file" >&5 if eval "test \"`echo '$''{'lt_cv_path_MAGIC_CMD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1846,7 +1857,7 @@ fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then echo $ac_n "checking for file""... $ac_c" 1>&6 -echo "configure:1850: checking for file" >&5 +echo "configure:1861: checking for file" >&5 if eval "test \"`echo '$''{'lt_cv_path_MAGIC_CMD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1917,7 +1928,7 @@ esac # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1921: checking for $ac_word" >&5 +echo "configure:1932: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1949,7 +1960,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1953: checking for $ac_word" >&5 +echo "configure:1964: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1984,7 +1995,7 @@ fi # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1988: checking for $ac_word" >&5 +echo "configure:1999: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2016,7 +2027,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2020: checking for $ac_word" >&5 +echo "configure:2031: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2083,8 +2094,21 @@ test x"$pic_mode" = xno && libtool_flags="$libtool_flags --prefer-non-pic" case $host in *-*-irix6*) # Find out which ABI we are using. - echo '#line 2087 "configure"' > conftest.$ac_ext - if { (eval echo configure:2088: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + echo '#line 2098 "configure"' > conftest.$ac_ext + if { (eval echo configure:2099: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" @@ -2096,6 +2120,7 @@ case $host in LD="${LD-ld} -64" ;; esac + fi fi rm -rf conftest* ;; @@ -2103,7 +2128,7 @@ case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext - if { (eval echo configure:2107: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + if { (eval echo configure:2132: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then case "`/usr/bin/file conftest.o`" in *ELF-32*) HPUX_IA64_MODE="32" @@ -2121,7 +2146,7 @@ ia64-*-hpux*) SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" echo $ac_n "checking whether the C compiler needs -belf""... $ac_c" 1>&6 -echo "configure:2125: checking whether the C compiler needs -belf" >&5 +echo "configure:2150: checking whether the C compiler needs -belf" >&5 if eval "test \"`echo '$''{'lt_cv_cc_needs_belf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2134,14 +2159,14 @@ ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$a cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2170: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* lt_cv_cc_needs_belf=yes else @@ -2309,7 +2334,7 @@ if test -z "$target" ; then fi echo $ac_n "checking whether to enable maintainer-specific portions of Makefiles""... $ac_c" 1>&6 -echo "configure:2313: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo "configure:2338: checking whether to enable maintainer-specific portions of Makefiles" >&5 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" @@ -2332,7 +2357,7 @@ fi echo $ac_n "checking whether to install libbfd""... $ac_c" 1>&6 -echo "configure:2336: checking whether to install libbfd" >&5 +echo "configure:2361: checking whether to install libbfd" >&5 # Check whether --enable-install-libbfd or --disable-install-libbfd was given. if test "${enable_install_libbfd+set}" = set; then enableval="$enable_install_libbfd" @@ -2369,7 +2394,7 @@ fi echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 -echo "configure:2373: checking for executable suffix" >&5 +echo "configure:2398: checking for executable suffix" >&5 if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2379,10 +2404,10 @@ else rm -f conftest* echo 'int main () { return 0; }' > conftest.$ac_ext ac_cv_exeext= - if { (eval echo configure:2383: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + if { (eval echo configure:2408: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then for file in conftest.*; do case $file in - *.c | *.o | *.obj) ;; + *.c | *.o | *.obj | *.ilk | *.pdb) ;; *) ac_cv_exeext=`echo $file | sed -e s/conftest//` ;; esac done @@ -2405,7 +2430,7 @@ ac_exeext=$EXEEXT # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2409: checking for $ac_word" >&5 +echo "configure:2434: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2435,7 +2460,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2439: checking for $ac_word" >&5 +echo "configure:2464: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2486,7 +2511,7 @@ fi # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2490: checking for $ac_word" >&5 +echo "configure:2515: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2518,7 +2543,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:2522: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:2547: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -2529,12 +2554,12 @@ cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF -#line 2533 "configure" +#line 2558 "configure" #include "confdefs.h" main(){return(0);} EOF -if { (eval echo configure:2538: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2563: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -2560,12 +2585,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:2564: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:2589: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:2569: checking whether we are using GNU C" >&5 +echo "configure:2594: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2574,7 +2599,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:2578: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:2603: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -2593,7 +2618,7 @@ ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:2597: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:2622: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2627,7 +2652,7 @@ fi ALL_LINGUAS="fr sv tr es da de id pt_BR" echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:2631: checking how to run the C preprocessor" >&5 +echo "configure:2656: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -2642,13 +2667,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2652: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2677: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -2659,13 +2684,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2669: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2694: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -2676,13 +2701,13 @@ else rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2686: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2711: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -2709,7 +2734,7 @@ echo "$ac_t""$CPP" 1>&6 # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2713: checking for $ac_word" >&5 +echo "configure:2738: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2737,12 +2762,12 @@ else fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:2741: checking for ANSI C header files" >&5 +echo "configure:2766: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2750,7 +2775,7 @@ else #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2754: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2779: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -2767,7 +2792,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -2785,7 +2810,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -2806,7 +2831,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -2817,7 +2842,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:2821: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2846: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then : else @@ -2841,12 +2866,12 @@ EOF fi echo $ac_n "checking for working const""... $ac_c" 1>&6 -echo "configure:2845: checking for working const" >&5 +echo "configure:2870: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2924: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -2916,21 +2941,21 @@ EOF fi echo $ac_n "checking for inline""... $ac_c" 1>&6 -echo "configure:2920: checking for inline" >&5 +echo "configure:2945: checking for inline" >&5 if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2959: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_inline=$ac_kw; break else @@ -2956,12 +2981,12 @@ EOF esac echo $ac_n "checking for off_t""... $ac_c" 1>&6 -echo "configure:2960: checking for off_t" >&5 +echo "configure:2985: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -2989,12 +3014,12 @@ EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 -echo "configure:2993: checking for size_t" >&5 +echo "configure:3018: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -3024,19 +3049,19 @@ fi # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:3028: checking for working alloca.h" >&5 +echo "configure:3053: checking for working alloca.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *p = alloca(2 * sizeof(int)); ; return 0; } EOF -if { (eval echo configure:3040: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3065: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_header_alloca_h=yes else @@ -3057,12 +3082,12 @@ EOF fi echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:3061: checking for alloca" >&5 +echo "configure:3086: checking for alloca" >&5 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3119: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* ac_cv_func_alloca_works=yes else @@ -3122,12 +3147,12 @@ EOF echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:3126: checking whether alloca needs Cray hooks" >&5 +echo "configure:3151: checking whether alloca needs Cray hooks" >&5 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&6 if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3156: checking for $ac_func" >&5 +echo "configure:3181: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3209: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3207,7 +3232,7 @@ done fi echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:3211: checking stack direction for C alloca" >&5 +echo "configure:3236: checking stack direction for C alloca" >&5 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3215,7 +3240,7 @@ else ac_cv_c_stack_direction=0 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3263: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_c_stack_direction=1 else @@ -3255,21 +3280,21 @@ EOF fi -for ac_hdr in stdlib.h unistd.h sys/stat.h sys/types.h +for ac_hdr in unistd.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:3263: checking for $ac_hdr" >&5 +echo "configure:3288: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3273: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3298: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -3298,12 +3323,12 @@ done for ac_func in getpagesize do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3302: checking for $ac_func" >&5 +echo "configure:3327: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3355: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3351,7 +3376,7 @@ fi done echo $ac_n "checking for working mmap""... $ac_c" 1>&6 -echo "configure:3355: checking for working mmap" >&5 +echo "configure:3380: checking for working mmap" >&5 if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3359,7 +3384,7 @@ else ac_cv_func_mmap_fixed_mapped=no else cat > conftest.$ac_ext < #include -#if HAVE_SYS_TYPES_H -# include -#endif - -#if HAVE_STDLIB_H -# include -#endif - -#if HAVE_SYS_STAT_H -# include -#endif - -#if HAVE_UNISTD_H -# include -#endif - /* This mess was copied from the GNU getpagesize.h. */ #ifndef HAVE_GETPAGESIZE +# ifdef HAVE_UNISTD_H +# include +# endif /* Assume that all systems that can run configure have sys/param.h. */ # ifndef HAVE_SYS_PARAM_H @@ -3512,7 +3524,7 @@ main() } EOF -if { (eval echo configure:3516: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3528: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_func_mmap_fixed_mapped=yes else @@ -3540,17 +3552,17 @@ unistd.h values.h sys/param.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:3544: checking for $ac_hdr" >&5 +echo "configure:3556: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3554: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3566: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -3580,12 +3592,12 @@ done __argz_count __argz_stringify __argz_next do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3584: checking for $ac_func" >&5 +echo "configure:3596: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3624: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3637,12 +3649,12 @@ done for ac_func in stpcpy do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3641: checking for $ac_func" >&5 +echo "configure:3653: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3681: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3699,19 +3711,19 @@ EOF if test $ac_cv_header_locale_h = yes; then echo $ac_n "checking for LC_MESSAGES""... $ac_c" 1>&6 -echo "configure:3703: checking for LC_MESSAGES" >&5 +echo "configure:3715: checking for LC_MESSAGES" >&5 if eval "test \"`echo '$''{'am_cv_val_LC_MESSAGES'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { return LC_MESSAGES ; return 0; } EOF -if { (eval echo configure:3715: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3727: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* am_cv_val_LC_MESSAGES=yes else @@ -3732,7 +3744,7 @@ EOF fi fi echo $ac_n "checking whether NLS is requested""... $ac_c" 1>&6 -echo "configure:3736: checking whether NLS is requested" >&5 +echo "configure:3748: checking whether NLS is requested" >&5 # Check whether --enable-nls or --disable-nls was given. if test "${enable_nls+set}" = set; then enableval="$enable_nls" @@ -3752,7 +3764,7 @@ fi EOF echo $ac_n "checking whether included gettext is requested""... $ac_c" 1>&6 -echo "configure:3756: checking whether included gettext is requested" >&5 +echo "configure:3768: checking whether included gettext is requested" >&5 # Check whether --with-included-gettext or --without-included-gettext was given. if test "${with_included_gettext+set}" = set; then withval="$with_included_gettext" @@ -3771,17 +3783,17 @@ fi ac_safe=`echo "libintl.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for libintl.h""... $ac_c" 1>&6 -echo "configure:3775: checking for libintl.h" >&5 +echo "configure:3787: checking for libintl.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:3785: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:3797: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -3798,19 +3810,19 @@ fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for gettext in libc""... $ac_c" 1>&6 -echo "configure:3802: checking for gettext in libc" >&5 +echo "configure:3814: checking for gettext in libc" >&5 if eval "test \"`echo '$''{'gt_cv_func_gettext_libc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { return (int) gettext ("") ; return 0; } EOF -if { (eval echo configure:3814: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3826: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gt_cv_func_gettext_libc=yes else @@ -3826,7 +3838,7 @@ echo "$ac_t""$gt_cv_func_gettext_libc" 1>&6 if test "$gt_cv_func_gettext_libc" != "yes"; then echo $ac_n "checking for bindtextdomain in -lintl""... $ac_c" 1>&6 -echo "configure:3830: checking for bindtextdomain in -lintl" >&5 +echo "configure:3842: checking for bindtextdomain in -lintl" >&5 ac_lib_var=`echo intl'_'bindtextdomain | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3834,7 +3846,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lintl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3861: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3861,19 +3873,19 @@ fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for gettext in libintl""... $ac_c" 1>&6 -echo "configure:3865: checking for gettext in libintl" >&5 +echo "configure:3877: checking for gettext in libintl" >&5 if eval "test \"`echo '$''{'gt_cv_func_gettext_libintl'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3889: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* gt_cv_func_gettext_libintl=yes else @@ -3901,7 +3913,7 @@ EOF # Extract the first word of "msgfmt", so it can be a program name with args. set dummy msgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3905: checking for $ac_word" >&5 +echo "configure:3917: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3935,12 +3947,12 @@ fi for ac_func in dcgettext do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3939: checking for $ac_func" >&5 +echo "configure:3951: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:3979: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3990,7 +4002,7 @@ done # Extract the first word of "gmsgfmt", so it can be a program name with args. set dummy gmsgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:3994: checking for $ac_word" >&5 +echo "configure:4006: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_GMSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4026,7 +4038,7 @@ fi # Extract the first word of "xgettext", so it can be a program name with args. set dummy xgettext; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4030: checking for $ac_word" >&5 +echo "configure:4042: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_XGETTEXT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4058,7 +4070,7 @@ else fi cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4082: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* CATOBJEXT=.gmo DATADIRNAME=share @@ -4098,7 +4110,7 @@ fi # Extract the first word of "msgfmt", so it can be a program name with args. set dummy msgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4102: checking for $ac_word" >&5 +echo "configure:4114: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4132,7 +4144,7 @@ fi # Extract the first word of "gmsgfmt", so it can be a program name with args. set dummy gmsgfmt; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4136: checking for $ac_word" >&5 +echo "configure:4148: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_GMSGFMT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4168,7 +4180,7 @@ fi # Extract the first word of "xgettext", so it can be a program name with args. set dummy xgettext; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4172: checking for $ac_word" >&5 +echo "configure:4184: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_XGETTEXT'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4258,7 +4270,7 @@ fi LINGUAS= else echo $ac_n "checking for catalogs to be installed""... $ac_c" 1>&6 -echo "configure:4262: checking for catalogs to be installed" >&5 +echo "configure:4274: checking for catalogs to be installed" >&5 NEW_LINGUAS= for lang in ${LINGUAS=$ALL_LINGUAS}; do case "$ALL_LINGUAS" in @@ -4286,17 +4298,17 @@ echo "configure:4262: checking for catalogs to be installed" >&5 if test "$CATOBJEXT" = ".cat"; then ac_safe=`echo "linux/version.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for linux/version.h""... $ac_c" 1>&6 -echo "configure:4290: checking for linux/version.h" >&5 +echo "configure:4302: checking for linux/version.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:4300: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:4312: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -4374,7 +4386,7 @@ if test "x$cross_compiling" = "xno"; then EXEEXT_FOR_BUILD='$(EXEEXT)' else echo $ac_n "checking for build system executable suffix""... $ac_c" 1>&6 -echo "configure:4378: checking for build system executable suffix" >&5 +echo "configure:4390: checking for build system executable suffix" >&5 if eval "test \"`echo '$''{'bfd_cv_build_exeext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4411,7 +4423,7 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:4415: checking for a BSD compatible install" >&5 +echo "configure:4427: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4468,17 +4480,17 @@ for ac_hdr in string.h strings.h stdlib.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:4472: checking for $ac_hdr" >&5 +echo "configure:4484: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:4482: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:4494: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -4666,6 +4678,7 @@ if test x${all_targets} = xfalse ; then bfd_w65_arch) ta="$ta w65-dis.lo" ;; bfd_we32k_arch) ;; bfd_xstormy16_arch) ta="$ta xstormy16-asm.lo xstormy16-desc.lo xstormy16-dis.lo xstormy16-ibld.lo xstormy16-opc.lo" using_cgen=yes ;; + bfd_xtensa_arch) ta="$ta xtensa-dis.lo" ;; bfd_z8k_arch) ta="$ta z8k-dis.lo" ;; bfd_frv_arch) ta="$ta frv-asm.lo frv-desc.lo frv-dis.lo frv-ibld.lo frv-opc.lo" using_cgen=yes ;; diff --git a/opcodes/configure.in b/opcodes/configure.in index 6e74b15cc3b..e4014f1bab4 100644 --- a/opcodes/configure.in +++ b/opcodes/configure.in @@ -241,6 +241,7 @@ if test x${all_targets} = xfalse ; then bfd_w65_arch) ta="$ta w65-dis.lo" ;; bfd_we32k_arch) ;; bfd_xstormy16_arch) ta="$ta xstormy16-asm.lo xstormy16-desc.lo xstormy16-dis.lo xstormy16-ibld.lo xstormy16-opc.lo" using_cgen=yes ;; + bfd_xtensa_arch) ta="$ta xtensa-dis.lo" ;; bfd_z8k_arch) ta="$ta z8k-dis.lo" ;; bfd_frv_arch) ta="$ta frv-asm.lo frv-desc.lo frv-dis.lo frv-ibld.lo frv-opc.lo" using_cgen=yes ;; diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c index 7e6ba0b807d..1408f39b1c1 100644 --- a/opcodes/disassemble.c +++ b/opcodes/disassemble.c @@ -68,6 +68,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define ARCH_vax #define ARCH_w65 #define ARCH_xstormy16 +#define ARCH_xtensa #define ARCH_z8k #define ARCH_frv #define ARCH_iq2000 @@ -343,6 +344,11 @@ disassembler (abfd) disassemble = print_insn_xstormy16; break; #endif +#ifdef ARCH_xtensa + case bfd_arch_xtensa: + disassemble = print_insn_xtensa; + break; +#endif #ifdef ARCH_z8k case bfd_arch_z8k: if (bfd_get_mach(abfd) == bfd_mach_z8001) diff --git a/opcodes/xtensa-dis.c b/opcodes/xtensa-dis.c new file mode 100644 index 00000000000..bf5f5bfa192 --- /dev/null +++ b/opcodes/xtensa-dis.c @@ -0,0 +1,526 @@ +/* xtensa-dis.c. Disassembly functions for Xtensa. + Copyright 2003 Free Software Foundation, Inc. + Contributed by Bob Wilson at Tensilica, Inc. (bwilson@tensilica.com) + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version 2, + or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#include +#include +#include +#include +#include "xtensa-isa.h" +#include "ansidecl.h" +#include "sysdep.h" +#include "dis-asm.h" + +#include + +#ifndef MAX +#define MAX(a,b) (a > b ? a : b) +#endif + +static char* state_names[256] = +{ + "lbeg", /* 0 */ + "lend", /* 1 */ + "lcount", /* 2 */ + "sar", /* 3 */ + "br", /* 4 */ + + "reserved_5", /* 5 */ + "reserved_6", /* 6 */ + "reserved_7", /* 7 */ + + "av", /* 8 */ + "avh", /* 9 */ + "bv", /* 10 */ + "sav", /* 11 */ + "scompare1", /* 12 */ + + "reserved_13", /* 13 */ + "reserved_14", /* 14 */ + "reserved_15", /* 15 */ + + "acclo", /* 16 */ + "acchi", /* 17 */ + + "reserved_18", /* 18 */ + "reserved_19", /* 19 */ + "reserved_20", /* 20 */ + "reserved_21", /* 21 */ + "reserved_22", /* 22 */ + "reserved_23", /* 23 */ + "reserved_24", /* 24 */ + "reserved_25", /* 25 */ + "reserved_26", /* 26 */ + "reserved_27", /* 27 */ + "reserved_28", /* 28 */ + "reserved_29", /* 29 */ + "reserved_30", /* 30 */ + "reserved_31", /* 31 */ + + "mr0", /* 32 */ + "mr1", /* 33 */ + "mr2", /* 34 */ + "mr3", /* 35 */ + + "reserved_36", /* 36 */ + "reserved_37", /* 37 */ + "reserved_38", /* 38 */ + "reserved_39", /* 39 */ + "reserved_40", /* 40 */ + "reserved_41", /* 41 */ + "reserved_42", /* 42 */ + "reserved_43", /* 43 */ + "reserved_44", /* 44 */ + "reserved_45", /* 45 */ + "reserved_46", /* 46 */ + "reserved_47", /* 47 */ + "reserved_48", /* 48 */ + "reserved_49", /* 49 */ + "reserved_50", /* 50 */ + "reserved_51", /* 51 */ + "reserved_52", /* 52 */ + "reserved_53", /* 53 */ + "reserved_54", /* 54 */ + "reserved_55", /* 55 */ + "reserved_56", /* 56 */ + "reserved_57", /* 57 */ + "reserved_58", /* 58 */ + "reserved_59", /* 59 */ + "reserved_60", /* 60 */ + "reserved_61", /* 61 */ + "reserved_62", /* 62 */ + "reserved_63", /* 63 */ + + "reserved_64", /* 64 */ + "reserved_65", /* 65 */ + "reserved_66", /* 66 */ + "reserved_67", /* 67 */ + "reserved_68", /* 68 */ + "reserved_69", /* 69 */ + "reserved_70", /* 70 */ + "reserved_71", /* 71 */ + + "wb", /* 72 */ + "ws", /* 73 */ + + "reserved_74", /* 74 */ + "reserved_75", /* 75 */ + "reserved_76", /* 76 */ + "reserved_77", /* 77 */ + "reserved_78", /* 78 */ + "reserved_79", /* 79 */ + "reserved_80", /* 80 */ + "reserved_81", /* 81 */ + "reserved_82", /* 82 */ + + "ptevaddr", /* 83 */ + + "reserved_84", /* 84 */ + "reserved_85", /* 85 */ + "reserved_86", /* 86 */ + "reserved_87", /* 87 */ + "reserved_88", /* 88 */ + "reserved_89", /* 89 */ + + "rasid", /* 90 */ + "itlbcfg", /* 91 */ + "dtlbcfg", /* 92 */ + + "reserved_93", /* 93 */ + "reserved_94", /* 94 */ + "reserved_95", /* 95 */ + + "ibreakenable", /* 96 */ + + "reserved_97", /* 97 */ + + "cacheattr", /* 98 */ + + "reserved_99", /* 99 */ + "reserved_100", /* 100 */ + "reserved_101", /* 101 */ + "reserved_102", /* 102 */ + "reserved_103", /* 103 */ + + "ddr", /* 104 */ + + "reserved_105", /* 105 */ + "reserved_106", /* 106 */ + "reserved_107", /* 107 */ + "reserved_108", /* 108 */ + "reserved_109", /* 109 */ + "reserved_110", /* 110 */ + "reserved_111", /* 111 */ + "reserved_112", /* 112 */ + "reserved_113", /* 113 */ + "reserved_114", /* 114 */ + "reserved_115", /* 115 */ + "reserved_116", /* 116 */ + "reserved_117", /* 117 */ + "reserved_118", /* 118 */ + "reserved_119", /* 119 */ + "reserved_120", /* 120 */ + "reserved_121", /* 121 */ + "reserved_122", /* 122 */ + "reserved_123", /* 123 */ + "reserved_124", /* 124 */ + "reserved_125", /* 125 */ + "reserved_126", /* 126 */ + "reserved_127", /* 127 */ + + "ibreaka0", /* 128 */ + "ibreaka1", /* 129 */ + "ibreaka2", /* 130 */ + "ibreaka3", /* 131 */ + "ibreaka4", /* 132 */ + "ibreaka5", /* 133 */ + "ibreaka6", /* 134 */ + "ibreaka7", /* 135 */ + "ibreaka8", /* 136 */ + "ibreaka9", /* 137 */ + "ibreaka10", /* 138 */ + "ibreaka11", /* 139 */ + "ibreaka12", /* 140 */ + "ibreaka13", /* 141 */ + "ibreaka14", /* 142 */ + "ibreaka15", /* 143 */ + + "dbreaka0", /* 144 */ + "dbreaka1", /* 145 */ + "dbreaka2", /* 146 */ + "dbreaka3", /* 147 */ + "dbreaka4", /* 148 */ + "dbreaka5", /* 149 */ + "dbreaka6", /* 150 */ + "dbreaka7", /* 151 */ + "dbreaka8", /* 152 */ + "dbreaka9", /* 153 */ + "dbreaka10", /* 154 */ + "dbreaka11", /* 155 */ + "dbreaka12", /* 156 */ + "dbreaka13", /* 157 */ + "dbreaka14", /* 158 */ + "dbreaka15", /* 159 */ + + "dbreakc0", /* 160 */ + "dbreakc1", /* 161 */ + "dbreakc2", /* 162 */ + "dbreakc3", /* 163 */ + "dbreakc4", /* 164 */ + "dbreakc5", /* 165 */ + "dbreakc6", /* 166 */ + "dbreakc7", /* 167 */ + "dbreakc8", /* 168 */ + "dbreakc9", /* 169 */ + "dbreakc10", /* 170 */ + "dbreakc11", /* 171 */ + "dbreakc12", /* 172 */ + "dbreakc13", /* 173 */ + "dbreakc14", /* 174 */ + "dbreakc15", /* 175 */ + + "reserved_176", /* 176 */ + + "epc1", /* 177 */ + "epc2", /* 178 */ + "epc3", /* 179 */ + "epc4", /* 180 */ + "epc5", /* 181 */ + "epc6", /* 182 */ + "epc7", /* 183 */ + "epc8", /* 184 */ + "epc9", /* 185 */ + "epc10", /* 186 */ + "epc11", /* 187 */ + "epc12", /* 188 */ + "epc13", /* 189 */ + "epc14", /* 190 */ + "epc15", /* 191 */ + "depc", /* 192 */ + + "reserved_193", /* 193 */ + + "eps2", /* 194 */ + "eps3", /* 195 */ + "eps4", /* 196 */ + "eps5", /* 197 */ + "eps6", /* 198 */ + "eps7", /* 199 */ + "eps8", /* 200 */ + "eps9", /* 201 */ + "eps10", /* 202 */ + "eps11", /* 203 */ + "eps12", /* 204 */ + "eps13", /* 205 */ + "eps14", /* 206 */ + "eps15", /* 207 */ + + "reserved_208", /* 208 */ + + "excsave1", /* 209 */ + "excsave2", /* 210 */ + "excsave3", /* 211 */ + "excsave4", /* 212 */ + "excsave5", /* 213 */ + "excsave6", /* 214 */ + "excsave7", /* 215 */ + "excsave8", /* 216 */ + "excsave9", /* 217 */ + "excsave10", /* 218 */ + "excsave11", /* 219 */ + "excsave12", /* 220 */ + "excsave13", /* 221 */ + "excsave14", /* 222 */ + "excsave15", /* 223 */ + "cpenable", /* 224 */ + + "reserved_225", /* 225 */ + + "interrupt", /* 226 */ + "interrupt2", /* 227 */ + "intenable", /* 228 */ + + "reserved_229", /* 229 */ + + "ps", /* 230 */ + + "reserved_231", /* 231 */ + + "exccause", /* 232 */ + "debugcause", /* 233 */ + "ccount", /* 234 */ + "prid", /* 235 */ + "icount", /* 236 */ + "icountlvl", /* 237 */ + "excvaddr", /* 238 */ + + "reserved_239", /* 239 */ + + "ccompare0", /* 240 */ + "ccompare1", /* 241 */ + "ccompare2", /* 242 */ + "ccompare3", /* 243 */ + + "misc0", /* 244 */ + "misc1", /* 245 */ + "misc2", /* 246 */ + "misc3", /* 247 */ + + "reserved_248", /* 248 */ + "reserved_249", /* 249 */ + "reserved_250", /* 250 */ + "reserved_251", /* 251 */ + "reserved_252", /* 252 */ + "reserved_253", /* 253 */ + "reserved_254", /* 254 */ + "reserved_255", /* 255 */ +}; + + +int show_raw_fields; + +static int fetch_data + PARAMS ((struct disassemble_info *info, bfd_vma memaddr, int numBytes)); +static void print_xtensa_operand + PARAMS ((bfd_vma, struct disassemble_info *, xtensa_operand, + unsigned operand_val, int print_sr_name)); + +struct dis_private { + bfd_byte *byte_buf; + jmp_buf bailout; +}; + +static int +fetch_data (info, memaddr, numBytes) + struct disassemble_info *info; + bfd_vma memaddr; + int numBytes; +{ + int length, status = 0; + struct dis_private *priv = (struct dis_private *) info->private_data; + int insn_size = (numBytes != 0 ? numBytes : + xtensa_insn_maxlength (xtensa_default_isa)); + + /* Read the maximum instruction size, padding with zeros if we go past + the end of the text section. This code will automatically adjust + length when we hit the end of the buffer. */ + + memset (priv->byte_buf, 0, insn_size); + for (length = insn_size; length > 0; length--) + { + status = (*info->read_memory_func) (memaddr, priv->byte_buf, length, + info); + if (status == 0) + return length; + } + (*info->memory_error_func) (status, memaddr, info); + longjmp (priv->bailout, 1); + /*NOTREACHED*/ +} + + +static void +print_xtensa_operand (memaddr, info, opnd, operand_val, print_sr_name) + bfd_vma memaddr; + struct disassemble_info *info; + xtensa_operand opnd; + unsigned operand_val; + int print_sr_name; +{ + char *kind = xtensa_operand_kind (opnd); + int signed_operand_val; + + if (show_raw_fields) + { + if (operand_val < 0xa) + (*info->fprintf_func) (info->stream, "%u", operand_val); + else + (*info->fprintf_func) (info->stream, "0x%x", operand_val); + return; + } + + operand_val = xtensa_operand_decode (opnd, operand_val); + signed_operand_val = (int) operand_val; + + if (xtensa_operand_isPCRelative (opnd)) + { + operand_val = xtensa_operand_undo_reloc (opnd, operand_val, memaddr); + info->target = operand_val; + (*info->print_address_func) (info->target, info); + } + else if (!strcmp (kind, "i")) + { + if (print_sr_name + && signed_operand_val >= 0 + && signed_operand_val <= 255) + (*info->fprintf_func) (info->stream, "%s", + state_names[signed_operand_val]); + else if ((signed_operand_val > -256) && (signed_operand_val < 256)) + (*info->fprintf_func) (info->stream, "%d", signed_operand_val); + else + (*info->fprintf_func) (info->stream, "0x%x",signed_operand_val); + } + else + (*info->fprintf_func) (info->stream, "%s%u", kind, operand_val); +} + + +/* Print the Xtensa instruction at address MEMADDR on info->stream. + Returns length of the instruction in bytes. */ + +int +print_insn_xtensa (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + unsigned operand_val; + int bytes_fetched, size, maxsize, i, noperands; + xtensa_isa isa; + xtensa_opcode opc; + char *op_name; + int print_sr_name; + struct dis_private priv; + static bfd_byte *byte_buf = NULL; + static xtensa_insnbuf insn_buffer = NULL; + + if (!xtensa_default_isa) + (void) xtensa_isa_init (); + + info->target = 0; + maxsize = xtensa_insn_maxlength (xtensa_default_isa); + + /* Set bytes_per_line to control the amount of whitespace between the hex + values and the opcode. For Xtensa, we always print one "chunk" and we + vary bytes_per_chunk to determine how many bytes to print. (objdump + would apparently prefer that we set bytes_per_chunk to 1 and vary + bytes_per_line but that makes it hard to fit 64-bit instructions on + an 80-column screen.) The value of bytes_per_line here is not exactly + right, because objdump adds an extra space for each chunk so that the + amount of whitespace depends on the chunk size. Oh well, it's good + enough.... Note that we set the minimum size to 4 to accomodate + literal pools. */ + info->bytes_per_line = MAX (maxsize, 4); + + /* Allocate buffers the first time through. */ + if (!insn_buffer) + insn_buffer = xtensa_insnbuf_alloc (xtensa_default_isa); + if (!byte_buf) + byte_buf = (bfd_byte *) malloc (MAX (maxsize, 4)); + + priv.byte_buf = byte_buf; + + info->private_data = (PTR) &priv; + if (setjmp (priv.bailout) != 0) + /* Error return. */ + return -1; + + /* Don't set "isa" before the setjmp to keep the compiler from griping. */ + isa = xtensa_default_isa; + + /* Fetch the maximum size instruction. */ + bytes_fetched = fetch_data (info, memaddr, 0); + + /* Copy the bytes into the decode buffer. */ + memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) * + sizeof (xtensa_insnbuf_word))); + xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf); + + opc = xtensa_decode_insn (isa, insn_buffer); + if (opc == XTENSA_UNDEFINED + || ((size = xtensa_insn_length (isa, opc)) > bytes_fetched)) + { + (*info->fprintf_func) (info->stream, ".byte %#02x", priv.byte_buf[0]); + return 1; + } + + op_name = (char *) xtensa_opcode_name (isa, opc); + (*info->fprintf_func) (info->stream, "%s", op_name); + + print_sr_name = (!strcasecmp (op_name, "wsr") + || !strcasecmp (op_name, "xsr") + || !strcasecmp (op_name, "rsr")); + + /* Print the operands (if any). */ + noperands = xtensa_num_operands (isa, opc); + if (noperands > 0) + { + int first = 1; + + (*info->fprintf_func) (info->stream, "\t"); + for (i = 0; i < noperands; i++) + { + xtensa_operand opnd = xtensa_get_operand (isa, opc, i); + + if (first) + first = 0; + else + (*info->fprintf_func) (info->stream, ", "); + operand_val = xtensa_operand_get_field (opnd, insn_buffer); + print_xtensa_operand (memaddr, info, opnd, operand_val, + print_sr_name); + } + } + + info->bytes_per_chunk = size; + info->display_endian = info->endian; + + return size; +} + -- 2.30.2