power_insn: support PPC multi-records
[openpower-isa.git] / src / openpower / decoder / power_insn.py
index 2f490c1729362f59811b0cf64f981a85d85b9c24..d4baa44715b4902e8369567dd056cf14971ae4a5 100644 (file)
@@ -234,6 +234,38 @@ class PPCRecord:
         return frozenset(self.comment.split("=")[-1].split("/"))
 
 
+class PPCMultiRecord(tuple):
+    @cached_property
+    def unified(self):
+        def merge(lhs, rhs):
+            value = 0
+            mask = 0
+            lvalue = lhs.opcode.value
+            rvalue = rhs.opcode.value
+            lmask = lhs.opcode.mask
+            rmask = rhs.opcode.mask
+            bits = max(lmask.bit_length(), rmask.bit_length())
+            for bit in range(bits):
+                lvstate = ((lvalue & (1 << bit)) != 0)
+                rvstate = ((rvalue & (1 << bit)) != 0)
+                lmstate = ((lmask & (1 << bit)) != 0)
+                rmstate = ((rmask & (1 << bit)) != 0)
+                vstate = lvstate
+                mstate = True
+                if (not lmstate or not rmstate) or (lvstate != rvstate):
+                    vstate = 0
+                    mstate = 0
+                value |= (vstate << bit)
+                mask |= (mstate << bit)
+
+            return _dataclasses.replace(lhs, opcode=Opcode(value=value, mask=mask))
+
+        return _functools.reduce(merge, self)
+
+    def __getattr__(self, attr):
+        return getattr(self.unified, attr)
+
+
 @_dataclasses.dataclass(eq=True, frozen=True)
 class SVP64Record:
     class ExtraMap(tuple):
@@ -284,7 +316,6 @@ class SVP64Record:
     cr_in: _CRInSel = _CRInSel.NONE
     cr_out: _CROutSel = _CROutSel.NONE
     extra: ExtraMap = ExtraMap()
-    pu: bool = False
     conditions: str = ""
     mode: _SVMode = _SVMode.NORMAL
 
@@ -295,7 +326,6 @@ class SVP64Record:
         "Etype": "etype",
         "CR in": "cr_in",
         "CR out": "cr_out",
-        "PU": "pu",
     }
 
     @classmethod
@@ -387,7 +417,7 @@ class Fields:
 
         def transform(item):
             (name, bitrange) = item
-            return (name, tuple(bitrange.values()))
+            return (name, list(bitrange.values()))
 
         self.__mapping = dict(map(transform, items))
 
@@ -411,40 +441,87 @@ class Operands:
     class Operand:
         name: str
 
-        def disassemble(self, value, record):
+        def disassemble(self, value, record, verbose=False):
             raise NotImplementedError
 
     @_dataclasses.dataclass(eq=True, frozen=True)
     class DynamicOperand(Operand):
-        def disassemble(self, value, record):
-            return str(int(value[record.fields[self.name]]))
+        def disassemble(self, value, record, verbose=False):
+            span = record.fields[self.name]
+            value = value[span]
+            if verbose:
+                return f"{int(value):0{value.bits}b} {span}"
+            else:
+                return str(int(value))
 
     @_dataclasses.dataclass(eq=True, frozen=True)
     class StaticOperand(Operand):
-        value: int = None
+        value: int
+
+        def disassemble(self, value, record, verbose=False):
+            span = record.fields[self.name]
+            value = value[span]
+            if verbose:
+                return f"{int(value):0{value.bits}b} {span}"
+            else:
+                return str(int(value))
 
     @_dataclasses.dataclass(eq=True, frozen=True)
     class DynamicOperandIFormLI(DynamicOperand):
-        def disassemble(self, value, record):
-            return hex(int(_selectconcat(
-                value[record.fields["LI"]],
-                _SelectableInt(value=0b00, bits=2))))
+        @property
+        def name(self):
+            return "LI"
+
+        @name.setter
+        def name(self, _):
+            pass
+
+        def disassemble(self, value, record, verbose=False):
+            span = record.fields["LI"]
+            value = value[span]
+            if verbose:
+                hints = "(target_addr=EXTS(LI || 0b00)))"
+                return f"{int(value):0{value.bits}b} {span} {hints}"
+            else:
+                return hex(int(_selectconcat(value,
+                    _SelectableInt(value=0b00, bits=2))))
 
     class DynamicOperandBFormBD(DynamicOperand):
-        def disassemble(self, value, record):
-            return hex(int(_selectconcat(
-                value[record.fields["BD"]],
-                _SelectableInt(value=0b00, bits=2))))
+        @property
+        def name(self):
+            return "BD"
+
+        @name.setter
+        def name(self, _):
+            pass
+
+        def disassemble(self, value, record, verbose=False):
+            span = record.fields["BD"]
+            value = value[span]
+            if verbose:
+                hints = "(target_addr=EXTS(BD || 0b00))"
+                return f"{int(value):0{value.bits}b} {span} {hints}"
+            else:
+                return hex(int(_selectconcat(value,
+                    _SelectableInt(value=0b00, bits=2))))
 
     @_dataclasses.dataclass(eq=True, frozen=True)
     class DynamicOperandGPR(DynamicOperand):
