add description of modes, copied from specs
[openpower-isa.git] / src / openpower / sv / sv_binutils.py
index 7ba427aa4b32f5b2032cbc8b866d180453f0b6c7..e11c9f4738f7f590f3b6b0eabd94316dd09e75c7 100644 (file)
@@ -1,12 +1,7 @@
 import abc as _abc
 import argparse as _argparse
-import codecs as _codecs
-import csv as _csv
 import dataclasses as _dataclasses
 import enum as _enum
-import pathlib as _pathlib
-import re as _re
-import sys as _sys
 
 from openpower.decoder.power_enums import (
     In1Sel as _In1Sel,
@@ -18,14 +13,16 @@ from openpower.decoder.power_enums import (
     SVPtype as _SVPtype,
     SVEtype as _SVEtype,
     SVEXTRA as _SVEXTRA,
+    RC as _RC,
 )
+from openpower.consts import SVP64MODE as _SVP64MODE
 from openpower.decoder.power_svp64 import SVP64RM as _SVP64RM
 
 
 DISCLAIMER = (
     "/*",
     " * this file is auto-generated, do not edit",
-    " * http://libre-soc.org/openpower/sv_binutiks.py",
+    " * https://git.libre-soc.org/?p=openpower-isa.git;a=blob;f=src/openpower/sv/sv_binutils.py",
     " * part of Libre-SOC, sponsored by NLnet",
     " */",
 )
@@ -35,7 +32,7 @@ def indent(strings):
     return map(lambda string: ("    " + string), strings)
 
 
-class Field:
+class CType:
     @classmethod
     @_abc.abstractmethod
     def c_decl(self, name):
@@ -51,7 +48,22 @@ class Field:
         pass
 
 
-class Enum(Field, _enum.Enum):
+class EnumMeta(_enum.EnumMeta):
+    def __call__(metacls, *args, **kwargs):
+        if len(args) > 1:
+            names = args[1]
+        else:
+            names = kwargs.pop("names")
+
+        if isinstance(names, type) and issubclass(names, _enum.Enum):
+            names = dict(names.__members__)
+        if isinstance(names, dict):
+            names = tuple(names.items())
+
+        return super().__call__(*args, names=names, **kwargs)
+
+
+class Enum(CType, _enum.Enum, metaclass=EnumMeta):
     @classmethod
     def c_decl(cls):
         c_tag = f"svp64_{cls.__name__.lower()}"
@@ -67,31 +79,116 @@ class Enum(Field, _enum.Enum):
     @classmethod
     def c_var(cls, name):
         c_tag = f"svp64_{cls.__name__.lower()}"
-        yield f"enum {c_tag} {name};"
+        yield f"enum {c_tag} {name}"
 
 
-# Python forbids inheriting from enum unless it's empty.
-In1Sel = Enum("In1Sel", {item.name:item.value for item in _In1Sel})
-In2Sel = Enum("In2Sel", {item.name:item.value for item in _In2Sel})
-In3Sel = Enum("In3Sel", {item.name:item.value for item in _In3Sel})
-OutSel = Enum("OutSel", {item.name:item.value for item in _OutSel})
-CRInSel = Enum("CRInSel", {item.name:item.value for item in _CRInSel})
-CROutSel = Enum("CROutSel", {item.name:item.value for item in _CROutSel})
-SVPType = Enum("SVPType", {item.name:item.value for item in _SVPtype})
-SVEType = Enum("SVEType", {item.name:item.value for item in _SVEtype})
-SVEXTRA = Enum("SVEXTRA", {item.name:item.value for item in _SVEXTRA})
+In1Sel = Enum("In1Sel", names=_In1Sel.__members__.items())
+In2Sel = Enum("In2Sel", names=_In2Sel.__members__.items())
+In3Sel = Enum("In3Sel", names=_In3Sel.__members__.items())
+OutSel = Enum("OutSel", names=_OutSel.__members__.items())
+CRInSel = Enum("CRInSel", names=_CRInSel.__members__.items())
+CROutSel = Enum("CROutSel", names=_CROutSel.__members__.items())
+SVPType = Enum("SVPType", names=_SVPtype.__members__.items())
+SVEType = Enum("SVEType", names=_SVEtype.__members__.items())
+SVEXTRA = Enum("SVEXTRA", names=_SVEXTRA.__members__.items())
 
 
-class Opcode(Field, str):
+class Constant(CType, _enum.Enum):
+    @classmethod
+    def c_decl(cls):
+        c_tag = f"svp64_{cls.__name__.lower()}"
+        yield f"/* {c_tag.upper()} constants */"
+        for (key, item) in cls.__members__.items():
+            key = f"{c_tag.upper()}_{key.upper()}"
+            value = f"0x{item.value:08x}U"
+            yield f"#define {key} {value}"
+
     def c_value(self, prefix="", suffix=""):
-        yield f"{prefix}\"{self}\"{suffix}"
+        c_tag = f"svp64_{self.__class__.__name__.lower()}"
+        yield f"{prefix}{c_tag.upper()}_{self.name.upper()}{suffix}"
+
+
+Mode = Constant("Mode", names=_SVP64MODE.__members__.items())
+
+
+class Opcode(CType):
+    def __init__(self, value, mask, bits):
+        self.__value = value
+        self.__mask = mask
+        self.__bits = bits
+
+        return super().__init__()
+
+    @property
+    def value(self):
+        return self.__value
+
+    @property
+    def mask(self):
+        return self.__mask
+
+    @property
+    def bits(self):
+        return self.__bits
+
+    def __repr__(self):
+        fmt = f"{{value:0{self.bits}b}}:{{mask:0{self.bits}b}}"
+        return fmt.format(value=self.value, mask=self.mask)
+
+    def __lt__(self, other):
+        if not isinstance(other, self.__class__):
+            return NotImplemented
+
+        return self.__value < other.__value
+
+    @classmethod
+    def c_decl(cls):
+        yield f"struct svp64_opcode {{"
+        yield from indent([
+            "uint32_t value;",
+            "uint32_t mask;",
+        ])
+        yield f"}};"
+
+    def c_value(self, prefix="", suffix=""):
+        yield f"{prefix}{{"
+        yield from indent([
+            f".value = UINT32_C(0x{self.value:08X}),",
+            f".mask = UINT32_C(0x{self.mask:08X}),",
+        ])
+        yield f"}}{suffix}"
 
     @classmethod
     def c_var(cls, name):
-        yield f"const char *{name};"
+        yield f"struct svp64_opcode {name}"
+
+
+class IntegerOpcode(Opcode):
+    def __init__(self, integer):
+        value = int(integer, 0)
+        bits = max(1, value.bit_length())
+        mask = int(("1" * bits), 2)
+
+        return super().__init__(value=value, mask=mask, bits=bits)
 
 
-class Name(Field, str):
+class PatternOpcode(Opcode):
+    def __init__(self, pattern):
+        value = 0
+        mask = 0
+        bits = len(pattern)
+        for bit in pattern:
+            value |= (bit == "1")
+            mask |= (bit != "-")
+            value <<= 1
+            mask <<= 1
+        value >>= 1
+        mask >>= 1
+
+        return super().__init__(value=value, mask=mask, bits=bits)
+
+
+class Name(CType, str):
     def __repr__(self):
         escaped = self.replace("\"", "\\\"")
         return f"\"{escaped}\""
@@ -101,13 +198,11 @@ class Name(Field, str):
 
     @classmethod
     def c_var(cls, name):
-        yield f"const char *{name};"
+        yield f"const char *{name}"
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class Entry:
-    name: Name
-    opcode: Opcode
+class Record(CType):
     in1: In1Sel
     in2: In2Sel
     in3: In3Sel
@@ -125,15 +220,48 @@ class Entry:
     sv_cr_in: SVEXTRA
     sv_cr_out: SVEXTRA
 
+    @classmethod
+    def c_decl(cls):
+        bits_all = 0
+        yield f"struct svp64_record {{"
+        for field in _dataclasses.fields(cls):
+            bits = len(field.type).bit_length()
+            yield from indent([f"uint64_t {field.name} : {bits};"])
+            bits_all += bits
+        bits_rsvd = (64 - (bits_all % 64))
+        if bits_rsvd:
+            yield from indent([f"uint64_t : {bits_rsvd};"])
+        yield f"}};"
+
+    def c_value(self, prefix="", suffix=""):
+        yield f"{prefix}{{"
+        for field in _dataclasses.fields(self):
+            name = field.name
+            attr = getattr(self, name)
+            yield from indent(attr.c_value(prefix=f".{name} = ", suffix=","))
+        yield f"}}{suffix}"
+
+    @classmethod
+    def c_var(cls, name):
+        yield f"struct svp64_record {name}"
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class Entry(CType):
+    name: Name
+    record: Record
+
+    def __lt__(self, other):
+        if not isinstance(other, self.__class__):
+            return NotImplemented
+
+        return self.name < other.name
+
     @classmethod
     def c_decl(cls):
         yield f"struct svp64_entry {{"
         for field in _dataclasses.fields(cls):
-            if issubclass(field.type, Enum):
-                bits = len(field.type).bit_length()
-                yield from indent([f"uint64_t {field.name} : {bits};"])
-            else:
-                yield from indent(field.type.c_var(name=field.name))
+            yield from indent(field.type.c_var(name=f"{field.name};"))
         yield f"}};"
 
     def c_value(self, prefix="", suffix=""):
@@ -146,28 +274,28 @@ class Entry:
 
     @classmethod
     def c_var(cls, name):
-        yield f"struct svp64_entry {name};"
+        yield f"struct svp64_entry {name}"
 
 
 class Codegen(_enum.Enum):
-    PPC_OPC_SVP64_H = _enum.auto()
-    PPC_OPC_SVP64_C = _enum.auto()
+    PPC_SVP64_H = _enum.auto()
+    PPC_SVP64_OPC_C = _enum.auto()
 
     @classmethod
     def _missing_(cls, value):
         return {
-            "ppc-opc-svp64.h": Codegen.PPC_OPC_SVP64_H,
-            "ppc-opc-svp64.c": Codegen.PPC_OPC_SVP64_C,
-        }[value]
+            "ppc-svp64.h": Codegen.PPC_SVP64_H,
+            "ppc-svp64-opc.c": Codegen.PPC_SVP64_OPC_C,
+        }.get(value)
 
     def __str__(self):
         return {
-            Codegen.PPC_OPC_SVP64_H: "ppc-opc-svp64.h",
-            Codegen.PPC_OPC_SVP64_C: "ppc-opc-svp64.c",
+            Codegen.PPC_SVP64_H: "ppc-svp64.h",
+            Codegen.PPC_SVP64_OPC_C: "ppc-svp64-opc.c",
         }[self]
 
     def generate(self, entries):
