sv_binutils: simplify array syntax
[openpower-isa.git] / src / openpower / sv / sv_binutils.py
index 6846076346d3bafac8202cfd378a4ec75d3f30e8..1c0e4106f202fa85f2ef71379c98fda0ac63c42d 100644 (file)
@@ -1,8 +1,8 @@
 import abc as _abc
 import argparse as _argparse
+import builtins as _builtins
 import dataclasses as _dataclasses
 import enum as _enum
-import re as _re
 
 from openpower.decoder.power_enums import (
     In1Sel as _In1Sel,
@@ -14,7 +14,9 @@ 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
 
 
@@ -31,128 +33,177 @@ def indent(strings):
     return map(lambda string: ("    " + string), strings)
 
 
-class CType:
-    @classmethod
+class CTypeMeta(type):
+    def __new__(metacls, name, bases, attrs, typedef=None):
+        cls = super().__new__(metacls, name, bases, attrs)
+        cls.__typedef = typedef
+
+        return cls
+
+    def __getitem__(cls, size):
+        name = f"{cls.__name__}[{'' if size is Ellipsis else size}]"
+        return type(name, (Array,), {}, type=cls, size=size)
+
+    @property
+    def c_typedef(cls):
+        return cls.__typedef
+
     @_abc.abstractmethod
-    def c_decl(self, name):
-        pass
+    def c_decl(cls):
+        yield from ()
+
+    def c_var(cls, name, prefix="", suffix=""):
+        yield f"{prefix}{cls.c_typedef} {name}{suffix}"
+
+
+class ArrayMeta(CTypeMeta):
+    def __new__(metacls, name, bases, attrs, type, size):
+        cls = super().__new__(metacls, name, bases, attrs)
+        cls.__type = type
+        cls.__ellipsis = (size is Ellipsis)
+        cls.__size = 0 if cls.__ellipsis else size
+
+        return cls
+
+    def __len__(cls):
+        return cls.__size
 
+    @property
+    def c_type(cls):
+        return cls.__type
+
+    def c_decl(cls):
+        size = "" if cls.__ellipsis else f"{cls.__size}"
+        yield f"{cls.c_type.c_typedef}[{size}]"
+
+    def c_var(cls, name, prefix="", suffix=""):
+        size = "" if cls.__ellipsis else f"{cls.__size}"
+        yield f"{prefix}{cls.c_type.c_typedef} {name}[{size}]{suffix}"
+
+
+class CType(metaclass=CTypeMeta):
     @_abc.abstractmethod
     def c_value(self, prefix="", suffix=""):
-        pass
+        yield from ()
 
-    @classmethod
-    @_abc.abstractmethod
-    def c_var(self, name):
-        pass
+
+class Array(CType, tuple, metaclass=ArrayMeta, type=CType, size=0):
+    def c_value(self, prefix="", suffix=""):
+        yield f"{prefix}{{"
+        for (index, item) in enumerate(self):
+            yield from indent(item.c_value(prefix=f"[{index}] = ", suffix=","))
+        yield f"}}{suffix}"
+
+
+class EnumMeta(_enum.EnumMeta, CTypeMeta):
+    def __call__(metacls, name, entries, tag=None, **kwargs):
+        if isinstance(entries, type) and issubclass(entries, _enum.Enum):
+            entries = dict(entries.__members__)
+        if isinstance(entries, dict):
+            entries = tuple(entries.items())
+        if tag is None:
+            tag = f"svp64_{name.lower()}"
+
+        cls = super().__call__(value=name, names=entries, **kwargs)
+        cls.__tag = tag
+        return cls
+
+    @property
+    def c_typedef(cls):
+        return f"enum {cls.c_tag}"
+
+    @property
+    def c_tag(cls):
+        return cls.__tag
+
+    def c_var(cls, name, prefix="", suffix=""):
+        yield f"{prefix}{cls.c_typedef} {name}{suffix}"
 
 
-class Enum(CType, _enum.Enum):
+class Enum(CType, _enum.Enum, metaclass=EnumMeta):
     @classmethod
     def c_decl(cls):
-        c_tag = f"svp64_{cls.__name__.lower()}"
-        yield f"enum {c_tag} {{"
+        yield f"{cls.c_typedef} {{"
         for item in cls:
             yield from indent(item.c_value(suffix=","))
         yield f"}};"
 
     def c_value(self, prefix="", suffix=""):
-        c_tag = f"svp64_{self.__class__.__name__.lower()}"
-        yield f"{prefix}{c_tag.upper()}_{self.name.upper()}{suffix}"
+        yield f"{prefix}{self.__class__.c_tag.upper()}_{self.name.upper()}{suffix}"
 
-    @classmethod
-    def c_var(cls, name):
-        c_tag = f"svp64_{cls.__name__.lower()}"
-        yield f"enum {c_tag} {name}"
 
+In1Sel = Enum("In1Sel", _In1Sel)
+In2Sel = Enum("In2Sel", _In2Sel)
+In3Sel = Enum("In3Sel", _In3Sel)
+OutSel = Enum("OutSel", _OutSel)
+CRInSel = Enum("CRInSel", _CRInSel)
+CROutSel = Enum("CROutSel", _CROutSel)
+SVPType = Enum("SVPType", _SVPtype)
+SVEType = Enum("SVEType", _SVEtype)
+SVEXTRA = Enum("SVEXTRA", _SVEXTRA)
 
-# 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})
 
+class Constant(CType, _enum.Enum, metaclass=EnumMeta):
+    @classmethod
+    def c_decl(cls):
+        yield f"/* {cls.c_tag.upper()} constants */"
+        for (key, item) in cls.__members__.items():
+            key = f"{cls.c_tag.upper()}_{key.upper()}"
+            value = f"0x{item.value:08x}U"
+            yield f"#define {key} {value}"
 
-class Opcode(CType):
-    def __init__(self, value, mask, bits):
-        self.__value = value
-        self.__mask = mask
-        self.__bits = bits
+    def c_value(self, prefix="", suffix=""):
+        yield f"{prefix}{self.__class__.c_tag.upper()}_{self.name.upper()}{suffix}"
 
-        return super().__init__()
 
-    @property
-    def value(self):
-        return self.__value
+Mode = Constant("Mode", _SVP64MODE)
 
-    @property
-    def mask(self):
-        return self.__mask
 
-    @property
-    def bits(self):
-        return self.__bits
+class StructMeta(CTypeMeta):
+    def __new__(metacls, name, bases, attrs, tag=None, **kwargs):
+        if tag is None:
+            tag = f"svp64_{name.lower()}"
+        if "typedef" not in kwargs:
+            kwargs["typedef"] = f"struct {tag}"
 
-    def __repr__(self):
-        fmt = f"{{value:0{self.bits}b}}:{{mask:0{self.bits}b}}"
-        return fmt.format(value=self.value, mask=self.mask)
+        cls = super().__new__(metacls, name, bases, attrs, **kwargs)
+        cls.__tag = tag
 
-    def __lt__(self, other):
-        if not isinstance(other, self.__class__):
-            return NotImplemented
+        return cls
 
-        return self.__value < other.__value
+    @property
+    def c_tag(cls):
+        return cls.__tag
 
-    @classmethod
     def c_decl(cls):
-        yield f"struct svp64_opcode {{"
-        yield from indent([
-            "uint32_t value;",
-            "uint32_t mask;",
-        ])
+        yield f"{cls.c_typedef} {{"
+        for field in _dataclasses.fields(cls):
+            yield from indent(field.type.c_var(name=f"{field.name}", suffix=";"))
         yield f"}};"
 
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class Struct(CType, metaclass=StructMeta):
     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}),",
