| /* RISC-V-specific support for ELF. |
| Copyright (C) 2011-2023 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 **); |
| |
| /* 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 */ |
| |
| /* Reserved for future relocs that the dynamic linker must understand. */ |
| EMPTY_HOWTO (12), |
| 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 */ |
| |
| /* 41 and 42 are reserved. */ |
| EMPTY_HOWTO (0), |
| EMPTY_HOWTO (0), |
| |
| /* 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 */ |
| |
| /* 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 */ |
| |
| /* 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 */ |
| }; |
| |
| /* 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_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_RVC_LUI, R_RISCV_RVC_LUI }, |
| { BFD_RELOC_RISCV_GPREL_I, R_RISCV_GPREL_I }, |
| { BFD_RELOC_RISCV_GPREL_S, R_RISCV_GPREL_S }, |
| { BFD_RELOC_RISCV_TPREL_I, R_RISCV_TPREL_I }, |
| { BFD_RELOC_RISCV_TPREL_S, R_RISCV_TPREL_S }, |
| { 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 }, |
| }; |
| |
| /* 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)) |
| { |
| (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| return NULL; |
| } |
| return &howto_table[r_type]; |
| } |
| |
| /* 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; |
| } |
| |
| /* Always add the IMPLICIT for the SUBSET. */ |
| |
| static bool |
| check_implicit_always (const char *implicit ATTRIBUTE_UNUSED, |
| riscv_subset_t *subset ATTRIBUTE_UNUSED) |
| { |
| return true; |
| } |
| |
| /* Add the IMPLICIT only when the version of SUBSET less than 2.1. */ |
| |
| static bool |
| check_implicit_for_i (const char *implicit ATTRIBUTE_UNUSED, |
| 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 *subset_name; |
| const char *implicit_name; |
| /* A function to determine if we need to add the implicit subset. */ |
| bool (*check_func) (const char *, riscv_subset_t *); |
| }; |
| static struct riscv_implicit_subset riscv_implicit_subsets[] = |
| { |
| {"e", "i", check_implicit_always}, |
| {"i", "zicsr", check_implicit_for_i}, |
| {"i", "zifencei", check_implicit_for_i}, |
| {"g", "i", check_implicit_always}, |
| {"g", "m", check_implicit_always}, |
| {"g", "a", check_implicit_always}, |
| {"g", "f", check_implicit_always}, |
| {"g", "d", check_implicit_always}, |
| {"g", "zicsr", check_implicit_always}, |
| {"g", "zifencei", check_implicit_always}, |
| {"m", "zmmul", check_implicit_always}, |
| {"h", "zicsr", check_implicit_always}, |
| {"q", "d", check_implicit_always}, |
| {"v", "d", check_implicit_always}, |
| {"v", "zve64d", check_implicit_always}, |
| {"v", "zvl128b", check_implicit_always}, |
| {"zve64d", "d", check_implicit_always}, |
| {"zve64d", "zve64f", check_implicit_always}, |
| {"zve64f", "zve32f", check_implicit_always}, |
| {"zve64f", "zve64x", check_implicit_always}, |
| {"zve64f", "zvl64b", check_implicit_always}, |
| {"zve32f", "f", check_implicit_always}, |
| {"zve32f", "zvl32b", check_implicit_always}, |
| {"zve32f", "zve32x", check_implicit_always}, |
| {"zve64x", "zve32x", check_implicit_always}, |
| {"zve64x", "zvl64b", check_implicit_always}, |
| {"zve32x", "zvl32b", 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}, |
| {"d", "f", check_implicit_always}, |
| {"zfh", "zfhmin", check_implicit_always}, |
| {"zfhmin", "f", check_implicit_always}, |
| {"f", "zicsr", check_implicit_always}, |
| {"zqinx", "zdinx", check_implicit_always}, |
| {"zdinx", "zfinx", check_implicit_always}, |
| {"zhinx", "zhinxmin", check_implicit_always}, |
| {"zhinxmin", "zfinx", check_implicit_always}, |
| {"zfinx", "zicsr", check_implicit_always}, |
| {"zk", "zkn", check_implicit_always}, |
| {"zk", "zkr", check_implicit_always}, |
| {"zk", "zkt", check_implicit_always}, |
| {"zkn", "zbkb", check_implicit_always}, |
| {"zkn", "zbkc", check_implicit_always}, |
| {"zkn", "zbkx", check_implicit_always}, |
| {"zkn", "zkne", check_implicit_always}, |
| {"zkn", "zknd", check_implicit_always}, |
| {"zkn", "zknh", check_implicit_always}, |
| {"zks", "zbkb", check_implicit_always}, |
| {"zks", "zbkc", check_implicit_always}, |
| {"zks", "zbkx", check_implicit_always}, |
| {"zks", "zksed", check_implicit_always}, |
| {"zks", "zksh", check_implicit_always}, |
| {"smaia", "ssaia", check_implicit_always}, |
| {"smstateen", "ssstateen", check_implicit_always}, |
| {"smepmp", "zicsr", check_implicit_always}, |
| {"ssaia", "zicsr", check_implicit_always}, |
| {"sscofpmf", "zicsr", check_implicit_always}, |
| {"ssstateen", "zicsr", check_implicit_always}, |
| {"sstc", "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 }, |
| {"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[] = |
| { |
| {"zicbom", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, |
| {"zicbop", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, |
| {"zicboz", ISA_SPEC_CLASS_DRAFT, 1, 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 }, |
| {"zihintpause", ISA_SPEC_CLASS_DRAFT, 2, 0, 0 }, |
| {"zmmul", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, |
| {"zawrs", 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 }, |
| {"zve32d", 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 }, |
| {"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, 0, 1, 0 }, |
| {NULL, 0, 0, 0, 0} |
| }; |
| |
| static struct riscv_supported_ext riscv_supported_std_s_ext[] = |
| { |
| {"smaia", 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 }, |
| {"sscofpmf", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, |
| {"ssstateen", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 }, |
| {"sstc", 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[] = |
| { |
| {"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 }, |
| {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; |
| } |
| |
| /* 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->subset_name; t++) |
| { |
| riscv_subset_t *subset = NULL; |
| if (riscv_lookup_subset (rps->subset_list, t->subset_name, &subset) |
| && t->check_func (t->implicit_name, subset)) |
| riscv_parse_add_subset (rps, t->implicit_name, |
| RISCV_UNKNOWN_VERSION, |
| RISCV_UNKNOWN_VERSION, true); |
| } |
| } |
| |
| /* 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_lookup_subset (rps->subset_list, "e", &subset) |
| && xlen > 32) |
| { |
| rps->error_handler |
| (_("rv%d does not support the `e' 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_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; |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| /* 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) |
| { |
| const char *p = str; |
| |
| 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 |
| (_("invalid ISA extension ends with <number>p " |
| "in .option arch `%s'"), str); |
| 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 |
| (_("unknown ISA extension `%s' in .option arch `%s'"), |
| subset, str); |
| free (subset); |
| return false; |
| } |
| |
| if (strcmp (subset, "i") == 0 |
| || strcmp (subset, "e") == 0 |
| || strcmp (subset, "g") == 0) |
| { |
| rps->error_handler |
| (_("cannot + or - base extension `%s' in .option " |
| "arch `%s'"), subset, str); |
| free (subset); |
| return false; |
| } |
| |
| if (removed) |
| riscv_remove_subset (rps->subset_list, subset); |
| else |
| riscv_parse_add_subset (rps, subset, major_version, minor_version, true); |
| p += end_of_version - subset; |
| free (subset); |
| } |
| while (*p++ == ','); |
| |
| riscv_parse_add_implicit_subsets (rps); |
| return riscv_parse_check_conflicts (rps); |
| } |
| |
| /* 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_ZICSR: |
| return riscv_subset_supports (rps, "zicsr"); |
| case INSN_CLASS_ZIFENCEI: |
| return riscv_subset_supports (rps, "zifencei"); |
| 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_A: |
| return riscv_subset_supports (rps, "a"); |
| 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"); |
| case INSN_CLASS_F_AND_C: |
| return (riscv_subset_supports (rps, "f") |
| && riscv_subset_supports (rps, "c")); |
| case INSN_CLASS_D_AND_C: |
| return (riscv_subset_supports (rps, "d") |
| && riscv_subset_supports (rps, "c")); |
| 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_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_SVINVAL: |
| return riscv_subset_supports (rps, "svinval"); |
| case INSN_CLASS_H: |
| return riscv_subset_supports (rps, "h"); |
| 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"); |
| 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_ZICSR: |
| return "zicsr"; |
| case INSN_CLASS_ZIFENCEI: |
| return "zifencei"; |
| case INSN_CLASS_ZIHINTPAUSE: |
| return "zihintpause"; |
| case INSN_CLASS_M: |
| return "m"; |
| case INSN_CLASS_ZMMUL: |
| return _ ("m' or `zmmul"); |
| case INSN_CLASS_A: |
| return "a"; |
| 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"; |
| case INSN_CLASS_F_AND_C: |
| if (!riscv_subset_supports (rps, "f") |
| && !riscv_subset_supports (rps, "c")) |
| return _("f' and `c"); |
| else if (!riscv_subset_supports (rps, "f")) |
| return "f"; |
| else |
| return "c"; |
| case INSN_CLASS_D_AND_C: |
| if (!riscv_subset_supports (rps, "d") |
| && !riscv_subset_supports (rps, "c")) |
| return _("d' and `c"); |
| else if (!riscv_subset_supports (rps, "d")) |
| return "d"; |
| else |
| return "c"; |
| 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_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_SVINVAL: |
| return "svinval"; |
| case INSN_CLASS_H: |
| return _("h"); |
| 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"; |
| default: |
| rps->error_handler |
| (_("internal: unreachable INSN_CLASS_*")); |
| return NULL; |
| } |
| } |