-        def disassemble(self, value, record):
-            return f"r{super().disassemble(value=value, record=record)}"
+        def disassemble(self, value, record, verbose=False):
+            result = super().disassemble(value=value,
+                record=record, verbose=verbose)
+            if not verbose:
+                result = f"r{result}"
+            return result
 
     @_dataclasses.dataclass(eq=True, frozen=True)
     class DynamicOperandFPR(DynamicOperand):
-        def disassemble(self, value, record):
-            return f"f{super().disassemble(value=value, record=record)}"
+        def disassemble(self, value, record, verbose=False):
+            result = super().disassemble(value=value,
+                record=record, verbose=verbose)
+            if not verbose:
+                result = f"f{result}"
+            return result
 
     def __init__(self, insn, iterable):
         branches = {
@@ -664,7 +741,7 @@ class Instruction(_Mapping):
     def __hash__(self):
         return hash(int(self))
 
-    def disassemble(self, db, byteorder="little"):
+    def disassemble(self, db, byteorder="little", verbose=False):
         raise NotImplementedError
 
 
@@ -676,7 +753,27 @@ class WordInstruction(Instruction):
     def integer(cls, value, byteorder="little"):
         return super().integer(bits=32, value=value, byteorder=byteorder)
 
-    def disassemble(self, db, byteorder="little"):
+    def spec(self, record):
+        dynamic_operands = []
+        for operand in record.operands.dynamic:
+            dynamic_operands.append(operand.name)
+        static_operands = []
+        for operand in record.operands.static:
+            static_operands.append(f"{operand.name}={operand.value}")
+        operands = ""
+        if dynamic_operands:
+            operands += f" {','.join(dynamic_operands)}"
+        if static_operands:
+            operands += f" ({' '.join(static_operands)})"
+        return f"{record.name}{operands}"
+
+    def opcode(self, record):
+        return f"0x{record.opcode.value:08x}"
+
+    def mask(self, record):
+        return f"0x{record.opcode.mask:08x}"
+
+    def disassemble(self, db, byteorder="little", verbose=False):
         integer = int(self)
         blob = integer.to_bytes(length=4, byteorder=byteorder)
         blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
@@ -684,17 +781,36 @@ class WordInstruction(Instruction):
         record = db[self]
         if record is None:
             yield f"{blob}    .long 0x{integer:08x}"
+            return
+
+        operands = []
+        for operand in record.operands.dynamic:
+            operand = operand.disassemble(value=self,
+                record=record, verbose=False)
+            operands.append(operand)
+        if operands:
+            operands = ",".join(operands)
+            operands = f" {operands}"
         else:
-            operands = []
-            for operand in record.operands.dynamic:
-                operand = operand.disassemble(self, record)
-                operands.append(operand)
-            if operands:
-                operands = ",".join(operands)
-                operands = f" {operands}"
-            else:
-                operands = ""
-            yield f"{blob}    {record.name}{operands}"
+            operands = ""
+
+        yield f"{blob}    {record.name}{operands}"
+
+        if verbose:
+            lindent = (" " * 4)
+            rindent = (len(blob) - len(lindent))
+            spec = self.spec(record=record)
+            opcode = self.opcode(record=record)
+            mask = self.mask(record=record)
+            yield f"{lindent}{'spec':{rindent}}{spec}"
+            yield f"{lindent}{'opcode':{rindent}}{opcode}"
+            yield f"{lindent}{'mask':{rindent}}{mask}"
+            for operand in record.operands:
+                name = operand.name
+                value = operand.disassemble(value=self,
+                    record=record, verbose=True)
+                yield f"{lindent}{name:{rindent}}{value}"
+
 
 class PrefixedInstruction(Instruction):
     class Prefix(WordInstruction.remap(range(0, 32))):
@@ -730,8 +846,8 @@ class Mode(_Mapping):
 
 
 class NormalMode(Mode):
-    class normal(Mode):
-        """normal mode"""
+    class simple(Mode):
+        """simple mode"""
         dz: Mode[3]
         sz: Mode[4]
 
@@ -795,7 +911,7 @@ class NormalMode(Mode):
         dz: Mode[3]
         sz: Mode[3]
 
-    normal: normal
+    simple: simple
     smr: smr
     pmr: pmr
     svmr: svmr
@@ -810,8 +926,8 @@ class NormalMode(Mode):
 
 
 class LDSTImmMode(Mode):
-    class normal(Mode):
-        """normal mode"""
+    class simple(Mode):
+        """simple mode"""
         zz: Mode[3]
         els: Mode[4]
         dz: Mode[3]
@@ -854,7 +970,7 @@ class LDSTImmMode(Mode):
         els: Mode[3]
         RC1: Mode[4]
 
-    normal: normal
+    simple: simple
     spu: spu
     ffrc1: ffrc1
     ffrc0: ffrc0
@@ -864,8 +980,8 @@ class LDSTImmMode(Mode):
 
 
 class LDSTIdxMode(Mode):
-    class normal(Mode):
-        """normal mode"""
+    class simple(Mode):
+        """simple mode"""
         SEA: Mode[2]
         sz: Mode[3]
         dz: Mode[3]
@@ -895,7 +1011,7 @@ class LDSTIdxMode(Mode):
         dz: Mode[3]
         sz: Mode[3]
 
-    normal: normal
+    simple: simple
     stride: stride
     sat: sat
     prrc1: prrc1
@@ -938,7 +1054,7 @@ class SVP64Instruction(PrefixedInstruction):
 
     prefix: Prefix
 
-    def disassemble(self, db, byteorder="little"):
+    def disassemble(self, db, byteorder="little", verbose=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))
@@ -951,9 +1067,88 @@ class SVP64Instruction(PrefixedInstruction):
         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}"
