power_insn: drop redundant dataclass incantations
[openpower-isa.git] / src / openpower / decoder / power_insn.py
index d4813a04b8f9a490e61126e76a4ff1c8efaabe66..bf6da71fec7fc9fdde859ec343c841523b18da8c 100644 (file)
@@ -3,6 +3,7 @@ import csv as _csv
 import dataclasses as _dataclasses
 import enum as _enum
 import functools as _functools
+import itertools as _itertools
 import os as _os
 import operator as _operator
 import pathlib as _pathlib
@@ -47,6 +48,18 @@ from openpower.decoder.power_fields import (
 from openpower.decoder.pseudo.pagereader import ISA as _ISA
 
 
+@_functools.total_ordering
+class Verbosity(_enum.Enum):
+    SHORT = _enum.auto()
+    NORMAL = _enum.auto()
+    VERBOSE = _enum.auto()
+
+    def __lt__(self, other):
+        if not isinstance(other, self.__class__):
+            return NotImplemented
+        return (self.value < other.value)
+
+
 def dataclass(cls, record, keymap=None, typemap=None):
     if keymap is None:
         keymap = {}
@@ -143,23 +156,6 @@ class PatternOpcode(Opcode):
         return super().__init__(value=value, mask=mask)
 
 
-class FieldsOpcode(Opcode):
-    def __init__(self, fields):
-        def field(opcode, field):
-            (value, mask) = opcode
-            (field, bits) = field
-            shifts = map(lambda bit: (31 - bit), reversed(tuple(bits)))
-            for (index, shift) in enumerate(shifts):
-                bit = ((field & (1 << index)) != 0)
-                value |= (bit << shift)
-                mask |= (1 << shift)
-            return (value, mask)
-
-        (value, mask) = _functools.reduce(field, fields, (0, 0))
-
-        return super().__init__(value=value, mask=mask)
-
-
 @_dataclasses.dataclass(eq=True, frozen=True)
 class PPCRecord:
     class FlagsMeta(type):
@@ -425,6 +421,9 @@ class BitSel:
     def __iter__(self):
         yield from range(self.start, (self.end + 1))
 
+    def __reversed__(self):
+        return tuple(reversed(tuple(self)))
+
     @property
     def start(self):
         return self.__start
@@ -505,19 +504,23 @@ class Fields:
 class Operand:
     name: str
 
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
+    def span(self, record):
+        return record.fields[self.name]
+
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
         raise NotImplementedError
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class DynamicOperand(Operand):
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
-        span = record.fields[self.name]
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        span = self.span(record=record)
         if isinstance(insn, SVP64Instruction):
             span = tuple(map(lambda bit: (bit + 32), span))
         value = insn[span]
 
-        if verbose:
+        if verbosity >= Verbosity.VERBOSE:
             span = map(str, span)
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
@@ -527,58 +530,68 @@ class DynamicOperand(Operand):
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
+class StaticOperand(Operand):
+    value: int
+
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        span = self.span(record=record)
+        if isinstance(insn, SVP64Instruction):
+            span = tuple(map(lambda bit: (bit + 32), span))
+        value = insn[span]
+
+        if verbosity >= Verbosity.VERBOSE:
+            span = map(str, span)
+            yield f"{indent}{self.name}"
+            yield f"{indent}{indent}{int(value):0{value.bits}b}"
+            yield f"{indent}{indent}{', '.join(span)}"
+        else:
+            yield str(int(value))
+
+
+class ImmediateOperand(DynamicOperand):
+    pass
+
+
 class DynamicOperandReg(DynamicOperand):
-    def spec(self, insn, record):
+    def spec(self, insn, record, merge):
         vector = False
-        span = record.fields[self.name]
+        span = self.span(record=record)
         if isinstance(insn, SVP64Instruction):
             span = tuple(map(lambda bit: (bit + 32), span))
         value = insn[span]
+        span = tuple(map(str, span))
 
         if isinstance(insn, SVP64Instruction):
             extra_idx = self.extra_idx(record=record)
+            if extra_idx is _SVExtra.NONE:
+                return (vector, value, span)
 
             if record.etype is _SVEtype.EXTRA3:
-                extra = insn.prefix.rm.extra3[extra_idx]
+                spec = insn.prefix.rm.extra3[extra_idx]
             elif record.etype is _SVEtype.EXTRA2:
-                extra = insn.prefix.rm.extra2[extra_idx]
+                spec = insn.prefix.rm.extra2[extra_idx]
             else:
                 raise ValueError(record.etype)
 
-            if extra != 0:
-                vector = bool(extra[0])
-                span = tuple(map(str, span))
-                extra_span = extra.__class__
+            if spec != 0:
+                vector = bool(spec[0])
+                spec_span = spec.__class__
                 if record.etype is _SVEtype.EXTRA3:
-                    extra_span = tuple(map(str, extra_span[1, 2]))
-                    extra = extra[1, 2]
+                    spec_span = tuple(map(str, spec_span[1, 2]))
+                    spec = spec[1, 2]
                 elif record.etype is _SVEtype.EXTRA2:
-                    extra_span = tuple(map(str, extra_span[1,]))
-                    extra = _SelectableInt(value=extra[1].value, bits=2)
+                    spec_span = tuple(map(str, spec_span[1,]))
+                    spec = _SelectableInt(value=spec[1].value, bits=2)
                     if vector:
-                        extra <<= 1
-                        extra_span = (extra_span + ("{0}",))
+                        spec <<= 1
+                        spec_span = (spec_span + ("{0}",))
                     else:
-                        extra_span = (("{0}",) + extra_span)
+                        spec_span = (("{0}",) + spec_span)
                 else:
                     raise ValueError(record.etype)
 
-                bits = (len(span) + len(extra_span))
-                value = _SelectableInt(value=value.value, bits=bits)
-                extra = _SelectableInt(value=extra.value, bits=bits)
-                if vector:
-                    value = ((value << 2) | extra)
-                    span = (span + extra_span)
-                else:
-                    value = ((extra << 5) | value)
-                    span = (extra_span + span)
-
-                value = _SelectableInt(value=value, bits=bits)
-
-        else:
-            value = insn[span]
-
-        span = tuple(map(str, span))
+                (value, span) = merge(vector, value, span, spec, spec_span)
 
         return (vector, value, span)
 
@@ -597,11 +610,11 @@ class DynamicOperandReg(DynamicOperand):
 
         return _SVExtra.NONE
 
-    def disassemble(self, insn, record, verbose=False, prefix="", indent="",
-                                        short=False):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, prefix="", indent=""):
         (vector, value, span) = self.spec(insn=insn, record=record)
 