-        def ppc_opc_svp64_h(entries):
+        def ppc_svp64_h(entries):
             yield from DISCLAIMER
             yield ""
 
@@ -175,125 +303,139 @@ class Codegen(_enum.Enum):
             yield f"#define {self.name}"
             yield ""
 
+            yield "#include <stdint.h>"
+            yield ""
+
+            yield "#ifdef __cplusplus"
+            yield "extern \"C\" {"
+            yield "#endif"
+            yield ""
+
             enums = (
-                SVPType, SVEType,
                 In1Sel, In2Sel, In3Sel, OutSel,
                 CRInSel, CROutSel,
-                SVEXTRA,
+                SVPType, SVEType, SVEXTRA,
+                Mode,
             )
             for enum in enums:
                 yield from enum.c_decl()
                 yield ""
 
+            yield from Record.c_decl()
+            yield ""
+
             yield from Entry.c_decl()
             yield ""
 
+            yield "extern const struct svp64_entry svp64_entries[];"
+            yield "extern const unsigned int svp64_num_entries;"
+            yield ""
+
+            yield f"#define SVP64_NAME_MAX {max(map(lambda entry: len(entry.name), entries))}"
+            yield ""
+
+            yield "#ifdef __cplusplus"
+            yield "}"
+            yield "#endif"
+            yield ""
+
             yield f"#endif /* {self.name} */"
             yield ""
 
