power_insn: support PPC multi-records
[openpower-isa.git] / src / openpower / decoder / power_insn.py
index f90cc32d2b27dac8eb1c577faf715b3ba8a3c890..d4baa44715b4902e8369567dd056cf14971ae4a5 100644 (file)
@@ -30,6 +30,7 @@ from openpower.decoder.power_enums import (
     SVMode as _SVMode,
     SVPtype as _SVPtype,
     SVExtra as _SVExtra,
+    RegType as _RegType,
     SVExtraRegType as _SVExtraRegType,
     SVExtraReg as _SVExtraReg,
 )
@@ -39,6 +40,7 @@ from openpower.decoder.selectable_int import (
 )
 from openpower.decoder.power_fields import (
     Field as _Field,
+    Array as _Array,
     Mapping as _Mapping,
     DecodeFields as _DecodeFields,
 )
@@ -198,7 +200,7 @@ class PPCRecord:
     cry_in: _CryIn = _CryIn.ZERO
     ldst_len: _LDSTLen = _LDSTLen.NONE
     upd: _LDSTMode = _LDSTMode.NONE
-    rc: _RCOE = _RCOE.NONE
+    Rc: _RCOE = _RCOE.NONE
     form: _Form = _Form.NONE
     conditions: str = ""
     unofficial: bool = False
@@ -210,6 +212,7 @@ class PPCRecord:
         "CR out": "cr_out",
         "cry in": "cry_in",
         "ldst len": "ldst_len",
+        "rc": "Rc",
         "CONDITIONS": "conditions",
     }
 
@@ -231,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):
@@ -281,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
 
@@ -292,7 +326,6 @@ class SVP64Record:
         "Etype": "etype",
         "CR in": "cr_in",
         "CR out": "cr_out",
-        "PU": "pu",
     }
 
     @classmethod
@@ -384,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))
 
@@ -403,37 +436,159 @@ class Fields:
         return self.__mapping.get(key, None)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class Operand:
-    pass
+class Operands:
+    @_dataclasses.dataclass(eq=True, frozen=True)
+    class Operand:
+        name: str
+
+        def disassemble(self, value, record, verbose=False):
+            raise NotImplementedError
+
+    @_dataclasses.dataclass(eq=True, frozen=True)
+    class DynamicOperand(Operand):
+        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
 
+        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):
+        @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):
+        @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, 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, 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 = {
+            "b": {"LI": self.__class__.DynamicOperandIFormLI},
+            "ba": {"LI": self.__class__.DynamicOperandIFormLI},
+            "bl": {"LI": self.__class__.DynamicOperandIFormLI},
+            "bla": {"LI": self.__class__.DynamicOperandIFormLI},
+            "bc": {"BD": self.__class__.DynamicOperandBFormBD},
+            "bca": {"BD": self.__class__.DynamicOperandBFormBD},
+            "bcl": {"BD": self.__class__.DynamicOperandBFormBD},
+            "bcla": {"BD": self.__class__.DynamicOperandBFormBD},
+        }
 
-class Operands(tuple):
-    def __new__(cls, iterable):
         operands = []
         for operand in iterable:
+            dynamic_cls = self.__class__.DynamicOperand
+            static_cls = self.__class__.StaticOperand
+
             if "=" in operand:
                 (name, value) = operand.split("=")
-                value = int(value)
-                operand = StaticOperand(name=name, value=value)
+                operand = static_cls(name=name, value=int(value))
             else:
-                operand = DynamicOperand(name=operand)
+                if insn in branches and operand in branches[insn]:
+                    dynamic_cls = branches[insn][operand]
+
+                if operand in _RegType.__members__:
+                    regtype = _RegType[operand]
+                    if regtype is _RegType.GPR:
+                        dynamic_cls = self.__class__.DynamicOperandGPR
+                    elif regtype is _RegType.FPR:
+                        dynamic_cls = self.__class__.DynamicOperandFPR
+
+                operand = dynamic_cls(name=operand)
+
             operands.append(operand)
-        return super().__new__(cls, operands)
 
+        self.__operands = operands
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperand(Operand):
-    name: str
+        return super().__init__()
 