-        if verbose:
+        if verbosity >= Verbosity.VERBOSE:
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
             yield f"{indent}{indent}{', '.join(span)}"
@@ -619,40 +632,51 @@ class DynamicOperandReg(DynamicOperand):
             yield f"{vector}{prefix}{int(value)}"
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class ImmediateOperand(DynamicOperand):
-    pass
+class DynamicOperandGPRFPR(DynamicOperandReg):
+    def spec(self, insn, record):
+        def merge(vector, value, span, spec, spec_span):
+            bits = (len(span) + len(spec_span))
+            value = _SelectableInt(value=value.value, bits=bits)
+            spec = _SelectableInt(value=spec.value, bits=bits)
+            if vector:
+                value = ((value << 2) | spec)
+                span = (span + spec_span)
+            else:
+                value = ((spec << 5) | value)
+                span = (spec_span + span)
 
+            return (value, span)
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class StaticOperand(Operand):
-    value: int
+        return super().spec(insn=insn, record=record, merge=merge)
 
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
-        span = record.fields[self.name]
-        if isinstance(insn, SVP64Instruction):
-            span = tuple(map(lambda bit: (bit + 32), span))
-        value = insn[span]
 
-        if verbose:
-            span = map(str, span)
-            yield f"{indent}{self.name}"
-            yield f"{indent}{indent}{int(value):0{value.bits}b}"
-            yield f"{indent}{indent}{', '.join(span)}"
-        else:
-            yield str(int(value))
+class DynamicOperandGPR(DynamicOperandGPRFPR):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        prefix = "" if (verbosity <= Verbosity.SHORT) else "r"
+        yield from super().disassemble(prefix=prefix,
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
+
+
+class DynamicOperandFPR(DynamicOperandGPRFPR):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        prefix = "" if (verbosity <= Verbosity.SHORT) else "f"
+        yield from super().disassemble(prefix=prefix,
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class DynamicOperandTargetAddr(DynamicOperandReg):
-    def disassemble(self, insn, record, field, verbose=False, indent="",
-                                               short=False):
-        span = record.fields[field]
+    def disassemble(self, insn, record, field,
+            verbosity=Verbosity.NORMAL, indent=""):
+        span = self.span(record=record)
         if isinstance(insn, SVP64Instruction):
             span = tuple(map(lambda bit: (bit + 32), span))
         value = insn[span]
 
-        if verbose:
+        if verbosity >= Verbosity.VERBOSE:
             span = tuple(map(str, span))
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{int(value):0{value.bits}b}00"
@@ -663,42 +687,65 @@ class DynamicOperandTargetAddr(DynamicOperandReg):
                 _SelectableInt(value=0b00, bits=2))))
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class DynamicOperandTargetAddrLI(DynamicOperandTargetAddr):
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
+    def span(self, record):
+        return record.fields["LI"]
+
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
         return super().disassemble(field="LI",
-            insn=insn, record=record, verbose=verbose, indent=indent,
-            short=short)
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
 
 class DynamicOperandTargetAddrBD(DynamicOperandTargetAddr):
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
+    def span(self, record):
+        return record.fields["BD"]
+
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
         return super().disassemble(field="BD",
-            insn=insn, record=record, verbose=verbose, indent=indent,
-            short=short)
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandGPR(DynamicOperandReg):
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
-        prefix = "" if short else "r"
-        yield from super().disassemble(prefix=prefix,
-            insn=insn, record=record, verbose=verbose, indent=indent,
-            short=short)
+class DynamicOperandDDX(DynamicOperand):
+    def span(self, record):
+        operands = map(DynamicOperand, ("d0", "d1", "d2"))
+        spans = map(lambda operand: operand.span(record=record), operands)
+        return sum(spans, tuple())
 
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        span = self.span(record=record)
+        if isinstance(insn, SVP64Instruction):
+            span = tuple(map(lambda bit: (bit + 32), span))
+        value = insn[span]
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandFPR(DynamicOperandReg):
-    def disassemble(self, insn, record, verbose=False, indent="", short=False):
-        prefix = "" if short else "f"
-        yield from super().disassemble(prefix=prefix,
-            insn=insn, record=record, verbose=verbose, indent=indent,
-            short=short)
+        if verbosity >= Verbosity.VERBOSE:
+            yield f"{indent}D"
+            mapping = {
+                "d0": "[0:9]",
+                "d1": "[10:15]",
+                "d2": "[16]",
+            }
+            for (subname, subspan) in mapping.items():
+                operand = DynamicOperand(name=subname)
+                span = operand.span(record=record)
+                if isinstance(insn, SVP64Instruction):
+                    span = tuple(map(lambda bit: (bit + 32), span))
+                value = insn[span]
+                span = map(str, span)
+                yield f"{indent}{indent}{operand.name} = D{subspan}"
+                yield f"{indent}{indent}{indent}{int(value):0{value.bits}b}"
+                yield f"{indent}{indent}{indent}{', '.join(span)}"
+        else:
+            yield str(int(value))
 
 
 class Operands(tuple):
     def __new__(cls, insn, iterable):
