mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-23 13:21:43 +08:00
RISC-V Profiles document defines number of "extensions" that indicate certain platform properties/capabilities just like 'Zkt' extension from the RISC-V cryptography extensions. This commit defines 20 platform property/capability extensions as defined in the RISC-V Profiles documentation. The only exception: 'Ssstateen' extension is defined separately because it defines a subset (supervisor/hypervisor view) of the 'Smstateen' extension. This is based on the ratified version of RISC-V Profiles: <https://github.com/riscv/riscv-profiles/releases/tag/v1.0> [Definition] "Main memory regions": Main memory regions (in contrast to I/O or vacant memory regions) with both the cacheability and coherence PMAs. [New Unprivileged Extensions] 1. 'Ziccif' "Main memory regions" support instruction fetch and any instruction fetches of naturally aligned power-of-2 sizes up to min(ILEN, XLEN) are atomic. 2. 'Ziccrse' "Main memory regions" provide the eventual success guarantee for LR/SC sequence (RsrvEventual). 3. 'Ziccamoa' "Main memory regions" support all currently-defined AMO operations including swap, logical and arithmetic operations (AMOArithmetic). 4. 'Za64rs' For LR/SC instructions, reservation sets are contiguous, naturally aligned and at most 64-bytes in size. 5. 'Za128rs' Likewise, but reservation sets are at most 128-bytes in size. 6. 'Zicclsm' Misaligned loads / stores to "main memory regions" are supported. Those include both regular scalar and vector accesses but does not include AMOs and other specialized forms of memory accesses. 7. 'Zic64b' Cache blocks are (exactly) 64-bytes in size and naturally aligned. [New Privileged Extensions] 1. 'Svbare' "satp" mode Bare is supported. 2. 'Svade' Page-fault exceptions are raised when a page is accessed when A bit is clear, or written when D bit is clear. 3. 'Ssccptr' "Main memory regions" support hardware page-table reads. 4. 'Sstvecd' "stvec" mode Direct is supported. When "stvec" mode is Direct, "stvec.BASE" is capable of holding any valid 4-byte aligned address. 5. 'Sstvala' "stval" is always written with a nonzero value whenever possible as specified in the Privileged Architecture documentation (version 20211203: see section 4.1.9). 6. 'Sscounterenw' For any "hpmcounter" that is not read-only zero, the corresponding bit in "scounteren" is writable. 7. 'Ssu64xl' "sstatus.UXL" is capable of holding the value 0b10 (UXLEN==64 is supported). 8. 'Shcounterenw' Similar to 'Sscounterenw' but the same rule applies to "hcounteren". 9. 'Shvstvala' Similar to 'Sstvala' but the same rule applies to "vstval". 10. 'Shtvala' "htval" is written with the faulting guest physical address as long as permitted by the ISA (a bit similar to 'Sstvala' and 'Shvstvala'). 11. 'Shvstvecd' Similar to 'Sstvecd' but the same rule applies to "vstvec". 12. 'Shvsatpa' All translation modes supported in "satp" are also supported in "vsatp". 13. 'Shgatpa' For each supported virtual memory scheme SvNN supported in "satp", the corresponding "hgatp" SvNNx4 mode is supported. The "hgatp" mode Bare is also supported. [Implications] (Due to reservation set size constraints) - 'Za64rs' -> 'Za128rs' (Due to the fact that a privileged "extension" directly refers a CSR) - 'Svbare' -> 'Zicsr' - 'Sstvecd' -> 'Zicsr' - 'Sstvala' -> 'Zicsr' - 'Sscounterenw' -> 'Zicsr' - 'Ssu64xl' -> 'Zicsr' (Due to the fact that a privileged "extension" indirectly depends on CSRs) - 'Svade' -> 'Zicsr' (Due to the fact that a privileged "extension" is a hypervisor property) - 'Shcounterenw' -> 'H' - 'Shvstvala' -> 'H' - 'Shtvala' -> 'H' - 'Shvstvecd' -> 'H' - 'Shvsatpa' -> 'H' - 'Shgatpa' -> 'H' bfd/ * elfxx-riscv.c (riscv_implicit_subsets): Updated for property and capability extensions. (riscv_supported_std_z_ext): Added zic64b, ziccamoa, ziccif, zicclsm, ziccrse, za64rs and za128rs extensions. (riscv_supported_std_s_ext): Added shcounterenw, shgatpa, shtvala, shvsatpa, shvstvala, shvstvecd, ssccptr, sscounterenw, sstvala, sstvecd, ssu64xlm svade and svbare extensions. gas/ * testsuite/gas/riscv/imply.d: Updated for property and capability extensions. * testsuite/gas/riscv/imply.s: Likewise. * testsuite/gas/riscv/march-help.l: Likewse.
3083 lines
93 KiB
C
3083 lines
93 KiB
C
/* RISC-V-specific support for ELF.
|
|
Copyright (C) 2011-2024 Free Software Foundation, Inc.
|
|
|
|
Contributed by Andrew Waterman (andrew@sifive.com).
|
|
Based on TILE-Gx and MIPS targets.
|
|
|
|
This file is part of BFD, the Binary File Descriptor library.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 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; see the file COPYING3. If not,
|
|
see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "sysdep.h"
|
|
#include "bfd.h"
|
|
#include "libbfd.h"
|
|
#include "elf-bfd.h"
|
|
#include "elf/riscv.h"
|
|
#include "opcode/riscv.h"
|
|
#include "libiberty.h"
|
|
#include "elfxx-riscv.h"
|
|
#include "safe-ctype.h"
|
|
|
|
#define MINUS_ONE ((bfd_vma)0 - 1)
|
|
|
|
/* Special handler for ADD/SUB relocations that allows them to be filled out
|
|
both in the pre-linked and post-linked file. This is necessary to make
|
|
pre-linked debug info work, as due to linker relaxations we need to emit
|
|
relocations for the debug info. */
|
|
static bfd_reloc_status_type riscv_elf_add_sub_reloc
|
|
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
|
|
static bfd_reloc_status_type riscv_elf_ignore_reloc
|
|
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
|
|
|
|
/* The relocation table used for SHT_RELA sections. */
|
|
|
|
static reloc_howto_type howto_table[] =
|
|
{
|
|
/* No relocation. */
|
|
HOWTO (R_RISCV_NONE, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_NONE", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32 bit relocation. */
|
|
HOWTO (R_RISCV_32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 64 bit relocation. */
|
|
HOWTO (R_RISCV_64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Relocation against a local symbol in a shared object. */
|
|
HOWTO (R_RISCV_RELATIVE, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RELATIVE", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_COPY, /* type */
|
|
0, /* rightshift */
|
|
0, /* this one is variable size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_bitfield, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_COPY", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_JUMP_SLOT, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_bitfield, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_JUMP_SLOT", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Dynamic TLS relocations. */
|
|
HOWTO (R_RISCV_TLS_DTPMOD32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPMOD32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_DTPMOD64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPMOD64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_DTPREL32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPREL32", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_DTPREL64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPREL64", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_TPREL32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_TPREL32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_TPREL64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_TPREL64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLSDESC, /* type */
|
|
0, /* rightshift */
|
|
0, /* size is handled by dynamic linker */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLSDESC", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
EMPTY_HOWTO (13),
|
|
EMPTY_HOWTO (14),
|
|
EMPTY_HOWTO (15),
|
|
|
|
/* 12-bit PC-relative branch offset. */
|
|
HOWTO (R_RISCV_BRANCH, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_BRANCH", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_BTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* 20-bit PC-relative jump offset. */
|
|
HOWTO (R_RISCV_JAL, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_JAL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_JTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* 32-bit PC-relative function call (AUIPC/JALR). */
|
|
HOWTO (R_RISCV_CALL, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_CALL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32),
|
|
/* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* Like R_RISCV_CALL, but not locally binding. */
|
|
HOWTO (R_RISCV_CALL_PLT, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_CALL_PLT", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32),
|
|
/* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative GOT access. */
|
|
HOWTO (R_RISCV_GOT_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_GOT_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative TLS IE GOT access. */
|
|
HOWTO (R_RISCV_TLS_GOT_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_GOT_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative TLS GD GOT reference. */
|
|
HOWTO (R_RISCV_TLS_GD_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_GD_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative reference. */
|
|
HOWTO (R_RISCV_PCREL_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_PCREL_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of a 32-bit PC-relative load or add. */
|
|
HOWTO (R_RISCV_PCREL_LO12_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_PCREL_LO12_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of a 32-bit PC-relative store. */
|
|
HOWTO (R_RISCV_PCREL_LO12_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_PCREL_LO12_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit absolute address. */
|
|
HOWTO (R_RISCV_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 12 bits of 32-bit load or add. */
|
|
HOWTO (R_RISCV_LO12_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_LO12_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 12 bits of 32-bit store. */
|
|
HOWTO (R_RISCV_LO12_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_LO12_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of TLS LE thread pointer offset. */
|
|
HOWTO (R_RISCV_TPREL_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_HI20", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of TLS LE thread pointer offset for loads and adds. */
|
|
HOWTO (R_RISCV_TPREL_LO12_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_LO12_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of TLS LE thread pointer offset for stores. */
|
|
HOWTO (R_RISCV_TPREL_LO12_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_LO12_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* TLS LE thread pointer usage. May be relaxed. */
|
|
HOWTO (R_RISCV_TPREL_ADD, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_ADD", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD8, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD8", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 16-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD16, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD16", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 64-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB8, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB8", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 16-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB16, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB16", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 64-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
EMPTY_HOWTO (41),
|
|
EMPTY_HOWTO (42),
|
|
|
|
/* Indicates an alignment statement. The addend field encodes how many
|
|
bytes of NOPs follow the statement. The desired alignment is the
|
|
addend rounded up to the next power of two. */
|
|
HOWTO (R_RISCV_ALIGN, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_ALIGN", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit PC-relative branch offset. */
|
|
HOWTO (R_RISCV_RVC_BRANCH, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RVC_BRANCH", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_CBTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* 11-bit PC-relative jump offset. */
|
|
HOWTO (R_RISCV_RVC_JUMP, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RVC_JUMP", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_CJTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
EMPTY_HOWTO (46),
|
|
EMPTY_HOWTO (47),
|
|
EMPTY_HOWTO (48),
|
|
EMPTY_HOWTO (49),
|
|
EMPTY_HOWTO (50),
|
|
|
|
/* The paired relocation may be relaxed. */
|
|
HOWTO (R_RISCV_RELAX, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RELAX", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 6-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB6, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB6", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0x3f, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 6-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET6, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET6", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0x3f, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET8, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET8", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 16-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET16, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET16", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit PC relative. */
|
|
HOWTO (R_RISCV_32_PCREL, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_32_PCREL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Relocation against a local ifunc symbol in a shared object. */
|
|
HOWTO (R_RISCV_IRELATIVE, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_IRELATIVE", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Reserved for R_RISCV_PLT32. */
|
|
EMPTY_HOWTO (59),
|
|
|
|
/* N-bit in-place setting, for unsigned-leb128 local label subtraction. */
|
|
HOWTO (R_RISCV_SET_ULEB128, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_ignore_reloc, /* special_function */
|
|
"R_RISCV_SET_ULEB128", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* N-bit in-place addition, for unsigned-leb128 local label subtraction. */
|
|
HOWTO (R_RISCV_SUB_ULEB128, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_ignore_reloc, /* special_function */
|
|
"R_RISCV_SUB_ULEB128", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLSDESC_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLSDESC_HI20", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLSDESC_LOAD_LO12, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLSDESC_LOAD_LO12", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLSDESC_ADD_LO12, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLSDESC_ADD_LO12", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLSDESC_CALL, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLSDESC_CALL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false) /* pcrel_offset */
|
|
};
|
|
|
|
static reloc_howto_type howto_table_internal[] =
|
|
{
|
|
/* R_RISCV_DELETE. */
|
|
EMPTY_HOWTO (0),
|
|
|
|
/* High 6 bits of 18-bit absolute address. */
|
|
HOWTO (R_RISCV_RVC_LUI, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RVC_LUI", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_CITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* GP-relative load. */
|
|
HOWTO (R_RISCV_GPREL_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_GPREL_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* GP-relative store. */
|
|
HOWTO (R_RISCV_GPREL_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_GPREL_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* TP-relative TLS LE load. */
|
|
HOWTO (R_RISCV_TPREL_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* TP-relative TLS LE store. */
|
|
HOWTO (R_RISCV_TPREL_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
};
|
|
|
|
/* A mapping from BFD reloc types to RISC-V ELF reloc types. */
|
|
struct elf_reloc_map
|
|
{
|
|
bfd_reloc_code_real_type bfd_val;
|
|
enum elf_riscv_reloc_type elf_val;
|
|
};
|
|
|
|
static const struct elf_reloc_map riscv_reloc_map[] =
|
|
{
|
|
{ BFD_RELOC_NONE, R_RISCV_NONE },
|
|
{ BFD_RELOC_32, R_RISCV_32 },
|
|
{ BFD_RELOC_64, R_RISCV_64 },
|
|
{ BFD_RELOC_RISCV_ADD8, R_RISCV_ADD8 },
|
|
{ BFD_RELOC_RISCV_ADD16, R_RISCV_ADD16 },
|
|
{ BFD_RELOC_RISCV_ADD32, R_RISCV_ADD32 },
|
|
{ BFD_RELOC_RISCV_ADD64, R_RISCV_ADD64 },
|
|
{ BFD_RELOC_RISCV_SUB8, R_RISCV_SUB8 },
|
|
{ BFD_RELOC_RISCV_SUB16, R_RISCV_SUB16 },
|
|
{ BFD_RELOC_RISCV_SUB32, R_RISCV_SUB32 },
|
|
{ BFD_RELOC_RISCV_SUB64, R_RISCV_SUB64 },
|
|
{ BFD_RELOC_CTOR, R_RISCV_64 },
|
|
{ BFD_RELOC_12_PCREL, R_RISCV_BRANCH },
|
|
{ BFD_RELOC_RISCV_HI20, R_RISCV_HI20 },
|
|
{ BFD_RELOC_RISCV_LO12_I, R_RISCV_LO12_I },
|
|
{ BFD_RELOC_RISCV_LO12_S, R_RISCV_LO12_S },
|
|
{ BFD_RELOC_RISCV_PCREL_LO12_I, R_RISCV_PCREL_LO12_I },
|
|
{ BFD_RELOC_RISCV_PCREL_LO12_S, R_RISCV_PCREL_LO12_S },
|
|
{ BFD_RELOC_RISCV_CALL, R_RISCV_CALL },
|
|
{ BFD_RELOC_RISCV_CALL_PLT, R_RISCV_CALL_PLT },
|
|
{ BFD_RELOC_RISCV_PCREL_HI20, R_RISCV_PCREL_HI20 },
|
|
{ BFD_RELOC_RISCV_JMP, R_RISCV_JAL },
|
|
{ BFD_RELOC_RISCV_GOT_HI20, R_RISCV_GOT_HI20 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPMOD32, R_RISCV_TLS_DTPMOD32 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPREL32, R_RISCV_TLS_DTPREL32 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPMOD64, R_RISCV_TLS_DTPMOD64 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPREL64, R_RISCV_TLS_DTPREL64 },
|
|
{ BFD_RELOC_RISCV_TLS_TPREL32, R_RISCV_TLS_TPREL32 },
|
|
{ BFD_RELOC_RISCV_TLS_TPREL64, R_RISCV_TLS_TPREL64 },
|
|
{ BFD_RELOC_RISCV_TPREL_HI20, R_RISCV_TPREL_HI20 },
|
|
{ BFD_RELOC_RISCV_TPREL_ADD, R_RISCV_TPREL_ADD },
|
|
{ BFD_RELOC_RISCV_TPREL_LO12_S, R_RISCV_TPREL_LO12_S },
|
|
{ BFD_RELOC_RISCV_TPREL_LO12_I, R_RISCV_TPREL_LO12_I },
|
|
{ BFD_RELOC_RISCV_TLS_GOT_HI20, R_RISCV_TLS_GOT_HI20 },
|
|
{ BFD_RELOC_RISCV_TLS_GD_HI20, R_RISCV_TLS_GD_HI20 },
|
|
{ BFD_RELOC_RISCV_TLSDESC_HI20, R_RISCV_TLSDESC_HI20 },
|
|
{ BFD_RELOC_RISCV_TLSDESC_LOAD_LO12, R_RISCV_TLSDESC_LOAD_LO12 },
|
|
{ BFD_RELOC_RISCV_TLSDESC_ADD_LO12, R_RISCV_TLSDESC_ADD_LO12 },
|
|
{ BFD_RELOC_RISCV_TLSDESC_CALL, R_RISCV_TLSDESC_CALL },
|
|
{ BFD_RELOC_RISCV_ALIGN, R_RISCV_ALIGN },
|
|
{ BFD_RELOC_RISCV_RVC_BRANCH, R_RISCV_RVC_BRANCH },
|
|
{ BFD_RELOC_RISCV_RVC_JUMP, R_RISCV_RVC_JUMP },
|
|
{ BFD_RELOC_RISCV_RELAX, R_RISCV_RELAX },
|
|
{ BFD_RELOC_RISCV_SUB6, R_RISCV_SUB6 },
|
|
{ BFD_RELOC_RISCV_SET6, R_RISCV_SET6 },
|
|
{ BFD_RELOC_RISCV_SET8, R_RISCV_SET8 },
|
|
{ BFD_RELOC_RISCV_SET16, R_RISCV_SET16 },
|
|
{ BFD_RELOC_RISCV_SET32, R_RISCV_SET32 },
|
|
{ BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL },
|
|
{ BFD_RELOC_RISCV_SET_ULEB128, R_RISCV_SET_ULEB128 },
|
|
{ BFD_RELOC_RISCV_SUB_ULEB128, R_RISCV_SUB_ULEB128 },
|
|
};
|
|
|
|
/* Given a BFD reloc type, return a howto structure. */
|
|
|
|
reloc_howto_type *
|
|
riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
|
|
bfd_reloc_code_real_type code)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE (riscv_reloc_map); i++)
|
|
if (riscv_reloc_map[i].bfd_val == code)
|
|
return &howto_table[(int) riscv_reloc_map[i].elf_val];
|
|
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return NULL;
|
|
}
|
|
|
|
reloc_howto_type *
|
|
riscv_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;
|
|
}
|
|
|
|
reloc_howto_type *
|
|
riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
|
|
{
|
|
if (r_type < ARRAY_SIZE (howto_table))
|
|
return &howto_table[r_type];
|
|
else if (r_type < R_RISCV_max + ARRAY_SIZE (howto_table_internal))
|
|
return &howto_table_internal[r_type - R_RISCV_max];
|
|
else
|
|
{
|
|
(*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
|
|
abfd, r_type);
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Special_function of RISCV_ADD and RISCV_SUB relocations. */
|
|
|
|
static bfd_reloc_status_type
|
|
riscv_elf_add_sub_reloc (bfd *abfd,
|
|
arelent *reloc_entry,
|
|
asymbol *symbol,
|
|
void *data,
|
|
asection *input_section,
|
|
bfd *output_bfd,
|
|
char **error_message ATTRIBUTE_UNUSED)
|
|
{
|
|
reloc_howto_type *howto = reloc_entry->howto;
|
|
bfd_vma relocation;
|
|
|
|
if (output_bfd != NULL
|
|
&& (symbol->flags & BSF_SECTION_SYM) == 0
|
|
&& (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0))
|
|
{
|
|
reloc_entry->address += input_section->output_offset;
|
|
return bfd_reloc_ok;
|
|
}
|
|
|
|
if (output_bfd != NULL)
|
|
return bfd_reloc_continue;
|
|
|
|
relocation = symbol->value + symbol->section->output_section->vma
|
|
+ symbol->section->output_offset + reloc_entry->addend;
|
|
|
|
bfd_size_type octets = reloc_entry->address
|
|
* bfd_octets_per_byte (abfd, input_section);
|
|
if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
|
|
input_section, octets))
|
|
return bfd_reloc_outofrange;
|
|
|
|
bfd_vma old_value = bfd_get (howto->bitsize, abfd,
|
|
data + reloc_entry->address);
|
|
|
|
switch (howto->type)
|
|
{
|
|
case R_RISCV_ADD8:
|
|
case R_RISCV_ADD16:
|
|
case R_RISCV_ADD32:
|
|
case R_RISCV_ADD64:
|
|
relocation = old_value + relocation;
|
|
break;
|
|
case R_RISCV_SUB6:
|
|
relocation = (old_value & ~howto->dst_mask)
|
|
| (((old_value & howto->dst_mask) - relocation)
|
|
& howto->dst_mask);
|
|
break;
|
|
case R_RISCV_SUB8:
|
|
case R_RISCV_SUB16:
|
|
case R_RISCV_SUB32:
|
|
case R_RISCV_SUB64:
|
|
relocation = old_value - relocation;
|
|
break;
|
|
}
|
|
bfd_put (howto->bitsize, abfd, relocation, data + reloc_entry->address);
|
|
|
|
return bfd_reloc_ok;
|
|
}
|
|
|
|
/* Special handler for relocations which don't have to be relocated.
|
|
This function just simply return bfd_reloc_ok. */
|
|
|
|
static bfd_reloc_status_type
|
|
riscv_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED,
|
|
arelent *reloc_entry,
|
|
asymbol *symbol ATTRIBUTE_UNUSED,
|
|
void *data ATTRIBUTE_UNUSED,
|
|
asection *input_section,
|
|
bfd *output_bfd,
|
|
char **error_message ATTRIBUTE_UNUSED)
|
|
{
|
|
if (output_bfd != NULL)
|
|
reloc_entry->address += input_section->output_offset;
|
|
return bfd_reloc_ok;
|
|
}
|
|
|
|
/* Always add implicit extensions for the SUBSET. */
|
|
|
|
static bool
|
|
check_implicit_always (riscv_subset_t *subset ATTRIBUTE_UNUSED)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* Add implicit extensions only when the version of SUBSET less than 2.1. */
|
|
|
|
static bool
|
|
check_implicit_for_i (riscv_subset_t *subset)
|
|
{
|
|
return (subset->major_version < 2
|
|
|| (subset->major_version == 2
|
|
&& subset->minor_version < 1));
|
|
}
|
|
|
|
/* Record all implicit information for the subsets. */
|
|
struct riscv_implicit_subset
|
|
{
|
|
const char *ext;
|
|
const char *implicit_exts;
|
|
/* A function to determine if we need to add the implicit subsets. */
|
|
bool (*check_func) (riscv_subset_t *);
|
|
};
|
|
/* Please added in order since this table is only run once time. */
|
|
static struct riscv_implicit_subset riscv_implicit_subsets[] =
|
|
{
|
|
{"g", "+i,+m,+a,+f,+d,+zicsr,+zifencei", check_implicit_always},
|
|
{"e", "+i", check_implicit_always},
|
|
{"i", "+zicsr,+zifencei", check_implicit_for_i},
|
|
{"zicntr", "+zicsr", check_implicit_always},
|
|
{"zihpm", "+zicsr", check_implicit_always},
|
|
|
|
{"m", "+zmmul", check_implicit_always},
|
|
|
|
{"zabha", "+a", check_implicit_always},
|
|
{"zacas", "+a", check_implicit_always},
|
|
{"a", "+zaamo,+zalrsc", check_implicit_always},
|
|
|
|
{"xsfvcp", "+zve32x", check_implicit_always},
|
|
{"v", "+zve64d,+zvl128b", check_implicit_always},
|
|
{"zvfh", "+zvfhmin,+zfhmin", check_implicit_always},
|
|
{"zvfhmin", "+zve32f", check_implicit_always},
|
|
{"zvfbfwma", "+zve32f,+zfbfmin", check_implicit_always},
|
|
{"zvfbfmin", "+zve32f", check_implicit_always},
|
|
{"zve64d", "+d,+zve64f", check_implicit_always},
|
|
{"zve64f", "+zve32f,+zve64x,+zvl64b", check_implicit_always},
|
|
{"zve32f", "+f,+zve32x,+zvl32b", check_implicit_always},
|
|
{"zve64x", "+zve32x,+zvl64b", check_implicit_always},
|
|
{"zve32x", "+zvl32b,+zicsr", check_implicit_always},
|
|
{"zvl65536b", "+zvl32768b", check_implicit_always},
|
|
{"zvl32768b", "+zvl16384b", check_implicit_always},
|
|
{"zvl16384b", "+zvl8192b", check_implicit_always},
|
|
{"zvl8192b", "+zvl4096b", check_implicit_always},
|
|
{"zvl4096b", "+zvl2048b", check_implicit_always},
|
|
{"zvl2048b", "+zvl1024b", check_implicit_always},
|
|
{"zvl1024b", "+zvl512b", check_implicit_always},
|
|
{"zvl512b", "+zvl256b", check_implicit_always},
|
|
{"zvl256b", "+zvl128b", check_implicit_always},
|
|
{"zvl128b", "+zvl64b", check_implicit_always},
|
|
{"zvl64b", "+zvl32b", check_implicit_always},
|
|
|
|
{"zcb", "+zca", check_implicit_always},
|
|
{"zcd", "+d,+zca", check_implicit_always},
|
|
{"zcf", "+f,+zca", check_implicit_always},
|
|
{"zcmp", "+zca", check_implicit_always},
|
|
|
|
{"shcounterenw", "+h", check_implicit_always},
|
|
{"shgatpa", "+h", check_implicit_always},
|
|
{"shtvala", "+h", check_implicit_always},
|
|
{"shvsatpa", "+h", check_implicit_always},
|
|
{"shvstvala", "+h", check_implicit_always},
|
|
{"shvstvecd", "+h", check_implicit_always},
|
|
{"h", "+zicsr", check_implicit_always},
|
|
{"zhinx", "+zhinxmin", check_implicit_always},
|
|
{"zhinxmin", "+zfinx", check_implicit_always},
|
|
|
|
{"q", "+d", check_implicit_always},
|
|
{"zqinx", "+zdinx", check_implicit_always},
|
|
|
|
{"d", "+f", check_implicit_always},
|
|
{"zdinx", "+zfinx", check_implicit_always},
|
|
|
|
{"zfa", "+f", check_implicit_always},
|
|
{"zfbfmin", "+zfhmin", check_implicit_always},
|
|
{"zfh", "+zfhmin", check_implicit_always},
|
|
{"zfhmin", "+f", check_implicit_always},
|
|
{"zfinx", "+zicsr", check_implicit_always},
|
|
{"f", "+zicsr", check_implicit_always},
|
|
|
|
{"b", "+zba,+zbb,+zbs", check_implicit_always},
|
|
|
|
{"zk", "+zkn,+zkr,+zkt", check_implicit_always},
|
|
{"zkn", "+zbkb,+zbkc,+zbkx,+zkne,+zknd,+zknh", check_implicit_always},
|
|
{"zks", "+zbkb,+zbkc,+zbkx,+zksed,+zksh", check_implicit_always},
|
|
{"zvbb", "+zvkb", check_implicit_always},
|
|
{"zvkng", "+zvkn,+zvkg", check_implicit_always},
|
|
{"zvknc", "+zvkn,+zvbc", check_implicit_always},
|
|
{"zvkn", "+zvkned,+zvknhb,+zvkb,+zvkt", check_implicit_always},
|
|
{"zvksg", "+zvks,+zvkg", check_implicit_always},
|
|
{"zvksc", "+zvks,+zvbc", check_implicit_always},
|
|
{"zvks", "+zvksed,+zvksh,+zvkb,+zvkt", check_implicit_always},
|
|
|
|
{"smaia", "+ssaia", check_implicit_always},
|
|
{"smcsrind", "+sscsrind", check_implicit_always},
|
|
{"smcntrpmf", "+zicsr", check_implicit_always},
|
|
{"smstateen", "+ssstateen", check_implicit_always},
|
|
{"smepmp", "+zicsr", check_implicit_always},
|
|
|
|
{"ssaia", "+zicsr", check_implicit_always},
|
|
{"sscsrind", "+zicsr", check_implicit_always},
|
|
{"sscofpmf", "+zicsr", check_implicit_always},
|
|
{"sscounterenw", "+zicsr", check_implicit_always},
|
|
{"ssstateen", "+zicsr", check_implicit_always},
|
|
{"sstc", "+zicsr", check_implicit_always},
|
|
{"sstvala", "+zicsr", check_implicit_always},
|
|
{"sstvecd", "+zicsr", check_implicit_always},
|
|
{"ssu64xl", "+zicsr", check_implicit_always},
|
|
|
|
{"svade", "+zicsr", check_implicit_always},
|
|
{"svadu", "+zicsr", check_implicit_always},
|
|
{"svbare", "+zicsr", check_implicit_always},
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
/* For default_enable field, decide if the extension should
|
|
be enbaled by default. */
|
|
|
|
#define EXT_DEFAULT 0x1
|
|
|
|
/* List all extensions that binutils should know about. */
|
|
|
|
struct riscv_supported_ext
|
|
{
|
|
const char *name;
|
|
enum riscv_spec_class isa_spec_class;
|
|
int major_version;
|
|
int minor_version;
|
|
unsigned long default_enable;
|
|
};
|
|
|
|
/* The standard extensions must be added in canonical order. */
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_ext[] =
|
|
{
|
|
{"e", ISA_SPEC_CLASS_20191213, 1, 9, 0 },
|
|
{"e", ISA_SPEC_CLASS_20190608, 1, 9, 0 },
|
|
{"e", ISA_SPEC_CLASS_2P2, 1, 9, 0 },
|
|
{"i", ISA_SPEC_CLASS_20191213, 2, 1, 0 },
|
|
{"i", ISA_SPEC_CLASS_20190608, 2, 1, 0 },
|
|
{"i", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
/* The g is a special case which we don't want to output it,
|
|
but still need it when adding implicit extensions. */
|
|
{"g", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, EXT_DEFAULT },
|
|
{"m", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"m", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"m", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"a", ISA_SPEC_CLASS_20191213, 2, 1, 0 },
|
|
{"a", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"a", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"f", ISA_SPEC_CLASS_20191213, 2, 2, 0 },
|
|
{"f", ISA_SPEC_CLASS_20190608, 2, 2, 0 },
|
|
{"f", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"d", ISA_SPEC_CLASS_20191213, 2, 2, 0 },
|
|
{"d", ISA_SPEC_CLASS_20190608, 2, 2, 0 },
|
|
{"d", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"q", ISA_SPEC_CLASS_20191213, 2, 2, 0 },
|
|
{"q", ISA_SPEC_CLASS_20190608, 2, 2, 0 },
|
|
{"q", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"c", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"c", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"c", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"v", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"h", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_z_ext[] =
|
|
{
|
|
{"zic64b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ziccamoa", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ziccif", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicclsm", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ziccrse", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicbom", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicbop", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicboz", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicond", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicntr", ISA_SPEC_CLASS_DRAFT, 2, 0, 0 },
|
|
{"zicsr", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"zicsr", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"zifencei", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"zifencei", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"zihintntl", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zihintpause", ISA_SPEC_CLASS_DRAFT, 2, 0, 0 },
|
|
{"zihpm", ISA_SPEC_CLASS_DRAFT, 2, 0, 0 },
|
|
{"zmmul", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"za64rs", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"za128rs", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zaamo", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zabha", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zacas", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zalrsc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zawrs", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfbfmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfa", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfhmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zdinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zqinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zhinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zhinxmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zba", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbs", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbkb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbkc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbkx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zk", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkn", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zknd", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkne", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zknh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkr", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zks", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zksed", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zksh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkt", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve32x", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve32f", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve64x", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve64f", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve64d", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvbb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvbc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvfbfmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvfbfwma", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvfh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvfhmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvkb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvkg", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvkn", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvkng", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvknc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvkned", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvknha", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvknhb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvksed", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvksh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvks", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvksg", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvksc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvkt", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl32b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl64b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl128b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl256b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl512b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl1024b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl2048b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl4096b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl8192b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl16384b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl32768b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl65536b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ztso", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zca", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zcb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zcf", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zcd", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zcmp", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_s_ext[] =
|
|
{
|
|
{"shcounterenw", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"shgatpa", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"shtvala", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"shvsatpa", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"shvstvala", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"shvstvecd", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"smaia", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"smcsrind", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"smcntrpmf", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"smepmp", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"smstateen", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ssaia", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ssccptr", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sscsrind", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sscofpmf", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sscounterenw", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ssstateen", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sstc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sstvala", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sstvecd", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"ssu64xl", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svade", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svadu", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svbare", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svinval", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svnapot", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svpbmt", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_zxm_ext[] =
|
|
{
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_vendor_x_ext[] =
|
|
{
|
|
{"xcvmac", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvalu", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvelw", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvbi", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvmem", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadba", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadbb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadbs", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadcmo", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadcondmov", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadfmemidx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadfmv", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadint", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadmac", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadmemidx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadmempair", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadsync", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadvector", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xtheadzvamo", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xventanacondops", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xsfvcp", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xsfcease", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
const struct riscv_supported_ext *riscv_all_supported_ext[] =
|
|
{
|
|
riscv_supported_std_ext,
|
|
riscv_supported_std_z_ext,
|
|
riscv_supported_std_s_ext,
|
|
riscv_supported_std_zxm_ext,
|
|
riscv_supported_vendor_x_ext,
|
|
NULL
|
|
};
|
|
|
|
/* ISA extension prefixed name class. Must define them in parsing order. */
|
|
enum riscv_prefix_ext_class
|
|
{
|
|
RV_ISA_CLASS_Z = 1,
|
|
RV_ISA_CLASS_S,
|
|
RV_ISA_CLASS_ZXM,
|
|
RV_ISA_CLASS_X,
|
|
RV_ISA_CLASS_SINGLE
|
|
};
|
|
|
|
/* Record the strings of the prefixed extensions, and their corresponding
|
|
classes. The more letters of the prefix string, the more forward it must
|
|
be defined. Otherwise, the riscv_get_prefix_class will map it to the
|
|
wrong classes. */
|
|
struct riscv_parse_prefix_config
|
|
{
|
|
/* Class of the extension. */
|
|
enum riscv_prefix_ext_class class;
|
|
|
|
/* Prefix string for error printing and internal parser usage. */
|
|
const char *prefix;
|
|
};
|
|
static const struct riscv_parse_prefix_config parse_config[] =
|
|
{
|
|
{RV_ISA_CLASS_ZXM, "zxm"},
|
|
{RV_ISA_CLASS_Z, "z"},
|
|
{RV_ISA_CLASS_S, "s"},
|
|
{RV_ISA_CLASS_X, "x"},
|
|
{RV_ISA_CLASS_SINGLE, NULL}
|
|
};
|
|
|
|
/* Get the prefixed name class for the extensions, the class also
|
|
means the order of the prefixed extensions. */
|
|
|
|
static enum riscv_prefix_ext_class
|
|
riscv_get_prefix_class (const char *arch)
|
|
{
|
|
int i = 0;
|
|
while (parse_config[i].class != RV_ISA_CLASS_SINGLE)
|
|
{
|
|
if (strncmp (arch, parse_config[i].prefix,
|
|
strlen (parse_config[i].prefix)) == 0)
|
|
return parse_config[i].class;
|
|
i++;
|
|
}
|
|
return RV_ISA_CLASS_SINGLE;
|
|
}
|
|
|
|
/* Check KNOWN_EXTS to see if the EXT is supported. */
|
|
|
|
static bool
|
|
riscv_known_prefixed_ext (const char *ext,
|
|
struct riscv_supported_ext *known_exts)
|
|
{
|
|
size_t i;
|
|
for (i = 0; known_exts[i].name != NULL; ++i)
|
|
if (strcmp (ext, known_exts[i].name) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/* Check whether the prefixed extension is recognized or not. Return
|
|
true if recognized, otehrwise return false. */
|
|
|
|
static bool
|
|
riscv_recognized_prefixed_ext (const char *ext)
|
|
{
|
|
enum riscv_prefix_ext_class class = riscv_get_prefix_class (ext);
|
|
switch (class)
|
|
{
|
|
case RV_ISA_CLASS_Z:
|
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_z_ext);
|
|
case RV_ISA_CLASS_ZXM:
|
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_zxm_ext);
|
|
case RV_ISA_CLASS_S:
|
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_s_ext);
|
|
case RV_ISA_CLASS_X:
|
|
/* Only the single x is unrecognized. */
|
|
if (strcmp (ext, "x") != 0)
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Canonical order for single letter extensions. */
|
|
static const char riscv_ext_canonical_order[] = "eigmafdqlcbkjtpvnh";
|
|
|
|
/* Array is used to compare the orders of standard extensions quickly. */
|
|
static int riscv_ext_order[26] = {0};
|
|
|
|
/* Init the riscv_ext_order array. */
|
|
|
|
static void
|
|
riscv_init_ext_order (void)
|
|
{
|
|
static bool inited = false;
|
|
if (inited)
|
|
return;
|
|
|
|
/* The orders of all standard extensions are positive. */
|
|
int order = 1;
|
|
|
|
for (const char *ext = &riscv_ext_canonical_order[0]; *ext; ++ext)
|
|
riscv_ext_order[(*ext - 'a')] = order++;
|
|
|
|
/* Some of the prefixed keyword are not single letter, so we set
|
|
their prefixed orders in the riscv_compare_subsets directly,
|
|
not through the riscv_ext_order. */
|
|
|
|
inited = true;
|
|
}
|
|
|
|
/* Similar to the strcmp. It returns an integer less than, equal to,
|
|
or greater than zero if `subset2` is found, respectively, to be less
|
|
than, to match, or be greater than `subset1`.
|
|
|
|
The order values,
|
|
Zero: Preserved keywords.
|
|
Positive number: Standard extensions.
|
|
Negative number: Prefixed keywords. */
|
|
|
|
int
|
|
riscv_compare_subsets (const char *subset1, const char *subset2)
|
|
{
|
|
int order1 = riscv_ext_order[(*subset1 - 'a')];
|
|
int order2 = riscv_ext_order[(*subset2 - 'a')];
|
|
|
|
/* Compare the standard extension first. */
|
|
if (order1 > 0 && order2 > 0)
|
|
return order1 - order2;
|
|
|
|
/* Set the prefixed orders to negative numbers. */
|
|
enum riscv_prefix_ext_class class1 = riscv_get_prefix_class (subset1);
|
|
enum riscv_prefix_ext_class class2 = riscv_get_prefix_class (subset2);
|
|
|
|
if (class1 != RV_ISA_CLASS_SINGLE)
|
|
order1 = - (int) class1;
|
|
if (class2 != RV_ISA_CLASS_SINGLE)
|
|
order2 = - (int) class2;
|
|
|
|
if (order1 == order2)
|
|
{
|
|
/* Compare the standard addition z extensions. */
|
|
if (class1 == RV_ISA_CLASS_Z)
|
|
{
|
|
order1 = riscv_ext_order[(*++subset1 - 'a')];
|
|
order2 = riscv_ext_order[(*++subset2 - 'a')];
|
|
if (order1 != order2)
|
|
return order1 - order2;
|
|
}
|
|
return strcasecmp (++subset1, ++subset2);
|
|
}
|
|
|
|
return order2 - order1;
|
|
}
|
|
|
|
/* Find subset in the list. Return TRUE and set `current` to the subset
|
|
if it is found. Otherwise, return FALSE and set `current` to the place
|
|
where we should insert the subset. However, return FALSE with the NULL
|
|
`current` means we should insert the subset at the head of subset list,
|
|
if needed. */
|
|
|
|
bool
|
|
riscv_lookup_subset (const riscv_subset_list_t *subset_list,
|
|
const char *subset,
|
|
riscv_subset_t **current)
|
|
{
|
|
riscv_subset_t *s, *pre_s = NULL;
|
|
|
|
/* If the subset is added in order, then just add it at the tail. */
|
|
if (subset_list->tail != NULL
|
|
&& riscv_compare_subsets (subset_list->tail->name, subset) < 0)
|
|
{
|
|
*current = subset_list->tail;
|
|
return false;
|
|
}
|
|
|
|
for (s = subset_list->head;
|
|
s != NULL;
|
|
pre_s = s, s = s->next)
|
|
{
|
|
int cmp = riscv_compare_subsets (s->name, subset);
|
|
if (cmp == 0)
|
|
{
|
|
*current = s;
|
|
return true;
|
|
}
|
|
else if (cmp > 0)
|
|
break;
|
|
}
|
|
*current = pre_s;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Add the extension to the subset list. Search the
|
|
list first, and then find the right place to add. */
|
|
|
|
void
|
|
riscv_add_subset (riscv_subset_list_t *subset_list,
|
|
const char *subset,
|
|
int major,
|
|
int minor)
|
|
{
|
|
riscv_subset_t *current, *new;
|
|
|
|
if (riscv_lookup_subset (subset_list, subset, ¤t))
|
|
return;
|
|
|
|
new = xmalloc (sizeof *new);
|
|
new->name = xstrdup (subset);
|
|
new->major_version = major;
|
|
new->minor_version = minor;
|
|
new->next = NULL;
|
|
|
|
if (current != NULL)
|
|
{
|
|
new->next = current->next;
|
|
current->next = new;
|
|
}
|
|
else
|
|
{
|
|
new->next = subset_list->head;
|
|
subset_list->head = new;
|
|
}
|
|
|
|
if (new->next == NULL)
|
|
subset_list->tail = new;
|
|
}
|
|
|
|
/* Get the default versions from the riscv_supported_*ext tables. */
|
|
|
|
static void
|
|
riscv_get_default_ext_version (enum riscv_spec_class *default_isa_spec,
|
|
const char *name,
|
|
int *major_version,
|
|
int *minor_version)
|
|
{
|
|
if (name == NULL
|
|
|| default_isa_spec == NULL
|
|
|| *default_isa_spec == ISA_SPEC_CLASS_NONE)
|
|
return;
|
|
|
|
struct riscv_supported_ext *table = NULL;
|
|
enum riscv_prefix_ext_class class = riscv_get_prefix_class (name);
|
|
switch (class)
|
|
{
|
|
case RV_ISA_CLASS_ZXM: table = riscv_supported_std_zxm_ext; break;
|
|
case RV_ISA_CLASS_Z: table = riscv_supported_std_z_ext; break;
|
|
case RV_ISA_CLASS_S: table = riscv_supported_std_s_ext; break;
|
|
case RV_ISA_CLASS_X: table = riscv_supported_vendor_x_ext; break;
|
|
default:
|
|
table = riscv_supported_std_ext;
|
|
}
|
|
|
|
int i = 0;
|
|
while (table != NULL && table[i].name != NULL)
|
|
{
|
|
if (strcmp (table[i].name, name) == 0
|
|
&& (table[i].isa_spec_class == ISA_SPEC_CLASS_DRAFT
|
|
|| table[i].isa_spec_class == *default_isa_spec))
|
|
{
|
|
*major_version = table[i].major_version;
|
|
*minor_version = table[i].minor_version;
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* Find the default versions for the extension before adding them to
|
|
the subset list, if their versions are RISCV_UNKNOWN_VERSION.
|
|
Afterwards, report errors if we can not find their default versions. */
|
|
|
|
static void
|
|
riscv_parse_add_subset (riscv_parse_subset_t *rps,
|
|
const char *subset,
|
|
int major,
|
|
int minor,
|
|
bool implicit)
|
|
{
|
|
int major_version = major;
|
|
int minor_version = minor;
|
|
|
|
if (major_version == RISCV_UNKNOWN_VERSION
|
|
|| minor_version == RISCV_UNKNOWN_VERSION)
|
|
riscv_get_default_ext_version (rps->isa_spec, subset,
|
|
&major_version, &minor_version);
|
|
|
|
/* We don't care the versions of the implicit extensions. */
|
|
if (!implicit
|
|
&& (major_version == RISCV_UNKNOWN_VERSION
|
|
|| minor_version == RISCV_UNKNOWN_VERSION))
|
|
{
|
|
if (subset[0] == 'x')
|
|
rps->error_handler
|
|
(_("x ISA extension `%s' must be set with the versions"),
|
|
subset);
|
|
/* Allow old ISA spec can recognize zicsr and zifencei. */
|
|
else if (strcmp (subset, "zicsr") != 0
|
|
&& strcmp (subset, "zifencei") != 0)
|
|
rps->error_handler
|
|
(_("cannot find default versions of the ISA extension `%s'"),
|
|
subset);
|
|
return;
|
|
}
|
|
|
|
riscv_add_subset (rps->subset_list, subset,
|
|
major_version, minor_version);
|
|
}
|
|
|
|
/* Release subset list. */
|
|
|
|
void
|
|
riscv_release_subset_list (riscv_subset_list_t *subset_list)
|
|
{
|
|
while (subset_list->head != NULL)
|
|
{
|
|
riscv_subset_t *next = subset_list->head->next;
|
|
free ((void *)subset_list->head->name);
|
|
free (subset_list->head);
|
|
subset_list->head = next;
|
|
}
|
|
|
|
subset_list->tail = NULL;
|
|
|
|
if (subset_list->arch_str != NULL)
|
|
{
|
|
free ((void*) subset_list->arch_str);
|
|
subset_list->arch_str = NULL;
|
|
}
|
|
}
|
|
|
|
/* Parsing extension version.
|
|
|
|
Return Value:
|
|
Points to the end of version
|
|
|
|
Arguments:
|
|
`p`: Curent parsing position.
|
|
`major_version`: Parsed major version.
|
|
`minor_version`: Parsed minor version. */
|
|
|
|
static const char *
|
|
riscv_parsing_subset_version (const char *p,
|
|
int *major_version,
|
|
int *minor_version)
|
|
{
|
|
bool major_p = true;
|
|
int version = 0;
|
|
char np;
|
|
|
|
*major_version = 0;
|
|
*minor_version = 0;
|
|
for (; *p; ++p)
|
|
{
|
|
if (*p == 'p')
|
|
{
|
|
np = *(p + 1);
|
|
|
|
/* Might be beginning of `p` extension. */
|
|
if (!ISDIGIT (np))
|
|
break;
|
|
|
|
*major_version = version;
|
|
major_p = false;
|
|
version = 0;
|
|
}
|
|
else if (ISDIGIT (*p))
|
|
version = (version * 10) + (*p - '0');
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (major_p)
|
|
*major_version = version;
|
|
else
|
|
*minor_version = version;
|
|
|
|
/* We can not find any version in string. */
|
|
if (*major_version == 0 && *minor_version == 0)
|
|
{
|
|
*major_version = RISCV_UNKNOWN_VERSION;
|
|
*minor_version = RISCV_UNKNOWN_VERSION;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Parsing function for both standard and prefixed extensions.
|
|
|
|
Return Value:
|
|
Points to the end of extensions.
|
|
|
|
Arguments:
|
|
`rps`: Hooks and status for parsing extensions.
|
|
`arch`: Full ISA string.
|
|
`p`: Curent parsing position. */
|
|
|
|
static const char *
|
|
riscv_parse_extensions (riscv_parse_subset_t *rps,
|
|
const char *arch,
|
|
const char *p)
|
|
{
|
|
/* First letter must start with i, e or g. */
|
|
if (*p != 'e' && *p != 'i' && *p != 'g')
|
|
{
|
|
rps->error_handler
|
|
(_("%s: first ISA extension must be `e', `i' or `g'"),
|
|
arch);
|
|
return NULL;
|
|
}
|
|
|
|
while (*p != '\0')
|
|
{
|
|
if (*p == '_')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
char *subset = xstrdup (p);
|
|
char *q = subset; /* Start of version. */
|
|
const char *end_of_version;
|
|
bool implicit = false;
|
|
|
|
enum riscv_prefix_ext_class class = riscv_get_prefix_class (p);
|
|
if (class == RV_ISA_CLASS_SINGLE)
|
|
{
|
|
if (riscv_ext_order[(*subset - 'a')] == 0)
|
|
{
|
|
rps->error_handler
|
|
(_("%s: unknown standard ISA extension or prefix class `%c'"),
|
|
arch, *subset);
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
q++;
|
|
}
|
|
else
|
|
{
|
|
/* Extract the whole prefixed extension by '_'. */
|
|
while (*++q != '\0' && *q != '_')
|
|
;
|
|
/* Look forward to the first letter which is not <major>p<minor>. */
|
|
bool find_any_version = false;
|
|
bool find_minor_version = false;
|
|
while (1)
|
|
{
|
|
q--;
|
|
if (ISDIGIT (*q))
|
|
find_any_version = true;
|
|
else if (find_any_version
|
|
&& !find_minor_version
|
|
&& *q == 'p'
|
|
&& ISDIGIT (*(q - 1)))
|
|
find_minor_version = true;
|
|
else
|
|
break;
|
|
}
|
|
q++;
|
|
|
|
/* Check if the end of extension is 'p' or not. If yes, then
|
|
the second letter from the end cannot be number. */
|
|
if (*(q - 1) == 'p' && ISDIGIT (*(q - 2)))
|
|
{
|
|
*q = '\0';
|
|
rps->error_handler
|
|
(_("%s: invalid prefixed ISA extension `%s' ends with <number>p"),
|
|
arch, subset);
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int major_version = RISCV_UNKNOWN_VERSION;
|
|
int minor_version = RISCV_UNKNOWN_VERSION;
|
|
end_of_version =
|
|
riscv_parsing_subset_version (q, &major_version, &minor_version);
|
|
*q = '\0';
|
|
if (end_of_version == NULL)
|
|
{
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
|
|
/* Check if the prefixed extension name is well-formed. */
|
|
if (class != RV_ISA_CLASS_SINGLE
|
|
&& rps->check_unknown_prefixed_ext
|
|
&& !riscv_recognized_prefixed_ext (subset))
|
|
{
|
|
rps->error_handler
|
|
(_("%s: unknown prefixed ISA extension `%s'"),
|
|
arch, subset);
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
|
|
/* Added g as an implicit extension. */
|
|
if (class == RV_ISA_CLASS_SINGLE
|
|
&& strcmp (subset, "g") == 0)
|
|
{
|
|
implicit = true;
|
|
major_version = RISCV_UNKNOWN_VERSION;
|
|
minor_version = RISCV_UNKNOWN_VERSION;
|
|
}
|
|
riscv_parse_add_subset (rps, subset,
|
|
major_version,
|
|
minor_version, implicit);
|
|
p += end_of_version - subset;
|
|
free (subset);
|
|
|
|
if (class != RV_ISA_CLASS_SINGLE
|
|
&& *p != '\0' && *p != '_')
|
|
{
|
|
rps->error_handler
|
|
(_("%s: prefixed ISA extension must separate with _"),
|
|
arch);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
static bool
|
|
riscv_update_subset1 (riscv_parse_subset_t *, riscv_subset_t *, const char *);
|
|
|
|
/* Add the implicit extensions. */
|
|
|
|
static void
|
|
riscv_parse_add_implicit_subsets (riscv_parse_subset_t *rps)
|
|
{
|
|
struct riscv_implicit_subset *t = riscv_implicit_subsets;
|
|
for (; t->ext; t++)
|
|
{
|
|
riscv_subset_t *subset = NULL;
|
|
if (riscv_lookup_subset (rps->subset_list, t->ext, &subset)
|
|
&& t->check_func (subset))
|
|
riscv_update_subset1 (rps, subset, t->implicit_exts);
|
|
}
|
|
}
|
|
|
|
/* Check extensions conflicts. */
|
|
|
|
static bool
|
|
riscv_parse_check_conflicts (riscv_parse_subset_t *rps)
|
|
{
|
|
riscv_subset_t *subset = NULL;
|
|
int xlen = *rps->xlen;
|
|
bool no_conflict = true;
|
|
|
|
if (riscv_subset_supports (rps, "e")
|
|
&& riscv_subset_supports (rps, "h"))
|
|
{
|
|
rps->error_handler
|
|
(_("rv%de does not support the `h' extension"), xlen);
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "q", &subset)
|
|
&& (subset->major_version < 2 || (subset->major_version == 2
|
|
&& subset->minor_version < 2))
|
|
&& xlen < 64)
|
|
{
|
|
rps->error_handler (_("rv%d does not support the `q' extension"), xlen);
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_subset_supports (rps, "zcmp")
|
|
&& riscv_subset_supports (rps, "zcd"))
|
|
{
|
|
rps->error_handler
|
|
(_("zcmp' is incompatible with `d/zcd' extension"));
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "zcf", &subset)
|
|
&& xlen > 32)
|
|
{
|
|
rps->error_handler
|
|
(_("rv%d does not support the `zcf' extension"), xlen);
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "zfinx", &subset)
|
|
&& riscv_lookup_subset (rps->subset_list, "f", &subset))
|
|
{
|
|
rps->error_handler
|
|
(_("`zfinx' is conflict with the `f/d/q/zfh/zfhmin' extension"));
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "xtheadvector", &subset)
|
|
&& riscv_lookup_subset (rps->subset_list, "v", &subset))
|
|
{
|
|
rps->error_handler
|
|
(_("`xtheadvector' is conflict with the `v' extension"));
|
|
no_conflict = false;
|
|
}
|
|
|
|
bool support_zve = false;
|
|
bool support_zvl = false;
|
|
riscv_subset_t *s = rps->subset_list->head;
|
|
for (; s != NULL; s = s->next)
|
|
{
|
|
if (!support_zve
|
|
&& strncmp (s->name, "zve", 3) == 0)
|
|
support_zve = true;
|
|
if (!support_zvl
|
|
&& strncmp (s->name, "zvl", 3) == 0)
|
|
support_zvl = true;
|
|
if (support_zve && support_zvl)
|
|
break;
|
|
}
|
|
if (support_zvl && !support_zve)
|
|
{
|
|
rps->error_handler
|
|
(_("zvl*b extensions need to enable either `v' or `zve' extension"));
|
|
no_conflict = false;
|
|
}
|
|
|
|
return no_conflict;
|
|
}
|
|
|
|
/* Set the default subset list according to the default_enable field
|
|
of riscv_supported_*ext tables. */
|
|
|
|
static void
|
|
riscv_set_default_arch (riscv_parse_subset_t *rps)
|
|
{
|
|
unsigned long enable = EXT_DEFAULT;
|
|
int i, j;
|
|
for (i = 0; riscv_all_supported_ext[i] != NULL; i++)
|
|
{
|
|
const struct riscv_supported_ext *table = riscv_all_supported_ext[i];
|
|
for (j = 0; table[j].name != NULL; j++)
|
|
{
|
|
bool implicit = false;
|
|
if (strcmp (table[j].name, "g") == 0)
|
|
implicit = true;
|
|
if (table[j].default_enable & enable)
|
|
riscv_parse_add_subset (rps, table[j].name,
|
|
RISCV_UNKNOWN_VERSION,
|
|
RISCV_UNKNOWN_VERSION, implicit);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Function for parsing ISA string.
|
|
|
|
Return Value:
|
|
Return TRUE on success.
|
|
|
|
Arguments:
|
|
`rps`: Hooks and status for parsing extensions.
|
|
`arch`: Full ISA string. */
|
|
|
|
bool
|
|
riscv_parse_subset (riscv_parse_subset_t *rps,
|
|
const char *arch)
|
|
{
|
|
const char *p;
|
|
|
|
/* Init the riscv_ext_order array to compare the order of extensions
|
|
quickly. */
|
|
riscv_init_ext_order ();
|
|
|
|
if (arch == NULL)
|
|
{
|
|
riscv_set_default_arch (rps);
|
|
riscv_parse_add_implicit_subsets (rps);
|
|
return riscv_parse_check_conflicts (rps);
|
|
}
|
|
|
|
for (p = arch; *p != '\0'; p++)
|
|
{
|
|
if (ISUPPER (*p))
|
|
{
|
|
rps->error_handler
|
|
(_("%s: ISA string cannot contain uppercase letters"),
|
|
arch);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
p = arch;
|
|
if (startswith (p, "rv32"))
|
|
{
|
|
*rps->xlen = 32;
|
|
p += 4;
|
|
}
|
|
else if (startswith (p, "rv64"))
|
|
{
|
|
*rps->xlen = 64;
|
|
p += 4;
|
|
}
|
|
else
|
|
{
|
|
/* ISA string shouldn't be NULL or empty here. For linker,
|
|
it might be empty when we failed to merge the ISA string
|
|
in the riscv_merge_attributes. For assembler, we might
|
|
give an empty string by .attribute arch, "" or -march=.
|
|
However, We have already issued the correct error message
|
|
in another side, so do not issue this error when the ISA
|
|
string is empty. */
|
|
if (strlen (arch))
|
|
rps->error_handler (
|
|
_("%s: ISA string must begin with rv32 or rv64"),
|
|
arch);
|
|
return false;
|
|
}
|
|
|
|
/* Parse single standard and prefixed extensions. */
|
|
if (riscv_parse_extensions (rps, arch, p) == NULL)
|
|
return false;
|
|
|
|
/* Finally add implicit extensions according to the current
|
|
extensions. */
|
|
riscv_parse_add_implicit_subsets (rps);
|
|
|
|
/* Check the conflicts. */
|
|
return riscv_parse_check_conflicts (rps);
|
|
}
|
|
|
|
/* Return the number of digits for the input. */
|
|
|
|
size_t
|
|
riscv_estimate_digit (unsigned num)
|
|
{
|
|
size_t digit = 0;
|
|
if (num == 0)
|
|
return 1;
|
|
|
|
for (digit = 0; num ; num /= 10)
|
|
digit++;
|
|
|
|
return digit;
|
|
}
|
|
|
|
/* Auxiliary function to estimate string length of subset list. */
|
|
|
|
static size_t
|
|
riscv_estimate_arch_strlen1 (const riscv_subset_t *subset)
|
|
{
|
|
if (subset == NULL)
|
|
return 6; /* For rv32/rv64/rv128 and string terminator. */
|
|
|
|
return riscv_estimate_arch_strlen1 (subset->next)
|
|
+ strlen (subset->name)
|
|
+ riscv_estimate_digit (subset->major_version)
|
|
+ 1 /* For version seperator 'p'. */
|
|
+ riscv_estimate_digit (subset->minor_version)
|
|
+ 1 /* For underscore. */;
|
|
}
|
|
|
|
/* Estimate the string length of this subset list. */
|
|
|
|
static size_t
|
|
riscv_estimate_arch_strlen (const riscv_subset_list_t *subset_list)
|
|
{
|
|
return riscv_estimate_arch_strlen1 (subset_list->head);
|
|
}
|
|
|
|
/* Auxiliary function to convert subset info to string. */
|
|
|
|
static void
|
|
riscv_arch_str1 (riscv_subset_t *subset,
|
|
char *attr_str, char *buf, size_t bufsz)
|
|
{
|
|
const char *underline = "_";
|
|
riscv_subset_t *subset_t = subset;
|
|
|
|
if (subset_t == NULL)
|
|
return;
|
|
|
|
/* No underline between rvXX and i/e. */
|
|
if ((strcasecmp (subset_t->name, "i") == 0)
|
|
|| (strcasecmp (subset_t->name, "e") == 0))
|
|
underline = "";
|
|
|
|
snprintf (buf, bufsz, "%s%s%dp%d",
|
|
underline,
|
|
subset_t->name,
|
|
subset_t->major_version,
|
|
subset_t->minor_version);
|
|
|
|
strncat (attr_str, buf, bufsz);
|
|
|
|
/* Skip 'i' extension after 'e', or skip extensions which
|
|
versions are unknown. */
|
|
while (subset_t->next
|
|
&& ((strcmp (subset_t->name, "e") == 0
|
|
&& strcmp (subset_t->next->name, "i") == 0)
|
|
|| subset_t->next->major_version == RISCV_UNKNOWN_VERSION
|
|
|| subset_t->next->minor_version == RISCV_UNKNOWN_VERSION))
|
|
subset_t = subset_t->next;
|
|
|
|
riscv_arch_str1 (subset_t->next, attr_str, buf, bufsz);
|
|
}
|
|
|
|
/* Convert subset information into string with explicit versions. */
|
|
|
|
char *
|
|
riscv_arch_str (unsigned xlen, const riscv_subset_list_t *subset)
|
|
{
|
|
size_t arch_str_len = riscv_estimate_arch_strlen (subset);
|
|
char *attr_str = xmalloc (arch_str_len);
|
|
char *buf = xmalloc (arch_str_len);
|
|
|
|
snprintf (attr_str, arch_str_len, "rv%u", xlen);
|
|
|
|
riscv_arch_str1 (subset->head, attr_str, buf, arch_str_len);
|
|
free (buf);
|
|
|
|
return attr_str;
|
|
}
|
|
|
|
/* Copy the subset in the subset list. */
|
|
|
|
static struct riscv_subset_t *
|
|
riscv_copy_subset (riscv_subset_list_t *subset_list,
|
|
riscv_subset_t *subset)
|
|
{
|
|
if (subset == NULL)
|
|
return NULL;
|
|
|
|
riscv_subset_t *new = xmalloc (sizeof *new);
|
|
new->name = xstrdup (subset->name);
|
|
new->major_version = subset->major_version;
|
|
new->minor_version = subset->minor_version;
|
|
new->next = riscv_copy_subset (subset_list, subset->next);
|
|
|
|
if (subset->next == NULL)
|
|
subset_list->tail = new;
|
|
|
|
return new;
|
|
}
|
|
|
|
/* Copy the subset list. */
|
|
|
|
riscv_subset_list_t *
|
|
riscv_copy_subset_list (riscv_subset_list_t *subset_list)
|
|
{
|
|
riscv_subset_list_t *new = xmalloc (sizeof *new);
|
|
new->head = riscv_copy_subset (new, subset_list->head);
|
|
new->arch_str = strdup (subset_list->arch_str);
|
|
return new;
|
|
}
|
|
|
|
/* Remove the SUBSET from the subset list. */
|
|
|
|
static void
|
|
riscv_remove_subset (riscv_subset_list_t *subset_list,
|
|
const char *subset)
|
|
{
|
|
riscv_subset_t *current = subset_list->head;
|
|
riscv_subset_t *pre = NULL;
|
|
for (; current != NULL; pre = current, current = current->next)
|
|
{
|
|
if (strcmp (current->name, subset) == 0)
|
|
{
|
|
if (pre == NULL)
|
|
subset_list->head = current->next;
|
|
else
|
|
pre->next = current->next;
|
|
if (current->next == NULL)
|
|
subset_list->tail = pre;
|
|
free ((void *) current->name);
|
|
free (current);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Auxiliary to add/remove extensions to/from the subset list.
|
|
This is called from riscv_update_subset or riscv_parse_add_implicit_subsets.
|
|
|
|
The EXPLICIT_SUBSET, the corresponding explicit extension. It is NULL means
|
|
called from riscv_update_subset./
|
|
|
|
The IMPLICIT_EXTS, +extension[version] [,...,+extension_n[version_n]]
|
|
-extension [,...,-extension_n],
|
|
full ISA. */
|
|
|
|
static bool
|
|
riscv_update_subset1 (riscv_parse_subset_t *rps,
|
|
riscv_subset_t *explicit_subset,
|
|
const char *implicit_exts)
|
|
{
|
|
const char *p = implicit_exts;
|
|
const char *errmsg_internal = explicit_subset == NULL ? "" : "internal: ";
|
|
const char *errmsg_caller = explicit_subset == NULL
|
|
? ".option arch" : "riscv_implicit_subsets";
|
|
|
|
do
|
|
{
|
|
int major_version = RISCV_UNKNOWN_VERSION;
|
|
int minor_version = RISCV_UNKNOWN_VERSION;
|
|
|
|
bool removed = false;
|
|
switch (*p)
|
|
{
|
|
case '+': removed = false; break;
|
|
case '-': removed = true; break;
|
|
default:
|
|
riscv_release_subset_list (rps->subset_list);
|
|
return riscv_parse_subset (rps, p);
|
|
}
|
|
++p;
|
|
|
|
char *subset = xstrdup (p);
|
|
char *q = subset;
|
|
const char *end_of_version;
|
|
/* Extract the whole prefixed extension by ','. */
|
|
while (*q != '\0' && *q != ',')
|
|
q++;
|
|
|
|
/* Look forward to the first letter which is not <major>p<minor>. */
|
|
bool find_any_version = false;
|
|
bool find_minor_version = false;
|
|
size_t len = q - subset;
|
|
size_t i;
|
|
for (i = len; i > 0; i--)
|
|
{
|
|
q--;
|
|
if (ISDIGIT (*q))
|
|
find_any_version = true;
|
|
else if (find_any_version
|
|
&& !find_minor_version
|
|
&& *q == 'p'
|
|
&& ISDIGIT (*(q - 1)))
|
|
find_minor_version = true;
|
|
else
|
|
break;
|
|
}
|
|
if (len > 0)
|
|
q++;
|
|
|
|
/* Check if the end of extension is 'p' or not. If yes, then
|
|
the second letter from the end cannot be number. */
|
|
if (len > 1 && *(q - 1) == 'p' && ISDIGIT (*(q - 2)))
|
|
{
|
|
*q = '\0';
|
|
rps->error_handler
|
|
(_("%sinvalid ISA extension ends with <number>p in %s `%s'"),
|
|
errmsg_internal, errmsg_caller, implicit_exts);
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
end_of_version =
|
|
riscv_parsing_subset_version (q, &major_version, &minor_version);
|
|
*q = '\0';
|
|
if (end_of_version == NULL)
|
|
{
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
if (strlen (subset) == 0
|
|
|| (strlen (subset) == 1
|
|
&& riscv_ext_order[(*subset - 'a')] == 0)
|
|
|| (strlen (subset) > 1
|
|
&& rps->check_unknown_prefixed_ext
|
|
&& !riscv_recognized_prefixed_ext (subset)))
|
|
{
|
|
rps->error_handler
|
|
(_("%sunknown ISA extension `%s' in %s `%s'"),
|
|
errmsg_internal, subset, errmsg_caller, implicit_exts);
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
if (explicit_subset == NULL
|
|
&& (strcmp (subset, "i") == 0
|
|
|| strcmp (subset, "e") == 0
|
|
|| strcmp (subset, "g") == 0))
|
|
{
|
|
rps->error_handler
|
|
(_("%scannot + or - base extension `%s' in %s `%s'"),
|
|
errmsg_internal, subset, errmsg_caller, implicit_exts);
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
if (removed)
|
|
riscv_remove_subset (rps->subset_list, subset);
|
|
else
|
|
{
|
|
riscv_subset_t *isubset = NULL;
|
|
if (!riscv_lookup_subset (rps->subset_list, subset, &isubset))
|
|
riscv_parse_add_subset (rps, subset, major_version, minor_version,
|
|
true/* implicit */);
|
|
}
|
|
p += end_of_version - subset;
|
|
free (subset);
|
|
}
|
|
while (*p++ == ',');
|
|
|
|
bool conflict = false;
|
|
if (explicit_subset == NULL)
|
|
{
|
|
riscv_parse_add_implicit_subsets (rps);
|
|
conflict = riscv_parse_check_conflicts (rps);
|
|
}
|
|
return conflict;
|
|
}
|
|
|
|
/* Add/Remove an extension to/from the subset list. This is used for
|
|
the .option rvc or norvc, and .option arch directives. */
|
|
|
|
bool
|
|
riscv_update_subset (riscv_parse_subset_t *rps,
|
|
const char *str)
|
|
{
|
|
return riscv_update_subset1 (rps, NULL, str);
|
|
}
|
|
|
|
/* Check if the FEATURE subset is supported or not in the subset list.
|
|
Return true if it is supported; Otherwise, return false. */
|
|
|
|
bool
|
|
riscv_subset_supports (riscv_parse_subset_t *rps,
|
|
const char *feature)
|
|
{
|
|
struct riscv_subset_t *subset;
|
|
return riscv_lookup_subset (rps->subset_list, feature, &subset);
|
|
}
|
|
|
|
/* Each instuction is belonged to an instruction class INSN_CLASS_*.
|
|
Call riscv_subset_supports to make sure if the instuction is valid. */
|
|
|
|
bool
|
|
riscv_multi_subset_supports (riscv_parse_subset_t *rps,
|
|
enum riscv_insn_class insn_class)
|
|
{
|
|
switch (insn_class)
|
|
{
|
|
case INSN_CLASS_I:
|
|
return riscv_subset_supports (rps, "i");
|
|
case INSN_CLASS_ZICBOM:
|
|
return riscv_subset_supports (rps, "zicbom");
|
|
case INSN_CLASS_ZICBOP:
|
|
return riscv_subset_supports (rps, "zicbop");
|
|
case INSN_CLASS_ZICBOZ:
|
|
return riscv_subset_supports (rps, "zicboz");
|
|
case INSN_CLASS_ZICOND:
|
|
return riscv_subset_supports (rps, "zicond");
|
|
case INSN_CLASS_ZICSR:
|
|
return riscv_subset_supports (rps, "zicsr");
|
|
case INSN_CLASS_ZIFENCEI:
|
|
return riscv_subset_supports (rps, "zifencei");
|
|
case INSN_CLASS_ZIHINTNTL:
|
|
return riscv_subset_supports (rps, "zihintntl");
|
|
case INSN_CLASS_ZIHINTNTL_AND_C:
|
|
return (riscv_subset_supports (rps, "zihintntl")
|
|
&& (riscv_subset_supports (rps, "c")
|
|
|| riscv_subset_supports (rps, "zca")));
|
|
case INSN_CLASS_ZIHINTPAUSE:
|
|
return riscv_subset_supports (rps, "zihintpause");
|
|
case INSN_CLASS_M:
|
|
return riscv_subset_supports (rps, "m");
|
|
case INSN_CLASS_ZMMUL:
|
|
return riscv_subset_supports (rps, "zmmul");
|
|
case INSN_CLASS_ZAAMO:
|
|
return riscv_subset_supports (rps, "zaamo");
|
|
case INSN_CLASS_ZABHA:
|
|
return riscv_subset_supports (rps, "zabha");
|
|
case INSN_CLASS_ZACAS:
|
|
return riscv_subset_supports (rps, "zacas");
|
|
case INSN_CLASS_ZABHA_AND_ZACAS:
|
|
return (riscv_subset_supports (rps, "zabha")
|
|
&& riscv_subset_supports (rps, "zacas"));
|
|
case INSN_CLASS_ZALRSC:
|
|
return riscv_subset_supports (rps, "zalrsc");
|
|
case INSN_CLASS_ZAWRS:
|
|
return riscv_subset_supports (rps, "zawrs");
|
|
case INSN_CLASS_F:
|
|
return riscv_subset_supports (rps, "f");
|
|
case INSN_CLASS_D:
|
|
return riscv_subset_supports (rps, "d");
|
|
case INSN_CLASS_Q:
|
|
return riscv_subset_supports (rps, "q");
|
|
case INSN_CLASS_C:
|
|
return (riscv_subset_supports (rps, "c")
|
|
|| riscv_subset_supports (rps, "zca"));
|
|
case INSN_CLASS_F_AND_C:
|
|
return (riscv_subset_supports (rps, "f")
|
|
&& (riscv_subset_supports (rps, "c")
|
|
|| riscv_subset_supports (rps, "zcf")));
|
|
case INSN_CLASS_D_AND_C:
|
|
return (riscv_subset_supports (rps, "d")
|
|
&& (riscv_subset_supports (rps, "c")
|
|
|| riscv_subset_supports (rps, "zcd")));
|
|
case INSN_CLASS_F_INX:
|
|
return (riscv_subset_supports (rps, "f")
|
|
|| riscv_subset_supports (rps, "zfinx"));
|
|
case INSN_CLASS_D_INX:
|
|
return (riscv_subset_supports (rps, "d")
|
|
|| riscv_subset_supports (rps, "zdinx"));
|
|
case INSN_CLASS_Q_INX:
|
|
return (riscv_subset_supports (rps, "q")
|
|
|| riscv_subset_supports (rps, "zqinx"));
|
|
case INSN_CLASS_ZFH_INX:
|
|
return (riscv_subset_supports (rps, "zfh")
|
|
|| riscv_subset_supports (rps, "zhinx"));
|
|
case INSN_CLASS_ZFHMIN:
|
|
return riscv_subset_supports (rps, "zfhmin");
|
|
case INSN_CLASS_ZFHMIN_INX:
|
|
return (riscv_subset_supports (rps, "zfhmin")
|
|
|| riscv_subset_supports (rps, "zhinxmin"));
|
|
case INSN_CLASS_ZFHMIN_AND_D_INX:
|
|
return ((riscv_subset_supports (rps, "zfhmin")
|
|
&& riscv_subset_supports (rps, "d"))
|
|
|| (riscv_subset_supports (rps, "zhinxmin")
|
|
&& riscv_subset_supports (rps, "zdinx")));
|
|
case INSN_CLASS_ZFHMIN_AND_Q_INX:
|
|
return ((riscv_subset_supports (rps, "zfhmin")
|
|
&& riscv_subset_supports (rps, "q"))
|
|
|| (riscv_subset_supports (rps, "zhinxmin")
|
|
&& riscv_subset_supports (rps, "zqinx")));
|
|
case INSN_CLASS_ZFBFMIN:
|
|
return riscv_subset_supports (rps, "zfbfmin");
|
|
case INSN_CLASS_ZFA:
|
|
return riscv_subset_supports (rps, "zfa");
|
|
case INSN_CLASS_D_AND_ZFA:
|
|
return riscv_subset_supports (rps, "d")
|
|
&& riscv_subset_supports (rps, "zfa");
|
|
case INSN_CLASS_Q_AND_ZFA:
|
|
return riscv_subset_supports (rps, "q")
|
|
&& riscv_subset_supports (rps, "zfa");
|
|
case INSN_CLASS_ZFH_AND_ZFA:
|
|
return riscv_subset_supports (rps, "zfh")
|
|
&& riscv_subset_supports (rps, "zfa");
|
|
case INSN_CLASS_ZFH_OR_ZVFH_AND_ZFA:
|
|
return (riscv_subset_supports (rps, "zfh")
|
|
|| riscv_subset_supports (rps, "zvfh"))
|
|
&& riscv_subset_supports (rps, "zfa");
|
|
case INSN_CLASS_ZBA:
|
|
return riscv_subset_supports (rps, "zba");
|
|
case INSN_CLASS_ZBB:
|
|
return riscv_subset_supports (rps, "zbb");
|
|
case INSN_CLASS_ZBC:
|
|
return riscv_subset_supports (rps, "zbc");
|
|
case INSN_CLASS_ZBS:
|
|
return riscv_subset_supports (rps, "zbs");
|
|
case INSN_CLASS_ZBKB:
|
|
return riscv_subset_supports (rps, "zbkb");
|
|
case INSN_CLASS_ZBKC:
|
|
return riscv_subset_supports (rps, "zbkc");
|
|
case INSN_CLASS_ZBKX:
|
|
return riscv_subset_supports (rps, "zbkx");
|
|
case INSN_CLASS_ZBB_OR_ZBKB:
|
|
return (riscv_subset_supports (rps, "zbb")
|
|
|| riscv_subset_supports (rps, "zbkb"));
|
|
case INSN_CLASS_ZBC_OR_ZBKC:
|
|
return (riscv_subset_supports (rps, "zbc")
|
|
|| riscv_subset_supports (rps, "zbkc"));
|
|
case INSN_CLASS_ZKND:
|
|
return riscv_subset_supports (rps, "zknd");
|
|
case INSN_CLASS_ZKNE:
|
|
return riscv_subset_supports (rps, "zkne");
|
|
case INSN_CLASS_ZKNH:
|
|
return riscv_subset_supports (rps, "zknh");
|
|
case INSN_CLASS_ZKND_OR_ZKNE:
|
|
return (riscv_subset_supports (rps, "zknd")
|
|
|| riscv_subset_supports (rps, "zkne"));
|
|
case INSN_CLASS_ZKSED:
|
|
return riscv_subset_supports (rps, "zksed");
|
|
case INSN_CLASS_ZKSH:
|
|
return riscv_subset_supports (rps, "zksh");
|
|
case INSN_CLASS_V:
|
|
return (riscv_subset_supports (rps, "v")
|
|
|| riscv_subset_supports (rps, "zve64x")
|
|
|| riscv_subset_supports (rps, "zve32x"));
|
|
case INSN_CLASS_ZVEF:
|
|
return (riscv_subset_supports (rps, "v")
|
|
|| riscv_subset_supports (rps, "zve64d")
|
|
|| riscv_subset_supports (rps, "zve64f")
|
|
|| riscv_subset_supports (rps, "zve32f"));
|
|
case INSN_CLASS_ZVBB:
|
|
return riscv_subset_supports (rps, "zvbb");
|
|
case INSN_CLASS_ZVBC:
|
|
return riscv_subset_supports (rps, "zvbc");
|
|
case INSN_CLASS_ZVFBFMIN:
|
|
return riscv_subset_supports (rps, "zvfbfmin");
|
|
case INSN_CLASS_ZVFBFWMA:
|
|
return riscv_subset_supports (rps, "zvfbfwma");
|
|
case INSN_CLASS_ZVKB:
|
|
return riscv_subset_supports (rps, "zvkb");
|
|
case INSN_CLASS_ZVKG:
|
|
return riscv_subset_supports (rps, "zvkg");
|
|
case INSN_CLASS_ZVKNED:
|
|
return riscv_subset_supports (rps, "zvkned");
|
|
case INSN_CLASS_ZVKNHA_OR_ZVKNHB:
|
|
return (riscv_subset_supports (rps, "zvknha")
|
|
|| riscv_subset_supports (rps, "zvknhb"));
|
|
case INSN_CLASS_ZVKSED:
|
|
return riscv_subset_supports (rps, "zvksed");
|
|
case INSN_CLASS_ZVKSH:
|
|
return riscv_subset_supports (rps, "zvksh");
|
|
case INSN_CLASS_ZCB:
|
|
return riscv_subset_supports (rps, "zcb");
|
|
case INSN_CLASS_ZCB_AND_ZBB:
|
|
return (riscv_subset_supports (rps, "zcb")
|
|
&& riscv_subset_supports (rps, "zbb"));
|
|
case INSN_CLASS_ZCB_AND_ZBA:
|
|
return (riscv_subset_supports (rps, "zcb")
|
|
&& riscv_subset_supports (rps, "zba"));
|
|
case INSN_CLASS_ZCB_AND_ZMMUL:
|
|
return (riscv_subset_supports (rps, "zcb")
|
|
&& riscv_subset_supports (rps, "zmmul"));
|
|
case INSN_CLASS_ZCMP:
|
|
return riscv_subset_supports (rps, "zcmp");
|
|
case INSN_CLASS_SVINVAL:
|
|
return riscv_subset_supports (rps, "svinval");
|
|
case INSN_CLASS_H:
|
|
return riscv_subset_supports (rps, "h");
|
|
case INSN_CLASS_XCVMAC:
|
|
return riscv_subset_supports (rps, "xcvmac");
|
|
case INSN_CLASS_XCVALU:
|
|
return riscv_subset_supports (rps, "xcvalu");
|
|
case INSN_CLASS_XCVELW:
|
|
return riscv_subset_supports (rps, "xcvelw");
|
|
case INSN_CLASS_XCVBI:
|
|
return riscv_subset_supports (rps, "xcvbi");
|
|
case INSN_CLASS_XCVMEM:
|
|
return riscv_subset_supports (rps, "xcvmem");
|
|
case INSN_CLASS_XTHEADBA:
|
|
return riscv_subset_supports (rps, "xtheadba");
|
|
case INSN_CLASS_XTHEADBB:
|
|
return riscv_subset_supports (rps, "xtheadbb");
|
|
case INSN_CLASS_XTHEADBS:
|
|
return riscv_subset_supports (rps, "xtheadbs");
|
|
case INSN_CLASS_XTHEADCMO:
|
|
return riscv_subset_supports (rps, "xtheadcmo");
|
|
case INSN_CLASS_XTHEADCONDMOV:
|
|
return riscv_subset_supports (rps, "xtheadcondmov");
|
|
case INSN_CLASS_XTHEADFMEMIDX:
|
|
return riscv_subset_supports (rps, "xtheadfmemidx");
|
|
case INSN_CLASS_XTHEADFMV:
|
|
return riscv_subset_supports (rps, "xtheadfmv");
|
|
case INSN_CLASS_XTHEADINT:
|
|
return riscv_subset_supports (rps, "xtheadint");
|
|
case INSN_CLASS_XTHEADMAC:
|
|
return riscv_subset_supports (rps, "xtheadmac");
|
|
case INSN_CLASS_XTHEADMEMIDX:
|
|
return riscv_subset_supports (rps, "xtheadmemidx");
|
|
case INSN_CLASS_XTHEADMEMPAIR:
|
|
return riscv_subset_supports (rps, "xtheadmempair");
|
|
case INSN_CLASS_XTHEADSYNC:
|
|
return riscv_subset_supports (rps, "xtheadsync");
|
|
case INSN_CLASS_XTHEADVECTOR:
|
|
return riscv_subset_supports (rps, "xtheadvector");
|
|
case INSN_CLASS_XTHEADZVAMO:
|
|
return riscv_subset_supports (rps, "xtheadzvamo");
|
|
case INSN_CLASS_XVENTANACONDOPS:
|
|
return riscv_subset_supports (rps, "xventanacondops");
|
|
case INSN_CLASS_XSFVCP:
|
|
return riscv_subset_supports (rps, "xsfvcp");
|
|
case INSN_CLASS_XSFCEASE:
|
|
return riscv_subset_supports (rps, "xsfcease");
|
|
default:
|
|
rps->error_handler
|
|
(_("internal: unreachable INSN_CLASS_*"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Each instuction is belonged to an instruction class INSN_CLASS_*.
|
|
Call riscv_subset_supports_ext to determine the missing extension. */
|
|
|
|
const char *
|
|
riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
|
|
enum riscv_insn_class insn_class)
|
|
{
|
|
switch (insn_class)
|
|
{
|
|
case INSN_CLASS_I:
|
|
return "i";
|
|
case INSN_CLASS_ZICBOM:
|
|
return "zicbom";
|
|
case INSN_CLASS_ZICBOP:
|
|
return "zicbop";
|
|
case INSN_CLASS_ZICBOZ:
|
|
return "zicboz";
|
|
case INSN_CLASS_ZICOND:
|
|
return "zicond";
|
|
case INSN_CLASS_ZICSR:
|
|
return "zicsr";
|
|
case INSN_CLASS_ZIFENCEI:
|
|
return "zifencei";
|
|
case INSN_CLASS_ZIHINTNTL:
|
|
return "zihintntl";
|
|
case INSN_CLASS_ZIHINTNTL_AND_C:
|
|
if (!riscv_subset_supports (rps, "zihintntl"))
|
|
{
|
|
if (!riscv_subset_supports (rps, "c")
|
|
&& !riscv_subset_supports (rps, "zca"))
|
|
return _("zihintntl' and `c', or `zihintntl' and `zca");
|
|
else
|
|
return "zihintntl";
|
|
}
|
|
else
|
|
return _("c' or `zca");
|
|
case INSN_CLASS_ZIHINTPAUSE:
|
|
return "zihintpause";
|
|
case INSN_CLASS_M:
|
|
return "m";
|
|
case INSN_CLASS_ZMMUL:
|
|
return _ ("m' or `zmmul");
|
|
case INSN_CLASS_ZAAMO:
|
|
return "zaamo";
|
|
case INSN_CLASS_ZABHA:
|
|
return "zabha";
|
|
case INSN_CLASS_ZACAS:
|
|
return "zacas";
|
|
case INSN_CLASS_ZALRSC:
|
|
return "zalrsc";
|
|
case INSN_CLASS_ZAWRS:
|
|
return "zawrs";
|
|
case INSN_CLASS_F:
|
|
return "f";
|
|
case INSN_CLASS_D:
|
|
return "d";
|
|
case INSN_CLASS_Q:
|
|
return "q";
|
|
case INSN_CLASS_C:
|
|
return _("c' or `zca");
|
|
case INSN_CLASS_F_AND_C:
|
|
if (!riscv_subset_supports (rps, "f"))
|
|
{
|
|
if (!riscv_subset_supports (rps, "c")
|
|
&& !riscv_subset_supports (rps, "zcf"))
|
|
return _("f' and `c', or `f' and `zcf");
|
|
else
|
|
return "f";
|
|
}
|
|
else
|
|
return _("c' or `zcf");
|
|
case INSN_CLASS_D_AND_C:
|
|
if (!riscv_subset_supports (rps, "d"))
|
|
{
|
|
if (!riscv_subset_supports (rps, "c")
|
|
&& !riscv_subset_supports (rps, "zcd"))
|
|
return _("d' and `c', or `d' and `zcd");
|
|
else
|
|
return "d";
|
|
}
|
|
else
|
|
return _("c' or `zcd");
|
|
case INSN_CLASS_F_INX:
|
|
return _("f' or `zfinx");
|
|
case INSN_CLASS_D_INX:
|
|
return _("d' or `zdinx");
|
|
case INSN_CLASS_Q_INX:
|
|
return _("q' or `zqinx");
|
|
case INSN_CLASS_ZFH_INX:
|
|
return _("zfh' or `zhinx");
|
|
case INSN_CLASS_ZFHMIN:
|
|
return "zfhmin";
|
|
case INSN_CLASS_ZFHMIN_INX:
|
|
return _("zfhmin' or `zhinxmin");
|
|
case INSN_CLASS_ZFHMIN_AND_D_INX:
|
|
if (riscv_subset_supports (rps, "zfhmin"))
|
|
return "d";
|
|
else if (riscv_subset_supports (rps, "d"))
|
|
return "zfhmin";
|
|
else if (riscv_subset_supports (rps, "zhinxmin"))
|
|
return "zdinx";
|
|
else if (riscv_subset_supports (rps, "zdinx"))
|
|
return "zhinxmin";
|
|
else
|
|
return _("zfhmin' and `d', or `zhinxmin' and `zdinx");
|
|
case INSN_CLASS_ZFHMIN_AND_Q_INX:
|
|
if (riscv_subset_supports (rps, "zfhmin"))
|
|
return "q";
|
|
else if (riscv_subset_supports (rps, "q"))
|
|
return "zfhmin";
|
|
else if (riscv_subset_supports (rps, "zhinxmin"))
|
|
return "zqinx";
|
|
else if (riscv_subset_supports (rps, "zqinx"))
|
|
return "zhinxmin";
|
|
else
|
|
return _("zfhmin' and `q', or `zhinxmin' and `zqinx");
|
|
case INSN_CLASS_ZFBFMIN:
|
|
return "zfbfmin";
|
|
case INSN_CLASS_ZFA:
|
|
return "zfa";
|
|
case INSN_CLASS_D_AND_ZFA:
|
|
if (!riscv_subset_supports (rps, "d")
|
|
&& !riscv_subset_supports (rps, "zfa"))
|
|
return _("d' and `zfa");
|
|
else if (!riscv_subset_supports (rps, "d"))
|
|
return "d";
|
|
else
|
|
return "zfa";
|
|
case INSN_CLASS_Q_AND_ZFA:
|
|
if (!riscv_subset_supports (rps, "q")
|
|
&& !riscv_subset_supports (rps, "zfa"))
|
|
return _("q' and `zfa");
|
|
else if (!riscv_subset_supports (rps, "q"))
|
|
return "q";
|
|
else
|
|
return "zfa";
|
|
case INSN_CLASS_ZFH_AND_ZFA:
|
|
if (!riscv_subset_supports (rps, "zfh")
|
|
&& !riscv_subset_supports (rps, "zfa"))
|
|
return _("zfh' and `zfa");
|
|
else if (!riscv_subset_supports (rps, "zfh"))
|
|
return "zfh";
|
|
else
|
|
return "zfa";
|
|
case INSN_CLASS_ZFH_OR_ZVFH_AND_ZFA:
|
|
if (!riscv_subset_supports (rps, "zfa"))
|
|
{
|
|
if (!riscv_subset_supports (rps, "zfh")
|
|
&& !riscv_subset_supports (rps, "zvfh"))
|
|
return _("zfh' and `zfa', or `zvfh' and `zfa");
|
|
else
|
|
return "zfa";
|
|
}
|
|
else
|
|
return _("zfh' or `zvfh");
|
|
case INSN_CLASS_ZBA:
|
|
return "zba";
|
|
case INSN_CLASS_ZBB:
|
|
return "zbb";
|
|
case INSN_CLASS_ZBC:
|
|
return "zbc";
|
|
case INSN_CLASS_ZBS:
|
|
return "zbs";
|
|
case INSN_CLASS_ZBKB:
|
|
return "zbkb";
|
|
case INSN_CLASS_ZBKC:
|
|
return "zbkc";
|
|
case INSN_CLASS_ZBKX:
|
|
return "zbkx";
|
|
case INSN_CLASS_ZBB_OR_ZBKB:
|
|
return _("zbb' or `zbkb");
|
|
case INSN_CLASS_ZBC_OR_ZBKC:
|
|
return _("zbc' or `zbkc");
|
|
case INSN_CLASS_ZKND:
|
|
return "zknd";
|
|
case INSN_CLASS_ZKNE:
|
|
return "zkne";
|
|
case INSN_CLASS_ZKNH:
|
|
return "zknh";
|
|
case INSN_CLASS_ZKND_OR_ZKNE:
|
|
return _("zknd' or `zkne");
|
|
case INSN_CLASS_ZKSED:
|
|
return "zksed";
|
|
case INSN_CLASS_ZKSH:
|
|
return "zksh";
|
|
case INSN_CLASS_V:
|
|
return _("v' or `zve64x' or `zve32x");
|
|
case INSN_CLASS_ZVEF:
|
|
return _("v' or `zve64d' or `zve64f' or `zve32f");
|
|
case INSN_CLASS_ZVBB:
|
|
return _("zvbb");
|
|
case INSN_CLASS_ZVBC:
|
|
return _("zvbc");
|
|
case INSN_CLASS_ZVFBFMIN:
|
|
return "zvfbfmin";
|
|
case INSN_CLASS_ZVFBFWMA:
|
|
return "zvfbfwma";
|
|
case INSN_CLASS_ZVKB:
|
|
return _("zvkb");
|
|
case INSN_CLASS_ZVKG:
|
|
return _("zvkg");
|
|
case INSN_CLASS_ZVKNED:
|
|
return _("zvkned");
|
|
case INSN_CLASS_ZVKNHA_OR_ZVKNHB:
|
|
return _("zvknha' or `zvknhb");
|
|
case INSN_CLASS_ZVKSED:
|
|
return _("zvksed");
|
|
case INSN_CLASS_ZVKSH:
|
|
return _("zvksh");
|
|
case INSN_CLASS_ZCB:
|
|
return "zcb";
|
|
case INSN_CLASS_ZCB_AND_ZBA:
|
|
return _("zcb' and `zba");
|
|
case INSN_CLASS_ZCB_AND_ZBB:
|
|
return _("zcb' and `zbb");
|
|
case INSN_CLASS_ZCB_AND_ZMMUL:
|
|
return _("zcb' and `zmmul', or `zcb' and `m");
|
|
case INSN_CLASS_ZCMP:
|
|
return "zcmp";
|
|
case INSN_CLASS_SVINVAL:
|
|
return "svinval";
|
|
case INSN_CLASS_H:
|
|
return _("h");
|
|
case INSN_CLASS_XCVMAC:
|
|
return "xcvmac";
|
|
case INSN_CLASS_XCVALU:
|
|
return "xcvalu";
|
|
case INSN_CLASS_XCVELW:
|
|
return "xcvelw";
|
|
case INSN_CLASS_XCVBI:
|
|
return "xcvbi";
|
|
case INSN_CLASS_XCVMEM:
|
|
return "xcvmem";
|
|
case INSN_CLASS_XTHEADBA:
|
|
return "xtheadba";
|
|
case INSN_CLASS_XTHEADBB:
|
|
return "xtheadbb";
|
|
case INSN_CLASS_XTHEADBS:
|
|
return "xtheadbs";
|
|
case INSN_CLASS_XTHEADCMO:
|
|
return "xtheadcmo";
|
|
case INSN_CLASS_XTHEADCONDMOV:
|
|
return "xtheadcondmov";
|
|
case INSN_CLASS_XTHEADFMEMIDX:
|
|
return "xtheadfmemidx";
|
|
case INSN_CLASS_XTHEADFMV:
|
|
return "xtheadfmv";
|
|
case INSN_CLASS_XTHEADINT:
|
|
return "xtheadint";
|
|
case INSN_CLASS_XTHEADMAC:
|
|
return "xtheadmac";
|
|
case INSN_CLASS_XTHEADMEMIDX:
|
|
return "xtheadmemidx";
|
|
case INSN_CLASS_XTHEADMEMPAIR:
|
|
return "xtheadmempair";
|
|
case INSN_CLASS_XTHEADSYNC:
|
|
return "xtheadsync";
|
|
case INSN_CLASS_XTHEADVECTOR:
|
|
return "xtheadvector";
|
|
case INSN_CLASS_XTHEADZVAMO:
|
|
return "xtheadzvamo";
|
|
case INSN_CLASS_XSFCEASE:
|
|
return "xsfcease";
|
|
default:
|
|
rps->error_handler
|
|
(_("internal: unreachable INSN_CLASS_*"));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Print supported extensions with versions if -march=help. */
|
|
|
|
void
|
|
riscv_print_extensions (void)
|
|
{
|
|
/* Record the previous printed extension.
|
|
Print the current one if they are not the same. */
|
|
const struct riscv_supported_ext *cur = NULL, *prev = NULL;
|
|
int i, j;
|
|
|
|
printf ("All available -march extensions for RISC-V:");
|
|
|
|
for (i = 0; riscv_all_supported_ext[i] != NULL; i++)
|
|
{
|
|
const struct riscv_supported_ext *exts = riscv_all_supported_ext[i];
|
|
prev = NULL;
|
|
for (j = 0; exts[j].name != NULL; j++)
|
|
{
|
|
cur = &exts[j];
|
|
/* Unclear version information, skip. */
|
|
if (cur->isa_spec_class == ISA_SPEC_CLASS_NONE
|
|
|| cur->major_version == RISCV_UNKNOWN_VERSION
|
|
|| cur->minor_version == RISCV_UNKNOWN_VERSION)
|
|
continue;
|
|
|
|
/* Same extension. */
|
|
if (prev && strcmp (prev->name, cur->name) == 0)
|
|
{
|
|
/* Same version, skip. */
|
|
if (prev->major_version == cur->major_version
|
|
&& prev->minor_version == cur->minor_version)
|
|
continue;
|
|
/* Different version, print version with comma. */
|
|
else
|
|
printf (", %d.%d", cur->major_version, cur->minor_version);
|
|
}
|
|
/* Different extension, print extension and version with newline. */
|
|
else
|
|
printf ("\n\t%-40s%d.%d", cur->name, cur->major_version,
|
|
cur->minor_version);
|
|
prev = &exts[j];
|
|
}
|
|
}
|
|
printf ("\n");
|
|
}
|