-    def disassemble(self, value, record):
-        return str(int(value[record.fields[self.name]]))
+    def __repr__(self):
+        return self.__operands.__repr__()
 
+    def __iter__(self):
+        yield from self.__operands
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class StaticOperand(Operand):
-    name: str
-    value: int = None
+    def __contains__(self, key):
+        return self.__getitem__(key) is not None
+
+    def __getitem__(self, key):
+        for operand in self.__operands:
+            if operand.name == key:
+                return operand
+
+        return None
+
+    @property
+    def dynamic(self):
+        for operand in self:
+            if isinstance(operand, self.__class__.DynamicOperand):
+                yield operand
+
+    @property
+    def static(self):
+        for operand in self:
+            if isinstance(operand, self.__class__.StaticOperand):
+                yield operand
 
 
 @_functools.total_ordering
@@ -458,9 +613,6 @@ class Record:
             return NotImplemented
         return (self.opcode < other.opcode)
 
-    def __repr__(self):
-        return f"{self.__class__.__name__}(name={self.name!r}, opcode={self.opcode})"
-
     @cached_property
     def opcode(self):
         fields = []
@@ -470,7 +622,7 @@ class Record:
         else:
             fields += [(self.ppc.opcode.value, self.section.bitsel)]
 
-        for operand in self.static_operands:
+        for operand in self.operands.static:
             fields += [(operand.value, self.fields[operand.name])]
 
         return FieldsOpcode(fields)
@@ -558,18 +710,6 @@ class Record:
             return _SVEtype.NONE
         return self.svp64.etype
 
-    @property
-    def dynamic_operands(self):
-        for operand in self.operands:
-            if isinstance(operand, DynamicOperand):
-                yield operand
-
-    @property
-    def static_operands(self):
-        for operand in self.operands:
-            if isinstance(operand, StaticOperand):
-                yield operand
-
 
 class Instruction(_Mapping):
     @classmethod
@@ -601,7 +741,7 @@ class Instruction(_Mapping):
     def __hash__(self):
         return hash(int(self))
 
-    def disassemble(self, db):
+    def disassemble(self, db, byteorder="little", verbose=False):
         raise NotImplementedError
 
 
@@ -613,22 +753,64 @@ class WordInstruction(Instruction):
     def integer(cls, value, byteorder="little"):
         return super().integer(bits=32, value=value, byteorder=byteorder)
 
-    def disassemble(self, db):
+    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))
+
         record = db[self]
         if record is None:
-            yield f".long 0x{int(self):08x}"
+            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.dynamic_operands:
-                operand = operand.disassemble(self, record)
-                operands.append(operand)
-            if operands:
-                operands = ",".join(operands)
-                operands = f" {operands}"
-            else:
-                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}"
 
-            yield f"{record.name}{operands}"
 
 class PrefixedInstruction(Instruction):
     class Prefix(WordInstruction.remap(range(0, 32))):
@@ -657,50 +839,316 @@ class PrefixedInstruction(Instruction):
 
         return super().integer(value=value)
 
