mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
542d8137f9
1 Zabha depends on Zaamo: <https://github.com/riscv/riscv-isa-manual/blob/main/src/zabha.adoc> 2 Zacas depends on Zaamo: <https://github.com/riscv/riscv-isa-manual/blob/main/src/zacas.adoc> bfd/ChangeLog: * elfxx-riscv.c: Zabha and Zacas implicitly depend on Zaamo. gas/ChangeLog: * testsuite/gas/riscv/imply.d: Updated. Signed-off-by: Xiao Zeng <zengxiao@eswincomputing.com>
3105 lines
94 KiB
C
3105 lines
94 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", "+zaamo", check_implicit_always},
|
|
{"zacas", "+zaamo", 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},
|
|
{"zcmop", "+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 },
|
|
{"zimop", ISA_SPEC_CLASS_DRAFT, 1, 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 },
|
|
{"zcmop", 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 },
|
|
{"smrnmi", 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[] =
|
|
{
|
|
{"xcvalu", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvbi", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvbitmanip", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvelw", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvmac", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvmem", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"xcvsimd", 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_ZIMOP:
|
|
return riscv_subset_supports (rps, "zimop");
|
|
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_ZCMOP:
|
|
return riscv_subset_supports (rps, "zcmop");
|
|
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_XCVALU:
|
|
return riscv_subset_supports (rps, "xcvalu");
|
|
case INSN_CLASS_XCVBI:
|
|
return riscv_subset_supports (rps, "xcvbi");
|
|
case INSN_CLASS_XCVBITMANIP:
|
|
return riscv_subset_supports (rps, "xcvbitmanip");
|
|
case INSN_CLASS_XCVELW:
|
|
return riscv_subset_supports (rps, "xcvelw");
|
|
case INSN_CLASS_XCVMAC:
|
|
return riscv_subset_supports (rps, "xcvmac");
|
|
case INSN_CLASS_XCVMEM:
|
|
return riscv_subset_supports (rps, "xcvmem");
|
|
case INSN_CLASS_XCVSIMD:
|
|
return riscv_subset_supports (rps, "xcvsimd");
|
|
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_ZIMOP:
|
|
return "zimop";
|
|
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_ZCMOP:
|
|
return "zcmop";
|
|
case INSN_CLASS_ZCMP:
|
|
return "zcmp";
|
|
case INSN_CLASS_SVINVAL:
|
|
return "svinval";
|
|
case INSN_CLASS_H:
|
|
return _("h");
|
|
case INSN_CLASS_XCVALU:
|
|
return "xcvalu";
|
|
case INSN_CLASS_XCVBI:
|
|
return "xcvbi";
|
|
case INSN_CLASS_XCVBITMANIP:
|
|
return "xcvbitmanip";
|
|
case INSN_CLASS_XCVELW:
|
|
return "xcvelw";
|
|
case INSN_CLASS_XCVMAC:
|
|
return "xcvmac";
|
|
case INSN_CLASS_XCVMEM:
|
|
return "xcvmem";
|
|
case INSN_CLASS_XCVSIMD:
|
|
return "xcvsimd";
|
|
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");
|
|
}
|