-        else:
-            yield f"{blob_prefix}    sv.{record.name}"
-            yield f"{blob_suffix}"
+            return
+
+        Rc = False
+        if record.operands["Rc"] is not None:
+            Rc = bool(self[record.fields["Rc"]])
+
+        subvl = self.prefix.rm.subvl
+        mode = self.prefix.rm.mode
+        sel = mode.sel
+
+        if record.svp64.mode is _SVMode.NORMAL:
+            mode = mode.normal
+            if sel == 0b00:
+                if mode[2] == 0b0:
+                    mode = mode.simple
+                else:
+                    if subvl == 0b00:
+                        if mode[3] == 0b0:
+                            mode = mode.smr
+                        else:
+                            mode = mode.pmr
+                    else:
+                        if mode[4] == 0b0:
+                            mode = mode.svmr
+                        else:
+                            mode = mode.pu
+            elif sel == 0b01:
+                if Rc:
+                    mode = mode.ffrc1
+                else:
+                    mode = mode.ffrc0
+            elif sel == 0b10:
+                if subvl == 0b00:
+                    mode = mode.sat
+                else:
+                    if mode[4]:
+                        mode = mode.satx
+                    else:
+                        mode = mode.satpu
+            elif sel == 0b11:
+                if Rc:
+                    mode = mode.prrc1
+                else:
+                    mode = mode.prrc0
+        elif record.svp64.mode is _SVMode.LDST_IMM:
+            mode = mode.ldst_imm
+            if sel == 0b00:
+                if mode[2] == 0b0:
+                    mode = mode.simple
+                else:
+                    mode = mode.spu
+            elif sel == 0b01:
+                if Rc:
+                    mode = mode.ffrc1
+                else:
+                    mode = mode.ffrc0
+            elif sel == 0b10:
+                mode = mode.sat
+            elif sel == 0b11:
+                if Rc:
+                    mode = mode.prrc1
+                else:
+                    mode = mode.prrc0
+        elif record.svp64.mode is _SVMode.LDST_IMM:
+            mode = mode.ldst_idx
+            if mode.sel == 0b00:
+                mode = mode.simple
+            elif mode.sel == 0b01:
+                mode = mode.stride
+            elif mode.sel == 0b10:
+                mode = mode.sat
+            elif mode.sel == 0b11:
+                if Rc:
+                    mode = mode.prrc1
+                else:
+                    mode = mode.prrc0
+
+        if type(mode) is Mode:
+            raise NotImplementedError
+
+        yield f"{blob_prefix}    sv.{record.name}"
+        yield f"{blob_suffix}"
 
 
 def parse(stream, factory):
@@ -1023,38 +1218,10 @@ class PPCDatabase:
                     for insn in parse(stream, factory):
                         records[section][insn.comment].add(insn)
 
-        # Once we collected all instructions with the same identifier,
-        # it's time to merge the different opcodes into the single pattern.
-        # At this point, we only consider masks; the algorithm as follows:
-        # 1. If any of two masks ignores the bit, it's ignored entirely.
-        # 2. If the bit is not equal between masks, it's ignored.
-        # 3. Otherwise the bits are equal and considered.
-        def merge(lhs, rhs):
-            value = 0
-            mask = 0
-            lvalue = lhs.opcode.value
-            rvalue = rhs.opcode.value
-            lmask = lhs.opcode.mask
-            rmask = rhs.opcode.mask
-            bits = max(lmask.bit_length(), rmask.bit_length())
-            for bit in range(bits):
-                lvstate = ((lvalue & (1 << bit)) != 0)
-                rvstate = ((rvalue & (1 << bit)) != 0)
-                lmstate = ((lmask & (1 << bit)) != 0)
-                rmstate = ((rmask & (1 << bit)) != 0)
-                vstate = lvstate
-                mstate = True
-                if (not lmstate or not rmstate) or (lvstate != rvstate):
-                    vstate = 0
-                    mstate = 0
-                value |= (vstate << bit)
-                mask |= (mstate << bit)
-            return _dataclasses.replace(lhs, opcode=Opcode(value=value, mask=mask))
-
         db = dd(set)
         for (section, group) in records.items():
             for records in group.values():
-                db[section].add(_functools.reduce(merge, records))
+                db[section].add(PPCMultiRecord(records))
 
         self.__db = db
         self.__mdwndb = mdwndb