-    def disassemble(self, db):
-        record = db[self.suffix]
-        if record is None:
-            yield f".llong 0x{int(self):016x}"
-        else:
-            yield f".llong 0x{int(self):016x} # {record.name}"
+
+class Mode(_Mapping):
+    _: _Field = range(0, 5)
+    sel: _Field = range(0, 2)
+
+
+class NormalMode(Mode):
+    class simple(Mode):
+        """simple mode"""
+        dz: Mode[3]
+        sz: Mode[4]
+
+    class smr(Mode):
+        """scalar reduce mode (mapreduce), SUBVL=1"""
+        RG: Mode[4]
+
+    class pmr(Mode):
+        """parallel reduce mode (mapreduce), SUBVL=1"""
+        pass
+
+    class svmr(Mode):
+        """subvector reduce mode, SUBVL>1"""
+        SVM: Mode[3]
+
+    class pu(Mode):
+        """Pack/Unpack mode, SUBVL>1"""
+        SVM: Mode[3]
+
+    class ffrc1(Mode):
+        """Rc=1: ffirst CR sel"""
+        inv: Mode[2]
+        CRbit: Mode[3, 4]
+
+    class ffrc0(Mode):
+        """Rc=0: ffirst z/nonz"""
+        inv: Mode[2]
+        VLi: Mode[3]
+        RC1: Mode[4]
+
+    class sat(Mode):
+        """sat mode: N=0/1 u/s, SUBVL=1"""
+        N: Mode[2]
+        dz: Mode[3]
+        sz: Mode[4]
+
+    class satx(Mode):
+        """sat mode: N=0/1 u/s, SUBVL>1"""
+        N: Mode[2]
+        zz: Mode[3]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    class satpu(Mode):
+        """Pack/Unpack sat mode: N=0/1 u/s, SUBVL>1"""
+        N: Mode[2]
+        zz: Mode[3]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    class prrc1(Mode):
+        """Rc=1: pred-result CR sel"""
+        inv: Mode[2]
+        CRbit: Mode[3, 4]
+
+    class prrc0(Mode):
+        """Rc=0: pred-result z/nonz"""
+        inv: Mode[2]
+        zz: Mode[3]
+        RC1: Mode[4]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    simple: simple
+    smr: smr
+    pmr: pmr
+    svmr: svmr
+    pu: pu
+    ffrc1: ffrc1
+    ffrc0: ffrc0
+    sat: sat
+    satx: satx
+    satpu: satpu
+    prrc1: prrc1
+    prrc0: prrc0
+
+
+class LDSTImmMode(Mode):
+    class simple(Mode):
+        """simple mode"""
+        zz: Mode[3]
+        els: Mode[4]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    class spu(Mode):
+        """Structured Pack/Unpack"""
+        zz: Mode[3]
+        els: Mode[4]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    class ffrc1(Mode):
+        """Rc=1: ffirst CR sel"""
+        inv: Mode[2]
+        CRbit: Mode[3, 4]
+
+    class ffrc0(Mode):
+        """Rc=0: ffirst z/nonz"""
+        inv: Mode[2]
+        els: Mode[3]
+        RC1: Mode[4]
+
+    class sat(Mode):
+        """sat mode: N=0/1 u/s"""
+        N: Mode[2]
+        zz: Mode[3]
+        els: Mode[4]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    class prrc1(Mode):
+        """Rc=1: pred-result CR sel"""
+        inv: Mode[2]
+        CRbit: Mode[3, 4]
+
+    class prrc0(Mode):
+        """Rc=0: pred-result z/nonz"""
+        inv: Mode[2]
+        els: Mode[3]
+        RC1: Mode[4]
+
+    simple: simple
+    spu: spu
+    ffrc1: ffrc1
+    ffrc0: ffrc0
+    sat: sat
+    prrc1: prrc1
+    prrc0: prrc0
+
+
+class LDSTIdxMode(Mode):
+    class simple(Mode):
+        """simple mode"""
+        SEA: Mode[2]
+        sz: Mode[3]
+        dz: Mode[3]
+
+    class stride(Mode):
+        """strided (scalar only source)"""
+        SEA: Mode[2]
+        dz: Mode[3]
+        sz: Mode[4]
+
+    class sat(Mode):
+        """sat mode: N=0/1 u/s"""
+        N: Mode[2]
+        dz: Mode[3]
+        sz: Mode[4]
+
+    class prrc1(Mode):
+        """Rc=1: pred-result CR sel"""
+        inv: Mode[2]
+        CRbit: Mode[3, 4]
+
+    class prrc0(Mode):
+        """Rc=0: pred-result z/nonz"""
+        inv: Mode[2]
+        zz: Mode[3]
+        RC1: Mode[4]
+        dz: Mode[3]
+        sz: Mode[3]
+
+    simple: simple
+    stride: stride
+    sat: sat
+    prrc1: prrc1
+    prrc0: prrc0
+
+
+class RM(_Mapping):
+    class Mode(Mode):
+        normal: NormalMode
+        ldst_imm: LDSTImmMode
+        ldst_idx: LDSTIdxMode
+
+    _: _Field = range(24)
+    mmode: _Field = (0,)
+    mask: _Field = range(1, 4)
+    elwidth: _Field = range(4, 6)
+    ewsrc: _Field = range(6, 8)
+    subvl: _Field = range(8, 10)
+    extra: _Field = range(10, 19)
+    mode: Mode.remap(range(19, 24))
+    extra2: _Array[4] = (
+        range(10, 12),
+        range(12, 14),
+        range(14, 16),
+        range(16, 18),
+    )
+    smask: _Field = range(16, 19)
+    extra3: _Array[3] = (
+        range(10, 13),
+        range(13, 16),
+        range(16, 19),
+    )
 
 
 class SVP64Instruction(PrefixedInstruction):
     """SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
     class Prefix(PrefixedInstruction.Prefix):
-        class RM(_Mapping):
-            _: _Field = range(24)
-            mmode: _Field = (0,)
-            mask: _Field = range(1, 4)
-            elwidth: _Field = range(4, 6)
-            ewsrc: _Field = range(6, 8)
-            subvl: _Field = range(8, 10)
-            extra: _Field = range(10, 19)
-            mode: _Field = range(19, 24)
-            extra2: _Field[4] = (
-                range(10, 12),
-                range(12, 14),
-                range(14, 16),
-                range(16, 18),
-            )
-            smask: _Field = range(16, 19)
-            extra3: _Field[3] = (
-                range(10, 13),
-                range(13, 16),
-                range(16, 19),
-            )
-
         id: _Field = (7, 9)
-        rm: RM = ((6, 8) + tuple(range(10, 32)))
+        rm: RM.remap((6, 8) + tuple(range(10, 32)))
 
     prefix: Prefix
 
-    def disassemble(self, db):
+    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))
+
+        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))
+
         record = db[self.suffix]
-        if record is None:
-            yield f".llong 0x{int(self):016x}"
-        else:
-            yield f".llong 0x{int(self):016x} # sv.{record.name}"
+        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}"
+            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):
@@ -710,9 +1158,53 @@ def parse(stream, factory):
     return tuple(map(factory, entries))
 
 
+class MarkdownDatabase:
+    def __init__(self):
+        db = {}
+        for (name, desc) in _ISA():
+            operands = []
+            if desc.regs:
+                (dynamic, *static) = desc.regs
+                operands.extend(dynamic)
+                operands.extend(static)
+            db[name] = Operands(insn=name, iterable=operands)
+        self.__db = db
+        return super().__init__()
+
+    def __iter__(self):
+        yield from self.__db.items()
+
+    def __getitem__(self, key):
+        return self.__db.__getitem__(key)
+
+
+class FieldsDatabase:
+    def __init__(self):
+        db = {}
+        df = _DecodeFields()
+        df.create_specs()
+        for (form, fields) in df.instrs.items():
+            if form in {"DQE", "TX"}:
+                continue
+            if form == "all":
+                form = "NONE"
+            db[_Form[form]] = Fields(fields)
+
+        self.__db = db
+
+        return super().__init__()
+
+    def __getitem__(self, key):
+        return self.__db.__getitem__(key)
+
+
 class PPCDatabase:
-    def __init__(self, root):
-        db = _collections.defaultdict(set)
+    def __init__(self, root, mdwndb, fieldsdb):
+        # The code below groups the instructions by section:identifier.
+        # We use the comment as an identifier, there's nothing better.
+        # The point is to capture different opcodes for the same instruction.
+        dd = _collections.defaultdict
+        records = dd(lambda: dd(set))
         path = (root / "insndb.csv")
         with open(path, "r", encoding="UTF-8") as stream:
             for section in parse(stream, Section.CSV):
@@ -723,24 +1215,72 @@ class PPCDatabase:
                 }[section.mode]
                 factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls)
                 with open(path, "r", encoding="UTF-8") as stream:
-                    db[section].update(parse(stream, factory))
+                    for insn in parse(stream, factory):
+                        records[section][insn.comment].add(insn)
+
+        db = dd(set)
+        for (section, group) in records.items():
+            for records in group.values():
+                db[section].add(PPCMultiRecord(records))
+
         self.__db = db
+        self.__mdwndb = mdwndb
+        self.__fieldsdb = fieldsdb
+
         return super().__init__()
 
     def __getitem__(self, key):
+        def exact_match(key, record):
+            for name in record.names:
+                if name == key:
+                    return True
+
+            return False
+
+        def Rc_match(key, record):
+            if not key.endswith("."):
+                return False
+
+            if not record.Rc is _RCOE.RC:
+                return False
+
+            return exact_match(key[:-1], record)
+
+        def LK_match(key, record):
+            if not key.endswith("l"):
+                return False
+
+            if "lk" not in record.flags:
+                return False
+
+            return exact_match(key[:-1], record)
+
+        def AA_match(key, record):
+            if not key.endswith("a"):
+                return False
+
+            if record.intop not in {_MicrOp.OP_B, _MicrOp.OP_BC}:
+                return False
+
+            if self.__mdwndb[key]["AA"] is None:
+                return False
+
+            return (exact_match(key[:-1], record) or
+                LK_match(key[:-1], record))
+
         for (section, records) in self.__db.items():
             for record in records:
-                for name in record.names:
-                    if ((key == name) or
-                            ((record.rc is _RC.RC) and
-                                key.endswith(".") and
-                                name == key[:-1])):
-                        return (section, record)
+                if (exact_match(key, record) or
+                        Rc_match(key, record) or
+                        LK_match(key, record) or
+                        AA_match(key, record)):
+                    return (section, record)
+
         return (None, None)
 
 
 class SVP64Database:
-    def __init__(self, root):
+    def __init__(self, root, ppcdb):
         db = set()
         pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
         for (prefix, _, names) in _os.walk(root):
@@ -749,53 +1289,23 @@ class SVP64Database:
                 path = (prefix / _pathlib.Path(name))
                 with open(path, "r", encoding="UTF-8") as stream:
                     db.update(parse(stream, SVP64Record.CSV))
+
         self.__db = {record.name:record for record in db}
+        self.__ppcdb = ppcdb
+
         return super().__init__()
 
     def __getitem__(self, key):
-        for name in key:
+        (_, record) = self.__ppcdb[key]
+        if record is None:
+            return None
+
+        for name in record.names:
             record = self.__db.get(name, None)
             if record is not None:
                 return record
-        return None
-
-
-class FieldsDatabase:
-    def __init__(self):
-        db = {}
-        df = _DecodeFields()
-        df.create_specs()
-        for (form, fields) in df.instrs.items():
-            if form in {"DQE", "TX"}:
-                continue
-            if form == "all":
-                form = "NONE"
-            db[_Form[form]] = Fields(fields)
-        self.__db = db
-        return super().__init__()
 
-    def __getitem__(self, key):
-        return self.__db.__getitem__(key)
-
-
-class MarkdownDatabase:
-    def __init__(self):
-        db = {}
-        for (name, desc) in _ISA():
-            operands = []
-            if desc.regs:
-                (dynamic, *static) = desc.regs
-                operands.extend(dynamic)
-                operands.extend(static)
-            db[name] = Operands(operands)
-        self.__db = db
-        return super().__init__()
-
-    def __iter__(self):
-        yield from self.__db.items()
-
-    def __getitem__(self, key):
-        return self.__db.__getitem__(key)
+        return None
 
 
 class Database:
@@ -804,15 +1314,15 @@ class Database:
 
         mdwndb = MarkdownDatabase()
         fieldsdb = FieldsDatabase()
-        svp64db = SVP64Database(root)
-        ppcdb = PPCDatabase(root)
+        ppcdb = PPCDatabase(root=root, mdwndb=mdwndb, fieldsdb=fieldsdb)
+        svp64db = SVP64Database(root=root, ppcdb=ppcdb)
 
         db = set()
         for (name, operands) in mdwndb:
             (section, ppc) = ppcdb[name]
             if ppc is None:
                 continue
-            svp64 = svp64db[ppc.names]
+            svp64 = svp64db[name]
             fields = fieldsdb[ppc.form]
             record = Record(name=name,
                 section=section, ppc=ppc, svp64=svp64,