/* Common hooks for RISC-V. Copyright (C) 2016-2022 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC 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 GCC; see the file COPYING3. If not see . */ #include #define INCLUDE_STRING #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "common/common-target.h" #include "common/common-target-def.h" #include "opts.h" #include "flags.h" #include "diagnostic-core.h" #include "config/riscv/riscv-protos.h" #include "config/riscv/riscv-subset.h" #ifdef TARGET_BIG_ENDIAN_DEFAULT #undef TARGET_DEFAULT_TARGET_FLAGS #define TARGET_DEFAULT_TARGET_FLAGS (MASK_BIG_ENDIAN) #endif /* Type for implied ISA info. */ struct riscv_implied_info_t { const char *ext; const char *implied_ext; }; /* Implied ISA info, must end with NULL sentinel. */ static const riscv_implied_info_t riscv_implied_info[] = { {"d", "f"}, {"f", "zicsr"}, {"d", "zicsr"}, {"zk", "zkn"}, {"zk", "zkr"}, {"zk", "zkt"}, {"zkn", "zbkb"}, {"zkn", "zbkc"}, {"zkn", "zbkx"}, {"zkn", "zkne"}, {"zkn", "zknd"}, {"zkn", "zknh"}, {"zks", "zbkb"}, {"zks", "zbkc"}, {"zks", "zbkx"}, {"zks", "zksed"}, {"zks", "zksh"}, {"v", "zvl128b"}, {"v", "zve64d"}, {"zve32f", "f"}, {"zve64f", "f"}, {"zve64d", "d"}, {"zve32x", "zvl32b"}, {"zve32f", "zve32x"}, {"zve32f", "zvl32b"}, {"zve64x", "zve32x"}, {"zve64x", "zvl64b"}, {"zve64f", "zve32f"}, {"zve64f", "zve64x"}, {"zve64f", "zvl64b"}, {"zve64d", "zve64f"}, {"zve64d", "zvl64b"}, {"zvl64b", "zvl32b"}, {"zvl128b", "zvl64b"}, {"zvl256b", "zvl128b"}, {"zvl512b", "zvl256b"}, {"zvl1024b", "zvl512b"}, {"zvl2048b", "zvl1024b"}, {"zvl4096b", "zvl2048b"}, {"zvl8192b", "zvl4096b"}, {"zvl16384b", "zvl8192b"}, {"zvl32768b", "zvl16384b"}, {"zvl65536b", "zvl32768b"}, {NULL, NULL} }; /* This structure holds version information for specific ISA version. */ struct riscv_ext_version { const char *name; enum riscv_isa_spec_class isa_spec_class; int major_version; int minor_version; }; /* All standard extensions defined in all supported ISA spec. */ static const struct riscv_ext_version riscv_ext_version_table[] = { /* name, ISA spec, major version, minor_version. */ {"e", ISA_SPEC_CLASS_20191213, 1, 9}, {"e", ISA_SPEC_CLASS_20190608, 1, 9}, {"e", ISA_SPEC_CLASS_2P2, 1, 9}, {"i", ISA_SPEC_CLASS_20191213, 2, 1}, {"i", ISA_SPEC_CLASS_20190608, 2, 1}, {"i", ISA_SPEC_CLASS_2P2, 2, 0}, {"m", ISA_SPEC_CLASS_20191213, 2, 0}, {"m", ISA_SPEC_CLASS_20190608, 2, 0}, {"m", ISA_SPEC_CLASS_2P2, 2, 0}, {"a", ISA_SPEC_CLASS_20191213, 2, 1}, {"a", ISA_SPEC_CLASS_20190608, 2, 0}, {"a", ISA_SPEC_CLASS_2P2, 2, 0}, {"f", ISA_SPEC_CLASS_20191213, 2, 2}, {"f", ISA_SPEC_CLASS_20190608, 2, 2}, {"f", ISA_SPEC_CLASS_2P2, 2, 0}, {"d", ISA_SPEC_CLASS_20191213, 2, 2}, {"d", ISA_SPEC_CLASS_20190608, 2, 2}, {"d", ISA_SPEC_CLASS_2P2, 2, 0}, {"c", ISA_SPEC_CLASS_20191213, 2, 0}, {"c", ISA_SPEC_CLASS_20190608, 2, 0}, {"c", ISA_SPEC_CLASS_2P2, 2, 0}, {"v", ISA_SPEC_CLASS_NONE, 1, 0}, {"zicsr", ISA_SPEC_CLASS_20191213, 2, 0}, {"zicsr", ISA_SPEC_CLASS_20190608, 2, 0}, {"zifencei", ISA_SPEC_CLASS_20191213, 2, 0}, {"zifencei", ISA_SPEC_CLASS_20190608, 2, 0}, {"zba", ISA_SPEC_CLASS_NONE, 1, 0}, {"zbb", ISA_SPEC_CLASS_NONE, 1, 0}, {"zbc", ISA_SPEC_CLASS_NONE, 1, 0}, {"zbs", ISA_SPEC_CLASS_NONE, 1, 0}, {"zbkb", ISA_SPEC_CLASS_NONE, 1, 0}, {"zbkc", ISA_SPEC_CLASS_NONE, 1, 0}, {"zbkx", ISA_SPEC_CLASS_NONE, 1, 0}, {"zkne", ISA_SPEC_CLASS_NONE, 1, 0}, {"zknd", ISA_SPEC_CLASS_NONE, 1, 0}, {"zknh", ISA_SPEC_CLASS_NONE, 1, 0}, {"zkr", ISA_SPEC_CLASS_NONE, 1, 0}, {"zksed", ISA_SPEC_CLASS_NONE, 1, 0}, {"zksh", ISA_SPEC_CLASS_NONE, 1, 0}, {"zkt", ISA_SPEC_CLASS_NONE, 1, 0}, {"zve32x", ISA_SPEC_CLASS_NONE, 1, 0}, {"zve32f", ISA_SPEC_CLASS_NONE, 1, 0}, {"zve32d", ISA_SPEC_CLASS_NONE, 1, 0}, {"zve64x", ISA_SPEC_CLASS_NONE, 1, 0}, {"zve64f", ISA_SPEC_CLASS_NONE, 1, 0}, {"zve64d", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl32b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl64b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl128b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl256b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl512b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl1024b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl2048b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl4096b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl8192b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl16384b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl32768b", ISA_SPEC_CLASS_NONE, 1, 0}, {"zvl65536b", ISA_SPEC_CLASS_NONE, 1, 0}, /* Terminate the list. */ {NULL, ISA_SPEC_CLASS_NONE, 0, 0} }; static const riscv_cpu_info riscv_cpu_tables[] = { #define RISCV_CORE(CORE_NAME, ARCH, TUNE) \ {CORE_NAME, ARCH, TUNE}, #include "../../../config/riscv/riscv-cores.def" {NULL, NULL, NULL} }; static const char *riscv_supported_std_ext (void); static riscv_subset_list *current_subset_list = NULL; const riscv_subset_list *riscv_current_subset_list () { return current_subset_list; } riscv_subset_t::riscv_subset_t () : name (), major_version (0), minor_version (0), next (NULL), explicit_version_p (false), implied_p (false) { } riscv_subset_list::riscv_subset_list (const char *arch, location_t loc) : m_arch (arch), m_loc (loc), m_head (NULL), m_tail (NULL), m_xlen (0) { } riscv_subset_list::~riscv_subset_list () { if (!m_head) return; riscv_subset_t *item = this->m_head; while (item != NULL) { riscv_subset_t *next = item->next; delete item; item = next; } } /* Get the rank for single-letter subsets, lower value meaning higher priority. */ static int single_letter_subset_rank (char ext) { int rank; switch (ext) { case 'i': return 0; case 'e': return 1; default: break; } const char *all_ext = riscv_supported_std_ext (); const char *ext_pos = strchr (all_ext, ext); if (ext_pos == NULL) /* If got an unknown extension letter, then give it an alphabetical order, but after all known standard extension. */ rank = strlen (all_ext) + ext - 'a'; else rank = (int)(ext_pos - all_ext) + 2 /* e and i has higher rank. */; return rank; } /* Get the rank for multi-letter subsets, lower value meaning higher priority. */ static int multi_letter_subset_rank (const std::string &subset) { gcc_assert (subset.length () >= 2); int high_order = -1; int low_order = 0; /* The order between multi-char extensions: s -> h -> z -> x. */ char multiletter_class = subset[0]; switch (multiletter_class) { case 's': high_order = 0; break; case 'h': high_order = 1; break; case 'z': high_order = 2; break; case 'x': high_order = 3; break; default: gcc_unreachable (); return -1; } if (multiletter_class == 'z') /* Order for z extension on spec: If multiple "Z" extensions are named, they should be ordered first by category, then alphabetically within a category - for example, "Zicsr_Zifencei_Zam". */ low_order = single_letter_subset_rank (subset[1]); else low_order = 0; return (high_order << 8) + low_order; } /* subset compare Returns an integral value indicating the relationship between the subsets: Return value indicates -1 B has higher order than A. 0 A and B are same subset. 1 A has higher order than B. */ static int subset_cmp (const std::string &a, const std::string &b) { if (a == b) return 0; size_t a_len = a.length (); size_t b_len = b.length (); /* Single-letter extension always get higher order than multi-letter extension. */ if (a_len == 1 && b_len != 1) return 1; if (a_len != 1 && b_len == 1) return -1; if (a_len == 1 && b_len == 1) { int rank_a = single_letter_subset_rank (a[0]); int rank_b = single_letter_subset_rank (b[0]); if (rank_a < rank_b) return 1; else return -1; } else { int rank_a = multi_letter_subset_rank(a); int rank_b = multi_letter_subset_rank(b); /* Using alphabetical/lexicographical order if they have same rank. */ if (rank_a == rank_b) /* The return value of strcmp has opposite meaning. */ return -strcmp (a.c_str (), b.c_str ()); else return (rank_a < rank_b) ? 1 : -1; } } /* Add new subset to list. */ void riscv_subset_list::add (const char *subset, int major_version, int minor_version, bool explicit_version_p, bool implied_p) { riscv_subset_t *ext = lookup (subset); if (ext) { if (ext->implied_p) { /* We won't add impiled `ext` if it already in list. */ gcc_assert (!implied_p); ext->implied_p = implied_p; ext->major_version = major_version; ext->minor_version = minor_version; } else error_at ( m_loc, "%<-march=%s%>: Extension `%s' appear more than one time.", m_arch, subset); return; } riscv_subset_t *s = new riscv_subset_t (); riscv_subset_t *itr; if (m_head == NULL) m_head = s; s->name = subset; s->major_version = major_version; s->minor_version = minor_version; s->explicit_version_p = explicit_version_p; s->implied_p = implied_p; s->next = NULL; if (m_tail == NULL) { m_tail = s; return; } /* e, i or g should be first subext, never come here. */ gcc_assert (subset[0] != 'e' && subset[0] != 'i' && subset[0] != 'g'); if (m_tail == m_head) { gcc_assert (m_head->next == NULL); m_head->next = s; m_tail = s; return; } gcc_assert (m_head->next != NULL); /* Subset list must in canonical order, but implied subset won't add in canonical order. */ for (itr = m_head; itr->next != NULL; itr = itr->next) { riscv_subset_t *next = itr->next; int cmp = subset_cmp (s->name, next->name); gcc_assert (cmp != 0); if (cmp > 0) { s->next = next; itr->next = s; return; } } /* Insert at tail of the list. */ itr->next = s; m_tail = s; } static void get_default_version (const char *ext, unsigned int *major_version, unsigned int *minor_version) { const riscv_ext_version *ext_ver; for (ext_ver = &riscv_ext_version_table[0]; ext_ver->name != NULL; ++ext_ver) if (strcmp (ext, ext_ver->name) == 0) { if ((ext_ver->isa_spec_class == riscv_isa_spec) || (ext_ver->isa_spec_class == ISA_SPEC_CLASS_NONE)) { *major_version = ext_ver->major_version; *minor_version = ext_ver->minor_version; return; } } /* Not found version info. */ *major_version = 0; *minor_version = 0; } /* Add new subset to list, but using default version from ISA spec version. */ void riscv_subset_list::add (const char *subset, bool implied_p) { unsigned int major_version = 0, minor_version = 0; get_default_version (subset, &major_version, &minor_version); add (subset, major_version, minor_version, false, implied_p); } /* Convert subset info to string with explicit version info, VERSION_P to determine append version info or not. */ std::string riscv_subset_list::to_string (bool version_p) const { std::ostringstream oss; oss << "rv" << m_xlen; bool first = true; riscv_subset_t *subset; bool skip_zifencei = false; bool skip_zicsr = false; /* For RISC-V ISA version 2.2 or earlier version, zicsr and zifencei is included in the base ISA. */ if (riscv_isa_spec == ISA_SPEC_CLASS_2P2) { skip_zifencei = true; skip_zicsr = true; } #ifndef HAVE_AS_MISA_SPEC /* Skip since older binutils doesn't recognize zicsr. */ skip_zicsr = true; #endif #ifndef HAVE_AS_MARCH_ZIFENCEI /* Skip since older binutils doesn't recognize zifencei, we made a mistake in that binutils 2.35 supports zicsr but not zifencei. */ skip_zifencei = true; #endif for (subset = m_head; subset != NULL; subset = subset->next) { if (subset->implied_p && skip_zifencei && subset->name == "zifencei") continue; if (subset->implied_p && skip_zicsr && subset->name == "zicsr") continue; /* For !version_p, we only separate extension with underline for multi-letter extension. */ if (!first && (version_p || subset->explicit_version_p || subset->name.length() > 1)) oss << '_'; first = false; oss << subset->name; /* Let binutils decide the extension version if we don't know. */ if ((version_p || subset->explicit_version_p) && (subset->major_version != 0 || subset->minor_version != 0)) oss << subset->major_version << 'p' << subset->minor_version; } return oss.str (); } /* Find subset in list with version checking, return NULL if not found. major/minor version checking can be ignored if major_version/minor_version is RISCV_DONT_CARE_VERSION. */ riscv_subset_t * riscv_subset_list::lookup (const char *subset, int major_version, int minor_version) const { riscv_subset_t *s; for (s = m_head; s != NULL; s = s->next) if (strcasecmp (s->name.c_str (), subset) == 0) { if ((major_version != RISCV_DONT_CARE_VERSION) && (s->major_version != major_version)) return NULL; if ((minor_version != RISCV_DONT_CARE_VERSION) && (s->minor_version != minor_version)) return NULL; return s; } return s; } /* Return string which contains all supported standard extensions in canonical order. */ static const char * riscv_supported_std_ext (void) { return "mafdqlcbjktpvn"; } /* Parsing subset version. Return Value: Points to the end of version Arguments: `ext`: This extension. `p`: Current 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. `std_ext_p`: True if parsing std extension. `explicit_version_p`: True if this subset is not using default version. */ const char * riscv_subset_list::parsing_subset_version (const char *ext, const char *p, unsigned *major_version, unsigned *minor_version, bool std_ext_p, bool *explicit_version_p) { bool major_p = true; unsigned version = 0; unsigned major = 0; unsigned minor = 0; *explicit_version_p = false; /* If we got `p`, that means we are still parsing standard extension. */ gcc_assert (std_ext_p || *p != 'p'); if (*p != 'p') { for (; *p; ++p) { if (*p == 'p') { if (!ISDIGIT (*(p+1))) { error_at (m_loc, "%<-march=%s%>: Expect number " "after %<%dp%>.", m_arch, version); return NULL; } if (!major_p) { error_at (m_loc, "%<-march=%s%>: For %<%s%dp%dp?%>, version " "number with more than 2 level is not supported.", m_arch, ext, major, 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) get_default_version (ext, major_version, minor_version); else { *explicit_version_p = true; *major_version = major; *minor_version = minor; } return p; } /* Parsing function for standard extensions. Return Value: Points to the end of extensions. Arguments: `p`: Current parsing position. */ const char * riscv_subset_list::parse_std_ext (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'; bool explicit_version_p = false; /* First letter must start with i, e or g. */ switch (*p) { case 'i': p++; p = parsing_subset_version ("i", p, &major_version, &minor_version, /* std_ext_p= */ true, &explicit_version_p); add ("i", major_version, minor_version, explicit_version_p, false); break; case 'e': p++; p = parsing_subset_version ("e", p, &major_version, &minor_version, /* std_ext_p= */ true, &explicit_version_p); add ("e", major_version, minor_version, explicit_version_p, false); if (m_xlen > 32) { error_at (m_loc, "%<-march=%s%>: rv%de is not a valid base ISA", m_arch, m_xlen); return NULL; } break; case 'g': p++; p = parsing_subset_version ("g", p, &major_version, &minor_version, /* std_ext_p= */ true, &explicit_version_p); if (major_version != 0 || minor_version != 0) { warning_at (m_loc, 0, "version of `g` will be omitted, please " "specify version for individual extension."); } /* We have special rule for G, we disallow rv32gm2p but allow rv32g_zicsr here, basically we treating G expand to imafd and implied zicsr and zifencei. */ add ("i", false); add ("m", false); add ("a", false); add ("f", false); add ("d", false); add ("zicsr", true); add ("zifencei", true); break; default: error_at (m_loc, "%<-march=%s%>: first ISA subset must be %, " "% or %", m_arch); return NULL; } while (p != NULL && *p) { char subset[2] = {0, 0}; if (*p == 'x' || *p == 's' || *p == 'h' || *p == 'z') 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) error_at (m_loc, "%<-march=%s%>: unsupported ISA subset %<%c%>", m_arch, *p); else error_at (m_loc, "%<-march=%s%>: ISA string is not in canonical order. " "%<%c%>", m_arch, *p); return NULL; } std_exts++; p++; subset[0] = std_ext; p = parsing_subset_version (subset, p, &major_version, &minor_version, /* std_ext_p= */ true, &explicit_version_p); add (subset, major_version, minor_version, explicit_version_p, false); } return p; } /* Check any implied extensions for EXT. */ void riscv_subset_list::handle_implied_ext (riscv_subset_t *ext) { const riscv_implied_info_t *implied_info; for (implied_info = &riscv_implied_info[0]; implied_info->ext; ++implied_info) { if (strcmp (ext->name.c_str (), implied_info->ext) != 0) continue; /* Skip if implied extension already present. */ if (lookup (implied_info->implied_ext)) continue; /* Version of implied extension will get from current ISA spec version. */ add (implied_info->implied_ext, true); } /* For RISC-V ISA version 2.2 or earlier version, zicsr and zifence is included in the base ISA. */ if (riscv_isa_spec == ISA_SPEC_CLASS_2P2) { if (lookup ("zicsr") == NULL) add ("zicsr", true); if (lookup ("zifencei") == NULL) add ("zifencei", true); } } /* Parsing function for multi-letter extensions. Return Value: Points to the end of extensions. Arguments: `p`: Current parsing position. `ext_type`: What kind of extensions, 's', 'h', 'z' or 'x'. `ext_type_str`: Full name for kind of extension. */ const char * riscv_subset_list::parse_multiletter_ext (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; char *subset = xstrdup (p); char *q = subset; const char *end_of_version; bool explicit_version_p = false; char *ext; char backup; size_t len; size_t end_of_version_pos, i; bool found_any_number = false; bool found_minor_version = false; /* Parse until end of this extension including version number. */ while (*++q != '\0' && *q != '_') ; backup = *q; *q = '\0'; len = q - subset; *q = backup; end_of_version_pos = len; /* Find the begin of version string. */ for (i = len -1; i > 0; --i) { if (ISDIGIT (subset[i])) { found_any_number = true; continue; } /* Might be version seperator, but need to check one more char, we only allow p, so we could stop parsing if found any more `p`. */ if (subset[i] == 'p' && !found_minor_version && found_any_number && ISDIGIT (subset[i-1])) { found_minor_version = true; continue; } end_of_version_pos = i + 1; break; } backup = subset[end_of_version_pos]; subset[end_of_version_pos] = '\0'; ext = xstrdup (subset); subset[end_of_version_pos] = backup; end_of_version = parsing_subset_version (ext, subset + end_of_version_pos, &major_version, &minor_version, /* std_ext_p= */ false, &explicit_version_p); free (ext); if (end_of_version == NULL) return NULL; subset[end_of_version_pos] = '\0'; if (strlen (subset) == 1) { error_at (m_loc, "%<-march=%s%>: name of %s must be more than 1 letter", m_arch, ext_type_str); free (subset); return NULL; } add (subset, major_version, minor_version, explicit_version_p, false); free (subset); p += end_of_version - subset; if (*p != '\0' && *p != '_') { error_at (m_loc, "%<-march=%s%>: %s must separate with _", m_arch, ext_type_str); return NULL; } } return p; } /* Parsing arch string to subset list, return NULL if parsing failed. */ riscv_subset_list * riscv_subset_list::parse (const char *arch, location_t loc) { riscv_subset_list *subset_list = new riscv_subset_list (arch, loc); riscv_subset_t *itr; const char *p = arch; if (startswith (p, "rv32")) { subset_list->m_xlen = 32; p += 4; } else if (startswith (p, "rv64")) { subset_list->m_xlen = 64; p += 4; } else { error_at (loc, "%<-march=%s%>: ISA string must begin with rv32 or rv64", arch); goto fail; } /* Parsing standard extension. */ p = subset_list->parse_std_ext (p); if (p == NULL) goto fail; /* Parsing supervisor extension. */ p = subset_list->parse_multiletter_ext (p, "s", "supervisor extension"); if (p == NULL) goto fail; /* Parsing hypervisor extension. */ p = subset_list->parse_multiletter_ext (p, "h", "hypervisor extension"); if (p == NULL) goto fail; /* Parsing sub-extensions. */ p = subset_list->parse_multiletter_ext (p, "z", "sub-extension"); if (p == NULL) goto fail; /* Parsing non-standard extension. */ p = subset_list->parse_multiletter_ext (p, "x", "non-standard extension"); if (p == NULL) goto fail; if (*p != '\0') { error_at (loc, "%<-march=%s%>: unexpected ISA string at end: %qs", arch, p); goto fail; } for (itr = subset_list->m_head; itr != NULL; itr = itr->next) { subset_list->handle_implied_ext (itr); } return subset_list; fail: delete subset_list; return NULL; } /* Return the current arch string. */ std::string riscv_arch_str (bool version_p) { if (current_subset_list) return current_subset_list->to_string (version_p); else return std::string(); } /* Type for pointer to member of gcc_options. */ typedef int (gcc_options::*opt_var_ref_t); /* Types for recording extension to internal flag. */ struct riscv_ext_flag_table_t { const char *ext; opt_var_ref_t var_ref; int mask; }; /* Mapping table between extension to internal flag. */ static const riscv_ext_flag_table_t riscv_ext_flag_table[] = { {"e", &gcc_options::x_target_flags, MASK_RVE}, {"m", &gcc_options::x_target_flags, MASK_MUL}, {"a", &gcc_options::x_target_flags, MASK_ATOMIC}, {"f", &gcc_options::x_target_flags, MASK_HARD_FLOAT}, {"d", &gcc_options::x_target_flags, MASK_DOUBLE_FLOAT}, {"c", &gcc_options::x_target_flags, MASK_RVC}, {"v", &gcc_options::x_target_flags, MASK_VECTOR}, {"zicsr", &gcc_options::x_riscv_zi_subext, MASK_ZICSR}, {"zifencei", &gcc_options::x_riscv_zi_subext, MASK_ZIFENCEI}, {"zba", &gcc_options::x_riscv_zb_subext, MASK_ZBA}, {"zbb", &gcc_options::x_riscv_zb_subext, MASK_ZBB}, {"zbc", &gcc_options::x_riscv_zb_subext, MASK_ZBC}, {"zbs", &gcc_options::x_riscv_zb_subext, MASK_ZBS}, {"zbkb", &gcc_options::x_riscv_zk_subext, MASK_ZBKB}, {"zbkc", &gcc_options::x_riscv_zk_subext, MASK_ZBKC}, {"zbkx", &gcc_options::x_riscv_zk_subext, MASK_ZBKX}, {"zknd", &gcc_options::x_riscv_zk_subext, MASK_ZKND}, {"zkne", &gcc_options::x_riscv_zk_subext, MASK_ZKNE}, {"zknh", &gcc_options::x_riscv_zk_subext, MASK_ZKNH}, {"zkr", &gcc_options::x_riscv_zk_subext, MASK_ZKR}, {"zksed", &gcc_options::x_riscv_zk_subext, MASK_ZKSED}, {"zksh", &gcc_options::x_riscv_zk_subext, MASK_ZKSH}, {"zkt", &gcc_options::x_riscv_zk_subext, MASK_ZKT}, {"zve32x", &gcc_options::x_target_flags, MASK_VECTOR}, {"zve32f", &gcc_options::x_target_flags, MASK_VECTOR}, {"zve64x", &gcc_options::x_target_flags, MASK_VECTOR}, {"zve64f", &gcc_options::x_target_flags, MASK_VECTOR}, {"zve64d", &gcc_options::x_target_flags, MASK_VECTOR}, /* We don't need to put complete EEW/EEW_FP info here, due to the implication relation of vector extension. e.g. v -> zve64d ... zve32x, so v has set MASK_VECTOR_EEW_FP_64, MASK_VECTOR_EEW_FP_32, MASK_VECTOR_EEW_64 and MASK_VECTOR_EEW_32 due to the extension implication. */ {"zve32x", &gcc_options::x_riscv_vector_eew_flags, MASK_VECTOR_EEW_32}, {"zve32f", &gcc_options::x_riscv_vector_eew_flags, MASK_VECTOR_EEW_FP_32}, {"zve64x", &gcc_options::x_riscv_vector_eew_flags, MASK_VECTOR_EEW_64}, {"zve64f", &gcc_options::x_riscv_vector_eew_flags, MASK_VECTOR_EEW_FP_32}, {"zve64d", &gcc_options::x_riscv_vector_eew_flags, MASK_VECTOR_EEW_FP_64}, {"zvl32b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL32B}, {"zvl64b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL64B}, {"zvl128b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL128B}, {"zvl256b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL256B}, {"zvl512b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL512B}, {"zvl1024b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL1024B}, {"zvl2048b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL2048B}, {"zvl4096b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL4096B}, {"zvl8192b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL8192B}, {"zvl16384b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL16384B}, {"zvl32768b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL32768B}, {"zvl65536b", &gcc_options::x_riscv_zvl_flags, MASK_ZVL65536B}, {NULL, NULL, 0} }; /* Parse a RISC-V ISA string into an option mask. Must clear or set all arch dependent mask bits, in case more than one -march string is passed. */ static void riscv_parse_arch_string (const char *isa, struct gcc_options *opts, location_t loc) { riscv_subset_list *subset_list; subset_list = riscv_subset_list::parse (isa, loc); if (!subset_list) return; if (opts) { const riscv_ext_flag_table_t *arch_ext_flag_tab; /* Clean up target flags before we set. */ for (arch_ext_flag_tab = &riscv_ext_flag_table[0]; arch_ext_flag_tab->ext; ++arch_ext_flag_tab) opts->*arch_ext_flag_tab->var_ref &= ~arch_ext_flag_tab->mask; if (subset_list->xlen () == 32) opts->x_target_flags &= ~MASK_64BIT; else if (subset_list->xlen () == 64) opts->x_target_flags |= MASK_64BIT; for (arch_ext_flag_tab = &riscv_ext_flag_table[0]; arch_ext_flag_tab->ext; ++arch_ext_flag_tab) { if (subset_list->lookup (arch_ext_flag_tab->ext)) opts->*arch_ext_flag_tab->var_ref |= arch_ext_flag_tab->mask; } } if (current_subset_list) delete current_subset_list; current_subset_list = subset_list; } /* Return the riscv_cpu_info entry for CPU, NULL if not found. */ const riscv_cpu_info * riscv_find_cpu (const char *cpu) { const riscv_cpu_info *cpu_info = &riscv_cpu_tables[0]; for (;cpu_info->name != NULL; ++cpu_info) { const char *name = cpu_info->name; if (strcmp (cpu, name) == 0) return cpu_info; } return NULL; } /* Implement TARGET_HANDLE_OPTION. */ static bool riscv_handle_option (struct gcc_options *opts, struct gcc_options *opts_set ATTRIBUTE_UNUSED, const struct cl_decoded_option *decoded, location_t loc) { switch (decoded->opt_index) { case OPT_march_: riscv_parse_arch_string (decoded->arg, opts, loc); return true; case OPT_mcpu_: if (riscv_find_cpu (decoded->arg) == NULL) error_at (loc, "%<-mcpu=%s%>: unknown CPU", decoded->arg); return true; default: return true; } } /* Expand arch string with implied extensions. */ const char * riscv_expand_arch (int argc ATTRIBUTE_UNUSED, const char **argv) { gcc_assert (argc == 1); location_t loc = UNKNOWN_LOCATION; riscv_parse_arch_string (argv[0], NULL, loc); const std::string arch = riscv_arch_str (false); if (arch.length()) return xasprintf ("-march=%s", arch.c_str()); else return ""; } /* Expand default -mtune option from -mcpu option, use default --with-tune value if -mcpu don't have valid value. */ const char * riscv_default_mtune (int argc, const char **argv) { gcc_assert (argc == 2); const riscv_cpu_info *cpu = riscv_find_cpu (argv[0]); const char *default_mtune = argv[1]; if (cpu) return cpu->tune; else return default_mtune; } /* Expand arch string with implied extensions from -mcpu option. */ const char * riscv_expand_arch_from_cpu (int argc ATTRIBUTE_UNUSED, const char **argv) { gcc_assert (argc > 0 && argc <= 2); const char *default_arch_str = NULL; const char *arch_str = NULL; if (argc >= 2) default_arch_str = argv[1]; const riscv_cpu_info *cpu = riscv_find_cpu (argv[0]); if (cpu == NULL) { if (default_arch_str == NULL) return ""; else arch_str = default_arch_str; } else arch_str = cpu->arch; location_t loc = UNKNOWN_LOCATION; riscv_parse_arch_string (arch_str, NULL, loc); const std::string arch = riscv_arch_str (false); return xasprintf ("-march=%s", arch.c_str()); } /* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */ static const struct default_options riscv_option_optimization_table[] = { { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, { OPT_LEVELS_2_PLUS, OPT_free, NULL, 1 }, { OPT_LEVELS_NONE, 0, NULL, 0 } }; #undef TARGET_OPTION_OPTIMIZATION_TABLE #define TARGET_OPTION_OPTIMIZATION_TABLE riscv_option_optimization_table #undef TARGET_HANDLE_OPTION #define TARGET_HANDLE_OPTION riscv_handle_option struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;