From 8e96621085f2a0364fe87a4019d6cc9bfc52cd11 Mon Sep 17 00:00:00 2001 From: Kito Cheng Date: Sat, 2 Mar 2019 00:36:22 +0000 Subject: RISC-V: Generalize -march support, add ELF attribute support. Kito Cheng Monk Chiang gcc/ * common/config/riscv/riscv-common.c: Include sstream. (riscv_subset_list::to_string): New. (riscv_arch_str): Likewise. * config.gcc (riscv*-*-*): Handle --with-riscv-attribute= * config.in: Regen. * config/riscv/riscv-protos.h (riscv_arch_str): New. * config/riscv/riscv.c (INCLUDE_STRING): Defined. (riscv_emit_attribute): New. (riscv_file_start): Emit attribute if needed. (riscv_option_override): Init riscv_emit_attribute_p. * config/riscv/riscv.opt (mriscv-attribute): New option. * configure.ac (riscv*-*-*): Check binutils is supporting ELF * configure: Regen. * doc/install.texi: Document --with-riscv-attribute. * doc/invoke.texi: Document -mriscv-attribute. * common/config/riscv/riscv-common.c: Include config/riscv/riscv-protos.h. (INCLUDE_STRING): Defined. (RISCV_DONT_CARE_VERSION): Defined. (riscv_subset_t): Declare. (riscv_subset_t::riscv_subset_t): New. (riscv_subset_list): Declare. (riscv_subset_list::riscv_subset_list): New. (riscv_subset_list::~riscv_subset_list): Likewise. (riscv_subset_list::parsing_subset_version): Likewise. (riscv_subset_list::parse_std_ext): Likewise. (riscv_subset_list::parse_sv_or_non_std_ext): Likewise. (riscv_subset_list::add): Likewise. (riscv_subset_list::lookup): Likewise. (riscv_subset_list::xlen): Likewise. (riscv_subset_list::parse): Likewise. (riscv_supported_std_ext): Likewise. (current_subset_list): Likewise. (riscv_parse_arch_string): Using riscv_subset_list::parse to parse. gcc/testsuite/ * gcc.target/riscv/attribute-1.c: New. * gcc.target/riscv/attribute-2.c: Likewise. * gcc.target/riscv/attribute-3.c: Likewise. * gcc.target/riscv/attribute-4.c: Likewise. * gcc.target/riscv/attribute-5.c: Likewise. * gcc.target/riscv/attribute-6.c: Likewise. * gcc.target/riscv/attribute-7.c: Likewise. * gcc.target/riscv/attribute-8.c: Likewise. * gcc.target/riscv/attribute-9.c: Likewise. * gcc.target/riscv/arch-1.c: New. * gcc.target/riscv/arch-2.c: Likewise. * gcc.target/riscv/arch-3.c: Likewise. * gcc.target/riscv/arch-4.c: Likewise. Co-Authored-By: Monk Chiang From-SVN: r269337 --- gcc/common/config/riscv/riscv-common.c | 563 +++++++++++++++++++++++++++++---- 1 file changed, 506 insertions(+), 57 deletions(-) (limited to 'gcc/common') diff --git a/gcc/common/config/riscv/riscv-common.c b/gcc/common/config/riscv/riscv-common.c index cb5bb7f..16ddb26 100644 --- a/gcc/common/config/riscv/riscv-common.c +++ b/gcc/common/config/riscv/riscv-common.c @@ -17,6 +17,9 @@ 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" @@ -26,99 +29,545 @@ along with GCC; see the file COPYING3. If not see #include "opts.h" #include "flags.h" #include "diagnostic-core.h" +#include "config/riscv/riscv-protos.h" -/* 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. */ +#define RISCV_DONT_CARE_VERSION -1 -static void -riscv_parse_arch_string (const char *isa, int *flags, location_t loc) +/* Subset info. */ +struct riscv_subset_t { - const char *p = isa; + riscv_subset_t (); - if (strncmp (p, "rv32", 4) == 0) - *flags &= ~MASK_64BIT, p += 4; - else if (strncmp (p, "rv64", 4) == 0) - *flags |= MASK_64BIT, p += 4; - else + std::string name; + int major_version; + int minor_version; + struct riscv_subset_t *next; +}; + +/* Subset list. */ +class riscv_subset_list +{ +private: + /* Original arch string. */ + const char *m_arch; + + /* Location of arch string, used for report error. */ + location_t m_loc; + + /* Head of subset info list. */ + riscv_subset_t *m_head; + + /* Tail of subset info list. */ + riscv_subset_t *m_tail; + + /* X-len of m_arch. */ + unsigned m_xlen; + + riscv_subset_list (const char *, location_t); + + const char *parsing_subset_version (const char *, unsigned *, unsigned *, + unsigned, unsigned, bool); + + const char *parse_std_ext (const char *); + + const char *parse_sv_or_non_std_ext (const char *, const char *, + const char *); + +public: + ~riscv_subset_list (); + + void add (const char *, int, int); + + riscv_subset_t *lookup (const char *, + int major_version = RISCV_DONT_CARE_VERSION, + int minor_version = RISCV_DONT_CARE_VERSION) const; + + std::string to_string () const; + + unsigned xlen() const {return m_xlen;}; + + static riscv_subset_list *parse (const char *, location_t); + +}; + +static const char *riscv_supported_std_ext (void); + +static riscv_subset_list *current_subset_list = NULL; + +riscv_subset_t::riscv_subset_t () + : name (), major_version (0), minor_version (0), next (NULL) +{ +} + +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) { - error_at (loc, "-march=%s: ISA string must begin with rv32 or rv64", isa); - return; + riscv_subset_t *next = item->next; + delete item; + item = next; } +} - if (*p == 'g') - { - p++; +/* Add new subset to list. */ - *flags &= ~MASK_RVE; +void +riscv_subset_list::add (const char *subset, int major_version, + int minor_version) +{ + riscv_subset_t *s = new riscv_subset_t (); - *flags |= MASK_MUL; - *flags |= MASK_ATOMIC; - *flags |= MASK_HARD_FLOAT; - *flags |= MASK_DOUBLE_FLOAT; - } - else if (*p == 'i') + if (m_head == NULL) + m_head = s; + + s->name = subset; + s->major_version = major_version; + s->minor_version = minor_version; + s->next = NULL; + + if (m_tail != NULL) + m_tail->next = s; + + m_tail = s; +} + +/* Convert subset info to string with explicit version info. */ + +std::string +riscv_subset_list::to_string () const +{ + std::ostringstream oss; + oss << "rv" << m_xlen; + + bool first = true; + riscv_subset_t *subset = m_head; + + while (subset != NULL) { - p++; + if (!first) + oss << '_'; + first = false; + + oss << subset->name + << subset->major_version + << 'p' + << subset->minor_version; + subset = subset->next; + } + + 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. */ - *flags &= ~MASK_RVE; +static const char * +riscv_supported_std_ext (void) +{ + return "mafdqlcbjtpvn"; +} + +/* Parsing subset version. - *flags &= ~MASK_MUL; - if (*p == 'm') - *flags |= MASK_MUL, p++; + Return Value: + Points to the end of version - *flags &= ~MASK_ATOMIC; - if (*p == 'a') - *flags |= MASK_ATOMIC, p++; + Arguments: + `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. + `default_major_version`: Default major version. + `default_minor_version`: Default minor version. + `std_ext_p`: True if parsing std extension. */ - *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT); - if (*p == 'f') +const char * +riscv_subset_list::parsing_subset_version (const char *p, + unsigned *major_version, + unsigned *minor_version, + unsigned default_major_version, + unsigned default_minor_version, + bool std_ext_p) +{ + bool major_p = true; + unsigned version = 0; + unsigned major = 0; + unsigned minor = 0; + char np; + + for (; *p; ++p) + { + if (*p == 'p') { - *flags |= MASK_HARD_FLOAT, p++; + np = *(p + 1); - if (*p == 'd') + if (!ISDIGIT (np)) { - *flags |= MASK_DOUBLE_FLOAT; - p++; + /* Might be beginning of `p` extension. */ + if (std_ext_p) + { + *major_version = version; + *minor_version = 0; + return p; + } + else + { + error_at (m_loc, "-march=%s: Expect number after `%dp'.", + m_arch, version); + return NULL; + } } + + major = version; + major_p = false; + version = 0; } + else if (ISDIGIT (*p)) + version = (version * 10) + (*p - '0'); + else + break; } - else if (*p == 'e') + + if (major_p) + major = version; + else + minor = version; + + if (major == 0 && minor == 0) + { + /* We didn't find 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; +} + +/* 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'; + + /* First letter must start with i, e or g. */ + switch (*p) { + case 'i': + p++; + p = parsing_subset_version (p, &major_version, &minor_version, + /* default_major_version= */ 2, + /* default_minor_version= */ 0, + /* std_ext_p= */ true); + add ("i", major_version, minor_version); + break; + + case 'e': p++; + p = parsing_subset_version (p, &major_version, &minor_version, + /* default_major_version= */ 1, + /* default_minor_version= */ 9, + /* std_ext_p= */ true); - *flags |= MASK_RVE; + add ("e", major_version, minor_version); - if (*flags & MASK_64BIT) + if (m_xlen > 32) { - error ("RV64E is not a valid base ISA"); - return; + error_at (m_loc, "-march=%s: rv%de is not a valid base ISA", m_arch, + m_xlen); + return NULL; } + break; - *flags &= ~MASK_MUL; - if (*p == 'm') - *flags |= MASK_MUL, p++; + case 'g': + p++; + p = parsing_subset_version (p, &major_version, &minor_version, + /* default_major_version= */ 2, + /* default_minor_version= */ 0, + /* std_ext_p= */ true); + add ("i", major_version, minor_version); - *flags &= ~MASK_ATOMIC; - if (*p == 'a') - *flags |= MASK_ATOMIC, p++; + for (; *std_exts != 'q'; std_exts++) + { + const char subset[] = {*std_exts, '\0'}; + add (subset, major_version, minor_version); + } + break; - *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT); + default: + error_at (m_loc, "-march=%s: first ISA subset must be `e', `i' or `g'", + m_arch); + return NULL; } - else + + while (*p) { - error_at (loc, "-march=%s: invalid ISA string", isa); - return; + 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) + 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++; + p = parsing_subset_version (p, &major_version, &minor_version, + /* default_major_version= */ 2, + /* default_minor_version= */ 0, + /* std_ext_p= */ true); + + subset[0] = std_ext; + + add (subset, major_version, minor_version); } + return p; +} - *flags &= ~MASK_RVC; - if (*p == 'c') - *flags |= MASK_RVC, p++; +/* Parsing function for non-standard and supervisor extensions. - if (*p) + Return Value: + Points to the end of extensions. + + Arguments: + `p`: Current parsing position. + `ext_type`: What kind of extensions, 'x', 's' or 'sx'. + `ext_type_str`: Full name for kind of extension. */ + +const char * +riscv_subset_list::parse_sv_or_non_std_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) { - error_at (loc, "-march=%s: unsupported ISA substring %qs", isa, p); - return; + 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 + = parsing_subset_version (q, &major_version, &minor_version, + /* default_major_version= */ 2, + /* default_minor_version= */ 0, + /* std_ext_p= */ FALSE); + + *q = '\0'; + + add (subset, major_version, minor_version); + 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); + const char *p = arch; + if (strncmp (p, "rv32", 4) == 0) + { + subset_list->m_xlen = 32; + p += 4; + } + else if (strncmp (p, "rv64", 4) == 0) + { + 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 non-standard extension. */ + p = subset_list->parse_sv_or_non_std_ext (p, "x", "non-standard extension"); + + if (p == NULL) + goto fail; + + /* Parsing supervisor extension. */ + p = subset_list->parse_sv_or_non_std_ext (p, "s", "supervisor extension"); + + if (p == NULL) + goto fail; + + /* Parsing non-standard supervisor extension. */ + p = subset_list->parse_sv_or_non_std_ext + (p, "sx", "non-standard supervisor extension"); + + if (p == NULL) + goto fail; + + return subset_list; + +fail: + delete subset_list; + return NULL; +} + +/* Return the current arch string. */ + +std::string +riscv_arch_str () +{ + gcc_assert (current_subset_list); + return current_subset_list->to_string (); +} + +/* 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, int *flags, location_t loc) +{ + riscv_subset_list *subset_list; + subset_list = riscv_subset_list::parse (isa, loc); + if (!subset_list) + return; + + if (subset_list->xlen () == 32) + *flags &= ~MASK_64BIT; + else if (subset_list->xlen () == 64) + *flags |= MASK_64BIT; + + *flags &= ~MASK_RVE; + if (subset_list->lookup ("e")) + *flags |= MASK_RVE; + + *flags &= ~MASK_MUL; + if (subset_list->lookup ("m")) + *flags |= MASK_MUL; + + *flags &= ~MASK_ATOMIC; + if (subset_list->lookup ("a")) + *flags |= MASK_ATOMIC; + + *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT); + if (subset_list->lookup ("f")) + *flags |= MASK_HARD_FLOAT; + + if (subset_list->lookup ("d")) + *flags |= MASK_DOUBLE_FLOAT; + + if (current_subset_list) + delete current_subset_list; + + current_subset_list = subset_list; } /* Implement TARGET_HANDLE_OPTION. */ -- cgit v1.1