-        branches = {
+        custom = {
             "b": {"target_addr": DynamicOperandTargetAddrLI},
             "ba": {"target_addr": DynamicOperandTargetAddrLI},
             "bl": {"target_addr": DynamicOperandTargetAddrLI},
@@ -707,6 +754,9 @@ class Operands(tuple):
             "bca": {"target_addr": DynamicOperandTargetAddrBD},
             "bcl": {"target_addr": DynamicOperandTargetAddrBD},
             "bcla": {"target_addr": DynamicOperandTargetAddrBD},
+            "addpcis": {"D": DynamicOperandDDX},
+            "fishmv": {"D": DynamicOperandDDX},
+            "fmvis": {"D": DynamicOperandDDX},
         }
 
         operands = []
@@ -728,8 +778,8 @@ class Operands(tuple):
                 if immediate is not None:
                     operands.append(ImmediateOperand(name=immediate))
 
-                if insn in branches and operand in branches[insn]:
-                    dynamic_cls = branches[insn][operand]
+                if insn in custom and operand in custom[insn]:
+                    dynamic_cls = custom[insn][operand]
 
                 if operand in _RegType.__members__:
                     regtype = _RegType[operand]
@@ -783,17 +833,38 @@ class Record:
 
     @cached_property
     def opcode(self):
-        fields = []
+        value = ([0] * 32)
+        mask = ([0] * 32)
+
         if self.section.opcode:
-            fields += [(self.section.opcode.value, BitSel((0, 5)))]
-            fields += [(self.ppc.opcode.value, self.section.bitsel)]
-        else:
-            fields += [(self.ppc.opcode.value, self.section.bitsel)]
+            for (src, dst) in enumerate(reversed(BitSel((0, 5)))):
+                value[dst] = ((self.section.opcode.value & (1 << src)) != 0)
+                mask[dst] = ((self.section.opcode.mask & (1 << src)) != 0)
+
+        for (src, dst) in enumerate(reversed(self.section.bitsel)):
+            value[dst] = ((self.ppc.opcode.value & (1 << src)) != 0)
+            mask[dst] = ((self.ppc.opcode.mask & (1 << src)) != 0)
 
         for operand in self.operands.static:
-            fields += [(operand.value, self.fields[operand.name])]
+            for (src, dst) in enumerate(reversed(operand.span(record=self))):
+                value[dst] = ((operand.value & (1 << src)) != 0)
+                mask[dst] = True
 
-        return FieldsOpcode(fields)
+        for operand in self.operands.dynamic:
+            for dst in operand.span(record=self):
+                value[dst] = False
+                mask[dst] = False
+
+        def onebit(bit):
+            return _SelectableInt(value=int(bit), bits=1)
+
+        value = _selectconcat(*map(onebit, value))
+        mask = _selectconcat(*map(onebit, mask))
+
+        value = int(value)
+        mask = int(mask)
+
+        return Opcode(value=value, mask=mask)
 
     @property
     def function(self):
@@ -898,7 +969,7 @@ class Instruction(_Mapping):
 
         return f"{prefix}{record.name}{operands}"
 
-    def dynamic_operands(self, db, short=False):
+    def dynamic_operands(self, db, verbosity=Verbosity.NORMAL):
         record = self.record(db=db)
 
         imm = False
@@ -906,8 +977,9 @@ class Instruction(_Mapping):
         imm_value = ""
         for operand in record.operands.dynamic:
             name = operand.name
-            value = " ".join(operand.disassemble(insn=self,
-                record=record, verbose=False, short=short))
+            dis = operand.disassemble(insn=self, record=record,
+                verbosity=min(verbosity, Verbosity.NORMAL))
+            value = " ".join(dis)
             if imm:
                 name = f"{imm_name}({name})"
                 value = f"{imm_value}({value})"
@@ -924,7 +996,9 @@ class Instruction(_Mapping):
         for operand in record.operands.static:
             yield (operand.name, operand.value)
 
-    def disassemble(self, db, byteorder="little", verbose=False, short=False):
+    def disassemble(self, db,
+            byteorder="little",
+            verbosity=Verbosity.NORMAL):
         raise NotImplementedError
 
 
@@ -952,28 +1026,30 @@ class WordInstruction(Instruction):
         record = self.record(db=db)
         return f"0x{record.opcode.mask:08x}"
 
-    def disassemble(self, db, byteorder="little", verbose=False, short=False):
+    def disassemble(self, db,
+            byteorder="little",
+            verbosity=Verbosity.NORMAL):
         integer = int(self)
-        if short:
+        if verbosity <= Verbosity.SHORT:
             blob = ""
         else:
             blob = integer.to_bytes(length=4, byteorder=byteorder)
             blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
             blob += "    "
 
-        record = self.record(db=db)
+        record = db[self]
         if record is None:
             yield f"{blob}.long 0x{integer:08x}"
             return
 
         operands = tuple(map(_operator.itemgetter(1),
-            self.dynamic_operands(db=db, short=short)))
+            self.dynamic_operands(db=db, verbosity=verbosity)))
         if operands:
             yield f"{blob}{record.name} {','.join(operands)}"
         else:
             yield f"{blob}{record.name}"
 