-        ])
+        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_opcode {name}"
-
 
-class IntegerOpcode(Opcode):
-    def __init__(self, integer):
-        value = int(integer, 0)
-        bits = max(1, value.bit_length())
-        mask = int(("1" * bits), 2)
+class Integer(CType, int):
+    def c_value(self, prefix="", suffix=""):
+        yield f"{prefix}{self}{suffix}"
 
-        return super().__init__(value=value, mask=mask, bits=bits)
 
+class Byte(Integer, typedef="uint8_t"):
+    pass
 
-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 Size(Integer, typedef="size_t"):
+    pass
 
 
 class Name(CType, str):
@@ -164,12 +215,12 @@ class Name(CType, str):
         yield f"{prefix}{self!r}{suffix}"
 
     @classmethod
-    def c_var(cls, name):
-        yield f"const char *{name}"
+    def c_var(cls, name, prefix="", suffix=""):
+        yield f"{prefix}const char *{name}{suffix}"
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class Record(CType):
+class Record(Struct):
     in1: In1Sel
     in2: In2Sel
     in3: In3Sel
@@ -200,21 +251,9 @@ class Record(CType):
             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):
+class Entry(Struct):
     name: Name
     record: Record
 
@@ -224,25 +263,6 @@ class Entry(CType):
 
         return self.name < other.name
 
-    @classmethod
-    def c_decl(cls):
-        yield f"struct svp64_entry {{"
-        for field in _dataclasses.fields(cls):
-            yield from indent(field.type.c_var(name=f"{field.name};"))
-        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_entry {name}"
-
 
 class Codegen(_enum.Enum):
     PPC_SVP64_H = _enum.auto()
@@ -282,6 +302,7 @@ class Codegen(_enum.Enum):
                 In1Sel, In2Sel, In3Sel, OutSel,
                 CRInSel, CROutSel,
                 SVPType, SVEType, SVEXTRA,
+                Mode,
             )
             for enum in enums:
                 yield from enum.c_decl()
@@ -290,6 +311,11 @@ class Codegen(_enum.Enum):
             yield from Record.c_decl()
             yield ""
 