-        def ppc_opc_svp64_c(entries):
-            yield from ()
+        def ppc_svp64_opc_c(entries):
+            yield from DISCLAIMER
+            yield ""
+
+            yield "#include \"opcode/ppc-svp64.h\""
+            yield ""
+
+            yield "const struct svp64_entry svp64_entries[] = {"
+            for (index, entry) in enumerate(entries):
+                yield from indent(entry.c_value(prefix=f"[{index}] = ", suffix=","))
+            yield f"}};"
+            yield ""
+
+            yield "const unsigned int svp64_num_entries = \\"
+            yield "    sizeof (svp64_entries) / sizeof (svp64_entries[0]);"
+            yield ""
 
         return {
-            Codegen.PPC_OPC_SVP64_H: ppc_opc_svp64_h,
-            Codegen.PPC_OPC_SVP64_C: ppc_opc_svp64_c,
+            Codegen.PPC_SVP64_H: ppc_svp64_h,
+            Codegen.PPC_SVP64_OPC_C: ppc_svp64_opc_c,
         }[self](entries)
 
 
-def regex_enum(enum):
-    assert issubclass(enum, _enum.Enum)
-    return "|".join(item.name for item in enum)
-
-
-PATTERN_VHDL_BINARY = r"(?:2#[01]+#)"
-PATTERN_DECIMAL = r"(?:[0-9]+)"
-PATTERN_PARTIAL_BINARY = r"(?:[01-]+)"
-
-# Examples of the entries to be caught by the pattern below:
-# 2 => (P2, EXTRA3, RA_OR_ZERO, NONE, NONE, RT, NONE, NONE, NONE, Idx1, NONE, NONE, Idx0, NONE, NONE, NONE), -- lwz
-# -----10110 => (P2, EXTRA3, NONE, FRB, NONE, FRT, NONE, NONE, CR1, NONE, Idx1, NONE, Idx0, NONE, NONE, Idx0), -- fsqrts
-# 2#0000000000# => (P2, EXTRA3, NONE, NONE, NONE, NONE, NONE, BFA, BF, NONE, NONE, NONE, NONE, NONE, Idx1, Idx0), -- mcrf
-PATTERN = "".join((
-    r"^\s*",
-    rf"(?P<opcode>{PATTERN_VHDL_BINARY}|{PATTERN_DECIMAL}|{PATTERN_PARTIAL_BINARY})",
-    r"\s?=>\s?",
-    r"\(",
-    r",\s".join((
-        rf"(?P<ptype>{regex_enum(_SVPtype)})",
-        rf"(?P<etype>{regex_enum(_SVEtype)})",
-        rf"(?P<in1>{regex_enum(_In1Sel)})",
-        rf"(?P<in2>{regex_enum(_In2Sel)})",
-        rf"(?P<in3>{regex_enum(_In3Sel)})",
-        rf"(?P<out>{regex_enum(_OutSel)})",
-        rf"(?P<out2>{regex_enum(_OutSel)})",
-        rf"(?P<cr_in>{regex_enum(_CRInSel)})",
-        rf"(?P<cr_out>{regex_enum(_CROutSel)})",
-        rf"(?P<sv_in1>{regex_enum(_SVEXTRA)})",
-        rf"(?P<sv_in2>{regex_enum(_SVEXTRA)})",
-        rf"(?P<sv_in3>{regex_enum(_SVEXTRA)})",
-        rf"(?P<sv_out>{regex_enum(_SVEXTRA)})",
-        rf"(?P<sv_out2>{regex_enum(_SVEXTRA)})",
-        rf"(?P<sv_cr_in>{regex_enum(_SVEXTRA)})",
-        rf"(?P<sv_cr_out>{regex_enum(_SVEXTRA)})",
-    )),
-    r"\)",
-    r",",
-    r"\s?--\s?",
-    r"(?P<name>[A-Za-z0-9_\./]+)",
-    r"\s*$",
-))
-REGEX = _re.compile(PATTERN)
+ISA = _SVP64RM()
+FIELDS = {field.name:field.type for field in _dataclasses.fields(Record)}
+FIELDS.update({field.name:field.type for field in _dataclasses.fields(Entry)})
 