-        if verbose:
+        if verbosity >= Verbosity.VERBOSE:
             indent = (" " * 4)
             binary = self.binary
             spec = self.spec(db=db, prefix="")
@@ -991,8 +1067,8 @@ class WordInstruction(Instruction):
             yield f"{indent}mask"
             yield f"{indent}{indent}{mask}"
             for operand in record.operands:
-                yield from operand.disassemble(insn=self,
-                        record=record, verbose=True, indent=indent, short=short)
+                yield from operand.disassemble(insn=self, record=record,
+                    verbosity=verbosity, indent=indent)
             yield ""
 
 
@@ -1202,11 +1278,11 @@ class LDSTIdxMode(Mode):
     prrc0: prrc0
 
 
-class ExtraSpec(_Mapping):
+class Extra(_Mapping):
     _: _Field = range(0, 9)
 
 
-class Extra2Spec(ExtraSpec):
+class Extra2(Extra):
     idx0: _Field = range(0, 2)
     idx1: _Field = range(2, 4)
     idx2: _Field = range(4, 6)
@@ -1228,7 +1304,7 @@ class Extra2Spec(ExtraSpec):
         self[key].assign(value)
 
 
-class Extra3Spec(ExtraSpec):
+class Extra3(Extra):
     idx0: _Field = range(0, 3)
     idx1: _Field = range(3, 6)
     idx2: _Field = range(6, 9)
