Check all inline frames if they are marked for skip
[deliverable/binutils-gdb.git] / bfd / elfxx-riscv.c
index 6936722f9d5c6b76c916db3a7749bf2d810f1fce..245717f70f137b6ed9b118104958144a8da055df 100644 (file)
@@ -1,5 +1,5 @@
 /* RISC-V-specific support for ELF.
-   Copyright (C) 2011-2017 Free Software Foundation, Inc.
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on TILE-Gx and MIPS targets.
 #include "opcode/riscv.h"
 #include "libiberty.h"
 #include "elfxx-riscv.h"
-#include <stdint.h>
+#include "safe-ctype.h"
 
 #define MINUS_ONE ((bfd_vma)0 - 1)
 
+/* Special handler for ADD/SUB relocations that allows them to be filled out
+   both in the pre-linked and post-linked file.  This is necessary to make
+   pre-linked debug info work, as due to linker relaxations we need to emit
+   relocations for the debug info.  */
+static bfd_reloc_status_type riscv_elf_add_sub_reloc
+  (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+
 /* The relocation table used for SHT_RELA sections.  */
 
 static reloc_howto_type howto_table[] =
@@ -106,8 +113,8 @@ static reloc_howto_type howto_table[] =
         bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_COPY",                /* name */
         FALSE,                         /* partial_inplace */
-        0,                             /* src_mask */
-        0,                             /* dst_mask */
+        0,                             /* src_mask */
+        0,                             /* dst_mask */
         FALSE),                        /* pcrel_offset */
 
   HOWTO (R_RISCV_JUMP_SLOT,            /* type */
@@ -120,8 +127,8 @@ static reloc_howto_type howto_table[] =
         bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_JUMP_SLOT",           /* name */
         FALSE,                         /* partial_inplace */
-        0,                             /* src_mask */
-        0,                             /* dst_mask */
+        0,                             /* src_mask */
+        0,                             /* dst_mask */
         FALSE),                        /* pcrel_offset */
 
   /* Dynamic TLS relocations.  */
@@ -132,7 +139,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_TLS_DTPMOD32",        /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -146,7 +153,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_TLS_DTPMOD64",        /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -160,7 +167,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_TLS_DTPREL32",        /* name */
         TRUE,                          /* partial_inplace */
         0,                             /* src_mask */
@@ -174,7 +181,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_TLS_DTPREL64",        /* name */
         TRUE,                          /* partial_inplace */
         0,                             /* src_mask */
@@ -188,7 +195,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_TLS_TPREL32",         /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -202,7 +209,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        bfd_elf_generic_reloc,         /* special_function */
         "R_RISCV_TLS_TPREL64",         /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -480,7 +487,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_ADD8",                /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -495,7 +502,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_ADD16",               /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -510,7 +517,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_ADD32",               /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -525,7 +532,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_ADD64",               /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -540,7 +547,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_SUB8",                /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -555,7 +562,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_SUB16",               /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -570,7 +577,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_SUB32",               /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -585,7 +592,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_SUB64",               /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -767,7 +774,7 @@ static reloc_howto_type howto_table[] =
         FALSE,                         /* pc_relative */
         0,                             /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        bfd_elf_generic_reloc,         /* special_function */
+        riscv_elf_add_sub_reloc,       /* special_function */
         "R_RISCV_SUB6",                /* name */
         FALSE,                         /* partial_inplace */
         0,                             /* src_mask */
@@ -833,6 +840,21 @@ static reloc_howto_type howto_table[] =
         0,                             /* src_mask */
         MINUS_ONE,                     /* dst_mask */
         FALSE),                        /* pcrel_offset */
+
+  /* 32-bit PC relative.  */
+  HOWTO (R_RISCV_32_PCREL,             /* type */
+        0,                             /* rightshift */
+        2,                             /* 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 */
+        MINUS_ONE,                     /* dst_mask */
+        FALSE),                        /* pcrel_offset */
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -894,6 +916,7 @@ static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_SET8, R_RISCV_SET8 },
   { BFD_RELOC_RISCV_SET16, R_RISCV_SET16 },
   { BFD_RELOC_RISCV_SET32, R_RISCV_SET32 },
+  { BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL },
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
@@ -925,13 +948,628 @@ riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
 }
 
 reloc_howto_type *
-riscv_elf_rtype_to_howto (unsigned int r_type)
+riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
 {
   if (r_type >= ARRAY_SIZE (howto_table))
     {
-      (*_bfd_error_handler) (_("unrecognized relocation (0x%x)"), r_type);
+      (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
+                            abfd, r_type);
       bfd_set_error (bfd_error_bad_value);
       return NULL;
     }
   return &howto_table[r_type];
 }