+def parse(path, opcode_cls):
+    visited = set()
 
-ISA = _SVP64RM()
-FIELDS = {field.name:field for field in _dataclasses.fields(Entry)}
-def parse(path):
-    for entry in ISA.get_svp64_csv(path):
-        # skip instructions that are not suitable
-        name = entry["name"] = entry.pop("comment")
+    def name_filter(name):
         if name.startswith("l") and name.endswith("br"):
-            continue
+            return False
         if name in {"mcrxr", "mcrxrx", "darn"}:
-            continue
+            return False
         if name in {"bctar", "bcctr"}:
-            continue
+            return False
         if "rfid" in name:
-            continue
+            return False
         if name in {"setvl"}:
-            continue
+            return False
+        if name in visited:
+            return False
 
-        entry = {key.lower().replace(" ", "_"):value for (key, value) in entry.items()}
-        for (key, value) in tuple(entry.items()):
-            key = key.lower().replace(" ", "_")
-            if key not in FIELDS:
-                entry.pop(key)
-            else:
-                field = FIELDS[key]
-                if issubclass(field.type, _enum.Enum):
-                    value = {item.name:item for item in field.type}[value]
-                else:
-                    value = field.type(value)
-                entry[key] = value
+        visited.add(name)
 
-        yield Entry(**entry)
+        return True
+
+    def item_mapper(item):
+        (key, value) = item
+        key = key.lower().replace(" ", "_")
+        cls = FIELDS.get(key, object)
+        if not isinstance(value, cls):
+            if issubclass(cls, _enum.Enum):
+                value = {item.name:item for item in cls}[value]
+            else:
+                value = cls(value)
+        return (key, value)
+
+    def item_filter(item):
+        (key, _) = item
+        return (key in FIELDS)
+
+    for record in ISA.get_svp64_csv(path):
+        opcode = opcode_cls(record.pop("opcode"))
+        names = record.pop("comment").split("=")[-1].split("/")
+        names = set(filter(name_filter, names))
+        if names:
+            rc = _RC[record["rc"] if record["rc"] else "NONE"]
+            if rc is _RC.RC:
+                names.update({f"{name}." for name in names})
+            record = dict(filter(item_filter, map(item_mapper, record.items())))
+            for name in map(Name, names):
+                yield Entry(name=name, record=Record(**record))
 
 
 def main(codegen):
     entries = []
-    paths = (
-        "minor_19.csv",
-        "minor_30.csv",
-        "minor_31.csv",
-        "minor_58.csv",
-        "minor_62.csv",
-        "minor_22.csv",
-        "minor_5.csv",
-        "minor_63.csv",
-        "minor_59.csv",
-        "major.csv",
-    )
-    for path in paths:
-        entries.extend(parse(path))
+    table = {
+        "minor_19.csv": IntegerOpcode,
+        "minor_30.csv": IntegerOpcode,
+        "minor_31.csv": IntegerOpcode,
+        "minor_58.csv": IntegerOpcode,
+        "minor_62.csv": IntegerOpcode,
+        "minor_22.csv": IntegerOpcode,
+        "minor_5.csv": PatternOpcode,
+        "minor_63.csv": PatternOpcode,
+        "minor_59.csv": PatternOpcode,
+        "major.csv": IntegerOpcode,
+        "extra.csv": PatternOpcode,
+    }
+    for (path, opcode_cls) in table.items():
+        entries.extend(parse(path, opcode_cls))
+    entries = sorted(frozenset(entries))
 
     for line in codegen.generate(entries):
         print(line)