+            for name in ("in1", "in2", "in3", "out", "out2", "cr_in", "cr_out"):
+                yield "unsigned char"
+                yield f"svp64_record_{name}_opsel(const struct svp64_record *record);"
+                yield ""
+
             yield from Entry.c_decl()
             yield ""
 
@@ -315,6 +341,66 @@ class Codegen(_enum.Enum):
             yield "#include \"opcode/ppc-svp64.h\""
             yield ""
 
+            def opsel(enum, name, table):
+                sep = (max(map(len, list(table.values()) + ["UNUSED"])) + 1)
+                c_tag = f"svp64_{enum.__name__.lower()}"
+                yield "unsigned char"
+                yield f"svp64_record_{name}_opsel(const struct svp64_record *record)"
+                yield "{"
+                yield from indent(["static const unsigned char table[] = {"])
+                for key in enum:
+                    value = table.get(key, "UNUSED")
+                    c_value = f"{c_tag.upper()}_{key.name.upper()}"
+                    yield from indent(indent([f"{value:{sep}}, /* {c_value} */"]))
+                yield from indent(["};"])
+                yield ""
+                yield from indent([f"return table[record->{name}];"])
+                yield "}"
+                yield ""
+
+            yield from opsel(In1Sel, "in1", {
+                In1Sel.RA: "RA",
+                In1Sel.RA_OR_ZERO: "RA",
+                In1Sel.SPR: "SPR",
+                In1Sel.RS: "RS",
+                In1Sel.FRA: "FRA",
+                In1Sel.FRS: "FRS",
+            })
+            yield from opsel(In2Sel, "in2", {
+                In2Sel.RB: "RB",
+                In2Sel.SPR: "SPR",
+                In2Sel.RS: "RS",
+                In2Sel.FRB: "FRB",
+            })
+            yield from opsel(In3Sel, "in3", {
+                In3Sel.RS: "RS",
+                In3Sel.RB: "RB",
+                In3Sel.FRS: "FRS",
+                In3Sel.FRC: "FRC",
+                In3Sel.RC: "RC",
+                In3Sel.RT: "RT",
+            })
+            for name in ("out", "out2"):
+                yield from opsel(OutSel, name, {
+                    OutSel.RT: "RT",
+                    OutSel.RA: "RA",
+                    OutSel.SPR: "SPR",
+                    OutSel.RT_OR_ZERO: "RT",
+                    OutSel.FRT: "FRT",
+                    OutSel.FRS: "FRS",
+                })
+            yield from opsel(CRInSel, "cr_in", {
+                CRInSel.BI: "BI",
+                CRInSel.BFA: "BFA",
+                CRInSel.BC: "CRB",
+                CRInSel.WHOLE_REG: "FXM",
+            })
+            yield from opsel(CROutSel, "cr_out", {
+                CROutSel.BF: "BF",
+                CROutSel.BT: "BT",
+                CROutSel.WHOLE_REG: "FXM",
+            })
+
             yield "const struct svp64_entry svp64_entries[] = {"
             for (index, entry) in enumerate(entries):
                 yield from indent(entry.c_value(prefix=f"[{index}] = ", suffix=","))
@@ -331,110 +417,75 @@ class Codegen(_enum.Enum):
         }[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 for field in _dataclasses.fields(Record)}
-FIELDS.update({field.name:field for field in _dataclasses.fields(Entry)})
-def parse(path, opcode_cls):
+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):
     visited = set()
+
+    def name_filter(name):
+        if name.startswith("l") and name.endswith("br"):
+            return False
+        if name in {"mcrxr", "mcrxrx", "darn"}:
+            return False
+        if name in {"bctar", "bcctr"}:
+            return False
+        if "rfid" in name:
+            return False
+        if name in {"setvl"}:
+            return False
+        if name in visited:
+            return False
+
+        visited.add(name)
+
+        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]
-        for name in map(Name, names.split("/")):
-            if name.startswith("l") and name.endswith("br"):
-                continue
-            if name in {"mcrxr", "mcrxrx", "darn"}:
-                continue
-            if name in {"bctar", "bcctr"}:
-                continue
-            if "rfid" in name:
-                continue
-            if name in {"setvl"}:
-                continue
-
-            record = {key.lower().replace(" ", "_"):value for (key, value) in record.items()}
-            for (key, value) in tuple(record.items()):
-                key = key.lower().replace(" ", "_")
-                if key not in FIELDS:
-                    record.pop(key)
-                    continue
-
-                field = FIELDS[key]
-                if not isinstance(value, field.type):
-                    if issubclass(field.type, _enum.Enum):
-                        value = {item.name:item for item in field.type}[value]
-                    else:
-                        value = field.type(value)
-
-                record[key] = value
-
-            if name not in visited:
+        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))
 
-            visited.add(name)
-
 
 def main(codegen):
     entries = []
-    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))
+    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",
+        "extra.csv",
+    )
+    for path in paths:
+        entries.extend(parse(path))
     entries = sorted(frozenset(entries))
 
     for line in codegen.generate(entries):