+
+/* Special_function of RISCV_ADD and RISCV_SUB relocations.  */
+
+static bfd_reloc_status_type
+riscv_elf_add_sub_reloc (bfd *abfd,
+                        arelent *reloc_entry,
+                        asymbol *symbol,
+                        void *data,
+                        asection *input_section,
+                        bfd *output_bfd,
+                        char **error_message ATTRIBUTE_UNUSED)
+{
+  reloc_howto_type *howto = reloc_entry->howto;
+  bfd_vma relocation;
+
+  if (output_bfd != NULL
+      && (symbol->flags & BSF_SECTION_SYM) == 0
+      && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0))
+    {
+      reloc_entry->address += input_section->output_offset;
+      return bfd_reloc_ok;
+    }
+
+  if (output_bfd != NULL)
+    return bfd_reloc_continue;
+
+  relocation = symbol->value + symbol->section->output_section->vma
+    + symbol->section->output_offset + reloc_entry->addend;
+  bfd_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:
+    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;
+}
+
+/* Parsing subset version.
+
+   Return Value:
+     Points to the end of version
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `march`: Full arch string.
+     `p`: Curent parsing position.
+     `major_version`: Parsing result of major version, using
+      default_major_version if version is not present in arch string.
+     `minor_version`: Parsing result of minor version, set to 0 if version is
+     not present in arch string, but set to `default_minor_version` if
+     `major_version` using default_major_version.
+     `default_major_version`: Default major version.
+     `default_minor_version`: Default minor version.
+     `std_ext_p`: True if parsing std extension.  */
+
+static const char *
+riscv_parsing_subset_version (riscv_parse_subset_t *rps,
+                             const char *march,
+                             const char *p,
+                             unsigned *major_version,
+                             unsigned *minor_version,
+                             unsigned default_major_version,
+                             unsigned default_minor_version,
+                             bfd_boolean std_ext_p)
+{
+  bfd_boolean major_p = TRUE;
+  unsigned version = 0;
+  unsigned major = 0;
+  unsigned minor = 0;
+  char np;
+
+  for (;*p; ++p)
+    {
+      if (*p == 'p')
+       {
+         np = *(p + 1);
+
+         if (!ISDIGIT (np))
+           {
+             /* Might be beginning of `p` extension.  */
+             if (std_ext_p)
+               {
+                 *major_version = version;
+                 *minor_version = 0;
+                 return p;
+               }
+             else
+               {
+                 rps->error_handler ("-march=%s: Expect number after `%dp'.",
+                                     march, version);
+                 return NULL;
+               }
+           }
+
+         major = version;
+         major_p = FALSE;
+         version = 0;
+       }
+      else if (ISDIGIT (*p))
+       version = (version * 10) + (*p - '0');
+      else
+       break;
+    }
+
+  if (major_p)
+    major = version;
+  else
+    minor = version;
+
+  if (major == 0 && minor == 0)
+    {
+      /* We don't found any version string, use default version.  */
+      *major_version = default_major_version;
+      *minor_version = default_minor_version;
+    }
+  else
+    {
+      *major_version = major;
+      *minor_version = minor;
+    }
+  return p;
+}
+
+/* Return string which contain all supported standard extensions in
+   canonical order.  */
+
+const char *
+riscv_supported_std_ext (void)
+{
+  return "mafdqlcbjtpvn";
+}
+
+/* Parsing function for standard extensions.
+
+   Return Value:
+     Points to the end of extensions.
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `march`: Full arch string.
+     `p`: Curent parsing position.  */
+
+static const char *
+riscv_parse_std_ext (riscv_parse_subset_t *rps,
+                    const char *march, const char *p)
+{
+  const char *all_std_exts = riscv_supported_std_ext ();
+  const char *std_exts = all_std_exts;
+
+  unsigned major_version = 0;
+  unsigned minor_version = 0;
+  char std_ext = '\0';
+
+  /* First letter must start with i, e or g.  */
+  switch (*p)
+    {
+      case 'i':
+       p++;
+       p = riscv_parsing_subset_version (
+             rps,
+             march,
+             p, &major_version, &minor_version,
+             /* default_major_version= */ 2,
+             /* default_minor_version= */ 0,
+             /* std_ext_p= */TRUE);
+       riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
+       break;
+
+      case 'e':
+       p++;
+       p = riscv_parsing_subset_version (
+             rps,
+             march,
+             p, &major_version, &minor_version,
+             /* default_major_version= */ 1,
+             /* default_minor_version= */ 9,
+             /* std_ext_p= */TRUE);
+
+       riscv_add_subset (rps->subset_list, "e", major_version, minor_version);
+       riscv_add_subset (rps->subset_list, "i", 2, 0);
+
+       if (*rps->xlen > 32)
+         {
+           rps->error_handler ("-march=%s: rv%de is not a valid base ISA",
+                               march, *rps->xlen);
+           return NULL;
+         }
+
+       break;
+
+      case 'g':
+       p++;
+       p = riscv_parsing_subset_version (
+             rps,
+             march,
+             p, &major_version, &minor_version,
+             /* default_major_version= */ 2,
+             /* default_minor_version= */ 0,
+             /* std_ext_p= */TRUE);
+       riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
+
+       for ( ; *std_exts != 'q'; std_exts++)
+         {
+           const char subset[] = {*std_exts, '\0'};
+           riscv_add_subset (
+             rps->subset_list, subset, major_version, minor_version);
+         }
+       break;
+
+      default:
+       rps->error_handler (
+         "-march=%s: first ISA subset must be `e', `i' or `g'", march);
+       return NULL;
+    }
+
+  while (*p)
+    {
+      char subset[2] = {0, 0};
+
+      if (*p == 'x' || *p == 's')
+       break;
+
+      if (*p == '_')
+       {
+         p++;
+         continue;
+       }
+
+      std_ext = *p;
+
+      /* Checking canonical order.  */
+      while (*std_exts && std_ext != *std_exts) std_exts++;
+
+      if (std_ext != *std_exts)
+       {
+         if (strchr (all_std_exts, std_ext) == NULL)
+           rps->error_handler (
+             "-march=%s: unsupported ISA subset `%c'", march, *p);
+         else
+           rps->error_handler (
+             "-march=%s: ISA string is not in canonical order. `%c'",
+             march, *p);
+         return NULL;
+       }
+
+      std_exts++;
+
+      p++;
+      p = riscv_parsing_subset_version (
+           rps,
+           march,
+           p, &major_version, &minor_version,
+           /* default_major_version= */ 2,
+           /* default_minor_version= */ 0,
+           /* std_ext_p= */TRUE);
+
+      subset[0] = std_ext;
+
+      riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
+    }
+  return p;
+}
+
+/* Parsing function for non-standard and supervisor extensions.
+
+   Return Value:
+     Points to the end of extensions.
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `march`: Full arch string.
+     `p`: Curent parsing position.
+     `ext_type`: What kind of extensions, 'x', 's' or 'sx'.
+     `ext_type_str`: Full name for kind of extension.  */
+
+static const char *
+riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
+                              const char *march,
+                              const char *p,
+                              const char *ext_type,
+                              const char *ext_type_str)
+{
+  unsigned major_version = 0;
+  unsigned minor_version = 0;
+  size_t ext_type_len = strlen (ext_type);
+
+  while (*p)
+    {
+      if (*p == '_')
+       {
+         p++;
+         continue;
+       }
+
+      if (strncmp (p, ext_type, ext_type_len) != 0)
+       break;
+
+      /* It's non-standard supervisor extension if it prefix with sx.  */
+      if ((ext_type[0] == 's') && (ext_type_len == 1)
+         && (*(p + 1) == 'x'))
+       break;
+
+      char *subset = xstrdup (p);
+      char *q = subset;
+      const char *end_of_version;
+
+      while (*++q != '\0' && *q != '_' && !ISDIGIT (*q))
+       ;
+
+      end_of_version =
+       riscv_parsing_subset_version (
+         rps,
+         march,
+         q, &major_version, &minor_version,
+         /* default_major_version= */ 2,
+         /* default_minor_version= */ 0,
+         /* std_ext_p= */FALSE);
+
+      *q = '\0';
+
+      riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
+      free (subset);
+      p += end_of_version - subset;
+
+      if (*p != '\0' && *p != '_')
+       {
+         rps->error_handler ("-march=%s: %s must seperate with _",
+                             march, ext_type_str);
+         return NULL;
+       }
+    }
+
+  return p;
+}
+
+/* Function for parsing arch string.
+
+   Return Value:
+     Return TRUE on success.
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `arch`: Arch string.  */
+
+bfd_boolean
+riscv_parse_subset (riscv_parse_subset_t *rps,
+                   const char *arch)
+{
+  const char *p = arch;
+
+  if (strncmp (p, "rv32", 4) == 0)
+    {
+      *rps->xlen = 32;
+      p += 4;
+    }
+  else if (strncmp (p, "rv64", 4) == 0)
+    {
+      *rps->xlen = 64;
+      p += 4;
+    }
+  else
+    {
+      rps->error_handler ("-march=%s: ISA string must begin with rv32 or rv64",
+                         arch);
+      return FALSE;
+    }
+
+  /* Parsing standard extension.  */
+  p = riscv_parse_std_ext (rps, arch, p);
+
+  if (p == NULL)
+    return FALSE;
+
+  /* Parsing non-standard extension.  */
+  p = riscv_parse_sv_or_non_std_ext (
+       rps, arch, p, "x", "non-standard extension");
+
+  if (p == NULL)
+    return FALSE;
+
+  /* Parsing supervisor extension.  */
+  p = riscv_parse_sv_or_non_std_ext (
+       rps, arch, p, "s", "supervisor extension");
+
+  if (p == NULL)
+    return FALSE;
+
+  /* Parsing non-standard supervisor extension.  */
+  p = riscv_parse_sv_or_non_std_ext (
+       rps, arch, p, "sx", "non-standard supervisor extension");
+
+  if (p == NULL)
+    return FALSE;
+
+  if (*p != '\0')
+    {
+      rps->error_handler ("-march=%s: unexpected ISA string at end: %s",
+                         arch, p);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "e")
+      && riscv_lookup_subset (rps->subset_list, "f"))
+    {
+      rps->error_handler ("-march=%s: rv32e does not support the `f' extension",
+                         arch);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "d")
+      && !riscv_lookup_subset (rps->subset_list, "f"))
+    {
+      rps->error_handler ("-march=%s: `d' extension requires `f' extension",
+                         arch);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "q")
+      && !riscv_lookup_subset (rps->subset_list, "d"))
+    {
+      rps->error_handler ("-march=%s: `q' extension requires `d' extension",
+                         arch);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "q") && *rps->xlen < 64)
+    {
+      rps->error_handler ("-march=%s: rv32 does not support the `q' extension",
+                         arch);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/* Add new subset to list.  */
+
+void
+riscv_add_subset (riscv_subset_list_t *subset_list,
+                 const char *subset,
+                 int major, int minor)
+{
+  riscv_subset_t *s = xmalloc (sizeof *s);
+
+  if (subset_list->head == NULL)
+    subset_list->head = s;
+
+  s->name = xstrdup (subset);
+  s->major_version = major;
+  s->minor_version = minor;
+  s->next = NULL;
+
+  if (subset_list->tail != NULL)
+    subset_list->tail->next = s;
+
+  subset_list->tail = s;
+}
+
+/* Find subset in list without version checking, return NULL if not found.  */
+
+riscv_subset_t *
+riscv_lookup_subset (const riscv_subset_list_t *subset_list,
+                    const char *subset)
+{
+  return riscv_lookup_subset_version (
+          subset_list, subset,
+          RISCV_DONT_CARE_VERSION,
+          RISCV_DONT_CARE_VERSION);
+}
+
+/* Find subset in list with version checking, return NULL if not found.  */
+
+riscv_subset_t *
+riscv_lookup_subset_version (const riscv_subset_list_t *subset_list,
+                            const char *subset,
+                            int major, int minor)
+{
+  riscv_subset_t *s;
+
+  for (s = subset_list->head; s != NULL; s = s->next)
+    if (strcasecmp (s->name, subset) == 0)
+      {
+       if ((major != RISCV_DONT_CARE_VERSION)
+           && (s->major_version != major))
+         return NULL;
+
+       if ((minor != RISCV_DONT_CARE_VERSION)
+           && (s->minor_version != minor))
+         return NULL;
+
+       return s;
+      }
+
+  return NULL;
+}
+
+/* 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;
+}
+
+/* Return the number of digits for the input.  */
+
+static 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 = "_";
+
+  if (subset == NULL)
+    return;
+
+  /* No underline between rvXX and i/e.   */
+  if ((strcasecmp (subset->name, "i") == 0)
+      || (strcasecmp (subset->name, "e") == 0))
+    underline = "";
+
+  snprintf (buf, bufsz, "%s%s%dp%d",
+           underline,
+           subset->name,
+           subset->major_version,
+           subset->minor_version);
+
+  strncat (attr_str, buf, bufsz);
+
+  /* Skip 'i' extension after 'e'.  */
+  if ((strcasecmp (subset->name, "e") == 0)
+      && subset->next
+      && (strcasecmp (subset->next->name, "i") == 0))
+    riscv_arch_str1 (subset->next->next, attr_str, buf, bufsz);
+  else
+    riscv_arch_str1 (subset->next, attr_str, buf, bufsz);
+}
+
+/* Convert subset info to string with explicit version info.  */
+
+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;
+}
This page took 0.031802 seconds and 4 git commands to generate.