aboutsummaryrefslogtreecommitdiff
path: root/disasm
diff options
context:
space:
mode:
authorJerry Zhao <jerryz123@berkeley.edu>2023-07-11 17:48:04 -0700
committerJerry Zhao <jerryz123@berkeley.edu>2023-07-11 18:53:54 -0700
commit47ab8926b04a592128b924da0bdbd622213568f8 (patch)
treea42f957586a9f95bb807164b3e2cf78dd44b9495 /disasm
parented5dccb2911f3b2dda731e5fe65e6c03ad0b46fb (diff)
downloadspike-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.in1
-rw-r--r--disasm/isa_parser.cc396
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');
+ }
+}