diff options
author | Jerry Zhao <jerryz123@berkeley.edu> | 2023-07-11 17:48:04 -0700 |
---|---|---|
committer | Jerry Zhao <jerryz123@berkeley.edu> | 2023-07-11 18:53:54 -0700 |
commit | 47ab8926b04a592128b924da0bdbd622213568f8 (patch) | |
tree | a42f957586a9f95bb807164b3e2cf78dd44b9495 /disasm | |
parent | ed5dccb2911f3b2dda731e5fe65e6c03ad0b46fb (diff) | |
download | spike-47ab8926b04a592128b924da0bdbd622213568f8.zip spike-47ab8926b04a592128b924da0bdbd622213568f8.tar.gz spike-47ab8926b04a592128b924da0bdbd622213568f8.tar.bz2 |
Move isa_parser_t to libdisasm out of libriscv
Diffstat (limited to 'disasm')
-rw-r--r-- | disasm/disasm.mk.in | 1 | ||||
-rw-r--r-- | disasm/isa_parser.cc | 396 |
2 files changed, 397 insertions, 0 deletions
diff --git a/disasm/disasm.mk.in b/disasm/disasm.mk.in index 445c430..090911b 100644 --- a/disasm/disasm.mk.in +++ b/disasm/disasm.mk.in @@ -1,5 +1,6 @@ disasm_srcs = \ disasm.cc \ + isa_parser.cc \ regnames.cc \ disasm_install_lib = yes diff --git a/disasm/isa_parser.cc b/disasm/isa_parser.cc new file mode 100644 index 0000000..59472a4 --- /dev/null +++ b/disasm/isa_parser.cc @@ -0,0 +1,396 @@ +#include "isa_parser.h" +#include "extension.h" + +static std::string strtolower(const char* str) +{ + std::string res; + for (const char *r = str; *r; r++) + res += std::tolower(*r); + return res; +} + +static void bad_option_string(const char *option, const char *value, + const char *msg) +{ + fprintf(stderr, "error: bad %s option '%s'. %s\n", option, value, msg); + abort(); +} + +static void bad_isa_string(const char* isa, const char* msg) +{ + bad_option_string("--isa", isa, msg); +} + +static void bad_priv_string(const char* priv) +{ + fprintf(stderr, "error: bad --priv option %s\n", priv); + abort(); +} + +isa_parser_t::isa_parser_t(const char* str, const char *priv) +{ + isa_string = strtolower(str); + const char* all_subsets = "mafdqchpv"; + + if (isa_string.compare(0, 4, "rv32") == 0) + max_xlen = 32; + else if (isa_string.compare(0, 4, "rv64") == 0) + max_xlen = 64; + else + bad_isa_string(str, "ISA strings must begin with RV32 or RV64"); + + switch (isa_string[4]) { + case 'g': + // G = IMAFD_Zicsr_Zifencei, but Spike includes the latter two + // unconditionally, so they need not be explicitly added here. + isa_string = isa_string.substr(0, 4) + "imafd" + isa_string.substr(5); + // Fall through + case 'i': + extension_table['I'] = true; + break; + + case 'e': + extension_table['E'] = true; + break; + + default: + bad_isa_string(str, ("'" + isa_string.substr(0, 4) + "' must be followed by I, E, or G").c_str()); + } + + const char* isa_str = isa_string.c_str(); + auto p = isa_str, subset = all_subsets; + for (p += 5; islower(*p) && !strchr("zsx", *p); ++p) { + while (*subset && (*p != *subset)) + ++subset; + + if (!*subset) { + if (strchr(all_subsets, *p)) + bad_isa_string(str, ("Extension '" + std::string(1, *p) + "' appears too late in ISA string").c_str()); + else + bad_isa_string(str, ("Unsupported extension '" + std::string(1, *p) + "'").c_str()); + } + + switch (*p) { + case 'p': extension_table[EXT_ZBPBO] = true; + extension_table[EXT_ZPN] = true; + extension_table[EXT_ZPSFOPERAND] = true; + extension_table[EXT_ZMMUL] = true; break; + case 'v': // even rv32iv implies double float + case 'q': extension_table['D'] = true; + // Fall through + case 'd': extension_table['F'] = true; + } + extension_table[toupper(*p)] = true; + while (isdigit(*(p + 1))) { + ++p; // skip major version, point, and minor version if presented + if (*(p + 1) == 'p') ++p; + } + p += *(p + 1) == '_'; // underscores may be used to improve readability + } + + while (islower(*p) || (*p == '_')) { + p += *p == '_'; // first underscore is optional + auto end = p; + do ++end; while (*end && *end != '_'); + auto ext_str = std::string(p, end); + if (ext_str == "zfh" || ext_str == "zfhmin") { + if (!extension_table['F']) + bad_isa_string(str, ("'" + ext_str + "' extension requires 'F'").c_str()); + extension_table[EXT_ZFHMIN] = true; + if (ext_str == "zfh") + extension_table[EXT_ZFH] = true; + } else if (ext_str == "zvfh" || ext_str == "zvfhmin") { + if (!extension_table['V']) + bad_isa_string(str, ("'" + ext_str + "' extension requires 'V'").c_str()); + + extension_table[EXT_ZVFHMIN] = true; + + if (ext_str == "zvfh") { + extension_table[EXT_ZVFH] = true; + // Zvfh implies Zfhmin + extension_table[EXT_ZFHMIN] = true; + } + } else if (ext_str == "zicsr") { + // Spike necessarily has Zicsr, because + // Zicsr is implied by the privileged architecture + } else if (ext_str == "zifencei") { + // For compatibility with version 2.0 of the base ISAs, we + // unconditionally include FENCE.I, so Zifencei adds nothing more. + } else if (ext_str == "zihintpause") { + // HINTs encoded in base-ISA instructions are always present. + } else if (ext_str == "zihintntl") { + // HINTs encoded in base-ISA instructions are always present. + } else if (ext_str == "zacas") { + extension_table[EXT_ZACAS] = true; + } else if (ext_str == "zmmul") { + extension_table[EXT_ZMMUL] = true; + } else if (ext_str == "zba") { + extension_table[EXT_ZBA] = true; + } else if (ext_str == "zbb") { + extension_table[EXT_ZBB] = true; + } else if (ext_str == "zbc") { + extension_table[EXT_ZBC] = true; + } else if (ext_str == "zbs") { + extension_table[EXT_ZBS] = true; + } else if (ext_str == "zbkb") { + extension_table[EXT_ZBKB] = true; + } else if (ext_str == "zbkc") { + extension_table[EXT_ZBKC] = true; + } else if (ext_str == "zbkx") { + extension_table[EXT_ZBKX] = true; + } else if (ext_str == "zdinx") { + extension_table[EXT_ZFINX] = true; + extension_table[EXT_ZDINX] = true; + } else if (ext_str == "zfbfmin") { + extension_table[EXT_ZFBFMIN] = true; + } else if (ext_str == "zfinx") { + extension_table[EXT_ZFINX] = true; + } else if (ext_str == "zhinx") { + extension_table[EXT_ZFINX] = true; + extension_table[EXT_ZHINX] = true; + extension_table[EXT_ZHINXMIN] = true; + } else if (ext_str == "zhinxmin") { + extension_table[EXT_ZFINX] = true; + extension_table[EXT_ZHINXMIN] = true; + } else if (ext_str == "zce") { + extension_table[EXT_ZCA] = true; + extension_table[EXT_ZCB] = true; + extension_table[EXT_ZCMT] = true; + extension_table[EXT_ZCMP] = true; + if (extension_table['F'] && max_xlen == 32) + extension_table[EXT_ZCF] = true; + } else if (ext_str == "zca") { + extension_table[EXT_ZCA] = true; + } else if (ext_str == "zcf") { + if (max_xlen != 32) + bad_isa_string(str, "'Zcf' requires RV32"); + extension_table[EXT_ZCF] = true; + } else if (ext_str == "zcb") { + extension_table[EXT_ZCB] = true; + } else if (ext_str == "zcd") { + extension_table[EXT_ZCD] = true; + } else if (ext_str == "zcmp") { + extension_table[EXT_ZCMP] = true; + } else if (ext_str == "zcmt") { + extension_table[EXT_ZCMT] = true; + } else if (ext_str == "zk") { + extension_table[EXT_ZBKB] = true; + extension_table[EXT_ZBKC] = true; + extension_table[EXT_ZBKX] = true; + extension_table[EXT_ZKND] = true; + extension_table[EXT_ZKNE] = true; + extension_table[EXT_ZKNH] = true; + extension_table[EXT_ZKR] = true; + } else if (ext_str == "zkn") { + extension_table[EXT_ZBKB] = true; + extension_table[EXT_ZBKC] = true; + extension_table[EXT_ZBKX] = true; + extension_table[EXT_ZKND] = true; + extension_table[EXT_ZKNE] = true; + extension_table[EXT_ZKNH] = true; + } else if (ext_str == "zknd") { + extension_table[EXT_ZKND] = true; + } else if (ext_str == "zkne") { + extension_table[EXT_ZKNE] = true; + } else if (ext_str == "zknh") { + extension_table[EXT_ZKNH] = true; + } else if (ext_str == "zks") { + extension_table[EXT_ZBKB] = true; + extension_table[EXT_ZBKC] = true; + extension_table[EXT_ZBKX] = true; + extension_table[EXT_ZKSED] = true; + extension_table[EXT_ZKSH] = true; + } else if (ext_str == "zksed") { + extension_table[EXT_ZKSED] = true; + } else if (ext_str == "zksh") { + extension_table[EXT_ZKSH] = true; + } else if (ext_str == "zkr") { + extension_table[EXT_ZKR] = true; + } else if (ext_str == "zkt") { + } else if (ext_str == "smepmp") { + extension_table[EXT_SMEPMP] = true; + } else if (ext_str == "smstateen") { + extension_table[EXT_SMSTATEEN] = true; + } else if (ext_str == "smrnmi") { + extension_table[EXT_SMRNMI] = true; + } else if (ext_str == "sscofpmf") { + extension_table[EXT_SSCOFPMF] = true; + } else if (ext_str == "svadu") { + extension_table[EXT_SVADU] = true; + } else if (ext_str == "svnapot") { + extension_table[EXT_SVNAPOT] = true; + } else if (ext_str == "svpbmt") { + extension_table[EXT_SVPBMT] = true; + } else if (ext_str == "svinval") { + extension_table[EXT_SVINVAL] = true; + } else if (ext_str == "zfa") { + extension_table[EXT_ZFA] = true; + } else if (ext_str == "zicbom") { + extension_table[EXT_ZICBOM] = true; + } else if (ext_str == "zicboz") { + extension_table[EXT_ZICBOZ] = true; + } else if (ext_str == "zicbop") { + } else if (ext_str == "zicntr") { + extension_table[EXT_ZICNTR] = true; + } else if (ext_str == "zicond") { + extension_table[EXT_ZICOND] = true; + } else if (ext_str == "zihpm") { + extension_table[EXT_ZIHPM] = true; + } else if (ext_str == "zvbb") { + extension_table[EXT_ZVBB] = true; + } else if (ext_str == "zvbc") { + extension_table[EXT_ZVBC] = true; + } else if (ext_str == "zvfbfmin") { + extension_table[EXT_ZVFBFMIN] = true; + } else if (ext_str == "zvfbfwma") { + extension_table[EXT_ZVFBFWMA] = true; + } else if (ext_str == "zvkg") { + extension_table[EXT_ZVKG] = true; + } else if (ext_str == "zvkn") { + extension_table[EXT_ZVBB] = true; + extension_table[EXT_ZVKNED] = true; + extension_table[EXT_ZVKNHB] = true; + } else if (ext_str == "zvknc") { + extension_table[EXT_ZVBB] = true; + extension_table[EXT_ZVBC] = true; + extension_table[EXT_ZVKNED] = true; + extension_table[EXT_ZVKNHB] = true; + } else if (ext_str == "zvkng") { + extension_table[EXT_ZVBB] = true; + extension_table[EXT_ZVKG] = true; + extension_table[EXT_ZVKNED] = true; + extension_table[EXT_ZVKNHB] = true; + } else if (ext_str == "zvkned") { + extension_table[EXT_ZVKNED] = true; + } else if (ext_str == "zvknha") { + extension_table[EXT_ZVKNHA] = true; + } else if (ext_str == "zvknhb") { + extension_table[EXT_ZVKNHB] = true; + } else if (ext_str == "zvks") { + extension_table[EXT_ZVBB] = true; + extension_table[EXT_ZVKSED] = true; + extension_table[EXT_ZVKSH] = true; + } else if (ext_str == "zvksc") { + extension_table[EXT_ZVBB] = true; + extension_table[EXT_ZVBC] = true; + extension_table[EXT_ZVKSED] = true; + extension_table[EXT_ZVKSH] = true; + } else if (ext_str == "zvksg") { + extension_table[EXT_ZVBB] = true; + extension_table[EXT_ZVKG] = true; + extension_table[EXT_ZVKSED] = true; + extension_table[EXT_ZVKSH] = true; + } else if (ext_str == "zvksed") { + extension_table[EXT_ZVKSED] = true; + } else if (ext_str == "zvksh") { + extension_table[EXT_ZVKSH] = true; + } else if (ext_str == "zvkt") { + } else if (ext_str == "sstc") { + extension_table[EXT_SSTC] = true; + } else if (ext_str[0] == 'x') { + extension_table['X'] = true; + if (ext_str.size() == 1) { + bad_isa_string(str, "single 'X' is not a proper name"); + } else if (ext_str != "xdummy") { + extension_t* x = find_extension(ext_str.substr(1).c_str())(); + if (!extensions.insert(std::make_pair(x->name(), x)).second) { + fprintf(stderr, "extensions must have unique names (got two named \"%s\"!)\n", x->name()); + abort(); + } + } + } else { + bad_isa_string(str, ("unsupported extension: " + ext_str).c_str()); + } + p = end; + } + if (*p) { + bad_isa_string(str, ("can't parse: " + std::string(p)).c_str()); + } + + if (extension_table[EXT_ZFBFMIN] && !extension_table['F']) { + bad_isa_string(str, "'Zfbfmin' extension requires 'F' extension"); + } + + if ((extension_table[EXT_ZVFBFMIN] || extension_table[EXT_ZVFBFWMA]) && !extension_table['V']) { + bad_isa_string(str, "'Zvfbfmin/Zvfbfwma' extension requires 'V' extension"); + } + + if (extension_table[EXT_ZFBFMIN] || extension_table[EXT_ZVFBFMIN] || extension_table[EXT_ZFHMIN]) { + extension_table[EXT_INTERNAL_ZFH_MOVE] = true; + } + + if (extension_table['C']) { + extension_table[EXT_ZCA] = true; + if (extension_table['F'] && max_xlen == 32) + extension_table[EXT_ZCF] = true; + if (extension_table['D']) + extension_table[EXT_ZCD] = true; + } + + if (extension_table[EXT_ZFINX] && extension_table['F']) { + bad_isa_string(str, ("Zfinx/Zdinx/Zhinx{min} extensions conflict with 'F/D/Q/Zfh{min}' extensions")); + } + + if (extension_table[EXT_ZCF] && !extension_table['F']) { + bad_isa_string(str, "'Zcf' extension requires 'F' extension"); + } + + if (extension_table[EXT_ZCD] && !extension_table['D']) { + bad_isa_string(str, "'Zcd' extension requires 'D' extension"); + } + + if ((extension_table[EXT_ZCMP] || extension_table[EXT_ZCMT]) && extension_table[EXT_ZCD]) { + bad_isa_string(str, "Zcmp' and 'Zcmt' extensions are incompatible with 'Zcd' extension"); + } + + if ((extension_table[EXT_ZCF] || extension_table[EXT_ZCD] || extension_table[EXT_ZCB] || + extension_table[EXT_ZCMP] || extension_table[EXT_ZCMT]) && !extension_table[EXT_ZCA]) { + bad_isa_string(str, "'Zcf/Zcd/Zcb/Zcmp/Zcmt' extensions require 'Zca' extension"); + } + + if (extension_table[EXT_ZACAS] && !extension_table['A']) { + bad_isa_string(str, "'Zacas' extension requires 'A' extension"); + } + + // Zpn conflicts with Zvknha/Zvknhb in both rv32 and rv64 + if (extension_table[EXT_ZPN] && (extension_table[EXT_ZVKNHA] || extension_table[EXT_ZVKNHB])) { + bad_isa_string(str, "'Zvkna' and 'Zvknhb' extensions are incompatible with 'Zpn' extension"); + } + // In rv64 only, Zpn (rv64_zpn) conflicts with Zvkg/Zvkned/Zvksh + if (max_xlen == 64 && extension_table[EXT_ZPN] && + (extension_table[EXT_ZVKG] || extension_table[EXT_ZVKNED] || extension_table[EXT_ZVKSH])) { + bad_isa_string(str, "'Zvkg', 'Zvkned', and 'Zvksh' extensions are incompatible with 'Zpn' extension in rv64"); + } +#ifdef WORDS_BIGENDIAN + // Access to the vector registers as element groups is unimplemented on big-endian setups. + if (extension_table[EXT_ZVKG] || extension_table[EXT_ZVKNHA] || extension_table[EXT_ZVKNHB] || + extension_table[EXT_ZVKSED] || extension_table[EXT_ZVKSH]) { + bad_isa_string(str, + "'Zvkg', 'Zvkned', 'Zvknha', 'Zvknhb', 'Zvksed', and 'Zvksh' " + "extensions are incompatible with WORDS_BIGENDIAN setups."); + } +#endif + std::string lowercase = strtolower(priv); + bool user = false, supervisor = false; + + if (lowercase == "m") + ; + else if (lowercase == "mu") + user = true; + else if (lowercase == "msu") + user = supervisor = true; + else + bad_priv_string(priv); + + extension_table['U'] = user; + extension_table['S'] = supervisor; + + if (extension_table['H'] && !supervisor) + bad_isa_string(str, "'H' extension requires S mode"); + + max_isa = max_xlen == 32 ? reg_t(1) << 30 : reg_t(2) << 62; + for (unsigned char ch = 'A'; ch <= 'Z'; ch++) { + if (extension_table[ch]) + max_isa |= 1UL << (ch - 'A'); + } +} |