@@ -1262,9 +1338,9 @@ class RM(_Mapping):
     mode: Mode.remap(range(19, 24))
     smask: _Field = range(16, 19)
 
-    extra: ExtraSpec.remap(range(10, 19))
-    extra2: Extra2Spec.remap(range(10, 19))
-    extra3: Extra3Spec.remap(range(10, 19))
+    extra: Extra.remap(range(10, 19))
+    extra2: Extra2.remap(range(10, 19))
+    extra3: Extra3.remap(range(10, 19))
 
 
 class SVP64Instruction(PrefixedInstruction):
@@ -1403,32 +1479,37 @@ class SVP64Instruction(PrefixedInstruction):
 
         raise ValueError(self)
 
-    def disassemble(self, db, byteorder="little", verbose=False, short=False):
-        integer_prefix = int(self.prefix)
-        blob_prefix = integer_prefix.to_bytes(length=4, byteorder=byteorder)
-        blob_prefix = " ".join(map(lambda byte: f"{byte:02x}", blob_prefix))
-
-        integer_suffix = int(self.suffix)
-        blob_suffix = integer_suffix.to_bytes(length=4, byteorder=byteorder)
-        blob_suffix = " ".join(map(lambda byte: f"{byte:02x}", blob_suffix))
+    def disassemble(self, db,
+            byteorder="little",
+            verbosity=Verbosity.NORMAL):
+        def blob(integer):
+            if verbosity <= Verbosity.SHORT:
+                return ""
+            else:
+                blob = integer.to_bytes(length=4, byteorder=byteorder)
+                blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+                return f"{blob}    "
 
-        record = self.record(db=db)
+        blob_prefix = blob(int(self.prefix))
+        blob_suffix = blob(int(self.suffix))
+        record = db[self]
         if record is None or record.svp64 is None:
-            yield f"{blob_prefix}    .long 0x{int(self.prefix):08x}"
-            yield f"{blob_suffix}    .long 0x{int(self.suffix):08x}"
+            yield f"{blob_prefix}.long 0x{int(self.prefix):08x}"
+            yield f"{blob_suffix}.long 0x{int(self.suffix):08x}"
             return
 
         operands = tuple(map(_operator.itemgetter(1),
-            self.dynamic_operands(db=db, short=short)))
+            self.dynamic_operands(db=db, verbosity=verbosity)))
         if operands:
-            yield f"{blob_prefix}    sv.{record.name} {','.join(operands)}"
+            yield f"{blob_prefix}sv.{record.name} {','.join(operands)}"
         else:
-            yield f"{blob_prefix}    {record.name}"
-        yield f"{blob_suffix}"
+            yield f"{blob_prefix}{record.name}"
+        if blob_suffix:
+            yield f"{blob_suffix}"
 
         (mode, mode_desc) = self.mode(db=db)
 
-        if verbose:
+        if verbosity >= Verbosity.VERBOSE:
             indent = (" " * 4)
             binary = self.binary
             spec = self.spec(db=db, prefix="sv.")
@@ -1450,8 +1531,8 @@ class SVP64Instruction(PrefixedInstruction):
             yield f"{indent}mask"
             yield f"{indent}{indent}{mask}"
             for operand in record.operands:
-                yield from operand.disassemble(insn=self,
-                        record=record, verbose=True, indent=indent, short=short)
+                yield from operand.disassemble(insn=self, record=record,
+                    verbosity=verbosity, indent=indent)
 
             yield f"{indent}mode"
             yield f"{indent}{indent}{mode_desc}"
@@ -1660,11 +1741,14 @@ class Database:
     def __getitem__(self, key):
         if isinstance(key, (int, Instruction)):
             key = int(key)
+            matches = []
             for record in self:
                 opcode = record.opcode
                 if ((opcode.value & opcode.mask) ==
                         (key & opcode.mask)):
-                    return record
+                    matches.append(record)
+            if matches:
+                return matches[-1]
             return None
         elif isinstance(key, Opcode):
             for record in self: