diff --git a/binutils-loongarch-support.patch b/binutils-loongarch-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..cb4135eda25abdd1992570fd05ee21dd5c61aa09 --- /dev/null +++ b/binutils-loongarch-support.patch @@ -0,0 +1,13120 @@ +From 8f2436219c54357e82ed8b2ad8b3508a46ca9cb9 Mon Sep 17 00:00:00 2001 +From: Li Xing +Date: Wed, 22 Jun 2022 17:28:28 +0800 +Subject: [PATCH] binutils loongarch support + +Signed-off-by: Li Xing +Signed-off-by: Junchao Zhao +--- + bfd/Makefile.am | 17 + + bfd/archures.c | 5 + + bfd/bfd-in2.h | 48 + + bfd/config.bfd | 15 + + bfd/configure.ac | 2 + + bfd/cpu-loongarch.c | 38 + + bfd/elf-bfd.h | 9 + + bfd/elf.c | 109 + + bfd/elfnn-loongarch.c | 3287 +++++++++++++++++++ + bfd/elfxx-loongarch.c | 172 + + bfd/elfxx-loongarch.h | 11 + + bfd/libbfd.h | 43 + + bfd/reloc.c | 89 + + bfd/targets.c | 7 + + binutils/readelf.c | 30 + + binutils/testsuite/binutils-all/objdump.exp | 4 +- + gas/Makefile.am | 26 +- + gas/config/loongarch-lex-wrapper.c | 25 + + gas/config/loongarch-lex.l | 39 + + gas/config/loongarch-parse.y | 420 +++ + gas/config/tc-loongarch.c | 1328 ++++++++ + gas/config/tc-loongarch.h | 77 + + gas/configure.ac | 9 + + gas/configure.tgt | 5 +- + gas/loongarch-lex.c | 1845 +++++++++++ + gas/loongarch-parse.c | 2006 +++++++++++ + gas/loongarch-parse.h | 99 + + include/dis-asm.h | 1 + + include/elf/common.h | 9 + + include/elf/loongarch.h | 95 + + include/opcode/loongarch.h | 215 ++ + ld/Makefile.am | 6 + + ld/configure.tgt | 2 + + ld/emulparams/elf64loongarch-defs.sh | 39 + + ld/emulparams/elf64loongarch.sh | 15 + + ld/emultempl/loongarchelf.em | 90 + + opcodes/Makefile.am | 3 + + opcodes/configure.ac | 1 + + opcodes/disassemble.c | 9 + + opcodes/disassemble.h | 1 + + opcodes/loongarch-coder.c | 446 +++ + opcodes/loongarch-dis.c | 311 ++ + opcodes/loongarch-opc.c | 1453 ++++++++ + 43 files changed, 12457 insertions(+), 4 deletions(-) + create mode 100644 bfd/cpu-loongarch.c + create mode 100644 bfd/elfnn-loongarch.c + create mode 100644 bfd/elfxx-loongarch.c + create mode 100644 bfd/elfxx-loongarch.h + create mode 100644 gas/config/loongarch-lex-wrapper.c + create mode 100644 gas/config/loongarch-lex.l + create mode 100644 gas/config/loongarch-parse.y + create mode 100644 gas/config/tc-loongarch.c + create mode 100644 gas/config/tc-loongarch.h + create mode 100644 gas/loongarch-lex.c + create mode 100644 gas/loongarch-parse.c + create mode 100644 gas/loongarch-parse.h + create mode 100644 include/elf/loongarch.h + create mode 100644 include/opcode/loongarch.h + create mode 100644 ld/emulparams/elf64loongarch-defs.sh + create mode 100644 ld/emulparams/elf64loongarch.sh + create mode 100644 ld/emultempl/loongarchelf.em + create mode 100644 opcodes/loongarch-coder.c + create mode 100644 opcodes/loongarch-dis.c + create mode 100644 opcodes/loongarch-opc.c + +diff --git a/bfd/Makefile.am b/bfd/Makefile.am +index a7990571..9b08572a 100644 +--- a/bfd/Makefile.am ++++ b/bfd/Makefile.am +@@ -120,6 +120,7 @@ ALL_MACHINES = \ + cpu-ip2k.lo \ + cpu-iq2000.lo \ + cpu-lm32.lo \ ++ cpu-loongarch.lo \ + cpu-m10200.lo \ + cpu-m10300.lo \ + cpu-m32c.lo \ +@@ -211,6 +212,7 @@ ALL_MACHINES_CFILES = \ + cpu-ip2k.c \ + cpu-iq2000.c \ + cpu-lm32.c \ ++ cpu-loongarch.c \ + cpu-m10200.c \ + cpu-m10300.c \ + cpu-m32c.c \ +@@ -690,6 +692,8 @@ BFD64_BACKENDS = \ + elf64-ia64.lo \ + elf64-ia64-vms.lo \ + elfxx-ia64.lo \ ++ elf32-loongarch.lo \ ++ elf64-loongarch.lo \ + elfn32-mips.lo \ + elf64-mips.lo \ + elfxx-mips.lo \ +@@ -812,6 +816,7 @@ SOURCE_CFILES = \ + BUILD_CFILES = \ + elf32-aarch64.c elf64-aarch64.c \ + elf32-ia64.c elf64-ia64.c \ ++ elf32-loongarch.c elf64-loongarch.c \ + elf32-riscv.c elf64-riscv.c \ + peigen.c pepigen.c pex64igen.c + +@@ -983,6 +988,18 @@ elf64-ia64.c : elfnn-ia64.c + $(SED) -e s/NN/64/g < $(srcdir)/elfnn-ia64.c > elf64-ia64.new + mv -f elf64-ia64.new elf64-ia64.c + ++elf32-loongarch.c : elfnn-loongarch.c ++ rm -f elf32-loongarch.c ++ echo "#line 1 \"$(srcdir)/elfnn-loongarch.c\"" > elf32-loongarch.new ++ sed -e s/NN/32/g < $(srcdir)/elfnn-loongarch.c >> elf32-loongarch.new ++ mv -f elf32-loongarch.new elf32-loongarch.c ++ ++elf64-loongarch.c : elfnn-loongarch.c ++ rm -f elf64-loongarch.c ++ echo "#line 1 \"$(srcdir)/elfnn-loongarch.c\"" > elf64-loongarch.new ++ sed -e s/NN/64/g < $(srcdir)/elfnn-loongarch.c >> elf64-loongarch.new ++ mv -f elf64-loongarch.new elf64-loongarch.c ++ + elf32-riscv.c : elfnn-riscv.c + rm -f elf32-riscv.c + echo "#line 1 \"$(srcdir)/elfnn-riscv.c\"" > elf32-riscv.new +diff --git a/bfd/archures.c b/bfd/archures.c +index a39925c7..efee3fc0 100644 +--- a/bfd/archures.c ++++ b/bfd/archures.c +@@ -540,6 +540,9 @@ DESCRIPTION + . bfd_arch_nfp, {* Netronome Flow Processor *} + .#define bfd_mach_nfp3200 0x3200 + .#define bfd_mach_nfp6000 0x6000 ++. bfd_arch_loongarch, {* Loongarch *} ++.#define bfd_mach_loongarch32 1 ++.#define bfd_mach_loongarch64 2 + . bfd_arch_last + . }; + */ +@@ -614,6 +617,7 @@ extern const bfd_arch_info_type bfd_iq2000_arch; + extern const bfd_arch_info_type bfd_k1om_arch; + extern const bfd_arch_info_type bfd_l1om_arch; + extern const bfd_arch_info_type bfd_lm32_arch; ++extern const bfd_arch_info_type bfd_loongarch_arch; + extern const bfd_arch_info_type bfd_m32c_arch; + extern const bfd_arch_info_type bfd_m32r_arch; + extern const bfd_arch_info_type bfd_m68hc11_arch; +@@ -710,6 +714,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] = + &bfd_k1om_arch, + &bfd_l1om_arch, + &bfd_lm32_arch, ++ &bfd_loongarch_arch, + &bfd_m32c_arch, + &bfd_m32r_arch, + &bfd_m68hc11_arch, +diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h +index e60bef9a..893af234 100644 +--- a/bfd/bfd-in2.h ++++ b/bfd/bfd-in2.h +@@ -2404,6 +2404,9 @@ enum bfd_architecture + bfd_arch_nfp, /* Netronome Flow Processor */ + #define bfd_mach_nfp3200 0x3200 + #define bfd_mach_nfp6000 0x6000 ++ bfd_arch_loongarch, /* LoongARCH */ ++#define bfd_mach_loongarch32 1 ++#define bfd_mach_loongarch64 2 + bfd_arch_last + }; + +@@ -6518,6 +6521,51 @@ assembler and not (currently) written to any object files. */ + BFD_RELOC_WASM32_CODE_POINTER, + BFD_RELOC_WASM32_INDEX, + BFD_RELOC_WASM32_PLT_SIG, ++ ++/* LoongISA relocations. */ ++ BFD_RELOC_LARCH_TLS_DTPMOD32, ++ BFD_RELOC_LARCH_TLS_DTPREL32, ++ BFD_RELOC_LARCH_TLS_DTPMOD64, ++ BFD_RELOC_LARCH_TLS_DTPREL64, ++ BFD_RELOC_LARCH_TLS_TPREL32, ++ BFD_RELOC_LARCH_TLS_TPREL64, ++ BFD_RELOC_LARCH_MARK_LA, ++ BFD_RELOC_LARCH_MARK_PCREL, ++ BFD_RELOC_LARCH_SOP_PUSH_PCREL, ++ BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE, ++ BFD_RELOC_LARCH_SOP_PUSH_DUP, ++ BFD_RELOC_LARCH_SOP_PUSH_GPREL, ++ BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL, ++ BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT, ++ BFD_RELOC_LARCH_SOP_PUSH_TLS_GD, ++ BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL, ++ BFD_RELOC_LARCH_SOP_ASSERT, ++ BFD_RELOC_LARCH_SOP_NOT, ++ BFD_RELOC_LARCH_SOP_SUB, ++ BFD_RELOC_LARCH_SOP_SL, ++ BFD_RELOC_LARCH_SOP_SR, ++ BFD_RELOC_LARCH_SOP_ADD, ++ BFD_RELOC_LARCH_SOP_AND, ++ BFD_RELOC_LARCH_SOP_IF_ELSE, ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_5, ++ BFD_RELOC_LARCH_SOP_POP_32_U_10_12, ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_12, ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_16, ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2, ++ BFD_RELOC_LARCH_SOP_POP_32_S_5_20, ++ BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2, ++ BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2, ++ BFD_RELOC_LARCH_SOP_POP_32_U, ++ BFD_RELOC_LARCH_ADD8, ++ BFD_RELOC_LARCH_ADD16, ++ BFD_RELOC_LARCH_ADD24, ++ BFD_RELOC_LARCH_ADD32, ++ BFD_RELOC_LARCH_ADD64, ++ BFD_RELOC_LARCH_SUB8, ++ BFD_RELOC_LARCH_SUB16, ++ BFD_RELOC_LARCH_SUB24, ++ BFD_RELOC_LARCH_SUB32, ++ BFD_RELOC_LARCH_SUB64, + BFD_RELOC_UNUSED }; + + typedef enum bfd_reloc_code_real bfd_reloc_code_real_type; +diff --git a/bfd/config.bfd b/bfd/config.bfd +index cf02b010..eca16b52 100644 +--- a/bfd/config.bfd ++++ b/bfd/config.bfd +@@ -174,6 +174,7 @@ i[34567]86) targ_archs=bfd_i386_arch ;; + i370) targ_archs=bfd_i370_arch ;; + ia16) targ_archs=bfd_i386_arch ;; + lm32) targ_archs=bfd_lm32_arch ;; ++loongarch*) targ_archs=bfd_loongarch_arch ;; + m6811*|m68hc11*) targ_archs="bfd_m68hc11_arch bfd_m68hc12_arch bfd_m9s12x_arch bfd_m9s12xg_arch" ;; + m6812*|m68hc12*) targ_archs="bfd_m68hc12_arch bfd_m68hc11_arch bfd_m9s12x_arch bfd_m9s12xg_arch" ;; + m68*) targ_archs=bfd_m68k_arch ;; +@@ -938,6 +939,20 @@ case "${targ}" in + targ_selvecs=lm32_elf32_vec + ;; + ++#ifdef BFD64 ++ loongarch32-*) ++ targ_defvec=loongarch_elf32_vec ++ targ_selvecs="loongarch_elf32_vec" ++ want64=false ++ ;; ++ ++ loongarch64-*) ++ targ_defvec=loongarch_elf64_vec ++ targ_selvecs="loongarch_elf32_vec loongarch_elf64_vec" ++ want64=true ++ ;; ++#endif ++ + m32c-*-elf | m32c-*-rtems*) + targ_defvec=m32c_elf32_vec + targ_underscore=yes +diff --git a/bfd/configure.ac b/bfd/configure.ac +index fa2e0ec0..a75c24b6 100644 +--- a/bfd/configure.ac ++++ b/bfd/configure.ac +@@ -508,6 +508,8 @@ do + l1om_elf64_fbsd_vec) tb="$tb elf64-x86-64.lo elfxx-x86.lo elf-ifunc.lo elf-nacl.lo elf64.lo $elf"; target_size=64 ;; + lm32_elf32_vec) tb="$tb elf32-lm32.lo elf32.lo $elf" ;; + lm32_elf32_fdpic_vec) tb="$tb elf32-lm32.lo elf32.lo $elf" ;; ++ loongarch_elf32_vec) tb="$tb elf32-loongarch.lo elfxx-loongarch.lo elf32.lo elf-ifunc.lo $elf" ;; ++ loongarch_elf64_vec) tb="$tb elf64-loongarch.lo elf64.lo elfxx-loongarch.lo elf32.lo elf-ifunc.lo $elf"; target_size=64 ;; + m32c_elf32_vec) tb="$tb elf32-m32c.lo elf32.lo $elf" ;; + m32r_elf32_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;; + m32r_elf32_le_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;; +diff --git a/bfd/cpu-loongarch.c b/bfd/cpu-loongarch.c +new file mode 100644 +index 00000000..c5579f29 +--- /dev/null ++++ b/bfd/cpu-loongarch.c +@@ -0,0 +1,38 @@ ++#include "sysdep.h" ++#include "bfd.h" ++#include "libbfd.h" ++ ++static const bfd_arch_info_type bfd_loongarch32_arch = ++{ ++ 32, /* 32 bits in a word. */ ++ 32, /* 64 bits in an address. */ ++ 8, /* 8 bits in a byte. */ ++ bfd_arch_loongarch, /* Architecture. */ ++ bfd_mach_loongarch32, /* Machine number - 0 for now. */ ++ "loongarch32", /* Architecture name. */ ++ "Loongarch32", /* Printable name. */ ++ 3, /* Section align power. */ ++ FALSE, /* This is the default architecture. */ ++ bfd_default_compatible, /* Architecture comparison function. */ ++ bfd_default_scan, /* String to architecture conversion. */ ++ bfd_arch_default_fill, /* Default fill. */ ++ NULL, /* Next in list. */ ++}; ++ ++const bfd_arch_info_type bfd_loongarch_arch = ++{ ++ 32, /* 32 bits in a word. */ ++ 64, /* 64 bits in an address. */ ++ 8, /* 8 bits in a byte. */ ++ bfd_arch_loongarch, /* Architecture. */ ++ bfd_mach_loongarch64, /* Machine number of loongarch64 is larger so that loongarch64 is compatible to loongarch32 */ ++ "loongarch64", /* Architecture name. */ ++ "Loongarch64", /* Printable name. */ ++ 3, /* Section align power. */ ++ TRUE, /* This is the default architecture. */ ++ bfd_default_compatible, /* Architecture comparison function. */ ++ bfd_default_scan, /* String to architecture conversion. */ ++ bfd_arch_default_fill, /* Default fill. */ ++ &bfd_loongarch32_arch, /* Next in list. */ ++}; ++ +diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h +index 5b50ef2a..d941e19c 100644 +--- a/bfd/elf-bfd.h ++++ b/bfd/elf-bfd.h +@@ -497,6 +497,7 @@ enum elf_target_id + I386_ELF_DATA, + IA64_ELF_DATA, + LM32_ELF_DATA, ++ LARCH_ELF_DATA, + M32R_ELF_DATA, + M68HC11_ELF_DATA, + M68K_ELF_DATA, +@@ -2604,6 +2605,14 @@ extern char *elfcore_write_aarch_hw_break + (bfd *, char *, int *, const void *, int); + extern char *elfcore_write_aarch_hw_watch + (bfd *, char *, int *, const void *, int); ++extern char *elfcore_write_loongarch_cpucfg ++ (bfd *, char *, int *, const void *, int); ++extern char *elfcore_write_loongarch_lbt ++ (bfd *, char *, int *, const void *, int); ++extern char *elfcore_write_loongarch_lsx ++ (bfd *, char *, int *, const void *, int); ++extern char *elfcore_write_loongarch_lasx ++ (bfd *, char *, int *, const void *, int); + extern char *elfcore_write_lwpstatus + (bfd *, char *, int *, long, int, const void *); + extern char *elfcore_write_register_note +diff --git a/bfd/elf.c b/bfd/elf.c +index ef2c7df0..8e0eb731 100644 +--- a/bfd/elf.c ++++ b/bfd/elf.c +@@ -9421,6 +9421,30 @@ elfcore_grok_aarch_hw_watch (bfd *abfd, Elf_Internal_Note *note) + return elfcore_make_note_pseudosection (abfd, ".reg-aarch-hw-watch", note); + } + ++static bfd_boolean ++elfcore_grok_loongarch_cpucfg (bfd *abfd, Elf_Internal_Note *note) ++{ ++ return elfcore_make_note_pseudosection (abfd, ".reg-loongarch-cpucfg", note); ++} ++ ++static bfd_boolean ++elfcore_grok_loongarch_lbt (bfd *abfd, Elf_Internal_Note *note) ++{ ++ return elfcore_make_note_pseudosection (abfd, ".reg-loongarch-lbt", note); ++} ++ ++static bfd_boolean ++elfcore_grok_loongarch_lsx (bfd *abfd, Elf_Internal_Note *note) ++{ ++ return elfcore_make_note_pseudosection (abfd, ".reg-loongarch-lsx", note); ++} ++ ++static bfd_boolean ++elfcore_grok_loongarch_lasx (bfd *abfd, Elf_Internal_Note *note) ++{ ++ return elfcore_make_note_pseudosection (abfd, ".reg-loongarch-lasx", note); ++} ++ + #if defined (HAVE_PRPSINFO_T) + typedef prpsinfo_t elfcore_psinfo_t; + #if defined (HAVE_PRPSINFO32_T) /* Sparc64 cross Sparc32 */ +@@ -9917,6 +9941,34 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note) + else + return TRUE; + ++ case NT_LARCH_CPUCFG: ++ if (note->namesz == 6 ++ && strcmp (note->namedata, "LINUX") == 0) ++ return elfcore_grok_loongarch_cpucfg (abfd, note); ++ else ++ return TRUE; ++ ++ case NT_LARCH_LBT: ++ if (note->namesz == 6 ++ && strcmp (note->namedata, "LINUX") == 0) ++ return elfcore_grok_loongarch_lbt (abfd, note); ++ else ++ return TRUE; ++ ++ case NT_LARCH_LSX: ++ if (note->namesz == 6 ++ && strcmp (note->namedata, "LINUX") == 0) ++ return elfcore_grok_loongarch_lsx (abfd, note); ++ else ++ return TRUE; ++ ++ case NT_LARCH_LASX: ++ if (note->namesz == 6 ++ && strcmp (note->namedata, "LINUX") == 0) ++ return elfcore_grok_loongarch_lasx (abfd, note); ++ else ++ return TRUE; ++ + case NT_PRPSINFO: + case NT_PSINFO: + if (bed->elf_backend_grok_psinfo) +@@ -11100,6 +11152,55 @@ elfcore_write_aarch_hw_watch (bfd *abfd, + note_name, NT_ARM_HW_WATCH, aarch_hw_watch, size); + } + ++char * ++elfcore_write_loongarch_cpucfg (bfd *abfd, ++ char *buf, ++ int *bufsiz, ++ const void *loongarch_cpucfg, ++ int size) ++{ ++ char *note_name = "LINUX"; ++ return elfcore_write_note (abfd, buf, bufsiz, ++ note_name, NT_LARCH_CPUCFG, ++ loongarch_cpucfg, size); ++} ++ ++char * ++elfcore_write_loongarch_lbt (bfd *abfd, ++ char *buf, ++ int *bufsiz, ++ const void *loongarch_lbt, ++ int size) ++{ ++ char *note_name = "LINUX"; ++ return elfcore_write_note (abfd, buf, bufsiz, ++ note_name, NT_LARCH_LBT, loongarch_lbt, size); ++} ++ ++char * ++elfcore_write_loongarch_lsx (bfd *abfd, ++ char *buf, ++ int *bufsiz, ++ const void *loongarch_lsx, ++ int size) ++{ ++ char *note_name = "LINUX"; ++ return elfcore_write_note (abfd, buf, bufsiz, ++ note_name, NT_LARCH_LSX, loongarch_lsx, size); ++} ++ ++char * ++elfcore_write_loongarch_lasx (bfd *abfd, ++ char *buf, ++ int *bufsiz, ++ const void *loongarch_lasx, ++ int size) ++{ ++ char *note_name = "LINUX"; ++ return elfcore_write_note (abfd, buf, bufsiz, ++ note_name, NT_LARCH_LASX, loongarch_lasx, size); ++} ++ + char * + elfcore_write_register_note (bfd *abfd, + char *buf, +@@ -11152,6 +11253,14 @@ elfcore_write_register_note (bfd *abfd, + return elfcore_write_aarch_hw_break (abfd, buf, bufsiz, data, size); + if (strcmp (section, ".reg-aarch-hw-watch") == 0) + return elfcore_write_aarch_hw_watch (abfd, buf, bufsiz, data, size); ++ if (strcmp (section, ".reg-loongarch-cpucfg") == 0) ++ return elfcore_write_loongarch_cpucfg (abfd, buf, bufsiz, data, size); ++ if (strcmp (section, ".reg-loongarch-lbt") == 0) ++ return elfcore_write_loongarch_lbt (abfd, buf, bufsiz, data, size); ++ if (strcmp (section, ".reg-loongarch-lsx") == 0) ++ return elfcore_write_loongarch_lsx (abfd, buf, bufsiz, data, size); ++ if (strcmp (section, ".reg-loongarch-lasx") == 0) ++ return elfcore_write_loongarch_lasx (abfd, buf, bufsiz, data, size); + return NULL; + } + +diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c +new file mode 100644 +index 00000000..f609a756 +--- /dev/null ++++ b/bfd/elfnn-loongarch.c +@@ -0,0 +1,3287 @@ ++#include "sysdep.h" ++#include "bfd.h" ++#include "libbfd.h" ++#define ARCH_SIZE NN ++#include "elf-bfd.h" ++#include "objalloc.h" ++#include "elf/loongarch.h" ++#include "elfxx-loongarch.h" ++ ++static bfd_boolean ++loongarch_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED, ++ arelent *cache_ptr, ++ Elf_Internal_Rela *dst) ++{ ++ cache_ptr->howto = loongarch_elf_rtype_to_howto (ELFNN_R_TYPE (dst->r_info)); ++ return cache_ptr->howto != NULL; ++} ++ ++/* Loongarch ELF linker hash entry. */ ++ ++struct loongarch_elf_link_hash_entry ++{ ++ struct elf_link_hash_entry elf; ++ ++ /* Track dynamic relocs copied for this symbol. */ ++ struct elf_dyn_relocs *dyn_relocs; ++ ++#define GOT_UNKNOWN 0 ++#define GOT_NORMAL 1 ++#define GOT_TLS_GD 2 ++#define GOT_TLS_IE 4 ++#define GOT_TLS_LE 8 ++ char tls_type; ++}; ++ ++#define loongarch_elf_hash_entry(ent) \ ++ ((struct loongarch_elf_link_hash_entry *)(ent)) ++ ++struct _bfd_loongarch_elf_obj_tdata ++{ ++ struct elf_obj_tdata root; ++ ++ /* tls_type for each local got entry. */ ++ char *local_got_tls_type; ++}; ++ ++#define _bfd_loongarch_elf_tdata(abfd) \ ++ ((struct _bfd_loongarch_elf_obj_tdata *) (abfd)->tdata.any) ++ ++#define _bfd_loongarch_elf_local_got_tls_type(abfd) \ ++ (_bfd_loongarch_elf_tdata (abfd)->local_got_tls_type) ++ ++#define _bfd_loongarch_elf_tls_type(abfd, h, symndx) \ ++ (*((h) != NULL ? &loongarch_elf_hash_entry (h)->tls_type \ ++ : &_bfd_loongarch_elf_local_got_tls_type (abfd) [symndx])) ++ ++#define is_loongarch_elf(bfd) \ ++ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ ++ && elf_tdata (bfd) != NULL \ ++ && elf_object_id (bfd) == LARCH_ELF_DATA) ++ ++struct loongarch_elf_link_hash_table ++{ ++ struct elf_link_hash_table elf; ++ ++ /* Short-cuts to get to dynamic linker sections. */ ++ asection *sdyntdata; ++ ++ /* Small local sym to section mapping cache. */ ++ struct sym_cache sym_cache; ++ ++ /* Used by local STT_GNU_IFUNC symbols. */ ++ htab_t loc_hash_table; ++ void *loc_hash_memory; ++ ++ /* The max alignment of output sections. */ ++ bfd_vma max_alignment; ++}; ++ ++/* Get the Loongarch ELF linker hash table from a link_info structure. */ ++#define loongarch_elf_hash_table(p) \ ++ (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \ ++ == LARCH_ELF_DATA \ ++ ? ((struct loongarch_elf_link_hash_table *) ((p)->hash)) : NULL) ++ ++#define MINUS_ONE ((bfd_vma)0 - 1) ++ ++#define sec_addr(sec) ((sec)->output_section->vma + (sec)->output_offset) ++ ++#define LARCH_ELF_LOG_WORD_BYTES (ARCH_SIZE == 32 ? 2 : 3) ++#define LARCH_ELF_WORD_BYTES (1 << LARCH_ELF_LOG_WORD_BYTES) ++ ++#define PLT_HEADER_INSNS 8 ++#define PLT_HEADER_SIZE (PLT_HEADER_INSNS * 4) ++ ++#define PLT_ENTRY_INSNS 4 ++#define PLT_ENTRY_SIZE (PLT_ENTRY_INSNS * 4) ++ ++#define GOT_ENTRY_SIZE (LARCH_ELF_WORD_BYTES) ++ ++/* .got.plt的前两项预留。我们约定: ++ 第一项在运行时被动态连接器填入_dl_runtime_resolve的地址 ++ 第二项在连接时,非0指plt header的地址(在no-pic下或prelink)。 ++ 第二项在运行时被动态连接器填入本模块的struct link_map实例的地址。 ++ 详见$glibc/sysdeps/loongarch/dl-machine.h中的elf_machine_runtime_setup */ ++#define GOTPLT_HEADER_SIZE (GOT_ENTRY_SIZE * 2) ++ ++/* .got和.got.plt不合并的好处是,.got.plt和.plt的entry是顺序对应的。 ++ 设法使得stub的size为2的幂,知道.plt和stub的地址就知道了index。 */ ++#define elf_backend_want_got_plt 1 ++ ++#define elf_backend_plt_readonly 1 ++ ++#define elf_backend_want_plt_sym 0 ++/* 1. 本来想着定义_PROCEDURE_LINKAGE_TABLE_,多了不嫌多。 ++ 2. 但实际上,这个符号会使得GDB调试ifunc函数失效。因为lazy-bind的情况下, ++ plt GOT entry中的地址都指向这个符号;GDB读取plt GOT entry来确定ifunc的 ++ 目标函数,从而以为_PROCEDURE_LINKAGE_TABLE_就是ifunc的目标函数(好奇的人 ++ 可以把断点打在elf_gnu_ifunc_record_cache上面看一下GDB的行为,总之是校验从 ++ GOT entry中拿到的地址,结果发现 'BMSYMBOL_VALUE_ADDRESS (msym) == addr' ++ 为真),然后直接跳转到plt header上面了,这当然不行。 ++ 3. 我们期望,调用ifunc函数时,要么跳到对应的plt stub上;要么GDB运行一遍 ++ resolver得到ifunc目标函数的地址后再调用(这是GDB公共代码的做法)。 ++ 4. 观察了aarch64的做法,发现他们就没_PROCEDURE_LINKAGE_TABLE_, ++ 就不存在msym.minsym,然后认为从GOT entry读ifunc目标函数失败直接退出了。 ++ 5. 我想了想,_PROCEDURE_LINKAGE_TABLE_的存在没有意义,因为对plt stub ++ 的处理是有重定位R_LARCH_SOP_PUSH_PLT_PCREL由静态连接器一手操办。 ++ 所以就把_PROCEDURE_LINKAGE_TABLE_去了吧。 */ ++#define elf_backend_plt_alignment 4 ++#define elf_backend_can_gc_sections 1 ++//#define elf_backend_can_refcount 1 ++#define elf_backend_want_got_sym 1 ++ ++/* .got的第一项预留。我们约定.got的第一项为.dynamic的连接时地址(如果有) */ ++#define elf_backend_got_header_size (GOT_ENTRY_SIZE * 1) ++ ++#define elf_backend_want_dynrelro 1 ++//#define elf_backend_rela_normal 1 ++//#define elf_backend_default_execstack 0 ++ ++/* Generate a PLT header. */ ++ ++static void ++loongarch_make_plt_header (bfd_vma got_plt_addr, ++ bfd_vma plt_header_addr, ++ uint32_t *entry) ++{ ++ int64_t pcrel = got_plt_addr - plt_header_addr; ++ int64_t hi = (pcrel & 0x800? 1 : 0) + (pcrel >> 12); ++ int64_t lo = pcrel & 0xfff; ++ if ((hi >> 19) != 0 && (hi >> 19) != -1) ++ abort ();//overflow ++ ++ /* pcaddu12i $t2, %hi(%pcrel(.got.plt)) ++ sub.[wd] $t1, $t1, $t3 ++ ld.[wd] $t3, $t2, %lo(%pcrel(.got.plt)) # _dl_runtime_resolve ++ addi.[wd] $t1, $t1, -(PLT_HEADER_SIZE + 12) + 4 ++ addi.[wd] $t0, $t2, %lo(%pcrel(.got.plt)) ++ srli.[wd] $t1, $t1, log2(16 / GOT_ENTRY_SIZE) ++ ld.[wd] $t0, $t0, GOT_ENTRY_SIZE ++ jirl $r0, $t3, 0 */ ++ ++ if (GOT_ENTRY_SIZE == 8) ++ { ++ entry[0] = 0x1c00000e ++ | (hi & 0xfffff) << 5; ++ entry[1] = 0x0011bdad; ++ entry[2] = 0x28c001cf ++ | (lo & 0xfff) << 10; ++ entry[3] = 0x02c001ad ++ | ((-(PLT_HEADER_SIZE + 12) + 4) & 0xfff) << 10; ++ entry[4] = 0x02c001cc ++ | (lo & 0xfff) << 10; ++ entry[5] = 0x004501ad ++ | (4 - LARCH_ELF_LOG_WORD_BYTES) << 10; ++ entry[6] = 0x28c0018c ++ | GOT_ENTRY_SIZE << 10; ++ entry[7] = 0x4c0001e0; ++ } ++ else ++ { ++ entry[0] = 0x1c00000e ++ | (hi & 0xfffff) << 5; ++ entry[1] = 0x00113dad; ++ entry[2] = 0x288001cf ++ | (lo & 0xfff) << 10; ++ entry[3] = 0x028001ad ++ | ((-(PLT_HEADER_SIZE + 12)) & 0xfff) << 10; ++ entry[4] = 0x028001cc ++ | (lo & 0xfff) << 10; ++ entry[5] = 0x004481ad ++ | (4 - LARCH_ELF_LOG_WORD_BYTES) << 10; ++ entry[6] = 0x2880018c ++ | GOT_ENTRY_SIZE << 10; ++ entry[7] = 0x4c0001e0; ++ } ++} ++ ++/* Generate a PLT entry. */ ++ ++static void ++loongarch_make_plt_entry (bfd_vma got_plt_entry_addr, ++ bfd_vma plt_entry_addr, ++ uint32_t *entry) ++{ ++ int64_t pcrel = got_plt_entry_addr - plt_entry_addr; ++ int64_t hi = (pcrel & 0x800? 1 : 0) + (pcrel >> 12); ++ int64_t lo = pcrel & 0xfff; ++ if ((hi >> 19) != 0 && (hi >> 19) != -1) ++ abort ();//overflow ++ ++ /* pcaddu12i $t3, %hi(%pcrel(.got.plt entry)) ++ ld.[wd] $t3, $t3, %lo(%pcrel(.got.plt entry)) ++ jirl $t1, $t3, 0 ++ addi $r0, $r0, 0 */ ++ ++ entry[0] = 0x1c00000f ++ | (hi & 0xfffff) << 5; ++ entry[1] = (GOT_ENTRY_SIZE == 8? 0x28c001ef : 0x288001ef) ++ | (lo & 0xfff) << 10; ++ //entry[2] = 0x4c0001ed; /* jirl $r13, $15, 0 */ ++ //entry[3] = 0x03400000; /* nop */ ++ //entry[2] = 0x1800002d; /* pcaddi $13, 4 */ ++ entry[2] = 0x1c00000d; /* pcaddu12i $13, 4 */ ++ entry[3] = 0x4c0001e0; /* jirl $r0, $15, 0 */ ++} ++ ++/* Create an entry in an Loongarch ELF linker hash table. */ ++ ++static struct bfd_hash_entry * ++link_hash_newfunc (struct bfd_hash_entry *entry, ++ struct bfd_hash_table *table, const char *string) ++{ ++ /* Allocate the structure if it has not already been allocated by a ++ subclass. */ ++ if (entry == NULL) ++ { ++ entry = bfd_hash_allocate ++ (table, sizeof (struct loongarch_elf_link_hash_entry)); ++ if (entry == NULL) ++ return entry; ++ } ++ ++ /* Call the allocation method of the superclass. */ ++ entry = _bfd_elf_link_hash_newfunc (entry, table, string); ++ if (entry != NULL) ++ { ++ struct loongarch_elf_link_hash_entry *eh; ++ ++ eh = (struct loongarch_elf_link_hash_entry *) entry; ++ eh->dyn_relocs = NULL; ++ eh->tls_type = GOT_UNKNOWN; ++ } ++ ++ return entry; ++} ++ ++/* Compute a hash of a local hash entry. We use elf_link_hash_entry ++ for local symbol so that we can handle local STT_GNU_IFUNC symbols ++ as global symbol. We reuse indx and dynstr_index for local symbol ++ hash since they aren't used by global symbols in this backend. */ ++ ++static hashval_t ++elfNN_loongarch_local_htab_hash (const void *ptr) ++{ ++ struct elf_link_hash_entry *h ++ = (struct elf_link_hash_entry *) ptr; ++ return ELF_LOCAL_SYMBOL_HASH (h->indx, h->dynstr_index); ++} ++ ++/* Compare local hash entries. */ ++ ++static int ++elfNN_loongarch_local_htab_eq (const void *ptr1, const void *ptr2) ++{ ++ struct elf_link_hash_entry *h1 ++ = (struct elf_link_hash_entry *) ptr1; ++ struct elf_link_hash_entry *h2 ++ = (struct elf_link_hash_entry *) ptr2; ++ ++ return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index; ++} ++ ++/* Find and/or create a hash entry for local symbol. */ ++static struct elf_link_hash_entry * ++elfNN_loongarch_get_local_sym_hash (struct loongarch_elf_link_hash_table *htab, ++ bfd *abfd, const Elf_Internal_Rela *rel, ++ bfd_boolean create) ++{ ++ struct loongarch_elf_link_hash_entry e, *ret; ++ asection *sec = abfd->sections; ++ hashval_t h = ELF_LOCAL_SYMBOL_HASH (sec->id, ELFNN_R_SYM (rel->r_info)); ++ void **slot; ++ ++ e.elf.indx = sec->id; ++ e.elf.dynstr_index = ELFNN_R_SYM (rel->r_info); ++ slot = htab_find_slot_with_hash ++ (htab->loc_hash_table, &e, h, create ? INSERT : NO_INSERT); ++ ++ if (!slot) ++ return NULL; ++ ++ if (*slot) ++ { ++ ret = (struct loongarch_elf_link_hash_entry *) *slot; ++ return &ret->elf; ++ } ++ ++ ret = (struct loongarch_elf_link_hash_entry *) ++ objalloc_alloc ((struct objalloc *) htab->loc_hash_memory, ++ sizeof (struct loongarch_elf_link_hash_entry)); ++ if (ret) ++ { ++ memset (ret, 0, sizeof (*ret)); ++ ret->elf.indx = sec->id; ++ ret->elf.pointer_equality_needed = 0; ++ ret->elf.dynstr_index = ELFNN_R_SYM (rel->r_info); ++ ret->elf.dynindx = -1; ++ ret->elf.needs_plt = 0; ++ ret->elf.plt.refcount = -1; ++ ret->elf.got.refcount = -1; ++ ret->elf.def_dynamic = 0; ++ ret->elf.def_regular = 1; ++ ret->elf.ref_dynamic = 0; /* this should be always 0 for local */ ++ ret->elf.ref_regular = 0; ++ ret->elf.forced_local = 1; ++ ret->elf.root.type = bfd_link_hash_defined; ++ *slot = ret; ++ } ++ return &ret->elf; ++} ++ ++/* Destroy an Loongarch elf linker hash table. */ ++ ++static void ++elfNN_loongarch_link_hash_table_free (bfd *obfd) ++{ ++ struct loongarch_elf_link_hash_table *ret ++ = (struct loongarch_elf_link_hash_table *) obfd->link.hash; ++ ++ if (ret->loc_hash_table) ++ htab_delete (ret->loc_hash_table); ++ if (ret->loc_hash_memory) ++ objalloc_free ((struct objalloc *) ret->loc_hash_memory); ++ ++ _bfd_elf_link_hash_table_free (obfd); ++} ++ ++/* Create a Loongarch ELF linker hash table. */ ++ ++static struct bfd_link_hash_table * ++loongarch_elf_link_hash_table_create (bfd *abfd) ++{ ++ struct loongarch_elf_link_hash_table *ret; ++ bfd_size_type amt = sizeof (struct loongarch_elf_link_hash_table); ++ ++ ret = (struct loongarch_elf_link_hash_table *) bfd_zmalloc (amt); ++ if (ret == NULL) ++ return NULL; ++ ++ if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc, ++ sizeof (struct loongarch_elf_link_hash_entry), LARCH_ELF_DATA)) ++ { ++ free (ret); ++ return NULL; ++ } ++ ++ ret->max_alignment = MINUS_ONE; ++ ++ ret->loc_hash_table = htab_try_create (1024, ++ elfNN_loongarch_local_htab_hash, ++ elfNN_loongarch_local_htab_eq, ++ NULL); ++ ret->loc_hash_memory = objalloc_create (); ++ if (!ret->loc_hash_table || !ret->loc_hash_memory) ++ { ++ elfNN_loongarch_link_hash_table_free (abfd); ++ return NULL; ++ } ++ ret->elf.root.hash_table_free = elfNN_loongarch_link_hash_table_free; ++ ++ return &ret->elf.root; ++} ++ ++/* Merge backend specific data from an object file to the output ++ object file when linking. */ ++ ++static bfd_boolean ++_bfd_loongarch_elf_merge_private_bfd_data (bfd *ibfd, ++ struct bfd_link_info *info) ++{ ++ bfd *obfd = info->output_bfd; ++ flagword in_flags = elf_elfheader (ibfd)->e_flags; ++ flagword out_flags = elf_elfheader (obfd)->e_flags; ++ ++ if (!is_loongarch_elf (ibfd) || !is_loongarch_elf (obfd)) ++ { ++ /* Make sure one of ibfd or obfd e_flags must be set. */ ++ /* FIXME: EF_LARCH_ABI_LP64 ? . */ ++ if (!is_loongarch_elf (ibfd) && !elf_flags_init (obfd)) ++ { ++ elf_flags_init (obfd) = TRUE; ++ elf_elfheader (obfd)->e_flags = EF_LARCH_ABI_LP64; ++ } ++ ++ if (!is_loongarch_elf (obfd) && !elf_flags_init (ibfd)) ++ { ++ elf_flags_init (ibfd) = TRUE; ++ elf_elfheader (ibfd)->e_flags = EF_LARCH_ABI_LP64; ++ } ++ ++ return TRUE; ++ } ++ ++ if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0) ++ { ++ _bfd_error_handler ++ (_("%pB: ABI is incompatible with that of the selected emulation:\n" ++ " target emulation `%s' does not match `%s'"), ++ ibfd, bfd_get_target (ibfd), bfd_get_target (obfd)); ++ return FALSE; ++ } ++ ++ if (!_bfd_elf_merge_object_attributes (ibfd, info)) ++ return FALSE; ++ ++ if (!elf_flags_init (obfd)) ++ { ++ elf_flags_init (obfd) = TRUE; ++ elf_elfheader (obfd)->e_flags = in_flags; ++ return TRUE; ++ } ++ ++ /* Disallow linking different float ABIs. */ ++ if ((out_flags ^ in_flags) & EF_LARCH_ABI) ++ { ++ _bfd_error_handler ++ (_("%pB: can't link different ABI object."), ibfd); ++ goto fail; ++ } ++ ++ return TRUE; ++ ++fail: ++ bfd_set_error (bfd_error_bad_value); ++ return FALSE; ++} ++ ++/* Create the .got section. */ ++ ++static bfd_boolean ++loongarch_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) ++{ ++ flagword flags; ++ asection *s, *s_got; ++ struct elf_link_hash_entry *h; ++ const struct elf_backend_data *bed = get_elf_backend_data (abfd); ++ struct elf_link_hash_table *htab = elf_hash_table (info); ++ ++ /* This function may be called more than once. */ ++ if (htab->sgot != NULL) ++ return TRUE; ++ ++ flags = bed->dynamic_sec_flags; ++ ++ s = bfd_make_section_anyway_with_flags ++ (abfd, bed->rela_plts_and_copies_p ? ".rela.got" : ".rel.got", ++ bed->dynamic_sec_flags | SEC_READONLY); ++ if (s == NULL ++ || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) ++ return FALSE; ++ htab->srelgot = s; ++ ++ s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags); ++ if (s == NULL ++ || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) ++ return FALSE; ++ htab->sgot = s; ++ ++ /* The first bit of the global offset table is the header. */ ++ s->size += bed->got_header_size; ++ ++ if (bed->want_got_plt) ++ { ++ s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags); ++ if (s == NULL ++ || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) ++ return FALSE; ++ htab->sgotplt = s; ++ ++ /* 相比_bfd_elf_create_got_section: ++ 一方面,RISCV似乎是希望.got.plt和.got都有header; ++ 而且_GLOBAL_OFFSET_TABLE_是.got的开头,而不是.got.plt的开头。 ++ 和公共部分需求有冲突。所以自己实现了 */ ++ ++ /* Reserve room for the header. */ ++ s->size = GOTPLT_HEADER_SIZE; ++ } ++ ++ if (bed->want_got_sym) ++ { ++ /* Define the symbol _GLOBAL_OFFSET_TABLE_ at the start of the .got ++ section. We don't do this in the linker script because we don't want ++ to define the symbol if we are not creating a global offset table. */ ++ h = _bfd_elf_define_linkage_sym (abfd, info, s_got, ++ "_GLOBAL_OFFSET_TABLE_"); ++ elf_hash_table (info)->hgot = h; ++ if (h == NULL) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++/* Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and ++ .rela.bss sections in DYNOBJ, and set up shortcuts to them in our ++ hash table. */ ++ ++static bfd_boolean ++loongarch_elf_create_dynamic_sections (bfd *dynobj, ++ struct bfd_link_info *info) ++{ ++ struct loongarch_elf_link_hash_table *htab; ++ ++ htab = loongarch_elf_hash_table (info); ++ BFD_ASSERT (htab != NULL); ++ ++ if (!loongarch_elf_create_got_section (dynobj, info)) ++ return FALSE; ++ ++ if (!_bfd_elf_create_dynamic_sections (dynobj, info)) ++ return FALSE; ++ ++ if (!bfd_link_pic (info)) ++ { ++ htab->sdyntdata = ++ bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn", ++ SEC_ALLOC | SEC_THREAD_LOCAL); ++ } ++ ++ if (!htab->elf.splt || !htab->elf.srelplt || !htab->elf.sdynbss ++ || (!bfd_link_pic (info) && (!htab->elf.srelbss || !htab->sdyntdata))) ++ abort (); ++ ++ return TRUE; ++} ++ ++static bfd_boolean ++loongarch_elf_record_tls_and_got_reference (bfd *abfd, ++ struct bfd_link_info *info, ++ struct elf_link_hash_entry *h, ++ unsigned long symndx, ++ char tls_type) ++{ ++ struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info); ++ Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; ++ ++ /* This is a global offset table entry for a local symbol. */ ++ if (elf_local_got_refcounts (abfd) == NULL) ++ { ++ bfd_size_type size = ++ symtab_hdr->sh_info * (sizeof (bfd_vma) + sizeof (tls_type)); ++ if (!(elf_local_got_refcounts (abfd) = bfd_zalloc (abfd, size))) ++ return FALSE; ++ _bfd_loongarch_elf_local_got_tls_type (abfd) ++ = (char *) (elf_local_got_refcounts (abfd) + symtab_hdr->sh_info); ++ } ++ ++ switch (tls_type) ++ { ++ case GOT_NORMAL: ++ case GOT_TLS_GD: ++ case GOT_TLS_IE: ++ /* need GOT */ ++ if (htab->elf.sgot == NULL ++ && !loongarch_elf_create_got_section (htab->elf.dynobj, info)) ++ return FALSE; ++ if (h) ++ { ++ if (h->got.refcount < 0) ++ h->got.refcount = 0; ++ h->got.refcount++; ++ } ++ else ++ elf_local_got_refcounts (abfd) [symndx] ++; ++ break; ++ case GOT_TLS_LE: ++ /* no need for GOT */ ++ break; ++ default: ++ _bfd_error_handler (_("%pB: Interl error: unreachable.")); ++ return FALSE; ++ } ++ ++ char *new_tls_type = &_bfd_loongarch_elf_tls_type (abfd, h, symndx); ++ *new_tls_type |= tls_type; ++ if ((*new_tls_type & GOT_NORMAL) && (*new_tls_type & ~GOT_NORMAL)) ++ { ++ _bfd_error_handler ++ (_("%pB: `%s' accessed both as normal and thread local symbol"), ++ abfd, h ? h->root.root.string : ""); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/* Look through the relocs for a section during the first phase, and ++ allocate space in the global offset table or procedure linkage ++ table. */ ++ ++static bfd_boolean ++loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, ++ asection *sec, const Elf_Internal_Rela *relocs) ++{ ++ struct loongarch_elf_link_hash_table *htab; ++ Elf_Internal_Shdr *symtab_hdr; ++ struct elf_link_hash_entry **sym_hashes; ++ const Elf_Internal_Rela *rel; ++ asection *sreloc = NULL; ++ ++ if (bfd_link_relocatable (info)) ++ return TRUE; ++ ++ htab = loongarch_elf_hash_table (info); ++ symtab_hdr = &elf_tdata (abfd)->symtab_hdr; ++ sym_hashes = elf_sym_hashes (abfd); ++ ++ if (htab->elf.dynobj == NULL) ++ htab->elf.dynobj = abfd; ++ ++ /* 这个函数的遍历每一个重定位,将一些信息归置到符号中。这之后的处理 ++ 都会通过遍历符号进行,根据符号中的信息来确定最终二进制文件的形态。 ++ 1.根据重定位类型记录那个符号是否需要GOT entry ++ 2.根据重定位类型记录那个符号的TLS引用模型 ++ 3.处理IFUNC ++ 4.等等 ++ */ ++ ++ for (rel = relocs; rel < relocs + sec->reloc_count; rel++) ++ { ++ unsigned int r_type; ++ unsigned int r_symndx; ++ struct elf_link_hash_entry *h; ++ Elf_Internal_Sym *isym = NULL; ++ ++ /* 意味着在dynamic_sections_created置位的情况下,这个重定位可能需要动态 ++ 连接器的帮助。如果是这样,我们会在动态重定位表中为其分配一个表项。 */ ++ int need_dynreloc; ++ ++ /* 意味着这个动态重定位仅需要符号的pcrel信息,即符号定义在自身模块内 ++ 及延伸出来的其他信息。如果是这样,我们就在连接时知道了这个重定位的值, ++ 就可以把这个动态重定位取消掉。 */ ++ int only_need_pcrel; ++ ++ r_symndx = ELFNN_R_SYM (rel->r_info); ++ r_type = ELFNN_R_TYPE (rel->r_info); ++ ++ if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) ++ { ++ _bfd_error_handler ++ (_("%pB: bad symbol index: %d"), abfd, r_symndx); ++ return FALSE; ++ } ++ ++ if (r_symndx < symtab_hdr->sh_info) ++ { ++ /* A local symbol. */ ++ isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx); ++ if (isym == NULL) ++ return FALSE; ++ ++ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) ++ { ++ h = elfNN_loongarch_get_local_sym_hash (htab, abfd, rel, TRUE); ++ if (h == NULL) ++ return FALSE; ++ ++ h->type = STT_GNU_IFUNC; ++ h->ref_regular = 1; ++ } ++ else ++ 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; ++ } ++ ++ if (h && h->type == STT_GNU_IFUNC) ++ { ++ if (htab->elf.dynobj == NULL) ++ htab->elf.dynobj = abfd; ++ ++ if (!htab->elf.splt ++ && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info)) ++ /* If '.plt' not represent, create '.iplt' to deal with ifunc. */ ++ return FALSE; ++ ++ if (h->plt.refcount < 0) ++ h->plt.refcount = 0; ++ h->plt.refcount++; ++ h->needs_plt = 1; ++ ++ elf_tdata (info->output_bfd)->has_gnu_symbols ++ |= elf_gnu_symbol_ifunc; ++ } ++ ++ need_dynreloc = 0; ++ only_need_pcrel = 0; ++ switch (r_type) ++ { ++ case R_LARCH_SOP_PUSH_GPREL: ++ if (!loongarch_elf_record_tls_and_got_reference ++ (abfd, info, h, r_symndx, GOT_NORMAL)) ++ return FALSE; ++ break; ++ ++ case R_LARCH_SOP_PUSH_TLS_GD: ++ if (!loongarch_elf_record_tls_and_got_reference ++ (abfd, info, h, r_symndx, GOT_TLS_GD)) ++ return FALSE; ++ break; ++ ++ case R_LARCH_SOP_PUSH_TLS_GOT: ++ if (bfd_link_pic (info)) ++ /* may fail for lazy-bind */ ++ info->flags |= DF_STATIC_TLS; ++ ++ if (!loongarch_elf_record_tls_and_got_reference ++ (abfd, info, h, r_symndx, GOT_TLS_IE)) ++ return FALSE; ++ break; ++ ++ case R_LARCH_SOP_PUSH_TLS_TPREL: ++ if (!bfd_link_executable (info)) ++ return FALSE; ++ ++ info->flags |= DF_STATIC_TLS; ++ ++ if (!loongarch_elf_record_tls_and_got_reference ++ (abfd, info, h, r_symndx, GOT_TLS_LE)) ++ return FALSE; ++ break; ++ ++ case R_LARCH_SOP_PUSH_ABSOLUTE: ++ if (h != NULL) ++ /* If this reloc is in a read-only section, we might ++ need a copy reloc. We can't check reliably at this ++ stage whether the section is read-only, as input ++ sections have not yet been mapped to output sections. ++ Tentatively set the flag for now, and correct in ++ adjust_dynamic_symbol. */ ++ /* 这个flag的本质是关注对一个符号的引用能否被动态连接器改变。 ++ 比如la.pcrel,在连接时会将符号的pc相对偏移量写入指令立即数; ++ 而代码段是只读的,动态连接器无法改动指令,这时,那个la只能引用 ++ local的那个符号的定义了,无法被动态连接器改变。 ++ 而使用got表的话,因为got entry可以被动态连接器改变,因此可以改变 ++ la到底哪个模块中的符号。 ++ 动态库里的符号定义可能被可执行文件中的符号定义覆盖,由此,动态库 ++ 中对符号的引用必须可以被动态连接器改变; ++ 而如果在可执行文件中la.pcrel一个动态库中的对象,按常理来说,如果 ++ 不走got表,这个引用是错误的。但如果我们真的把这个符号定义在 ++ 可执行文件中,而将动态库中对象的初始值复制到可执行文件中 ++ (R_LARCH_COPY),这其实等效于引用动态库中的对象了。 ++ 由此,如果某个重定位一旦可能不被动态链接器控制,这个flag被置位, ++ 接下来的处理会根据情况加上R_LARCH_COPY重定位。这样,我们也只能 ++ 在可执行文件中做这件事;动态库中的R_LARCH_COPY是很奇怪的。 */ ++ h->non_got_ref = 1; ++ break; ++ ++ case R_LARCH_SOP_PUSH_PCREL: ++ if (h != NULL) ++ { ++ h->non_got_ref = 1; ++ ++ /* We try to create PLT stub for all non-local function. */ ++ if (h->plt.refcount < 0) ++ h->plt.refcount = 0; ++ h->plt.refcount++; ++ } ++ break; ++ ++ case R_LARCH_SOP_PUSH_PLT_PCREL: ++ /* This symbol requires a procedure linkage table entry. We ++ actually build the entry in adjust_dynamic_symbol, ++ because this might be a case of linking PIC code without ++ linking in any dynamic objects, in which case we don't ++ need to generate a procedure linkage table after all. */ ++ if (h != NULL) ++ { ++ h->needs_plt = 1; ++ if (h->plt.refcount < 0) ++ h->plt.refcount = 0; ++ h->plt.refcount++; ++ } ++ break; ++ ++ case R_LARCH_TLS_DTPREL32: ++ case R_LARCH_TLS_DTPREL64: ++ need_dynreloc = 1; ++ only_need_pcrel = 1; ++ break; ++ ++ case R_LARCH_JUMP_SLOT: ++ case R_LARCH_32: ++ case R_LARCH_64: ++ need_dynreloc = 1; ++ ++ /* If resolved symbol is defined in this object, ++ 1. Under pie, the symbol is known. We convert it ++ into R_LARCH_RELATIVE and need load-addr still. ++ 2. Under pde, the symbol is known and we can discard R_LARCH_NN. ++ 3. Under dll, R_LARCH_NN can't be changed normally, since ++ its defination could be covered by the one in executable. ++ For symbolic, we convert it into R_LARCH_RELATIVE. ++ Thus, only under pde, it needs pcrel only. We discard it. */ ++ only_need_pcrel = bfd_link_pde (info); ++ ++ if (h != NULL) ++ h->non_got_ref = 1; ++ break; ++ ++ case R_LARCH_GNU_VTINHERIT: ++ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) ++ return FALSE; ++ break; ++ ++ case R_LARCH_GNU_VTENTRY: ++ if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) ++ return FALSE; ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* Record some info for sizing and allocating dynamic entry */ ++ if (need_dynreloc && (sec->flags & SEC_ALLOC)) ++ { ++ /* When creating a shared object, we must copy these ++ relocs into the output file. We create a reloc ++ section in dynobj and make room for the reloc. */ ++ struct elf_dyn_relocs *p; ++ struct elf_dyn_relocs **head; ++ ++ if (sreloc == NULL) ++ { ++ sreloc = _bfd_elf_make_dynamic_reloc_section ++ (sec, htab->elf.dynobj, LARCH_ELF_LOG_WORD_BYTES, ++ abfd, /*rela?*/ TRUE); ++ ++ if (sreloc == NULL) ++ return FALSE; ++ } ++ ++ /* If this is a global symbol, we count the number of ++ relocations we need for this symbol. */ ++ if (h != NULL) ++ head = &((struct loongarch_elf_link_hash_entry *) h)->dyn_relocs; ++ else ++ { ++ /* Track dynamic relocs needed for local syms too. ++ We really need local syms available to do this ++ easily. Oh well. */ ++ ++ asection *s; ++ void *vpp; ++ ++ s = bfd_section_from_elf_index (abfd, isym->st_shndx); ++ if (s == NULL) ++ s = sec; ++ ++ vpp = &elf_section_data (s)->local_dynrel; ++ head = (struct elf_dyn_relocs **) vpp; ++ } ++ ++ p = *head; ++ if (p == NULL || p->sec != sec) ++ { ++ bfd_size_type amt = sizeof *p; ++ p = (struct elf_dyn_relocs *) bfd_alloc (htab->elf.dynobj, amt); ++ if (p == NULL) ++ return FALSE; ++ p->next = *head; ++ *head = p; ++ p->sec = sec; ++ p->count = 0; ++ p->pc_count = 0; ++ } ++ ++ p->count++; ++ p->pc_count += only_need_pcrel; ++ } ++ } ++ ++ return TRUE; ++} ++ ++/* Find dynamic relocs for H that apply to read-only sections. */ ++ ++static asection * ++readonly_dynrelocs (struct elf_link_hash_entry *h) ++{ ++ struct elf_dyn_relocs *p; ++ ++ for (p = loongarch_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next) ++ { ++ asection *s = p->sec->output_section; ++ ++ if (s != NULL && (s->flags & SEC_READONLY) != 0) ++ return p->sec; ++ } ++ return NULL; ++} ++ ++/* 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 ++loongarch_elf_adjust_dynamic_symbol (struct bfd_link_info *info, ++ struct elf_link_hash_entry *h) ++{ ++ struct loongarch_elf_link_hash_table *htab; ++ struct loongarch_elf_link_hash_entry * eh; ++ bfd *dynobj; ++ asection *s, *srel; ++ ++ htab = loongarch_elf_hash_table (info); ++ BFD_ASSERT (htab != NULL); ++ ++ dynobj = htab->elf.dynobj; ++ ++ /* Make sure we know what is going on here. */ ++ BFD_ASSERT (dynobj != NULL ++ && (h->needs_plt ++ || h->type == STT_GNU_IFUNC ++ || h->is_weakalias ++ || (h->def_dynamic ++ && h->ref_regular ++ && !h->def_regular))); ++ ++ /* If this is a function, put it in the procedure linkage table. We ++ will fill in the contents of the procedure linkage table later ++ (although we could actually do it here). */ ++ if (h->type == STT_FUNC || h->type == STT_GNU_IFUNC || h->needs_plt) ++ { ++ if (h->plt.refcount < 0 ++ || (h->type != STT_GNU_IFUNC ++ && (SYMBOL_REFERENCES_LOCAL (info, h) ++ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT ++ && h->root.type == bfd_link_hash_undefweak)))) ++ { ++ /* This case can occur if we saw a R_LARCH_SOP_PUSH_PLT_PCREL reloc ++ in an input file, but the symbol was never referred to by a ++ dynamic object, or if all references were garbage collected. ++ In such a case, we don't actually need to build a PLT entry. */ ++ h->plt.offset = MINUS_ONE; ++ h->needs_plt = 0; ++ } ++ else ++ h->needs_plt = 1; ++ ++ return TRUE; ++ } ++ else ++ h->plt.offset = MINUS_ONE; ++ ++ /* 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->is_weakalias) ++ { ++ struct elf_link_hash_entry *def = weakdef (h); ++ BFD_ASSERT (def->root.type == bfd_link_hash_defined); ++ h->root.u.def.section = def->root.u.def.section; ++ h->root.u.def.value = def->root.u.def.value; ++ return TRUE; ++ } ++ ++ /* This is a reference to a symbol defined by a dynamic object which ++ is not a function. */ ++ ++ /* If we are creating a shared library, we must presume that the ++ only references to the symbol are via the global offset table. ++ For such cases we need not do anything here; the relocations will ++ be handled correctly by relocate_section. */ ++ if (bfd_link_dll (info)) ++ return TRUE; ++ ++ /* If there are no references to this symbol that do not use the ++ GOT, we don't need to generate a copy reloc. */ ++ if (!h->non_got_ref) ++ return TRUE; ++ ++ /* If -z nocopyreloc was given, we won't generate them either. */ ++ if (info->nocopyreloc) ++ { ++ h->non_got_ref = 0; ++ return TRUE; ++ } ++ ++ /* If we don't find any dynamic relocs in read-only sections, then ++ we'll be keeping the dynamic relocs and avoiding the copy reloc. */ ++ if (!readonly_dynrelocs (h)) ++ { ++ h->non_got_ref = 0; ++ return TRUE; ++ } ++ ++ /* We must allocate the symbol in our .dynbss section, which will ++ become part of the .bss section of the executable. There will be ++ an entry for this symbol in the .dynsym section. The dynamic ++ object will contain position independent code, so all references ++ from the dynamic object to this symbol will go through the global ++ offset table. The dynamic linker will use the .dynsym entry to ++ determine the address it must put in the global offset table, so ++ both the dynamic object and the regular object will refer to the ++ same memory location for the variable. */ ++ ++ /* We must generate a R_LARCH_COPY reloc to tell the dynamic linker ++ to copy the initial value out of the dynamic object and into the ++ runtime process image. We need to remember the offset into the ++ .rel.bss section we are going to use. */ ++ eh = (struct loongarch_elf_link_hash_entry *) h; ++ if (eh->tls_type & ~GOT_NORMAL) ++ { ++ s = htab->sdyntdata; ++ srel = htab->elf.srelbss; ++ } ++ else ++ if ((h->root.u.def.section->flags & SEC_READONLY) != 0) ++ { ++ s = htab->elf.sdynrelro; ++ srel = htab->elf.sreldynrelro; ++ } ++ else ++ { ++ s = htab->elf.sdynbss; ++ srel = htab->elf.srelbss; ++ } ++ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0) ++ { ++ srel->size += sizeof (ElfNN_External_Rela); ++ h->needs_copy = 1; ++ } ++ ++ return _bfd_elf_adjust_dynamic_copy (info, h, s); ++} ++ ++ ++/* Allocate space in .plt, .got and associated reloc sections for ++ dynamic relocs. */ ++ ++static bfd_boolean ++allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) ++{ ++ struct bfd_link_info *info; ++ struct loongarch_elf_link_hash_table *htab; ++ struct loongarch_elf_link_hash_entry *eh; ++ struct elf_dyn_relocs *p; ++ ++ if (h->root.type == bfd_link_hash_indirect) ++ return TRUE; ++ ++ eh = (struct loongarch_elf_link_hash_entry *) h; ++ info = (struct bfd_link_info *) inf; ++ htab = loongarch_elf_hash_table (info); ++ BFD_ASSERT (htab != NULL); ++ ++ /* 在这里针对符号对.got .iplt .plt的扩充和后续elf_finish_dynamic_symbol补充 ++ 内容对照;WILL_CALL_FINISH_DYNAMIC_SYMBOL似乎指的是这个符号在将来会不会被 ++ finish_dynamic_symbol调用。 ++ a. 对于非IFUNC符号,被allocate_dynrelocs照顾到的符号h要保证在链接后期被 ++ elf_finish_dynamic_symbol调用 ++ b. STT_GNU_IFUNC符号一定走plt,但是对于那些local转化为h的符号,默认是不会 ++ 被调用allocate_dynrelocs和elf_finish_dynamic_symbol的,要手动遍历 ++ 这些符号来调用这两个函数,从而为它们分配plt stub; ++ 而WILL_CALL_FINISH_DYNAMIC_SYMBOL返回false,因此下面的逻辑都是 ++ WILL_CALL_FINISH_DYNAMIC_SYMBOL和对IFUNC的判断配合起来。 */ ++ ++ do ++ { ++ asection *plt, *gotplt, *relplt; ++ ++ if (!h->needs_plt) ++ break; ++ ++ h->needs_plt = 0; ++ ++ if (htab->elf.splt) ++ { ++ if (h->dynindx == -1 && !h->forced_local ++ && !bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ ++ if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h) ++ && h->type != STT_GNU_IFUNC) ++ break; ++ ++ plt = htab->elf.splt; ++ gotplt = htab->elf.sgotplt; ++ relplt = htab->elf.srelplt; ++ } ++ else if (htab->elf.iplt) ++ { ++ /* .iplt only for IFUNC */ ++ if (h->type != STT_GNU_IFUNC) ++ break; ++ ++ plt = htab->elf.iplt; ++ gotplt = htab->elf.igotplt; ++ relplt = htab->elf.irelplt; ++ } ++ else ++ break; ++ ++ if (plt->size == 0) ++ plt->size = PLT_HEADER_SIZE; ++ ++ h->plt.offset = plt->size; ++ plt->size += PLT_ENTRY_SIZE; ++ gotplt->size += GOT_ENTRY_SIZE; ++ relplt->size += sizeof (ElfNN_External_Rela); ++ ++ h->needs_plt = 1; ++ } ++ while (0); ++ ++ if (!h->needs_plt) ++ h->plt.offset = MINUS_ONE; ++ ++ if (0 < h->got.refcount) ++ { ++ asection *s; ++ bfd_boolean dyn; ++ int tls_type = loongarch_elf_hash_entry (h)->tls_type; ++ ++ /* Make sure this symbol is output as a dynamic symbol. ++ Undefined weak syms won't yet be marked as dynamic. */ ++ if (h->dynindx == -1 && !h->forced_local ++ && !bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ ++ s = htab->elf.sgot; ++ h->got.offset = s->size; ++ dyn = htab->elf.dynamic_sections_created; ++ if (tls_type & (GOT_TLS_GD | GOT_TLS_IE)) ++ { ++ /* TLS_GD needs two dynamic relocs and two GOT slots. */ ++ if (tls_type & GOT_TLS_GD) ++ { ++ s->size += 2 * GOT_ENTRY_SIZE; ++ htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela); ++ } ++ ++ /* TLS_IE needs one dynamic reloc and one GOT slot. */ ++ if (tls_type & GOT_TLS_IE) ++ { ++ s->size += GOT_ENTRY_SIZE; ++ htab->elf.srelgot->size += sizeof (ElfNN_External_Rela); ++ } ++ } ++ else ++ { ++ s->size += GOT_ENTRY_SIZE; ++ if ((WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h) ++ && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) ++ || h->type == STT_GNU_IFUNC) ++ htab->elf.srelgot->size += sizeof (ElfNN_External_Rela); ++ } ++ } ++ else ++ h->got.offset = MINUS_ONE; ++ ++ if (eh->dyn_relocs == NULL) ++ return TRUE; ++ ++ /* 如果某些函数未被定义,SYMBOL_CALLS_LOCAL返回1; ++ 而SYMBOL_REFERENCES_LOCAL返回0。 ++ 似乎是因为未定义的函数可以有plt从而将其转化为local的。 */ ++ if (SYMBOL_REFERENCES_LOCAL (info, h)) ++ { ++ struct elf_dyn_relocs **pp; ++ ++ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) ++ { ++ p->count -= p->pc_count; ++ p->pc_count = 0; ++ if (p->count == 0) ++ *pp = p->next; ++ else ++ pp = &p->next; ++ } ++ } ++ ++ if (h->root.type == bfd_link_hash_undefweak) ++ { ++ if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) ++ eh->dyn_relocs = NULL; ++ else if (h->dynindx == -1 && !h->forced_local ++ /* Make sure this symbol is output as a dynamic symbol. ++ Undefined weak syms won't yet be marked as dynamic. */ ++ && !bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ } ++ ++ for (p = eh->dyn_relocs; p != NULL; p = p->next) ++ { ++ asection *sreloc = elf_section_data (p->sec)->sreloc; ++ sreloc->size += p->count * sizeof (ElfNN_External_Rela); ++ } ++ ++ return TRUE; ++} ++ ++static bfd_boolean ++elfNN_loongarch_allocate_local_dynrelocs (void **slot, void *inf) ++{ ++ struct elf_link_hash_entry *h ++ = (struct elf_link_hash_entry *) *slot; ++ ++ if (!h->def_regular ++ || !h->ref_regular ++ || !h->forced_local ++ || h->root.type != bfd_link_hash_defined) ++ abort (); ++ ++ return allocate_dynrelocs (h, inf); ++} ++ ++/* Set DF_TEXTREL if we find any dynamic relocs that apply to ++ read-only sections. */ ++ ++static bfd_boolean ++maybe_set_textrel (struct elf_link_hash_entry *h, void *info_p) ++{ ++ asection *sec; ++ ++ if (h->root.type == bfd_link_hash_indirect) ++ return TRUE; ++ ++ sec = readonly_dynrelocs (h); ++ if (sec != NULL) ++ { ++ struct bfd_link_info *info = (struct bfd_link_info *) info_p; ++ ++ info->flags |= DF_TEXTREL; ++ info->callbacks->minfo ( ++ _("%pB: dynamic relocation against `%pT' in read-only section `%pA'\n"), ++ sec->owner, h->root.root.string, sec); ++ ++ /* Not an error, just cut short the traversal. */ ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static bfd_boolean ++loongarch_elf_size_dynamic_sections (bfd *output_bfd, ++ struct bfd_link_info *info) ++{ ++ struct loongarch_elf_link_hash_table *htab; ++ bfd *dynobj; ++ asection *s; ++ bfd *ibfd; ++ ++ htab = loongarch_elf_hash_table (info); ++ BFD_ASSERT (htab != NULL); ++ dynobj = htab->elf.dynobj; ++ BFD_ASSERT (dynobj != NULL); ++ ++ if (htab->elf.dynamic_sections_created) ++ { ++ /* Set the contents of the .interp section to the interpreter. */ ++ if (bfd_link_executable (info) && !info->nointerp) ++ { ++ const char *interpreter; ++ flagword flags = elf_elfheader (output_bfd)->e_flags; ++ s = bfd_get_linker_section (dynobj, ".interp"); ++ BFD_ASSERT (s != NULL); ++ if ((flags & EF_LARCH_ABI) == EF_LARCH_ABI_LP32) ++ interpreter = "/lib32/ld.so.1"; ++ else if ((flags & EF_LARCH_ABI) == EF_LARCH_ABI_LP64) ++ interpreter = "/lib64/ld.so.1"; ++ else ++ interpreter = "/lib/ld.so.1"; ++ s->contents = (unsigned char *) interpreter; ++ s->size = strlen (interpreter) + 1; ++ } ++ } ++ ++ /* Set up .got offsets for local syms, and space for local dynamic ++ relocs. */ ++ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next) ++ { ++ bfd_signed_vma *local_got; ++ bfd_signed_vma *end_local_got; ++ char *local_tls_type; ++ bfd_size_type locsymcount; ++ Elf_Internal_Shdr *symtab_hdr; ++ asection *srel; ++ ++ if (!is_loongarch_elf (ibfd)) ++ continue; ++ ++ for (s = ibfd->sections; s != NULL; s = s->next) ++ { ++ struct elf_dyn_relocs *p; ++ ++ for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) ++ { ++ p->count -= p->pc_count; ++ if (!bfd_is_abs_section (p->sec) ++ && bfd_is_abs_section (p->sec->output_section)) ++ { ++ /* Input section has been discarded, either because ++ it is a copy of a linkonce section or due to ++ linker script /DISCARD/, so we'll be discarding ++ the relocs too. */ ++ } ++ else if (0 < p->count) ++ { ++ srel = elf_section_data (p->sec)->sreloc; ++ srel->size += p->count * sizeof (ElfNN_External_Rela); ++ if ((p->sec->output_section->flags & SEC_READONLY) != 0) ++ info->flags |= DF_TEXTREL; ++ } ++ } ++ } ++ ++ local_got = elf_local_got_refcounts (ibfd); ++ if (!local_got) ++ continue; ++ ++ symtab_hdr = &elf_symtab_hdr (ibfd); ++ locsymcount = symtab_hdr->sh_info; ++ end_local_got = local_got + locsymcount; ++ local_tls_type = _bfd_loongarch_elf_local_got_tls_type (ibfd); ++ s = htab->elf.sgot; ++ srel = htab->elf.srelgot; ++ for (; local_got < end_local_got; ++local_got, ++local_tls_type) ++ { ++ if (0 < *local_got) ++ { ++ *local_got = s->size; ++ s->size += GOT_ENTRY_SIZE; ++ ++ if (*local_tls_type & GOT_TLS_GD) ++ s->size += GOT_ENTRY_SIZE; ++ ++ if (bfd_link_pic (info) /* R_LARCH_RELATIVE */ ++ || (*local_tls_type & ++ (GOT_TLS_GD /* R_LARCH_TLS_DTPRELNN */ ++ | GOT_TLS_IE /* R_LARCH_TLS_TPRELNN */))) ++ srel->size += sizeof (ElfNN_External_Rela); ++ } ++ else ++ *local_got = MINUS_ONE; ++ } ++ } ++ ++ /* Allocate global sym .plt and .got entries, and space for global ++ sym dynamic relocs. */ ++ elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info); ++ /* Allocate .plt and .got entries, and space for local ifunc symbols. */ ++ htab_traverse (htab->loc_hash_table, ++ elfNN_loongarch_allocate_local_dynrelocs, ++ info); ++ ++ /* Don't allocate .got.plt section if there are no PLT. */ ++ if (htab->elf.sgotplt ++ && htab->elf.sgotplt->size == GOTPLT_HEADER_SIZE ++ && (htab->elf.splt == NULL ++ || htab->elf.splt->size == 0)) ++ htab->elf.sgotplt->size = 0; ++ ++ /* The check_relocs and adjust_dynamic_symbol entry points have ++ determined the sizes of the various dynamic sections. Allocate ++ memory for them. */ ++ for (s = dynobj->sections; s != NULL; s = s->next) ++ { ++ if ((s->flags & SEC_LINKER_CREATED) == 0) ++ continue; ++ ++ if (s == htab->elf.splt ++ || s == htab->elf.iplt ++ || s == htab->elf.sgot ++ || s == htab->elf.sgotplt ++ || s == htab->elf.igotplt ++ || s == htab->elf.sdynbss ++ || s == htab->elf.sdynrelro) ++ { ++ /* Strip this section if we don't need it; see the ++ comment below. */ ++ } ++ else if (strncmp (s->name, ".rela", 5) == 0) ++ { ++ if (s->size != 0) ++ { ++ /* We use the reloc_count field as a counter if we need ++ to copy relocs into the output file. */ ++ s->reloc_count = 0; ++ } ++ } ++ else ++ { ++ /* It's not one of our sections. */ ++ continue; ++ } ++ ++ if (s->size == 0) ++ { ++ /* If we don't need this section, strip it from the ++ output file. This is mostly to handle .rela.bss and ++ .rela.plt. We must create both sections in ++ create_dynamic_sections, because they must be created ++ before the linker maps input sections to output ++ sections. The linker does that before ++ adjust_dynamic_symbol is called, and it is that ++ function which decides whether anything needs to go ++ into these sections. */ ++ s->flags |= SEC_EXCLUDE; ++ continue; ++ } ++ ++ if ((s->flags & SEC_HAS_CONTENTS) == 0) ++ continue; ++ ++ /* Allocate memory for the section contents. Zero the memory ++ for the benefit of .rela.plt, which has 4 unused entries ++ at the beginning, and we don't want garbage. */ ++ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); ++ if (s->contents == NULL) ++ return FALSE; ++ } ++ ++ if (elf_hash_table (info)->dynamic_sections_created) ++ { ++ /* Add some entries to the .dynamic section. We fill in the ++ values later, in loongarch_elf_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_elf_add_dynamic_entry (info, TAG, VAL) ++ ++ if (bfd_link_executable (info)) ++ { ++ if (!add_dynamic_entry (DT_DEBUG, 0)) ++ return FALSE; ++ } ++ ++ if (htab->elf.srelplt->size != 0) ++ { ++ 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 (!add_dynamic_entry (DT_RELA, 0) ++ || !add_dynamic_entry (DT_RELASZ, 0) ++ || !add_dynamic_entry (DT_RELAENT, sizeof (ElfNN_External_Rela))) ++ return FALSE; ++ ++ /* If any dynamic relocs apply to a read-only section, ++ then we need a DT_TEXTREL entry. */ ++ if ((info->flags & DF_TEXTREL) == 0) ++ elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info); ++ ++ if (info->flags & DF_TEXTREL) ++ { ++ if (!add_dynamic_entry (DT_TEXTREL, 0)) ++ return FALSE; ++ /* Clear the DF_TEXTREL flag. It will be set again if we ++ write out an actual text relocation; we may not, because ++ at this point we do not know whether e.g. any .eh_frame ++ absolute relocations have been converted to PC-relative. */ ++ info->flags &= ~DF_TEXTREL; ++ } ++ } ++#undef add_dynamic_entry ++ ++ return TRUE; ++} ++ ++ ++#define LARCH_LD_STACK_DEPTH 16 ++static int64_t lisa_opc_stack[LARCH_LD_STACK_DEPTH]; ++static size_t lisa_stack_top = 0; ++ ++static bfd_reloc_status_type ++loongarch_push (int64_t val) ++{ ++ if (LARCH_LD_STACK_DEPTH <= lisa_stack_top) ++ return bfd_reloc_outofrange; ++ lisa_opc_stack[lisa_stack_top++] = val; ++ return bfd_reloc_ok; ++} ++ ++static bfd_reloc_status_type ++loongarch_pop (int64_t *val) ++{ ++ if (lisa_stack_top == 0) ++ return bfd_reloc_outofrange; ++ BFD_ASSERT (val); ++ *val = lisa_opc_stack[--lisa_stack_top]; ++ return bfd_reloc_ok; ++} ++ ++static bfd_reloc_status_type ++loongarch_top (int64_t *val) ++{ ++ if (lisa_stack_top == 0) ++ return bfd_reloc_outofrange; ++ BFD_ASSERT (val); ++ *val = lisa_opc_stack[lisa_stack_top - 1]; ++ return bfd_reloc_ok; ++} ++ ++static void ++loongarch_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel) ++{ ++ const struct elf_backend_data *bed; ++ bfd_byte *loc; ++ ++ bed = get_elf_backend_data (abfd); ++ loc = s->contents + (s->reloc_count++ * bed->s->sizeof_rela); ++ bed->s->swap_reloca_out (abfd, rel, loc); ++} ++ ++/* Emplace a static relocation. */ ++ ++static bfd_reloc_status_type ++perform_relocation (const Elf_Internal_Rela *rel, ++ bfd_vma value, ++ bfd *input_bfd, ++ bfd_byte *contents) ++{ ++ ++ uint32_t insn1; ++ int64_t opr1, opr2, opr3; ++ bfd_reloc_status_type r = bfd_reloc_ok; ++ switch (ELFNN_R_TYPE (rel->r_info)) ++ { ++ case R_LARCH_SOP_PUSH_PCREL: ++ case R_LARCH_SOP_PUSH_ABSOLUTE: ++ case R_LARCH_SOP_PUSH_GPREL: ++ case R_LARCH_SOP_PUSH_TLS_TPREL: ++ case R_LARCH_SOP_PUSH_TLS_GOT: ++ case R_LARCH_SOP_PUSH_TLS_GD: ++ case R_LARCH_SOP_PUSH_PLT_PCREL: ++ r = loongarch_push (value); ++ break; ++ ++ case R_LARCH_SOP_PUSH_DUP: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1) != bfd_reloc_ok ++ || loongarch_push (opr1) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_ASSERT: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok && opr1 == FALSE) ++ r = bfd_reloc_notsupported; ++ break; ++ ++ case R_LARCH_SOP_NOT: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (!opr1) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_SUB: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr2) != bfd_reloc_ok ++ || loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1 - opr2) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_SL: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr2) != bfd_reloc_ok ++ || loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1 << opr2) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_SR: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr2) != bfd_reloc_ok ++ || loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1 >> opr2) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_AND: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr2) != bfd_reloc_ok ++ || loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1 & opr2) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_ADD: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr2) != bfd_reloc_ok ++ || loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1 + opr2) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_IF_ELSE: ++ r = bfd_reloc_outofrange; ++ if (loongarch_pop (&opr3) != bfd_reloc_ok ++ || loongarch_pop (&opr2) != bfd_reloc_ok ++ || loongarch_pop (&opr1) != bfd_reloc_ok ++ || loongarch_push (opr1 ? opr2 : opr3) != bfd_reloc_ok) ++ break; ++ r = bfd_reloc_ok; ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_10_5: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & ~(uint64_t)0xf) != 0x0 ++ && (opr1 & ~(uint64_t)0xf) != ~(uint64_t)0xf) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & (~(uint32_t)0x7c00)) | ((opr1 & 0x1f) << 10); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_U_10_12: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if (opr1 & ~(uint64_t)0xfff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & (~(uint32_t)0x3ffc00)) | ((opr1 & 0xfff) << 10); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_10_12: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & ~(uint64_t)0x7ff) != 0x0 ++ && (opr1 & ~(uint64_t)0x7ff) != ~(uint64_t)0x7ff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & (~(uint32_t)0x3ffc00)) | ((opr1 & 0xfff) << 10); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_10_16: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & ~(uint64_t)0x7fff) != 0x0 ++ && (opr1 & ~(uint64_t)0x7fff) != ~(uint64_t)0x7fff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & 0xfc0003ff) | ((opr1 & 0xffff) << 10); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_10_16_S2: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & 0x3) != 0) ++ r = bfd_reloc_overflow; ++ opr1 >>= 2; ++ if ((opr1 & ~(uint64_t)0x7fff) != 0x0 ++ && (opr1 & ~(uint64_t)0x7fff) != ~(uint64_t)0x7fff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & 0xfc0003ff) | ((opr1 & 0xffff) << 10); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_0_5_10_16_S2: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & 0x3) != 0) ++ r = bfd_reloc_overflow; ++ opr1 >>= 2; ++ if ((opr1 & ~(uint64_t)0xfffff) != 0x0 ++ && (opr1 & ~(uint64_t)0xfffff) != ~(uint64_t)0xfffff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & 0xfc0003e0) ++ | ((opr1 & 0xffff) << 10) | ((opr1 & 0x1f0000) >> 16); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_5_20: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & ~(uint64_t)0x7ffff) != 0x0 ++ && (opr1 & ~(uint64_t)0x7ffff) != ~(uint64_t)0x7ffff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & (~(uint32_t)0x1ffffe0)) | ((opr1 & 0xfffff) << 5); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_S_0_10_10_16_S2: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if ((opr1 & 0x3) != 0) ++ r = bfd_reloc_overflow; ++ opr1 >>= 2; ++ if ((opr1 & ~(uint64_t)0x1ffffff) != 0x0 ++ && (opr1 & ~(uint64_t)0x1ffffff) != ~(uint64_t)0x1ffffff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ insn1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ insn1 = (insn1 & 0xfc000000) ++ | ((opr1 & 0xffff) << 10) | ((opr1 & 0x3ff0000) >> 16); ++ bfd_put (32, input_bfd, insn1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_SOP_POP_32_U: ++ r = loongarch_pop (&opr1); ++ if (r != bfd_reloc_ok) ++ break; ++ if (opr1 & ~(uint64_t)0xffffffff) ++ r = bfd_reloc_overflow; ++ if (r != bfd_reloc_ok) ++ break; ++ bfd_put (32, input_bfd, opr1, contents + rel->r_offset); ++ break; ++ ++ case R_LARCH_TLS_DTPREL32: ++ case R_LARCH_32: ++ bfd_put (32, input_bfd, value, contents + rel->r_offset); ++ break; ++ case R_LARCH_TLS_DTPREL64: ++ case R_LARCH_64: ++ bfd_put (64, input_bfd, value, contents + rel->r_offset); ++ break; ++ case R_LARCH_ADD8: ++ opr1 = bfd_get (8, input_bfd, contents + rel->r_offset); ++ bfd_put (8, input_bfd, opr1 + value, contents + rel->r_offset); ++ break; ++ case R_LARCH_ADD16: ++ opr1 = bfd_get (16, input_bfd, contents + rel->r_offset); ++ bfd_put (16, input_bfd, opr1 + value, contents + rel->r_offset); ++ break; ++ case R_LARCH_ADD24: ++ opr1 = bfd_get (24, input_bfd, contents + rel->r_offset); ++ bfd_put (24, input_bfd, opr1 + value, contents + rel->r_offset); ++ break; ++ case R_LARCH_ADD32: ++ opr1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ bfd_put (32, input_bfd, opr1 + value, contents + rel->r_offset); ++ break; ++ case R_LARCH_ADD64: ++ opr1 = bfd_get (64, input_bfd, contents + rel->r_offset); ++ bfd_put (64, input_bfd, opr1 + value, contents + rel->r_offset); ++ break; ++ case R_LARCH_SUB8: ++ opr1 = bfd_get (8, input_bfd, contents + rel->r_offset); ++ bfd_put (8, input_bfd, opr1 - value, contents + rel->r_offset); ++ break; ++ case R_LARCH_SUB16: ++ opr1 = bfd_get (16, input_bfd, contents + rel->r_offset); ++ bfd_put (16, input_bfd, opr1 - value, contents + rel->r_offset); ++ break; ++ case R_LARCH_SUB24: ++ opr1 = bfd_get (24, input_bfd, contents + rel->r_offset); ++ bfd_put (24, input_bfd, opr1 - value, contents + rel->r_offset); ++ break; ++ case R_LARCH_SUB32: ++ opr1 = bfd_get (32, input_bfd, contents + rel->r_offset); ++ bfd_put (32, input_bfd, opr1 - value, contents + rel->r_offset); ++ break; ++ case R_LARCH_SUB64: ++ opr1 = bfd_get (64, input_bfd, contents + rel->r_offset); ++ bfd_put (64, input_bfd, opr1 - value, contents + rel->r_offset); ++ break; ++ ++ default: ++ r = bfd_reloc_notsupported; ++ } ++ return r; ++} ++ ++ ++#define LARCH_RECENT_RELOC_QUEUE_LENGTH 72 ++static struct ++{ ++ bfd *bfd; ++ asection *section; ++ bfd_vma r_offset; ++ int r_type; ++ bfd_vma relocation; ++ Elf_Internal_Sym *sym; ++ struct elf_link_hash_entry *h; ++ bfd_vma addend; ++ int64_t top_then; ++} lisa_reloc_queue [LARCH_RECENT_RELOC_QUEUE_LENGTH]; ++static size_t lisa_reloc_queue_head = 0; ++static size_t lisa_reloc_queue_tail = 0; ++ ++static const char * ++loongarch_sym_name (bfd *input_bfd, struct elf_link_hash_entry *h, ++ Elf_Internal_Sym *sym) ++{ ++ const char *ret = NULL; ++ if (sym) ++ ret = bfd_elf_string_from_elf_section ++ (input_bfd, elf_symtab_hdr (input_bfd).sh_link, sym->st_name); ++ else if (h) ++ ret = h->root.root.string; ++ ++ if (ret == NULL || *ret == '\0') ++ ret = ""; ++ return ret; ++} ++ ++static void ++loongarch_record_one_reloc (bfd *abfd, asection *section, int r_type, ++ bfd_vma r_offset, Elf_Internal_Sym *sym, ++ struct elf_link_hash_entry *h, bfd_vma addend) ++{ ++ if ((lisa_reloc_queue_head == 0 ++ && lisa_reloc_queue_tail == LARCH_RECENT_RELOC_QUEUE_LENGTH - 1) ++ || (lisa_reloc_queue_head == lisa_reloc_queue_tail + 1)) ++ lisa_reloc_queue_head = ++ (lisa_reloc_queue_head + 1) % LARCH_RECENT_RELOC_QUEUE_LENGTH; ++ lisa_reloc_queue[lisa_reloc_queue_tail].bfd = abfd; ++ lisa_reloc_queue[lisa_reloc_queue_tail].section = section; ++ lisa_reloc_queue[lisa_reloc_queue_tail].r_offset = r_offset; ++ lisa_reloc_queue[lisa_reloc_queue_tail].r_type = r_type; ++ lisa_reloc_queue[lisa_reloc_queue_tail].sym = sym; ++ lisa_reloc_queue[lisa_reloc_queue_tail].h = h; ++ lisa_reloc_queue[lisa_reloc_queue_tail].addend = addend; ++ loongarch_top (&lisa_reloc_queue[lisa_reloc_queue_tail].top_then); ++ lisa_reloc_queue_tail = ++ (lisa_reloc_queue_tail + 1) % LARCH_RECENT_RELOC_QUEUE_LENGTH; ++} ++ ++static void ++loongarch_dump_reloc_record (void (*p) (const char *fmt, ...)) ++{ ++ size_t i = lisa_reloc_queue_head; ++ bfd *a_bfd = NULL; ++ asection *section = NULL; ++ bfd_vma r_offset = 0; ++ int inited = 0; ++ p ("Dump relocate record:\n"); ++ p ("stack top\t\trelocation name\t\tsymbol"); ++ while (i != lisa_reloc_queue_tail) ++ { ++ if (a_bfd != lisa_reloc_queue[i].bfd ++ || section != lisa_reloc_queue[i].section ++ || r_offset != lisa_reloc_queue[i].r_offset) ++ { ++ a_bfd = lisa_reloc_queue[i].bfd; ++ section = lisa_reloc_queue[i].section; ++ r_offset = lisa_reloc_queue[i].r_offset; ++ p ("\nat %pB(%pA+0x%v):\n", ++ lisa_reloc_queue[i].bfd, ++ lisa_reloc_queue[i].section, ++ lisa_reloc_queue[i].r_offset); ++ } ++ ++ if (!inited) ++ inited = 1, p ("...\n"); ++ ++ reloc_howto_type *howto = ++ loongarch_elf_rtype_to_howto (lisa_reloc_queue[i].r_type); ++ p ("0x%V %s\t`%s'", ++ (bfd_vma) lisa_reloc_queue[i].top_then, ++ howto ? howto->name : "", ++ loongarch_sym_name (lisa_reloc_queue[i].bfd, ++ lisa_reloc_queue[i].h, ++ lisa_reloc_queue[i].sym)); ++ ++ long addend = lisa_reloc_queue[i].addend; ++ if (addend < 0) ++ p (" - %ld", -addend); ++ else if (0 < addend) ++ p (" + %ld(0x%v)", addend, lisa_reloc_queue[i].addend); ++ ++ p ("\n"); ++ i = (i + 1) % LARCH_RECENT_RELOC_QUEUE_LENGTH; ++ } ++ p ("\n" "-- Record dump end --\n\n"); ++} ++ ++ ++static bfd_boolean ++loongarch_elf_relocate_section (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_Rela *rel; ++ Elf_Internal_Rela *relend; ++ bfd_boolean fatal = FALSE; ++ asection *sreloc = elf_section_data (input_section)->sreloc; ++ struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info); ++ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd); ++ struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd); ++ bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd); ++ bfd_boolean is_pic = bfd_link_pic (info); ++ bfd_boolean is_dyn = elf_hash_table (info)->dynamic_sections_created; ++ asection *plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt; ++ asection *got = htab->elf.sgot; ++ ++ relend = relocs + input_section->reloc_count; ++ for (rel = relocs; rel < relend; rel++) ++ { ++ int r_type = ELFNN_R_TYPE (rel->r_info); ++ unsigned long r_symndx = ELFNN_R_SYM (rel->r_info); ++ bfd_vma pc = sec_addr (input_section) + rel->r_offset; ++ reloc_howto_type *howto = loongarch_elf_rtype_to_howto (r_type); ++ asection *sec = NULL; ++ Elf_Internal_Sym *sym = NULL; ++ struct elf_link_hash_entry *h = NULL; ++ const char *name; ++ bfd_reloc_status_type r = bfd_reloc_ok; ++ bfd_boolean is_ie, is_undefweak, unresolved_reloc, defined_local; ++ bfd_boolean resolved_local, resolved_dynly, resolved_to_const; ++ char tls_type; ++ bfd_vma relocation; ++ bfd_vma off, ie_off; ++ int i, j; ++ ++ if (howto == NULL ++ || r_type == R_LARCH_GNU_VTINHERIT ++ || r_type == R_LARCH_GNU_VTENTRY) ++ continue; ++ ++ /* This is a final link. */ ++ if (r_symndx < symtab_hdr->sh_info) ++ { ++ is_undefweak = FALSE; ++ unresolved_reloc = FALSE; ++ sym = local_syms + r_symndx; ++ sec = local_sections[r_symndx]; ++ relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); ++ ++ /* Relocate against local STT_GNU_IFUNC symbol. */ ++ if (!bfd_link_relocatable (info) ++ && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) ++ { ++ h = elfNN_loongarch_get_local_sym_hash ++ (htab, input_bfd, rel, FALSE); ++ if (h == NULL) ++ abort (); ++ ++ /* Set STT_GNU_IFUNC symbol value. */ ++ h->root.u.def.value = sym->st_value; ++ h->root.u.def.section = sec; ++ } ++ defined_local = TRUE; ++ resolved_local = TRUE; ++ resolved_dynly = FALSE; ++ resolved_to_const = FALSE; ++ if (bfd_link_relocatable(info) ++ && ELF_ST_TYPE(sym->st_info) == STT_SECTION) { ++ rel->r_addend += sec->output_offset; ++ } ++ } ++ else ++ { ++ bfd_boolean warned, ignored; ++ ++ RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, ++ r_symndx, symtab_hdr, sym_hashes, ++ h, sec, relocation, ++ unresolved_reloc, warned, ignored); ++ /* here means symbol isn't local symbol only and 'h != NULL' */ ++ ++ /* 'unresolved_syms_in_objects' specify how to deal with undefined ++ symbol. And 'dynamic_undefined_weak' specify what to do when ++ meeting undefweak. */ ++ ++ if ((is_undefweak = h->root.type == bfd_link_hash_undefweak)) ++ { ++ defined_local = FALSE; ++ resolved_local = FALSE; ++ resolved_to_const = !is_dyn || h->dynindx == -1 ++ || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h); ++ resolved_dynly = !resolved_local && !resolved_to_const; ++ } ++ else if (warned) ++ { ++ /* Symbol undefined offen means failed already. I don't know why ++ 'warned' here but I guess it want to continue relocating as if ++ no error occures to find other errors as more as possible. */ ++ ++ /* To avoid generating warning messages about truncated ++ relocations, set the relocation's address to be the same as ++ the start of this section. */ ++ relocation = input_section->output_section ++ ? input_section->output_section->vma : 0; ++ ++ defined_local = relocation != 0; ++ resolved_local = defined_local; ++ resolved_to_const = !resolved_local; ++ resolved_dynly = FALSE; ++ } ++ else ++ { ++ defined_local = !unresolved_reloc && !ignored; ++ resolved_local = ++ defined_local && SYMBOL_REFERENCES_LOCAL (info, h); ++ resolved_dynly = !resolved_local; ++ resolved_to_const = !resolved_local && !resolved_dynly; ++ } ++ } ++ ++ name = loongarch_sym_name (input_bfd, h, sym); ++ ++ if (sec != NULL && discarded_section (sec)) ++ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, ++ rel, 1, relend, howto, 0, contents); ++ ++ if (bfd_link_relocatable (info)) ++ continue; ++ ++ /* r_symndx will be STN_UNDEF (zero) only for relocs against symbols ++ from removed linkonce sections, or sections discarded by a linker ++ script. Also for R_*_SOP_PUSH_ABSOLUTE and PCREL to specify const. */ ++ if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec)) ++ resolved_dynly = resolved_local = defined_local = FALSE ++ , resolved_to_const = TRUE; ++ ++ if (h && h->type == STT_GNU_IFUNC) ++ { ++ /* a. 动态连接器可以直接处理STT_GNU_IFUNC,因为动态连接器可以察觉到 ++ 一个动态符号是STT_GNU_IFUNC,从而在装载时执行resolver; ++ 但local符号不行,因为根本没有动态符号,所以要将R_LARCH_64转化为 ++ R_LARCH_IRELATIVE,类似的,所有其他重定位类型可能都要针对IFUNC ++ 做一些特殊操作,我觉得有点麻烦了。 ++ b. 此外,比如代码段的重定位无法动态改变其的引用位置,所以必须走plt ++ 来实现IFUNC。 ++ c. 因此,为方便实现,我们将plt stub的位置当作IFUNC符号的定义。 */ ++ if (h->plt.offset == MINUS_ONE) ++ info->callbacks->info ++ ("%X%pB(%pA+0x%v): error: %s against `%s':\n" ++ "STT_GNU_IFUNC must have PLT stub" "\n", ++ input_bfd, input_section, (bfd_vma) rel->r_offset, ++ howto->name, name); ++ defined_local = TRUE; ++ resolved_local = TRUE; ++ resolved_dynly = FALSE; ++ resolved_to_const = FALSE; ++ relocation = sec_addr (plt) + h->plt.offset; ++ } ++ ++ unresolved_reloc = resolved_dynly; ++ ++ BFD_ASSERT (resolved_local + resolved_dynly + resolved_to_const == 1); ++ ++ /* a. 命题 'resolved_dynly' ==> 'h && h->dynindx != -1' 成立。 ++ b. 需要动态重定位一个符号当然需要动态符号表。这个断言失败意味着某个 ++ 动态符号没有执行bfd_elf_link_record_dynamic_symbol。之前的逻辑有问题 ++ c. 另外,即使resolved_dynly为真,也不一定真的生成动态重定位表项,因为 ++ 有时section没有SEC_ALLOC这个flag,当段不需要被加载进内存自然不需要动态 ++ 重定位。 */ ++ BFD_ASSERT (!resolved_dynly || (h && h->dynindx != -1)); ++ ++ BFD_ASSERT (!resolved_local || defined_local); ++ ++ is_ie = FALSE; ++ switch (r_type) ++ { ++#define LARCH_ASSERT(cond, bfd_fail_state, message) \ ++ ({if (!(cond)) { \ ++ r = bfd_fail_state; \ ++ switch (r) { \ ++ /* 'dangerous' means we do it but can't promise it's ok \ ++ 'unsupport' means out of ability of relocation type \ ++ 'undefined' means we can't deal with the undefined symbol */ \ ++ case bfd_reloc_undefined: \ ++ info->callbacks->undefined_symbol \ ++ (info, name, input_bfd, input_section, rel->r_offset, TRUE); \ ++ default: \ ++ fatal = TRUE; \ ++ info->callbacks->info \ ++ ("%X%pB(%pA+0x%v): error: %s against %s`%s':\n" \ ++ message "\n", \ ++ input_bfd, input_section, (bfd_vma) rel->r_offset, \ ++ howto->name, is_undefweak? "[undefweak] " : "", name); \ ++ break; \ ++ case bfd_reloc_dangerous: \ ++ info->callbacks->info \ ++ ("%pB(%pA+0x%v): warning: %s against %s`%s':\n" \ ++ message "\n", \ ++ input_bfd, input_section, (bfd_vma) rel->r_offset, \ ++ howto->name, is_undefweak? "[undefweak] " : "", name); \ ++ break; \ ++ case bfd_reloc_ok: \ ++ case bfd_reloc_continue: \ ++ info->callbacks->info \ ++ ("%pB(%pA+0x%v): message: %s against %s`%s':\n" \ ++ message "\n", \ ++ input_bfd, input_section, (bfd_vma) rel->r_offset, \ ++ howto->name, is_undefweak? "[undefweak] " : "", name); \ ++ break; \ ++ } \ ++ if (fatal) break; \ ++ }}) ++ case R_LARCH_MARK_PCREL: ++ case R_LARCH_MARK_LA: ++ case R_LARCH_NONE: ++ r = bfd_reloc_continue; ++ unresolved_reloc = FALSE; ++ break; ++ ++ case R_LARCH_32: ++ case R_LARCH_64: ++ if (resolved_dynly || (is_pic && resolved_local)) ++ { ++ Elf_Internal_Rela outrel; ++ ++ /* When generating a shared object, these relocations are copied ++ into the output file to be resolved at run time. */ ++ ++ outrel.r_offset = ++ _bfd_elf_section_offset (output_bfd, info, input_section, ++ rel->r_offset); ++ ++ unresolved_reloc = !((bfd_vma) -2 <= outrel.r_offset) ++ && (input_section->flags & SEC_ALLOC); ++ ++ outrel.r_offset += sec_addr (input_section); ++ if (resolved_dynly) ++ { ++ outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type); ++ outrel.r_addend = rel->r_addend; ++ } ++ else ++ { ++ outrel.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE); ++ outrel.r_addend = relocation + rel->r_addend; ++ } ++ ++ if (unresolved_reloc) ++ loongarch_elf_append_rela (output_bfd, sreloc, &outrel); ++ } ++ ++ relocation += rel->r_addend; ++ break; ++ ++ case R_LARCH_ADD8: ++ case R_LARCH_ADD16: ++ case R_LARCH_ADD24: ++ case R_LARCH_ADD32: ++ case R_LARCH_ADD64: ++ case R_LARCH_SUB8: ++ case R_LARCH_SUB16: ++ case R_LARCH_SUB24: ++ case R_LARCH_SUB32: ++ case R_LARCH_SUB64: ++ LARCH_ASSERT (!resolved_dynly, bfd_reloc_undefined, ++"Can't be resolved dynamically. If this procedure is hand-writing assemble,\n" ++"there must be something like '.dword sym1 - sym2' to generate these relocs\n" ++"and we can't get known link-time address of these symbols."); ++ relocation += rel->r_addend; ++ break; ++ ++ case R_LARCH_TLS_DTPREL32: ++ case R_LARCH_TLS_DTPREL64: ++ if (resolved_dynly) ++ { ++ Elf_Internal_Rela outrel; ++ ++ outrel.r_offset = ++ _bfd_elf_section_offset (output_bfd, info, input_section, ++ rel->r_offset); ++ ++ unresolved_reloc = !((bfd_vma) -2 <= outrel.r_offset) ++ && (input_section->flags & SEC_ALLOC); ++ outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type); ++ outrel.r_offset += sec_addr (input_section); ++ outrel.r_addend = rel->r_addend; ++ if (unresolved_reloc) ++ loongarch_elf_append_rela (output_bfd, sreloc, &outrel); ++ break; ++ } ++ ++ LARCH_ASSERT (!resolved_to_const, bfd_reloc_notsupported, ++ "Internal:"); ++ ++ case R_LARCH_SOP_PUSH_TLS_TPREL: ++ if (resolved_local) ++ { ++ LARCH_ASSERT (elf_hash_table (info)->tls_sec, ++ bfd_reloc_notsupported, "TLS section not be created"); ++ relocation -= elf_hash_table (info)->tls_sec->vma; ++ } ++ ++ LARCH_ASSERT (resolved_local, bfd_reloc_undefined, ++ "TLS LE just can be resolved local only."); ++ break; ++ ++ case R_LARCH_SOP_PUSH_ABSOLUTE: ++ if (is_undefweak) ++ { ++ LARCH_ASSERT (!resolved_dynly, bfd_reloc_dangerous, ++"Someone require us to resolve undefweak symbol dynamically.\n" ++"But this reloc can't be done. I think I can't throw error for this\n" ++"so I resolved it to 0. I suggest to re-compile with '-fpic'."); ++ relocation = 0; ++ unresolved_reloc = FALSE; ++ break; ++ } ++ ++ if (resolved_to_const) ++ { ++ relocation += rel->r_addend; ++ break; ++ } ++ ++ LARCH_ASSERT (!is_pic, bfd_reloc_notsupported, ++"Under PIC we don't know load address. Re-compile src with '-fpic'?"); ++ ++ if (resolved_dynly) ++ { ++ LARCH_ASSERT (plt && h && h->plt.offset != MINUS_ONE, ++ bfd_reloc_undefined, ++"Can't be resolved dynamically. Try to re-compile src with '-fpic'?"); ++ ++ LARCH_ASSERT (rel->r_addend == 0, bfd_reloc_notsupported, ++ "Shouldn't be with r_addend."); ++ ++ relocation = sec_addr (plt) + h->plt.offset; ++ unresolved_reloc = FALSE; ++ break; ++ } ++ ++ if (resolved_local) ++ { ++ relocation += rel->r_addend; ++ break; ++ } ++ ++ break; ++ ++ case R_LARCH_SOP_PUSH_PCREL: ++ case R_LARCH_SOP_PUSH_PLT_PCREL: ++ unresolved_reloc = FALSE; ++ ++ if (resolved_to_const) ++ { ++ relocation += rel->r_addend; ++ break; ++ } ++ else if (is_undefweak) ++ { ++ i = 0, j = 0; ++ relocation = 0; ++ if (resolved_dynly) ++ { ++ if (h && h->plt.offset != MINUS_ONE) ++ i = 1, j = 2; ++ else ++ LARCH_ASSERT (0, bfd_reloc_dangerous, ++"Undefweak need to be resolved dynamically, but PLT stub doesn't represent."); ++ } ++ } ++ else ++ { ++ LARCH_ASSERT ++ (defined_local || (h && h->plt.offset != MINUS_ONE), ++ bfd_reloc_undefined, ++ "PLT stub does not represent and symbol not defined."); ++ ++ if (resolved_local) ++ i = 0, j = 2; ++ else /* if (resolved_dynly) */ ++ { ++ LARCH_ASSERT ++ (h && h->plt.offset != MINUS_ONE, bfd_reloc_dangerous, ++"Internal: PLT stub doesn't represent. Resolve it with pcrel"); ++ i = 1, j = 3; ++ } ++ } ++ ++ for (; i < j; i++) ++ { ++ if ((i & 1) == 0 && defined_local) ++ { ++ relocation -= pc; ++ relocation += rel->r_addend; ++ break; ++ } ++ ++ if ((i & 1) && h && h->plt.offset != MINUS_ONE) ++ { ++ LARCH_ASSERT (rel->r_addend == 0, bfd_reloc_notsupported, ++ "PLT shouldn't be with r_addend."); ++ relocation = sec_addr (plt) + h->plt.offset - pc; ++ break; ++ } ++ } ++ break; ++ ++ case R_LARCH_SOP_PUSH_GPREL: ++ unresolved_reloc = FALSE; ++ ++ LARCH_ASSERT (rel->r_addend == 0, bfd_reloc_notsupported, ++ "Shouldn't be with r_addend."); ++ ++ /* 约定在GOT表中写入连接时地址。动态连接器通过_GLOBAL_OFFSET_TABLE_的 ++ 连接时地址和运行时地址拿到模块的加载地址,拿到连接时地址的办法就是 ++ 拿到那个got entry。一些体系结构通过.dynamic的段基址拿到模块加载 ++ 地址,我没有这么做,因为这个段在static-pie下不存在。 */ ++ ++ if (h != NULL) ++ { ++ off = h->got.offset; ++ ++ LARCH_ASSERT (off != MINUS_ONE, bfd_reloc_notsupported, ++ "Internal: GOT entry doesn't represent."); ++ ++ if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (is_dyn, is_pic, h) ++ || (is_pic && SYMBOL_REFERENCES_LOCAL (info, h))) ++ { ++ /* This is actually a static link, or it is a ++ -Bsymbolic link and the symbol is defined ++ locally, or the symbol was forced to be local ++ because of a version file. We must initialize ++ this entry in the global offset table. Since the ++ offset must always be a multiple of the word size, ++ we use the least significant bit to record whether ++ we have initialized it already. ++ ++ When doing a dynamic link, we create a .rela.got ++ relocation entry to initialize the value. This ++ is done in the finish_dynamic_symbol routine. */ ++ ++ /* 在这里先不用管STT_GNU_IFUNC。elf_finish_dynamic_symbol ++ 会单独处理。 */ ++ ++ LARCH_ASSERT (!resolved_dynly, bfd_reloc_dangerous, ++ "Internal: here shouldn't dynamic."); ++ LARCH_ASSERT (defined_local || resolved_to_const, ++ bfd_reloc_undefined, "Internal: "); ++ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ bfd_put_NN (output_bfd, relocation, got->contents + off); ++ h->got.offset |= 1; ++ } ++ } ++ } ++ else ++ { ++ LARCH_ASSERT (local_got_offsets, bfd_reloc_notsupported, ++ "Internal: local got offsets not reporesent."); ++ ++ off = local_got_offsets[r_symndx]; ++ ++ LARCH_ASSERT (off != MINUS_ONE, bfd_reloc_notsupported, ++ "Internal: GOT entry doesn't represent."); ++ ++ /* The offset must always be a multiple of the word size. ++ So, we can use the least significant bit to record ++ whether we have already processed this entry. */ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ if (is_pic) ++ { ++ asection *s; ++ Elf_Internal_Rela outrel; ++ /* We need to generate a R_LARCH_RELATIVE reloc ++ for the dynamic linker. */ ++ s = htab->elf.srelgot; ++ LARCH_ASSERT (s, bfd_reloc_notsupported, ++ "Internal: '.rel.got' not represent"); ++ ++ outrel.r_offset = sec_addr (got) + off; ++ outrel.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE); ++ outrel.r_addend = relocation; /* link-time addr */ ++ loongarch_elf_append_rela (output_bfd, s, &outrel); ++ } ++ ++ bfd_put_NN (output_bfd, relocation, got->contents + off); ++ local_got_offsets[r_symndx] |= 1; ++ } ++ } ++ relocation = off; ++ break; ++ ++ case R_LARCH_SOP_PUSH_TLS_GOT: ++ is_ie = TRUE; ++ case R_LARCH_SOP_PUSH_TLS_GD: ++ unresolved_reloc = FALSE; ++ ++ LARCH_ASSERT (rel->r_addend == 0, bfd_reloc_notsupported, ++ "Shouldn't be with r_addend."); ++ ++ if (resolved_to_const && is_undefweak && h->dynindx != -1) ++ { ++ /* What if undefweak? Let rtld make a decision. */ ++ resolved_to_const = resolved_local = FALSE; ++ resolved_dynly = TRUE; ++ } ++ ++ LARCH_ASSERT (!resolved_to_const, bfd_reloc_notsupported, ++ "Internal: Shouldn't be resolved to const."); ++ ++ if (h != NULL) ++ { ++ off = h->got.offset; ++ h->got.offset |= 1; ++ } ++ else ++ { ++ off = local_got_offsets[r_symndx]; ++ local_got_offsets[r_symndx] |= 1; ++ } ++ ++ LARCH_ASSERT (off != MINUS_ONE, bfd_reloc_notsupported, ++ "Internal: TLS GOT entry doesn't represent."); ++ ++ tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx); ++ ++ /* If this symbol is referenced by both GD and IE TLS, the IE ++ reference's GOT slot follows the GD reference's slots. */ ++ ie_off = 0; ++ if ((tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_IE)) ++ ie_off = 2 * GOT_ENTRY_SIZE; ++ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ bfd_vma tls_block_off = 0; ++ Elf_Internal_Rela outrel; ++ ++ if (resolved_local) ++ { ++ LARCH_ASSERT ++ (elf_hash_table (info)->tls_sec, bfd_reloc_notsupported, ++ "Internal: TLS sec not represent."); ++ tls_block_off = relocation ++ - elf_hash_table (info)->tls_sec->vma; ++ } ++ ++ if (tls_type & GOT_TLS_GD) ++ { ++ outrel.r_offset = sec_addr (got) + off; ++ outrel.r_addend = 0; ++ bfd_put_NN (output_bfd, 0, got->contents + off); ++ if (resolved_local && bfd_link_executable (info)) ++ /* a. 第一个被装载模块的Module ID为1。$glibc/elf/rtld.c中 ++ 的dl_main有一句'main_map->l_tls_modid = 1'; ++ b. 静态程序的Module ID不重要,但为了省事仍然是1。 ++ 详见$glibc/csu/libc-tls.c中的init_static_tls。 */ ++ bfd_put_NN (output_bfd, 1, got->contents + off); ++ else if (resolved_local/* && !bfd_link_executable (info) */) ++ { ++ outrel.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_DTPMODNN); ++ loongarch_elf_append_rela ++ (output_bfd, htab->elf.srelgot, &outrel); ++ } ++ else /* if (resolved_dynly) */ ++ { ++ outrel.r_info = ++ ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_DTPMODNN); ++ loongarch_elf_append_rela ++ (output_bfd, htab->elf.srelgot, &outrel); ++ } ++ ++ outrel.r_offset += GOT_ENTRY_SIZE; ++ bfd_put_NN (output_bfd, tls_block_off, ++ got->contents + off + GOT_ENTRY_SIZE); ++ if (resolved_local) ++ /* DTPREL known */; ++ else /* if (resolved_dynly) */ ++ { ++ outrel.r_info = ++ ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_DTPRELNN); ++ loongarch_elf_append_rela ++ (output_bfd, htab->elf.srelgot, &outrel); ++ } ++ } ++ ++ if (tls_type & GOT_TLS_IE) ++ { ++ outrel.r_offset = sec_addr (got) + off + ie_off; ++ bfd_put_NN (output_bfd, tls_block_off, ++ got->contents + off + ie_off); ++ if (resolved_local && bfd_link_executable (info)) ++ /* TPREL known */; ++ else if (resolved_local/* && !bfd_link_executable (info) */) ++ { ++ outrel.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN); ++ outrel.r_addend = tls_block_off; ++ loongarch_elf_append_rela ++ (output_bfd, htab->elf.srelgot, &outrel); ++ } ++ else /* if (resolved_dynly) */ ++ { ++ outrel.r_info = ++ ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_TPRELNN); ++ outrel.r_addend = 0; ++ loongarch_elf_append_rela ++ (output_bfd, htab->elf.srelgot, &outrel); ++ } ++ } ++ } ++ ++ relocation = off + (is_ie ? ie_off : 0); ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (fatal) ++ break; ++ ++ do ++ { ++ /* 'unresolved_reloc' means we haven't done it yet. ++ We need help of dynamic linker to fix this memory location up. */ ++ if (!unresolved_reloc) ++ break; ++ ++ if (_bfd_elf_section_offset (output_bfd, info, input_section, ++ rel->r_offset) == MINUS_ONE) ++ /* WHY? May because it's invalid so skip checking. ++ But why dynamic reloc a invalid section? */ ++ break; ++ ++ if (input_section->output_section->flags & SEC_DEBUGGING) ++ { ++ LARCH_ASSERT (0, bfd_reloc_dangerous, ++ "Seems dynamic linker not process sections 'SEC_DEBUGGING'."); ++ break; ++ } ++ if (!is_dyn) ++ break; ++ ++ if ((info->flags & DF_TEXTREL) == 0) ++ if (input_section->output_section->flags & SEC_READONLY) ++ info->flags |= DF_TEXTREL; ++ } ++ while (0); ++#undef LARCH_ASSERT ++ ++ if (fatal) ++ break; ++ ++ loongarch_record_one_reloc (input_bfd, input_section, r_type, ++ rel->r_offset, sym, h, rel->r_addend); ++ ++ if (r != bfd_reloc_continue) ++ r = perform_relocation (rel, relocation, input_bfd, contents); ++ ++ switch (r) ++ { ++ case bfd_reloc_dangerous: ++ case bfd_reloc_continue: ++ case bfd_reloc_ok: ++ continue; ++ ++ case bfd_reloc_overflow: ++ /* Overflow value can't be filled in */ ++ loongarch_dump_reloc_record (info->callbacks->info); ++ info->callbacks->reloc_overflow ++ (info, (h ? &h->root : NULL), name, howto->name, ++ rel->r_addend, input_bfd, input_section, rel->r_offset); ++ break; ++ ++ case bfd_reloc_outofrange: ++ /* Stack state incorrect */ ++ loongarch_dump_reloc_record (info->callbacks->info); ++ info->callbacks->info ++ ("%X%H: Internal stack state is incorrect.\n" ++ "Want to push to full stack or pop from empty stack?\n", ++ input_bfd, input_section, rel->r_offset); ++ break; ++ ++ case bfd_reloc_notsupported: ++ info->callbacks->info ++ ("%X%H: Unknown relocation type.\n", ++ input_bfd, input_section, rel->r_offset); ++ break; ++ ++ default: ++ info->callbacks->info ++ ("%X%H: Internal: unknown error.\n", ++ input_bfd, input_section, rel->r_offset); ++ break; ++ } ++ ++ fatal = TRUE; ++ break; ++ } ++ ++ return !fatal; ++} ++ ++/* Finish up dynamic symbol handling. We set the contents of various ++ dynamic sections here. */ ++ ++static bfd_boolean ++loongarch_elf_finish_dynamic_symbol (bfd *output_bfd, ++ struct bfd_link_info *info, ++ struct elf_link_hash_entry *h, ++ Elf_Internal_Sym *sym) ++{ ++ struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info); ++ const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); ++ asection *plt = NULL; ++ ++ if (h->plt.offset != MINUS_ONE) ++ { ++ size_t i, plt_idx; ++ asection *gotplt, *relplt; ++ bfd_vma got_address; ++ uint32_t plt_entry[PLT_ENTRY_INSNS]; ++ bfd_byte *loc; ++ Elf_Internal_Rela rela; ++ ++ plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE; ++ ++ /* one of '.plt' and '.iplt' represents */ ++ BFD_ASSERT (!!htab->elf.splt ^ !!htab->elf.iplt); ++ ++ if (htab->elf.splt) ++ { ++ BFD_ASSERT ((h->type == STT_GNU_IFUNC ++ && SYMBOL_REFERENCES_LOCAL (info, h)) ++ || h->dynindx != -1); ++ ++ plt = htab->elf.splt; ++ gotplt = htab->elf.sgotplt; ++ relplt = htab->elf.srelplt; ++ got_address = sec_addr (gotplt) + GOTPLT_HEADER_SIZE ++ + plt_idx * GOT_ENTRY_SIZE; ++ } ++ else /* if (htab->elf.iplt) */ ++ { ++ BFD_ASSERT (h->type == STT_GNU_IFUNC ++ && SYMBOL_REFERENCES_LOCAL (info, h)); ++ ++ plt = htab->elf.iplt; ++ gotplt = htab->elf.igotplt; ++ relplt = htab->elf.irelplt; ++ got_address = sec_addr (gotplt) ++ + plt_idx * GOT_ENTRY_SIZE; ++ } ++ ++ /* Find out where the .plt entry should go. */ ++ loc = plt->contents + h->plt.offset; ++ ++ /* Fill in the PLT entry itself. */ ++ loongarch_make_plt_entry ++ (got_address, sec_addr (plt) + h->plt.offset, plt_entry); ++ for (i = 0; i < PLT_ENTRY_INSNS; i++) ++ bfd_put_32 (output_bfd, plt_entry[i], loc + 4 * i); ++ ++ /* Fill in the initial value of the .got.plt entry. */ ++ loc = gotplt->contents + (got_address - sec_addr (gotplt)); ++ bfd_put_NN (output_bfd, sec_addr (plt), loc); ++ ++ rela.r_offset = got_address; ++ if (h->type == STT_GNU_IFUNC && SYMBOL_REFERENCES_LOCAL (info, h)) ++ { ++ rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE); ++ rela.r_addend = h->root.u.def.value ++ + h->root.u.def.section->output_section->vma ++ + h->root.u.def.section->output_offset; ++ } ++ else ++ { ++ /* Fill in the entry in the .rela.plt section. */ ++ rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_JUMP_SLOT); ++ rela.r_addend = 0; ++ } ++ ++ loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela); ++ bed->s->swap_reloca_out (output_bfd, &rela, loc); ++ ++ if (!h->def_regular) ++ { ++ /* Mark the symbol as undefined, rather than as defined in ++ the .plt section. Leave the value alone. */ ++ sym->st_shndx = SHN_UNDEF; ++ /* If the symbol is weak, we do need to clear the value. ++ Otherwise, the PLT entry would provide a definition for ++ the symbol even if the symbol wasn't defined anywhere, ++ and so the symbol would never be NULL. */ ++ if (!h->ref_regular_nonweak) ++ sym->st_value = 0; ++ } ++ } ++ ++ if (h->got.offset != MINUS_ONE ++ ++ && /* TLS got entry have been handled in elf_relocate_section */ ++ !(loongarch_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE)) ++ ++ && /* have allocated got entry but not allocated rela before */ ++ !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) ++ { ++ asection *sgot, *srela; ++ Elf_Internal_Rela rela; ++ bfd_vma off = h->got.offset & ~(bfd_vma) 1; ++ ++ /* This symbol has an entry in the GOT. Set it up. */ ++ ++ sgot = htab->elf.sgot; ++ srela = htab->elf.srelgot; ++ BFD_ASSERT (sgot && srela); ++ ++ rela.r_offset = sec_addr (sgot) + off; ++ ++ if (h->type == STT_GNU_IFUNC) ++ { ++ if (/* 加入这个条件的原因是,对于静态链接,IRELATIVE重定位类型在 ++ __libc_start_main中调用apply_irel,通过链接脚本提供的 ++ __rela_iplt_start和__rela_iplt_end遍历.rela.iplt中的动态重定位 ++ 表项,来调用各个resolver并将返回结果写入.igot.plt中。 ++ 问题是照顾不到.rela.iplt之外的IRELATIVE重定位,因此我们在静态 ++ 连接的情况下绝对不将IRELATIVE写入.igot.plt之外。这样做在运行时 ++ 可能会有一些性能影响,毕竟ifunc函数都走plt,需要load两次 ++ got entry。没什么好的解决方法,未来可以搞.iplt2用于延迟调用 ++ resolver。 */ ++ elf_hash_table (info)->dynamic_sections_created ++ ++ && SYMBOL_REFERENCES_LOCAL (info, h)) ++ { ++ asection *sec = h->root.u.def.section; ++ rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE); ++ rela.r_addend = h->root.u.def.value ++ + sec->output_section->vma ++ + sec->output_offset; ++ bfd_put_NN (output_bfd, 0, sgot->contents + off); ++ } ++ else ++ { ++ BFD_ASSERT (plt); ++ rela.r_info = ++ ELFNN_R_INFO ++ (0, bfd_link_pic (info) ? R_LARCH_RELATIVE : R_LARCH_NONE); ++ rela.r_addend = plt->output_section->vma ++ + plt->output_offset ++ + h->plt.offset; ++ bfd_put_NN (output_bfd, rela.r_addend, sgot->contents + off); ++ } ++ } ++ else if (bfd_link_pic (info) && SYMBOL_REFERENCES_LOCAL (info, h)) ++ { ++ BFD_ASSERT (h->got.offset & 1/* has been filled in addr */); ++ asection *sec = h->root.u.def.section; ++ rela.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE); ++ rela.r_addend = h->root.u.def.value ++ + sec->output_section->vma ++ + sec->output_offset; ++ } ++ else ++ { ++ BFD_ASSERT ((h->got.offset & 1) == 0); ++ BFD_ASSERT (h->dynindx != -1); ++ rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_NN); ++ rela.r_addend = 0; ++ } ++ ++ loongarch_elf_append_rela (output_bfd, srela, &rela); ++ } ++ ++ if (h->needs_copy) ++ { ++ Elf_Internal_Rela rela; ++ asection *s; ++ ++ /* This symbols needs a copy reloc. Set it up. */ ++ BFD_ASSERT (h->dynindx != -1); ++ ++ rela.r_offset = sec_addr (h->root.u.def.section) + h->root.u.def.value; ++ rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_COPY); ++ rela.r_addend = 0; ++ if (h->root.u.def.section == htab->elf.sdynrelro) ++ s = htab->elf.sreldynrelro; ++ else ++ s = htab->elf.srelbss; ++ loongarch_elf_append_rela (output_bfd, s, &rela); ++ } ++ ++ /* Mark some specially defined symbols as absolute. */ ++ if (h == htab->elf.hdynamic || h == htab->elf.hgot || h == htab->elf.hplt) ++ sym->st_shndx = SHN_ABS; ++ ++ return TRUE; ++} ++ ++/* Finish up the dynamic sections. */ ++ ++static bfd_boolean ++loongarch_finish_dyn (bfd *output_bfd, struct bfd_link_info *info, ++ bfd *dynobj, asection *sdyn) ++{ ++ struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info); ++ const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); ++ size_t dynsize = bed->s->sizeof_dyn, skipped_size = 0; ++ bfd_byte *dyncon, *dynconend; ++ ++ dynconend = sdyn->contents + sdyn->size; ++ for (dyncon = sdyn->contents; dyncon < dynconend; dyncon += dynsize) ++ { ++ Elf_Internal_Dyn dyn; ++ asection *s; ++ int skipped = 0; ++ ++ bed->s->swap_dyn_in (dynobj, dyncon, &dyn); ++ ++ switch (dyn.d_tag) ++ { ++ case DT_PLTGOT: ++ s = htab->elf.sgotplt; ++ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset; ++ break; ++ case DT_JMPREL: ++ s = htab->elf.srelplt; ++ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset; ++ break; ++ case DT_PLTRELSZ: ++ s = htab->elf.srelplt; ++ dyn.d_un.d_val = s->size; ++ break; ++ case DT_TEXTREL: ++ if ((info->flags & DF_TEXTREL) == 0) ++ skipped = 1; ++ break; ++ case DT_FLAGS: ++ if ((info->flags & DF_TEXTREL) == 0) ++ dyn.d_un.d_val &= ~DF_TEXTREL; ++ break; ++ } ++ if (skipped) ++ skipped_size += dynsize; ++ else ++ bed->s->swap_dyn_out (output_bfd, &dyn, dyncon - skipped_size); ++ } ++ /* Wipe out any trailing entries if we shifted down a dynamic tag. */ ++ memset (dyncon - skipped_size, 0, skipped_size); ++ return TRUE; ++} ++ ++/* Finish up local dynamic symbol handling. We set the contents of ++ various dynamic sections here. */ ++ ++static bfd_boolean ++elfNN_loongarch_finish_local_dynamic_symbol (void **slot, void *inf) ++{ ++ struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) *slot; ++ struct bfd_link_info *info = (struct bfd_link_info *) inf; ++ ++ return loongarch_elf_finish_dynamic_symbol (info->output_bfd, info, h, NULL); ++} ++ ++static bfd_boolean ++loongarch_elf_finish_dynamic_sections (bfd *output_bfd, ++ struct bfd_link_info *info) ++{ ++ bfd *dynobj; ++ asection *sdyn, *plt, *gotplt; ++ struct loongarch_elf_link_hash_table *htab; ++ ++ htab = loongarch_elf_hash_table (info); ++ BFD_ASSERT (htab); ++ dynobj = htab->elf.dynobj; ++ sdyn = bfd_get_linker_section (dynobj, ".dynamic"); ++ ++ if (elf_hash_table (info)->dynamic_sections_created) ++ { ++ BFD_ASSERT (htab->elf.splt && sdyn); ++ ++ if (!loongarch_finish_dyn (output_bfd, info, dynobj, sdyn)) ++ return FALSE; ++ } ++ ++ if ((plt = htab->elf.splt)) ++ gotplt = htab->elf.sgotplt; ++ else if ((plt = htab->elf.iplt)) ++ gotplt = htab->elf.igotplt; ++ ++ if (plt && 0 < plt->size) ++ { ++ size_t i; ++ uint32_t plt_header[PLT_HEADER_INSNS]; ++ loongarch_make_plt_header (sec_addr (gotplt), sec_addr (plt), plt_header); ++ for (i = 0; i < PLT_HEADER_INSNS; i++) ++ bfd_put_32 (output_bfd, plt_header[i], plt->contents + 4 * i); ++ ++ elf_section_data (plt->output_section)->this_hdr.sh_entsize ++ = PLT_ENTRY_SIZE; ++ } ++ ++ if (htab->elf.sgotplt) ++ { ++ asection *output_section = htab->elf.sgotplt->output_section; ++ ++ if (bfd_is_abs_section (output_section)) ++ { ++ _bfd_error_handler ++ (_("discarded output section: `%pA'"), htab->elf.sgotplt); ++ return FALSE; ++ } ++ ++ if (0 < htab->elf.sgotplt->size) ++ { ++ /* Write the first two entries in .got.plt, needed for the dynamic ++ linker. */ ++ bfd_put_NN (output_bfd, MINUS_ONE, htab->elf.sgotplt->contents); ++ ++ /* 第二项非0时动态连接器认为它是plt header的地址,从而影响到所有 ++ R_LARCH_JUMP_SLOT。这似乎是为了prelink预留的。 */ ++ bfd_put_NN (output_bfd, (bfd_vma) 0, ++ htab->elf.sgotplt->contents + GOT_ENTRY_SIZE); ++ } ++ ++ elf_section_data (output_section)->this_hdr.sh_entsize = GOT_ENTRY_SIZE; ++ } ++ ++ if (htab->elf.sgot) ++ { ++ asection *output_section = htab->elf.sgot->output_section; ++ ++ if (0 < htab->elf.sgot->size) ++ { ++ /* Set the first entry in the global offset table to the address of ++ the dynamic section. */ ++ bfd_vma val = sdyn ? sec_addr (sdyn) : 0; ++ bfd_put_NN (output_bfd, val, htab->elf.sgot->contents); ++ } ++ ++ elf_section_data (output_section)->this_hdr.sh_entsize = GOT_ENTRY_SIZE; ++ } ++ ++ /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */ ++ htab_traverse ++ (htab->loc_hash_table, elfNN_loongarch_finish_local_dynamic_symbol, info); ++ ++ return TRUE; ++} ++ ++/* Return address for Ith PLT stub in section PLT, for relocation REL ++ or (bfd_vma) -1 if it should not be included. */ ++ ++static bfd_vma ++loongarch_elf_plt_sym_val (bfd_vma i, const asection *plt, ++ const arelent *rel ATTRIBUTE_UNUSED) ++{ ++ return plt->vma + PLT_HEADER_SIZE + i * PLT_ENTRY_SIZE; ++} ++ ++static enum elf_reloc_type_class ++loongarch_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED, ++ const asection *rel_sec ATTRIBUTE_UNUSED, ++ const Elf_Internal_Rela *rela) ++{ ++ struct loongarch_elf_link_hash_table *htab; ++ htab = loongarch_elf_hash_table (info); ++ ++ if (htab->elf.dynsym != NULL ++ && htab->elf.dynsym->contents != NULL) ++ { ++ /* Check relocation against STT_GNU_IFUNC symbol if there are ++ dynamic symbols. ++ 一定要保证先完成非IFUNC重定位。因为如果IFUNC的resolover尚未完成 ++ 重定位,那么调用它返回的结果是错误的。非IFUNC重定位类型,比如R_LARCH_64 ++ 也可以携带IFUNC符号信息。我们在elf_machine_rela中可以察觉到一个符号是 ++ IFUNC,而在动态重定位时调用resolver。但八成这个resolver也需要一个 ++ R_LARCH_64重定位,可能还未完成,这时就会出问题。 ++ 在这里识别出来和STT_GNU_IFUNC相关的重定位,将他们往后排。 */ ++ bfd *abfd = info->output_bfd; ++ const struct elf_backend_data *bed = get_elf_backend_data (abfd); ++ unsigned long r_symndx = ELFNN_R_SYM (rela->r_info); ++ if (r_symndx != STN_UNDEF) ++ { ++ Elf_Internal_Sym sym; ++ if (!bed->s->swap_symbol_in (abfd, ++ htab->elf.dynsym->contents + r_symndx * bed->s->sizeof_sym, ++ 0, &sym)) ++ { ++ /* xgettext:c-format */ ++ _bfd_error_handler (_("%pB symbol number %lu references" ++ " nonexistent SHT_SYMTAB_SHNDX section"), ++ abfd, r_symndx); ++ /* Ideally an error class should be returned here. */ ++ } ++ else if (ELF_ST_TYPE (sym.st_info) == STT_GNU_IFUNC) ++ return reloc_class_ifunc; ++ } ++ } ++ ++ switch (ELFNN_R_TYPE (rela->r_info)) ++ { ++ case R_LARCH_IRELATIVE: ++ return reloc_class_ifunc; ++ case R_LARCH_RELATIVE: ++ return reloc_class_relative; ++ case R_LARCH_JUMP_SLOT: ++ return reloc_class_plt; ++ case R_LARCH_COPY: ++ return reloc_class_copy; ++ default: ++ return reloc_class_normal; ++ } ++} ++ ++ ++/* Copy the extra info we tack onto an elf_link_hash_entry. */ ++ ++static void ++loongarch_elf_copy_indirect_symbol (struct bfd_link_info *info, ++ struct elf_link_hash_entry *dir, ++ struct elf_link_hash_entry *ind) ++{ ++ struct loongarch_elf_link_hash_entry *edir, *eind; ++ ++ edir = (struct loongarch_elf_link_hash_entry *) dir; ++ eind = (struct loongarch_elf_link_hash_entry *) ind; ++ ++ if (eind->dyn_relocs != NULL) ++ { ++ if (edir->dyn_relocs != NULL) ++ { ++ struct elf_dyn_relocs **pp; ++ struct elf_dyn_relocs *p; ++ ++ /* Add reloc counts against the indirect sym to the direct sym ++ list. Merge any entries against the same section. */ ++ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) ++ { ++ struct elf_dyn_relocs *q; ++ ++ for (q = edir->dyn_relocs; q != NULL; q = q->next) ++ if (q->sec == p->sec) ++ { ++ q->pc_count += p->pc_count; ++ q->count += p->count; ++ *pp = p->next; ++ break; ++ } ++ if (q == NULL) ++ pp = &p->next; ++ } ++ *pp = edir->dyn_relocs; ++ } ++ ++ edir->dyn_relocs = eind->dyn_relocs; ++ eind->dyn_relocs = NULL; ++ } ++ ++ if (ind->root.type == bfd_link_hash_indirect ++ && dir->got.refcount < 0) ++ { ++ edir->tls_type = eind->tls_type; ++ eind->tls_type = GOT_UNKNOWN; ++ } ++ _bfd_elf_link_hash_copy_indirect (info, dir, ind); ++} ++ ++//#if ARCH_SIZE == 32 ++//# define PRSTATUS_SIZE 0 /* FIXME */ ++//# define PRSTATUS_OFFSET_PR_CURSIG 0 ++//# define PRSTATUS_OFFSET_PR_PID 0 ++//# define PRSTATUS_OFFSET_PR_REG 0 ++//# define ELF_GREGSET_T_SIZE 264 ++//# define PRPSINFO_SIZE 0 ++//# define PRPSINFO_OFFSET_PR_PID 0 ++//# define PRPSINFO_OFFSET_PR_FNAME 0 ++//# define PRPSINFO_OFFSET_PR_PSARGS 0 ++//#else ++#define PRSTATUS_SIZE 384 ++#define PRSTATUS_OFFSET_PR_CURSIG 12 ++#define PRSTATUS_OFFSET_PR_PID 32 ++#define PRSTATUS_OFFSET_PR_REG 112 ++#define ELF_GREGSET_T_SIZE 264 ++ ++#define PRPSINFO_SIZE 144 ++#define PRPSINFO_OFFSET_PR_PID 32 ++#define PRPSINFO_OFFSET_PR_FNAME 48 ++#define PRPSINFO_OFFSET_PR_PSARGS 64 ++#define PRPSINFO_SIZE_PR_FNAMW 16 ++#define PRPSINFO_SIZE_PR_PSARGS 80 ++//#endif ++ ++/* Support for core dump NOTE sections. */ ++ ++static bfd_boolean ++loongarch_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) ++{ ++ switch (note->descsz) ++ { ++ default: ++ return FALSE; ++ ++ case PRSTATUS_SIZE: /* sizeof(struct elf_prstatus) on Linux/Loongarch. */ ++ /* pr_cursig */ ++ elf_tdata (abfd)->core->signal ++ = bfd_get_16 (abfd, note->descdata + PRSTATUS_OFFSET_PR_CURSIG); ++ ++ /* pr_pid */ ++ elf_tdata (abfd)->core->lwpid ++ = bfd_get_32 (abfd, note->descdata + PRSTATUS_OFFSET_PR_PID); ++ break; ++ } ++ ++ /* Make a ".reg/999" section. */ ++ return _bfd_elfcore_make_pseudosection (abfd, ".reg", ELF_GREGSET_T_SIZE, ++ note->descpos + PRSTATUS_OFFSET_PR_REG); ++} ++ ++static bfd_boolean ++loongarch_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) ++{ ++ switch (note->descsz) ++ { ++ default: ++ return FALSE; ++ ++ case PRPSINFO_SIZE: /* sizeof(struct elf_prpsinfo) on Linux/Loongarch. */ ++ /* pr_pid */ ++ elf_tdata (abfd)->core->pid ++ = bfd_get_32 (abfd, note->descdata + PRPSINFO_OFFSET_PR_PID); ++ ++ /* pr_fname */ ++ elf_tdata (abfd)->core->program = _bfd_elfcore_strndup ++ (abfd, note->descdata + PRPSINFO_OFFSET_PR_FNAME, PRPSINFO_OFFSET_PR_FNAME); ++ ++ /* pr_psargs */ ++ elf_tdata (abfd)->core->command = _bfd_elfcore_strndup ++ (abfd, note->descdata + PRPSINFO_OFFSET_PR_PSARGS, PRPSINFO_SIZE_PR_PSARGS); ++ break; ++ } ++ ++ /* 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; ++} ++ ++/* Set the right mach type. */ ++static bfd_boolean ++loongarch_elf_object_p (bfd *abfd) ++{ ++ /* There are only two mach types in Loongarch currently. */ ++ if (strcmp (abfd->xvec->name, "elf64-loongarch") == 0) ++ bfd_default_set_arch_mach (abfd, bfd_arch_loongarch, bfd_mach_loongarch64); ++ else ++ bfd_default_set_arch_mach (abfd, bfd_arch_loongarch, bfd_mach_loongarch32); ++ return TRUE; ++} ++ ++static asection * ++loongarch_elf_gc_mark_hook (asection *sec, ++ struct bfd_link_info *info, ++ Elf_Internal_Rela *rel, ++ struct elf_link_hash_entry *h, ++ Elf_Internal_Sym *sym) ++{ ++ if (h != NULL) ++ switch (ELFNN_R_TYPE (rel->r_info)) ++ { ++ case R_LARCH_GNU_VTINHERIT: ++ case R_LARCH_GNU_VTENTRY: ++ return NULL; ++ } ++ ++ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); ++} ++ ++static bfd_boolean ++_loongarch_bfd_set_section_contents(bfd *abfd, ++ sec_ptr section, ++ const void *location, ++ file_ptr offset, ++ bfd_size_type conut) ++ ++{ ++ if (elf_elfheader(abfd)->e_flags ==0) ++ if(abfd->arch_info->arch == bfd_arch_loongarch) ++ if (abfd->arch_info->mach ==bfd_mach_loongarch32) ++ elf_elfheader(abfd)->e_flags = EF_LARCH_ABI_LP32; ++ else if (abfd->arch_info->mach ==bfd_mach_loongarch64) ++ elf_elfheader(abfd)->e_flags = EF_LARCH_ABI_LP64; ++ else ++ return FALSE; ++ return _bfd_elf_set_section_contents(abfd,section,location,offset,conut); ++} ++ ++ ++ ++#define TARGET_LITTLE_SYM loongarch_elfNN_vec ++#define TARGET_LITTLE_NAME "elfNN-loongarch" ++#define ELF_ARCH bfd_arch_loongarch ++#define ELF_TARGET_ID LARCH_ELF_DATA ++#define ELF_MACHINE_CODE EM_LOONGARCH ++#define ELF_MAXPAGESIZE 0x4000 ++#define bfd_elfNN_bfd_reloc_type_lookup loongarch_reloc_type_lookup ++#define bfd_elfNN_bfd_link_hash_table_create loongarch_elf_link_hash_table_create ++#define bfd_elfNN_bfd_reloc_name_lookup loongarch_reloc_name_lookup ++#define elf_info_to_howto_rel NULL /* fall through to elf_info_to_howto */ ++#define elf_info_to_howto loongarch_info_to_howto_rela ++#define bfd_elfNN_bfd_merge_private_bfd_data \ ++ _bfd_loongarch_elf_merge_private_bfd_data ++ ++#define bfd_elfNN_set_section_contents _loongarch_bfd_set_section_contents ++ ++ ++#define elf_backend_reloc_type_class loongarch_reloc_type_class ++#define elf_backend_copy_indirect_symbol loongarch_elf_copy_indirect_symbol ++#define elf_backend_create_dynamic_sections loongarch_elf_create_dynamic_sections ++#define elf_backend_check_relocs loongarch_elf_check_relocs ++#define elf_backend_adjust_dynamic_symbol loongarch_elf_adjust_dynamic_symbol ++#define elf_backend_size_dynamic_sections loongarch_elf_size_dynamic_sections ++#define elf_backend_relocate_section loongarch_elf_relocate_section ++#define elf_backend_finish_dynamic_symbol loongarch_elf_finish_dynamic_symbol ++#define elf_backend_finish_dynamic_sections loongarch_elf_finish_dynamic_sections ++#define elf_backend_object_p loongarch_elf_object_p ++#define elf_backend_gc_mark_hook loongarch_elf_gc_mark_hook ++#define elf_backend_plt_sym_val loongarch_elf_plt_sym_val ++#define elf_backend_grok_prstatus loongarch_elf_grok_prstatus ++#define elf_backend_grok_psinfo loongarch_elf_grok_psinfo ++ ++#include "elfNN-target.h" +diff --git a/bfd/elfxx-loongarch.c b/bfd/elfxx-loongarch.c +new file mode 100644 +index 00000000..3f8534bf +--- /dev/null ++++ b/bfd/elfxx-loongarch.c +@@ -0,0 +1,172 @@ ++#include "sysdep.h" ++#include "bfd.h" ++#include "libbfd.h" ++#include "elf-bfd.h" ++#include "elf/loongarch.h" ++#include "elfxx-loongarch.h" ++ ++ ++ ++/* This does not include any relocation information, but should be ++ good enough for GDB or objdump to read the file. */ ++ ++static reloc_howto_type howto_table[] = ++{ ++#define LOONGARCH_HOWTO(r_name) HOWTO (R_LARCH_##r_name,0,3,32,FALSE,0,complain_overflow_signed,bfd_elf_generic_reloc,"R_LARCH_"#r_name,FALSE,0,0,FALSE) ++LOONGARCH_HOWTO (NONE), ++LOONGARCH_HOWTO (32), ++LOONGARCH_HOWTO (64), ++LOONGARCH_HOWTO (RELATIVE), ++LOONGARCH_HOWTO (COPY), ++LOONGARCH_HOWTO (JUMP_SLOT), ++LOONGARCH_HOWTO (TLS_DTPMOD32), ++LOONGARCH_HOWTO (TLS_DTPMOD64), ++LOONGARCH_HOWTO (TLS_DTPREL32), ++LOONGARCH_HOWTO (TLS_DTPREL64), ++LOONGARCH_HOWTO (TLS_TPREL32), ++LOONGARCH_HOWTO (TLS_TPREL64), ++LOONGARCH_HOWTO (IRELATIVE), ++ ++LOONGARCH_HOWTO (MARK_LA), ++LOONGARCH_HOWTO (MARK_PCREL), ++ HOWTO (R_LARCH_SOP_PUSH_PCREL, /* type */ ++ 2, /* rightshift */ ++ 3, /* size */ ++ 32, /* bitsize */ ++ TRUE/* FIXME: somewhat use this */, /* pc_relative */ ++ 0, /* bitpos */ ++ complain_overflow_signed, /* complain_on_overflow */ ++ bfd_elf_generic_reloc, /* special_function */ ++ "R_LARCH_SOP_PUSH_PCREL", /* name */ ++ FALSE, /* partial_inplace */ ++ 0x03ffffff, /* src_mask */ ++ 0x03ffffff, /* dst_mask */ ++ FALSE), /* pcrel_offset */ ++LOONGARCH_HOWTO (SOP_PUSH_ABSOLUTE), ++LOONGARCH_HOWTO (SOP_PUSH_DUP), ++LOONGARCH_HOWTO (SOP_PUSH_GPREL), ++LOONGARCH_HOWTO (SOP_PUSH_TLS_TPREL), ++LOONGARCH_HOWTO (SOP_PUSH_TLS_GOT), ++LOONGARCH_HOWTO (SOP_PUSH_TLS_GD), ++LOONGARCH_HOWTO (SOP_PUSH_PLT_PCREL), ++LOONGARCH_HOWTO (SOP_ASSERT), ++LOONGARCH_HOWTO (SOP_NOT), ++LOONGARCH_HOWTO (SOP_SUB), ++LOONGARCH_HOWTO (SOP_SL), ++LOONGARCH_HOWTO (SOP_SR), ++LOONGARCH_HOWTO (SOP_ADD), ++LOONGARCH_HOWTO (SOP_AND), ++LOONGARCH_HOWTO (SOP_IF_ELSE), ++LOONGARCH_HOWTO (SOP_POP_32_S_10_5), ++LOONGARCH_HOWTO (SOP_POP_32_U_10_12), ++LOONGARCH_HOWTO (SOP_POP_32_S_10_12), ++LOONGARCH_HOWTO (SOP_POP_32_S_10_16), ++LOONGARCH_HOWTO (SOP_POP_32_S_10_16_S2), ++LOONGARCH_HOWTO (SOP_POP_32_S_5_20), ++LOONGARCH_HOWTO (SOP_POP_32_S_0_5_10_16_S2), ++LOONGARCH_HOWTO (SOP_POP_32_S_0_10_10_16_S2), ++LOONGARCH_HOWTO (SOP_POP_32_U), ++LOONGARCH_HOWTO (ADD8), ++LOONGARCH_HOWTO (ADD16), ++LOONGARCH_HOWTO (ADD24), ++LOONGARCH_HOWTO (ADD32), ++LOONGARCH_HOWTO (ADD64), ++LOONGARCH_HOWTO (SUB8), ++LOONGARCH_HOWTO (SUB16), ++LOONGARCH_HOWTO (SUB24), ++LOONGARCH_HOWTO (SUB32), ++LOONGARCH_HOWTO (SUB64), ++}; ++ ++struct elf_reloc_map ++{ ++ bfd_reloc_code_real_type bfd_val; ++ enum elf_loongarch_reloc_type elf_val; ++}; ++ ++static const struct elf_reloc_map loong_reloc_map[] = ++{ ++ { BFD_RELOC_NONE, R_LARCH_NONE }, ++ { BFD_RELOC_32, R_LARCH_32 }, ++ { BFD_RELOC_64, R_LARCH_64 }, ++ ++#define LOONGARCH_reloc_map(r_name) {BFD_RELOC_LARCH_##r_name,R_LARCH_##r_name} ++LOONGARCH_reloc_map (TLS_DTPMOD32), ++LOONGARCH_reloc_map (TLS_DTPMOD64), ++LOONGARCH_reloc_map (TLS_DTPREL32), ++LOONGARCH_reloc_map (TLS_DTPREL64), ++LOONGARCH_reloc_map (TLS_TPREL32), ++LOONGARCH_reloc_map (TLS_TPREL64), ++ ++LOONGARCH_reloc_map (MARK_LA), ++LOONGARCH_reloc_map (MARK_PCREL), ++LOONGARCH_reloc_map (SOP_PUSH_PCREL), ++LOONGARCH_reloc_map (SOP_PUSH_ABSOLUTE), ++LOONGARCH_reloc_map (SOP_PUSH_DUP), ++LOONGARCH_reloc_map (SOP_PUSH_GPREL), ++LOONGARCH_reloc_map (SOP_PUSH_TLS_TPREL), ++LOONGARCH_reloc_map (SOP_PUSH_TLS_GOT), ++LOONGARCH_reloc_map (SOP_PUSH_TLS_GD), ++LOONGARCH_reloc_map (SOP_PUSH_PLT_PCREL), ++LOONGARCH_reloc_map (SOP_ASSERT), ++LOONGARCH_reloc_map (SOP_NOT), ++LOONGARCH_reloc_map (SOP_SUB), ++LOONGARCH_reloc_map (SOP_SL), ++LOONGARCH_reloc_map (SOP_SR), ++LOONGARCH_reloc_map (SOP_ADD), ++LOONGARCH_reloc_map (SOP_AND), ++LOONGARCH_reloc_map (SOP_IF_ELSE), ++LOONGARCH_reloc_map (SOP_POP_32_S_10_5), ++LOONGARCH_reloc_map (SOP_POP_32_U_10_12), ++LOONGARCH_reloc_map (SOP_POP_32_S_10_12), ++LOONGARCH_reloc_map (SOP_POP_32_S_10_16), ++LOONGARCH_reloc_map (SOP_POP_32_S_10_16_S2), ++LOONGARCH_reloc_map (SOP_POP_32_S_5_20), ++LOONGARCH_reloc_map (SOP_POP_32_S_0_5_10_16_S2), ++LOONGARCH_reloc_map (SOP_POP_32_S_0_10_10_16_S2), ++LOONGARCH_reloc_map (SOP_POP_32_U), ++LOONGARCH_reloc_map (ADD8), ++LOONGARCH_reloc_map (ADD16), ++LOONGARCH_reloc_map (ADD24), ++LOONGARCH_reloc_map (ADD32), ++LOONGARCH_reloc_map (ADD64), ++LOONGARCH_reloc_map (SUB8), ++LOONGARCH_reloc_map (SUB16), ++LOONGARCH_reloc_map (SUB24), ++LOONGARCH_reloc_map (SUB32), ++LOONGARCH_reloc_map (SUB64), ++}; ++ ++reloc_howto_type * ++loongarch_elf_rtype_to_howto (unsigned int r_type) ++{ ++ size_t i; ++ for (i = 0; i < ARRAY_SIZE (howto_table); i++) ++ if (howto_table[i].type == r_type) ++ return &howto_table[i]; ++ return NULL; ++} ++ ++reloc_howto_type * ++loongarch_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) ++{ ++ unsigned int i; ++ for (i = 0; i < ARRAY_SIZE (loong_reloc_map); i++) ++ if (loong_reloc_map[i].bfd_val == code) ++ return loongarch_elf_rtype_to_howto ((int) loong_reloc_map[i].elf_val); ++ // return &howto_table[(int) loong_reloc_map[i].elf_val]; ++ ++ return NULL; ++} ++ ++reloc_howto_type * ++loongarch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE (howto_table); i++) ++ if (howto_table[i].name && strcasecmp (howto_table[i].name, r_name) == 0) ++ return &howto_table[i]; ++ ++ return NULL; ++} +diff --git a/bfd/elfxx-loongarch.h b/bfd/elfxx-loongarch.h +new file mode 100644 +index 00000000..0e980864 +--- /dev/null ++++ b/bfd/elfxx-loongarch.h +@@ -0,0 +1,11 @@ ++#include "elf/common.h" ++#include "elf/internal.h" ++ ++extern reloc_howto_type * ++loongarch_elf_rtype_to_howto (unsigned int r_type); ++ ++extern reloc_howto_type * ++loongarch_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code); ++ ++extern reloc_howto_type * ++loongarch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name); +diff --git a/bfd/libbfd.h b/bfd/libbfd.h +index bc5ce1a4..4b1d1c63 100644 +--- a/bfd/libbfd.h ++++ b/bfd/libbfd.h +@@ -3189,6 +3189,49 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", + "BFD_RELOC_WASM32_CODE_POINTER", + "BFD_RELOC_WASM32_INDEX", + "BFD_RELOC_WASM32_PLT_SIG", ++ "BFD_RELOC_LARCH_TLS_DTPMOD32", ++ "BFD_RELOC_LARCH_TLS_DTPREL32", ++ "BFD_RELOC_LARCH_TLS_DTPMOD64", ++ "BFD_RELOC_LARCH_TLS_DTPREL64", ++ "BFD_RELOC_LARCH_TLS_TPREL32", ++ "BFD_RELOC_LARCH_TLS_TPREL64", ++ "BFD_RELOC_LARCH_MARK_LA", ++ "BFD_RELOC_LARCH_MARK_PCREL", ++ "BFD_RELOC_LARCH_SOP_PUSH_PCREL", ++ "BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE", ++ "BFD_RELOC_LARCH_SOP_PUSH_DUP", ++ "BFD_RELOC_LARCH_SOP_PUSH_GPREL", ++ "BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL", ++ "BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT", ++ "BFD_RELOC_LARCH_SOP_PUSH_TLS_GD", ++ "BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL", ++ "BFD_RELOC_LARCH_SOP_ASSERT", ++ "BFD_RELOC_LARCH_SOP_NOT", ++ "BFD_RELOC_LARCH_SOP_SUB", ++ "BFD_RELOC_LARCH_SOP_SL", ++ "BFD_RELOC_LARCH_SOP_SR", ++ "BFD_RELOC_LARCH_SOP_ADD", ++ "BFD_RELOC_LARCH_SOP_AND", ++ "BFD_RELOC_LARCH_SOP_IF_ELSE", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_10_5", ++ "BFD_RELOC_LARCH_SOP_POP_32_U_10_12", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_10_12", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_10_16", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_5_20", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2", ++ "BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2", ++ "BFD_RELOC_LARCH_SOP_POP_32_U", ++ "BFD_RELOC_LARCH_ADD8", ++ "BFD_RELOC_LARCH_ADD16", ++ "BFD_RELOC_LARCH_ADD24", ++ "BFD_RELOC_LARCH_ADD32", ++ "BFD_RELOC_LARCH_ADD64", ++ "BFD_RELOC_LARCH_SUB8", ++ "BFD_RELOC_LARCH_SUB16", ++ "BFD_RELOC_LARCH_SUB24", ++ "BFD_RELOC_LARCH_SUB32", ++ "BFD_RELOC_LARCH_SUB64", + "@@overflow: BFD_RELOC_UNUSED@@", + }; + #endif +diff --git a/bfd/reloc.c b/bfd/reloc.c +index 074223f5..6a923a61 100644 +--- a/bfd/reloc.c ++++ b/bfd/reloc.c +@@ -7939,6 +7939,95 @@ ENUMX + ENUMDOC + WebAssembly relocations. + ++ENUM ++ BFD_RELOC_LARCH_TLS_DTPMOD32 ++ENUMX ++ BFD_RELOC_LARCH_TLS_DTPREL32 ++ENUMX ++ BFD_RELOC_LARCH_TLS_DTPMOD64 ++ENUMX ++ BFD_RELOC_LARCH_TLS_DTPREL64 ++ENUMX ++ BFD_RELOC_LARCH_TLS_TPREL32 ++ENUMX ++ BFD_RELOC_LARCH_TLS_TPREL64 ++ENUMX ++ BFD_RELOC_LARCH_MARK_LA ++ENUMX ++ BFD_RELOC_LARCH_MARK_PCREL ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_PCREL ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_DUP ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_GPREL ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_TLS_GD ++ENUMX ++ BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL ++ENUMX ++ BFD_RELOC_LARCH_SOP_ASSERT ++ENUMX ++ BFD_RELOC_LARCH_SOP_NOT ++ENUMX ++ BFD_RELOC_LARCH_SOP_SUB ++ENUMX ++ BFD_RELOC_LARCH_SOP_SL ++ENUMX ++ BFD_RELOC_LARCH_SOP_SR ++ENUMX ++ BFD_RELOC_LARCH_SOP_ADD ++ENUMX ++ BFD_RELOC_LARCH_SOP_AND ++ENUMX ++ BFD_RELOC_LARCH_SOP_IF_ELSE ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_5 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_U_10_12 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_12 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_16 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_5_20 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2 ++ENUMX ++ BFD_RELOC_LARCH_SOP_POP_32_U ++ENUMX ++ BFD_RELOC_LARCH_ADD8 ++ENUMX ++ BFD_RELOC_LARCH_ADD16 ++ENUMX ++ BFD_RELOC_LARCH_ADD24 ++ENUMX ++ BFD_RELOC_LARCH_ADD32 ++ENUMX ++ BFD_RELOC_LARCH_ADD64 ++ENUMX ++ BFD_RELOC_LARCH_SUB8 ++ENUMX ++ BFD_RELOC_LARCH_SUB16 ++ENUMX ++ BFD_RELOC_LARCH_SUB24 ++ENUMX ++ BFD_RELOC_LARCH_SUB32 ++ENUMX ++ BFD_RELOC_LARCH_SUB64 ++ENUMDOC ++ LoongISA relocations. ++ + ENDSENUM + BFD_RELOC_UNUSED + CODE_FRAGMENT +diff --git a/bfd/targets.c b/bfd/targets.c +index 02c6de4f..ef678ee3 100644 +--- a/bfd/targets.c ++++ b/bfd/targets.c +@@ -710,6 +710,8 @@ extern const bfd_target l1om_elf64_vec; + extern const bfd_target l1om_elf64_fbsd_vec; + extern const bfd_target lm32_elf32_vec; + extern const bfd_target lm32_elf32_fdpic_vec; ++extern const bfd_target loongarch_elf32_vec; ++extern const bfd_target loongarch_elf64_vec; + extern const bfd_target m32c_elf32_vec; + extern const bfd_target m32r_elf32_vec; + extern const bfd_target m32r_elf32_le_vec; +@@ -1166,6 +1168,11 @@ static const bfd_target * const _bfd_target_vector[] = + + &lm32_elf32_vec, + ++#ifdef BFD64 ++ &loongarch_elf32_vec, ++ &loongarch_elf64_vec, ++#endif ++ + &m32c_elf32_vec, + + &m32r_elf32_vec, +diff --git a/binutils/readelf.c b/binutils/readelf.c +index 212b9810..f3e989c8 100644 +--- a/binutils/readelf.c ++++ b/binutils/readelf.c +@@ -114,6 +114,7 @@ + #include "elf/ia64.h" + #include "elf/ip2k.h" + #include "elf/lm32.h" ++#include "elf/loongarch.h" + #include "elf/iq2000.h" + #include "elf/m32c.h" + #include "elf/m32r.h" +@@ -990,6 +991,7 @@ guess_is_rela (unsigned int e_machine) + case EM_MICROBLAZE: + case EM_MICROBLAZE_OLD: + case EM_WEBASSEMBLY: ++ case EM_LOONGARCH: + return TRUE; + + case EM_68HC05: +@@ -1701,6 +1703,10 @@ dump_relocations (Filedata * filedata, + else + rtype = elf_nfp_reloc_type (type); + break; ++ ++ case EM_LOONGARCH: ++ rtype = elf_loongarch_reloc_type (type); ++ break; + } + + if (rtype == NULL) +@@ -2643,6 +2649,7 @@ get_machine_name (unsigned e_machine) + case EM_CYGNUS_MEP: return "Toshiba MeP Media Engine"; + case EM_ADAPTEVA_EPIPHANY: return "Adapteva EPIPHANY"; + case EM_CYGNUS_FRV: return "Fujitsu FR-V"; ++ case EM_LOONGARCH: return "LoongArch"; + + default: + snprintf (buff, sizeof (buff), _(": 0x%x"), e_machine); +@@ -3850,6 +3857,16 @@ get_machine_flags (Filedata * filedata, unsigned e_flags, unsigned e_machine) + + if (e_flags & ~ EF_MSP430_MACH) + strcat (buf, _(": unknown extra flag bits also present")); ++ break; ++ ++ case EM_LOONGARCH: ++ switch (e_flags & EF_LARCH_ABI) ++ { ++ case EF_LARCH_ABI_LP64: strcat (buf, ", LP64"); break; ++ case EF_LARCH_ABI_XLP32: strcat (buf, ", XLP32"); break; ++ case EF_LARCH_ABI_LP32: strcat (buf, ", LP32"); break; ++ } ++ break; + } + } + +@@ -12502,6 +12519,8 @@ is_32bit_abs_reloc (Filedata * filedata, unsigned int reloc_type) + return reloc_type == 2; /* R_IQ2000_32. */ + case EM_LATTICEMICO32: + return reloc_type == 3; /* R_LM32_32. */ ++ case EM_LOONGARCH: ++ return reloc_type == 1; /* R_LARCH_32. */ + case EM_M32C_OLD: + case EM_M32C: + return reloc_type == 3; /* R_M32C_32. */ +@@ -12710,6 +12729,8 @@ is_64bit_abs_reloc (Filedata * filedata, unsigned int reloc_type) + case EM_IA_64: + return (reloc_type == 0x26 /* R_IA64_DIR64MSB. */ + || reloc_type == 0x27 /* R_IA64_DIR64LSB. */); ++ case EM_LOONGARCH: ++ return reloc_type == 2; /* R_LARCH_64 */ + case EM_PARISC: + return reloc_type == 80; /* R_PARISC_DIR64. */ + case EM_PPC64: +@@ -13034,6 +13055,7 @@ is_none_reloc (Filedata * filedata, unsigned int reloc_type) + case EM_X86_64: /* R_X86_64_NONE. */ + case EM_XC16X: + case EM_WEBASSEMBLY: /* R_WASM32_NONE. */ ++ case EM_LOONGARCH: /* R_LARCH_NONE */ + return reloc_type == 0; + + case EM_AARCH64: +@@ -16852,6 +16874,14 @@ get_note_type (Filedata * filedata, unsigned e_type) + return _("NT_ARM_HW_BREAK (AArch hardware breakpoint registers)"); + case NT_ARM_HW_WATCH: + return _("NT_ARM_HW_WATCH (AArch hardware watchpoint registers)"); ++ case NT_LARCH_CPUCFG: ++ return _("NT_LARCH_CPUCFG (Loongarch CPU config registers)"); ++ case NT_LARCH_LBT: ++ return _("NT_LARCH_LBT (Loongarch Loongson Binary Translation registers)"); ++ case NT_LARCH_LSX: ++ return _("NT_LARCH_LSX (Loongarch Loongson SIMD Extension registers)"); ++ case NT_LARCH_LASX: ++ return _("NT_LARCH_LASX (Loongarch Loongson Advanced SIMD Extension registers)"); + case NT_PSTATUS: + return _("NT_PSTATUS (pstatus structure)"); + case NT_FPREGS: +diff --git a/binutils/testsuite/binutils-all/objdump.exp b/binutils/testsuite/binutils-all/objdump.exp +index f006d64f..9c07394f 100644 +--- a/binutils/testsuite/binutils-all/objdump.exp ++++ b/binutils/testsuite/binutils-all/objdump.exp +@@ -34,8 +34,8 @@ send_user "Version [binutil_version $OBJDUMP]" + set got [binutils_run $OBJDUMP "$OBJDUMPFLAGS -i"] + + set cpus_expected [list] +-lappend cpus_expected aarch64 alpha am33-2 arc ARC700 ARCv2 arm cris +-lappend cpus_expected d10v d30v fr30 fr500 fr550 h8 hppa i386 i860 i960 iamcu ip2022 ++lappend cpus_expected aarch64 alpha am33-2 arc ARC700 ARCv2 arm cris d10v d30v ++lappend cpus_expected fr30 fr500 fr550 h8 hppa i386 i860 i960 iamcu ip2022 Loongarch64 + lappend cpus_expected m16c m32c m32r m68hc11 m68hc12 m68k m88k MCore mep c5 h1 MicroBlaze + lappend cpus_expected mips mn10200 mn10300 ms1 msp MSP430 nds32 n1h_v3 ns32k + lappend cpus_expected or1k or1knd pj powerpc pyramid riscv romp rs6000 s390 sh sparc +diff --git a/gas/Makefile.am b/gas/Makefile.am +index 758f41dc..3ae74dab 100644 +--- a/gas/Makefile.am ++++ b/gas/Makefile.am +@@ -156,6 +156,7 @@ TARGET_CPU_CFILES = \ + config/tc-ip2k.c \ + config/tc-iq2000.c \ + config/tc-lm32.c \ ++ config/tc-loongarch.c \ + config/tc-m32c.c \ + config/tc-m32r.c \ + config/tc-m68hc11.c \ +@@ -233,6 +234,7 @@ TARGET_CPU_HFILES = \ + config/tc-ip2k.h \ + config/tc-iq2000.h \ + config/tc-lm32.h \ ++ config/tc-loongarch.h \ + config/tc-m32c.h \ + config/tc-m32r.h \ + config/tc-m68hc11.h \ +@@ -380,7 +382,8 @@ EXTRA_SCRIPTS = .gdbinit + EXTRA_DIST = m68k-parse.c itbl-parse.c itbl-parse.h itbl-lex.c \ + bfin-parse.c bfin-parse.h bfin-lex.c \ + rl78-parse.c rl78-parse.h \ +- rx-parse.c rx-parse.h ++ rx-parse.c rx-parse.h \ ++ loongarch-parse.c loongarch-parse.h loongarch-lex.c + + diststuff: $(EXTRA_DIST) info + +@@ -521,6 +524,27 @@ rx-parse.h: rx-parse.c + rx-defs.h: ; @true + $(srcdir)/config/rx-defs.h: ; @true + ++EXTRA_as_new_SOURCES += config/loongarch-parse.y ++loongarch-parse.c: $(srcdir)/config/loongarch-parse.y ++ $(SHELL) $(YLWRAP) $(srcdir)/config/loongarch-parse.y y.tab.c loongarch-parse.c y.tab.h loongarch-parse.h -- $(YACCCOMPILE) -d ; ++loongarch-parse.h: loongarch-parse.c ++loongarch-parse.h: ; @true ++$(srcdir)/config/loongarch-parse.h: ; @true ++ ++loongarch-lex.c: $(srcdir)/config/loongarch-lex.l ++ $(SHELL) $(YLWRAP) $(srcdir)/config/loongarch-lex.l lex.yy.c loongarch-lex.c -- $(LEXCOMPILE) ++loongarch-lex-wrapper.@OBJEXT@: $(srcdir)/config/loongarch-lex-wrapper.c loongarch-lex.c loongarch-parse.h ++if am__fastdepCC ++ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(srcdir)/config/loongarch-lex-wrapper.c $(NO_WERROR) ++ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++else ++if AMDEP ++ source='loongarch-lex-wrapper.c' object='$@' libtool=no @AMDEPBACKSLASH@ ++ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++endif ++ $(COMPILE) -c $(srcdir)/config/loongarch-lex-wrapper.c $(NO_WERROR) ++endif ++ + # The instruction table specification lexical analyzer and parser. + + # Disable -Werror, if it has been enabled, since old versions of bison/ +diff --git a/gas/config/loongarch-lex-wrapper.c b/gas/config/loongarch-lex-wrapper.c +new file mode 100644 +index 00000000..8ddf1dd2 +--- /dev/null ++++ b/gas/config/loongarch-lex-wrapper.c +@@ -0,0 +1,25 @@ ++/* Copyright (C) 2012-2018 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 3, or (at your option) ++ any later version. ++ ++ GAS is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with GAS; see the file COPYING. If not, write to the Free ++ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA ++ 02110-1301, USA. */ ++ ++/* The C source file generated by flex includes stdio.h before any of ++ the C code in bfin-lex.l. Make sure we include sysdep.h first, so ++ that config.h can set the correct values for various things. */ ++ ++#include "sysdep.h" ++#include "loongarch-lex.c" +diff --git a/gas/config/loongarch-lex.l b/gas/config/loongarch-lex.l +new file mode 100644 +index 00000000..d88fc93a +--- /dev/null ++++ b/gas/config/loongarch-lex.l +@@ -0,0 +1,39 @@ ++%option noyywrap ++%{ ++#include "as.h" ++#include "loongarch-parse.h" ++%} ++ ++D [0-9] ++/* We consider anything greater than \x7f to be a "letter" for UTF-8 ++ support. See the lex_type array in ../read.c. */ ++L [a-zA-Z_\.\$\x80-\xff] ++H [0-9A-Fa-f] ++ ++hex 0[xX]{H}+ ++oct 0[0-7]+ ++bin 0[bB][01]+ ++dec ([1-9]{D}*)|0 ++id ({D}+[fb])|({L}({D}|{L})*)|(:{dec}[bf]) ++ws [ \t\v\f]+ ++ ++%% ++ ++{dec} { yylval.imm = strtoull (yytext, 0, 0); return INTEGER; } ++{hex} { yylval.imm = strtoull (yytext + 2, 0, 16); return INTEGER; } ++{bin} { yylval.imm = strtoull (yytext + 2, 0, 2); return INTEGER; } ++{oct} { yylval.imm = strtoull (yytext + 1, 0, 8); return INTEGER; } ++{id} { yylval.c_str = strdup (yytext);return IDENTIFIER; } ++{ws} { } ++ ++">>" { return RIGHT_OP; } ++"<<" { return LEFT_OP; } ++"&&" { return AND_OP; } ++"||" { return OR_OP; } ++"<=" { return LE_OP; } ++">=" { return GE_OP; } ++"==" { return EQ_OP; } ++"!=" { return NE_OP; } ++. { return yytext[0];} ++ ++%% +diff --git a/gas/config/loongarch-parse.y b/gas/config/loongarch-parse.y +new file mode 100644 +index 00000000..aa7e51f5 +--- /dev/null ++++ b/gas/config/loongarch-parse.y +@@ -0,0 +1,420 @@ ++%{ ++#include "as.h" ++#include "loongarch-parse.h" ++void yyerror(const char *s) {} ++extern int yylex (void); ++extern void yy_scan_string (const char *); ++extern void ++get_internal_label (expressionS *label_expr, ++ unsigned long label, ++ int augend); ++ ++ ++static struct reloc_info *top, *end; ++ ++static expressionS const_0 = ++{ ++ .X_op = O_constant, ++ .X_add_number = 0 ++}; ++ ++static int ++is_const (struct reloc_info *info) ++{ ++ return info->type == BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE ++ && info->value.X_op == O_constant; ++} ++ ++int ++loongarch_parse_expr (const char *expr, ++ struct reloc_info *reloc_stack_top, ++ size_t max_reloc_num, ++ size_t *reloc_num, ++ offsetT *imm) ++{ ++ int ret; ++ top = reloc_stack_top; ++ end = top + max_reloc_num; ++ yy_scan_string (expr); ++ ret = yyparse (); ++ if (ret == 0) ++ { ++ if (is_const (top - 1)) ++ *imm = (--top)->value.X_add_number; ++ else ++ *imm = 0; ++ *reloc_num = top - reloc_stack_top; ++ } ++ return ret; ++} ++ ++static void ++emit_const (offsetT imm) ++{ ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE; ++ top->value.X_op = O_constant; ++ top->value.X_add_number = imm; ++ top++; ++} ++ ++static const char * ++my_getExpression (expressionS *ep, const char *str) ++{ ++ char *save_in, *ret; ++ if (*str == ':') ++ { ++ unsigned long j; ++ char *str_1 = (char *) str; ++ str_1++; ++ j = strtol (str_1, &str_1, 10); ++ get_internal_label (ep, j, *str_1 == 'f'); ++ return NULL; ++ } ++ save_in = input_line_pointer; ++ input_line_pointer = (char *)str; ++ expression (ep); ++ ret = input_line_pointer; ++ input_line_pointer = save_in; ++ return ret; ++} ++ ++static void ++reloc (const char *op_c_str, const char *id_c_str, offsetT addend) ++{ ++ expressionS id_sym_expr; ++ ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ ++ if (id_c_str) ++ { ++ my_getExpression (&id_sym_expr, id_c_str); ++ id_sym_expr.X_add_number += addend; ++ } ++ else ++ { ++ id_sym_expr.X_op = O_constant; ++ id_sym_expr.X_add_number = addend; ++ } ++ ++ if (strcmp (op_c_str, "abs") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE; ++ top++; ++ } ++ else if (strcmp (op_c_str, "pcrel") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_PCREL; ++ top++; ++ } ++ else if (strcmp (op_c_str, "gprel") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_GPREL; ++ top++; ++ } ++ else if (strcmp (op_c_str, "tprel") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL; ++ top++; ++ } ++ else if (strcmp (op_c_str, "tlsgot") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT; ++ top++; ++ } ++ else if (strcmp (op_c_str, "tlsgd") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GD; ++ top++; ++ } ++ else if (strcmp (op_c_str, "plt") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL; ++ top++; ++ } ++ else ++ as_fatal (_("unknown reloc hint: %s"), op_c_str); ++} ++ ++static void ++emit_unary (char op) ++{ ++ struct reloc_info *s_top = top - 1; ++ if (is_const (s_top)) ++ { ++ offsetT opr = s_top->value.X_add_number; ++ switch (op) ++ { ++ case '+': ++ break; ++ case '-': ++ opr = -opr; ++ break; ++ case '~': ++ opr = ~opr; ++ break; ++ case '!': ++ opr = !opr; ++ break; ++ default: ++ abort (); ++ } ++ s_top->value.X_add_number = opr; ++ } ++ else ++ { ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ switch (op) ++ { ++ case '!': ++ top->type = BFD_RELOC_LARCH_SOP_NOT; ++ default: ++ abort (); ++ } ++ top->value = const_0; ++ top++; ++ } ++} ++ ++static void ++emit_bin (int op) ++{ ++ struct reloc_info *last_1st = top - 1, *last_2nd = top - 2; ++ if (is_const (last_1st) && is_const (last_2nd)) ++ { ++ offsetT opr1 = last_2nd->value.X_add_number; ++ offsetT opr2 = last_1st->value.X_add_number; ++ switch (op) ++ { ++ case '*': ++ opr1 = opr1 * opr2; ++ break; ++ case '/': ++ opr1 = opr1 / opr2; ++ break; ++ case '%': ++ opr1 = opr1 % opr2; ++ break; ++ case '+': ++ opr1 = opr1 + opr2; ++ break; ++ case '-': ++ opr1 = opr1 - opr2; ++ break; ++ case LEFT_OP: ++ opr1 = opr1 << opr2; ++ break; ++ case RIGHT_OP: ++ /* Algorithm right shift */ ++ opr1 = (offsetT)opr1 >> (offsetT)opr2; ++ break; ++ case '<': ++ opr1 = opr1 < opr2; ++ break; ++ case '>': ++ opr1 = opr1 > opr2; ++ break; ++ case LE_OP: ++ opr1 = opr1 <= opr2; ++ break; ++ case GE_OP: ++ opr1 = opr1 >= opr2; ++ break; ++ case EQ_OP: ++ opr1 = opr1 == opr2; ++ break; ++ case NE_OP: ++ opr1 = opr1 != opr2; ++ break; ++ case '&': ++ opr1 = opr1 & opr2; ++ break; ++ case '^': ++ opr1 = opr1 ^ opr2; ++ break; ++ case '|': ++ opr1 = opr1 | opr2; ++ break; ++ case AND_OP: ++ opr1 = opr1 && opr2; ++ break; ++ case OR_OP: ++ opr1 = opr1 || opr2; ++ break; ++ default: ++ abort (); ++ } ++ last_2nd->value.X_add_number = opr1; ++ last_1st->type = 0; ++ top--; ++ } ++ else ++ { ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ switch (op) ++ { ++ case '+': ++ top->type = BFD_RELOC_LARCH_SOP_ADD; ++ break; ++ case '-': ++ top->type = BFD_RELOC_LARCH_SOP_SUB; ++ break; ++ case LEFT_OP: ++ top->type = BFD_RELOC_LARCH_SOP_SL; ++ break; ++ case RIGHT_OP: ++ top->type = BFD_RELOC_LARCH_SOP_SR; ++ break; ++ case '&': ++ top->type = BFD_RELOC_LARCH_SOP_AND; ++ break; ++ default: ++ abort (); ++ } ++ top->value = const_0; ++ top++; ++ } ++} ++ ++static void ++emit_if_else (void) ++{ ++ struct reloc_info *last_1st = top - 1; ++ struct reloc_info *last_2nd = top - 2; ++ struct reloc_info *last_3rd = top - 3; ++ if (is_const (last_1st) && is_const (last_2nd) && is_const (last_3rd)) ++ { ++ offsetT opr1 = last_3rd->value.X_add_number; ++ offsetT opr2 = last_2nd->value.X_add_number; ++ offsetT opr3 = last_1st->value.X_add_number; ++ opr1 = opr1 ? opr2 : opr3; ++ last_3rd->value.X_add_number = opr1; ++ last_2nd->type = 0; ++ last_1st->type = 0; ++ top -= 2; ++ } ++ else ++ { ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ top->type = BFD_RELOC_LARCH_SOP_IF_ELSE; ++ top->value = const_0; ++ top++; ++ } ++} ++ ++%} ++ ++%union { ++char *c_str; ++offsetT imm; ++} ++ ++%token INTEGER ++%token IDENTIFIER ++%type addend ++ ++%token LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP AND_OP OR_OP ++%start expression ++%% ++ ++primary_expression ++ : INTEGER {emit_const ($1);} ++ | '(' expression ')' ++ | '%' IDENTIFIER '(' IDENTIFIER addend ')' {reloc ($2, $4, $5); free ($2); free ($4);} ++ | '%' IDENTIFIER '(' INTEGER addend ')' {reloc ($2, NULL, $4 + $5); free ($2);} ++ ; ++ ++addend ++ : addend '-' INTEGER {$$ -= $3;} ++ | addend '+' INTEGER {$$ += $3;} ++ | {$$ = 0;} ++ ; ++ ++unary_expression ++ : primary_expression ++ | '+' unary_expression {emit_unary ('+');} ++ | '-' unary_expression {emit_unary ('-');} ++ | '~' unary_expression {emit_unary ('~');} ++ | '!' unary_expression {emit_unary ('!');} ++ ; ++ ++multiplicative_expression ++ : unary_expression ++ | multiplicative_expression '*' unary_expression {emit_bin ('*');} ++ | multiplicative_expression '/' unary_expression {emit_bin ('/');} ++ | multiplicative_expression '%' unary_expression {emit_bin ('%');} ++ ; ++ ++additive_expression ++ : multiplicative_expression ++ | additive_expression '+' multiplicative_expression {emit_bin ('+');} ++ | additive_expression '-' multiplicative_expression {emit_bin ('-');} ++ ; ++ ++shift_expression ++ : additive_expression ++ | shift_expression LEFT_OP additive_expression {emit_bin (LEFT_OP);} ++ | shift_expression RIGHT_OP additive_expression {emit_bin (RIGHT_OP);} ++ ; ++ ++relational_expression ++ : shift_expression ++ | relational_expression '<' shift_expression {emit_bin ('<');} ++ | relational_expression '>' shift_expression {emit_bin ('>');} ++ | relational_expression LE_OP shift_expression {emit_bin (LE_OP);} ++ | relational_expression GE_OP shift_expression {emit_bin (GE_OP);} ++ ; ++ ++equality_expression ++ : relational_expression ++ | equality_expression EQ_OP relational_expression {emit_bin (EQ_OP);} ++ | equality_expression NE_OP relational_expression {emit_bin (NE_OP);} ++ ; ++ ++and_expression ++ : equality_expression ++ | and_expression '&' equality_expression {emit_bin ('&');} ++ ; ++ ++exclusive_or_expression ++ : and_expression ++ | exclusive_or_expression '^' and_expression {emit_bin ('^');} ++ ; ++ ++inclusive_or_expression ++ : exclusive_or_expression ++ | inclusive_or_expression '|' exclusive_or_expression {emit_bin ('|');} ++ ; ++ ++logical_and_expression ++ : inclusive_or_expression ++ | logical_and_expression AND_OP inclusive_or_expression {emit_bin (AND_OP);} ++ ; ++ ++logical_or_expression ++ : logical_and_expression ++ | logical_or_expression OR_OP logical_and_expression {emit_bin (OR_OP);} ++ ; ++ ++conditional_expression ++ : logical_or_expression ++ | logical_or_expression '?' expression ':' conditional_expression {emit_if_else ();} ++ ; ++ ++expression ++ : conditional_expression ++ ; ++%% ++ +diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c +new file mode 100644 +index 00000000..9c4a9102 +--- /dev/null ++++ b/gas/config/tc-loongarch.c +@@ -0,0 +1,1328 @@ ++#include "as.h" ++#include "dw2gencfi.h" ++#include "tc-loongarch.h" ++#include "elf/loongarch.h" ++#include "opcode/loongarch.h" ++#include "obj-elf.h" ++#include ++#include ++#include ++#include ++ ++/* All information about an instruction during assemble */ ++struct loongarch_cl_insn ++{ ++ /* First split string */ ++ const char *name; ++ const char *arg_strs[MAX_ARG_NUM_PLUS_2]; ++ size_t arg_num; ++ ++ /* Second analyze name_str and each actual args string to match the insn ++ in 'loongarch-opc.c'. And actual args may need be relocated. ++ We get length of insn. If 'insn_length == 0 && insn_mo->macro != NULL', ++ it's a macro insntruction and we call 'md_assemble' recursively ++ after expanding it. */ ++ int match_now; ++ int all_match; ++ ++ const struct loongarch_opcode *insn; ++ size_t insn_length; ++ ++ offsetT args[MAX_ARG_NUM_PLUS_2]; ++ struct reloc_info reloc_info[MAX_RELOC_NUMBER_A_INSN]; ++ size_t reloc_num; ++ ++ /* For relax reserved. We not support relax now. ++ 'insn_length < relax_max_length' means need to relax. ++ And 'insn_length == relax_max_length' means no need to relax. */ ++ size_t relax_max_length; ++ relax_substateT subtype; ++ ++ /* Then we get the binary representation of insn ++ and write it in to section. */ ++ insn_t insn_bin; ++ ++ /* The frag that contains the instruction. */ ++ struct frag *frag; ++ /* The offset into FRAG of the first instruction byte. */ ++ long where; ++ /* The relocs associated with the instruction, if any. */ ++ fixS *fixp[MAX_RELOC_NUMBER_A_INSN]; ++}; ++ ++/* This array holds the chars that always start a comment. If the ++ pre-processor is disabled, these aren't very useful */ ++const char comment_chars[] = "#"; ++ ++/* This array holds the chars that only start a comment at the beginning of ++ a line. If the line seems to have the form '# 123 filename' ++ .line and .file directives will appear in the pre-processed output */ ++/* Note that input_file.c hand checks for '#' at the beginning of the ++ first line of the input file. This is because the compiler outputs ++#NO_APP at the beginning of its output. */ ++/* Also note that C style comments are always supported. */ ++const char line_comment_chars[] = "#"; ++ ++/* This array holds machine specific line separator characters. */ ++const char line_separator_chars[] = ";"; ++ ++/* Chars that can be used to separate mant from exp in floating point nums */ ++const char EXP_CHARS[] = "eE"; ++ ++/* Chars that mean this number is a floating point constant */ ++/* As in 0f12.456 */ ++/* or 0d1.2345e12 */ ++const char FLT_CHARS[] = "rRsSfFdDxXpP"; ++ ++const char *md_shortopts = "O::g::G:"; ++ ++enum options ++{ ++ OPTION_IGNORE = OPTION_MD_BASE, ++ ++ OPTION_SOFT_FLOAT, ++ OPTION_HARD_FLOAT, ++ OPTION_ABI, ++ ++ OPTION_LA_LOCAL_WITH_ABS, ++ OPTION_LA_GLOBAL_WITH_PCREL, ++ OPTION_LA_GLOBAL_WITH_ABS, ++ ++ OPTION_END_OF_ENUM, ++}; ++ ++struct option md_longopts[] = ++{ ++ {"msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT}, ++ {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT}, ++ {"mabi", required_argument, NULL, OPTION_ABI}, ++ ++ {"mla-local-with-abs", no_argument, NULL, OPTION_LA_LOCAL_WITH_ABS}, ++ {"mla-global-with-pcrel", no_argument, NULL, OPTION_LA_GLOBAL_WITH_PCREL}, ++ {"mla-global-with-abs", no_argument, NULL, OPTION_LA_GLOBAL_WITH_ABS}, ++ ++ {NULL, no_argument, NULL, 0} ++}; ++ ++size_t md_longopts_size = sizeof (md_longopts); ++ ++int ++md_parse_option (int c, const char *arg) ++{ ++ int ret = 1; ++ switch (c) ++ { ++ case OPTION_SOFT_FLOAT: ++ LARCH_opts.ase_float = 0; ++ break; ++ case OPTION_HARD_FLOAT: ++ LARCH_opts.ase_float = 1; ++ break; ++ case OPTION_ABI: ++ if (strcasecmp (arg, "lp64") == 0) ++ LARCH_opts.abi_is_lp64 = 1; ++ else if (strcasecmp (arg, "lp32") == 0) ++ LARCH_opts.abi_is_lp32 = 1; ++ else ++ ret = 0; ++ break; ++ case OPTION_LA_LOCAL_WITH_ABS: ++ LARCH_opts.la_local_with_abs = 1; ++ break; ++ case OPTION_LA_GLOBAL_WITH_PCREL: ++ LARCH_opts.la_global_with_pcrel = 1; ++ break; ++ case OPTION_LA_GLOBAL_WITH_ABS: ++ LARCH_opts.la_global_with_abs = 1; ++ break; ++ case OPTION_IGNORE: ++ break; ++ } ++ return ret; ++} ++ ++static struct hash_control *r_htab = NULL; ++static struct hash_control *f_htab = NULL; ++static struct hash_control *c_htab = NULL; ++static struct hash_control *cr_htab = NULL; ++static struct hash_control *v_htab = NULL; ++static struct hash_control *x_htab = NULL; ++ ++void ++loongarch_after_parse_args () ++{ ++ size_t i; ++ ++ LARCH_opts.ase_test = 1; ++ LARCH_opts.ase_fix = 1; ++ LARCH_opts.ase_float = 1; ++ LARCH_opts.ase_128vec = 1; ++ LARCH_opts.ase_256vec = 1; ++ ++ if (!r_htab) ++ r_htab = hash_new (), hash_insert (r_htab, "", 0); ++ if (!f_htab) ++ f_htab = hash_new (), hash_insert (f_htab, "", 0); ++ if (!c_htab) ++ c_htab = hash_new (), hash_insert (c_htab, "", 0); ++ if (!cr_htab) ++ cr_htab = hash_new (), hash_insert (cr_htab, "", 0); ++ if (!v_htab) ++ v_htab = hash_new (), hash_insert (v_htab, "", 0); ++ if (!x_htab) ++ x_htab = hash_new (), hash_insert (x_htab, "", 0); ++ ++ for (i = 0; i < ARRAY_SIZE (loongarch_r_normal_name); i++) ++ hash_insert (r_htab, loongarch_r_normal_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_f_normal_name); i++) ++ hash_insert (f_htab, loongarch_f_normal_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_c_normal_name); i++) ++ hash_insert (c_htab, loongarch_c_normal_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_cr_normal_name); i++) ++ hash_insert (cr_htab, loongarch_cr_normal_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_v_normal_name); i++) ++ hash_insert (v_htab, loongarch_v_normal_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_x_normal_name); i++) ++ hash_insert (x_htab, loongarch_x_normal_name[i], (void *) (i + 1)); ++ ++ if (LARCH_opts.abi_is_lp64 ++ + LARCH_opts.abi_is_lp32 ++ == 0) ++ { ++ // as_warn (_("default LoongISA ABI is lp64")); ++ LARCH_opts.abi_is_lp64 = 1; ++ } ++ ++ if (1 < LARCH_opts.abi_is_lp64 ++ + LARCH_opts.abi_is_lp32) ++ as_fatal (_("we can specify only ONE abi")); ++ ++ if (LARCH_opts.abi_is_lp64) ++ { ++ LARCH_opts.addrwidth_is_64 = 1; ++ LARCH_opts.rlen_is_64 = 1; ++ for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name); i++) ++ hash_insert (r_htab, loongarch_r_lp64_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name1); i++) ++ hash_insert (r_htab, loongarch_r_lp64_name1[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_f_lp64_name); i++) ++ hash_insert (f_htab, loongarch_f_lp64_name[i], (void *) (i + 1)); ++ for (i = 0; i < ARRAY_SIZE (loongarch_f_lp64_name1); i++) ++ hash_insert (f_htab, loongarch_f_lp64_name1[i], (void *) (i + 1)); ++ } ++ ++ if (LARCH_opts.abi_is_lp32) ++ { ++ LARCH_opts.addrwidth_is_32 = 1; ++ LARCH_opts.rlen_is_32 = 1; ++ } ++ ++} ++ ++const char * ++loongarch_target_format () ++{ ++ return LARCH_opts.addrwidth_is_32? "elf32-loongarch" : "elf64-loongarch"; ++} ++ ++void ++md_begin () ++{ ++ if (LARCH_opts.ase_test) ++ { ++ const struct loongarch_opcode *it; ++ struct loongarch_ase *ase; ++ for (ase = loongarch_ASEs; ase->enabled; ase++) ++ for (it = ase->opcodes; it->name; it++) ++ { ++ if (loongarch_check_format (it->format) != 0) ++ as_fatal (_("insn name: %s\tformat: %s\tsyntax error"), ++ it->name, it->format); ++ if (it->mask == 0 && it->macro == 0) ++ as_fatal (_("insn name: %s\nformat: %s\nwe want macro but macro is NULL"), ++ it->name, it->format); ++ if (it->macro && loongarch_check_macro (it->format, it->macro) != 0) ++ as_fatal (_("insn name: %s\nformat: %s\nmacro: %s\tsyntax error"), ++ it->name, it->format, it->macro); ++ } ++ } ++ ++ /* FIXME: expressionS use 'offsetT' as constant, we want this is 64-bit type */ ++ assert (8 <= sizeof (offsetT)); ++} ++ ++void ++md_operand (expressionS *e) ++{ ++ /* Because we use 'expression' to check if a actual arg is a expr at first. ++ If not, we want a returning. */ ++ if (e->X_op == O_absent) ++ e->X_op = O_illegal; ++} ++ ++static const expressionS const_0 = {.X_op = O_constant, .X_add_number = 0}; ++ ++static const char * ++my_getExpression (expressionS *ep, const char *str) ++{ ++ char *save_in, *ret; ++ save_in = input_line_pointer; ++ input_line_pointer = (char *)str; ++ expression (ep); ++ ret = input_line_pointer; ++ input_line_pointer = save_in; ++ return ret; ++} ++ ++ ++/* for compitable with MIPS pesudo insn like '.set reorder' ++ but actually ignore them. */ ++static void ++s_loongarch_set (int x ATTRIBUTE_UNUSED) ++{ ++ char *name = input_line_pointer, ch; ++ ++ while (!is_end_of_line[(unsigned char) *input_line_pointer]) ++ ++input_line_pointer; ++ ch = *input_line_pointer; ++ *input_line_pointer = '\0'; ++ ++ if (strchr (name, ',')) ++ { ++ /* Generic ".set" directive; use the generic handler. */ ++ *input_line_pointer = ch; ++ input_line_pointer = name; ++ s_set (0); ++ return; ++ } ++ ++ *input_line_pointer = ch; ++ demand_empty_rest_of_line (); ++} ++ ++static void ++s_loongarch_align (int arg) ++{ ++ const char *t = input_line_pointer; ++ while (!is_end_of_line[(unsigned char) *t] && *t != ',') ++ ++t; ++ if (*t == ',') ++ s_align_ptwo (arg); ++ else ++ s_align_ptwo (0); ++} ++ ++/* Handle the .dtprelword and .dtpreldword pseudo-ops. They generate ++ a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for ++ use in DWARF debug information. */ ++ ++static void ++s_dtprel (int bytes) ++{ ++ expressionS ex; ++ char *p; ++ ++ expression (&ex); ++ ++ if (ex.X_op != O_symbol) ++ { ++ as_bad (_("Unsupported use of %s"), (bytes == 8 ++ ? ".dtpreldword" ++ : ".dtprelword")); ++ ignore_rest_of_line (); ++ } ++ ++ p = frag_more (bytes); ++ md_number_to_chars (p, 0, bytes); ++ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, ++ (bytes == 8 ++ ? BFD_RELOC_LARCH_TLS_DTPREL64 ++ : BFD_RELOC_LARCH_TLS_DTPREL32)); ++ ++ demand_empty_rest_of_line (); ++} ++ ++static const pseudo_typeS loongarch_pseudo_table[] = ++{ ++ {"align", s_loongarch_align, -4}, ++ {"dword", cons, 8}, ++ {"word", cons, 4}, ++ {"half", cons, 2}, ++ {"dtprelword", s_dtprel, 4}, ++ {"dtpreldword", s_dtprel, 8}, ++ { NULL, NULL, 0 }, ++}; ++ ++void ++loongarch_pop_insert (void) ++{ ++ pop_insert (loongarch_pseudo_table); ++} ++ ++ ++#define INTERNAL_LABEL_SPECIAL 10 ++static unsigned long internal_label_count[INTERNAL_LABEL_SPECIAL] = {0}; ++ ++static const char * ++loongarch_internal_label_name (unsigned long label, int augend) ++{ ++ static char symbol_name_build[24]; ++ unsigned long want_label; ++ char *p; ++ ++ want_label = internal_label_count[label] + augend; ++ ++ p = symbol_name_build; ++#ifdef LOCAL_LABEL_PREFIX ++ *p++ = LOCAL_LABEL_PREFIX; ++#endif ++ *p++ = 'L'; ++ for (; label; label /= 10) ++ *p++ = label % 10 + '0'; ++ /* make sure internal label never belong to normal label namespace */ ++ *p++ = ':'; ++ for (; want_label; want_label /= 10) ++ *p++ = want_label % 10 + '0'; ++ *p++ = '\0'; ++ return symbol_name_build; ++} ++ ++static void ++setup_internal_label_here (unsigned long label) ++{ ++ assert (label < INTERNAL_LABEL_SPECIAL); ++ internal_label_count[label]++; ++ colon (loongarch_internal_label_name (label ,0)); ++} ++ ++extern void /* no static. used by 'loongarch-parse.y' */ ++get_internal_label (expressionS *label_expr, ++ unsigned long label, ++ int augend/* 0 for previous, 1 for next */); ++ ++void ++get_internal_label (expressionS *label_expr, ++ unsigned long label, ++ int augend/* 0 for previous, 1 for next */) ++{ ++ assert (label < INTERNAL_LABEL_SPECIAL); ++ if (augend == 0 && internal_label_count[label] == 0) ++ as_fatal (_("internal error: we have no internal label yet")); ++ label_expr->X_op = O_symbol; ++ label_expr->X_add_symbol = ++ symbol_find_or_make (loongarch_internal_label_name (label, augend)); ++ label_expr->X_add_number = 0; ++} ++ ++extern int loongarch_parse_expr (const char *expr, ++ struct reloc_info *reloc_stack_top, ++ size_t max_reloc_num, size_t *reloc_num, ++ offsetT *imm_if_no_reloc); ++ ++int ++is_internal_label (const char *c_str) ++{ ++ do ++ { ++ if (*c_str != ':') ++ break; ++ c_str++; ++ if (!('0' <= *c_str && *c_str <= '9')) ++ break; ++ while ('0' <= *c_str && *c_str <= '9') ++ c_str++; ++ if (*c_str != 'b' && *c_str != 'f') ++ break; ++ c_str++; ++ return *c_str == '\0'; ++ } ++ while (0); ++ return 0; ++} ++ ++int ++is_label (const char *c_str) ++{ ++ if (is_internal_label (c_str)) ++ return 1; ++ else if ('0' <= *c_str && *c_str <= '9') ++ { ++ /* [0-9]+[bf] */ ++ while ('0' <= *c_str && *c_str <= '9') ++ c_str++; ++ return *c_str == 'b' || *c_str == 'f'; ++ } ++ else if (is_name_beginner (*c_str)) ++ { ++ /* [a-zA-Z\._\$][0-9a-zA-Z\._\$]* */ ++ c_str++; ++ while (is_part_of_name (*c_str)) ++ c_str++; ++ return *c_str == '\0'; ++ } ++ else ++ return 0; ++} ++ ++int ++is_label_with_addend (const char *c_str) ++{ ++ if (is_internal_label (c_str)) ++ return 1; ++ else if ('0' <= *c_str && *c_str <= '9') ++ { ++ /* [0-9]+[bf] */ ++ while ('0' <= *c_str && *c_str <= '9') ++ c_str++; ++ if (*c_str == 'b' || *c_str == 'f') ++ c_str++; ++ else ++ return 0; ++ return *c_str == '\0' ++ || ((*c_str == '-' || *c_str == '+') ++ && is_unsigned (c_str + 1)); ++ } ++ else if (is_name_beginner (*c_str)) ++ { ++ /* [a-zA-Z\._\$][0-9a-zA-Z\._\$]* */ ++ c_str++; ++ while (is_part_of_name (*c_str)) ++ c_str++; ++ return *c_str == '\0' ++ || ((*c_str == '-' || *c_str == '+') ++ && is_unsigned (c_str + 1)); ++ } ++ else ++ return 0; ++} ++ ++extern int ++loongarch_parse_expr (const char *expr, ++ struct reloc_info *reloc_stack_top, ++ size_t max_reloc_num, ++ size_t *reloc_num, ++ offsetT *imm_if_no_reloc); ++ ++static int32_t ++loongarch_args_parser_can_match_arg_helper (char esc_ch1, ++ char esc_ch2, ++ const char *bit_field, ++ const char *arg, ++ void *context) ++{ ++ struct loongarch_cl_insn *ip = context; ++ offsetT imm, ret = 0; ++ size_t reloc_num_we_have = MAX_RELOC_NUMBER_A_INSN - ip->reloc_num; ++ size_t reloc_num = 0; ++ ++ if (!ip->match_now) ++ return 0; ++ ++ switch (esc_ch1) ++ { ++ case 'l': ++ switch (esc_ch2) ++ { ++ default: ++ ip->match_now = is_label (arg); ++ if (!ip->match_now && is_label_with_addend (arg)) ++ as_fatal (_("This label shouldn't be with addend.")); ++ break; ++ case 'a': ++ ip->match_now = is_label_with_addend (arg); ++ break; ++ } ++ break; ++ case 's': ++ case 'u': ++ ip->match_now = ++ loongarch_parse_expr (arg, ip->reloc_info + ip->reloc_num, ++ reloc_num_we_have, &reloc_num, &imm) == 0; ++ ++ if (!ip->match_now) ++ break; ++ ++ if (esc_ch1 == 's') ++ switch (esc_ch2) ++ { ++ case 'c': ++ ip->match_now = reloc_num == 0; ++ break; ++ } ++ else ++ switch (esc_ch2) ++ { ++ case 'c': ++ ip->match_now = reloc_num == 0 && 0 <= imm; ++ break; ++ } ++ ++ if (!ip->match_now) ++ break; ++ ++ ret = imm; ++ if (reloc_num) ++ { ++ bfd_reloc_code_real_type reloc_type = BFD_RELOC_NONE; ++ reloc_num_we_have -= reloc_num; ++ if (reloc_num_we_have == 0) ++ as_fatal (_("expr too huge") /* want one more reloc */); ++ if (esc_ch1 == 'u') ++ { ++ if (strncmp (bit_field, "10:12", strlen ("10:12")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_U_10_12; ++ } ++ else if (esc_ch1 == 's') ++ { ++ if (strncmp (bit_field, "10:16<<2", strlen ("10:16<<2")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2; ++ else if (strncmp (bit_field, "0:5|10:16<<2", strlen ("0:5|10:16<<2")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2; ++ else if (strncmp (bit_field, "0:10|10:16<<2", strlen ("0:10|10:16<<2")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2; ++ else if (strncmp (bit_field, "10:12", strlen ("10:12")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_12; ++ else if (strncmp (bit_field, "5:20", strlen ("5:20")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_5_20; ++ else if (strncmp (bit_field, "10:16", strlen ("10:16")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_16; ++ else if (strncmp (bit_field, "10:5", strlen ("10:5")) == 0) ++ reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_5; ++ } ++ if (reloc_type == BFD_RELOC_NONE) ++ as_fatal (_("not support reloc bit-field\nfmt: %c%c %s\nargs: %s"), ++ esc_ch1, esc_ch2, bit_field , arg); ++ reloc_num++; ++ ip->reloc_num += reloc_num; ++ ip->reloc_info[ip->reloc_num - 1].type = reloc_type; ++ ip->reloc_info[ip->reloc_num - 1].value = const_0; ++ } ++ break; ++ case 'r': ++ imm = (offsetT) hash_find (r_htab, arg); ++ ip->match_now = 0 < imm; ++ ret = imm - 1; ++ break; ++ case 'f': ++ imm = (offsetT) hash_find (f_htab, arg); ++ ip->match_now = 0 < imm; ++ ret = imm - 1; ++ break; ++ case 'c': ++ switch (esc_ch2) ++ { ++ case 'r': ++ imm = (offsetT) hash_find (cr_htab, arg); ++ break; ++ default: ++ imm = (offsetT) hash_find (c_htab, arg); ++ } ++ ip->match_now = 0 < imm; ++ ret = imm - 1; ++ break; ++ case 'v': ++ imm = (offsetT) hash_find (v_htab, arg); ++ ip->match_now = 0 < imm; ++ ret = imm - 1; ++ break; ++ case 'x': ++ imm = (offsetT) hash_find (x_htab, arg); ++ ip->match_now = 0 < imm; ++ ret = imm - 1; ++ break; ++ case '\0': ++ ip->all_match = ip->match_now; ++ ip->insn_length = ip->insn->mask ? loongarch_insn_length (ip->insn->match) : 0; ++ /* FIXME: now we have no relax insn */ ++ ip->relax_max_length = ip->insn_length; ++ break; ++ default: ++ as_fatal (_("unknown escape")); ++ } ++ ++ do ++ { ++ // check imm overflow ++ int bit_width, bits_needed_s, bits_needed_u; ++ char *t; ++ ++ if (!ip->match_now) ++ break; ++ ++ if (0 < reloc_num) ++ break; ++ ++ bit_width = loongarch_get_bit_field_width (bit_field, &t); ++ ++ if (bit_width == -1) ++ // no specify bit width ++ break; ++ ++ // 在这里求出实际填入的二进制数。这部分内容和 loongarch_encode_imm ++ // 有重合。但是需要在这里加入一些判断内容,比如分支指令立即数 ++ // 右移两位,要保证立即数低两位为0 ++ imm = ret; ++ if (t[0] == '<' && t[1] == '<') ++ { ++ int i = strtol (t += 2, &t, 10), j; ++ for (j = i; 0 < j; j--, imm >>= 1) ++ if (imm & 1) ++ as_fatal (_("require imm low %d bit is 0."), i); ++ } ++ ++ if (*t == '+') ++ imm -= strtol (t, &t, 10); ++ ++ bits_needed_s = loongarch_bits_imm_needed (imm, 1); ++ bits_needed_u = loongarch_bits_imm_needed (imm, 0); ++ ++ // 在这里判断立即数是否溢出。关于有符号立即数我有两种理解 ++ // 一是代数意义上的溢出,如果传入的值超出定义域,那么报错。 ++ // 二是程序员可能希望指定位域表示,那么值可能是一个使得符号位为1的正数。 ++ // riscv的溢出判断是第一种,这里按照riscv的方法来做 ++ if ((esc_ch1 == 's' && bit_width < bits_needed_s) ++ || (esc_ch1 != 's' && bit_width < bits_needed_u)) ++ // how to do after we detect overflow ++ as_fatal (_("Immediate overflow.\n" ++ "format: %c%c%s\n" ++ "arg: %s"), ++ esc_ch1, esc_ch2, bit_field, arg); ++ } ++ while (0); ++ ++ if (esc_ch1 != '\0') ++ { ++ ip->args[ip->arg_num] = ret; ++ ip->arg_num++; ++ } ++ return ret; ++} ++ ++static void ++get_loongarch_opcode (struct loongarch_cl_insn *insn) ++{ ++ const struct loongarch_opcode *it; ++ struct loongarch_ase *ase; ++ for (ase = loongarch_ASEs; ase->enabled; ase++) ++ { ++ if (!*ase->enabled ++ || (ase->include && !*ase->include) ++ || (ase->exclude && *ase->exclude)) ++ continue; ++ ++ if (!ase->name_hash_entry) ++ { ++ ase->name_hash_entry = hash_new (); ++ for (it = ase->opcodes; it->name; it++) ++ hash_insert (ase->name_hash_entry, it->name, (void *) it); ++ } ++ ++ if ((it = hash_find (ase->name_hash_entry, insn->name)) == NULL) ++ continue; ++ ++ do ++ { ++ insn->insn = it; ++ insn->match_now = 1; ++ insn->all_match = 0; ++ insn->arg_num = 0; ++ insn->reloc_num = 0; ++ insn->insn_bin = ++ loongarch_foreach_args (it->format, insn->arg_strs, ++ loongarch_args_parser_can_match_arg_helper, insn); ++ if (insn->all_match ++ && !(it->include && !*it->include) ++ && !(it->exclude && *it->exclude)) ++ { ++ insn->insn_bin |= it->match; ++ return; ++ } ++ it++; ++ } ++ while (it->name && strcasecmp (it->name, insn->name) == 0); ++ } ++} ++ ++static int ++check_this_insn_before_appending (struct loongarch_cl_insn *ip) ++{ ++ int ret = 0; ++ if (strcmp (ip->name, "la.abs") == 0) ++ { ++ ip->reloc_info[ip->reloc_num].type = BFD_RELOC_LARCH_MARK_LA; ++ my_getExpression (&ip->reloc_info[ip->reloc_num].value, ip->arg_strs[1]); ++ ip->reloc_num++; ++ } ++ else if (ip->insn->mask == 0xffff8000 ++ && ((ip->insn_bin & 0xfff00000) == 0x38600000 ++ || (ip->insn_bin & 0xffff0000) == 0x38700000 ++ || (ip->insn_bin & 0xffff0000) == 0x38710000)) ++ { ++ /* for AMO insn amswap.[wd], amadd.[wd], etc. */ ++ if (ip->args[0] != 0 && ++ (ip->args[0] == ip->args[1] || ip->args[0] == ip->args[2])) ++ as_fatal (_( ++"AMO insns require rd != base && rd != rt when rd isn't $r0")); ++ } ++ else if ((ip->insn->mask == 0xffe08000 ++ && (ip->insn_bin & 0xffe00000) == 0x00600000) ++ || (ip->insn->mask == 0xffc00000 ++ && (ip->insn_bin & 0xff800000) == 0x00800000)) ++ { ++ /* for bstr(ins|pick).[wd] */ ++ if (ip->args[2] < ip->args[3]) ++ as_fatal (_("bstr(ins|pick).[wd] require msbd >= lsbd")); ++ } ++ else if (ip->insn->mask != 0 ++ && (ip->insn_bin & 0xfe0003c0) == 0x04000000 ++ && (strcmp ("csrxchg", ip->name) == 0 ++ || strcmp ("gcsrxchg", ip->name) == 0)) ++ as_fatal (_("g?csrxchg require rj != $r0 && rj != $r1")); ++ ++ return ret; ++} ++ ++static void ++install_insn (const struct loongarch_cl_insn *insn) ++{ ++ char *f = insn->frag->fr_literal + insn->where; ++ if (0 < insn->insn_length) ++ md_number_to_chars (f, insn->insn_bin, insn->insn_length); ++} ++ ++static void ++move_insn (struct loongarch_cl_insn *insn, fragS *frag, long where) ++{ ++ size_t i; ++ insn->frag = frag; ++ insn->where = where; ++ for (i = 0; i < insn->reloc_num; i++) ++ { ++ insn->fixp[i]->fx_frag = frag; ++ insn->fixp[i]->fx_where = where; ++ } ++ install_insn (insn); ++} ++ ++/* Add INSN to the end of the output. */ ++static void ++append_fixed_insn (struct loongarch_cl_insn *insn) ++{ ++ char *f = frag_more (insn->insn_length); ++ move_insn (insn, frag_now, f - frag_now->fr_literal); ++} ++ ++static void ++append_fixp_and_insn (struct loongarch_cl_insn *ip) ++{ ++ reloc_howto_type *howto; ++ bfd_reloc_code_real_type reloc_type; ++ struct reloc_info *reloc_info = ip->reloc_info; ++ size_t i; ++ for (i = 0; i < ip->reloc_num; i++) ++ { ++ reloc_type = reloc_info[i].type; ++ howto = bfd_reloc_type_lookup (stdoutput, reloc_type); ++ if (howto == NULL) ++ as_fatal (_("no HOWTO loong relocation number %d"), reloc_type); ++ ++ ip->fixp[i] = ++ fix_new_exp (ip->frag, ip->where, bfd_get_reloc_size (howto), ++ &reloc_info[i].value, FALSE, reloc_type); ++ } ++ ++ if (ip->insn_length < ip->relax_max_length) ++ as_fatal (_("Internal error: not support relax now")); ++ else ++ append_fixed_insn (ip); ++ dwarf2_emit_insn (0); ++} ++ ++//ask helper for returning a malloced c_str or NULL ++static char * ++assember_macro_helper (const char * const args[], void *context_ptr) ++{ ++ struct loongarch_cl_insn *insn = context_ptr; ++ char *ret = NULL; ++ if (strcmp (insn->name, "li.d") == 0 ++ || strcmp (insn->name, "li.w") == 0) ++ { ++ char args_buf[50], insns_buf[200]; ++ const char *arg_strs[6]; ++ uint32_t hi32, lo32; ++ ++ /* We pay attention to sign extend beacause it is chance of reduce insn. ++ The exception is 12-bit and hi-12-bit unsigned, ++ we need a 'ori' or a 'lu52i.d' accordingly. */ ++ char all0_bit_vec, sign_bit_vec, allf_bit_vec, paritial_is_sext_of_prev; ++ ++ lo32 = insn->args[1] & 0xffffffff; ++ hi32 = insn->args[1] >> 32; ++ ++ ++ if (strcmp (insn->name, "li.w") == 0) ++ { ++ if (hi32 != 0 && hi32 != 0xffffffff) ++ as_fatal (_("li overflow: hi32:0x%x lo32:0x%x"), hi32, lo32); ++ hi32 = lo32 & 0x80000000 ? 0xffffffff : 0; ++ } ++ ++ if (strcmp (insn->name, "li.d") == 0 && LARCH_opts.rlen_is_32) ++ as_fatal (_("we can't li.d on 32bit-arch")); ++ ++ snprintf (args_buf, sizeof (args_buf), "0x%x,0x%x,0x%x,0x%x,%s", ++ (hi32 >> 20) & 0xfff, hi32 & 0xfffff, ++ (lo32 >> 12) & 0xfffff, lo32 & 0xfff, args[0]); ++ loongarch_split_args_by_comma (args_buf, arg_strs); ++ ++ all0_bit_vec = (((hi32 & 0xfff00000) == 0) << 3) ++ | (((hi32 & 0x000fffff) == 0) << 2) ++ | (((lo32 & 0xfffff000) == 0) << 1) ++ | ((lo32 & 0x00000fff) == 0); ++ sign_bit_vec = (((hi32 & 0x80000000) != 0) << 3) ++ | (((hi32 & 0x00080000) != 0) << 2) ++ | (((lo32 & 0x80000000) != 0) << 1) ++ | ((lo32 & 0x00000800) != 0); ++ allf_bit_vec = (((hi32 & 0xfff00000) == 0xfff00000) << 3) ++ | (((hi32 & 0x000fffff) == 0x000fffff) << 2) ++ | (((lo32 & 0xfffff000) == 0xfffff000) << 1) ++ | ((lo32 & 0x00000fff) == 0x00000fff); ++ paritial_is_sext_of_prev = (all0_bit_vec ^ allf_bit_vec) ++ & (all0_bit_vec ^ (sign_bit_vec << 1)); ++ ++ static const char * const li_32bit [] = { ++ "lu12i.w %5,%3&0x80000?%3-0x100000:%3;ori %5,%5,%4;", ++ "lu12i.w %5,%3&0x80000?%3-0x100000:%3;", ++ "addi.w %5,$r0,%4&0x800?%4-0x1000:%4;", ++ "or %5,$r0,$r0;", ++ }; ++ static const char * const li_hi_32bit[] = { ++ "lu32i.d %5,%2&0x80000?%2-0x100000:%2;" ++ "lu52i.d %5,%5,%1&0x800?%1-0x1000:%1;", ++ "lu52i.d %5,%5,%1&0x800?%1-0x1000:%1;", ++ "lu32i.d %5,%2&0x80000?%2-0x100000:%2;", ++ "", ++ }; ++ do ++ { ++ insns_buf[0] = '\0'; ++ if (paritial_is_sext_of_prev == 0x7) ++ { ++ strcat (insns_buf, "lu52i.d %5,$r0,%1&0x800?%1-0x1000:%1;"); ++ break; ++ } ++ if ((all0_bit_vec & 0x3) == 0x2) ++ strcat (insns_buf, "ori %5,$r0,%4;"); ++ else ++ strcat (insns_buf, li_32bit[paritial_is_sext_of_prev & 0x3]); ++ strcat (insns_buf, li_hi_32bit[paritial_is_sext_of_prev >> 2]); ++ } ++ while (0); ++ ++ ret = loongarch_expand_macro (insns_buf, arg_strs, NULL, NULL); ++ } ++ return ret; ++} ++ ++//accept instructions separated by ';' ++//assuming 'not starting with space and not ending with space' or pass in empty c_str ++static void ++loongarch_assemble_INSNs (char *str) ++{ ++ char *rest; ++ ++ for (rest = str; *rest != ';' && *rest != '\0'; rest++); ++ if (*rest == ';') ++ *rest++ = '\0'; ++ ++ if (*str == ':') ++ { ++ str++; ++ setup_internal_label_here (strtol (str, &str, 10)); ++ str++; ++ } ++ ++ do ++ { ++ if (*str == '\0') ++ break; ++ ++ struct loongarch_cl_insn the_one = {0}; ++ the_one.name = str; ++ ++ for (; *str && *str != ' '; str++); ++ if (*str == ' ') ++ *str++ = '\0'; ++ ++ loongarch_split_args_by_comma (str, the_one.arg_strs); ++ get_loongarch_opcode (&the_one); ++ ++ if (!the_one.all_match) ++ as_fatal (_("no match insn: %s\t%s"), ++ the_one.name, loongarch_cat_splited_strs (the_one.arg_strs)); ++ ++ if (check_this_insn_before_appending (&the_one) != 0) ++ break; ++ ++ append_fixp_and_insn (&the_one); ++ if (the_one.insn_length == 0 && the_one.insn->macro) ++ { ++ char *c_str = ++ loongarch_expand_macro (the_one.insn->macro, ++ the_one.arg_strs, assember_macro_helper, &the_one); ++ loongarch_assemble_INSNs (c_str); ++ free (c_str); ++ } ++ } ++ while (0); ++ ++ if (*rest != '\0') ++ loongarch_assemble_INSNs (rest); ++} ++ ++void ++md_assemble (char *str) ++{ ++ loongarch_assemble_INSNs (str); ++} ++ ++const char *md_atof (int type, char *litP, int *sizeP) ++{ ++ return ieee_md_atof (type, litP, sizeP, FALSE); ++} ++ ++void md_number_to_chars (char *buf, valueT val, int n) ++{ ++ number_to_chars_littleendian (buf, val, n); ++} ++ ++/* The location from which a PC relative jump should be calculated, ++ given a PC relative reloc. */ ++long ++md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED) ++{ ++ return 0; ++} ++ ++void ++md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) ++{ ++ static int64_t stack_top; ++ static int last_reloc_is_sop_push_pcrel_1 = 0; ++ int last_reloc_is_sop_push_pcrel = last_reloc_is_sop_push_pcrel_1; ++ insn_t insn; ++ last_reloc_is_sop_push_pcrel_1 = 0; ++ ++ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where; ++ switch (fixP->fx_r_type) ++ { ++ case BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL: ++ case BFD_RELOC_LARCH_SOP_PUSH_TLS_GD: ++ case BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT: ++ if (fixP->fx_addsy) ++ S_SET_THREAD_LOCAL (fixP->fx_addsy); ++ case BFD_RELOC_LARCH_SOP_PUSH_PCREL: ++ case BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL: ++ if (fixP->fx_addsy == NULL) ++ as_bad_where (fixP->fx_file, fixP->fx_line, ++ _("Relocation against a constant")); ++ if (fixP->fx_r_type == BFD_RELOC_LARCH_SOP_PUSH_PCREL) ++ { ++ /* 分支到内部符号的重定位尽量在这里解决。一方面是为了反汇编更好看; ++ 也是为了方便PMON的模块加载。PMON连接器的重定位类型枚举空间很小, ++ 无法实现全的重定位。编译时加入-mabiabs使得所有外部符号都有la.abs, ++ 从而有MARK_LA这个重定位,PMON连接器重定位时填4条la指令。 */ ++ last_reloc_is_sop_push_pcrel_1 = 1; ++ if (S_GET_SEGMENT (fixP->fx_addsy) == seg) ++ stack_top = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset ++ - (fixP->fx_where + fixP->fx_frag->fr_address); ++ else ++ stack_top = 0; ++ } ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_10_5: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & ~(uint64_t)0xf) != 0x0 ++ && (stack_top & ~(uint64_t)0xf) != ~(uint64_t)0xf) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & (~(uint32_t)0x7c00)) | ((stack_top & 0x1f) << 10); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_U_10_12: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if (stack_top & ~(uint64_t)0xfff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & (~(uint32_t)0x3ffc00)) | ((stack_top & 0xfff) << 10); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_10_12: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & ~(uint64_t)0x7ff) != 0x0 ++ && (stack_top & ~(uint64_t)0x7ff) != ~(uint64_t)0x7ff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & (~(uint32_t)0x3ffc00)) | ((stack_top & 0xfff) << 10); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_10_16: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & ~(uint64_t)0x7fff) != 0x0 ++ && (stack_top & ~(uint64_t)0x7fff) != ~(uint64_t)0x7fff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & 0xfc0003ff) | ((stack_top & 0xffff) << 10); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & 0x3) != 0) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ stack_top >>= 2; ++ if ((stack_top & ~(uint64_t)0x7fff) != 0x0 ++ && (stack_top & ~(uint64_t)0x7fff) != ~(uint64_t)0x7fff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & 0xfc0003ff) | ((stack_top & 0xffff) << 10); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & 0x3) != 0) ++ break; ++ stack_top >>= 2; ++ if ((stack_top & ~(uint64_t)0xfffff) != 0x0 ++ && (stack_top & ~(uint64_t)0xfffff) != ~(uint64_t)0xfffff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & 0xfc0003e0) ++ | ((stack_top & 0xffff) << 10) | ((stack_top & 0x1f0000) >> 16); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_5_20: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & ~(uint64_t)0x7ffff) != 0x0 ++ && (stack_top & ~(uint64_t)0x7ffff) != ~(uint64_t)0x7ffff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & (~(uint32_t)0x1ffffe0)) | ((stack_top & 0xfffff) << 5); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if ((stack_top & 0x3) != 0) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ stack_top >>= 2; ++ if ((stack_top & ~(uint64_t)0x1ffffff) != 0x0 ++ && (stack_top & ~(uint64_t)0x1ffffff) != ~(uint64_t)0x1ffffff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ insn = bfd_getl32 (buf); ++ insn = (insn & 0xfc000000) ++ | ((stack_top & 0xffff) << 10) | ((stack_top & 0x3ff0000) >> 16); ++ bfd_putl32 (insn, buf); ++ break; ++ ++ case BFD_RELOC_LARCH_SOP_POP_32_U: ++ if (!last_reloc_is_sop_push_pcrel) ++ break; ++ if (stack_top & ~(uint64_t)0xffffffff) ++ as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); ++ bfd_putl32 (stack_top, buf); ++ break; ++ ++ case BFD_RELOC_64: ++ case BFD_RELOC_32: ++ if (fixP->fx_subsy) ++ { ++ case BFD_RELOC_24: ++ case BFD_RELOC_16: ++ case BFD_RELOC_8: ++ fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP)); ++ fixP->fx_next->fx_addsy = fixP->fx_subsy; ++ fixP->fx_next->fx_subsy = NULL; ++ fixP->fx_next->fx_offset = 0; ++ fixP->fx_subsy = NULL; ++ ++ switch (fixP->fx_r_type) ++ { ++ case BFD_RELOC_64: ++ fixP->fx_r_type = BFD_RELOC_LARCH_ADD64; ++ fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB64; ++ break; ++ case BFD_RELOC_32: ++ fixP->fx_r_type = BFD_RELOC_LARCH_ADD32; ++ fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB32; ++ break; ++ case BFD_RELOC_24: ++ fixP->fx_r_type = BFD_RELOC_LARCH_ADD24; ++ fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB24; ++ break; ++ case BFD_RELOC_16: ++ fixP->fx_r_type = BFD_RELOC_LARCH_ADD16; ++ fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB16; ++ break; ++ case BFD_RELOC_8: ++ fixP->fx_r_type = BFD_RELOC_LARCH_ADD8; ++ fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB8; ++ break; ++ default: ++ break; ++ } ++ md_number_to_chars (buf, 0, fixP->fx_size); ++ if (fixP->fx_next->fx_addsy == NULL) ++ fixP->fx_next->fx_done = 1; ++ } ++ if (fixP->fx_addsy == NULL) ++ { ++ fixP->fx_done = 1; ++ md_number_to_chars (buf, *valP, fixP->fx_size); ++ } ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++int ++loongarch_relax_frag (asection *sec ATTRIBUTE_UNUSED, fragS *fragp ATTRIBUTE_UNUSED, long stretch ATTRIBUTE_UNUSED) ++{ ++ return 0; ++} ++ ++int ++md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED, asection *segtype ATTRIBUTE_UNUSED) ++{ ++ return 0; ++} ++ ++/* Translate internal representation of relocation info to BFD target ++ format. */ ++arelent * ++tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) ++{ ++ arelent *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; ++ reloc->addend = fixp->fx_offset; ++ ++ 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; ++ } ++ ++ return reloc; ++} ++ ++/* Convert a machine dependent frag. */ ++void ++md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED, fragS *fragp ATTRIBUTE_UNUSED) ++{ ++ // fragp->fr_fix += 8; ++} ++ ++/* Standard calling conventions leave the CFA at SP on entry. */ ++void ++loongarch_cfi_frame_initial_instructions (void) ++{ ++ cfi_add_CFA_def_cfa_register (3 /* $sp */); ++} ++ ++int ++loongarch_dwarf2_addr_size (void) ++{ ++ return LARCH_opts.addrwidth_is_32? 4 : 8; ++} ++ ++void ++tc_loongarch_parse_to_dw2regnum (expressionS *exp) ++{ ++ expression_and_evaluate (exp); ++} ++ ++void ++md_show_usage (FILE *stream) ++{ ++ fprintf (stream, _("\ ++ LoongISA options:\n\ ++ ")); ++} ++ ++/* Fill in an rs_align_code fragment. We want to fill 'andi $r0,$r0,0'. */ ++void ++loongarch_handle_align (fragS *fragp) ++{ ++// char nop_opcode; ++ char *p; ++ int bytes, size, excess; ++ valueT opcode; ++ ++ if (fragp->fr_type != rs_align_code) ++ return; ++ ++ struct loongarch_cl_insn nop = ++ {.name = "andi", .arg_strs = {"$r0", "$r0", "0", NULL}}; ++ ++ get_loongarch_opcode (&nop); ++ gas_assert (nop.all_match); ++ ++ p = fragp->fr_literal + fragp->fr_fix; ++ opcode = nop.insn_bin; ++ size = 4; ++ ++ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; ++ excess = bytes % size; ++ ++ /* Handle the leading part if we're not inserting a whole number of ++ instructions, and make it the end of the fixed part of the frag. ++ Try to fit in a short microMIPS NOP if applicable and possible, ++ and use zeroes otherwise. */ ++ gas_assert (excess < 4); ++ fragp->fr_fix += excess; ++ switch (excess) ++ { ++ case 3: ++ *p++ = '\0'; ++ case 2: ++ *p++ = '\0'; ++ case 1: ++ *p++ = '\0'; ++ case 0: ++ break; ++ } ++ ++ md_number_to_chars (p, opcode, size); ++ fragp->fr_var = size; ++} ++ ++void ++loongarch_elf_final_processing (void) ++{ ++ if (LARCH_opts.abi_is_lp64) ++ elf_elfheader (stdoutput)->e_flags |= EF_LARCH_ABI_LP64; ++ else if (LARCH_opts.abi_is_lp32) ++ elf_elfheader (stdoutput)->e_flags |= EF_LARCH_ABI_LP32; ++} +diff --git a/gas/config/tc-loongarch.h b/gas/config/tc-loongarch.h +new file mode 100644 +index 00000000..f2647972 +--- /dev/null ++++ b/gas/config/tc-loongarch.h +@@ -0,0 +1,77 @@ ++#ifndef TC_LOONGARCH ++#define TC_LOONGARCH ++ ++#define TARGET_BYTES_BIG_ENDIAN 0 ++#define TARGET_ARCH bfd_arch_loongarch ++ ++#define WORKING_DOT_WORD 1 ++#define REPEAT_CONS_EXPRESSIONS ++ ++// early than md_begin ++#define md_after_parse_args loongarch_after_parse_args ++extern void loongarch_after_parse_args (void); ++ ++extern void loongarch_pop_insert (void); ++#define md_pop_insert() loongarch_pop_insert () ++ ++#define TARGET_FORMAT loongarch_target_format() ++extern const char * loongarch_target_format (void); ++ ++ ++#define md_relax_frag(segment, fragp, stretch) \ ++ loongarch_relax_frag (segment, fragp, stretch) ++extern int loongarch_relax_frag (asection *, struct frag *, long); ++#define md_section_align(seg,size) (size) ++#define md_undefined_symbol(name) (0) ++ ++/* This is called to see whether a reloc against a defined symbol ++ should be converted into a reloc against a section. */ ++#define tc_fix_adjustable(fixp) 0 ++ ++/* Values passed to md_apply_fix don't include symbol values. */ ++#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1 ++#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1 ++#define DIFF_EXPR_OK 1 ++ ++#define TARGET_USE_CFIPOP 1 ++#define DWARF2_DEFAULT_RETURN_COLUMN 1 /* $ra */ ++#define DWARF2_CIE_DATA_ALIGNMENT -4 ++extern int loongarch_dwarf2_addr_size (void); ++#define DWARF2_FDE_RELOC_SIZE loongarch_dwarf2_addr_size () ++#define DWARF2_ADDR_SIZE(bfd) loongarch_dwarf2_addr_size () ++#define CFI_DIFF_EXPR_OK 0 ++ ++#define tc_cfi_frame_initial_instructions loongarch_cfi_frame_initial_instructions ++extern void loongarch_cfi_frame_initial_instructions (void); ++ ++// 我们不实现 tc_regname_to_dw2regnum,而是重载tc_parse_to_dw2regnum。 ++// 因为MIPS汇编兼容需要CFA来实现C++异常抛出。我们仅仅对数字作映射, ++// 不再考虑寄存器的名字了。 ++//#define tc_regname_to_dw2regnum tc_loongarch_regname_to_dw2regnum ++#define tc_parse_to_dw2regnum tc_loongarch_parse_to_dw2regnum ++extern void tc_loongarch_parse_to_dw2regnum (expressionS *); ++ ++// a enumerated values to specific how to deal with align in '.text' ++// now we want to fill 'andi $r0,$r0,0x0' ++#define loongarch_nop_opcode() 0 ++#define NOP_OPCODE (loongarch_nop_opcode ()) ++ ++#define HANDLE_ALIGN(fragp) loongarch_handle_align (fragp) ++extern void loongarch_handle_align (struct frag *); ++#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4) ++ ++#define elf_tc_final_processing loongarch_elf_final_processing ++extern void loongarch_elf_final_processing (void); ++ ++#define MAX_RELOC_NUMBER_A_INSN 20 ++ ++struct reloc_info ++{ ++ bfd_reloc_code_real_type type; ++ expressionS value; ++}; ++ ++int is_label_with_addend (const char *); ++int is_label (const char *); ++ ++#endif +diff --git a/gas/configure.ac b/gas/configure.ac +index 4bd2077c..41f779ee 100644 +--- a/gas/configure.ac ++++ b/gas/configure.ac +@@ -430,6 +430,15 @@ changequote([,])dnl + using_cgen=yes + ;; + ++ loongarch) ++ for f in loongarch-parse.o loongarch-lex-wrapper.o; do ++ case " $extra_objects " in ++ *" $f "*) ;; ++ *) extra_objects="$extra_objects $f" ;; ++ esac ++ done ++ ;; ++ + m32c) + using_cgen=yes + ;; +diff --git a/gas/configure.tgt b/gas/configure.tgt +index abf7e02e..76b1abcb 100644 +--- a/gas/configure.tgt ++++ b/gas/configure.tgt +@@ -67,6 +67,7 @@ case ${cpu} in + ip2k) cpu_type=ip2k endian=big ;; + iq2000) cpu_type=iq2000 endian=big ;; + lm32) cpu_type=lm32 ;; ++ loongarch*) cpu_type=loongarch ;; + m32c) cpu_type=m32c endian=little ;; + m32r) cpu_type=m32r endian=big ;; + m32rle) cpu_type=m32r endian=little ;; +@@ -311,6 +312,8 @@ case ${generic_target} in + + lm32-*-*) fmt=elf ;; + ++ loongarch*) fmt=elf ;; ++ + m32c-*-elf) fmt=elf ;; + + m32r-*-elf*) fmt=elf ;; +@@ -497,7 +500,7 @@ case ${generic_target} in + esac + + case ${cpu_type} in +- aarch64 | alpha | arm | i386 | ia64 | microblaze | mips | ns32k | or1k | or1knd | pdp11 | ppc | riscv | sparc | z80 | z8k) ++ aarch64 | alpha | arm | i386 | ia64 | loongarch | microblaze | mips | ns32k | or1k | or1knd | pdp11 | ppc | riscv | sparc | z80 | z8k) + bfd_gas=yes + ;; + esac +diff --git a/gas/loongarch-lex.c b/gas/loongarch-lex.c +new file mode 100644 +index 00000000..daefca0b +--- /dev/null ++++ b/gas/loongarch-lex.c +@@ -0,0 +1,1845 @@ ++ ++#line 3 "loongarch-lex.c" ++ ++#define YY_INT_ALIGNED short int ++ ++/* A lexical scanner generated by flex */ ++ ++#define FLEX_SCANNER ++#define YY_FLEX_MAJOR_VERSION 2 ++#define YY_FLEX_MINOR_VERSION 6 ++#define YY_FLEX_SUBMINOR_VERSION 4 ++#if YY_FLEX_SUBMINOR_VERSION > 0 ++#define FLEX_BETA ++#endif ++ ++/* First, we deal with platform-specific or compiler-specific issues. */ ++ ++/* begin standard C headers. */ ++#include ++#include ++#include ++#include ++ ++/* end standard C headers. */ ++ ++/* flex integer type definitions */ ++ ++#ifndef FLEXINT_H ++#define FLEXINT_H ++ ++/* C99 systems have . Non-C99 systems may or may not. */ ++ ++#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L ++ ++/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, ++ * if you want the limit (max/min) macros for int types. ++ */ ++#ifndef __STDC_LIMIT_MACROS ++#define __STDC_LIMIT_MACROS 1 ++#endif ++ ++#include ++typedef int8_t flex_int8_t; ++typedef uint8_t flex_uint8_t; ++typedef int16_t flex_int16_t; ++typedef uint16_t flex_uint16_t; ++typedef int32_t flex_int32_t; ++typedef uint32_t flex_uint32_t; ++#else ++typedef signed char flex_int8_t; ++typedef short int flex_int16_t; ++typedef int flex_int32_t; ++typedef unsigned char flex_uint8_t; ++typedef unsigned short int flex_uint16_t; ++typedef unsigned int flex_uint32_t; ++ ++/* Limits of integral types. */ ++#ifndef INT8_MIN ++#define INT8_MIN (-128) ++#endif ++#ifndef INT16_MIN ++#define INT16_MIN (-32767-1) ++#endif ++#ifndef INT32_MIN ++#define INT32_MIN (-2147483647-1) ++#endif ++#ifndef INT8_MAX ++#define INT8_MAX (127) ++#endif ++#ifndef INT16_MAX ++#define INT16_MAX (32767) ++#endif ++#ifndef INT32_MAX ++#define INT32_MAX (2147483647) ++#endif ++#ifndef UINT8_MAX ++#define UINT8_MAX (255U) ++#endif ++#ifndef UINT16_MAX ++#define UINT16_MAX (65535U) ++#endif ++#ifndef UINT32_MAX ++#define UINT32_MAX (4294967295U) ++#endif ++ ++#ifndef SIZE_MAX ++#define SIZE_MAX (~(size_t)0) ++#endif ++ ++#endif /* ! C99 */ ++ ++#endif /* ! FLEXINT_H */ ++ ++/* begin standard C++ headers. */ ++ ++/* TODO: this is always defined, so inline it */ ++#define yyconst const ++ ++#if defined(__GNUC__) && __GNUC__ >= 3 ++#define yynoreturn __attribute__((__noreturn__)) ++#else ++#define yynoreturn ++#endif ++ ++/* Returned upon end-of-file. */ ++#define YY_NULL 0 ++ ++/* Promotes a possibly negative, possibly signed char to an ++ * integer in range [0..255] for use as an array index. ++ */ ++#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) ++ ++/* Enter a start condition. This macro really ought to take a parameter, ++ * but we do it the disgusting crufty way forced on us by the ()-less ++ * definition of BEGIN. ++ */ ++#define BEGIN (yy_start) = 1 + 2 * ++/* Translate the current start state into a value that can be later handed ++ * to BEGIN to return to the state. The YYSTATE alias is for lex ++ * compatibility. ++ */ ++#define YY_START (((yy_start) - 1) / 2) ++#define YYSTATE YY_START ++/* Action number for EOF rule of a given start state. */ ++#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) ++/* Special action meaning "start processing a new file". */ ++#define YY_NEW_FILE yyrestart( yyin ) ++#define YY_END_OF_BUFFER_CHAR 0 ++ ++/* Size of default input buffer. */ ++#ifndef YY_BUF_SIZE ++#ifdef __ia64__ ++/* On IA-64, the buffer size is 16k, not 8k. ++ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. ++ * Ditto for the __ia64__ case accordingly. ++ */ ++#define YY_BUF_SIZE 32768 ++#else ++#define YY_BUF_SIZE 16384 ++#endif /* __ia64__ */ ++#endif ++ ++/* The state buf must be large enough to hold one state per character in the main buffer. ++ */ ++#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) ++ ++#ifndef YY_TYPEDEF_YY_BUFFER_STATE ++#define YY_TYPEDEF_YY_BUFFER_STATE ++typedef struct yy_buffer_state *YY_BUFFER_STATE; ++#endif ++ ++#ifndef YY_TYPEDEF_YY_SIZE_T ++#define YY_TYPEDEF_YY_SIZE_T ++typedef size_t yy_size_t; ++#endif ++ ++extern int yyleng; ++ ++extern FILE *yyin, *yyout; ++ ++#define EOB_ACT_CONTINUE_SCAN 0 ++#define EOB_ACT_END_OF_FILE 1 ++#define EOB_ACT_LAST_MATCH 2 ++ ++ #define YY_LESS_LINENO(n) ++ #define YY_LINENO_REWIND_TO(ptr) ++ ++/* Return all but the first "n" matched characters back to the input stream. */ ++#define yyless(n) \ ++ do \ ++ { \ ++ /* Undo effects of setting up yytext. */ \ ++ int yyless_macro_arg = (n); \ ++ YY_LESS_LINENO(yyless_macro_arg);\ ++ *yy_cp = (yy_hold_char); \ ++ YY_RESTORE_YY_MORE_OFFSET \ ++ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ ++ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ ++ } \ ++ while ( 0 ) ++#define unput(c) yyunput( c, (yytext_ptr) ) ++ ++#ifndef YY_STRUCT_YY_BUFFER_STATE ++#define YY_STRUCT_YY_BUFFER_STATE ++struct yy_buffer_state ++ { ++ FILE *yy_input_file; ++ ++ char *yy_ch_buf; /* input buffer */ ++ char *yy_buf_pos; /* current position in input buffer */ ++ ++ /* Size of input buffer in bytes, not including room for EOB ++ * characters. ++ */ ++ int yy_buf_size; ++ ++ /* Number of characters read into yy_ch_buf, not including EOB ++ * characters. ++ */ ++ int yy_n_chars; ++ ++ /* Whether we "own" the buffer - i.e., we know we created it, ++ * and can realloc() it to grow it, and should free() it to ++ * delete it. ++ */ ++ int yy_is_our_buffer; ++ ++ /* Whether this is an "interactive" input source; if so, and ++ * if we're using stdio for input, then we want to use getc() ++ * instead of fread(), to make sure we stop fetching input after ++ * each newline. ++ */ ++ int yy_is_interactive; ++ ++ /* Whether we're considered to be at the beginning of a line. ++ * If so, '^' rules will be active on the next match, otherwise ++ * not. ++ */ ++ int yy_at_bol; ++ ++ int yy_bs_lineno; /**< The line count. */ ++ int yy_bs_column; /**< The column count. */ ++ ++ /* Whether to try to fill the input buffer when we reach the ++ * end of it. ++ */ ++ int yy_fill_buffer; ++ ++ int yy_buffer_status; ++ ++#define YY_BUFFER_NEW 0 ++#define YY_BUFFER_NORMAL 1 ++ /* When an EOF's been seen but there's still some text to process ++ * then we mark the buffer as YY_EOF_PENDING, to indicate that we ++ * shouldn't try reading from the input source any more. We might ++ * still have a bunch of tokens to match, though, because of ++ * possible backing-up. ++ * ++ * When we actually see the EOF, we change the status to "new" ++ * (via yyrestart()), so that the user can continue scanning by ++ * just pointing yyin at a new input file. ++ */ ++#define YY_BUFFER_EOF_PENDING 2 ++ ++ }; ++#endif /* !YY_STRUCT_YY_BUFFER_STATE */ ++ ++/* Stack of input buffers. */ ++static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ ++static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ ++static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ ++ ++/* We provide macros for accessing buffer states in case in the ++ * future we want to put the buffer states in a more general ++ * "scanner state". ++ * ++ * Returns the top of the stack, or NULL. ++ */ ++#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ++ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ ++ : NULL) ++/* Same as previous macro, but useful when we know that the buffer stack is not ++ * NULL or when we need an lvalue. For internal use only. ++ */ ++#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] ++ ++/* yy_hold_char holds the character lost when yytext is formed. */ ++static char yy_hold_char; ++static int yy_n_chars; /* number of characters read into yy_ch_buf */ ++int yyleng; ++ ++/* Points to current character in buffer. */ ++static char *yy_c_buf_p = NULL; ++static int yy_init = 0; /* whether we need to initialize */ ++static int yy_start = 0; /* start state number */ ++ ++/* Flag which is used to allow yywrap()'s to do buffer switches ++ * instead of setting up a fresh yyin. A bit of a hack ... ++ */ ++static int yy_did_buffer_switch_on_eof; ++ ++void yyrestart ( FILE *input_file ); ++void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); ++YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); ++void yy_delete_buffer ( YY_BUFFER_STATE b ); ++void yy_flush_buffer ( YY_BUFFER_STATE b ); ++void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); ++void yypop_buffer_state ( void ); ++ ++static void yyensure_buffer_stack ( void ); ++static void yy_load_buffer_state ( void ); ++static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); ++#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) ++ ++YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); ++YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); ++YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); ++ ++void *yyalloc ( yy_size_t ); ++void *yyrealloc ( void *, yy_size_t ); ++void yyfree ( void * ); ++ ++#define yy_new_buffer yy_create_buffer ++#define yy_set_interactive(is_interactive) \ ++ { \ ++ if ( ! YY_CURRENT_BUFFER ){ \ ++ yyensure_buffer_stack (); \ ++ YY_CURRENT_BUFFER_LVALUE = \ ++ yy_create_buffer( yyin, YY_BUF_SIZE ); \ ++ } \ ++ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ ++ } ++#define yy_set_bol(at_bol) \ ++ { \ ++ if ( ! YY_CURRENT_BUFFER ){\ ++ yyensure_buffer_stack (); \ ++ YY_CURRENT_BUFFER_LVALUE = \ ++ yy_create_buffer( yyin, YY_BUF_SIZE ); \ ++ } \ ++ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ ++ } ++#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) ++ ++/* Begin user sect3 */ ++ ++#define yywrap() (/*CONSTCOND*/1) ++#define YY_SKIP_YYWRAP ++typedef flex_uint8_t YY_CHAR; ++ ++FILE *yyin = NULL, *yyout = NULL; ++ ++typedef int yy_state_type; ++ ++extern int yylineno; ++int yylineno = 1; ++ ++extern char *yytext; ++#ifdef yytext_ptr ++#undef yytext_ptr ++#endif ++#define yytext_ptr yytext ++ ++static yy_state_type yy_get_previous_state ( void ); ++static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); ++static int yy_get_next_buffer ( void ); ++static void yynoreturn yy_fatal_error ( const char* msg ); ++ ++/* Done after the current pattern has been matched and before the ++ * corresponding action - sets up yytext. ++ */ ++#define YY_DO_BEFORE_ACTION \ ++ (yytext_ptr) = yy_bp; \ ++ yyleng = (int) (yy_cp - yy_bp); \ ++ (yy_hold_char) = *yy_cp; \ ++ *yy_cp = '\0'; \ ++ (yy_c_buf_p) = yy_cp; ++#define YY_NUM_RULES 16 ++#define YY_END_OF_BUFFER 17 ++/* This struct is not used in this scanner, ++ but its presence is necessary. */ ++struct yy_trans_info ++ { ++ flex_int32_t yy_verify; ++ flex_int32_t yy_nxt; ++ }; ++static const flex_int16_t yy_accept[40] = ++ { 0, ++ 0, 0, 17, 15, 6, 16, 15, 5, 15, 1, ++ 1, 15, 15, 15, 15, 15, 6, 14, 5, 9, ++ 4, 0, 0, 0, 5, 5, 1, 0, 0, 8, ++ 11, 13, 12, 7, 10, 3, 2, 0, 0 ++ } ; ++ ++static const YY_CHAR yy_ec[256] = ++ { 0, ++ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, ++ 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 2, 4, 1, 1, 5, 1, 6, 1, 1, ++ 1, 1, 1, 1, 1, 5, 1, 7, 8, 9, ++ 9, 9, 9, 9, 9, 10, 10, 11, 1, 12, ++ 13, 14, 1, 1, 15, 16, 15, 15, 15, 15, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 17, 5, 5, ++ 1, 1, 1, 1, 5, 1, 15, 18, 15, 15, ++ ++ 15, 19, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 17, ++ 5, 5, 1, 20, 1, 1, 1, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ++ 5, 5, 5, 5, 5 ++ } ; ++ ++static const YY_CHAR yy_meta[21] = ++ { 0, ++ 1, 1, 1, 1, 2, 1, 3, 3, 3, 3, ++ 1, 1, 1, 1, 4, 4, 2, 5, 5, 1 ++ } ; ++ ++static const flex_int16_t yy_base[45] = ++ { 0, ++ 0, 0, 74, 75, 71, 75, 59, 0, 65, 14, ++ 7, 63, 15, 56, 21, 47, 59, 75, 0, 75, ++ 20, 32, 35, 0, 37, 75, 28, 30, 32, 75, ++ 75, 75, 75, 75, 75, 45, 0, 36, 75, 54, ++ 57, 26, 60, 63 ++ } ; ++ ++static const flex_int16_t yy_def[45] = ++ { 0, ++ 39, 1, 39, 39, 39, 39, 39, 40, 39, 39, ++ 41, 42, 39, 39, 39, 39, 39, 39, 40, 39, ++ 10, 21, 39, 43, 39, 39, 41, 39, 44, 39, ++ 39, 39, 39, 39, 39, 39, 43, 44, 0, 39, ++ 39, 39, 39, 39 ++ } ; ++ ++static const flex_int16_t yy_nxt[96] = ++ { 0, ++ 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, ++ 12, 13, 14, 15, 8, 8, 8, 8, 8, 16, ++ 21, 21, 21, 22, 26, 26, 30, 31, 29, 23, ++ 24, 25, 26, 33, 34, 39, 39, 26, 22, 22, ++ 22, 36, 36, 36, 36, 26, 26, 26, 26, 26, ++ 26, 36, 36, 26, 26, 19, 19, 19, 19, 27, ++ 17, 27, 37, 37, 37, 38, 35, 38, 32, 28, ++ 20, 18, 17, 39, 3, 39, 39, 39, 39, 39, ++ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, ++ 39, 39, 39, 39, 39 ++ ++ } ; ++ ++static const flex_int16_t yy_chk[96] = ++ { 0, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 10, 10, 10, 10, 11, 11, 13, 13, 42, 10, ++ 10, 10, 10, 15, 15, 21, 21, 21, 22, 22, ++ 22, 23, 23, 25, 25, 27, 27, 28, 28, 29, ++ 29, 36, 36, 38, 38, 40, 40, 40, 40, 41, ++ 17, 41, 43, 43, 43, 44, 16, 44, 14, 12, ++ 9, 7, 5, 3, 39, 39, 39, 39, 39, 39, ++ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, ++ 39, 39, 39, 39, 39 ++ ++ } ; ++ ++static yy_state_type yy_last_accepting_state; ++static char *yy_last_accepting_cpos; ++ ++extern int yy_flex_debug; ++int yy_flex_debug = 0; ++ ++/* The intent behind this definition is that it'll catch ++ * any uses of REJECT which flex missed. ++ */ ++#define REJECT reject_used_but_not_detected ++#define yymore() yymore_used_but_not_detected ++#define YY_MORE_ADJ 0 ++#define YY_RESTORE_YY_MORE_OFFSET ++char *yytext; ++#line 1 "./config/loongarch-lex.l" ++#line 3 "./config/loongarch-lex.l" ++#include "as.h" ++#include "loongarch-parse.h" ++#line 479 "loongarch-lex.c" ++/* We consider anything greater than \x7f to be a "letter" for UTF-8 ++ support. See the lex_type array in ../read.c. */ ++#line 482 "loongarch-lex.c" ++ ++#define INITIAL 0 ++ ++#ifndef YY_NO_UNISTD_H ++/* Special case for "unistd.h", since it is non-ANSI. We include it way ++ * down here because we want the user's section 1 to have been scanned first. ++ * The user has a chance to override it with an option. ++ */ ++#include ++#endif ++ ++#ifndef YY_EXTRA_TYPE ++#define YY_EXTRA_TYPE void * ++#endif ++ ++static int yy_init_globals ( void ); ++ ++/* Accessor methods to globals. ++ These are made visible to non-reentrant scanners for convenience. */ ++ ++int yylex_destroy ( void ); ++ ++int yyget_debug ( void ); ++ ++void yyset_debug ( int debug_flag ); ++ ++YY_EXTRA_TYPE yyget_extra ( void ); ++ ++void yyset_extra ( YY_EXTRA_TYPE user_defined ); ++ ++FILE *yyget_in ( void ); ++ ++void yyset_in ( FILE * _in_str ); ++ ++FILE *yyget_out ( void ); ++ ++void yyset_out ( FILE * _out_str ); ++ ++ int yyget_leng ( void ); ++ ++char *yyget_text ( void ); ++ ++int yyget_lineno ( void ); ++ ++void yyset_lineno ( int _line_number ); ++ ++/* Macros after this point can all be overridden by user definitions in ++ * section 1. ++ */ ++ ++#ifndef YY_SKIP_YYWRAP ++#ifdef __cplusplus ++extern "C" int yywrap ( void ); ++#else ++extern int yywrap ( void ); ++#endif ++#endif ++ ++#ifndef YY_NO_UNPUT ++ ++ static void yyunput ( int c, char *buf_ptr ); ++ ++#endif ++ ++#ifndef yytext_ptr ++static void yy_flex_strncpy ( char *, const char *, int ); ++#endif ++ ++#ifdef YY_NEED_STRLEN ++static int yy_flex_strlen ( const char * ); ++#endif ++ ++#ifndef YY_NO_INPUT ++#ifdef __cplusplus ++static int yyinput ( void ); ++#else ++static int input ( void ); ++#endif ++ ++#endif ++ ++/* Amount of stuff to slurp up with each read. */ ++#ifndef YY_READ_BUF_SIZE ++#ifdef __ia64__ ++/* On IA-64, the buffer size is 16k, not 8k */ ++#define YY_READ_BUF_SIZE 16384 ++#else ++#define YY_READ_BUF_SIZE 8192 ++#endif /* __ia64__ */ ++#endif ++ ++/* Copy whatever the last rule matched to the standard output. */ ++#ifndef ECHO ++/* This used to be an fputs(), but since the string might contain NUL's, ++ * we now use fwrite(). ++ */ ++#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) ++#endif ++ ++/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, ++ * is returned in "result". ++ */ ++#ifndef YY_INPUT ++#define YY_INPUT(buf,result,max_size) \ ++ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ ++ { \ ++ int c = '*'; \ ++ int n; \ ++ for ( n = 0; n < max_size && \ ++ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ ++ buf[n] = (char) c; \ ++ if ( c == '\n' ) \ ++ buf[n++] = (char) c; \ ++ if ( c == EOF && ferror( yyin ) ) \ ++ YY_FATAL_ERROR( "input in flex scanner failed" ); \ ++ result = n; \ ++ } \ ++ else \ ++ { \ ++ errno=0; \ ++ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ ++ { \ ++ if( errno != EINTR) \ ++ { \ ++ YY_FATAL_ERROR( "input in flex scanner failed" ); \ ++ break; \ ++ } \ ++ errno=0; \ ++ clearerr(yyin); \ ++ } \ ++ }\ ++\ ++ ++#endif ++ ++/* No semi-colon after return; correct usage is to write "yyterminate();" - ++ * we don't want an extra ';' after the "return" because that will cause ++ * some compilers to complain about unreachable statements. ++ */ ++#ifndef yyterminate ++#define yyterminate() return YY_NULL ++#endif ++ ++/* Number of entries by which start-condition stack grows. */ ++#ifndef YY_START_STACK_INCR ++#define YY_START_STACK_INCR 25 ++#endif ++ ++/* Report a fatal error. */ ++#ifndef YY_FATAL_ERROR ++#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) ++#endif ++ ++/* end tables serialization structures and prototypes */ ++ ++/* Default declaration of generated scanner - a define so the user can ++ * easily add parameters. ++ */ ++#ifndef YY_DECL ++#define YY_DECL_IS_OURS 1 ++ ++extern int yylex (void); ++ ++#define YY_DECL int yylex (void) ++#endif /* !YY_DECL */ ++ ++/* Code executed at the beginning of each rule, after yytext and yyleng ++ * have been set up. ++ */ ++#ifndef YY_USER_ACTION ++#define YY_USER_ACTION ++#endif ++ ++/* Code executed at the end of each rule. */ ++#ifndef YY_BREAK ++#define YY_BREAK /*LINTED*/break; ++#endif ++ ++#define YY_RULE_SETUP \ ++ YY_USER_ACTION ++ ++/** The main scanner function which does all the work. ++ */ ++YY_DECL ++{ ++ yy_state_type yy_current_state; ++ char *yy_cp, *yy_bp; ++ int yy_act; ++ ++ if ( !(yy_init) ) ++ { ++ (yy_init) = 1; ++ ++#ifdef YY_USER_INIT ++ YY_USER_INIT; ++#endif ++ ++ if ( ! (yy_start) ) ++ (yy_start) = 1; /* first start state */ ++ ++ if ( ! yyin ) ++ yyin = stdin; ++ ++ if ( ! yyout ) ++ yyout = stdout; ++ ++ if ( ! YY_CURRENT_BUFFER ) { ++ yyensure_buffer_stack (); ++ YY_CURRENT_BUFFER_LVALUE = ++ yy_create_buffer( yyin, YY_BUF_SIZE ); ++ } ++ ++ yy_load_buffer_state( ); ++ } ++ ++ { ++#line 20 "./config/loongarch-lex.l" ++ ++ ++#line 702 "loongarch-lex.c" ++ ++ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ ++ { ++ yy_cp = (yy_c_buf_p); ++ ++ /* Support of yytext. */ ++ *yy_cp = (yy_hold_char); ++ ++ /* yy_bp points to the position in yy_ch_buf of the start of ++ * the current run. ++ */ ++ yy_bp = yy_cp; ++ ++ yy_current_state = (yy_start); ++yy_match: ++ do ++ { ++ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; ++ if ( yy_accept[yy_current_state] ) ++ { ++ (yy_last_accepting_state) = yy_current_state; ++ (yy_last_accepting_cpos) = yy_cp; ++ } ++ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) ++ { ++ yy_current_state = (int) yy_def[yy_current_state]; ++ if ( yy_current_state >= 40 ) ++ yy_c = yy_meta[yy_c]; ++ } ++ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++ ++yy_cp; ++ } ++ while ( yy_base[yy_current_state] != 75 ); ++ ++yy_find_action: ++ yy_act = yy_accept[yy_current_state]; ++ if ( yy_act == 0 ) ++ { /* have to back up */ ++ yy_cp = (yy_last_accepting_cpos); ++ yy_current_state = (yy_last_accepting_state); ++ yy_act = yy_accept[yy_current_state]; ++ } ++ ++ YY_DO_BEFORE_ACTION; ++ ++do_action: /* This label is used only to access EOF actions. */ ++ ++ switch ( yy_act ) ++ { /* beginning of action switch */ ++ case 0: /* must back up */ ++ /* undo the effects of YY_DO_BEFORE_ACTION */ ++ *yy_cp = (yy_hold_char); ++ yy_cp = (yy_last_accepting_cpos); ++ yy_current_state = (yy_last_accepting_state); ++ goto yy_find_action; ++ ++case 1: ++YY_RULE_SETUP ++#line 22 "./config/loongarch-lex.l" ++{ yylval.imm = strtoull (yytext, 0, 0); return INTEGER; } ++ YY_BREAK ++case 2: ++YY_RULE_SETUP ++#line 23 "./config/loongarch-lex.l" ++{ yylval.imm = strtoull (yytext + 2, 0, 16); return INTEGER; } ++ YY_BREAK ++case 3: ++YY_RULE_SETUP ++#line 24 "./config/loongarch-lex.l" ++{ yylval.imm = strtoull (yytext + 2, 0, 2); return INTEGER; } ++ YY_BREAK ++case 4: ++YY_RULE_SETUP ++#line 25 "./config/loongarch-lex.l" ++{ yylval.imm = strtoull (yytext + 1, 0, 8); return INTEGER; } ++ YY_BREAK ++case 5: ++YY_RULE_SETUP ++#line 26 "./config/loongarch-lex.l" ++{ yylval.c_str = strdup (yytext);return IDENTIFIER; } ++ YY_BREAK ++case 6: ++YY_RULE_SETUP ++#line 27 "./config/loongarch-lex.l" ++{ } ++ YY_BREAK ++case 7: ++YY_RULE_SETUP ++#line 29 "./config/loongarch-lex.l" ++{ return RIGHT_OP; } ++ YY_BREAK ++case 8: ++YY_RULE_SETUP ++#line 30 "./config/loongarch-lex.l" ++{ return LEFT_OP; } ++ YY_BREAK ++case 9: ++YY_RULE_SETUP ++#line 31 "./config/loongarch-lex.l" ++{ return AND_OP; } ++ YY_BREAK ++case 10: ++YY_RULE_SETUP ++#line 32 "./config/loongarch-lex.l" ++{ return OR_OP; } ++ YY_BREAK ++case 11: ++YY_RULE_SETUP ++#line 33 "./config/loongarch-lex.l" ++{ return LE_OP; } ++ YY_BREAK ++case 12: ++YY_RULE_SETUP ++#line 34 "./config/loongarch-lex.l" ++{ return GE_OP; } ++ YY_BREAK ++case 13: ++YY_RULE_SETUP ++#line 35 "./config/loongarch-lex.l" ++{ return EQ_OP; } ++ YY_BREAK ++case 14: ++YY_RULE_SETUP ++#line 36 "./config/loongarch-lex.l" ++{ return NE_OP; } ++ YY_BREAK ++case 15: ++YY_RULE_SETUP ++#line 37 "./config/loongarch-lex.l" ++{ return yytext[0];} ++ YY_BREAK ++case 16: ++YY_RULE_SETUP ++#line 39 "./config/loongarch-lex.l" ++ECHO; ++ YY_BREAK ++#line 839 "loongarch-lex.c" ++case YY_STATE_EOF(INITIAL): ++ yyterminate(); ++ ++ case YY_END_OF_BUFFER: ++ { ++ /* Amount of text matched not including the EOB char. */ ++ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; ++ ++ /* Undo the effects of YY_DO_BEFORE_ACTION. */ ++ *yy_cp = (yy_hold_char); ++ YY_RESTORE_YY_MORE_OFFSET ++ ++ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) ++ { ++ /* We're scanning a new file or input source. It's ++ * possible that this happened because the user ++ * just pointed yyin at a new source and called ++ * yylex(). If so, then we have to assure ++ * consistency between YY_CURRENT_BUFFER and our ++ * globals. Here is the right place to do so, because ++ * this is the first action (other than possibly a ++ * back-up) that will match for the new input source. ++ */ ++ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; ++ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; ++ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; ++ } ++ ++ /* Note that here we test for yy_c_buf_p "<=" to the position ++ * of the first EOB in the buffer, since yy_c_buf_p will ++ * already have been incremented past the NUL character ++ * (since all states make transitions on EOB to the ++ * end-of-buffer state). Contrast this with the test ++ * in input(). ++ */ ++ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) ++ { /* This was really a NUL. */ ++ yy_state_type yy_next_state; ++ ++ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; ++ ++ yy_current_state = yy_get_previous_state( ); ++ ++ /* Okay, we're now positioned to make the NUL ++ * transition. We couldn't have ++ * yy_get_previous_state() go ahead and do it ++ * for us because it doesn't know how to deal ++ * with the possibility of jamming (and we don't ++ * want to build jamming into it because then it ++ * will run more slowly). ++ */ ++ ++ yy_next_state = yy_try_NUL_trans( yy_current_state ); ++ ++ yy_bp = (yytext_ptr) + YY_MORE_ADJ; ++ ++ if ( yy_next_state ) ++ { ++ /* Consume the NUL. */ ++ yy_cp = ++(yy_c_buf_p); ++ yy_current_state = yy_next_state; ++ goto yy_match; ++ } ++ ++ else ++ { ++ yy_cp = (yy_c_buf_p); ++ goto yy_find_action; ++ } ++ } ++ ++ else switch ( yy_get_next_buffer( ) ) ++ { ++ case EOB_ACT_END_OF_FILE: ++ { ++ (yy_did_buffer_switch_on_eof) = 0; ++ ++ if ( yywrap( ) ) ++ { ++ /* Note: because we've taken care in ++ * yy_get_next_buffer() to have set up ++ * yytext, we can now set up ++ * yy_c_buf_p so that if some total ++ * hoser (like flex itself) wants to ++ * call the scanner after we return the ++ * YY_NULL, it'll still work - another ++ * YY_NULL will get returned. ++ */ ++ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; ++ ++ yy_act = YY_STATE_EOF(YY_START); ++ goto do_action; ++ } ++ ++ else ++ { ++ if ( ! (yy_did_buffer_switch_on_eof) ) ++ YY_NEW_FILE; ++ } ++ break; ++ } ++ ++ case EOB_ACT_CONTINUE_SCAN: ++ (yy_c_buf_p) = ++ (yytext_ptr) + yy_amount_of_matched_text; ++ ++ yy_current_state = yy_get_previous_state( ); ++ ++ yy_cp = (yy_c_buf_p); ++ yy_bp = (yytext_ptr) + YY_MORE_ADJ; ++ goto yy_match; ++ ++ case EOB_ACT_LAST_MATCH: ++ (yy_c_buf_p) = ++ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; ++ ++ yy_current_state = yy_get_previous_state( ); ++ ++ yy_cp = (yy_c_buf_p); ++ yy_bp = (yytext_ptr) + YY_MORE_ADJ; ++ goto yy_find_action; ++ } ++ break; ++ } ++ ++ default: ++ YY_FATAL_ERROR( ++ "fatal flex scanner internal error--no action found" ); ++ } /* end of action switch */ ++ } /* end of scanning one token */ ++ } /* end of user's declarations */ ++} /* end of yylex */ ++ ++/* yy_get_next_buffer - try to read in a new buffer ++ * ++ * Returns a code representing an action: ++ * EOB_ACT_LAST_MATCH - ++ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position ++ * EOB_ACT_END_OF_FILE - end of file ++ */ ++static int yy_get_next_buffer (void) ++{ ++ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; ++ char *source = (yytext_ptr); ++ int number_to_move, i; ++ int ret_val; ++ ++ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) ++ YY_FATAL_ERROR( ++ "fatal flex scanner internal error--end of buffer missed" ); ++ ++ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) ++ { /* Don't try to fill the buffer, so this is an EOF. */ ++ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) ++ { ++ /* We matched a single character, the EOB, so ++ * treat this as a final EOF. ++ */ ++ return EOB_ACT_END_OF_FILE; ++ } ++ ++ else ++ { ++ /* We matched some text prior to the EOB, first ++ * process it. ++ */ ++ return EOB_ACT_LAST_MATCH; ++ } ++ } ++ ++ /* Try to read more data. */ ++ ++ /* First move last chars to start of buffer. */ ++ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); ++ ++ for ( i = 0; i < number_to_move; ++i ) ++ *(dest++) = *(source++); ++ ++ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) ++ /* don't do the read, it's not guaranteed to return an EOF, ++ * just force an EOF ++ */ ++ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; ++ ++ else ++ { ++ int num_to_read = ++ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; ++ ++ while ( num_to_read <= 0 ) ++ { /* Not enough room in the buffer - grow it. */ ++ ++ /* just a shorter name for the current buffer */ ++ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; ++ ++ int yy_c_buf_p_offset = ++ (int) ((yy_c_buf_p) - b->yy_ch_buf); ++ ++ if ( b->yy_is_our_buffer ) ++ { ++ int new_size = b->yy_buf_size * 2; ++ ++ if ( new_size <= 0 ) ++ b->yy_buf_size += b->yy_buf_size / 8; ++ else ++ b->yy_buf_size *= 2; ++ ++ b->yy_ch_buf = (char *) ++ /* Include room in for 2 EOB chars. */ ++ yyrealloc( (void *) b->yy_ch_buf, ++ (yy_size_t) (b->yy_buf_size + 2) ); ++ } ++ else ++ /* Can't grow it, we don't own it. */ ++ b->yy_ch_buf = NULL; ++ ++ if ( ! b->yy_ch_buf ) ++ YY_FATAL_ERROR( ++ "fatal error - scanner input buffer overflow" ); ++ ++ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; ++ ++ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - ++ number_to_move - 1; ++ ++ } ++ ++ if ( num_to_read > YY_READ_BUF_SIZE ) ++ num_to_read = YY_READ_BUF_SIZE; ++ ++ /* Read in more data. */ ++ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), ++ (yy_n_chars), num_to_read ); ++ ++ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); ++ } ++ ++ if ( (yy_n_chars) == 0 ) ++ { ++ if ( number_to_move == YY_MORE_ADJ ) ++ { ++ ret_val = EOB_ACT_END_OF_FILE; ++ yyrestart( yyin ); ++ } ++ ++ else ++ { ++ ret_val = EOB_ACT_LAST_MATCH; ++ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = ++ YY_BUFFER_EOF_PENDING; ++ } ++ } ++ ++ else ++ ret_val = EOB_ACT_CONTINUE_SCAN; ++ ++ if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { ++ /* Extend the array by 50%, plus the number we really need. */ ++ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); ++ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( ++ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); ++ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) ++ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); ++ /* "- 2" to take care of EOB's */ ++ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); ++ } ++ ++ (yy_n_chars) += number_to_move; ++ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; ++ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; ++ ++ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; ++ ++ return ret_val; ++} ++ ++/* yy_get_previous_state - get the state just before the EOB char was reached */ ++ ++ static yy_state_type yy_get_previous_state (void) ++{ ++ yy_state_type yy_current_state; ++ char *yy_cp; ++ ++ yy_current_state = (yy_start); ++ ++ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) ++ { ++ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); ++ if ( yy_accept[yy_current_state] ) ++ { ++ (yy_last_accepting_state) = yy_current_state; ++ (yy_last_accepting_cpos) = yy_cp; ++ } ++ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) ++ { ++ yy_current_state = (int) yy_def[yy_current_state]; ++ if ( yy_current_state >= 40 ) ++ yy_c = yy_meta[yy_c]; ++ } ++ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++ } ++ ++ return yy_current_state; ++} ++ ++/* yy_try_NUL_trans - try to make a transition on the NUL character ++ * ++ * synopsis ++ * next_state = yy_try_NUL_trans( current_state ); ++ */ ++ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) ++{ ++ int yy_is_jam; ++ char *yy_cp = (yy_c_buf_p); ++ ++ YY_CHAR yy_c = 1; ++ if ( yy_accept[yy_current_state] ) ++ { ++ (yy_last_accepting_state) = yy_current_state; ++ (yy_last_accepting_cpos) = yy_cp; ++ } ++ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) ++ { ++ yy_current_state = (int) yy_def[yy_current_state]; ++ if ( yy_current_state >= 40 ) ++ yy_c = yy_meta[yy_c]; ++ } ++ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++ yy_is_jam = (yy_current_state == 39); ++ ++ return yy_is_jam ? 0 : yy_current_state; ++} ++ ++#ifndef YY_NO_UNPUT ++ ++ static void yyunput (int c, char * yy_bp ) ++{ ++ char *yy_cp; ++ ++ yy_cp = (yy_c_buf_p); ++ ++ /* undo effects of setting up yytext */ ++ *yy_cp = (yy_hold_char); ++ ++ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) ++ { /* need to shift things up to make room */ ++ /* +2 for EOB chars. */ ++ int number_to_move = (yy_n_chars) + 2; ++ char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ ++ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; ++ char *source = ++ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; ++ ++ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) ++ *--dest = *--source; ++ ++ yy_cp += (int) (dest - source); ++ yy_bp += (int) (dest - source); ++ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = ++ (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; ++ ++ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) ++ YY_FATAL_ERROR( "flex scanner push-back overflow" ); ++ } ++ ++ *--yy_cp = (char) c; ++ ++ (yytext_ptr) = yy_bp; ++ (yy_hold_char) = *yy_cp; ++ (yy_c_buf_p) = yy_cp; ++} ++ ++#endif ++ ++#ifndef YY_NO_INPUT ++#ifdef __cplusplus ++ static int yyinput (void) ++#else ++ static int input (void) ++#endif ++ ++{ ++ int c; ++ ++ *(yy_c_buf_p) = (yy_hold_char); ++ ++ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) ++ { ++ /* yy_c_buf_p now points to the character we want to return. ++ * If this occurs *before* the EOB characters, then it's a ++ * valid NUL; if not, then we've hit the end of the buffer. ++ */ ++ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) ++ /* This was really a NUL. */ ++ *(yy_c_buf_p) = '\0'; ++ ++ else ++ { /* need more input */ ++ int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); ++ ++(yy_c_buf_p); ++ ++ switch ( yy_get_next_buffer( ) ) ++ { ++ case EOB_ACT_LAST_MATCH: ++ /* This happens because yy_g_n_b() ++ * sees that we've accumulated a ++ * token and flags that we need to ++ * try matching the token before ++ * proceeding. But for input(), ++ * there's no matching to consider. ++ * So convert the EOB_ACT_LAST_MATCH ++ * to EOB_ACT_END_OF_FILE. ++ */ ++ ++ /* Reset buffer status. */ ++ yyrestart( yyin ); ++ ++ /*FALLTHROUGH*/ ++ ++ case EOB_ACT_END_OF_FILE: ++ { ++ if ( yywrap( ) ) ++ return 0; ++ ++ if ( ! (yy_did_buffer_switch_on_eof) ) ++ YY_NEW_FILE; ++#ifdef __cplusplus ++ return yyinput(); ++#else ++ return input(); ++#endif ++ } ++ ++ case EOB_ACT_CONTINUE_SCAN: ++ (yy_c_buf_p) = (yytext_ptr) + offset; ++ break; ++ } ++ } ++ } ++ ++ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ ++ *(yy_c_buf_p) = '\0'; /* preserve yytext */ ++ (yy_hold_char) = *++(yy_c_buf_p); ++ ++ return c; ++} ++#endif /* ifndef YY_NO_INPUT */ ++ ++/** Immediately switch to a different input stream. ++ * @param input_file A readable stream. ++ * ++ * @note This function does not reset the start condition to @c INITIAL . ++ */ ++ void yyrestart (FILE * input_file ) ++{ ++ ++ if ( ! YY_CURRENT_BUFFER ){ ++ yyensure_buffer_stack (); ++ YY_CURRENT_BUFFER_LVALUE = ++ yy_create_buffer( yyin, YY_BUF_SIZE ); ++ } ++ ++ yy_init_buffer( YY_CURRENT_BUFFER, input_file ); ++ yy_load_buffer_state( ); ++} ++ ++/** Switch to a different input buffer. ++ * @param new_buffer The new input buffer. ++ * ++ */ ++ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) ++{ ++ ++ /* TODO. We should be able to replace this entire function body ++ * with ++ * yypop_buffer_state(); ++ * yypush_buffer_state(new_buffer); ++ */ ++ yyensure_buffer_stack (); ++ if ( YY_CURRENT_BUFFER == new_buffer ) ++ return; ++ ++ if ( YY_CURRENT_BUFFER ) ++ { ++ /* Flush out information for old buffer. */ ++ *(yy_c_buf_p) = (yy_hold_char); ++ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); ++ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); ++ } ++ ++ YY_CURRENT_BUFFER_LVALUE = new_buffer; ++ yy_load_buffer_state( ); ++ ++ /* We don't actually know whether we did this switch during ++ * EOF (yywrap()) processing, but the only time this flag ++ * is looked at is after yywrap() is called, so it's safe ++ * to go ahead and always set it. ++ */ ++ (yy_did_buffer_switch_on_eof) = 1; ++} ++ ++static void yy_load_buffer_state (void) ++{ ++ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; ++ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; ++ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; ++ (yy_hold_char) = *(yy_c_buf_p); ++} ++ ++/** Allocate and initialize an input buffer state. ++ * @param file A readable stream. ++ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. ++ * ++ * @return the allocated buffer state. ++ */ ++ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) ++{ ++ YY_BUFFER_STATE b; ++ ++ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); ++ if ( ! b ) ++ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); ++ ++ b->yy_buf_size = size; ++ ++ /* yy_ch_buf has to be 2 characters longer than the size given because ++ * we need to put in 2 end-of-buffer characters. ++ */ ++ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); ++ if ( ! b->yy_ch_buf ) ++ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); ++ ++ b->yy_is_our_buffer = 1; ++ ++ yy_init_buffer( b, file ); ++ ++ return b; ++} ++ ++/** Destroy the buffer. ++ * @param b a buffer created with yy_create_buffer() ++ * ++ */ ++ void yy_delete_buffer (YY_BUFFER_STATE b ) ++{ ++ ++ if ( ! b ) ++ return; ++ ++ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ ++ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; ++ ++ if ( b->yy_is_our_buffer ) ++ yyfree( (void *) b->yy_ch_buf ); ++ ++ yyfree( (void *) b ); ++} ++ ++/* Initializes or reinitializes a buffer. ++ * This function is sometimes called more than once on the same buffer, ++ * such as during a yyrestart() or at EOF. ++ */ ++ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) ++ ++{ ++ int oerrno = errno; ++ ++ yy_flush_buffer( b ); ++ ++ b->yy_input_file = file; ++ b->yy_fill_buffer = 1; ++ ++ /* If b is the current buffer, then yy_init_buffer was _probably_ ++ * called from yyrestart() or through yy_get_next_buffer. ++ * In that case, we don't want to reset the lineno or column. ++ */ ++ if (b != YY_CURRENT_BUFFER){ ++ b->yy_bs_lineno = 1; ++ b->yy_bs_column = 0; ++ } ++ ++ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; ++ ++ errno = oerrno; ++} ++ ++/** Discard all buffered characters. On the next scan, YY_INPUT will be called. ++ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. ++ * ++ */ ++ void yy_flush_buffer (YY_BUFFER_STATE b ) ++{ ++ if ( ! b ) ++ return; ++ ++ b->yy_n_chars = 0; ++ ++ /* We always need two end-of-buffer characters. The first causes ++ * a transition to the end-of-buffer state. The second causes ++ * a jam in that state. ++ */ ++ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; ++ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; ++ ++ b->yy_buf_pos = &b->yy_ch_buf[0]; ++ ++ b->yy_at_bol = 1; ++ b->yy_buffer_status = YY_BUFFER_NEW; ++ ++ if ( b == YY_CURRENT_BUFFER ) ++ yy_load_buffer_state( ); ++} ++ ++/** Pushes the new state onto the stack. The new state becomes ++ * the current state. This function will allocate the stack ++ * if necessary. ++ * @param new_buffer The new state. ++ * ++ */ ++void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) ++{ ++ if (new_buffer == NULL) ++ return; ++ ++ yyensure_buffer_stack(); ++ ++ /* This block is copied from yy_switch_to_buffer. */ ++ if ( YY_CURRENT_BUFFER ) ++ { ++ /* Flush out information for old buffer. */ ++ *(yy_c_buf_p) = (yy_hold_char); ++ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); ++ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); ++ } ++ ++ /* Only push if top exists. Otherwise, replace top. */ ++ if (YY_CURRENT_BUFFER) ++ (yy_buffer_stack_top)++; ++ YY_CURRENT_BUFFER_LVALUE = new_buffer; ++ ++ /* copied from yy_switch_to_buffer. */ ++ yy_load_buffer_state( ); ++ (yy_did_buffer_switch_on_eof) = 1; ++} ++ ++/** Removes and deletes the top of the stack, if present. ++ * The next element becomes the new top. ++ * ++ */ ++void yypop_buffer_state (void) ++{ ++ if (!YY_CURRENT_BUFFER) ++ return; ++ ++ yy_delete_buffer(YY_CURRENT_BUFFER ); ++ YY_CURRENT_BUFFER_LVALUE = NULL; ++ if ((yy_buffer_stack_top) > 0) ++ --(yy_buffer_stack_top); ++ ++ if (YY_CURRENT_BUFFER) { ++ yy_load_buffer_state( ); ++ (yy_did_buffer_switch_on_eof) = 1; ++ } ++} ++ ++/* Allocates the stack if it does not exist. ++ * Guarantees space for at least one push. ++ */ ++static void yyensure_buffer_stack (void) ++{ ++ yy_size_t num_to_alloc; ++ ++ if (!(yy_buffer_stack)) { ++ ++ /* First allocation is just for 2 elements, since we don't know if this ++ * scanner will even need a stack. We use 2 instead of 1 to avoid an ++ * immediate realloc on the next call. ++ */ ++ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ ++ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc ++ (num_to_alloc * sizeof(struct yy_buffer_state*) ++ ); ++ if ( ! (yy_buffer_stack) ) ++ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); ++ ++ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); ++ ++ (yy_buffer_stack_max) = num_to_alloc; ++ (yy_buffer_stack_top) = 0; ++ return; ++ } ++ ++ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ ++ ++ /* Increase the buffer to prepare for a possible push. */ ++ yy_size_t grow_size = 8 /* arbitrary grow size */; ++ ++ num_to_alloc = (yy_buffer_stack_max) + grow_size; ++ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ++ ((yy_buffer_stack), ++ num_to_alloc * sizeof(struct yy_buffer_state*) ++ ); ++ if ( ! (yy_buffer_stack) ) ++ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); ++ ++ /* zero only the new slots.*/ ++ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); ++ (yy_buffer_stack_max) = num_to_alloc; ++ } ++} ++ ++/** Setup the input buffer state to scan directly from a user-specified character buffer. ++ * @param base the character buffer ++ * @param size the size in bytes of the character buffer ++ * ++ * @return the newly allocated buffer state object. ++ */ ++YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) ++{ ++ YY_BUFFER_STATE b; ++ ++ if ( size < 2 || ++ base[size-2] != YY_END_OF_BUFFER_CHAR || ++ base[size-1] != YY_END_OF_BUFFER_CHAR ) ++ /* They forgot to leave room for the EOB's. */ ++ return NULL; ++ ++ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); ++ if ( ! b ) ++ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); ++ ++ b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ ++ b->yy_buf_pos = b->yy_ch_buf = base; ++ b->yy_is_our_buffer = 0; ++ b->yy_input_file = NULL; ++ b->yy_n_chars = b->yy_buf_size; ++ b->yy_is_interactive = 0; ++ b->yy_at_bol = 1; ++ b->yy_fill_buffer = 0; ++ b->yy_buffer_status = YY_BUFFER_NEW; ++ ++ yy_switch_to_buffer( b ); ++ ++ return b; ++} ++ ++/** Setup the input buffer state to scan a string. The next call to yylex() will ++ * scan from a @e copy of @a str. ++ * @param yystr a NUL-terminated string to scan ++ * ++ * @return the newly allocated buffer state object. ++ * @note If you want to scan bytes that may contain NUL values, then use ++ * yy_scan_bytes() instead. ++ */ ++YY_BUFFER_STATE yy_scan_string (const char * yystr ) ++{ ++ ++ return yy_scan_bytes( yystr, (int) strlen(yystr) ); ++} ++ ++/** Setup the input buffer state to scan the given bytes. The next call to yylex() will ++ * scan from a @e copy of @a bytes. ++ * @param yybytes the byte buffer to scan ++ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. ++ * ++ * @return the newly allocated buffer state object. ++ */ ++YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) ++{ ++ YY_BUFFER_STATE b; ++ char *buf; ++ yy_size_t n; ++ int i; ++ ++ /* Get memory for full buffer, including space for trailing EOB's. */ ++ n = (yy_size_t) (_yybytes_len + 2); ++ buf = (char *) yyalloc( n ); ++ if ( ! buf ) ++ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); ++ ++ for ( i = 0; i < _yybytes_len; ++i ) ++ buf[i] = yybytes[i]; ++ ++ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; ++ ++ b = yy_scan_buffer( buf, n ); ++ if ( ! b ) ++ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); ++ ++ /* It's okay to grow etc. this buffer, and we should throw it ++ * away when we're done. ++ */ ++ b->yy_is_our_buffer = 1; ++ ++ return b; ++} ++ ++#ifndef YY_EXIT_FAILURE ++#define YY_EXIT_FAILURE 2 ++#endif ++ ++static void yynoreturn yy_fatal_error (const char* msg ) ++{ ++ fprintf( stderr, "%s\n", msg ); ++ exit( YY_EXIT_FAILURE ); ++} ++ ++/* Redefine yyless() so it works in section 3 code. */ ++ ++#undef yyless ++#define yyless(n) \ ++ do \ ++ { \ ++ /* Undo effects of setting up yytext. */ \ ++ int yyless_macro_arg = (n); \ ++ YY_LESS_LINENO(yyless_macro_arg);\ ++ yytext[yyleng] = (yy_hold_char); \ ++ (yy_c_buf_p) = yytext + yyless_macro_arg; \ ++ (yy_hold_char) = *(yy_c_buf_p); \ ++ *(yy_c_buf_p) = '\0'; \ ++ yyleng = yyless_macro_arg; \ ++ } \ ++ while ( 0 ) ++ ++/* Accessor methods (get/set functions) to struct members. */ ++ ++/** Get the current line number. ++ * ++ */ ++int yyget_lineno (void) ++{ ++ ++ return yylineno; ++} ++ ++/** Get the input stream. ++ * ++ */ ++FILE *yyget_in (void) ++{ ++ return yyin; ++} ++ ++/** Get the output stream. ++ * ++ */ ++FILE *yyget_out (void) ++{ ++ return yyout; ++} ++ ++/** Get the length of the current token. ++ * ++ */ ++int yyget_leng (void) ++{ ++ return yyleng; ++} ++ ++/** Get the current token. ++ * ++ */ ++ ++char *yyget_text (void) ++{ ++ return yytext; ++} ++ ++/** Set the current line number. ++ * @param _line_number line number ++ * ++ */ ++void yyset_lineno (int _line_number ) ++{ ++ ++ yylineno = _line_number; ++} ++ ++/** Set the input stream. This does not discard the current ++ * input buffer. ++ * @param _in_str A readable stream. ++ * ++ * @see yy_switch_to_buffer ++ */ ++void yyset_in (FILE * _in_str ) ++{ ++ yyin = _in_str ; ++} ++ ++void yyset_out (FILE * _out_str ) ++{ ++ yyout = _out_str ; ++} ++ ++int yyget_debug (void) ++{ ++ return yy_flex_debug; ++} ++ ++void yyset_debug (int _bdebug ) ++{ ++ yy_flex_debug = _bdebug ; ++} ++ ++static int yy_init_globals (void) ++{ ++ /* Initialization is the same as for the non-reentrant scanner. ++ * This function is called from yylex_destroy(), so don't allocate here. ++ */ ++ ++ (yy_buffer_stack) = NULL; ++ (yy_buffer_stack_top) = 0; ++ (yy_buffer_stack_max) = 0; ++ (yy_c_buf_p) = NULL; ++ (yy_init) = 0; ++ (yy_start) = 0; ++ ++/* Defined in main.c */ ++#ifdef YY_STDINIT ++ yyin = stdin; ++ yyout = stdout; ++#else ++ yyin = NULL; ++ yyout = NULL; ++#endif ++ ++ /* For future reference: Set errno on error, since we are called by ++ * yylex_init() ++ */ ++ return 0; ++} ++ ++/* yylex_destroy is for both reentrant and non-reentrant scanners. */ ++int yylex_destroy (void) ++{ ++ ++ /* Pop the buffer stack, destroying each element. */ ++ while(YY_CURRENT_BUFFER){ ++ yy_delete_buffer( YY_CURRENT_BUFFER ); ++ YY_CURRENT_BUFFER_LVALUE = NULL; ++ yypop_buffer_state(); ++ } ++ ++ /* Destroy the stack itself. */ ++ yyfree((yy_buffer_stack) ); ++ (yy_buffer_stack) = NULL; ++ ++ /* Reset the globals. This is important in a non-reentrant scanner so the next time ++ * yylex() is called, initialization will occur. */ ++ yy_init_globals( ); ++ ++ return 0; ++} ++ ++/* ++ * Internal utility routines. ++ */ ++ ++#ifndef yytext_ptr ++static void yy_flex_strncpy (char* s1, const char * s2, int n ) ++{ ++ ++ int i; ++ for ( i = 0; i < n; ++i ) ++ s1[i] = s2[i]; ++} ++#endif ++ ++#ifdef YY_NEED_STRLEN ++static int yy_flex_strlen (const char * s ) ++{ ++ int n; ++ for ( n = 0; s[n]; ++n ) ++ ; ++ ++ return n; ++} ++#endif ++ ++void *yyalloc (yy_size_t size ) ++{ ++ return malloc(size); ++} ++ ++void *yyrealloc (void * ptr, yy_size_t size ) ++{ ++ ++ /* The cast to (char *) in the following accommodates both ++ * implementations that use char* generic pointers, and those ++ * that use void* generic pointers. It works with the latter ++ * because both ANSI C and C++ allow castless assignment from ++ * any pointer type to void*, and deal with argument conversions ++ * as though doing an assignment. ++ */ ++ return realloc(ptr, size); ++} ++ ++void yyfree (void * ptr ) ++{ ++ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ ++} ++ ++#define YYTABLES_NAME "yytables" ++ ++#line 39 "./config/loongarch-lex.l" ++ ++ +diff --git a/gas/loongarch-parse.c b/gas/loongarch-parse.c +new file mode 100644 +index 00000000..d2c70f1d +--- /dev/null ++++ b/gas/loongarch-parse.c +@@ -0,0 +1,2006 @@ ++/* A Bison parser, made by GNU Bison 3.3.2. */ ++ ++/* Bison implementation for Yacc-like parsers in C ++ ++ Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, ++ Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* As a special exception, you may create a larger work that contains ++ part or all of the Bison parser skeleton and distribute that work ++ under terms of your choice, so long as that work isn't itself a ++ parser generator using the skeleton or a modified version thereof ++ as a parser skeleton. Alternatively, if you modify or redistribute ++ the parser skeleton itself, you may (at your option) remove this ++ special exception, which will cause the skeleton and the resulting ++ Bison output files to be licensed under the GNU General Public ++ License without this special exception. ++ ++ This special exception was added by the Free Software Foundation in ++ version 2.2 of Bison. */ ++ ++/* C LALR(1) parser skeleton written by Richard Stallman, by ++ simplifying the original so-called "semantic" parser. */ ++ ++/* All symbols defined below should begin with yy or YY, to avoid ++ infringing on user name space. This should be done even for local ++ variables, as they might otherwise be expanded by user macros. ++ There are some unavoidable exceptions within include files to ++ define necessary library symbols; they are noted "INFRINGES ON ++ USER NAME SPACE" below. */ ++ ++/* Undocumented macros, especially those whose name start with YY_, ++ are private implementation details. Do not rely on them. */ ++ ++/* Identify Bison output. */ ++#define YYBISON 1 ++ ++/* Bison version. */ ++#define YYBISON_VERSION "3.3.2" ++ ++/* Skeleton name. */ ++#define YYSKELETON_NAME "yacc.c" ++ ++/* Pure parsers. */ ++#define YYPURE 0 ++ ++/* Push parsers. */ ++#define YYPUSH 0 ++ ++/* Pull parsers. */ ++#define YYPULL 1 ++ ++ ++ ++ ++/* First part of user prologue. */ ++#line 1 "./config/loongarch-parse.y" /* yacc.c:337 */ ++ ++#include "as.h" ++#include "loongarch-parse.h" ++void yyerror(const char *s) {} ++extern int yylex (void); ++extern void yy_scan_string (const char *); ++extern void ++get_internal_label (expressionS *label_expr, ++ unsigned long label, ++ int augend); ++ ++ ++static struct reloc_info *top, *end; ++ ++static expressionS const_0 = ++{ ++ .X_op = O_constant, ++ .X_add_number = 0 ++}; ++ ++static int ++is_const (struct reloc_info *info) ++{ ++ return info->type == BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE ++ && info->value.X_op == O_constant; ++} ++ ++int ++loongarch_parse_expr (const char *expr, ++ struct reloc_info *reloc_stack_top, ++ size_t max_reloc_num, ++ size_t *reloc_num, ++ offsetT *imm) ++{ ++ int ret; ++ top = reloc_stack_top; ++ end = top + max_reloc_num; ++ yy_scan_string (expr); ++ ret = yyparse (); ++ if (ret == 0) ++ { ++ if (is_const (top - 1)) ++ *imm = (--top)->value.X_add_number; ++ else ++ *imm = 0; ++ *reloc_num = top - reloc_stack_top; ++ } ++ return ret; ++} ++ ++static void ++emit_const (offsetT imm) ++{ ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE; ++ top->value.X_op = O_constant; ++ top->value.X_add_number = imm; ++ top++; ++} ++ ++static const char * ++my_getExpression (expressionS *ep, const char *str) ++{ ++ char *save_in, *ret; ++ if (*str == ':') ++ { ++ unsigned long j; ++ char *str_1 = (char *) str; ++ str_1++; ++ j = strtol (str_1, &str_1, 10); ++ get_internal_label (ep, j, *str_1 == 'f'); ++ return NULL; ++ } ++ save_in = input_line_pointer; ++ input_line_pointer = (char *)str; ++ expression (ep); ++ ret = input_line_pointer; ++ input_line_pointer = save_in; ++ return ret; ++} ++ ++static void ++reloc (const char *op_c_str, const char *id_c_str, offsetT addend) ++{ ++ expressionS id_sym_expr; ++ ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ ++ if (id_c_str) ++ { ++ my_getExpression (&id_sym_expr, id_c_str); ++ id_sym_expr.X_add_number += addend; ++ } ++ else ++ { ++ id_sym_expr.X_op = O_constant; ++ id_sym_expr.X_add_number = addend; ++ } ++ ++ if (strcmp (op_c_str, "abs") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE; ++ top++; ++ } ++ else if (strcmp (op_c_str, "pcrel") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_PCREL; ++ top++; ++ } ++ else if (strcmp (op_c_str, "gprel") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_GPREL; ++ top++; ++ } ++ else if (strcmp (op_c_str, "tprel") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL; ++ top++; ++ } ++ else if (strcmp (op_c_str, "tlsgot") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT; ++ top++; ++ } ++ else if (strcmp (op_c_str, "tlsgd") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GD; ++ top++; ++ } ++ else if (strcmp (op_c_str, "plt") == 0) ++ { ++ top->value = id_sym_expr; ++ top->type = BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL; ++ top++; ++ } ++ else ++ as_fatal (_("unknown reloc hint: %s"), op_c_str); ++} ++ ++static void ++emit_unary (char op) ++{ ++ struct reloc_info *s_top = top - 1; ++ if (is_const (s_top)) ++ { ++ offsetT opr = s_top->value.X_add_number; ++ switch (op) ++ { ++ case '+': ++ break; ++ case '-': ++ opr = -opr; ++ break; ++ case '~': ++ opr = ~opr; ++ break; ++ case '!': ++ opr = !opr; ++ break; ++ default: ++ abort (); ++ } ++ s_top->value.X_add_number = opr; ++ } ++ else ++ { ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ switch (op) ++ { ++ case '!': ++ top->type = BFD_RELOC_LARCH_SOP_NOT; ++ default: ++ abort (); ++ } ++ top->value = const_0; ++ top++; ++ } ++} ++ ++static void ++emit_bin (int op) ++{ ++ struct reloc_info *last_1st = top - 1, *last_2nd = top - 2; ++ if (is_const (last_1st) && is_const (last_2nd)) ++ { ++ offsetT opr1 = last_2nd->value.X_add_number; ++ offsetT opr2 = last_1st->value.X_add_number; ++ switch (op) ++ { ++ case '*': ++ opr1 = opr1 * opr2; ++ break; ++ case '/': ++ opr1 = opr1 / opr2; ++ break; ++ case '%': ++ opr1 = opr1 % opr2; ++ break; ++ case '+': ++ opr1 = opr1 + opr2; ++ break; ++ case '-': ++ opr1 = opr1 - opr2; ++ break; ++ case LEFT_OP: ++ opr1 = opr1 << opr2; ++ break; ++ case RIGHT_OP: ++ /* Algorithm right shift */ ++ opr1 = (offsetT)opr1 >> (offsetT)opr2; ++ break; ++ case '<': ++ opr1 = opr1 < opr2; ++ break; ++ case '>': ++ opr1 = opr1 > opr2; ++ break; ++ case LE_OP: ++ opr1 = opr1 <= opr2; ++ break; ++ case GE_OP: ++ opr1 = opr1 >= opr2; ++ break; ++ case EQ_OP: ++ opr1 = opr1 == opr2; ++ break; ++ case NE_OP: ++ opr1 = opr1 != opr2; ++ break; ++ case '&': ++ opr1 = opr1 & opr2; ++ break; ++ case '^': ++ opr1 = opr1 ^ opr2; ++ break; ++ case '|': ++ opr1 = opr1 | opr2; ++ break; ++ case AND_OP: ++ opr1 = opr1 && opr2; ++ break; ++ case OR_OP: ++ opr1 = opr1 || opr2; ++ break; ++ default: ++ abort (); ++ } ++ last_2nd->value.X_add_number = opr1; ++ last_1st->type = 0; ++ top--; ++ } ++ else ++ { ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ switch (op) ++ { ++ case '+': ++ top->type = BFD_RELOC_LARCH_SOP_ADD; ++ break; ++ case '-': ++ top->type = BFD_RELOC_LARCH_SOP_SUB; ++ break; ++ case LEFT_OP: ++ top->type = BFD_RELOC_LARCH_SOP_SL; ++ break; ++ case RIGHT_OP: ++ top->type = BFD_RELOC_LARCH_SOP_SR; ++ break; ++ case '&': ++ top->type = BFD_RELOC_LARCH_SOP_AND; ++ break; ++ default: ++ abort (); ++ } ++ top->value = const_0; ++ top++; ++ } ++} ++ ++static void ++emit_if_else (void) ++{ ++ struct reloc_info *last_1st = top - 1; ++ struct reloc_info *last_2nd = top - 2; ++ struct reloc_info *last_3rd = top - 3; ++ if (is_const (last_1st) && is_const (last_2nd) && is_const (last_3rd)) ++ { ++ offsetT opr1 = last_3rd->value.X_add_number; ++ offsetT opr2 = last_2nd->value.X_add_number; ++ offsetT opr3 = last_1st->value.X_add_number; ++ opr1 = opr1 ? opr2 : opr3; ++ last_3rd->value.X_add_number = opr1; ++ last_2nd->type = 0; ++ last_1st->type = 0; ++ top -= 2; ++ } ++ else ++ { ++ if (end <= top) ++ as_fatal (_("expr too huge")); ++ top->type = BFD_RELOC_LARCH_SOP_IF_ELSE; ++ top->value = const_0; ++ top++; ++ } ++} ++ ++ ++#line 388 "loongarch-parse.c" /* yacc.c:337 */ ++# ifndef YY_NULLPTR ++# if defined __cplusplus ++# if 201103L <= __cplusplus ++# define YY_NULLPTR nullptr ++# else ++# define YY_NULLPTR 0 ++# endif ++# else ++# define YY_NULLPTR ((void*)0) ++# endif ++# endif ++ ++/* Enabling verbose error messages. */ ++#ifdef YYERROR_VERBOSE ++# undef YYERROR_VERBOSE ++# define YYERROR_VERBOSE 1 ++#else ++# define YYERROR_VERBOSE 0 ++#endif ++ ++/* In a future release of Bison, this section will be replaced ++ by #include "y.tab.h". */ ++#ifndef YY_YY_LOONGARCH_PARSE_H_INCLUDED ++# define YY_YY_LOONGARCH_PARSE_H_INCLUDED ++/* Debug traces. */ ++#ifndef YYDEBUG ++# define YYDEBUG 0 ++#endif ++#if YYDEBUG ++extern int yydebug; ++#endif ++ ++/* Token type. */ ++#ifndef YYTOKENTYPE ++# define YYTOKENTYPE ++ enum yytokentype ++ { ++ INTEGER = 258, ++ IDENTIFIER = 259, ++ LEFT_OP = 260, ++ RIGHT_OP = 261, ++ LE_OP = 262, ++ GE_OP = 263, ++ EQ_OP = 264, ++ NE_OP = 265, ++ AND_OP = 266, ++ OR_OP = 267 ++ }; ++#endif ++/* Tokens. */ ++#define INTEGER 258 ++#define IDENTIFIER 259 ++#define LEFT_OP 260 ++#define RIGHT_OP 261 ++#define LE_OP 262 ++#define GE_OP 263 ++#define EQ_OP 264 ++#define NE_OP 265 ++#define AND_OP 266 ++#define OR_OP 267 ++ ++/* Value type. */ ++#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED ++ ++union YYSTYPE ++{ ++#line 319 "./config/loongarch-parse.y" /* yacc.c:352 */ ++ ++char *c_str; ++offsetT imm; ++ ++#line 460 "loongarch-parse.c" /* yacc.c:352 */ ++}; ++ ++typedef union YYSTYPE YYSTYPE; ++# define YYSTYPE_IS_TRIVIAL 1 ++# define YYSTYPE_IS_DECLARED 1 ++#endif ++ ++ ++extern YYSTYPE yylval; ++ ++int yyparse (void); ++ ++#endif /* !YY_YY_LOONGARCH_PARSE_H_INCLUDED */ ++ ++ ++ ++#ifdef short ++# undef short ++#endif ++ ++#ifdef YYTYPE_UINT8 ++typedef YYTYPE_UINT8 yytype_uint8; ++#else ++typedef unsigned char yytype_uint8; ++#endif ++ ++#ifdef YYTYPE_INT8 ++typedef YYTYPE_INT8 yytype_int8; ++#else ++typedef signed char yytype_int8; ++#endif ++ ++#ifdef YYTYPE_UINT16 ++typedef YYTYPE_UINT16 yytype_uint16; ++#else ++typedef unsigned short yytype_uint16; ++#endif ++ ++#ifdef YYTYPE_INT16 ++typedef YYTYPE_INT16 yytype_int16; ++#else ++typedef short yytype_int16; ++#endif ++ ++#ifndef YYSIZE_T ++# ifdef __SIZE_TYPE__ ++# define YYSIZE_T __SIZE_TYPE__ ++# elif defined size_t ++# define YYSIZE_T size_t ++# elif ! defined YYSIZE_T ++# include /* INFRINGES ON USER NAME SPACE */ ++# define YYSIZE_T size_t ++# else ++# define YYSIZE_T unsigned ++# endif ++#endif ++ ++#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) ++ ++#ifndef YY_ ++# if defined YYENABLE_NLS && YYENABLE_NLS ++# if ENABLE_NLS ++# include /* INFRINGES ON USER NAME SPACE */ ++# define YY_(Msgid) dgettext ("bison-runtime", Msgid) ++# endif ++# endif ++# ifndef YY_ ++# define YY_(Msgid) Msgid ++# endif ++#endif ++ ++#ifndef YY_ATTRIBUTE ++# if (defined __GNUC__ \ ++ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ ++ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C ++# define YY_ATTRIBUTE(Spec) __attribute__(Spec) ++# else ++# define YY_ATTRIBUTE(Spec) /* empty */ ++# endif ++#endif ++ ++#ifndef YY_ATTRIBUTE_PURE ++# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) ++#endif ++ ++#ifndef YY_ATTRIBUTE_UNUSED ++# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) ++#endif ++ ++/* Suppress unused-variable warnings by "using" E. */ ++#if ! defined lint || defined __GNUC__ ++# define YYUSE(E) ((void) (E)) ++#else ++# define YYUSE(E) /* empty */ ++#endif ++ ++#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ ++/* Suppress an incorrect diagnostic about yylval being uninitialized. */ ++# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ ++ _Pragma ("GCC diagnostic push") \ ++ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ ++ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") ++# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ ++ _Pragma ("GCC diagnostic pop") ++#else ++# define YY_INITIAL_VALUE(Value) Value ++#endif ++#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++# define YY_IGNORE_MAYBE_UNINITIALIZED_END ++#endif ++#ifndef YY_INITIAL_VALUE ++# define YY_INITIAL_VALUE(Value) /* Nothing. */ ++#endif ++ ++ ++#if ! defined yyoverflow || YYERROR_VERBOSE ++ ++/* The parser invokes alloca or malloc; define the necessary symbols. */ ++ ++# ifdef YYSTACK_USE_ALLOCA ++# if YYSTACK_USE_ALLOCA ++# ifdef __GNUC__ ++# define YYSTACK_ALLOC __builtin_alloca ++# elif defined __BUILTIN_VA_ARG_INCR ++# include /* INFRINGES ON USER NAME SPACE */ ++# elif defined _AIX ++# define YYSTACK_ALLOC __alloca ++# elif defined _MSC_VER ++# include /* INFRINGES ON USER NAME SPACE */ ++# define alloca _alloca ++# else ++# define YYSTACK_ALLOC alloca ++# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS ++# include /* INFRINGES ON USER NAME SPACE */ ++ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ ++# ifndef EXIT_SUCCESS ++# define EXIT_SUCCESS 0 ++# endif ++# endif ++# endif ++# endif ++# endif ++ ++# ifdef YYSTACK_ALLOC ++ /* Pacify GCC's 'empty if-body' warning. */ ++# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) ++# ifndef YYSTACK_ALLOC_MAXIMUM ++ /* The OS might guarantee only one guard page at the bottom of the stack, ++ and a page size can be as small as 4096 bytes. So we cannot safely ++ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number ++ to allow for a few compiler-allocated temporary stack slots. */ ++# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ ++# endif ++# else ++# define YYSTACK_ALLOC YYMALLOC ++# define YYSTACK_FREE YYFREE ++# ifndef YYSTACK_ALLOC_MAXIMUM ++# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM ++# endif ++# if (defined __cplusplus && ! defined EXIT_SUCCESS \ ++ && ! ((defined YYMALLOC || defined malloc) \ ++ && (defined YYFREE || defined free))) ++# include /* INFRINGES ON USER NAME SPACE */ ++# ifndef EXIT_SUCCESS ++# define EXIT_SUCCESS 0 ++# endif ++# endif ++# ifndef YYMALLOC ++# define YYMALLOC malloc ++# if ! defined malloc && ! defined EXIT_SUCCESS ++void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ ++# endif ++# endif ++# ifndef YYFREE ++# define YYFREE free ++# if ! defined free && ! defined EXIT_SUCCESS ++void free (void *); /* INFRINGES ON USER NAME SPACE */ ++# endif ++# endif ++# endif ++#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ ++ ++ ++#if (! defined yyoverflow \ ++ && (! defined __cplusplus \ ++ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) ++ ++/* A type that is properly aligned for any stack member. */ ++union yyalloc ++{ ++ yytype_int16 yyss_alloc; ++ YYSTYPE yyvs_alloc; ++}; ++ ++/* The size of the maximum gap between one aligned stack and the next. */ ++# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) ++ ++/* The size of an array large to enough to hold all stacks, each with ++ N elements. */ ++# define YYSTACK_BYTES(N) \ ++ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ ++ + YYSTACK_GAP_MAXIMUM) ++ ++# define YYCOPY_NEEDED 1 ++ ++/* Relocate STACK from its old location to the new one. The ++ local variables YYSIZE and YYSTACKSIZE give the old and new number of ++ elements in the stack, and YYPTR gives the new location of the ++ stack. Advance YYPTR to a properly aligned location for the next ++ stack. */ ++# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ ++ do \ ++ { \ ++ YYSIZE_T yynewbytes; \ ++ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ ++ Stack = &yyptr->Stack_alloc; \ ++ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ ++ yyptr += yynewbytes / sizeof (*yyptr); \ ++ } \ ++ while (0) ++ ++#endif ++ ++#if defined YYCOPY_NEEDED && YYCOPY_NEEDED ++/* Copy COUNT objects from SRC to DST. The source and destination do ++ not overlap. */ ++# ifndef YYCOPY ++# if defined __GNUC__ && 1 < __GNUC__ ++# define YYCOPY(Dst, Src, Count) \ ++ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) ++# else ++# define YYCOPY(Dst, Src, Count) \ ++ do \ ++ { \ ++ YYSIZE_T yyi; \ ++ for (yyi = 0; yyi < (Count); yyi++) \ ++ (Dst)[yyi] = (Src)[yyi]; \ ++ } \ ++ while (0) ++# endif ++# endif ++#endif /* !YYCOPY_NEEDED */ ++ ++/* YYFINAL -- State number of the termination state. */ ++#define YYFINAL 47 ++/* YYLAST -- Last index in YYTABLE. */ ++#define YYLAST 73 ++ ++/* YYNTOKENS -- Number of terminals. */ ++#define YYNTOKENS 29 ++/* YYNNTS -- Number of nonterminals. */ ++#define YYNNTS 16 ++/* YYNRULES -- Number of rules. */ ++#define YYNRULES 44 ++/* YYNSTATES -- Number of states. */ ++#define YYNSTATES 81 ++ ++#define YYUNDEFTOK 2 ++#define YYMAXUTOK 267 ++ ++/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM ++ as returned by yylex, with out-of-bounds checking. */ ++#define YYTRANSLATE(YYX) \ ++ ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) ++ ++/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM ++ as returned by yylex. */ ++static const yytype_uint8 yytranslate[] = ++{ ++ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 19, 2, 2, 2, 15, 24, 2, ++ 13, 14, 20, 17, 2, 16, 2, 21, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 28, 2, ++ 22, 2, 23, 27, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 25, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 26, 2, 18, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, ++ 5, 6, 7, 8, 9, 10, 11, 12 ++}; ++ ++#if YYDEBUG ++ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ ++static const yytype_uint16 yyrline[] = ++{ ++ 0, 333, 333, 334, 335, 336, 340, 341, 342, 346, ++ 347, 348, 349, 350, 354, 355, 356, 357, 361, 362, ++ 363, 367, 368, 369, 373, 374, 375, 376, 377, 381, ++ 382, 383, 387, 388, 392, 393, 397, 398, 402, 403, ++ 407, 408, 412, 413, 417 ++}; ++#endif ++ ++#if YYDEBUG || YYERROR_VERBOSE || 0 ++/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. ++ First, the terminals, then, starting at YYNTOKENS, nonterminals. */ ++static const char *const yytname[] = ++{ ++ "$end", "error", "$undefined", "INTEGER", "IDENTIFIER", "LEFT_OP", ++ "RIGHT_OP", "LE_OP", "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", "'('", ++ "')'", "'%'", "'-'", "'+'", "'~'", "'!'", "'*'", "'/'", "'<'", "'>'", ++ "'&'", "'^'", "'|'", "'?'", "':'", "$accept", "primary_expression", ++ "addend", "unary_expression", "multiplicative_expression", ++ "additive_expression", "shift_expression", "relational_expression", ++ "equality_expression", "and_expression", "exclusive_or_expression", ++ "inclusive_or_expression", "logical_and_expression", ++ "logical_or_expression", "conditional_expression", "expression", YY_NULLPTR ++}; ++#endif ++ ++# ifdef YYPRINT ++/* YYTOKNUM[NUM] -- (External) token number corresponding to the ++ (internal) symbol number NUM (which must be that of a token). */ ++static const yytype_uint16 yytoknum[] = ++{ ++ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, ++ 265, 266, 267, 40, 41, 37, 45, 43, 126, 33, ++ 42, 47, 60, 62, 38, 94, 124, 63, 58 ++}; ++# endif ++ ++#define YYPACT_NINF -11 ++ ++#define yypact_value_is_default(Yystate) \ ++ (!!((Yystate) == (-11))) ++ ++#define YYTABLE_NINF -1 ++ ++#define yytable_value_is_error(Yytable_value) \ ++ 0 ++ ++ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing ++ STATE-NUM. */ ++static const yytype_int8 yypact[] = ++{ ++ 2, -11, 2, 10, 2, 2, 2, 2, -11, -11, ++ -9, -7, 36, 0, 37, -8, -1, 8, 27, 1, ++ -11, 43, 31, 40, -11, -11, -11, -11, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, -11, -11, 45, ++ -11, -11, -11, -9, -9, -7, -7, 36, 36, 36, ++ 36, 0, 0, 37, -8, -1, 8, 27, 22, -11, ++ -11, 2, 19, 23, -11, -11, 55, 56, -11, -11, ++ -11 ++}; ++ ++ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. ++ Performed when YYTABLE does not specify something else to do. Zero ++ means the default is an error. */ ++static const yytype_uint8 yydefact[] = ++{ ++ 0, 2, 0, 0, 0, 0, 0, 0, 9, 14, ++ 18, 21, 24, 29, 32, 34, 36, 38, 40, 42, ++ 44, 0, 0, 0, 11, 10, 12, 13, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, ++ 17, 15, 16, 20, 19, 22, 23, 27, 28, 25, ++ 26, 30, 31, 33, 35, 37, 39, 41, 0, 8, ++ 8, 0, 0, 0, 43, 5, 0, 0, 4, 6, ++ 7 ++}; ++ ++ /* YYPGOTO[NTERM-NUM]. */ ++static const yytype_int8 yypgoto[] = ++{ ++ -11, -11, -10, -3, 20, 21, -6, 17, 24, 25, ++ 18, 26, 28, -11, -5, -2 ++}; ++ ++ /* YYDEFGOTO[NTERM-NUM]. */ ++static const yytype_int8 yydefgoto[] = ++{ ++ -1, 8, 72, 9, 10, 11, 12, 13, 14, 15, ++ 16, 17, 18, 19, 20, 21 ++}; ++ ++ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If ++ positive, shift that token. If negative, reduce the rule whose ++ number is the opposite. If YYTABLE_NINF, syntax error. */ ++static const yytype_uint8 yytable[] = ++{ ++ 22, 24, 25, 26, 27, 1, 28, 35, 36, 31, ++ 32, 29, 30, 45, 23, 2, 41, 3, 4, 5, ++ 6, 7, 37, 38, 42, 50, 51, 52, 46, 57, ++ 58, 59, 60, 75, 43, 76, 77, 78, 44, 76, ++ 77, 33, 34, 47, 68, 48, 39, 40, 69, 70, ++ 71, 53, 54, 49, 55, 56, 61, 62, 79, 80, ++ 73, 65, 0, 0, 0, 63, 74, 64, 0, 0, ++ 66, 0, 0, 67 ++}; ++ ++static const yytype_int8 yycheck[] = ++{ ++ 2, 4, 5, 6, 7, 3, 15, 7, 8, 16, ++ 17, 20, 21, 12, 4, 13, 24, 15, 16, 17, ++ 18, 19, 22, 23, 25, 28, 29, 30, 27, 35, ++ 36, 37, 38, 14, 26, 16, 17, 14, 11, 16, ++ 17, 5, 6, 0, 46, 14, 9, 10, 3, 4, ++ 28, 31, 32, 13, 33, 34, 39, 40, 3, 3, ++ 70, 43, -1, -1, -1, 41, 71, 42, -1, -1, ++ 44, -1, -1, 45 ++}; ++ ++ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing ++ symbol of state STATE-NUM. */ ++static const yytype_uint8 yystos[] = ++{ ++ 0, 3, 13, 15, 16, 17, 18, 19, 30, 32, ++ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, ++ 43, 44, 44, 4, 32, 32, 32, 32, 15, 20, ++ 21, 16, 17, 5, 6, 7, 8, 22, 23, 9, ++ 10, 24, 25, 26, 11, 12, 27, 0, 14, 13, ++ 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, ++ 35, 36, 36, 37, 38, 39, 40, 41, 44, 3, ++ 4, 28, 31, 31, 43, 14, 16, 17, 14, 3, ++ 3 ++}; ++ ++ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ ++static const yytype_uint8 yyr1[] = ++{ ++ 0, 29, 30, 30, 30, 30, 31, 31, 31, 32, ++ 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, ++ 34, 35, 35, 35, 36, 36, 36, 36, 36, 37, ++ 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, ++ 42, 42, 43, 43, 44 ++}; ++ ++ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ ++static const yytype_uint8 yyr2[] = ++{ ++ 0, 2, 1, 3, 6, 6, 3, 3, 0, 1, ++ 2, 2, 2, 2, 1, 3, 3, 3, 1, 3, ++ 3, 1, 3, 3, 1, 3, 3, 3, 3, 1, ++ 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, ++ 1, 3, 1, 5, 1 ++}; ++ ++ ++#define yyerrok (yyerrstatus = 0) ++#define yyclearin (yychar = YYEMPTY) ++#define YYEMPTY (-2) ++#define YYEOF 0 ++ ++#define YYACCEPT goto yyacceptlab ++#define YYABORT goto yyabortlab ++#define YYERROR goto yyerrorlab ++ ++ ++#define YYRECOVERING() (!!yyerrstatus) ++ ++#define YYBACKUP(Token, Value) \ ++ do \ ++ if (yychar == YYEMPTY) \ ++ { \ ++ yychar = (Token); \ ++ yylval = (Value); \ ++ YYPOPSTACK (yylen); \ ++ yystate = *yyssp; \ ++ goto yybackup; \ ++ } \ ++ else \ ++ { \ ++ yyerror (YY_("syntax error: cannot back up")); \ ++ YYERROR; \ ++ } \ ++ while (0) ++ ++/* Error token number */ ++#define YYTERROR 1 ++#define YYERRCODE 256 ++ ++ ++ ++/* Enable debugging if requested. */ ++#if YYDEBUG ++ ++# ifndef YYFPRINTF ++# include /* INFRINGES ON USER NAME SPACE */ ++# define YYFPRINTF fprintf ++# endif ++ ++# define YYDPRINTF(Args) \ ++do { \ ++ if (yydebug) \ ++ YYFPRINTF Args; \ ++} while (0) ++ ++/* This macro is provided for backward compatibility. */ ++#ifndef YY_LOCATION_PRINT ++# define YY_LOCATION_PRINT(File, Loc) ((void) 0) ++#endif ++ ++ ++# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ ++do { \ ++ if (yydebug) \ ++ { \ ++ YYFPRINTF (stderr, "%s ", Title); \ ++ yy_symbol_print (stderr, \ ++ Type, Value); \ ++ YYFPRINTF (stderr, "\n"); \ ++ } \ ++} while (0) ++ ++ ++/*-----------------------------------. ++| Print this symbol's value on YYO. | ++`-----------------------------------*/ ++ ++static void ++yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep) ++{ ++ FILE *yyoutput = yyo; ++ YYUSE (yyoutput); ++ if (!yyvaluep) ++ return; ++# ifdef YYPRINT ++ if (yytype < YYNTOKENS) ++ YYPRINT (yyo, yytoknum[yytype], *yyvaluep); ++# endif ++ YYUSE (yytype); ++} ++ ++ ++/*---------------------------. ++| Print this symbol on YYO. | ++`---------------------------*/ ++ ++static void ++yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep) ++{ ++ YYFPRINTF (yyo, "%s %s (", ++ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); ++ ++ yy_symbol_value_print (yyo, yytype, yyvaluep); ++ YYFPRINTF (yyo, ")"); ++} ++ ++/*------------------------------------------------------------------. ++| yy_stack_print -- Print the state stack from its BOTTOM up to its | ++| TOP (included). | ++`------------------------------------------------------------------*/ ++ ++static void ++yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) ++{ ++ YYFPRINTF (stderr, "Stack now"); ++ for (; yybottom <= yytop; yybottom++) ++ { ++ int yybot = *yybottom; ++ YYFPRINTF (stderr, " %d", yybot); ++ } ++ YYFPRINTF (stderr, "\n"); ++} ++ ++# define YY_STACK_PRINT(Bottom, Top) \ ++do { \ ++ if (yydebug) \ ++ yy_stack_print ((Bottom), (Top)); \ ++} while (0) ++ ++ ++/*------------------------------------------------. ++| Report that the YYRULE is going to be reduced. | ++`------------------------------------------------*/ ++ ++static void ++yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) ++{ ++ unsigned long yylno = yyrline[yyrule]; ++ int yynrhs = yyr2[yyrule]; ++ int yyi; ++ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", ++ yyrule - 1, yylno); ++ /* The symbols being reduced. */ ++ for (yyi = 0; yyi < yynrhs; yyi++) ++ { ++ YYFPRINTF (stderr, " $%d = ", yyi + 1); ++ yy_symbol_print (stderr, ++ yystos[yyssp[yyi + 1 - yynrhs]], ++ &yyvsp[(yyi + 1) - (yynrhs)] ++ ); ++ YYFPRINTF (stderr, "\n"); ++ } ++} ++ ++# define YY_REDUCE_PRINT(Rule) \ ++do { \ ++ if (yydebug) \ ++ yy_reduce_print (yyssp, yyvsp, Rule); \ ++} while (0) ++ ++/* Nonzero means print parse trace. It is left uninitialized so that ++ multiple parsers can coexist. */ ++int yydebug; ++#else /* !YYDEBUG */ ++# define YYDPRINTF(Args) ++# define YY_SYMBOL_PRINT(Title, Type, Value, Location) ++# define YY_STACK_PRINT(Bottom, Top) ++# define YY_REDUCE_PRINT(Rule) ++#endif /* !YYDEBUG */ ++ ++ ++/* YYINITDEPTH -- initial size of the parser's stacks. */ ++#ifndef YYINITDEPTH ++# define YYINITDEPTH 200 ++#endif ++ ++/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only ++ if the built-in stack extension method is used). ++ ++ Do not make this value too large; the results are undefined if ++ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) ++ evaluated with infinite-precision integer arithmetic. */ ++ ++#ifndef YYMAXDEPTH ++# define YYMAXDEPTH 10000 ++#endif ++ ++ ++#if YYERROR_VERBOSE ++ ++# ifndef yystrlen ++# if defined __GLIBC__ && defined _STRING_H ++# define yystrlen strlen ++# else ++/* Return the length of YYSTR. */ ++static YYSIZE_T ++yystrlen (const char *yystr) ++{ ++ YYSIZE_T yylen; ++ for (yylen = 0; yystr[yylen]; yylen++) ++ continue; ++ return yylen; ++} ++# endif ++# endif ++ ++# ifndef yystpcpy ++# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE ++# define yystpcpy stpcpy ++# else ++/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in ++ YYDEST. */ ++static char * ++yystpcpy (char *yydest, const char *yysrc) ++{ ++ char *yyd = yydest; ++ const char *yys = yysrc; ++ ++ while ((*yyd++ = *yys++) != '\0') ++ continue; ++ ++ return yyd - 1; ++} ++# endif ++# endif ++ ++# ifndef yytnamerr ++/* Copy to YYRES the contents of YYSTR after stripping away unnecessary ++ quotes and backslashes, so that it's suitable for yyerror. The ++ heuristic is that double-quoting is unnecessary unless the string ++ contains an apostrophe, a comma, or backslash (other than ++ backslash-backslash). YYSTR is taken from yytname. If YYRES is ++ null, do not copy; instead, return the length of what the result ++ would have been. */ ++static YYSIZE_T ++yytnamerr (char *yyres, const char *yystr) ++{ ++ if (*yystr == '"') ++ { ++ YYSIZE_T yyn = 0; ++ char const *yyp = yystr; ++ ++ for (;;) ++ switch (*++yyp) ++ { ++ case '\'': ++ case ',': ++ goto do_not_strip_quotes; ++ ++ case '\\': ++ if (*++yyp != '\\') ++ goto do_not_strip_quotes; ++ else ++ goto append; ++ ++ append: ++ default: ++ if (yyres) ++ yyres[yyn] = *yyp; ++ yyn++; ++ break; ++ ++ case '"': ++ if (yyres) ++ yyres[yyn] = '\0'; ++ return yyn; ++ } ++ do_not_strip_quotes: ; ++ } ++ ++ if (! yyres) ++ return yystrlen (yystr); ++ ++ return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres); ++} ++# endif ++ ++/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message ++ about the unexpected token YYTOKEN for the state stack whose top is ++ YYSSP. ++ ++ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is ++ not large enough to hold the message. In that case, also set ++ *YYMSG_ALLOC to the required number of bytes. Return 2 if the ++ required number of bytes is too large to store. */ ++static int ++yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, ++ yytype_int16 *yyssp, int yytoken) ++{ ++ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); ++ YYSIZE_T yysize = yysize0; ++ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; ++ /* Internationalized format string. */ ++ const char *yyformat = YY_NULLPTR; ++ /* Arguments of yyformat. */ ++ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; ++ /* Number of reported tokens (one for the "unexpected", one per ++ "expected"). */ ++ int yycount = 0; ++ ++ /* There are many possibilities here to consider: ++ - If this state is a consistent state with a default action, then ++ the only way this function was invoked is if the default action ++ is an error action. In that case, don't check for expected ++ tokens because there are none. ++ - The only way there can be no lookahead present (in yychar) is if ++ this state is a consistent state with a default action. Thus, ++ detecting the absence of a lookahead is sufficient to determine ++ that there is no unexpected or expected token to report. In that ++ case, just report a simple "syntax error". ++ - Don't assume there isn't a lookahead just because this state is a ++ consistent state with a default action. There might have been a ++ previous inconsistent state, consistent state with a non-default ++ action, or user semantic action that manipulated yychar. ++ - Of course, the expected token list depends on states to have ++ correct lookahead information, and it depends on the parser not ++ to perform extra reductions after fetching a lookahead from the ++ scanner and before detecting a syntax error. Thus, state merging ++ (from LALR or IELR) and default reductions corrupt the expected ++ token list. However, the list is correct for canonical LR with ++ one exception: it will still contain any token that will not be ++ accepted due to an error action in a later state. ++ */ ++ if (yytoken != YYEMPTY) ++ { ++ int yyn = yypact[*yyssp]; ++ yyarg[yycount++] = yytname[yytoken]; ++ if (!yypact_value_is_default (yyn)) ++ { ++ /* Start YYX at -YYN if negative to avoid negative indexes in ++ YYCHECK. In other words, skip the first -YYN actions for ++ this state because they are default actions. */ ++ int yyxbegin = yyn < 0 ? -yyn : 0; ++ /* Stay within bounds of both yycheck and yytname. */ ++ int yychecklim = YYLAST - yyn + 1; ++ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; ++ int yyx; ++ ++ for (yyx = yyxbegin; yyx < yyxend; ++yyx) ++ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR ++ && !yytable_value_is_error (yytable[yyx + yyn])) ++ { ++ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) ++ { ++ yycount = 1; ++ yysize = yysize0; ++ break; ++ } ++ yyarg[yycount++] = yytname[yyx]; ++ { ++ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); ++ if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) ++ yysize = yysize1; ++ else ++ return 2; ++ } ++ } ++ } ++ } ++ ++ switch (yycount) ++ { ++# define YYCASE_(N, S) \ ++ case N: \ ++ yyformat = S; \ ++ break ++ default: /* Avoid compiler warnings. */ ++ YYCASE_(0, YY_("syntax error")); ++ YYCASE_(1, YY_("syntax error, unexpected %s")); ++ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); ++ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); ++ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); ++ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); ++# undef YYCASE_ ++ } ++ ++ { ++ YYSIZE_T yysize1 = yysize + yystrlen (yyformat); ++ if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) ++ yysize = yysize1; ++ else ++ return 2; ++ } ++ ++ if (*yymsg_alloc < yysize) ++ { ++ *yymsg_alloc = 2 * yysize; ++ if (! (yysize <= *yymsg_alloc ++ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) ++ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; ++ return 1; ++ } ++ ++ /* Avoid sprintf, as that infringes on the user's name space. ++ Don't have undefined behavior even if the translation ++ produced a string with the wrong number of "%s"s. */ ++ { ++ char *yyp = *yymsg; ++ int yyi = 0; ++ while ((*yyp = *yyformat) != '\0') ++ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) ++ { ++ yyp += yytnamerr (yyp, yyarg[yyi++]); ++ yyformat += 2; ++ } ++ else ++ { ++ yyp++; ++ yyformat++; ++ } ++ } ++ return 0; ++} ++#endif /* YYERROR_VERBOSE */ ++ ++/*-----------------------------------------------. ++| Release the memory associated to this symbol. | ++`-----------------------------------------------*/ ++ ++static void ++yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) ++{ ++ YYUSE (yyvaluep); ++ if (!yymsg) ++ yymsg = "Deleting"; ++ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); ++ ++ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++ YYUSE (yytype); ++ YY_IGNORE_MAYBE_UNINITIALIZED_END ++} ++ ++ ++ ++ ++/* The lookahead symbol. */ ++int yychar; ++ ++/* The semantic value of the lookahead symbol. */ ++YYSTYPE yylval; ++/* Number of syntax errors so far. */ ++int yynerrs; ++ ++ ++/*----------. ++| yyparse. | ++`----------*/ ++ ++int ++yyparse (void) ++{ ++ int yystate; ++ /* Number of tokens to shift before error messages enabled. */ ++ int yyerrstatus; ++ ++ /* The stacks and their tools: ++ 'yyss': related to states. ++ 'yyvs': related to semantic values. ++ ++ Refer to the stacks through separate pointers, to allow yyoverflow ++ to reallocate them elsewhere. */ ++ ++ /* The state stack. */ ++ yytype_int16 yyssa[YYINITDEPTH]; ++ yytype_int16 *yyss; ++ yytype_int16 *yyssp; ++ ++ /* The semantic value stack. */ ++ YYSTYPE yyvsa[YYINITDEPTH]; ++ YYSTYPE *yyvs; ++ YYSTYPE *yyvsp; ++ ++ YYSIZE_T yystacksize; ++ ++ int yyn; ++ int yyresult; ++ /* Lookahead token as an internal (translated) token number. */ ++ int yytoken = 0; ++ /* The variables used to return semantic value and location from the ++ action routines. */ ++ YYSTYPE yyval; ++ ++#if YYERROR_VERBOSE ++ /* Buffer for error messages, and its allocated size. */ ++ char yymsgbuf[128]; ++ char *yymsg = yymsgbuf; ++ YYSIZE_T yymsg_alloc = sizeof yymsgbuf; ++#endif ++ ++#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) ++ ++ /* The number of symbols on the RHS of the reduced rule. ++ Keep to zero when no symbol should be popped. */ ++ int yylen = 0; ++ ++ yyssp = yyss = yyssa; ++ yyvsp = yyvs = yyvsa; ++ yystacksize = YYINITDEPTH; ++ ++ YYDPRINTF ((stderr, "Starting parse\n")); ++ ++ yystate = 0; ++ yyerrstatus = 0; ++ yynerrs = 0; ++ yychar = YYEMPTY; /* Cause a token to be read. */ ++ goto yysetstate; ++ ++ ++/*------------------------------------------------------------. ++| yynewstate -- push a new state, which is found in yystate. | ++`------------------------------------------------------------*/ ++yynewstate: ++ /* In all cases, when you get here, the value and location stacks ++ have just been pushed. So pushing a state here evens the stacks. */ ++ yyssp++; ++ ++ ++/*--------------------------------------------------------------------. ++| yynewstate -- set current state (the top of the stack) to yystate. | ++`--------------------------------------------------------------------*/ ++yysetstate: ++ *yyssp = (yytype_int16) yystate; ++ ++ if (yyss + yystacksize - 1 <= yyssp) ++#if !defined yyoverflow && !defined YYSTACK_RELOCATE ++ goto yyexhaustedlab; ++#else ++ { ++ /* Get the current used size of the three stacks, in elements. */ ++ YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1); ++ ++# if defined yyoverflow ++ { ++ /* Give user a chance to reallocate the stack. Use copies of ++ these so that the &'s don't force the real ones into ++ memory. */ ++ YYSTYPE *yyvs1 = yyvs; ++ yytype_int16 *yyss1 = yyss; ++ ++ /* Each stack pointer address is followed by the size of the ++ data in use in that stack, in bytes. This used to be a ++ conditional around just the two extra args, but that might ++ be undefined if yyoverflow is a macro. */ ++ yyoverflow (YY_("memory exhausted"), ++ &yyss1, yysize * sizeof (*yyssp), ++ &yyvs1, yysize * sizeof (*yyvsp), ++ &yystacksize); ++ yyss = yyss1; ++ yyvs = yyvs1; ++ } ++# else /* defined YYSTACK_RELOCATE */ ++ /* Extend the stack our own way. */ ++ if (YYMAXDEPTH <= yystacksize) ++ goto yyexhaustedlab; ++ yystacksize *= 2; ++ if (YYMAXDEPTH < yystacksize) ++ yystacksize = YYMAXDEPTH; ++ ++ { ++ yytype_int16 *yyss1 = yyss; ++ union yyalloc *yyptr = ++ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); ++ if (! yyptr) ++ goto yyexhaustedlab; ++ YYSTACK_RELOCATE (yyss_alloc, yyss); ++ YYSTACK_RELOCATE (yyvs_alloc, yyvs); ++# undef YYSTACK_RELOCATE ++ if (yyss1 != yyssa) ++ YYSTACK_FREE (yyss1); ++ } ++# endif ++ ++ yyssp = yyss + yysize - 1; ++ yyvsp = yyvs + yysize - 1; ++ ++ YYDPRINTF ((stderr, "Stack size increased to %lu\n", ++ (unsigned long) yystacksize)); ++ ++ if (yyss + yystacksize - 1 <= yyssp) ++ YYABORT; ++ } ++#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ ++ ++ YYDPRINTF ((stderr, "Entering state %d\n", yystate)); ++ ++ if (yystate == YYFINAL) ++ YYACCEPT; ++ ++ goto yybackup; ++ ++ ++/*-----------. ++| yybackup. | ++`-----------*/ ++yybackup: ++ /* Do appropriate processing given the current state. Read a ++ lookahead token if we need one and don't already have one. */ ++ ++ /* First try to decide what to do without reference to lookahead token. */ ++ yyn = yypact[yystate]; ++ if (yypact_value_is_default (yyn)) ++ goto yydefault; ++ ++ /* Not known => get a lookahead token if don't already have one. */ ++ ++ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ ++ if (yychar == YYEMPTY) ++ { ++ YYDPRINTF ((stderr, "Reading a token: ")); ++ yychar = yylex (); ++ } ++ ++ if (yychar <= YYEOF) ++ { ++ yychar = yytoken = YYEOF; ++ YYDPRINTF ((stderr, "Now at end of input.\n")); ++ } ++ else ++ { ++ yytoken = YYTRANSLATE (yychar); ++ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); ++ } ++ ++ /* If the proper action on seeing token YYTOKEN is to reduce or to ++ detect an error, take that action. */ ++ yyn += yytoken; ++ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) ++ goto yydefault; ++ yyn = yytable[yyn]; ++ if (yyn <= 0) ++ { ++ if (yytable_value_is_error (yyn)) ++ goto yyerrlab; ++ yyn = -yyn; ++ goto yyreduce; ++ } ++ ++ /* Count tokens shifted since error; after three, turn off error ++ status. */ ++ if (yyerrstatus) ++ yyerrstatus--; ++ ++ /* Shift the lookahead token. */ ++ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); ++ ++ /* Discard the shifted token. */ ++ yychar = YYEMPTY; ++ ++ yystate = yyn; ++ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++ *++yyvsp = yylval; ++ YY_IGNORE_MAYBE_UNINITIALIZED_END ++ ++ goto yynewstate; ++ ++ ++/*-----------------------------------------------------------. ++| yydefault -- do the default action for the current state. | ++`-----------------------------------------------------------*/ ++yydefault: ++ yyn = yydefact[yystate]; ++ if (yyn == 0) ++ goto yyerrlab; ++ goto yyreduce; ++ ++ ++/*-----------------------------. ++| yyreduce -- do a reduction. | ++`-----------------------------*/ ++yyreduce: ++ /* yyn is the number of a rule to reduce with. */ ++ yylen = yyr2[yyn]; ++ ++ /* If YYLEN is nonzero, implement the default value of the action: ++ '$$ = $1'. ++ ++ Otherwise, the following line sets YYVAL to garbage. ++ This behavior is undocumented and Bison ++ users should not rely upon it. Assigning to YYVAL ++ unconditionally makes the parser a bit smaller, and it avoids a ++ GCC warning that YYVAL may be used uninitialized. */ ++ yyval = yyvsp[1-yylen]; ++ ++ ++ YY_REDUCE_PRINT (yyn); ++ switch (yyn) ++ { ++ case 2: ++#line 333 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_const ((yyvsp[0].imm));} ++#line 1602 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 4: ++#line 335 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {reloc ((yyvsp[-4].c_str), (yyvsp[-2].c_str), (yyvsp[-1].imm)); free ((yyvsp[-4].c_str)); free ((yyvsp[-2].c_str));} ++#line 1608 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 5: ++#line 336 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {reloc ((yyvsp[-4].c_str), NULL, (yyvsp[-2].imm) + (yyvsp[-1].imm)); free ((yyvsp[-4].c_str));} ++#line 1614 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 6: ++#line 340 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {(yyval.imm) -= (yyvsp[0].imm);} ++#line 1620 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 7: ++#line 341 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {(yyval.imm) += (yyvsp[0].imm);} ++#line 1626 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 8: ++#line 342 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {(yyval.imm) = 0;} ++#line 1632 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 10: ++#line 347 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_unary ('+');} ++#line 1638 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 11: ++#line 348 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_unary ('-');} ++#line 1644 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 12: ++#line 349 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_unary ('~');} ++#line 1650 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 13: ++#line 350 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_unary ('!');} ++#line 1656 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 15: ++#line 355 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('*');} ++#line 1662 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 16: ++#line 356 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('/');} ++#line 1668 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 17: ++#line 357 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('%');} ++#line 1674 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 19: ++#line 362 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('+');} ++#line 1680 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 20: ++#line 363 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('-');} ++#line 1686 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 22: ++#line 368 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (LEFT_OP);} ++#line 1692 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 23: ++#line 369 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (RIGHT_OP);} ++#line 1698 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 25: ++#line 374 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('<');} ++#line 1704 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 26: ++#line 375 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('>');} ++#line 1710 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 27: ++#line 376 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (LE_OP);} ++#line 1716 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 28: ++#line 377 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (GE_OP);} ++#line 1722 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 30: ++#line 382 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (EQ_OP);} ++#line 1728 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 31: ++#line 383 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (NE_OP);} ++#line 1734 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 33: ++#line 388 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('&');} ++#line 1740 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 35: ++#line 393 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('^');} ++#line 1746 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 37: ++#line 398 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin ('|');} ++#line 1752 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 39: ++#line 403 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (AND_OP);} ++#line 1758 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 41: ++#line 408 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_bin (OR_OP);} ++#line 1764 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ case 43: ++#line 413 "./config/loongarch-parse.y" /* yacc.c:1652 */ ++ {emit_if_else ();} ++#line 1770 "loongarch-parse.c" /* yacc.c:1652 */ ++ break; ++ ++ ++#line 1774 "loongarch-parse.c" /* yacc.c:1652 */ ++ default: break; ++ } ++ /* User semantic actions sometimes alter yychar, and that requires ++ that yytoken be updated with the new translation. We take the ++ approach of translating immediately before every use of yytoken. ++ One alternative is translating here after every semantic action, ++ but that translation would be missed if the semantic action invokes ++ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or ++ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an ++ incorrect destructor might then be invoked immediately. In the ++ case of YYERROR or YYBACKUP, subsequent parser actions might lead ++ to an incorrect destructor call or verbose syntax error message ++ before the lookahead is translated. */ ++ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); ++ ++ YYPOPSTACK (yylen); ++ yylen = 0; ++ YY_STACK_PRINT (yyss, yyssp); ++ ++ *++yyvsp = yyval; ++ ++ /* Now 'shift' the result of the reduction. Determine what state ++ that goes to, based on the state we popped back to and the rule ++ number reduced by. */ ++ { ++ const int yylhs = yyr1[yyn] - YYNTOKENS; ++ const int yyi = yypgoto[yylhs] + *yyssp; ++ yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ++ ? yytable[yyi] ++ : yydefgoto[yylhs]); ++ } ++ ++ goto yynewstate; ++ ++ ++/*--------------------------------------. ++| yyerrlab -- here on detecting error. | ++`--------------------------------------*/ ++yyerrlab: ++ /* Make sure we have latest lookahead translation. See comments at ++ user semantic actions for why this is necessary. */ ++ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); ++ ++ /* If not already recovering from an error, report this error. */ ++ if (!yyerrstatus) ++ { ++ ++yynerrs; ++#if ! YYERROR_VERBOSE ++ yyerror (YY_("syntax error")); ++#else ++# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ ++ yyssp, yytoken) ++ { ++ char const *yymsgp = YY_("syntax error"); ++ int yysyntax_error_status; ++ yysyntax_error_status = YYSYNTAX_ERROR; ++ if (yysyntax_error_status == 0) ++ yymsgp = yymsg; ++ else if (yysyntax_error_status == 1) ++ { ++ if (yymsg != yymsgbuf) ++ YYSTACK_FREE (yymsg); ++ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); ++ if (!yymsg) ++ { ++ yymsg = yymsgbuf; ++ yymsg_alloc = sizeof yymsgbuf; ++ yysyntax_error_status = 2; ++ } ++ else ++ { ++ yysyntax_error_status = YYSYNTAX_ERROR; ++ yymsgp = yymsg; ++ } ++ } ++ yyerror (yymsgp); ++ if (yysyntax_error_status == 2) ++ goto yyexhaustedlab; ++ } ++# undef YYSYNTAX_ERROR ++#endif ++ } ++ ++ ++ ++ if (yyerrstatus == 3) ++ { ++ /* If just tried and failed to reuse lookahead token after an ++ error, discard it. */ ++ ++ if (yychar <= YYEOF) ++ { ++ /* Return failure if at end of input. */ ++ if (yychar == YYEOF) ++ YYABORT; ++ } ++ else ++ { ++ yydestruct ("Error: discarding", ++ yytoken, &yylval); ++ yychar = YYEMPTY; ++ } ++ } ++ ++ /* Else will try to reuse lookahead token after shifting the error ++ token. */ ++ goto yyerrlab1; ++ ++ ++/*---------------------------------------------------. ++| yyerrorlab -- error raised explicitly by YYERROR. | ++`---------------------------------------------------*/ ++yyerrorlab: ++ /* Pacify compilers when the user code never invokes YYERROR and the ++ label yyerrorlab therefore never appears in user code. */ ++ if (0) ++ YYERROR; ++ ++ /* Do not reclaim the symbols of the rule whose action triggered ++ this YYERROR. */ ++ YYPOPSTACK (yylen); ++ yylen = 0; ++ YY_STACK_PRINT (yyss, yyssp); ++ yystate = *yyssp; ++ goto yyerrlab1; ++ ++ ++/*-------------------------------------------------------------. ++| yyerrlab1 -- common code for both syntax error and YYERROR. | ++`-------------------------------------------------------------*/ ++yyerrlab1: ++ yyerrstatus = 3; /* Each real token shifted decrements this. */ ++ ++ for (;;) ++ { ++ yyn = yypact[yystate]; ++ if (!yypact_value_is_default (yyn)) ++ { ++ yyn += YYTERROR; ++ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) ++ { ++ yyn = yytable[yyn]; ++ if (0 < yyn) ++ break; ++ } ++ } ++ ++ /* Pop the current state because it cannot handle the error token. */ ++ if (yyssp == yyss) ++ YYABORT; ++ ++ ++ yydestruct ("Error: popping", ++ yystos[yystate], yyvsp); ++ YYPOPSTACK (1); ++ yystate = *yyssp; ++ YY_STACK_PRINT (yyss, yyssp); ++ } ++ ++ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++ *++yyvsp = yylval; ++ YY_IGNORE_MAYBE_UNINITIALIZED_END ++ ++ ++ /* Shift the error token. */ ++ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); ++ ++ yystate = yyn; ++ goto yynewstate; ++ ++ ++/*-------------------------------------. ++| yyacceptlab -- YYACCEPT comes here. | ++`-------------------------------------*/ ++yyacceptlab: ++ yyresult = 0; ++ goto yyreturn; ++ ++ ++/*-----------------------------------. ++| yyabortlab -- YYABORT comes here. | ++`-----------------------------------*/ ++yyabortlab: ++ yyresult = 1; ++ goto yyreturn; ++ ++ ++#if !defined yyoverflow || YYERROR_VERBOSE ++/*-------------------------------------------------. ++| yyexhaustedlab -- memory exhaustion comes here. | ++`-------------------------------------------------*/ ++yyexhaustedlab: ++ yyerror (YY_("memory exhausted")); ++ yyresult = 2; ++ /* Fall through. */ ++#endif ++ ++ ++/*-----------------------------------------------------. ++| yyreturn -- parsing is finished, return the result. | ++`-----------------------------------------------------*/ ++yyreturn: ++ if (yychar != YYEMPTY) ++ { ++ /* Make sure we have latest lookahead translation. See comments at ++ user semantic actions for why this is necessary. */ ++ yytoken = YYTRANSLATE (yychar); ++ yydestruct ("Cleanup: discarding lookahead", ++ yytoken, &yylval); ++ } ++ /* Do not reclaim the symbols of the rule whose action triggered ++ this YYABORT or YYACCEPT. */ ++ YYPOPSTACK (yylen); ++ YY_STACK_PRINT (yyss, yyssp); ++ while (yyssp != yyss) ++ { ++ yydestruct ("Cleanup: popping", ++ yystos[*yyssp], yyvsp); ++ YYPOPSTACK (1); ++ } ++#ifndef yyoverflow ++ if (yyss != yyssa) ++ YYSTACK_FREE (yyss); ++#endif ++#if YYERROR_VERBOSE ++ if (yymsg != yymsgbuf) ++ YYSTACK_FREE (yymsg); ++#endif ++ return yyresult; ++} ++#line 419 "./config/loongarch-parse.y" /* yacc.c:1918 */ ++ ++ +diff --git a/gas/loongarch-parse.h b/gas/loongarch-parse.h +new file mode 100644 +index 00000000..222c9ff5 +--- /dev/null ++++ b/gas/loongarch-parse.h +@@ -0,0 +1,99 @@ ++/* A Bison parser, made by GNU Bison 3.3.2. */ ++ ++/* Bison interface for Yacc-like parsers in C ++ ++ Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, ++ Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* As a special exception, you may create a larger work that contains ++ part or all of the Bison parser skeleton and distribute that work ++ under terms of your choice, so long as that work isn't itself a ++ parser generator using the skeleton or a modified version thereof ++ as a parser skeleton. Alternatively, if you modify or redistribute ++ the parser skeleton itself, you may (at your option) remove this ++ special exception, which will cause the skeleton and the resulting ++ Bison output files to be licensed under the GNU General Public ++ License without this special exception. ++ ++ This special exception was added by the Free Software Foundation in ++ version 2.2 of Bison. */ ++ ++/* Undocumented macros, especially those whose name start with YY_, ++ are private implementation details. Do not rely on them. */ ++ ++#ifndef YY_YY_LOONGARCH_PARSE_H_INCLUDED ++# define YY_YY_LOONGARCH_PARSE_H_INCLUDED ++/* Debug traces. */ ++#ifndef YYDEBUG ++# define YYDEBUG 0 ++#endif ++#if YYDEBUG ++extern int yydebug; ++#endif ++ ++/* Token type. */ ++#ifndef YYTOKENTYPE ++# define YYTOKENTYPE ++ enum yytokentype ++ { ++ INTEGER = 258, ++ IDENTIFIER = 259, ++ LEFT_OP = 260, ++ RIGHT_OP = 261, ++ LE_OP = 262, ++ GE_OP = 263, ++ EQ_OP = 264, ++ NE_OP = 265, ++ AND_OP = 266, ++ OR_OP = 267 ++ }; ++#endif ++/* Tokens. */ ++#define INTEGER 258 ++#define IDENTIFIER 259 ++#define LEFT_OP 260 ++#define RIGHT_OP 261 ++#define LE_OP 262 ++#define GE_OP 263 ++#define EQ_OP 264 ++#define NE_OP 265 ++#define AND_OP 266 ++#define OR_OP 267 ++ ++/* Value type. */ ++#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED ++ ++union YYSTYPE ++{ ++#line 319 "./config/loongarch-parse.y" /* yacc.c:1921 */ ++ ++char *c_str; ++offsetT imm; ++ ++#line 87 "loongarch-parse.h" /* yacc.c:1921 */ ++}; ++ ++typedef union YYSTYPE YYSTYPE; ++# define YYSTYPE_IS_TRIVIAL 1 ++# define YYSTYPE_IS_DECLARED 1 ++#endif ++ ++ ++extern YYSTYPE yylval; ++ ++int yyparse (void); ++ ++#endif /* !YY_YY_LOONGARCH_PARSE_H_INCLUDED */ +diff --git a/include/dis-asm.h b/include/dis-asm.h +index 0ddf0475..055b8ce8 100644 +--- a/include/dis-asm.h ++++ b/include/dis-asm.h +@@ -258,6 +258,7 @@ extern void print_mips_disassembler_options (FILE *); + extern void print_nfp_disassembler_options (FILE *); + extern void print_ppc_disassembler_options (FILE *); + extern void print_riscv_disassembler_options (FILE *); ++extern void print_loongarch_disassembler_options (FILE *); + extern void print_arm_disassembler_options (FILE *); + extern void print_arc_disassembler_options (FILE *); + extern void print_s390_disassembler_options (FILE *); +diff --git a/include/elf/common.h b/include/elf/common.h +index 8a470745..0de652c4 100644 +--- a/include/elf/common.h ++++ b/include/elf/common.h +@@ -339,6 +339,7 @@ + #define EM_LANAI 244 /* Lanai 32-bit processor. */ + #define EM_BPF 247 /* Linux BPF – in-kernel virtual machine. */ + #define EM_NFP 250 /* Netronome Flow Processor. */ ++#define EM_LOONGARCH 258 /* LoongArch */ + + /* If it is necessary to assign new unofficial EM_* values, please pick large + random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision +@@ -642,6 +643,14 @@ + /* note name must be "LINUX". */ + #define NT_ARM_SVE 0x405 /* AArch SVE registers. */ + /* note name must be "LINUX". */ ++#define NT_LARCH_CPUCFG 0x900 /* Loongarch CPU config registers */ ++ /* note name must be "LINUX". */ ++#define NT_LARCH_LBT 0x901 /* Loongarch Loongson Binary Translation registers */ ++ /* note name must be "LINUX". */ ++#define NT_LARCH_LSX 0x902 /* Loongarch Loongson SIMD Extension registers */ ++ /* note name must be "LINUX". */ ++#define NT_LARCH_LASX 0x903 /* Loongarch Loongson Advanced SIMD Extension registers */ ++ /* note name must be "LINUX". */ + #define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */ + #define NT_FILE 0x46494c45 /* Description of mapped files. */ + +diff --git a/include/elf/loongarch.h b/include/elf/loongarch.h +new file mode 100644 +index 00000000..02085dd3 +--- /dev/null ++++ b/include/elf/loongarch.h +@@ -0,0 +1,95 @@ ++#ifndef _ELF_LOONG_H ++#define _ELF_LOONG_H ++ ++#include ++#include "elf/reloc-macros.h" ++#include "libiberty.h" ++ ++START_RELOC_NUMBERS (elf_loongarch_reloc_type) ++/* used by the dynamic linker */ ++RELOC_NUMBER (R_LARCH_NONE, 0) ++RELOC_NUMBER (R_LARCH_32, 1) ++RELOC_NUMBER (R_LARCH_64, 2) ++RELOC_NUMBER (R_LARCH_RELATIVE, 3) ++RELOC_NUMBER (R_LARCH_COPY, 4) ++RELOC_NUMBER (R_LARCH_JUMP_SLOT, 5) ++RELOC_NUMBER (R_LARCH_TLS_DTPMOD32, 6) ++RELOC_NUMBER (R_LARCH_TLS_DTPMOD64, 7) ++RELOC_NUMBER (R_LARCH_TLS_DTPREL32, 8) ++RELOC_NUMBER (R_LARCH_TLS_DTPREL64, 9) ++RELOC_NUMBER (R_LARCH_TLS_TPREL32, 10) ++RELOC_NUMBER (R_LARCH_TLS_TPREL64, 11) ++RELOC_NUMBER (R_LARCH_IRELATIVE, 12) ++ ++/* Reserved for future relocs that the dynamic linker must understand. */ ++ ++/* used by the static linker for relocating .text */ ++RELOC_NUMBER (R_LARCH_MARK_LA, 20) ++RELOC_NUMBER (R_LARCH_MARK_PCREL, 21) ++ ++/* 这个重定位类型将symbol距离重定位位置的pc相对位置偏移量压栈。 ++ 它against symbol,因为如果是个常数,虽然在no-pic的情况下可以得到结果,但因为 ++ 重定位位置相对这个常数的偏移量一定很大,八成填不进去;而在pic的情况下, ++ 偏移量无法在静态连接时确定。因此我们约定这个重定位不可能against constant */ ++RELOC_NUMBER (R_LARCH_SOP_PUSH_PCREL, 22) ++ ++/* 这个重定位against a symbol or a constant。它将symbol的运行时绝对地址 ++ 或常数压栈,因此在pic的情况下会报错。另外我不太清楚常数和ABS段的关系。 */ ++RELOC_NUMBER (R_LARCH_SOP_PUSH_ABSOLUTE, 23) ++ ++RELOC_NUMBER (R_LARCH_SOP_PUSH_DUP, 24) ++RELOC_NUMBER (R_LARCH_SOP_PUSH_GPREL, 25) ++RELOC_NUMBER (R_LARCH_SOP_PUSH_TLS_TPREL, 26) ++RELOC_NUMBER (R_LARCH_SOP_PUSH_TLS_GOT, 27) ++RELOC_NUMBER (R_LARCH_SOP_PUSH_TLS_GD, 28) ++RELOC_NUMBER (R_LARCH_SOP_PUSH_PLT_PCREL, 29) ++ ++RELOC_NUMBER (R_LARCH_SOP_ASSERT, 30) ++RELOC_NUMBER (R_LARCH_SOP_NOT, 31) ++RELOC_NUMBER (R_LARCH_SOP_SUB, 32) ++RELOC_NUMBER (R_LARCH_SOP_SL, 33) ++RELOC_NUMBER (R_LARCH_SOP_SR, 34) ++RELOC_NUMBER (R_LARCH_SOP_ADD, 35) ++RELOC_NUMBER (R_LARCH_SOP_AND, 36) ++RELOC_NUMBER (R_LARCH_SOP_IF_ELSE, 37) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_10_5, 38) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_U_10_12, 39) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_10_12, 40) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_10_16, 41) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_10_16_S2, 42) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_5_20, 43) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_0_5_10_16_S2, 44) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_S_0_10_10_16_S2, 45) ++RELOC_NUMBER (R_LARCH_SOP_POP_32_U, 46) ++ ++/* used by the static linker for relocating non .text */ ++/* 这几个重定位类型是为了照顾到 ".dword sym1 - sym2" 这种求差的写法。 ++ 这些重定位类型处理的是连接时地址,一般情况下它们是成对出现的。 ++ 在直接求负数".dword - sym1"的情况下,R_LARCH_SUBxx会单独出现。但注意, ++ 那个位置填进去的是连接时地址。 */ ++RELOC_NUMBER (R_LARCH_ADD8, 47) ++RELOC_NUMBER (R_LARCH_ADD16, 48) ++RELOC_NUMBER (R_LARCH_ADD24, 49) ++RELOC_NUMBER (R_LARCH_ADD32, 50) ++RELOC_NUMBER (R_LARCH_ADD64, 51) ++RELOC_NUMBER (R_LARCH_SUB8, 52) ++RELOC_NUMBER (R_LARCH_SUB16, 53) ++RELOC_NUMBER (R_LARCH_SUB24, 54) ++RELOC_NUMBER (R_LARCH_SUB32, 55) ++RELOC_NUMBER (R_LARCH_SUB64, 56) ++ ++/* I don't know what it is. Existing in almost all other arch */ ++RELOC_NUMBER (R_LARCH_GNU_VTINHERIT, 57) ++RELOC_NUMBER (R_LARCH_GNU_VTENTRY, 58) ++ ++END_RELOC_NUMBERS (R_LARCH_count) ++ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++ ++#define EF_LARCH_ABI 0x0003 ++#define EF_LARCH_ABI_LP64 0x0003 ++#define EF_LARCH_ABI_XLP32 0x0002 ++#define EF_LARCH_ABI_LP32 0x0001 ++ ++#endif /* _ELF_LOONG_H */ +diff --git a/include/opcode/loongarch.h b/include/opcode/loongarch.h +new file mode 100644 +index 00000000..47a9d94b +--- /dev/null ++++ b/include/opcode/loongarch.h +@@ -0,0 +1,215 @@ ++#ifndef _LOONGARCH_H_ ++#define _LOONGARCH_H_ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef uint32_t insn_t; ++ ++struct loongarch_opcode ++{ ++ const insn_t match; ++ const insn_t mask; /* High 1 byte is main opcode and it must be 0xf. */ ++#define LARCH_INSN_OPC(insn) ((insn & 0xf0000000) >> 28) ++ const char * const name; ++ ++ /* ++ ACTUAL PARAMETER: ++ ++ // BNF with regular expression. ++args : token* end ++ ++ // just few char separate 'iden' ++token : ',' ++| '(' ++| ')' ++| iden // maybe a label (include at least one alphabet), maybe a number, maybe a expr ++| regname ++ ++regname : '$' iden ++ ++iden : [a-zA-Z0-9\.\+\-]+ ++ ++end : '\0' ++ ++ ++FORMAT: A string to describe the format of actual parameter including bit field infomation. ++For example, "r5:5,r0:5,sr10:16<<2" matches "$12,$13,12345" and "$4,$7,a_label". ++That 'sr' means the instruction may need relocate. '10:16' means bit field of instruction. ++In a 'format', every 'escape's can be replaced to 'iden' or 'regname' acrroding to its meaning. ++We fill all information needed by disassembing and assembing to 'format'. ++ ++ // BNF with regular expression. ++format : escape (literal+ escape)* literal* end ++| (literal+ escape)* literal* end ++ ++end : '\0' // Get here means parse end. ++ ++ // The intersection between any two among FIRST (end), FIRST (literal) and FIRST (escape) must be empty. ++ // So we can build a simple parser. ++literal : ',' ++| '(' ++| ')' ++ ++ // Double '<'s means the real number is the immediate after shifting left. ++escape : esc_ch bit_field '<' '<' dec2 ++| esc_ch bit_field ++| esc_ch // for MACRO. non-macro format must indicate 'bit_field' ++ ++ // '|' means to concatenate nonadjacent bit fields ++ // For example, "10:16|0:4" means ++ // "16 bits starting from the 10th bit concatenating with 4 bits starting from the 0th bit". ++ // This is to say "[25..10]||[3..0]" (little endian). ++b_field : dec2 ':' dec2 ++| dec2 ':' dec2 '|' bit_field ++ ++esc_ch : 's' 'r' // signed immediate or label need relocate ++| 's' // signed immediate no need relocate ++| 'u' // unsigned immediate ++| 'l' // label needed relocate ++| 'r' // general purpose registers ++| 'f' // FPU registers ++| 'v' // 128 bit SIMD register ++| 'x' // 256 bit SIMD register ++ ++dec2 : [1-9][0-9]? ++| 0 ++ ++*/ ++ const char * const format; ++ ++ /* ++MACRO: Indicate how a macro instruction expand for assembling. ++The main is to replace the '%num'(means the 'num'th 'escape' in 'format') in 'macro' string to get the real instruction. ++As for marco insn "b" in MIPS, we can say its name is "b", format is "l", macro is "j %1". So "b 3f" will be expanded to "j 3f". ++As for marco insn "li" in MIPS, we can say its name is "li", format is "s", macro is "ori" ++ ++ ++Maybe need ++*/ ++ const char * const macro; ++ const int *include; ++ const int *exclude; ++ ++ const unsigned long pinfo; ++#define USELESS 0x0l ++ ++}; ++ ++struct hash_control; ++ ++struct loongarch_ase ++{ ++ const int *enabled; ++ struct loongarch_opcode * const opcodes; ++ const int *include; ++ const int *exclude; ++ ++ /* for disassemble to create main opcode hash table. */ ++ const struct loongarch_opcode *opc_htab[16]; ++ unsigned char opc_htab_inited; ++ ++ /* for GAS to create hash table. */ ++ struct hash_control *name_hash_entry; ++}; ++ ++extern int is_unsigned (const char *); ++extern int is_signed (const char *); ++extern int is_branch_label (const char *); ++ ++extern int ++loongarch_get_bit_field_width (const char *bit_field, char **end); ++extern int32_t ++loongarch_decode_imm (const char *bit_field, insn_t insn, int si); ++ ++#define MAX_ARG_NUM_PLUS_2 9 ++ ++extern size_t ++loongarch_split_args_by_comma (char *args, const char *arg_strs[]); ++extern char * ++loongarch_cat_splited_strs (const char *arg_strs[]); ++extern insn_t ++loongarch_foreach_args (const char *format, const char *arg_strs[], ++ int32_t (*helper) (char esc1, char esc2, ++ const char *bit_field, ++ const char *arg, void *context), ++ void *context); ++ ++extern int ++loongarch_check_format (const char *format); ++extern int ++loongarch_check_macro (const char *format, const char *macro); ++ ++extern char * ++loongarch_expand_macro_with_format_map (const char *format, const char *macro, ++ const char * const arg_strs[], ++ const char * (*map) ( ++ char esc1, char esc2, ++ const char *arg), ++ char * (*helper) ( ++ const char * const arg_strs[], ++ void *context), ++ void *context); ++extern char * ++loongarch_expand_macro (const char *macro, const char * const arg_strs[], ++ char * (*helper) (const char * const arg_strs[], ++ void *context), ++ void *context); ++extern size_t ++loongarch_bits_imm_needed (int64_t imm, int si); ++ ++/* 将字符串中指定的连续字符化为1个 */ ++extern void ++loongarch_eliminate_adjacent_repeat_char (char *dest, char c); ++ ++/* 下面两个函数计划作为libopcode.a拿出来给一些系统软件反汇编用 */ ++extern int ++loongarch_parse_dis_options (const char *opts_in); ++extern void ++loongarch_disassemble_one (int64_t pc, insn_t insn, ++ int (*fprintf_func) ++ (void *stream, const char *format, ...), ++ void *stream); ++ ++extern const char * const loongarch_r_normal_name[32]; ++extern const char * const loongarch_r_lp64_name[32]; ++extern const char * const loongarch_r_lp64_name1[32]; ++extern const char * const loongarch_f_normal_name[32]; ++extern const char * const loongarch_f_lp64_name[32]; ++extern const char * const loongarch_f_lp64_name1[32]; ++extern const char * const loongarch_c_normal_name[8]; ++extern const char * const loongarch_cr_normal_name[4]; ++extern const char * const loongarch_v_normal_name[32]; ++extern const char * const loongarch_x_normal_name[32]; ++ ++extern struct loongarch_ase loongarch_ASEs[]; ++ ++extern struct loongarch_ASEs_option ++{ ++ int ase_test; ++ int ase_fix; ++ int ase_float; ++ int ase_128vec; ++ int ase_256vec; ++ ++ int addrwidth_is_32; ++ int addrwidth_is_64; ++ int rlen_is_32; ++ int rlen_is_64; ++ int la_local_with_abs; ++ int la_global_with_pcrel; ++ int la_global_with_abs; ++ ++ int abi_is_lp32; ++ int abi_is_lp64; ++} LARCH_opts; ++ ++extern size_t loongarch_insn_length (insn_t insn); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _LOONGARCH_H_ */ +diff --git a/ld/Makefile.am b/ld/Makefile.am +index 82718165..ee4a5790 100644 +--- a/ld/Makefile.am ++++ b/ld/Makefile.am +@@ -492,6 +492,7 @@ ALL_64_EMULATION_SOURCES = \ + eelf64btsmip.c \ + eelf64btsmip_fbsd.c \ + eelf64hppa.c \ ++ eelf64loongarch.c \ + eelf64lppc.c \ + eelf64lriscv.c \ + eelf64ltsmip.c \ +@@ -1988,6 +1989,11 @@ eelf64btsmip_fbsd.c: $(srcdir)/emulparams/elf64btsmip_fbsd.sh \ + eelf64hppa.c: $(srcdir)/emulparams/elf64hppa.sh \ + $(ELF_DEPS) $(srcdir)/scripttempl/elf64hppa.sc ${GEN_DEPENDS} + ++eelf64loongarch.c: $(srcdir)/emulparams/elf64loongarch.sh \ ++ $(srcdir)/emulparams/elf64loongarch-defs.sh $(ELF_DEPS) \ ++ $(srcdir)/emultempl/loongarchelf.em $(srcdir)/scripttempl/elf.sc \ ++ ${GEN_DEPENDS} ++ + eelf64lppc.c: $(srcdir)/emulparams/elf64lppc.sh \ + $(srcdir)/emulparams/elf64ppc.sh \ + $(srcdir)/emulparams/dynamic_undefined_weak.sh \ +diff --git a/ld/configure.tgt b/ld/configure.tgt +index 1a70497a..a9a46150 100644 +--- a/ld/configure.tgt ++++ b/ld/configure.tgt +@@ -432,6 +432,8 @@ iq2000-*-elf) targ_emul=elf32iq2000 ; targ_extra_emuls="elf32iq10" + lm32-*-*linux*) targ_emul=elf32lm32fd ;; + lm32-*-*) targ_emul=elf32lm32 ; targ_extra_emuls="elf32lm32fd" + ;; ++loongarch32-*) targ_emul=elf32loongarch ;; ++loongarch64-*) targ_emul=elf64loongarch ;; + m32c-*-elf | m32c-*-rtems*) + targ_emul=elf32m32c + ;; +diff --git a/ld/emulparams/elf64loongarch-defs.sh b/ld/emulparams/elf64loongarch-defs.sh +new file mode 100644 +index 00000000..072519ea +--- /dev/null ++++ b/ld/emulparams/elf64loongarch-defs.sh +@@ -0,0 +1,39 @@ ++# This is an ELF platform. ++SCRIPT_NAME=elf ++ARCH=loongarch ++NO_REL_RELOCS=yes ++ ++TEMPLATE_NAME=elf32 ++EXTRA_EM_FILE=loongarchelf ++ ++ELFSIZE=64 ++ ++if test `echo "$host" | sed -e s/64//` = `echo "$target" | sed -e s/64//`; then ++ case " $EMULATION_LIBPATH " in ++ *" ${EMULATION_NAME} "*) ++ NATIVE=yes ++ ;; ++ esac ++fi ++ ++# Enable shared library support for everything except an embedded elf target. ++case "$target" in ++ loongarch*-elf) ++ ;; ++ *) ++ GENERATE_SHLIB_SCRIPT=yes ++ GENERATE_PIE_SCRIPT=yes ++ ;; ++esac ++ ++# In all cases, the number is big-endian. ++# LoongArch nop is 'andi $r0,$r0,0'. ++NOP=0x00004003 ++ ++TEXT_START_ADDR=0x120000000 ++MAXPAGESIZE="CONSTANT (MAXPAGESIZE)" ++COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)" ++ ++SEPARATE_GOTPLT=0 ++INITIAL_READONLY_SECTIONS=".interp : { *(.interp) } ${CREATE_PIE-${INITIAL_READONLY_SECTIONS}}" ++INITIAL_READONLY_SECTIONS="${RELOCATING+${CREATE_SHLIB-${INITIAL_READONLY_SECTIONS}}}" +diff --git a/ld/emulparams/elf64loongarch.sh b/ld/emulparams/elf64loongarch.sh +new file mode 100644 +index 00000000..7331fe16 +--- /dev/null ++++ b/ld/emulparams/elf64loongarch.sh +@@ -0,0 +1,15 @@ ++# LA64 code using LP64D ABI. ++# ABI not in emulation name to avoid breaking backward compatibility. ++. ${srcdir}/emulparams/elf64loongarch-defs.sh ++OUTPUT_FORMAT="elf64-loongarch" ++ ++# On Linux, first look for 64 bit LP64D target libraries in /lib64/lp64d as per ++# the glibc ABI, and then /lib64 for backward compatility. ++case "$target" in ++ loong64*-linux*) ++ case "$EMULATION_NAME" in ++ *64*) ++ LIBPATH_SUFFIX="64/lib64 64";; ++ esac ++ ;; ++esac +diff --git a/ld/emultempl/loongarchelf.em b/ld/emultempl/loongarchelf.em +new file mode 100644 +index 00000000..3570797f +--- /dev/null ++++ b/ld/emultempl/loongarchelf.em +@@ -0,0 +1,90 @@ ++# This shell script emits a C file. -*- C -*- ++# Copyright (C) 2004-2018 Free Software Foundation, Inc. ++# ++# This file is part of the GNU Binutils. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, ++# MA 02110-1301, USA. ++ ++fragment <>= sizeof (t) * 8 - width; ++ ret <<= width; ++ ret |= t; ++ ++ if (*bit_field_1 != '|') ++ break; ++ bit_field_1++; ++ } ++ ++ if (*bit_field_1 == '<' && *(++bit_field_1) == '<') ++ { ++ width = atoi(bit_field_1 + 1); ++ ret <<= width; ++ len += width; ++ } ++ else if (*bit_field_1 == '+') ++ ret += atoi(bit_field_1 + 1); ++ ++ if (si) ++ { ++ ret <<= sizeof (ret) * 8 - len; ++ ret >>= sizeof (ret) * 8 - len; ++ } ++ return ret; ++} ++ ++static insn_t ++loongarch_encode_imm (const char *bit_field, int32_t imm) ++{ ++ char *bit_field_1 = (char *) bit_field; ++ char *t = bit_field_1; ++ int width, b_start; ++ insn_t ret = 0, i; ++ ++ width = loongarch_get_bit_field_width (t, &t); ++ if (width == -1) ++ return ret; ++ ++ if (*t == '<' && *(++t) == '<') ++ width += atoi (t + 1); ++ else if (*t == '+') ++ imm -= atoi (t + 1); ++ ++ imm <<= sizeof (imm) * 8 - width; ++ while (1) ++ { ++ b_start = strtol (bit_field_1, &bit_field_1, 10); ++ if (*bit_field_1 != ':') ++ break; ++ width = strtol (bit_field_1 + 1, &bit_field_1, 10); ++ i = imm; ++ i >>= sizeof (i) * 8 - width; ++ i <<= b_start; ++ ret |= i; ++ imm <<= width; ++ ++ if (*bit_field_1 != '|') ++ break; ++ bit_field_1++; ++ } ++ return ret; ++} ++ ++/* parse such FORMAT ++ "" ++ "u" ++ "v0:5,r5:5,s10:10<<2" ++ "r0:5,r5:5,r10:5,u15:2+1" ++ "r,r,u0:5+32,u0:5+1" ++*/ ++static int ++loongarch_parse_format (const char *format, ++ char *esc1s, char *esc2s, const char **bit_fields) ++{ ++ size_t arg_num = 0; ++ ++ if (*format == '\0') ++ goto end; ++ ++ while (1) ++ { ++ /* esc1 esc2 ++ for "[a-zA-Z][a-zA-Z]?" */ ++ if (('a' <= *format && *format <= 'z') ++ || ('A' <= *format && *format <= 'Z')) ++ { ++ *esc1s++ = *format++; ++ if (('a' <= *format && *format <= 'z') ++ || ('A' <= *format && *format <= 'Z')) ++ *esc2s++ = *format++; ++ else ++ *esc2s++ = '\0'; ++ } ++ else ++ return -1; ++ ++ arg_num++; ++ if (MAX_ARG_NUM_PLUS_2 - 2 < arg_num) ++ /* need larger MAX_ARG_NUM_PLUS_2 */ ++ return -1; ++ ++ *bit_fields++ = format; ++ ++ if ('0' <= *format && *format <= '9') ++ { ++ /* for "[0-9]+:[0-9]+(\|[0-9]+:[0-9]+)*" */ ++ while (1) ++ { ++ while ('0' <= *format && *format <= '9') ++ format++; ++ ++ if (*format != ':') ++ return -1; ++ format++; ++ ++ if (!('0' <= *format && *format <= '9')) ++ return -1; ++ while ('0' <= *format && *format <= '9') ++ format++; ++ ++ if (*format != '|') ++ break; ++ format++; ++ } ++ ++ /* for "((\+|<<)[1-9][0-9]*)?" */ ++ do ++ { ++ if (*format == '+') ++ format++; ++ else if (format[0] == '<' && format[1] == '<') ++ format += 2; ++ else ++ break; ++ ++ if (!('1' <= *format && *format <= '9')) ++ return -1; ++ while ('0' <= *format && *format <= '9') ++ format++; ++ } ++ while (0); ++ } ++ ++ if (*format == ',') ++ format++; ++ else if (*format == '\0') ++ break; ++ else ++ return -1; ++ } ++ ++end: ++ *esc1s = '\0'; ++ return 0; ++} ++ ++size_t ++loongarch_split_args_by_comma (char *args, const char * arg_strs[]) ++{ ++ size_t num = 0; ++ ++ if (*args) ++ arg_strs[num++] = args; ++ for (; *args; args++) ++ if (*args == ',') ++ { ++ if (MAX_ARG_NUM_PLUS_2 - 1 == num) ++ break; ++ else ++ *args = '\0', arg_strs[num++] = args + 1; ++ } ++ arg_strs[num] = NULL; ++ return num; ++} ++ ++char * ++loongarch_cat_splited_strs (const char *arg_strs[]) ++{ ++ char *ret; ++ size_t n, l; ++ ++ for (l = 0, n = 0; arg_strs[n]; n++) ++ l += strlen (arg_strs[n]); ++ ret = malloc (l + n + 1); ++ ret[0] = '\0'; ++ if (0 < n) ++ strcat (ret, arg_strs[0]); ++ for (l = 1; l < n; l++) ++ strcat (ret, ","), strcat (ret, arg_strs[l]); ++ return ret; ++} ++ ++insn_t ++loongarch_foreach_args (const char *format, const char *arg_strs[], ++ int32_t (*helper) (char esc1, char esc2, ++ const char *bit_field, ++ const char *arg, void *context), ++ void *context) ++{ ++ char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; ++ const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; ++ size_t i; ++ insn_t ret = 0; ++ int ok; ++ ++ ok = loongarch_parse_format (format, esc1s, esc2s, bit_fields) == 0; ++ ++ /* make sure the num of actual args is equal to the num of escape */ ++ for (i = 0; esc1s[i] && arg_strs[i]; i++); ++ ok = ok && !esc1s[i] && !arg_strs[i]; ++ ++ if (ok && helper) ++ { ++ for (i = 0; arg_strs[i]; i++) ++ ret |= loongarch_encode_imm (bit_fields[i], ++ helper (esc1s[i], esc2s[i], bit_fields[i], ++ arg_strs[i], context)); ++ ret |= helper ('\0', '\0', NULL, NULL, context); ++ } ++ ++ return ret; ++} ++ ++int ++loongarch_check_format (const char *format) ++{ ++ char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; ++ const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; ++ ++ if (!format) ++ return -1; ++ ++ return loongarch_parse_format (format, esc1s, esc2s, bit_fields); ++} ++ ++int ++loongarch_check_macro (const char *format, const char *macro) ++{ ++ int num_of_args; ++ char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; ++ const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; ++ ++ if (!format || !macro ++ || loongarch_parse_format (format, esc1s, esc2s, bit_fields) != 0) ++ return -1; ++ ++ for (num_of_args = 0; esc1s[num_of_args]; num_of_args++); ++ ++ for (; macro[0]; macro++) ++ if (macro[0] == '%') ++ { ++ macro++; ++ if ('1' <= macro[0] && macro[0] <= '9') ++ { ++ if (num_of_args < macro[0] - '0') ++ /* out of args num */ ++ return -1; ++ } ++ else if (macro[0] == 'f'); ++ else if (macro[0] == '%'); ++ else ++ return -1; ++ } ++ return 0; ++} ++ ++static const char * ++I (char esc_ch1 ATTRIBUTE_UNUSED, ++ char esc_ch2 ATTRIBUTE_UNUSED, ++ const char *c_str) ++{ ++ return c_str; ++} ++ ++char * ++loongarch_expand_macro_with_format_map (const char *format, const char *macro, ++ const char * const arg_strs[], ++ const char * (*map) ( ++ char esc1, char esc2, ++ const char *arg), ++ char * (*helper) ( ++ const char * const arg_strs[], ++ void *context), ++ void *context) ++{ ++ char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; ++ const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; ++ const char *src; ++ char *dest; ++ char buffer[8192]; ++ ++ if (format) ++ loongarch_parse_format (format, esc1s, esc2s, bit_fields); ++ ++ src = macro; ++ dest = buffer; ++ ++ while (*src) ++ if (*src == '%') ++ { ++ src++; ++ if ('1' <= *src && *src <= '9') ++ { ++ size_t i = *src - '1'; ++ const char *t = map (esc1s[i], esc2s[i], arg_strs[i]); ++ while (*t) ++ *dest++ = *t++; ++ } ++ else if (*src == '%') ++ *dest++ = '%'; ++ else if (*src == 'f' && helper) ++ { ++ char *b, *t; ++ t = b = (*helper) (arg_strs, context); ++ if (b) ++ { ++ while (*t) ++ *dest++ = *t++; ++ free (b); ++ } ++ } ++ src++; ++ } ++ else ++ *dest++ = *src++; ++ ++ *dest = '\0'; ++ return strdup (buffer); ++} ++ ++char * ++loongarch_expand_macro (const char *macro, const char * const arg_strs[], ++ char * (*helper) (const char * const arg_strs[], ++ void *context), ++ void *context) ++{ ++ return loongarch_expand_macro_with_format_map ++ (NULL, macro, arg_strs, I, helper, context); ++} ++ ++size_t ++loongarch_bits_imm_needed (int64_t imm, int si) ++{ ++ size_t ret; ++ if (si) ++ { ++ if (imm < 0) ++ { ++ for (ret = 0; imm < 0; imm <<= 1, ret++); ++ ret = 64 - ret + 1; ++ } ++ else ++ ret = loongarch_bits_imm_needed (imm, 0) + 1; ++ } ++ else ++ { ++ uint64_t t = imm; ++ for (ret = 0; t; t >>= 1, ret++); ++ } ++ return ret; ++} ++ ++void ++loongarch_eliminate_adjacent_repeat_char (char *dest, char c) ++{ ++ if (c == '\0') ++ return; ++ char *src = dest; ++ while (*dest) ++ { ++ while (src[0] == c && src[0] == src[1]) ++ src++; ++ *dest++ = *src++; ++ } ++} +diff --git a/opcodes/loongarch-dis.c b/opcodes/loongarch-dis.c +new file mode 100644 +index 00000000..888c55f5 +--- /dev/null ++++ b/opcodes/loongarch-dis.c +@@ -0,0 +1,311 @@ ++#include "sysdep.h" ++#include "disassemble.h" ++#include "opintl.h" ++#include "opcode/loongarch.h" ++#include ++ ++static const struct loongarch_opcode * ++get_loongarch_opcode_by_binfmt (insn_t insn) ++{ ++ const struct loongarch_opcode *it; ++ struct loongarch_ase *ase; ++ size_t i; ++ for (ase = loongarch_ASEs; ase->enabled; ase++) ++ { ++ if (!*ase->enabled ++ || (ase->include && !*ase->include) ++ || (ase->exclude && *ase->exclude)) ++ continue; ++ ++ if (!ase->opc_htab_inited) ++ { ++ for (it = ase->opcodes; it->mask; it++) ++ if (!ase->opc_htab[LARCH_INSN_OPC (it->match)] ++ && it->macro == NULL) ++ ase->opc_htab[LARCH_INSN_OPC (it->match)] = it; ++ for (i = 0; i < 16; i++) ++ if (!ase->opc_htab[i]) ++ ase->opc_htab[i] = it; ++ ase->opc_htab_inited = 1; ++ } ++ ++ it = ase->opc_htab[LARCH_INSN_OPC(insn)]; ++ for (; it->name; it++) ++ if ((insn & it->mask) == it->match ++ && it->mask ++ && !(it->include && !*it->include) ++ && !(it->exclude && *it->exclude)) ++ return it; ++ } ++ return NULL; ++} ++ ++static const char * const *loongarch_r_disname = NULL; ++static const char * const *loongarch_f_disname = NULL; ++static const char * const *loongarch_c_disname = NULL; ++static const char * const *loongarch_cr_disname = NULL; ++static const char * const *loongarch_v_disname = NULL; ++static const char * const *loongarch_x_disname = NULL; ++ ++static void ++set_default_loongarch_dis_options (void) ++{ ++ LARCH_opts.ase_test = 1; ++ LARCH_opts.ase_fix = 1; ++ LARCH_opts.ase_float = 1; ++ LARCH_opts.ase_128vec = 1; ++ LARCH_opts.ase_256vec = 1; ++ ++ loongarch_r_disname = loongarch_r_normal_name; ++ loongarch_f_disname = loongarch_f_normal_name; ++ loongarch_c_disname = loongarch_c_normal_name; ++ loongarch_cr_disname = loongarch_cr_normal_name; ++ loongarch_v_disname = loongarch_v_normal_name; ++ loongarch_x_disname = loongarch_x_normal_name; ++} ++ ++static int ++parse_loongarch_dis_option (const char *option ATTRIBUTE_UNUSED) ++{ ++ return -1; ++} ++ ++static int ++parse_loongarch_dis_options (const char *opts_in) ++{ ++ set_default_loongarch_dis_options (); ++ ++ if (opts_in == NULL) ++ return 0; ++ ++ char opts[strlen (opts_in) + 1], *opt, *opt_end; ++ strcpy (opts, opts_in); ++ ++ for (opt = opt_end = opts; opt_end != NULL; opt = opt_end + 1) ++ { ++ if ((opt_end = strchr (opt, ',')) != NULL) ++ *opt_end = 0; ++ if (parse_loongarch_dis_option (opt) != 0) ++ return -1; ++ } ++ return 0; ++} ++ ++static int32_t ++dis_one_arg (char esc1, char esc2, const char *bit_field, ++ const char *arg ATTRIBUTE_UNUSED, void *context) ++{ ++ static int need_comma = 0; ++ struct disassemble_info *info = context; ++ insn_t insn = *(insn_t *) info->private_data; ++ int32_t imm, u_imm; ++ ++ if (esc1) ++ { ++ if (need_comma) ++ info->fprintf_func (info->stream, ","); ++ need_comma = 1; ++ imm = loongarch_decode_imm (bit_field, insn, 1); ++ u_imm = loongarch_decode_imm (bit_field, insn, 0); ++ } ++ ++ switch (esc1) ++ { ++ case 'r': ++ info->fprintf_func (info->stream, "%s", loongarch_r_disname[u_imm]); ++ break; ++ case 'f': ++ info->fprintf_func (info->stream, "%s", loongarch_f_disname[u_imm]); ++ break; ++ case 'c': ++ switch (esc2) ++ { ++ case 'r': ++ info->fprintf_func (info->stream, "%s", loongarch_cr_disname[u_imm]); ++ break; ++ default: ++ info->fprintf_func (info->stream, "%s", loongarch_c_disname[u_imm]); ++ } ++ break; ++ case 'v': ++ info->fprintf_func (info->stream, "%s", loongarch_v_disname[u_imm]); ++ break; ++ case 'x': ++ info->fprintf_func (info->stream, "%s", loongarch_x_disname[u_imm]); ++ break; ++ case 'u': ++ info->fprintf_func (info->stream, "0x%x", u_imm); ++ break; ++ case 's': ++ if (imm == 0) ++ info->fprintf_func (info->stream, "%d", imm); ++ else ++ info->fprintf_func (info->stream, "%d(0x%x)", imm, u_imm); ++ switch (esc2) ++ { ++ case 'b': ++ info->insn_type = dis_branch; ++ info->target += imm; ++ } ++ break; ++ case '\0': ++ need_comma = 0; ++ } ++ return 0; ++} ++ ++static void ++disassemble_one (insn_t insn, struct disassemble_info *info) ++{ ++ const struct loongarch_opcode *opc = get_loongarch_opcode_by_binfmt (insn); ++ ++#ifdef LOONGARCH_DEBUG ++ char have_space[32] = {0}; ++ insn_t t; ++ int i; ++ const char *t_f = opc ? opc->format : NULL; ++ if(t_f) ++ while (*t_f) ++ { ++ while (('a' <= t_f[0] && t_f[0] <= 'z') ++ || ('A' <= t_f[0] && t_f[0] <= 'Z') ++ t_f[0] == ',') ++ t_f++; ++ while (1) ++ { ++ i = strtol (t_f, &t_f, 10); ++ have_space[i] = 1; ++ t_f++; //':' ++ i += strtol (t_f, &t_f, 10); ++ have_space[i] = 1; ++ if (t_f[0] == '|') ++ t_f++; ++ else ++ break; ++ } ++ if (t_f[0] == '<') ++ t_f += 2; // '<' '<' ++ strtol (t_f, &t_f, 10); ++ } ++ ++ have_space[28] = 1; ++ have_space[0] = 0; ++ t = ~((insn_t)-1 >> 1); ++ for (i = 31; 0 <= i ; i--) ++ { ++ if (t & insn) ++ info->fprintf_func (info->stream, "1"); ++ else ++ info->fprintf_func (info->stream, "0"); ++ if (have_space[i]) ++ info->fprintf_func (info->stream, " "); ++ t = t >> 1; ++ } ++ info->fprintf_func (info->stream, "\t"); ++#endif ++ ++ if (!opc) ++ { ++ info->insn_type = dis_noninsn; ++ info->fprintf_func (info->stream, "0x%08x", insn); ++ return; ++ } ++ ++ info->insn_type = dis_nonbranch; ++ info->fprintf_func (info->stream, "%s", opc->name); ++ ++ { ++ char fake_args[strlen (opc->format) + 1]; ++ const char *fake_arg_strs[MAX_ARG_NUM_PLUS_2]; ++ strcpy (fake_args, opc->format); ++ if (0 < loongarch_split_args_by_comma (fake_args, fake_arg_strs)) ++ info->fprintf_func (info->stream, "\t"); ++ info->private_data = &insn; ++ loongarch_foreach_args (opc->format, fake_arg_strs, dis_one_arg, info); ++ } ++ ++ if (info->insn_type == dis_branch || info->insn_type == dis_condbranch ++ /* || someother if we have extra info to print */) ++ info->fprintf_func (info->stream, " #"); ++ ++ if (info->insn_type == dis_branch || info->insn_type == dis_condbranch) ++ { ++ info->fprintf_func (info->stream, " "); ++ info->print_address_func (info->target, info); ++ } ++} ++ ++int ++print_insn_loongarch (bfd_vma memaddr, struct disassemble_info *info) ++{ ++ insn_t insn; ++ int status; ++ ++ static int not_init_yet = 1; ++ if (not_init_yet) ++ { ++ parse_loongarch_dis_options (info->disassembler_options); ++ not_init_yet = 0; ++ } ++ ++ info->bytes_per_chunk = 4; ++ info->bytes_per_line = 4; ++ info->display_endian = BFD_ENDIAN_LITTLE; ++ info->insn_info_valid = 1; ++ info->target = memaddr; ++ ++ if ((status = info->read_memory_func ++ (memaddr, (bfd_byte *) &insn, sizeof (insn), info)) ++ != 0) ++ { ++ info->memory_error_func (status, memaddr, info); ++ return -1; //loongarch_insn_length (0); ++ } ++ ++ disassemble_one (insn, info); ++ ++ return loongarch_insn_length (insn); ++} ++ ++void ++print_loongarch_disassembler_options (FILE *stream) ++{ ++ fprintf (stream, _("\n\ ++ LoongISA:\n")); ++ ++ fprintf (stream, _("\n")); ++} ++ ++int ++loongarch_parse_dis_options (const char *opts_in) ++{ ++ return parse_loongarch_dis_options (opts_in); ++} ++ ++static void ++my_print_address_func (bfd_vma addr, struct disassemble_info *dinfo) ++{ ++ dinfo->fprintf_func (dinfo->stream, "0x%llx", (long long) addr); ++} ++ ++void ++loongarch_disassemble_one (int64_t pc, insn_t insn, ++ int (*fprintf_func) ++ (void *stream, const char *format, ...), ++ void *stream) ++{ ++ static struct disassemble_info my_disinfo = { ++ .print_address_func = my_print_address_func, ++ }; ++ static int not_init_yet = 1; ++ if (not_init_yet) ++ { ++ loongarch_parse_dis_options (NULL); ++ not_init_yet = 0; ++ } ++ ++ my_disinfo.fprintf_func = fprintf_func; ++ my_disinfo.stream = stream; ++ my_disinfo.target = pc; ++ disassemble_one (insn, &my_disinfo); ++} +diff --git a/opcodes/loongarch-opc.c b/opcodes/loongarch-opc.c +new file mode 100644 +index 00000000..2d095dcf +--- /dev/null ++++ b/opcodes/loongarch-opc.c +@@ -0,0 +1,1453 @@ ++#include ++#include "opcode/loongarch.h" ++ ++struct loongarch_ASEs_option LARCH_opts = ++{ ++ .ase_test = 0, ++ .ase_fix = 0, ++ .ase_float = 0, ++ .ase_128vec = 0, ++ .ase_256vec = 0, ++ ++ .addrwidth_is_32 = 0, ++ .addrwidth_is_64 = 0, ++ .rlen_is_32 = 0, ++ .rlen_is_64 = 0, ++ .la_local_with_abs = 0, ++ .la_global_with_pcrel = 0, ++ .la_global_with_abs = 0, ++ ++ .abi_is_lp32 = 0, ++ .abi_is_lp64 = 0, ++}; ++ ++/* 预留。按理来说应该传入足够的信息使得这个函数可以返回指令长度 */ ++size_t loongarch_insn_length (insn_t insn) ++{ ++ return insn ? 4 : 4; /* eliminate warning */ ++} ++ ++const char * const loongarch_r_normal_name[32] = ++{ ++ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", ++ "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", ++ "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", ++ "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31", ++}; ++ ++const char * const loongarch_r_lp64_name[32] = ++{ ++ "$zero", "$ra", "$tp", "$sp", "$a0", "$a1", "$a2", "$a3", ++ "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3", ++ "$t4", "$t5", "$t6", "$t7", "$t8", "$x", "$fp", "$s0", ++ "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$s8", ++}; ++ ++const char * const loongarch_r_lp64_name1[32] = ++{ ++ "", "", "", "", "$v0", "$v1", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++}; ++ ++const char * const loongarch_f_normal_name[32] = ++{ ++ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", ++ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", ++ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", ++ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", ++}; ++ ++const char * const loongarch_f_lp64_name[32] = ++{ ++ "$fa0", "$fa1", "$fa2", "$fa3", "$fa4", "$fa5", "$fa6", "$fa7", ++ "$ft0", "$ft1", "$ft2", "$ft3", "$ft4", "$ft5", "$ft6", "$ft7", ++ "$ft8", "$ft9", "$ft10", "$ft11", "$ft12", "$ft13", "$ft14", "$ft15", ++ "$fs0", "$fs1", "$fs2", "$fs3", "$fs4", "$fs5", "$fs6", "$fs7", ++}; ++ ++const char * const loongarch_f_lp64_name1[32] = ++{ ++ "$fv0", "$fv1", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", ++}; ++ ++const char * const loongarch_c_normal_name[8] = ++{ ++ "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", "$fcc6", "$fcc7", ++}; ++ ++ ++const char * const loongarch_cr_normal_name[4] = ++{ ++ "$scr0", "$scr1", "$scr2", "$scr3", ++}; ++ ++const char * const loongarch_v_normal_name[32] = ++{ ++ "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6", "$vr7", ++ "$vr8", "$vr9", "$vr10", "$vr11", "$vr12", "$vr13", "$vr14", "$vr15", ++ "$vr16", "$vr17", "$vr18", "$vr19", "$vr20", "$vr21", "$vr22", "$vr23", ++ "$vr24", "$vr25", "$vr26", "$vr27", "$vr28", "$vr29", "$vr30", "$vr31", ++}; ++ ++const char * const loongarch_x_normal_name[32] = ++{ ++ "$xr0", "$xr1", "$xr2", "$xr3", "$xr4", "$xr5", "$xr6", "$xr7", ++ "$xr8", "$xr9", "$xr10", "$xr11", "$xr12", "$xr13", "$xr14", "$xr15", ++ "$xr16", "$xr17", "$xr18", "$xr19", "$xr20", "$xr21", "$xr22", "$xr23", ++ "$xr24", "$xr25", "$xr26", "$xr27", "$xr28", "$xr29", "$xr30", "$xr31", ++}; ++ ++#define SOME_INFORMATION 0 ++const int ALWAYS_EXCLUSION = 1; ++static int FIX_FOR_SOME_ARCH = 1; ++ ++static struct loongarch_opcode loongarch_test_opcodes[] = { ++/* match, mask, name, format, macro, include, exclude, pinfo */ ++{ ++0, 0, "a insn always excluded", "r0:5,r5:5,r10:5", ++"insn_1 args_1" ++"insn_2 args_2" ++, 0, &ALWAYS_EXCLUSION, SOME_INFORMATION ++}, ++{ ++0, 0, ++"never disassemble when mask is 0" ++"and we only expand marco when mask is 0 for assemble", ++"r", "macro expand %1" ++, 0, 0, 0 ++}, ++{0, 0, "wrong_insn", "r", ++"throw_error use_fake_insn.for_example.something_goes_wrong_with_%1" ++, 0, 0, 0}, ++ ++{0, 0, "normal_insn", "r", ++"overloading of insn for your arch", &FIX_FOR_SOME_ARCH, 0, 0}, ++{0, 0, "normal_insn", "l", ++"throw_error if_you_dont_want", &FIX_FOR_SOME_ARCH, 0, 0}, ++ ++{0, 0, "normal_insn", "r", "", 0, 0, 0}, ++{0, 0, "normal_insn", "l", "", 0, 0, 0}, ++{0} /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_macro_opcodes[] = { ++/* match, mask, name, format, macro, include, exclude, pinfo */ ++{0, 0, "li.w", "r,sc", "%f", 0, 0, 0}, ++{0, 0, "li.d", "r,sc", "%f", 0, 0, 0}, ++ ++{0, 0, "la", "r,la", "la.global %1,%2", 0, 0, 0}, ++ ++{0, 0, "la.global", "r,la", "la.pcrel %1,%2", &LARCH_opts.la_global_with_pcrel, 0, 0}, ++{0, 0, "la.global", "r,r,la", "la.pcrel %1,%2,%3", &LARCH_opts.la_global_with_pcrel, 0, 0}, ++{0, 0, "la.global", "r,la", "la.abs %1,%2", &LARCH_opts.la_global_with_abs, 0, 0}, ++{0, 0, "la.global", "r,r,la", "la.abs %1,%3", &LARCH_opts.la_global_with_abs, 0, 0}, ++{0, 0, "la.global", "r,l", "la.got %1,%2", 0, 0, 0}, ++{0, 0, "la.global", "r,r,l", "la.got %1,%2,%3", 0, 0, 0}, ++ ++{0, 0, "la.local", "r,la", "la.abs %1,%2", &LARCH_opts.la_local_with_abs, 0, 0}, ++{0, 0, "la.local", "r,r,la", "la.abs %1,%3", &LARCH_opts.la_local_with_abs, 0, 0}, ++{0, 0, "la.local", "r,la", "la.pcrel %1,%2", 0, 0, 0}, ++{0, 0, "la.local", "r,r,la", "la.pcrel %1,%2,%3", 0, 0, 0}, ++ ++{0, 0, "la.abs", "r,la", ++"lu12i.w %1,%%abs(%2)>>12;" ++"ori %1,%1,%%abs(%2)&0xfff;" ++, &LARCH_opts.addrwidth_is_32, 0, 0}, ++{0, 0, "la.abs", "r,la", ++"lu12i.w %1,%%abs(%2)<<32>>44;" ++"ori %1,%1,%%abs(%2)&0xfff;" ++"lu32i.d %1,%%abs(%2)<<12>>44;" ++"lu52i.d %1,%1,%%abs(%2)>>52;" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++{0, 0, "la.pcrel", "r,la", ++"pcaddu12i %1,%%pcrel(%2+0x800)<<32>>44;" ++"addi.w %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);" ++, &LARCH_opts.addrwidth_is_32, 0, 0}, ++ ++{0, 0, "la.pcrel", "r,la", ++"pcaddu12i %1,%%pcrel(%2+0x800)>>12;" ++"addi.d %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++{0, 0, "la.pcrel", "r,r,la", ++"pcaddu12i %1,(%%pcrel(%3)-(%%pcrel(%3+0x80000000)>>32<<32))<<32>>44;" ++"ori %2,$r0,(%%pcrel(%3+4)-(%%pcrel(%3+4+0x80000000)>>32<<32))&0xfff;" ++"lu32i.d %2,%%pcrel(%3+8+0x80000000)<<12>>44;" ++"lu52i.d %2,%2,%%pcrel(%3+12+0x80000000)>>52;" ++"add.d %1,%1,%2;" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++{0, 0, "la.got", "r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))<<32>>44;" ++"ld.w %1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);" ++, &LARCH_opts.addrwidth_is_32, 0, 0}, ++ ++{0, 0, "la.got", "r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))>>12;" ++"ld.d %1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++{0, 0, "la.got", "r,r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%gprel(%3)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+0x80000000)+%%gprel(%3))>>32<<32))<<32>>44;" ++"ori %2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%3)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x80000000)+%%gprel(%3))>>32<<32))&0xfff;" ++"lu32i.d %2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%gprel(%3))<<12>>44;" ++"lu52i.d %2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%gprel(%3))>>52;" ++"ldx.d %1,%1,%2;" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++{0, 0, "la.tls.le", "r,la", ++"lu12i.w %1,%%tprel(%2)>>12;" ++"ori %1,%1,%%tprel(%2)&0xfff" ++, &LARCH_opts.addrwidth_is_32, 0, 0}, ++//{0, 0, "la.tls.le", "r,la", ++//"lu12i.w %1,%%tprel(%2)>>12;" ++//"ori %1,%1,%%tprel(%2)&0xfff" ++//, &LARCH_opts.addrwidth_is_64, 0, 0}, ++{0, 0, "la.tls.le", "r,la", ++"lu12i.w %1,%%tprel(%2)<<32>>44;" ++"ori %1,%1,%%tprel(%2)&0xfff;" ++"lu32i.d %1,%%tprel(%2)<<12>>44;" ++"lu52i.d %1,%1,%%tprel(%2)>>52;" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++{0, 0, "la.tls.ie", "r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgot(%2))<<32>>44;" ++"ld.w %1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%2)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%%tlsgot(%2))>>12<<12);" ++, &LARCH_opts.addrwidth_is_32, 0, 0}, ++ ++{0, 0, "la.tls.ie", "r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgot(%2))>>12;" ++"ld.d %1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%2)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%%tlsgot(%2))>>12<<12);" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++{0, 0, "la.tls.ie", "r,r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%tlsgot(%3)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+0x80000000)+%%tlsgot(%3))>>32<<32))<<32>>44;" ++"ori %2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%3)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x80000000)+%%tlsgot(%3))>>32<<32))&0xfff;" ++"lu32i.d %2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%tlsgot(%3))<<12>>44;" ++"lu52i.d %2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%tlsgot(%3))>>52;" ++"ldx.d %1,%1,%2;" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++{0, 0, "la.tls.ld", "r,l", "la.tls.gd %1,%2", 0, 0, 0}, ++{0, 0, "la.tls.ld", "r,r,l", ++"la.tls.gd %1,%2,%3" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++ ++{0, 0, "la.tls.gd", "r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgd(%2))<<32>>44;" ++"addi.w %1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%2)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%%tlsgd(%2))>>12<<12);" ++, &LARCH_opts.addrwidth_is_32, 0, 0}, ++ ++{0, 0, "la.tls.gd", "r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgd(%2))>>12;" ++"addi.d %1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%2)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x800)+%%tlsgd(%2))>>12<<12);" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++{0, 0, "la.tls.gd", "r,r,l", ++"pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%tlsgd(%3)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+0x80000000)+%%tlsgd(%3))>>32<<32))<<32>>44;" ++"ori %2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%3)-((%%pcrel(_GLOBAL_OFFSET_TABLE_+4+0x80000000)+%%tlsgd(%3))>>32<<32))&0xfff;" ++"lu32i.d %2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%tlsgd(%3))<<12>>44;" ++"lu52i.d %2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%tlsgd(%3))>>52;" ++"add.d %1,%1,%2;" ++, &LARCH_opts.addrwidth_is_64, 0, 0}, ++ ++{0} /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_fix_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x00001000, 0xfffffc00, "clo.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00001400, 0xfffffc00, "clz.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00001800, 0xfffffc00, "cto.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00001c00, 0xfffffc00, "ctz.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00002000, 0xfffffc00, "clo.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00002400, 0xfffffc00, "clz.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00002800, 0xfffffc00, "cto.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00002c00, 0xfffffc00, "ctz.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00003000, 0xfffffc00, "revb.2h", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00003400, 0xfffffc00, "revb.4h", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00003800, 0xfffffc00, "revb.2w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00003c00, 0xfffffc00, "revb.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00004000, 0xfffffc00, "revh.2w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00004400, 0xfffffc00, "revh.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00004800, 0xfffffc00, "bitrev.4b", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00004c00, 0xfffffc00, "bitrev.8b", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00005000, 0xfffffc00, "bitrev.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00005400, 0xfffffc00, "bitrev.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00005800, 0xfffffc00, "ext.w.h", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00005c00, 0xfffffc00, "ext.w.b", "r0:5,r5:5", 0, 0, 0, 0 }, ++ /* or %1,%2,$r0 */ ++ { 0x00150000, 0xfffffc00, "move", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00006000, 0xfffffc00, "rdtimel.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00006400, 0xfffffc00, "rdtimeh.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00006800, 0xfffffc00, "rdtime.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00006c00, 0xfffffc00, "cpucfg", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x00010000, 0xffff801f, "asrtle.d", "r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00018000, 0xffff801f, "asrtgt.d", "r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00040000, 0xfffe0000, "alsl.w", "r0:5,r5:5,r10:5,u15:2+1", 0, 0, 0, 0 }, ++ { 0x00060000, 0xfffe0000, "alsl.wu", "r0:5,r5:5,r10:5,u15:2+1", 0, 0, 0, 0 }, ++ { 0x00080000, 0xfffe0000, "bytepick.w", "r0:5,r5:5,r10:5,u15:2", 0, 0, 0, 0 }, ++ { 0x000c0000, 0xfffc0000, "bytepick.d", "r0:5,r5:5,r10:5,u15:3", 0, 0, 0, 0 }, ++ { 0x00100000, 0xffff8000, "add.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00108000, 0xffff8000, "add.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00110000, 0xffff8000, "sub.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00118000, 0xffff8000, "sub.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00120000, 0xffff8000, "slt", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00128000, 0xffff8000, "sltu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00130000, 0xffff8000, "maskeqz", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00138000, 0xffff8000, "masknez", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00140000, 0xffff8000, "nor", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00148000, 0xffff8000, "and", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00150000, 0xffff8000, "or", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00158000, 0xffff8000, "xor", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00160000, 0xffff8000, "orn", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00168000, 0xffff8000, "andn", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00170000, 0xffff8000, "sll.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00178000, 0xffff8000, "srl.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00180000, 0xffff8000, "sra.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00188000, 0xffff8000, "sll.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00190000, 0xffff8000, "srl.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00198000, 0xffff8000, "sra.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001b0000, 0xffff8000, "rotr.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001b8000, 0xffff8000, "rotr.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001c0000, 0xffff8000, "mul.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001c8000, 0xffff8000, "mulh.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001d0000, 0xffff8000, "mulh.wu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001d8000, 0xffff8000, "mul.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001e0000, 0xffff8000, "mulh.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001e8000, 0xffff8000, "mulh.du", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001f0000, 0xffff8000, "mulw.d.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x001f8000, 0xffff8000, "mulw.d.wu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00200000, 0xffff8000, "div.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00208000, 0xffff8000, "mod.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00210000, 0xffff8000, "div.wu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00218000, 0xffff8000, "mod.wu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00220000, 0xffff8000, "div.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00228000, 0xffff8000, "mod.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00230000, 0xffff8000, "div.du", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00238000, 0xffff8000, "mod.du", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00240000, 0xffff8000, "crc.w.b.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00248000, 0xffff8000, "crc.w.h.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00250000, 0xffff8000, "crc.w.w.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00258000, 0xffff8000, "crc.w.d.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00260000, 0xffff8000, "crcc.w.b.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00268000, 0xffff8000, "crcc.w.h.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00270000, 0xffff8000, "crcc.w.w.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x00278000, 0xffff8000, "crcc.w.d.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x002a0000, 0xffff8000, "break", "u0:15", 0, 0, 0, 0 }, ++ { 0x002a8000, 0xffff8000, "dbcl", "u0:15", 0, 0, 0, 0 }, ++ { 0x002b0000, 0xffff8000, "syscall", "u0:15", 0, 0, 0, 0 }, ++ { 0x002c0000, 0xfffe0000, "alsl.d", "r0:5,r5:5,r10:5,u15:2+1", 0, 0, 0, 0 }, ++ { 0x00408000, 0xffff8000, "slli.w", "r0:5,r5:5,u10:5", 0, 0, 0, 0 }, ++ { 0x00410000, 0xffff0000, "slli.d", "r0:5,r5:5,u10:6", 0, 0, 0, 0 }, ++ { 0x00448000, 0xffff8000, "srli.w", "r0:5,r5:5,u10:5", 0, 0, 0, 0 }, ++ { 0x00450000, 0xffff0000, "srli.d", "r0:5,r5:5,u10:6", 0, 0, 0, 0 }, ++ { 0x00488000, 0xffff8000, "srai.w", "r0:5,r5:5,u10:5", 0, 0, 0, 0 }, ++ { 0x00490000, 0xffff0000, "srai.d", "r0:5,r5:5,u10:6", 0, 0, 0, 0 }, ++ { 0x004c8000, 0xffff8000, "rotri.w", "r0:5,r5:5,u10:5", 0, 0, 0, 0 }, ++ { 0x004d0000, 0xffff0000, "rotri.d", "r0:5,r5:5,u10:6", 0, 0, 0, 0 }, ++ { 0x00600000, 0xffe08000, "bstrins.w", "r0:5,r5:5,u16:5,u10:5", 0, 0, 0, 0 }, ++ { 0x00608000, 0xffe08000, "bstrpick.w", "r0:5,r5:5,u16:5,u10:5", 0, 0, 0, 0 }, ++ { 0x00800000, 0xffc00000, "bstrins.d", "r0:5,r5:5,u16:6,u10:6", 0, 0, 0, 0 }, ++ { 0x00c00000, 0xffc00000, "bstrpick.d", "r0:5,r5:5,u16:6,u10:6", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_single_float_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x01008000, 0xffff8000, "fadd.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01028000, 0xffff8000, "fsub.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01048000, 0xffff8000, "fmul.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01068000, 0xffff8000, "fdiv.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01088000, 0xffff8000, "fmax.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x010a8000, 0xffff8000, "fmin.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x010c8000, 0xffff8000, "fmaxa.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x010e8000, 0xffff8000, "fmina.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01108000, 0xffff8000, "fscaleb.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01128000, 0xffff8000, "fcopysign.s", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01140400, 0xfffffc00, "fabs.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01141400, 0xfffffc00, "fneg.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01142400, 0xfffffc00, "flogb.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01143400, 0xfffffc00, "fclass.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01144400, 0xfffffc00, "fsqrt.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01145400, 0xfffffc00, "frecip.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01146400, 0xfffffc00, "frsqrt.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01149400, 0xfffffc00, "fmov.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0114a400, 0xfffffc00, "movgr2fr.w", "f0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0114ac00, 0xfffffc00, "movgr2frh.w", "f0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0114b400, 0xfffffc00, "movfr2gr.s", "r0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0114bc00, 0xfffffc00, "movfrh2gr.s", "r0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0114c000, 0xfffffc00, "movgr2fcsr", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0114c800, 0xfffffc00, "movfcsr2gr", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0114d000, 0xfffffc18, "movfr2cf", "c0:3,f5:5", 0, 0, 0, 0 }, ++ { 0x0114d400, 0xffffff00, "movcf2fr", "f0:5,c5:3", 0, 0, 0, 0 }, ++ { 0x0114d800, 0xfffffc18, "movgr2cf", "c0:3,r5:5", 0, 0, 0, 0 }, ++ { 0x0114dc00, 0xffffff00, "movcf2gr", "r0:5,c5:3", 0, 0, 0, 0 }, ++ { 0x011a0400, 0xfffffc00, "ftintrm.w.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a2400, 0xfffffc00, "ftintrm.l.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a4400, 0xfffffc00, "ftintrp.w.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a6400, 0xfffffc00, "ftintrp.l.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a8400, 0xfffffc00, "ftintrz.w.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011aa400, 0xfffffc00, "ftintrz.l.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011ac400, 0xfffffc00, "ftintrne.w.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011ae400, 0xfffffc00, "ftintrne.l.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011b0400, 0xfffffc00, "ftint.w.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011b2400, 0xfffffc00, "ftint.l.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011d1000, 0xfffffc00, "ffint.s.w", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011d1800, 0xfffffc00, "ffint.s.l", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011e4400, 0xfffffc00, "frint.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_double_float_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x01010000, 0xffff8000, "fadd.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01030000, 0xffff8000, "fsub.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01050000, 0xffff8000, "fmul.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01070000, 0xffff8000, "fdiv.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01090000, 0xffff8000, "fmax.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x010b0000, 0xffff8000, "fmin.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x010d0000, 0xffff8000, "fmaxa.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x010f0000, 0xffff8000, "fmina.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01110000, 0xffff8000, "fscaleb.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01130000, 0xffff8000, "fcopysign.d", "f0:5,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x01140800, 0xfffffc00, "fabs.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01141800, 0xfffffc00, "fneg.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01142800, 0xfffffc00, "flogb.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01143800, 0xfffffc00, "fclass.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01144800, 0xfffffc00, "fsqrt.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01145800, 0xfffffc00, "frecip.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01146800, 0xfffffc00, "frsqrt.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01149800, 0xfffffc00, "fmov.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0114a800, 0xfffffc00, "movgr2fr.d", "f0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0114b800, 0xfffffc00, "movfr2gr.d", "r0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01191800, 0xfffffc00, "fcvt.s.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x01192400, 0xfffffc00, "fcvt.d.s", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a0800, 0xfffffc00, "ftintrm.w.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a2800, 0xfffffc00, "ftintrm.l.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a4800, 0xfffffc00, "ftintrp.w.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a6800, 0xfffffc00, "ftintrp.l.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011a8800, 0xfffffc00, "ftintrz.w.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011aa800, 0xfffffc00, "ftintrz.l.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011ac800, 0xfffffc00, "ftintrne.w.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011ae800, 0xfffffc00, "ftintrne.l.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011b0800, 0xfffffc00, "ftint.w.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011b2800, 0xfffffc00, "ftint.l.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011d2000, 0xfffffc00, "ffint.d.w", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011d2800, 0xfffffc00, "ffint.d.l", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0x011e4800, 0xfffffc00, "frint.d", "f0:5,f5:5", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_lmm_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x02000000, 0xffc00000, "slti", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x02400000, 0xffc00000, "sltui", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x02800000, 0xffc00000, "addi.w", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x02c00000, 0xffc00000, "addi.d", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x03000000, 0xffc00000, "lu52i.d", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "nop", "", "andi $r0,$r0,0", 0, 0, 0 }, ++ { 0x03400000, 0xffc00000, "andi", "r0:5,r5:5,u10:12", 0, 0, 0, 0 }, ++ { 0x03800000, 0xffc00000, "ori", "r0:5,r5:5,u10:12", 0, 0, 0, 0 }, ++ { 0x03c00000, 0xffc00000, "xori", "r0:5,r5:5,u10:12", 0, 0, 0, 0 }, ++ { 0x10000000, 0xfc000000, "addu16i.d", "r0:5,r5:5,s10:16", 0, 0, 0, 0 }, ++ { 0x14000000, 0xfe000000, "lu12i.w", "r0:5,s5:20", 0, 0, 0, 0 }, ++ { 0x16000000, 0xfe000000, "lu32i.d", "r0:5,s5:20", 0, 0, 0, 0 }, ++ { 0x18000000, 0xfe000000, "pcaddi", "r0:5,s5:20", 0, 0, 0, 0 }, ++ { 0x1a000000, 0xfe000000, "pcalau12i", "r0:5,s5:20", 0, 0, 0, 0 }, ++ { 0x1c000000, 0xfe000000, "pcaddu12i", "r0:5,s5:20", 0, 0, 0, 0 }, ++ { 0x1e000000, 0xfe000000, "pcaddu18i", "r0:5,s5:20", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_privilege_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x04000000, 0xff0003e0, "csrrd", "r0:5,u10:14", 0, 0, 0, 0 }, ++ { 0x04000020, 0xff0003e0, "csrwr", "r0:5,u10:14", 0, 0, 0, 0 }, ++ { 0x04000000, 0xff000000, "csrxchg", "r0:5,r5:5,u10:14", 0, 0, 0, 0 }, ++ { 0x06000000, 0xffc00000, "cacop", "u0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x06400000, 0xfffc0000, "lddir", "r0:5,r5:5,u10:8", 0, 0, 0, 0 }, ++ { 0x06440000, 0xfffc001f, "ldpte", "r5:5,u10:8", 0, 0, 0, 0 }, ++ { 0x06480000, 0xfffffc00, "iocsrrd.b", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06480400, 0xfffffc00, "iocsrrd.h", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06480800, 0xfffffc00, "iocsrrd.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06480c00, 0xfffffc00, "iocsrrd.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06481000, 0xfffffc00, "iocsrwr.b", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06481400, 0xfffffc00, "iocsrwr.h", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06481800, 0xfffffc00, "iocsrwr.w", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06481c00, 0xfffffc00, "iocsrwr.d", "r0:5,r5:5", 0, 0, 0, 0 }, ++ { 0x06482000, 0xffffffff, "tlbclr", "", 0, 0, 0, 0 }, ++ { 0x06482400, 0xffffffff, "tlbflush", "", 0, 0, 0, 0 }, ++ { 0x06482800, 0xffffffff, "tlbsrch", "", 0, 0, 0, 0 }, ++ { 0x06482c00, 0xffffffff, "tlbrd", "", 0, 0, 0, 0 }, ++ { 0x06483000, 0xffffffff, "tlbwr", "", 0, 0, 0, 0 }, ++ { 0x06483400, 0xffffffff, "tlbfill", "", 0, 0, 0, 0 }, ++ { 0x06483800, 0xffffffff, "ertn", "", 0, 0, 0, 0 }, ++ { 0x06488000, 0xffff8000, "idle", "u0:15", 0, 0, 0, 0 }, ++ { 0x06498000, 0xffff8000, "invtlb", "u0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_4opt_single_float_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x08100000, 0xfff00000, "fmadd.s", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x08500000, 0xfff00000, "fmsub.s", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x08900000, 0xfff00000, "fnmadd.s", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x08d00000, 0xfff00000, "fnmsub.s", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x0c100000, 0xffff8018, "fcmp.caf.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c108000, 0xffff8018, "fcmp.saf.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c110000, 0xffff8018, "fcmp.clt.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c118000, 0xffff8018, "fcmp.slt.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c118000, 0xffff8018, "fcmp.sgt.s", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c120000, 0xffff8018, "fcmp.ceq.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c128000, 0xffff8018, "fcmp.seq.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c130000, 0xffff8018, "fcmp.cle.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c138000, 0xffff8018, "fcmp.sle.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c138000, 0xffff8018, "fcmp.sge.s", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c140000, 0xffff8018, "fcmp.cun.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c148000, 0xffff8018, "fcmp.sun.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c150000, 0xffff8018, "fcmp.cult.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c150000, 0xffff8018, "fcmp.cugt.s", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c158000, 0xffff8018, "fcmp.sult.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c160000, 0xffff8018, "fcmp.cueq.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c168000, 0xffff8018, "fcmp.sueq.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c170000, 0xffff8018, "fcmp.cule.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c170000, 0xffff8018, "fcmp.cuge.s", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c178000, 0xffff8018, "fcmp.sule.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c180000, 0xffff8018, "fcmp.cne.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c188000, 0xffff8018, "fcmp.sne.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c1a0000, 0xffff8018, "fcmp.cor.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c1a8000, 0xffff8018, "fcmp.sor.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c1c0000, 0xffff8018, "fcmp.cune.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c1c8000, 0xffff8018, "fcmp.sune.s", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0d000000, 0xfffc0000, "fsel", "f0:5,f5:5,f10:5,c15:3", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++static struct loongarch_opcode loongarch_4opt_double_float_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x08200000, 0xfff00000, "fmadd.d", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x08600000, 0xfff00000, "fmsub.d", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x08a00000, 0xfff00000, "fnmadd.d", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x08e00000, 0xfff00000, "fnmsub.d", "f0:5,f5:5,f10:5,f15:5", 0, 0, 0, 0 }, ++ { 0x0c200000, 0xffff8018, "fcmp.caf.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c208000, 0xffff8018, "fcmp.saf.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c210000, 0xffff8018, "fcmp.clt.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c218000, 0xffff8018, "fcmp.slt.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c218000, 0xffff8018, "fcmp.sgt.d", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c220000, 0xffff8018, "fcmp.ceq.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c228000, 0xffff8018, "fcmp.seq.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c230000, 0xffff8018, "fcmp.cle.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c238000, 0xffff8018, "fcmp.sle.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c238000, 0xffff8018, "fcmp.sge.d", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c240000, 0xffff8018, "fcmp.cun.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c248000, 0xffff8018, "fcmp.sun.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c250000, 0xffff8018, "fcmp.cult.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c250000, 0xffff8018, "fcmp.cugt.d", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c258000, 0xffff8018, "fcmp.sult.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c260000, 0xffff8018, "fcmp.cueq.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c268000, 0xffff8018, "fcmp.sueq.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c270000, 0xffff8018, "fcmp.cule.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c270000, 0xffff8018, "fcmp.cuge.d", "c0:3,f10:5,f5:5", 0, 0, 0, 0 }, ++ { 0x0c278000, 0xffff8018, "fcmp.sule.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c280000, 0xffff8018, "fcmp.cne.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c288000, 0xffff8018, "fcmp.sne.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c2a0000, 0xffff8018, "fcmp.cor.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c2a8000, 0xffff8018, "fcmp.sor.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c2c0000, 0xffff8018, "fcmp.cune.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0x0c2c8000, 0xffff8018, "fcmp.sune.d", "c0:3,f5:5,f10:5", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_load_store_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x20000000, 0xff000000, "ll.w", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x21000000, 0xff000000, "sc.w", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x22000000, 0xff000000, "ll.d", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x23000000, 0xff000000, "sc.d", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x24000000, 0xff000000, "ldptr.w", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x25000000, 0xff000000, "stptr.w", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x26000000, 0xff000000, "ldptr.d", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x27000000, 0xff000000, "stptr.d", "r0:5,r5:5,s10:14<<2", 0, 0, 0, 0 }, ++ { 0x28000000, 0xffc00000, "ld.b", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x28400000, 0xffc00000, "ld.h", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x28800000, 0xffc00000, "ld.w", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x28c00000, 0xffc00000, "ld.d", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x29000000, 0xffc00000, "st.b", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x29400000, 0xffc00000, "st.h", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x29800000, 0xffc00000, "st.w", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x29c00000, 0xffc00000, "st.d", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x2a000000, 0xffc00000, "ld.bu", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x2a400000, 0xffc00000, "ld.hu", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x2a800000, 0xffc00000, "ld.wu", "r0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x2ac00000, 0xffc00000, "preld", "u0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x38000000, 0xffff8000, "ldx.b", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38040000, 0xffff8000, "ldx.h", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38080000, 0xffff8000, "ldx.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x380c0000, 0xffff8000, "ldx.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38100000, 0xffff8000, "stx.b", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38140000, 0xffff8000, "stx.h", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38180000, 0xffff8000, "stx.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x381c0000, 0xffff8000, "stx.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38200000, 0xffff8000, "ldx.bu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38240000, 0xffff8000, "ldx.hu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38280000, 0xffff8000, "ldx.wu", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x382c0000, 0xffff8000, "preldx", "u0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amswap.w", "r,r,r,u0:0", "amswap.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38600000, 0xffff8000, "amswap.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amswap.d", "r,r,r,u0:0", "amswap.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38608000, 0xffff8000, "amswap.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amadd.w", "r,r,r,u0:0", "amadd.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38610000, 0xffff8000, "amadd.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amadd.d", "r,r,r,u0:0", "amadd.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38618000, 0xffff8000, "amadd.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amand.w", "r,r,r,u0:0", "amand.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38620000, 0xffff8000, "amand.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amand.d", "r,r,r,u0:0", "amand.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38628000, 0xffff8000, "amand.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amor.w", "r,r,r,u0:0", "amor.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38630000, 0xffff8000, "amor.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amor.d", "r,r,r,u0:0", "amor.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38638000, 0xffff8000, "amor.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amxor.w", "r,r,r,u0:0", "amxor.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38640000, 0xffff8000, "amxor.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amxor.d", "r,r,r,u0:0", "amxor.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38648000, 0xffff8000, "amxor.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax.w", "r,r,r,u0:0", "ammax.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38650000, 0xffff8000, "ammax.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax.d", "r,r,r,u0:0", "ammax.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38658000, 0xffff8000, "ammax.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin.w", "r,r,r,u0:0", "ammin.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38660000, 0xffff8000, "ammin.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin.d", "r,r,r,u0:0", "ammin.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38668000, 0xffff8000, "ammin.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax.wu", "r,r,r,u0:0", "ammax.wu %1,%2,%3", 0, 0, 0 }, ++ { 0x38670000, 0xffff8000, "ammax.wu", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax.du", "r,r,r,u0:0", "ammax.du %1,%2,%3", 0, 0, 0 }, ++ { 0x38678000, 0xffff8000, "ammax.du", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin.wu", "r,r,r,u0:0", "ammin.wu %1,%2,%3", 0, 0, 0 }, ++ { 0x38680000, 0xffff8000, "ammin.wu", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin.du", "r,r,r,u0:0", "ammin.du %1,%2,%3", 0, 0, 0 }, ++ { 0x38688000, 0xffff8000, "ammin.du", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amswap_db.w", "r,r,r,u0:0", "amswap_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x38690000, 0xffff8000, "amswap_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amswap_db.d", "r,r,r,u0:0", "amswap_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x38698000, 0xffff8000, "amswap_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amadd_db.w", "r,r,r,u0:0", "amadd_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x386a0000, 0xffff8000, "amadd_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amadd_db.d", "r,r,r,u0:0", "amadd_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x386a8000, 0xffff8000, "amadd_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amand_db.w", "r,r,r,u0:0", "amand_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x386b0000, 0xffff8000, "amand_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amand_db.d", "r,r,r,u0:0", "amand_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x386b8000, 0xffff8000, "amand_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amor_db.w", "r,r,r,u0:0", "amor_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x386c0000, 0xffff8000, "amor_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amor_db.d", "r,r,r,u0:0", "amor_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x386c8000, 0xffff8000, "amor_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amxor_db.w", "r,r,r,u0:0", "amxor_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x386d0000, 0xffff8000, "amxor_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "amxor_db.d", "r,r,r,u0:0", "amxor_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x386d8000, 0xffff8000, "amxor_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax_db.w", "r,r,r,u0:0", "ammax_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x386e0000, 0xffff8000, "ammax_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax_db.d", "r,r,r,u0:0", "ammax_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x386e8000, 0xffff8000, "ammax_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin_db.w", "r,r,r,u0:0", "ammin_db.w %1,%2,%3", 0, 0, 0 }, ++ { 0x386f0000, 0xffff8000, "ammin_db.w", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin_db.d", "r,r,r,u0:0", "ammin_db.d %1,%2,%3", 0, 0, 0 }, ++ { 0x386f8000, 0xffff8000, "ammin_db.d", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax_db.wu", "r,r,r,u0:0", "ammax_db.wu %1,%2,%3", 0, 0, 0 }, ++ { 0x38700000, 0xffff8000, "ammax_db.wu", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammax_db.du", "r,r,r,u0:0", "ammax_db.du %1,%2,%3", 0, 0, 0 }, ++ { 0x38708000, 0xffff8000, "ammax_db.du", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin_db.wu", "r,r,r,u0:0", "ammin_db.wu %1,%2,%3", 0, 0, 0 }, ++ { 0x38710000, 0xffff8000, "ammin_db.wu", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ammin_db.du", "r,r,r,u0:0", "ammin_db.du %1,%2,%3", 0, 0, 0 }, ++ { 0x38718000, 0xffff8000, "ammin_db.du", "r0:5,r10:5,r5:5", 0, 0, 0, 0 }, ++ { 0x38720000, 0xffff8000, "dbar", "u0:15", 0, 0, 0, 0 }, ++ { 0x38728000, 0xffff8000, "ibar", "u0:15", 0, 0, 0, 0 }, ++ { 0x38780000, 0xffff8000, "ldgt.b", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38788000, 0xffff8000, "ldgt.h", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38790000, 0xffff8000, "ldgt.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x38798000, 0xffff8000, "ldgt.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387a0000, 0xffff8000, "ldle.b", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387a8000, 0xffff8000, "ldle.h", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387b0000, 0xffff8000, "ldle.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387b8000, 0xffff8000, "ldle.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387c0000, 0xffff8000, "stgt.b", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387c8000, 0xffff8000, "stgt.h", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387d0000, 0xffff8000, "stgt.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387d8000, 0xffff8000, "stgt.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387e0000, 0xffff8000, "stle.b", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387e8000, 0xffff8000, "stle.h", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387f0000, 0xffff8000, "stle.w", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0x387f8000, 0xffff8000, "stle.d", "r0:5,r5:5,r10:5", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_single_float_load_store_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x2b000000, 0xffc00000, "fld.s", "f0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x2b400000, 0xffc00000, "fst.s", "f0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x38300000, 0xffff8000, "fldx.s", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38380000, 0xffff8000, "fstx.s", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38740000, 0xffff8000, "fldgt.s", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38750000, 0xffff8000, "fldle.s", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38760000, 0xffff8000, "fstgt.s", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38770000, 0xffff8000, "fstle.s", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_double_float_load_store_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x2b800000, 0xffc00000, "fld.d", "f0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x2bc00000, 0xffc00000, "fst.d", "f0:5,r5:5,s10:12", 0, 0, 0, 0 }, ++ { 0x38340000, 0xffff8000, "fldx.d", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x383c0000, 0xffff8000, "fstx.d", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38748000, 0xffff8000, "fldgt.d", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38758000, 0xffff8000, "fldle.d", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38768000, 0xffff8000, "fstgt.d", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0x38778000, 0xffff8000, "fstle.d", "f0:5,r5:5,r10:5", 0, &LARCH_opts.ase_lp64, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_float_jmp_opcodes[] = ++{ ++ { 0x0, 0x0, "bceqz", "c,la", "bceqz %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x48000000, 0xfc000300, "bceqz", "c5:3,sb0:5|10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bcnez", "c,la", "bcnez %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x48000100, 0xfc000300, "bcnez", "c5:3,sb0:5|10:16<<2", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++ ++static struct loongarch_opcode loongarch_jmp_opcodes[] = ++{ ++ /* match, mask, name, format, macro, include, exclude, pinfo. */ ++ { 0x0, 0x0, "bltz", "r,la", "bltz %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x60000000, 0xfc00001f, "bltz", "r5:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bgtz", "r,la", "bgtz %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x60000000, 0xfc0003e0, "bgtz", "r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bgez", "r,la", "bgez %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x64000000, 0xfc00001f, "bgez", "r5:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "blez", "r,la", "blez %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x64000000, 0xfc0003e0, "blez", "r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "beqz", "r,la", "beqz %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x40000000, 0xfc000000, "beqz", "r5:5,sb0:5|10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bnez", "r,la", "bnez %1,%%pcrel(%2)", 0, 0, 0 }, ++ { 0x44000000, 0xfc000000, "bnez", "r5:5,sb0:5|10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "jr", "r", "jirl $r0,%1,0", 0, 0, 0 }, ++ { 0x50000000, 0xfc000000, "b", "sb0:10|10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "b", "la", "b %%pcrel(%1)", 0, 0, 0 }, ++ { 0x4c000000, 0xfc000000, "jirl", "r0:5,r5:5,s10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bl", "la", "bl %%pcrel(%1)", 0, 0, 0 }, ++ { 0x54000000, 0xfc000000, "bl", "sb0:10|10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "beq", "r,r,la", "beq %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x58000000, 0xfc000000, "beq", "r5:5,r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bne", "r,r,la", "bne %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x5c000000, 0xfc000000, "bne", "r5:5,r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "blt", "r,r,la", "blt %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x60000000, 0xfc000000, "blt", "r5:5,r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bgt", "r,r,la", "bgt %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x60000000, 0xfc000000, "bgt", "r0:5,r5:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bge", "r,r,la", "bge %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x64000000, 0xfc000000, "bge", "r5:5,r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "ble", "r,r,la", "ble %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x64000000, 0xfc000000, "ble", "r0:5,r5:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bltu", "r,r,la", "bltu %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x68000000, 0xfc000000, "bltu", "r5:5,r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bgtu", "r,r,la", "bgtu %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x68000000, 0xfc000000, "bgtu", "r0:5,r5:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bgeu", "r,r,la", "bgeu %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x6c000000, 0xfc000000, "bgeu", "r5:5,r0:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0x0, 0x0, "bleu", "r,r,la", "bleu %1,%2,%%pcrel(%3)", 0, 0, 0 }, ++ { 0x6c000000, 0xfc000000, "bleu", "r0:5,r5:5,sb10:16<<2", 0, 0, 0, 0 }, ++ { 0 } /* Terminate the list. */ ++}; ++static struct loongarch_opcode loongarch_128vec_opcodes[] = { ++/* match, mask, name, format, macro, include, exclude, pinfo */ ++{0x70000000, 0xffff8000, "vseq.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70008000, 0xffff8000, "vseq.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70010000, 0xffff8000, "vseq.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70018000, 0xffff8000, "vseq.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70020000, 0xffff8000, "vsle.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70028000, 0xffff8000, "vsle.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70030000, 0xffff8000, "vsle.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70038000, 0xffff8000, "vsle.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70040000, 0xffff8000, "vsle.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70048000, 0xffff8000, "vsle.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70050000, 0xffff8000, "vsle.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70058000, 0xffff8000, "vsle.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70060000, 0xffff8000, "vslt.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70068000, 0xffff8000, "vslt.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70070000, 0xffff8000, "vslt.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70078000, 0xffff8000, "vslt.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70080000, 0xffff8000, "vslt.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70088000, 0xffff8000, "vslt.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70090000, 0xffff8000, "vslt.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70098000, 0xffff8000, "vslt.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700a0000, 0xffff8000, "vadd.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700a8000, 0xffff8000, "vadd.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700b0000, 0xffff8000, "vadd.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700b8000, 0xffff8000, "vadd.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700c0000, 0xffff8000, "vsub.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700c8000, 0xffff8000, "vsub.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700d0000, 0xffff8000, "vsub.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x700d8000, 0xffff8000, "vsub.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70460000, 0xffff8000, "vsadd.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70468000, 0xffff8000, "vsadd.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70470000, 0xffff8000, "vsadd.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70478000, 0xffff8000, "vsadd.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70480000, 0xffff8000, "vssub.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70488000, 0xffff8000, "vssub.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70490000, 0xffff8000, "vssub.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70498000, 0xffff8000, "vssub.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704a0000, 0xffff8000, "vsadd.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704a8000, 0xffff8000, "vsadd.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704b0000, 0xffff8000, "vsadd.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704b8000, 0xffff8000, "vsadd.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704c0000, 0xffff8000, "vssub.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704c8000, 0xffff8000, "vssub.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704d0000, 0xffff8000, "vssub.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x704d8000, 0xffff8000, "vssub.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70540000, 0xffff8000, "vhaddw.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70548000, 0xffff8000, "vhaddw.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70550000, 0xffff8000, "vhaddw.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70558000, 0xffff8000, "vhaddw.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70560000, 0xffff8000, "vhsubw.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70568000, 0xffff8000, "vhsubw.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70570000, 0xffff8000, "vhsubw.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70578000, 0xffff8000, "vhsubw.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70580000, 0xffff8000, "vhaddw.hu.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70588000, 0xffff8000, "vhaddw.wu.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70590000, 0xffff8000, "vhaddw.du.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70598000, 0xffff8000, "vhaddw.qu.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705a0000, 0xffff8000, "vhsubw.hu.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705a8000, 0xffff8000, "vhsubw.wu.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705b0000, 0xffff8000, "vhsubw.du.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705b8000, 0xffff8000, "vhsubw.qu.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705c0000, 0xffff8000, "vadda.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705c8000, 0xffff8000, "vadda.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705d0000, 0xffff8000, "vadda.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x705d8000, 0xffff8000, "vadda.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70600000, 0xffff8000, "vabsd.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70608000, 0xffff8000, "vabsd.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70610000, 0xffff8000, "vabsd.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70618000, 0xffff8000, "vabsd.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70620000, 0xffff8000, "vabsd.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70628000, 0xffff8000, "vabsd.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70630000, 0xffff8000, "vabsd.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70638000, 0xffff8000, "vabsd.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70640000, 0xffff8000, "vavg.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70648000, 0xffff8000, "vavg.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70650000, 0xffff8000, "vavg.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70658000, 0xffff8000, "vavg.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70660000, 0xffff8000, "vavg.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70668000, 0xffff8000, "vavg.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70670000, 0xffff8000, "vavg.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70678000, 0xffff8000, "vavg.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70680000, 0xffff8000, "vavgr.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70688000, 0xffff8000, "vavgr.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70690000, 0xffff8000, "vavgr.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70698000, 0xffff8000, "vavgr.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x706a0000, 0xffff8000, "vavgr.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x706a8000, 0xffff8000, "vavgr.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x706b0000, 0xffff8000, "vavgr.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x706b8000, 0xffff8000, "vavgr.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70700000, 0xffff8000, "vmax.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70708000, 0xffff8000, "vmax.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70710000, 0xffff8000, "vmax.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70718000, 0xffff8000, "vmax.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70720000, 0xffff8000, "vmin.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70728000, 0xffff8000, "vmin.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70730000, 0xffff8000, "vmin.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70738000, 0xffff8000, "vmin.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70740000, 0xffff8000, "vmax.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70748000, 0xffff8000, "vmax.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70750000, 0xffff8000, "vmax.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70758000, 0xffff8000, "vmax.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70760000, 0xffff8000, "vmin.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70768000, 0xffff8000, "vmin.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70770000, 0xffff8000, "vmin.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70778000, 0xffff8000, "vmin.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70840000, 0xffff8000, "vmul.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70848000, 0xffff8000, "vmul.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70850000, 0xffff8000, "vmul.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70858000, 0xffff8000, "vmul.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70860000, 0xffff8000, "vmuh.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70868000, 0xffff8000, "vmuh.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70870000, 0xffff8000, "vmuh.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70878000, 0xffff8000, "vmuh.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70880000, 0xffff8000, "vmuh.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70888000, 0xffff8000, "vmuh.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70890000, 0xffff8000, "vmuh.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70898000, 0xffff8000, "vmuh.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a80000, 0xffff8000, "vmadd.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a88000, 0xffff8000, "vmadd.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a90000, 0xffff8000, "vmadd.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a98000, 0xffff8000, "vmadd.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70aa0000, 0xffff8000, "vmsub.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70aa8000, 0xffff8000, "vmsub.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ab0000, 0xffff8000, "vmsub.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ab8000, 0xffff8000, "vmsub.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e00000, 0xffff8000, "vdiv.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e08000, 0xffff8000, "vdiv.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e10000, 0xffff8000, "vdiv.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e18000, 0xffff8000, "vdiv.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e20000, 0xffff8000, "vmod.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e28000, 0xffff8000, "vmod.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e30000, 0xffff8000, "vmod.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e38000, 0xffff8000, "vmod.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e40000, 0xffff8000, "vdiv.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e48000, 0xffff8000, "vdiv.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e50000, 0xffff8000, "vdiv.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e58000, 0xffff8000, "vdiv.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e60000, 0xffff8000, "vmod.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e68000, 0xffff8000, "vmod.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e70000, 0xffff8000, "vmod.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e78000, 0xffff8000, "vmod.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e80000, 0xffff8000, "vsll.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e88000, 0xffff8000, "vsll.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e90000, 0xffff8000, "vsll.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70e98000, 0xffff8000, "vsll.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ea0000, 0xffff8000, "vsrl.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ea8000, 0xffff8000, "vsrl.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70eb0000, 0xffff8000, "vsrl.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70eb8000, 0xffff8000, "vsrl.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ec0000, 0xffff8000, "vsra.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ec8000, 0xffff8000, "vsra.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ed0000, 0xffff8000, "vsra.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ed8000, 0xffff8000, "vsra.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ee0000, 0xffff8000, "vrotr.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ee8000, 0xffff8000, "vrotr.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ef0000, 0xffff8000, "vrotr.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ef8000, 0xffff8000, "vrotr.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f00000, 0xffff8000, "vsrlr.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f08000, 0xffff8000, "vsrlr.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f10000, 0xffff8000, "vsrlr.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f18000, 0xffff8000, "vsrlr.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f20000, 0xffff8000, "vsrar.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f28000, 0xffff8000, "vsrar.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f30000, 0xffff8000, "vsrar.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f38000, 0xffff8000, "vsrar.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f48000, 0xffff8000, "vsrln.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f50000, 0xffff8000, "vsrln.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f58000, 0xffff8000, "vsrln.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f68000, 0xffff8000, "vsran.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f70000, 0xffff8000, "vsran.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f78000, 0xffff8000, "vsran.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f88000, 0xffff8000, "vsrlrn.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f90000, 0xffff8000, "vsrlrn.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70f98000, 0xffff8000, "vsrlrn.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fa8000, 0xffff8000, "vsrarn.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fb0000, 0xffff8000, "vsrarn.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fb8000, 0xffff8000, "vsrarn.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fc8000, 0xffff8000, "vssrln.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fd0000, 0xffff8000, "vssrln.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fd8000, 0xffff8000, "vssrln.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70fe8000, 0xffff8000, "vssran.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ff0000, 0xffff8000, "vssran.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ff8000, 0xffff8000, "vssran.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71008000, 0xffff8000, "vssrlrn.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71010000, 0xffff8000, "vssrlrn.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71018000, 0xffff8000, "vssrlrn.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71028000, 0xffff8000, "vssrarn.b.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71030000, 0xffff8000, "vssrarn.h.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71038000, 0xffff8000, "vssrarn.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71048000, 0xffff8000, "vssrln.bu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71050000, 0xffff8000, "vssrln.hu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71058000, 0xffff8000, "vssrln.wu.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71068000, 0xffff8000, "vssran.bu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71070000, 0xffff8000, "vssran.hu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71078000, 0xffff8000, "vssran.wu.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71088000, 0xffff8000, "vssrlrn.bu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71090000, 0xffff8000, "vssrlrn.hu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71098000, 0xffff8000, "vssrlrn.wu.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710a8000, 0xffff8000, "vssrarn.bu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710b0000, 0xffff8000, "vssrarn.hu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710b8000, 0xffff8000, "vssrarn.wu.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710c0000, 0xffff8000, "vbitclr.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710c8000, 0xffff8000, "vbitclr.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710d0000, 0xffff8000, "vbitclr.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710d8000, 0xffff8000, "vbitclr.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710e0000, 0xffff8000, "vbitset.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710e8000, 0xffff8000, "vbitset.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710f0000, 0xffff8000, "vbitset.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x710f8000, 0xffff8000, "vbitset.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71100000, 0xffff8000, "vbitrev.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71108000, 0xffff8000, "vbitrev.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71110000, 0xffff8000, "vbitrev.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71118000, 0xffff8000, "vbitrev.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71160000, 0xffff8000, "vpackev.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71168000, 0xffff8000, "vpackev.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71170000, 0xffff8000, "vpackev.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71178000, 0xffff8000, "vpackev.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71180000, 0xffff8000, "vpackod.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71188000, 0xffff8000, "vpackod.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71190000, 0xffff8000, "vpackod.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71198000, 0xffff8000, "vpackod.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711a0000, 0xffff8000, "vilvl.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711a8000, 0xffff8000, "vilvl.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711b0000, 0xffff8000, "vilvl.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711b8000, 0xffff8000, "vilvl.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711c0000, 0xffff8000, "vilvh.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711c8000, 0xffff8000, "vilvh.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711d0000, 0xffff8000, "vilvh.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711d8000, 0xffff8000, "vilvh.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711e0000, 0xffff8000, "vpickev.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711e8000, 0xffff8000, "vpickev.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711f0000, 0xffff8000, "vpickev.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x711f8000, 0xffff8000, "vpickev.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71200000, 0xffff8000, "vpickod.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71208000, 0xffff8000, "vpickod.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71210000, 0xffff8000, "vpickod.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71218000, 0xffff8000, "vpickod.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71220000, 0xffff8000, "vreplve.b", "v0:5,v5:5,r10:5", 0, 0, 0, 0}, ++{0x71228000, 0xffff8000, "vreplve.h", "v0:5,v5:5,r10:5", 0, 0, 0, 0}, ++{0x71230000, 0xffff8000, "vreplve.w", "v0:5,v5:5,r10:5", 0, 0, 0, 0}, ++{0x71238000, 0xffff8000, "vreplve.d", "v0:5,v5:5,r10:5", 0, 0, 0, 0}, ++{0x71260000, 0xffff8000, "vand.v", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71268000, 0xffff8000, "vor.v", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71270000, 0xffff8000, "vxor.v", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71278000, 0xffff8000, "vnor.v", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71280000, 0xffff8000, "vandn.v", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71288000, 0xffff8000, "vorn.v", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712b0000, 0xffff8000, "vfrstp.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712b8000, 0xffff8000, "vfrstp.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712d0000, 0xffff8000, "vadd.q", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712d8000, 0xffff8000, "vsub.q", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712e0000, 0xffff8000, "vsigncov.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712e8000, 0xffff8000, "vsigncov.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712f0000, 0xffff8000, "vsigncov.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x712f8000, 0xffff8000, "vsigncov.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71308000, 0xffff8000, "vfadd.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71310000, 0xffff8000, "vfadd.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71328000, 0xffff8000, "vfsub.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71330000, 0xffff8000, "vfsub.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71388000, 0xffff8000, "vfmul.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71390000, 0xffff8000, "vfmul.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x713a8000, 0xffff8000, "vfdiv.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x713b0000, 0xffff8000, "vfdiv.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x713c8000, 0xffff8000, "vfmax.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x713d0000, 0xffff8000, "vfmax.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x713e8000, 0xffff8000, "vfmin.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x713f0000, 0xffff8000, "vfmin.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71408000, 0xffff8000, "vfmaxa.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71410000, 0xffff8000, "vfmaxa.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71428000, 0xffff8000, "vfmina.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71430000, 0xffff8000, "vfmina.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71460000, 0xffff8000, "vfcvt.h.s", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71468000, 0xffff8000, "vfcvt.s.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71480000, 0xffff8000, "vffint.s.l", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x71498000, 0xffff8000, "vftint.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x714a0000, 0xffff8000, "vftintrm.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x714a8000, 0xffff8000, "vftintrp.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x714b0000, 0xffff8000, "vftintrz.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x714b8000, 0xffff8000, "vftintrne.w.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x717a8000, 0xffff8000, "vshuf.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x717b0000, 0xffff8000, "vshuf.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x717b8000, 0xffff8000, "vshuf.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x72800000, 0xffff8000, "vseqi.b", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72808000, 0xffff8000, "vseqi.h", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72810000, 0xffff8000, "vseqi.w", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72818000, 0xffff8000, "vseqi.d", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72820000, 0xffff8000, "vslei.b", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72828000, 0xffff8000, "vslei.h", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72830000, 0xffff8000, "vslei.w", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72838000, 0xffff8000, "vslei.d", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72840000, 0xffff8000, "vslei.bu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72848000, 0xffff8000, "vslei.hu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72850000, 0xffff8000, "vslei.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72858000, 0xffff8000, "vslei.du", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72860000, 0xffff8000, "vslti.b", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72868000, 0xffff8000, "vslti.h", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72870000, 0xffff8000, "vslti.w", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72878000, 0xffff8000, "vslti.d", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72880000, 0xffff8000, "vslti.bu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72888000, 0xffff8000, "vslti.hu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72890000, 0xffff8000, "vslti.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72898000, 0xffff8000, "vslti.du", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728a0000, 0xffff8000, "vaddi.bu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728a8000, 0xffff8000, "vaddi.hu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728b0000, 0xffff8000, "vaddi.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728b8000, 0xffff8000, "vaddi.du", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728c0000, 0xffff8000, "vsubi.bu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728c8000, 0xffff8000, "vsubi.hu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728d0000, 0xffff8000, "vsubi.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728d8000, 0xffff8000, "vsubi.du", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728e0000, 0xffff8000, "vbsll.v", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x728e8000, 0xffff8000, "vbsrl.v", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72900000, 0xffff8000, "vmaxi.b", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72908000, 0xffff8000, "vmaxi.h", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72910000, 0xffff8000, "vmaxi.w", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72918000, 0xffff8000, "vmaxi.d", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72920000, 0xffff8000, "vmini.b", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72928000, 0xffff8000, "vmini.h", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72930000, 0xffff8000, "vmini.w", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72938000, 0xffff8000, "vmini.d", "v0:5,v5:5,s10:5", 0, 0, 0, 0}, ++{0x72940000, 0xffff8000, "vmaxi.bu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72948000, 0xffff8000, "vmaxi.hu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72950000, 0xffff8000, "vmaxi.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72958000, 0xffff8000, "vmaxi.du", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72960000, 0xffff8000, "vmini.bu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72968000, 0xffff8000, "vmini.hu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72970000, 0xffff8000, "vmini.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72978000, 0xffff8000, "vmini.du", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x729a0000, 0xffff8000, "vfrstpi.b", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x729a8000, 0xffff8000, "vfrstpi.h", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x729c0000, 0xfffffc00, "vclo.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c0400, 0xfffffc00, "vclo.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c0800, 0xfffffc00, "vclo.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c0c00, 0xfffffc00, "vclo.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c1000, 0xfffffc00, "vclz.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c1400, 0xfffffc00, "vclz.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c1800, 0xfffffc00, "vclz.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c1c00, 0xfffffc00, "vclz.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c2000, 0xfffffc00, "vpcnt.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c2400, 0xfffffc00, "vpcnt.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c2800, 0xfffffc00, "vpcnt.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c2c00, 0xfffffc00, "vpcnt.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c3000, 0xfffffc00, "vneg.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c3400, 0xfffffc00, "vneg.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c3800, 0xfffffc00, "vneg.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c3c00, 0xfffffc00, "vneg.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c4000, 0xfffffc00, "vmskltz.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c4400, 0xfffffc00, "vmskltz.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c4800, 0xfffffc00, "vmskltz.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c4c00, 0xfffffc00, "vmskltz.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c5000, 0xfffffc00, "vmskgez.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c6000, 0xfffffc00, "vmsknz.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729c9800, 0xfffffc18, "vseteqz.v", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729c9c00, 0xfffffc18, "vsetnez.v", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729ca000, 0xfffffc18, "vsetanyeqz.b", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729ca400, 0xfffffc18, "vsetanyeqz.h", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729ca800, 0xfffffc18, "vsetanyeqz.w", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729cac00, 0xfffffc18, "vsetanyeqz.d", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729cb000, 0xfffffc18, "vsetallnez.b", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729cb400, 0xfffffc18, "vsetallnez.h", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729cb800, 0xfffffc18, "vsetallnez.w", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729cbc00, 0xfffffc18, "vsetallnez.d", "c0:3,v5:5", 0, 0, 0, 0}, ++{0x729cc400, 0xfffffc00, "vflogb.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729cc800, 0xfffffc00, "vflogb.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729cd400, 0xfffffc00, "vfclass.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729cd800, 0xfffffc00, "vfclass.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ce400, 0xfffffc00, "vfsqrt.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ce800, 0xfffffc00, "vfsqrt.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729cf400, 0xfffffc00, "vfrecip.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729cf800, 0xfffffc00, "vfrecip.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d0400, 0xfffffc00, "vfrsqrt.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d0800, 0xfffffc00, "vfrsqrt.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d3400, 0xfffffc00, "vfrint.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d3800, 0xfffffc00, "vfrint.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d4400, 0xfffffc00, "vfrintrm.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d4800, 0xfffffc00, "vfrintrm.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d5400, 0xfffffc00, "vfrintrp.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d5800, 0xfffffc00, "vfrintrp.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d6400, 0xfffffc00, "vfrintrz.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d6800, 0xfffffc00, "vfrintrz.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d7400, 0xfffffc00, "vfrintrne.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729d7800, 0xfffffc00, "vfrintrne.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729de800, 0xfffffc00, "vfcvtl.s.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729dec00, 0xfffffc00, "vfcvth.s.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729df000, 0xfffffc00, "vfcvtl.d.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729df400, 0xfffffc00, "vfcvth.d.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e0000, 0xfffffc00, "vffint.s.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e0400, 0xfffffc00, "vffint.s.wu", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e0800, 0xfffffc00, "vffint.d.l", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e0c00, 0xfffffc00, "vffint.d.lu", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e1000, 0xfffffc00, "vffintl.d.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e1400, 0xfffffc00, "vffinth.d.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e3000, 0xfffffc00, "vftint.w.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e3400, 0xfffffc00, "vftint.l.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e3800, 0xfffffc00, "vftintrm.w.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e3c00, 0xfffffc00, "vftintrm.l.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e4000, 0xfffffc00, "vftintrp.w.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e4400, 0xfffffc00, "vftintrp.l.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e4800, 0xfffffc00, "vftintrz.w.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e4c00, 0xfffffc00, "vftintrz.l.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e5000, 0xfffffc00, "vftintrne.w.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e5400, 0xfffffc00, "vftintrne.l.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e5800, 0xfffffc00, "vftint.wu.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e5c00, 0xfffffc00, "vftint.lu.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e7000, 0xfffffc00, "vftintrz.wu.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e7400, 0xfffffc00, "vftintrz.lu.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e8000, 0xfffffc00, "vftintl.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e8400, 0xfffffc00, "vftinth.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e8800, 0xfffffc00, "vftintrml.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e8c00, 0xfffffc00, "vftintrmh.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e9000, 0xfffffc00, "vftintrpl.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e9400, 0xfffffc00, "vftintrph.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e9800, 0xfffffc00, "vftintrzl.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729e9c00, 0xfffffc00, "vftintrzh.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ea000, 0xfffffc00, "vftintrnel.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ea400, 0xfffffc00, "vftintrneh.l.s", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ee000, 0xfffffc00, "vexth.h.b", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ee400, 0xfffffc00, "vexth.w.h", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ee800, 0xfffffc00, "vexth.d.w", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729eec00, 0xfffffc00, "vexth.q.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ef000, 0xfffffc00, "vexth.hu.bu", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ef400, 0xfffffc00, "vexth.wu.hu", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729ef800, 0xfffffc00, "vexth.du.wu", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729efc00, 0xfffffc00, "vexth.qu.du", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x729f0000, 0xfffffc00, "vreplgr2vr.b", "v0:5,r5:5", 0, 0, 0, 0}, ++{0x729f0400, 0xfffffc00, "vreplgr2vr.h", "v0:5,r5:5", 0, 0, 0, 0}, ++{0x729f0800, 0xfffffc00, "vreplgr2vr.w", "v0:5,r5:5", 0, 0, 0, 0}, ++{0x729f0c00, 0xfffffc00, "vreplgr2vr.d", "v0:5,r5:5", 0, 0, 0, 0}, ++{0x72a02000, 0xffffe000, "vrotri.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x72a04000, 0xffffc000, "vrotri.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x72a08000, 0xffff8000, "vrotri.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72a10000, 0xffff0000, "vrotri.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x72a42000, 0xffffe000, "vsrlri.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x72a44000, 0xffffc000, "vsrlri.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x72a48000, 0xffff8000, "vsrlri.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72a50000, 0xffff0000, "vsrlri.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x72a82000, 0xffffe000, "vsrari.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x72a84000, 0xffffc000, "vsrari.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x72a88000, 0xffff8000, "vsrari.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x72a90000, 0xffff0000, "vsrari.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x72eb8000, 0xffffc000, "vinsgr2vr.b", "v0:5,r5:5,u10:4", 0, 0, 0, 0}, ++{0x72ebc000, 0xffffe000, "vinsgr2vr.h", "v0:5,r5:5,u10:3", 0, 0, 0, 0}, ++{0x72ebe000, 0xfffff000, "vinsgr2vr.w", "v0:5,r5:5,u10:2", 0, 0, 0, 0}, ++{0x72ebf000, 0xfffff800, "vinsgr2vr.d", "v0:5,r5:5,u10:1", 0, 0, 0, 0}, ++{0x72ef8000, 0xffffc000, "vpickve2gr.b", "r0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x72efc000, 0xffffe000, "vpickve2gr.h", "r0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x72efe000, 0xfffff000, "vpickve2gr.w", "r0:5,v5:5,u10:2", 0, 0, 0, 0}, ++{0x72eff000, 0xfffff800, "vpickve2gr.d", "r0:5,v5:5,u10:1", 0, 0, 0, 0}, ++{0x72f38000, 0xffffc000, "vpickve2gr.bu", "r0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x72f3c000, 0xffffe000, "vpickve2gr.hu", "r0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x72f3e000, 0xfffff000, "vpickve2gr.wu", "r0:5,v5:5,u10:2", 0, 0, 0, 0}, ++{0x72f3f000, 0xfffff800, "vpickve2gr.du", "r0:5,v5:5,u10:1", 0, 0, 0, 0}, ++{0x72f78000, 0xffffc000, "vreplvei.b", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x72f7c000, 0xffffe000, "vreplvei.h", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x72f7e000, 0xfffff000, "vreplvei.w", "v0:5,v5:5,u10:2", 0, 0, 0, 0}, ++{0x72f7f000, 0xfffff800, "vreplvei.d", "v0:5,v5:5,u10:1", 0, 0, 0, 0}, ++{0x73082000, 0xffffe000, "vsllwil.h.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73084000, 0xffffc000, "vsllwil.w.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73088000, 0xffff8000, "vsllwil.d.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73090000, 0xfffffc00, "vextl.q.d", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x730c2000, 0xffffe000, "vsllwil.hu.bu", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x730c4000, 0xffffc000, "vsllwil.wu.hu", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x730c8000, 0xffff8000, "vsllwil.du.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x730d0000, 0xfffffc00, "vextl.qu.du", "v0:5,v5:5", 0, 0, 0, 0}, ++{0x73102000, 0xffffe000, "vbitclri.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73104000, 0xffffc000, "vbitclri.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73108000, 0xffff8000, "vbitclri.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73110000, 0xffff0000, "vbitclri.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73142000, 0xffffe000, "vbitseti.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73144000, 0xffffc000, "vbitseti.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73148000, 0xffff8000, "vbitseti.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73150000, 0xffff0000, "vbitseti.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73182000, 0xffffe000, "vbitrevi.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73184000, 0xffffc000, "vbitrevi.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73188000, 0xffff8000, "vbitrevi.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73190000, 0xffff0000, "vbitrevi.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73242000, 0xffffe000, "vsat.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73244000, 0xffffc000, "vsat.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73248000, 0xffff8000, "vsat.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73250000, 0xffff0000, "vsat.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73282000, 0xffffe000, "vsat.bu", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73284000, 0xffffc000, "vsat.hu", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73288000, 0xffff8000, "vsat.wu", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73290000, 0xffff0000, "vsat.du", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x732c2000, 0xffffe000, "vslli.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x732c4000, 0xffffc000, "vslli.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x732c8000, 0xffff8000, "vslli.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x732d0000, 0xffff0000, "vslli.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73302000, 0xffffe000, "vsrli.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73304000, 0xffffc000, "vsrli.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73308000, 0xffff8000, "vsrli.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73310000, 0xffff0000, "vsrli.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73342000, 0xffffe000, "vsrai.b", "v0:5,v5:5,u10:3", 0, 0, 0, 0}, ++{0x73344000, 0xffffc000, "vsrai.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73348000, 0xffff8000, "vsrai.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73350000, 0xffff0000, "vsrai.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73404000, 0xffffc000, "vsrlni.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73408000, 0xffff8000, "vsrlni.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73410000, 0xffff0000, "vsrlni.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73420000, 0xfffe0000, "vsrlni.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73484000, 0xffffc000, "vssrlni.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73488000, 0xffff8000, "vssrlni.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73490000, 0xffff0000, "vssrlni.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x734a0000, 0xfffe0000, "vssrlni.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73444000, 0xffffc000, "vsrlrni.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73448000, 0xffff8000, "vsrlrni.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73450000, 0xffff0000, "vsrlrni.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73460000, 0xfffe0000, "vsrlrni.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x734c4000, 0xffffc000, "vssrlni.bu.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x734c8000, 0xffff8000, "vssrlni.hu.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x734d0000, 0xffff0000, "vssrlni.wu.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x734e0000, 0xfffe0000, "vssrlni.du.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73504000, 0xffffc000, "vssrlrni.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73508000, 0xffff8000, "vssrlrni.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73510000, 0xffff0000, "vssrlrni.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73520000, 0xfffe0000, "vssrlrni.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73544000, 0xffffc000, "vssrlrni.bu.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73548000, 0xffff8000, "vssrlrni.hu.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73550000, 0xffff0000, "vssrlrni.wu.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73560000, 0xfffe0000, "vssrlrni.du.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73584000, 0xffffc000, "vsrani.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73588000, 0xffff8000, "vsrani.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73590000, 0xffff0000, "vsrani.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x735a0000, 0xfffe0000, "vsrani.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x735c4000, 0xffffc000, "vsrarni.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x735c8000, 0xffff8000, "vsrarni.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x735d0000, 0xffff0000, "vsrarni.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x735e0000, 0xfffe0000, "vsrarni.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73604000, 0xffffc000, "vssrani.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73608000, 0xffff8000, "vssrani.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73610000, 0xffff0000, "vssrani.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73620000, 0xfffe0000, "vssrani.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73644000, 0xffffc000, "vssrani.bu.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73648000, 0xffff8000, "vssrani.hu.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73650000, 0xffff0000, "vssrani.wu.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x73660000, 0xfffe0000, "vssrani.du.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73684000, 0xffffc000, "vssrarni.b.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x73688000, 0xffff8000, "vssrarni.h.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x73690000, 0xffff0000, "vssrarni.w.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x736a0000, 0xfffe0000, "vssrarni.d.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x736c4000, 0xffffc000, "vssrarni.bu.h", "v0:5,v5:5,u10:4", 0, 0, 0, 0}, ++{0x736c8000, 0xffff8000, "vssrarni.hu.w", "v0:5,v5:5,u10:5", 0, 0, 0, 0}, ++{0x736d0000, 0xffff0000, "vssrarni.wu.d", "v0:5,v5:5,u10:6", 0, 0, 0, 0}, ++{0x736e0000, 0xfffe0000, "vssrarni.du.q", "v0:5,v5:5,u10:7", 0, 0, 0, 0}, ++{0x73800000, 0xfffc0000, "vextrins.d", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73840000, 0xfffc0000, "vextrins.w", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73880000, 0xfffc0000, "vextrins.h", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x738c0000, 0xfffc0000, "vextrins.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73900000, 0xfffc0000, "vshuf4i.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73940000, 0xfffc0000, "vshuf4i.h", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73980000, 0xfffc0000, "vshuf4i.w", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x739c0000, 0xfffc0000, "vshuf4i.d", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73c40000, 0xfffc0000, "vbitseli.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73d00000, 0xfffc0000, "vandi.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73d40000, 0xfffc0000, "vori.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73d80000, 0xfffc0000, "vxori.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0x73dc0000, 0xfffc0000, "vnori.b", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0, 0, "vrepli.b", "v,s0:10", "vldi %1,(%2)&0x3ff", 0, 0, 0}, ++ ++{0x701e0000, 0xffff8000, "vaddwev.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x701e8000, 0xffff8000, "vaddwev.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x701f0000, 0xffff8000, "vaddwev.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x701f8000, 0xffff8000, "vaddwev.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x702e0000, 0xffff8000, "vaddwev.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x702e8000, 0xffff8000, "vaddwev.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x702f0000, 0xffff8000, "vaddwev.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x702f8000, 0xffff8000, "vaddwev.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x703e0000, 0xffff8000, "vaddwev.h.bu.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x703e8000, 0xffff8000, "vaddwev.w.hu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x703f0000, 0xffff8000, "vaddwev.d.wu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x703f8000, 0xffff8000, "vaddwev.q.du.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70220000, 0xffff8000, "vaddwod.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70228000, 0xffff8000, "vaddwod.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70230000, 0xffff8000, "vaddwod.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70238000, 0xffff8000, "vaddwod.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70320000, 0xffff8000, "vaddwod.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70328000, 0xffff8000, "vaddwod.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70330000, 0xffff8000, "vaddwod.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70338000, 0xffff8000, "vaddwod.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70400000, 0xffff8000, "vaddwod.h.bu.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70408000, 0xffff8000, "vaddwod.w.hu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70410000, 0xffff8000, "vaddwod.d.wu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70418000, 0xffff8000, "vaddwod.q.du.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ac0000, 0xffff8000, "vmaddwev.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ac8000, 0xffff8000, "vmaddwev.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ad0000, 0xffff8000, "vmaddwev.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ad8000, 0xffff8000, "vmaddwev.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b40000, 0xffff8000, "vmaddwev.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b48000, 0xffff8000, "vmaddwev.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b50000, 0xffff8000, "vmaddwev.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b58000, 0xffff8000, "vmaddwev.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70bc0000, 0xffff8000, "vmaddwev.h.bu.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70bc8000, 0xffff8000, "vmaddwev.w.hu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70bd0000, 0xffff8000, "vmaddwev.d.wu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70bd8000, 0xffff8000, "vmaddwev.q.du.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ae0000, 0xffff8000, "vmaddwod.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70ae8000, 0xffff8000, "vmaddwod.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70af0000, 0xffff8000, "vmaddwod.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70af8000, 0xffff8000, "vmaddwod.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b60000, 0xffff8000, "vmaddwod.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b68000, 0xffff8000, "vmaddwod.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b70000, 0xffff8000, "vmaddwod.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70b78000, 0xffff8000, "vmaddwod.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70be0000, 0xffff8000, "vmaddwod.h.bu.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70be8000, 0xffff8000, "vmaddwod.w.hu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70bf0000, 0xffff8000, "vmaddwod.d.wu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70bf8000, 0xffff8000, "vmaddwod.q.du.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70900000, 0xffff8000, "vmulwev.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70908000, 0xffff8000, "vmulwev.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70910000, 0xffff8000, "vmulwev.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70918000, 0xffff8000, "vmulwev.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70980000, 0xffff8000, "vmulwev.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70988000, 0xffff8000, "vmulwev.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70990000, 0xffff8000, "vmulwev.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70998000, 0xffff8000, "vmulwev.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a00000, 0xffff8000, "vmulwev.h.bu.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a08000, 0xffff8000, "vmulwev.w.hu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a10000, 0xffff8000, "vmulwev.d.wu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a18000, 0xffff8000, "vmulwev.q.du.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70920000, 0xffff8000, "vmulwod.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70928000, 0xffff8000, "vmulwod.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70930000, 0xffff8000, "vmulwod.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70938000, 0xffff8000, "vmulwod.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x709a0000, 0xffff8000, "vmulwod.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x709a8000, 0xffff8000, "vmulwod.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x709b0000, 0xffff8000, "vmulwod.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x709b8000, 0xffff8000, "vmulwod.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a20000, 0xffff8000, "vmulwod.h.bu.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a28000, 0xffff8000, "vmulwod.w.hu.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a30000, 0xffff8000, "vmulwod.d.wu.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70a38000, 0xffff8000, "vmulwod.q.du.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70200000, 0xffff8000, "vsubwev.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70208000, 0xffff8000, "vsubwev.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70210000, 0xffff8000, "vsubwev.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70218000, 0xffff8000, "vsubwev.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70300000, 0xffff8000, "vsubwev.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70308000, 0xffff8000, "vsubwev.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70310000, 0xffff8000, "vsubwev.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70318000, 0xffff8000, "vsubwev.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70240000, 0xffff8000, "vsubwod.h.b", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70248000, 0xffff8000, "vsubwod.w.h", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70250000, 0xffff8000, "vsubwod.d.w", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70258000, 0xffff8000, "vsubwod.q.d", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70340000, 0xffff8000, "vsubwod.h.bu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70348000, 0xffff8000, "vsubwod.w.hu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70350000, 0xffff8000, "vsubwod.d.wu", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0x70358000, 0xffff8000, "vsubwod.q.du", "v0:5,v5:5,v10:5", 0, 0, 0, 0}, ++{0, 0, "vrepli.d", "v,s0:10", "vldi %1,((%2)&0x3ff)|0xc00", 0, 0, 0}, ++{0, 0, "vrepli.h", "v,s0:10", "vldi %1,((%2)&0x3ff)|0x400", 0, 0, 0}, ++{0, 0, "vrepli.w", "v,s0:10", "vldi %1,((%2)&0x3ff)|0x800", 0, 0, 0}, ++{0x73e00000, 0xfffc0000, "vldi", "v0:5,s5:13", 0, 0, 0, 0}, ++{0x73e40000, 0xfffc0000, "vpermi.w", "v0:5,v5:5,u10:8", 0, 0, 0, 0}, ++{0} /* Terminate the list. */ ++}; ++ ++struct loongarch_ase loongarch_ASEs[] = { ++ {&LARCH_opts.ase_fix, loongarch_macro_opcodes, 0, 0, {0}, 0, 0}, ++ { &LARCH_opts.ase_ilp32, loongarch_lmm_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_ilp32, loongarch_privilege_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_ilp32, loongarch_load_store_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_ilp32, loongarch_fix_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_ilp32, loongarch_jmp_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_sif, loongarch_float_jmp_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_sif, loongarch_single_float_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_dof, loongarch_double_float_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_sif, loongarch_4opt_single_float_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_dof, loongarch_4opt_double_float_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_sif, loongarch_single_float_load_store_opcodes, 0, 0, { 0 }, 0, 0 }, ++ { &LARCH_opts.ase_dof, loongarch_double_float_load_store_opcodes, 0, 0, { 0 }, 0, 0 }, ++ {&LARCH_opts.ase_128vec, loongarch_128vec_opcodes, 0, 0, {0}, 0, 0}, ++ {&LARCH_opts.ase_256vec, loongarch_256vec_opcodes, 0, 0, {0}, 0, 0}, ++ {0}, ++}; ++ +-- +2.20.1 + diff --git a/binutils.spec b/binutils.spec index 48489100ed0fc100ac4a6dfce931e35a22934361..4832a70d6e5ccb9b60879bfc6fa6742630d74975 100644 --- a/binutils.spec +++ b/binutils.spec @@ -24,7 +24,7 @@ # /usr/bin/aarch64-linux-gnu-ar # /usr/bin/aarch64-linux-gnu-as # [etc] -%define anolis_release .0.1 +%define anolis_release .0.2 %if 0%{!?binutils_target:1} @@ -614,6 +614,8 @@ Patch97: binutils-plugin-error.patch #Add by Anolis Patch1000: 0001-binutils-anolis-rebrand.patch + +Patch98: binutils-loongarch-support.patch #end #---------------------------------------------------------------------------- @@ -850,6 +852,7 @@ using libelf instead of BFD. %patch96 -p1 %patch97 -p1 %patch1000 -p1 +%patch98 -p1 # We cannot run autotools as there is an exact requirement of autoconf-2.59. # FIXME - this is no longer true. Maybe try reinstating autotool use ? @@ -905,7 +908,7 @@ export CFLAGS="$RPM_OPT_FLAGS" CARGS= -case %{binutils_target} in i?86*|sparc*|ppc*|s390*|sh*|arm*|aarch64*) +case %{binutils_target} in i?86*|sparc*|ppc*|s390*|sh*|arm*|aarch64*|loongarch64) CARGS="$CARGS --enable-64-bit-bfd" ;; esac @@ -1299,6 +1302,9 @@ exit 0 #---------------------------------------------------------------------------- %changelog +* Wed Jun 22 2022 Xing Li - 2.30-113.0.2 +- Add loongarch support. + * Wed Apr 13 2022 Xue haolin - 2.30-113.0.1 - Rebrand to Anolis OS.