power_insn: major refactoring and cleanup
authorDmitry Selyutin <ghostmansd@gmail.com>
Mon, 19 Dec 2022 22:34:00 +0000 (01:34 +0300)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 2 Jun 2023 18:51:16 +0000 (19:51 +0100)
src/openpower/decoder/power_insn.py
src/openpower/sv/sv_analysis.py
src/openpower/sv/sv_binutils_fptrans.py
src/openpower/sv/trans/pysvp64dis.py
src/openpower/sv/trans/svp64.py
src/openpower/sv/trans/test_pysvp64dis.py
src/openpower/sv/trans/test_pysvp64dis_branch.py

index e90b987e9aaa1c2affc6bc7ae5f7d4e605494d6d..5679a8804e9c06a9f55618e1aa8042b161486e40 100644 (file)
@@ -3,7 +3,6 @@ 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
@@ -56,7 +55,7 @@ from openpower.decoder.pseudo.pagereader import ISA as _ISA
 
 
 @_functools.total_ordering
-class Verbosity(_enum.Enum):
+class Style(_enum.Enum):
     SHORT = _enum.auto()
     NORMAL = _enum.auto()
     VERBOSE = _enum.auto()
@@ -170,6 +169,8 @@ class Opcode:
         return ((self.value & self.mask) == (key & self.mask))
 
 
+@_functools.total_ordering
+@_dataclasses.dataclass(eq=True, frozen=True)
 class IntegerOpcode(Opcode):
     def __init__(self, value):
         if value.startswith("0b"):
@@ -183,6 +184,8 @@ class IntegerOpcode(Opcode):
         return super().__init__(value=value, mask=mask)
 
 
+@_functools.total_ordering
+@_dataclasses.dataclass(eq=True, frozen=True)
 class PatternOpcode(Opcode):
     def __init__(self, pattern):
         if not isinstance(pattern, str):
@@ -222,13 +225,13 @@ class PPCRecord:
                 "sgl pipe",
             )
 
-    class Flags(frozenset, metaclass=FlagsMeta):
+    class Flags(tuple, metaclass=FlagsMeta):
         def __new__(cls, flags=frozenset()):
             flags = frozenset(flags)
             diff = (flags - frozenset(cls))
             if diff:
                 raise ValueError(flags)
-            return super().__new__(cls, flags)
+            return super().__new__(cls, sorted(flags))
 
     opcode: Opcode
     comment: str
@@ -297,7 +300,8 @@ class PPCRecord:
 class PPCMultiRecord(tuple):
     def __getattr__(self, attr):
         if attr == "opcode":
-            raise AttributeError(attr)
+            if len(self) != 1:
+                raise AttributeError(attr)
         return getattr(self[0], attr)
 
 
@@ -448,18 +452,25 @@ class SVP64Record:
     extra_reg_cr_in2 = property(_functools.partial(extra_reg, key="cr_in2"))
     extra_reg_cr_out = property(_functools.partial(extra_reg, key="cr_out"))
 
-    @property
-    def cr_3bit(self):
-        regtype = None
+    @cached_property
+    def extra_CR(self):
+        extra = None
         for idx in range(0, 4):
-            for entry in self.svp64.extra[idx]:
+            for entry in self.extra[idx]:
                 if entry.regtype is _SVExtraRegType.DST:
-                    if regtype is not None:
+                    if extra is not None:
                         raise ValueError(self.svp64)
-                    regtype = _RegType(entry.reg)
-        if regtype not in (_RegType.CR_5BIT, _RegType.CR_3BIT):
+                    extra = entry
+                    break
+
+        if _RegType(extra.reg) not in (_RegType.CR_3BIT, _RegType.CR_5BIT):
             raise ValueError(self.svp64)
-        return (regtype is _RegType.CR_3BIT)
+
+        return extra
+
+    @cached_property
+    def extra_CR_3bit(self):
+        return (_RegType(self.extra_CR.reg) is _RegType.CR_3BIT)
 
 
 class BitSel:
@@ -664,10 +675,10 @@ class Operands:
                             cls = GPROperand
                         elif regtype is _RegType.FPR:
                             cls = FPROperand
-                        elif regtype is _RegType.CR_5BIT:
-                            cls = CR5Operand
                         elif regtype is _RegType.CR_3BIT:
                             cls = CR3Operand
+                        elif regtype is _RegType.CR_5BIT:
+                            cls = CR5Operand
 
                 if imm_name is not None:
                     mapping[imm_name] = (imm_cls, {"name": imm_name})
@@ -715,8 +726,8 @@ class Operands:
 
 
 class Arguments(tuple):
-    def __new__(cls, arguments, operands):
-        iterable = iter(tuple(arguments))
+    def __new__(cls, fields, operands):
+        iterable = iter(tuple(fields))
         operands = iter(tuple(operands))
         arguments = []
 
@@ -731,7 +742,8 @@ class Arguments(tuple):
             except StopIteration:
                 raise ValueError("operands count mismatch")
 
-            if isinstance(operand, ImmediateOperand):
+            (op_cls, _) = operand
+            if issubclass(op_cls, ImmediateOperand):
                 argument = argument.replace("(", " ").replace(")", "")
                 (imm_argument, _, argument) = argument.partition(" ")
                 try:
@@ -786,81 +798,76 @@ class Record:
         rhs = (min(other.opcodes), other.name)
         return (lhs < rhs)
 
-    @cached_property
-    def PO(self):
-        PO = self.section.opcode
-        if PO is None:
-            assert len(self.ppc) == 1
-            PO = self.ppc[0].opcode
-
-        return POStaticOperand(record=self,
-            name="PO", value=int(PO.value), mask=int(PO.mask))
-
-    @cached_property
-    def XO(self):
-        def XO(ppc):
-            XO = ppc.opcode
-            PO = self.section.opcode
-            if PO is None:
-                PO = XO
-                XO = None
-
-            if XO is None:
-                return XOStaticOperand(record=self,
-                    name="XO", value=0, mask=0)
-            else:
-                return XOStaticOperand(record=self,
-                    name="XO", value=int(XO.value), mask=int(XO.mask))
-
-        return tuple(dict.fromkeys(map(XO, self.ppc)))
-
-    @cached_property
+    @property
     def static_operands(self):
-        operands = []
-
-        operands.append(self.PO)
-        operands.extend(self.XO)
-
-        for (cls, kwargs) in self.mdwn.operands.static:
-            operands.append(cls(record=self, **kwargs))
+        def XO(ppc):
+            return (XOStaticOperand, {
+                "span": self.section.bitsel,
+                "value": ppc.opcode.value,
+            })
 
-        return tuple(operands)
+        yield (POStaticOperand, {"value": self.PO})
+        yield from map(XO, self.ppc)
+        yield from self.mdwn.operands.static
 
-    @cached_property
+    @property
     def dynamic_operands(self):
-        operands = []
-
-        for (cls, kwargs) in self.mdwn.operands.dynamic:
-            operands.append(cls(record=self, **kwargs))
-
-        return tuple(operands)
+        yield from self.mdwn.operands.dynamic
 
-    @property
+    @cached_property
     def opcodes(self):
-        bits = 32
-        if self.svp64 is not None:
-            bits = 64
-        origin_value = ([0] * bits)
-        origin_mask = ([0] * bits)
-
-        for operand in ((self.PO,) + tuple(self.static_operands)):
-            for (src, dst) in enumerate(reversed(operand.span)):
-                origin_value[dst] = int((operand.value & (1 << src)) != 0)
-                origin_mask[dst] = 1
+        def binary(mapping):
+            return int("".join(str(int(mapping[bit])) for bit in sorted(mapping)), 2)
+
+        def PO_XO(value, mask, opcode, bits):
+            value = dict(value)
+            mask = dict(mask)
+            for (src, dst) in enumerate(reversed(bits)):
+                value[dst] = ((opcode.value & (1 << src)) != 0)
+                mask[dst] = ((opcode.mask & (1 << src)) != 0)
+            return (value, mask)
+
+        def PO(value, mask, opcode, bits):
+            return PO_XO(value=value, mask=mask, opcode=opcode, bits=bits)
+
+        def XO(value, mask, opcode, bits):
+            (value, mask) = PO_XO(value=value, mask=mask, opcode=opcode, bits=bits)
+            for (op_cls, op_kwargs) in self.mdwn.operands.static:
+                operand = op_cls(record=self, **op_kwargs)
+                for (src, dst) in enumerate(reversed(operand.span)):
+                    value[dst] = ((operand.value & (1 << src)) != 0)
+                    mask[dst] = True
+            return (value, mask)
+
+        pairs = []
+        value = {bit:False for bit in range(32)}
+        mask = {bit:False for bit in range(32)}
+        if self.section.opcode is not None:
+            (value, mask) = PO(value=value, mask=mask,
+                opcode=self.section.opcode, bits=range(0, 6))
+        for ppc in self.ppc:
+            pairs.append(XO(value=value, mask=mask,
+                opcode=ppc.opcode, bits=self.section.bitsel))
+
+        result = []
+        for (value, mask) in pairs:
+            value = Opcode.Value(binary(value))
+            mask = Opcode.Mask(binary(mask))
+            result.append(Opcode(value=value, mask=mask))
+
+        return tuple(result)
 
-        def opcode(XO):
-            value = list(origin_value)
-            mask = list(origin_mask)
-            for (src, dst) in enumerate(reversed(XO.span)):
-                value[dst] = int((XO.value & (1 << src)) != 0)
-                mask[dst] = 1
-
-            value = Opcode.Value(int(("".join(map(str, value))), 2))
-            mask = Opcode.Mask(int(("".join(map(str, mask))), 2))
-
-            return Opcode(value=value, mask=mask)
+    @cached_property
+    def PO(self):
+        opcode = self.section.opcode
+        if opcode is None:
+            opcode = self.ppc[0].opcode
+            if isinstance(opcode, PatternOpcode):
+                value = int(opcode.value)
+                bits = opcode.value.bit_length()
+                return int(_SelectableInt(value=value, bits=bits)[0:6])
 
-        return tuple(dict.fromkeys(map(opcode, self.XO)))
+        return int(opcode.value)
 
     def match(self, key):
         for opcode in self.opcodes:
@@ -936,20 +943,25 @@ class Record:
         return self["Rc"].value
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class Operand:
-    name: str
-    record: Record = _dataclasses.field(repr=False)
+    def __init__(self, record, name):
+        self.__record = record
+        self.__name = name
 
-    def __post_init__(self):
-        pass
+    def __repr__(self):
+        return f"{self.__class__.__name__}({self.name})"
+
+    @property
+    def name(self):
+        return self.__name
+
+    @property
+    def record(self):
+        return self.__record
 
     @cached_property
     def span(self):
-        span = self.record.fields[self.name]
-        if self.record.svp64 is not None:
-            span = tuple(map(lambda bit: (bit + 32), span))
-        return span
+        return self.record.fields[self.name]
 
     def assemble(self, value, insn):
         span = self.span
@@ -960,18 +972,17 @@ class Operand:
         insn[span] = value
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
-        raise NotImplementedError
+            style=Style.NORMAL, indent=""):
+        raise NotImplementedError()
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class DynamicOperand(Operand):
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
+            style=Style.NORMAL, indent=""):
         span = self.span
         value = insn[span]
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             span = map(str, span)
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
@@ -980,7 +991,6 @@ class DynamicOperand(Operand):
             yield str(int(value))
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class SignedOperand(DynamicOperand):
     def assemble(self, value, insn):
         if isinstance(value, str):
@@ -994,13 +1004,13 @@ class SignedOperand(DynamicOperand):
         insn[span] = value
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
+            style=Style.NORMAL, indent=""):
         span = self.span
         value = insn[span].to_signed_int()
         sign = "-" if (value < 0) else ""
         value = abs(value)
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             span = map(str, span)
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{sign}{value}"
@@ -1009,19 +1019,27 @@ class SignedOperand(DynamicOperand):
             yield f"{sign}{value}"
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class StaticOperand(Operand):
-    value: int
+    def __init__(self, record, name, value):
+        self.__value = value
+        return super().__init__(record=record, name=name)
+
+    def __repr__(self):
+        return f"{self.__class__.__name__}({self.name}, value={self.value})"
+
+    @property
+    def value(self):
+        return self.__value
 
     def assemble(self, insn):
         return super().assemble(value=self.value, insn=insn)
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
+            style=Style.NORMAL, indent=""):
         span = self.span
         value = insn[span]
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             span = map(str, span)
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
@@ -1030,77 +1048,55 @@ class StaticOperand(Operand):
             yield str(int(value))
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class POStaticOperand(StaticOperand):
-    mask: int
+class SpanStaticOperand(StaticOperand):
+    def __init__(self, record, name, value, span):
+        self.__span = span
+        return super().__init__(record=record, name=name, value=value)
 
-    @cached_property
+    @property
     def span(self):
-        span = tuple(range(0, 6))
-        if self.record.svp64 is not None:
-            span = tuple(map(lambda bit: (bit + 32), span))
-        return span
+        return self.__span
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class XOStaticOperand(StaticOperand):
-    mask: int
-
-    def __post_init__(self):
-        if self.record.section.opcode is None:
-            assert self.value == 0
-            assert self.mask == 0
-            object.__setattr__(self, "span", ())
-            return
+class POStaticOperand(SpanStaticOperand):
+    def __init__(self, record, value):
+        return super().__init__(record=record, name="PO", value=value, span=range(0, 6))
+
 
-        bits = self.record.section.bitsel
-        value = _SelectableInt(value=self.value, bits=len(bits))
+class XOStaticOperand(SpanStaticOperand):
+    def __init__(self, record, value, span):
+        bits = record.section.bitsel
+        value = _SelectableInt(value=value, bits=len(bits))
         span = dict(zip(bits, range(len(bits))))
         span_rev = {value:key for (key, value) in span.items()}
 
-        # This part is tricky: we could have used self.record.static_operands,
+        # This part is tricky: we could have used record.static_operands,
         # but this would cause an infinite recursion, since this code is called
-        # from the self.record.static_operands method already.
+        # from the record.static_operands method already.
         operands = []
-        operands.extend(self.record.mdwn.operands.static)
-        operands.extend(self.record.mdwn.operands.dynamic)
+        operands.extend(record.mdwn.operands.static)
+        operands.extend(record.mdwn.operands.dynamic)
         for (cls, kwargs) in operands:
-            operand = cls(record=self.record, **kwargs)
+            operand = cls(record=record, **kwargs)
             for idx in operand.span:
                 rev = span.pop(idx, None)
                 if rev is not None:
                     span_rev.pop(rev, None)
 
-        # This part is simpler: we drop bits which are not in the mask.
-        for bit in tuple(span.values()):
-            rev = (len(bits) - bit - 1)
-            if ((self.mask & (1 << bit)) == 0):
-                idx = span_rev.pop(rev, None)
-                if idx is not None:
-                    span.pop(idx, None)
-
         value = int(_selectconcat(*(value[bit] for bit in span.values())))
         span = tuple(span.keys())
-        if self.record.svp64 is not None:
-            span = tuple(map(lambda bit: (bit + 32), span))
-
-        object.__setattr__(self, "value", value)
-        object.__setattr__(self, "span", span)
 
-        return super().__post_init__()
+        return super().__init__(record=record, name="XO", value=value, span=span)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class ImmediateOperand(DynamicOperand):
     pass
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class SignedImmediateOperand(SignedOperand, ImmediateOperand):
     pass
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class NonZeroOperand(DynamicOperand):
     def assemble(self, value, insn):
         if isinstance(value, str):
@@ -1111,11 +1107,11 @@ class NonZeroOperand(DynamicOperand):
         return super().assemble(value=value, insn=insn)
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
+            style=Style.NORMAL, indent=""):
         span = self.span
         value = insn[span]
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             span = map(str, span)
             yield f"{indent}{self.name}"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
@@ -1124,7 +1120,6 @@ class NonZeroOperand(DynamicOperand):
             yield str(int(value) + 1)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class ExtendableOperand(DynamicOperand):
     def sv_spec_enter(self, value, span):
         return (value, span)
@@ -1215,7 +1210,7 @@ class ExtendableOperand(DynamicOperand):
         return _SVExtra.NONE
 
     def remap(self, value, vector):
-        raise NotImplementedError
+        raise NotImplementedError()
 
     def assemble(self, value, insn, prefix):
         vector = False
@@ -1252,10 +1247,10 @@ class ExtendableOperand(DynamicOperand):
         return super().assemble(value=value, insn=insn)
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, prefix="", indent=""):
+            style=Style.NORMAL, prefix="", indent=""):
         (vector, value, span) = self.spec(insn=insn)
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             mode = "vector" if vector else "scalar"
             yield f"{indent}{self.name} ({mode})"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
@@ -1272,7 +1267,6 @@ class ExtendableOperand(DynamicOperand):
             yield f"{vector}{prefix}{int(value)}"
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class SimpleRegisterOperand(ExtendableOperand):
     def remap(self, value, vector):
         if vector:
@@ -1305,41 +1299,36 @@ class SimpleRegisterOperand(ExtendableOperand):
         return (value, extra)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class GPROperand(SimpleRegisterOperand):
     def assemble(self, value, insn):
         return super().assemble(value=value, insn=insn, prefix="r")
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
-        prefix = "" if (verbosity <= Verbosity.SHORT) else "r"
+            style=Style.NORMAL, indent=""):
+        prefix = "" if (style <= Style.SHORT) else "r"
         yield from super().disassemble(prefix=prefix, insn=insn,
-            verbosity=verbosity, indent=indent)
+            style=style, indent=indent)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class GPRPairOperand(GPROperand):
     pass
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class FPROperand(SimpleRegisterOperand):
     def assemble(self, value, insn):
         return super().assemble(value=value, insn=insn, prefix="f")
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
-        prefix = "" if (verbosity <= Verbosity.SHORT) else "f"
+            style=Style.NORMAL, indent=""):
+        prefix = "" if (style <= Style.SHORT) else "f"
         yield from super().disassemble(prefix=prefix, insn=insn,
-            verbosity=verbosity, indent=indent)
+            style=style, indent=indent)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class FPRPairOperand(FPROperand):
     pass
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class ConditionRegisterFieldOperand(ExtendableOperand):
     def pattern(name_pattern):
         (name, pattern) = name_pattern
@@ -1375,34 +1364,34 @@ class ConditionRegisterFieldOperand(ExtendableOperand):
 
     def remap(self, value, vector, regtype):
         if regtype is _RegType.CR_5BIT:
-            subvalue = (value & 0x3)
+            subvalue = (value & 0b11)
             value >>= 2
 
         if vector:
-            extra = (value & 0xf)
+            extra = (value & 0b1111)
             value >>= 4
         else:
             extra = (value >> 3)
-            value &= 0x7
+            value &= 0b111
 
         if self.record.etype is _SVEType.EXTRA2:
             if vector:
-                assert (extra & 0x7) == 0, \
+                assert (extra & 0b111) == 0, \
                     "vector CR cannot fit into EXTRA2"
-                extra = (0x2 | (extra >> 3))
+                extra = (0b10 | (extra >> 3))
             else:
                 assert (extra >> 1) == 0, \
                     "scalar CR cannot fit into EXTRA2"
-                extra &= 0x1
+                extra &= 0b01
         elif self.record.etype is _SVEType.EXTRA3:
             if vector:
-                assert (extra & 0x3) == 0, \
+                assert (extra & 0b11) == 0, \
                     "vector CR cannot fit into EXTRA3"
-                extra = (0x4 | (extra >> 2))
+                extra = (0b100 | (extra >> 2))
             else:
                 assert (extra >> 2) == 0, \
                     "scalar CR cannot fit into EXTRA3"
-                extra &= 0x3
+                extra &= 0b11
 
         if regtype is _RegType.CR_5BIT:
             value = ((value << 2) | subvalue)
@@ -1434,13 +1423,17 @@ class ConditionRegisterFieldOperand(ExtendableOperand):
                     value = ((CR * N) + BIT)
                     break
 
+            value = str(value)
+            if vector:
+                value = f"*{value}"
+
         return super().assemble(value=value, insn=insn, prefix="cr")
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, prefix="", indent=""):
+            style=Style.NORMAL, prefix="", indent=""):
         (vector, value, span) = self.spec(insn=insn)
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             mode = "vector" if vector else "scalar"
             yield f"{indent}{self.name} ({mode})"
             yield f"{indent}{indent}{int(value):0{value.bits}b}"
@@ -1454,29 +1447,27 @@ class ConditionRegisterFieldOperand(ExtendableOperand):
                     yield f"{indent}{indent}{etype}{extra_idx!r}"
         else:
             vector = "*" if vector else ""
-            cr = int(value >> 2)
-            cc = int(value & 3)
-            cond = ("lt", "gt", "eq", "so")[cc]
-            if verbosity >= Verbosity.NORMAL:
-                if cr != 0:
+            CR = int(value >> 2)
+            CC = int(value & 3)
+            cond = ("lt", "gt", "eq", "so")[CC]
+            if style >= Style.NORMAL:
+                if CR != 0:
                     if isinstance(insn, SVP64Instruction):
-                        yield f"{vector}cr{cr}.{cond}"
+                        yield f"{vector}cr{CR}.{cond}"
                     else:
-                        yield f"4*cr{cr}+{cond}"
+                        yield f"4*cr{CR}+{cond}"
                 else:
                     yield cond
             else:
                 yield f"{vector}{prefix}{int(value)}"
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class CR3Operand(ConditionRegisterFieldOperand):
     def remap(self, value, vector):
         return super().remap(value=value, vector=vector,
             regtype=_RegType.CR_3BIT)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class CR5Operand(ConditionRegisterFieldOperand):
     def remap(self, value, vector):
         return super().remap(value=value, vector=vector,
@@ -1492,22 +1483,32 @@ class CR5Operand(ConditionRegisterFieldOperand):
         return (value, span)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class EXTSOperand(SignedOperand):
     field: str # real name to report
     nz: int = 0 # number of zeros
     fmt: str = "d" # integer formatter
 
-    def __post_init__(self):
-        if not self.field:
-            object.__setattr__(self, "field", self.name)
+    def __init__(self, record, name, field, nz=0, fmt="d"):
+        self.__field = field
+        self.__nz = nz
+        self.__fmt = fmt
+        return super().__init__(record=record, name=name)
+
+    @property
+    def field(self):
+        return self.__field
+
+    @property
+    def nz(self):
+        return self.__nz
+
+    @property
+    def fmt(self):
+        return self.__fmt
 
     @cached_property
     def span(self):
-        span = self.record.fields[self.field]
-        if self.record.svp64 is not None:
-            span = tuple(map(lambda bit: (bit + 32), span))
-        return span
+        return self.record.fields[self.field]
 
     def assemble(self, value, insn):
         span = self.span
@@ -1516,13 +1517,13 @@ class EXTSOperand(SignedOperand):
         insn[span] = (value >> self.nz)
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
+            style=Style.NORMAL, indent=""):
         span = self.span
         value = insn[span].to_signed_int()
         sign = "-" if (value < 0) else ""
         value = (abs(value) << self.nz)
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             span = (tuple(map(str, span)) + (("{0}",) * self.nz))
             zeros = ("0" * self.nz)
             hint = f"{self.name} = EXTS({self.field} || {zeros})"
@@ -1534,54 +1535,47 @@ class EXTSOperand(SignedOperand):
             yield f"{sign}{value:{self.fmt}}"
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class TargetAddrOperand(EXTSOperand):
-    nz: int = 2
-    fmt: str = "#x"
+    def __init__(self, record, name, field):
+        return super().__init__(record=record, name=name, field=field, nz=2, fmt="#x")
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class TargetAddrOperandLI(TargetAddrOperand):
-    field: str = "LI"
+    def __init__(self, record, name):
+        return super().__init__(record=record, name=name, field="LI")
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class TargetAddrOperandBD(TargetAddrOperand):
-    field: str = "BD"
+    def __init__(self, record, name):
+        return super().__init__(record=record, name=name, field="BD")
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class EXTSOperandDS(EXTSOperand, ImmediateOperand):
-    field: str = "DS"
-    nz: int = 2
+    def __init__(self, record, name):
+        return super().__init__(record=record, name=name, field="DS", nz=2)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class EXTSOperandDQ(EXTSOperand, ImmediateOperand):
-    field: str = "DQ"
-    nz: int = 4
+    def __init__(self, record, name):
+        return super().__init__(record=record, name=name, field="DQ", nz=4)
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
 class DOperandDX(SignedOperand):
     @cached_property
     def span(self):
         cls = lambda name: DynamicOperand(record=self.record, name=name)
         operands = map(cls, ("d0", "d1", "d2"))
         spans = map(lambda operand: operand.span, operands)
-        span = sum(spans, tuple())
-        if self.record.svp64 is not None:
-            span = tuple(map(lambda bit: (bit + 32), span))
-        return span
+        return sum(spans, tuple())
 
     def disassemble(self, insn,
-            verbosity=Verbosity.NORMAL, indent=""):
+            style=Style.NORMAL, indent=""):
         span = self.span
         value = insn[span].to_signed_int()
         sign = "-" if (value < 0) else ""
         value = abs(value)
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             yield f"{indent}D"
             mapping = {
                 "d0": "[0:9]",
@@ -1653,7 +1647,7 @@ class Instruction(_Mapping):
             self.dynamic_operands(db=db)))
 
         static_operands = []
-        for (name, value) in self.static_operands(db=db):
+        for (name, value) in record.static_operands:
             static_operands.append(f"{name}={value}")
 
         operands = ""
@@ -1666,16 +1660,23 @@ class Instruction(_Mapping):
 
         return f"{prefix}{record.name}{operands}"
 
-    def dynamic_operands(self, db, verbosity=Verbosity.NORMAL):
+    def static_operands(self, db):
+        record = self.record(db=db, entry=self)
+        for (op_cls, op_kwargs) in record.static_operands:
+            operand = op_cls(record=record, **op_kwargs)
+            yield (operand.name, operand.value)
+
+    def dynamic_operands(self, db, style=Style.NORMAL):
         record = self.record(db=db, entry=self)
 
         imm = False
         imm_name = ""
         imm_value = ""
-        for operand in record.dynamic_operands:
+        for (op_cls, op_kwargs) in record.dynamic_operands:
+            operand = op_cls(record=record, **op_kwargs)
             name = operand.name
             value = " ".join(operand.disassemble(insn=self,
-                verbosity=min(verbosity, Verbosity.NORMAL)))
+                style=min(style, Style.NORMAL)))
             if imm:
                 name = f"{imm_name}({name})"
                 value = f"{imm_value}({value})"
@@ -1687,19 +1688,14 @@ class Instruction(_Mapping):
             if not imm:
                 yield (name, value)
 
-    def static_operands(self, db):
-        record = self.record(db=db, entry=self)
-        for operand in record.static_operands:
-            yield (operand.name, operand.value)
-
     @classmethod
-    def assemble(cls, db, opcode, arguments=None):
-        raise NotImplementedError(f"{cls.__name__}.assemble")
+    def assemble(cls, db, entry, arguments=None):
+        raise NotImplementedError()
 
     def disassemble(self, db,
             byteorder="little",
-            verbosity=Verbosity.NORMAL):
-        raise NotImplementedError
+            style=Style.NORMAL):
+        raise NotImplementedError()
 
 
 class WordInstruction(Instruction):
@@ -1724,25 +1720,38 @@ class WordInstruction(Instruction):
         return "".join(map(str, bits))
 
     @classmethod
-    def assemble(cls, db, opcode, arguments=None):
+    def assemble(cls, db, entry, arguments=None, specifiers=None):
         if arguments is None:
             arguments = ()
+        if specifiers is None:
+            specifiers = ()
 
-        record = cls.record(db=db, entry=opcode)
+        record = cls.record(db=db, entry=entry)
         insn = cls.integer(value=0)
-        for operand in record.static_operands:
+
+        opcode = record.section.opcode
+        if opcode is None:
+            opcode = record.ppc.opcode
+        bits = record.section.bitsel
+        for (src, dst) in enumerate(reversed(bits)):
+            insn[dst] = ((opcode.value & (1 << src)) != 0)
+
+        for (op_cls, op_kwargs) in record.static_operands:
+            operand = op_cls(record=record, **op_kwargs)
             operand.assemble(insn=insn)
 
         dynamic_operands = tuple(record.dynamic_operands)
-        for (value, operand) in Arguments(arguments, dynamic_operands):
+        for (value, (op_cls, op_kwargs)) in Arguments(arguments, dynamic_operands):
+            operand = op_cls(record=record, **op_kwargs)
             operand.assemble(value=value, insn=insn)
 
         return insn
 
     def disassemble(self, db,
             byteorder="little",
-            verbosity=Verbosity.NORMAL):
-        if verbosity <= Verbosity.SHORT:
+            style=Style.NORMAL,
+            compatibility=False):
+        if style <= Style.SHORT:
             blob = ""
         else:
             blob = self.bytes(byteorder=byteorder)
@@ -1755,14 +1764,14 @@ class WordInstruction(Instruction):
             return
 
         operands = tuple(map(_operator.itemgetter(1),
-            self.dynamic_operands(db=db, verbosity=verbosity)))
+            self.dynamic_operands(db=db, style=style)))
         if operands:
             operands = ",".join(operands)
             yield f"{blob}{record.name} {operands}"
         else:
             yield f"{blob}{record.name}"
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             indent = (" " * 4)
             binary = self.binary
             spec = self.spec(db=db, prefix="")
@@ -1782,7 +1791,7 @@ class WordInstruction(Instruction):
             for (cls, kwargs) in record.mdwn.operands:
                 operand = cls(record=record, **kwargs)
                 yield from operand.disassemble(insn=self,
-                    verbosity=verbosity, indent=indent)
+                    style=style, indent=indent)
             yield ""
 
 
@@ -1886,8 +1895,8 @@ class BaseRM(_Mapping):
                 3: "vec4",
             }[subvl]
 
-    def disassemble(self, verbosity=Verbosity.NORMAL):
-        if verbosity >= Verbosity.VERBOSE:
+    def disassemble(self, style=Style.NORMAL):
+        if style >= Style.VERBOSE:
             indent = (" " * 4)
             for (name, span) in self.traverse(path="RM"):
                 value = self.storage[span]
@@ -2311,7 +2320,7 @@ class CROpBaseRM(BaseRM):
 
 
 class CROpSimpleRM(PredicateBaseRM, ZZCombinedBaseRM, CROpBaseRM):
-    """cr_op: simple mode"""
+    """crop: simple mode"""
     RG: BaseRM[20]
     dz: BaseRM[22]
     sz: BaseRM[23]
@@ -2322,36 +2331,55 @@ class CROpSimpleRM(PredicateBaseRM, ZZCombinedBaseRM, CROpBaseRM):
 
         yield from super().specifiers(record=record)
 
+
 class CROpMRRM(MRBaseRM, ZZCombinedBaseRM, CROpBaseRM):
-    """cr_op: scalar reduce mode (mapreduce), SUBVL=1"""
+    """crop: scalar reduce mode (mapreduce), SUBVL=1"""
     RG: BaseRM[20]
     dz: BaseRM[22]
     sz: BaseRM[23]
 
 
-class CROpFF3RM(FFPRRc1BaseRM, VLiBaseRM, ZZBaseRM, PredicateBaseRM, CROpBaseRM):
-    """cr_op: ffirst 3-bit mode"""
+class CROpFF3RM(FFPRRc0BaseRM, PredicateBaseRM, VLiBaseRM, DZBaseRM, SZBaseRM, CROpBaseRM):
+    """crop: ffirst 3-bit mode"""
+    RC1: BaseRM[19]
     VLi: BaseRM[20]
     inv: BaseRM[21]
-    CR: BaseRM[22, 23]
-    zz: BaseRM[6]
-    sz: BaseRM[6]
-    dz: BaseRM[6]
+    dz: BaseRM[22]
+    sz: BaseRM[23]
 
     def specifiers(self, record):
         yield from super().specifiers(record=record, mode="ff")
 
 
-class CROpFF5RM(FFPRRc0BaseRM, PredicateBaseRM,
-        VLiBaseRM, ZZCombinedBaseRM, CROpBaseRM):
+# FIXME: almost everything in this class contradicts the specs.
+# However, this is the direct translation of the pysvp64asm code.
+# Please revisit this code; there is an inactive sketch below.
+class CROpFF5RM(FFPRRc1BaseRM, PredicateBaseRM, VLiBaseRM, CROpBaseRM):
     """cr_op: ffirst 5-bit mode"""
     VLi: BaseRM[20]
     inv: BaseRM[21]
-    RC1: BaseRM[19] # cheat: set RC=1 based on ffirst mode being set
+    CR: BaseRM[22, 23]
     dz: BaseRM[22]
     sz: BaseRM[23]
 
     def specifiers(self, record):
+        # FIXME: this is a slightly hackish way.
+        # Ideally the class should be generated.
+        if False:
+            span = None
+            for (op_cls, op_kwargs) in record.dynamic_operands:
+                operand = op_cls(record=record, **op_kwargs)
+                if operand.name == record.svp64.extra_CR.reg.name:
+                    span = tuple(map(lambda bit: (bit + 32), operand.span))
+                    break
+
+            value = int(self.storage[span])
+            CR = int(value >> 2)
+            CC = int(value & 3)
+            if CR != 0:
+                cond = ("lt", "gt", "eq", "so")[CC]
+                yield f"ff={cond}"
+
         yield from super().specifiers(record=record, mode="ff")
 
 
@@ -2460,102 +2488,9 @@ class RM(BaseRM):
     normal: NormalRM
     ldst_imm: LDSTImmRM
     ldst_idx: LDSTIdxRM
-    cr_op: CROpRM
+    crop: CROpRM
     branch: BranchRM
 
-    def select(self, record):
-        rm = self
-        Rc = record.Rc
-
-        # the idea behind these tables is that they are now literally
-        # in identical format to insndb.csv and minor_xx.csv and can
-        # be done precisely as that.  the only thing to watch out for
-        # is the insertion of Rc=1 as a "mask/value" bit and likewise
-        # regtype detection (3-bit BF/BFA, 5-bit BA/BB/BT) also inserted
-        # as the LSB.
-        table = None
-        if record.svp64.mode is _SVMode.NORMAL:
-            # concatenate mode 5-bit with Rc (LSB) then do a mask/map search
-            #    mode  Rc  mask  Rc  member
-            table = (
-                (0b000000, 0b111000, "simple"), # simple     (no Rc)
-                (0b001000, 0b111000, "mr"),     # mapreduce  (no Rc)
-                (0b010001, 0b110001, "ffrc1"),  # ffirst,     Rc=1
-                (0b010000, 0b110001, "ffrc0"),  # ffirst,     Rc=0
-                (0b100000, 0b110000, "sat"),    # saturation (no Rc)
-                (0b110000, 0b110001, "prrc0"),  # predicate,  Rc=0
-                (0b110001, 0b110001, "prrc1"),  # predicate,  Rc=1
-            )
-            rm = rm.normal
-            search = ((int(rm.mode) << 1) | Rc)
-
-        elif record.svp64.mode is _SVMode.LDST_IMM:
-            # concatenate mode 5-bit with Rc (LSB) then do a mask/map search
-            #    mode  Rc  mask  Rc  member
-            # ironically/coincidentally this table is identical to NORMAL
-            # mode except reserved in place of mr
-            table = (
-                (0b000000, 0b111000, "simple"), # simple     (no Rc)
-                (0b001000, 0b111000, "post"),   # post       (no Rc)
-                (0b010001, 0b110001, "ffrc1"),  # ffirst,     Rc=1
-                (0b010000, 0b110001, "ffrc0"),  # ffirst,     Rc=0
-                (0b100000, 0b110000, "sat"),    # saturation (no Rc)
-                (0b110001, 0b110001, "prrc1"),  # predicate,  Rc=1
-                (0b110000, 0b110001, "prrc0"),  # predicate,  Rc=0
-            )
-            rm = rm.ldst_imm
-            search = ((int(rm.mode) << 1) | Rc)
-
-        elif record.svp64.mode is _SVMode.LDST_IDX:
-            # concatenate mode 5-bit with Rc (LSB) then do a mask/map search
-            #    mode  Rc  mask  Rc  member
-            table = (
-                (0b000000, 0b110000, "simple"), # simple     (no Rc)
-                (0b010000, 0b110000, "stride"), # strided,   (no Rc)
-                (0b100000, 0b110000, "sat"),    # saturation (no Rc)
-                (0b110001, 0b110001, "prrc1"),  # predicate,  Rc=1
-                (0b110000, 0b110001, "prrc0"),  # predicate,  Rc=0
-            )
-            rm = rm.ldst_idx
-            search = ((int(rm.mode) << 1) | Rc)
-
-        elif record.svp64.mode is _SVMode.CROP:
-            # concatenate mode 5-bit with regtype (LSB) then do mask/map search
-            #    mode  3b  mask  3b  member
-            table = (
-                (0b000000, 0b111000, "simple"), # simple
-                (0b001000, 0b111000, "mr"),     # mapreduce
-                (0b100001, 0b100001, "ff3"),    # ffirst, 3-bit CR
-                (0b100000, 0b100000, "ff5"),    # ffirst, 5-bit CR
-            )
-            rm = rm.cr_op
-            search = ((int(rm.mode) << 1) | int(record.svp64.cr_3bit))
-
-        elif record.svp64.mode is _SVMode.BRANCH:
-            # just mode 2-bit
-            #    mode  mask  member
-            table = (
-                (0b00, 0b11, "simple"), # simple
-                (0b01, 0b11, "vls"),    # VLset
-                (0b10, 0b11, "ctr"),    # CTR mode
-                (0b11, 0b11, "ctrvls"), # CTR+VLset mode
-            )
-            # slightly weird: doesn't have a 5-bit "mode" field like others
-            rm = rm.branch
-            search = int(rm.mode.sel)
-
-        # look up in table
-        if table is not None:
-            for (value, mask, member) in table:
-                if ((value & mask) == (search & mask)):
-                    rm = getattr(rm, member)
-                    break
-
-        if rm.__class__ is self.__class__:
-            raise ValueError(self)
-
-        return rm
-
 
 @_dataclasses.dataclass(eq=True, frozen=True)
 class Specifier:
@@ -2563,18 +2498,18 @@ class Specifier:
 
     @classmethod
     def match(cls, desc, record):
-        raise NotImplementedError
+        raise NotImplementedError()
 
     def validate(self, others):
         pass
 
     def assemble(self, insn):
-        raise NotImplementedError
+        raise NotImplementedError()
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
 class SpecifierWidth(Specifier):
-    value: _SVP64Width
+    width: _SVP64Width
 
     @classmethod
     def match(cls, desc, record, etalon):
@@ -2583,9 +2518,9 @@ class SpecifierWidth(Specifier):
         value = value.strip()
         if mode != etalon:
             return None
-        value = _SVP64Width(value)
+        width = _SVP64Width(value)
 
-        return cls(record=record, mode=mode, value=value)
+        return cls(record=record, width=width)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2595,8 +2530,9 @@ class SpecifierW(SpecifierWidth):
         return super().match(desc=desc, record=record, etalon="w")
 
     def assemble(self, insn):
-        insn.prefix.rm.ewsrc = int(self.value)
-        insn.prefix.rm.elwidth = int(self.value)
+        selector = insn.select(record=self.record)
+        selector.ewsrc = self.width.value
+        selector.elwidth = self.width.value
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2606,7 +2542,8 @@ class SpecifierSW(SpecifierWidth):
         return super().match(desc=desc, record=record, etalon="sw")
 
     def assemble(self, insn):
-        insn.prefix.rm.ewsrc = int(self.value)
+        selector = insn.select(record=self.record)
+        selector.ewsrc = self.width.value
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2616,7 +2553,8 @@ class SpecifierDW(SpecifierWidth):
         return super().match(desc=desc, record=record, etalon="dw")
 
     def assemble(self, insn):
-        insn.prefix.rm.elwidth = int(self.value)
+        selector = insn.select(record=self.record)
+        selector.elwidth = self.width.value
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2633,7 +2571,8 @@ class SpecifierSubVL(Specifier):
         return cls(record=record, value=value)
 
     def assemble(self, insn):
-        insn.prefix.rm.subvl = int(self.value.value)
+        selector = insn.select(record=self.record)
+        selector.subvl = int(self.value.value)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2667,47 +2606,30 @@ class SpecifierFFPR(SpecifierPredicate):
                 _SVP64PredMode.RC1,
             ))
 
-    def assemble(self, insn):
-        rm = insn.prefix.rm
-        if rm.mode.sel != 0:
-            raise ValueError("cannot override mode")
-
+    def validate(self, others):
         if self.record.svp64.mode is _SVMode.CROP:
             if self.mode == "pr":
                 raise ValueError("crop: 'pr' mode not supported")
-            rm.mode.sel = 0b10
-            if self.record.svp64.cr_3bit:
-                rm = rm.cr_op.ff3
-            else:
-                rm = rm.cr_op.ff5
+            if (self.record.svp64.extra_CR_3bit and
+                    (self.pred.mode is not _SVP64PredMode.RC1)):
+                raise ValueError("3-bit CRs only support RC1/~RC1 BO")
+
+    def assemble(self, insn):
+        selector = insn.select(record=self.record)
+        if selector.mode.sel != 0:
+            raise ValueError("cannot override mode")
+        if self.record.svp64.mode is _SVMode.CROP:
+            selector.mode.sel = 0b10
+            selector.inv = self.pred.inv
+            if not self.record.svp64.extra_CR_3bit:
+                selector.CR = self.pred.state
         else:
-            if self.record.svp64.mode is _SVMode.NORMAL:
-                rm = rm.normal
-            elif self.record.svp64.mode is _SVMode.LDST_IMM:
-                rm = rm.ldst_imm
-            elif self.record.svp64.mode is _SVMode.LDST_IDX:
-                rm = rm.ldst_idx
-                if self.mode == "ff":
-                    raise ValueError("ld/st idx: 'ff' mode not supported")
+            selector.mode.sel = 0b01 if self.mode == "ff" else 0b11
+            selector.inv = self.pred.inv
+            if self.record.Rc:
+                selector.CR = self.pred.state
             else:
-                raise ValueError(f"{self.mode!r} not supported")
-
-            # These 2-bit values should have bits swapped
-            def bitswap(value):
-                return (((value & 0b10) >> 1) | ((value & 0b01) << 1))
-
-            rm.mode.sel = {
-                "ff": bitswap(_SVP64RMMode.FFIRST.value),
-                "pr": bitswap(_SVP64RMMode.PREDRES.value),
-            }[self.mode]
-
-            Rc = int(self.record.Rc)
-            rm = getattr(rm, f"{self.mode}rc{Rc}")
-            rm.inv = self.pred.inv
-            if Rc:
-                rm.CR = self.pred.state
-            else:
-                rm.RC1 = self.pred.state
+                selector.RC1 = self.pred.state
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2736,7 +2658,7 @@ class SpecifierMask(SpecifierPredicate):
             ))
 
     def assemble(self, insn):
-        raise NotImplementedError
+        raise NotImplementedError()
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2753,7 +2675,12 @@ class SpecifierM(SpecifierMask):
                 raise ValueError("dest-mask and predicate mask conflict")
 
     def assemble(self, insn):
-        insn.prefix.rm.mask = int(self.pred)
+        selector = insn.select(record=self.record)
+        selector.mask = int(self.pred)
+        if ((self.record.ptype is _SVPType.P2) and
+                (self.record.svp64.mode is not _SVMode.BRANCH)):
+            selector.smask = int(self.pred)
+        selector.mmode = (self.pred.mode is _SVP64PredMode.CR)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2774,11 +2701,13 @@ class SpecifierSM(SpecifierMask):
 
             if twin is None:
                 raise ValueError("missing dest-mask in CR twin predication")
-            if self.pred != twin.pred:
-                raise ValueError(f"predicate masks mismatch: {self!r} vs {twin!r}")
+            if self.pred.mode != twin.pred.mode:
+                raise ValueError(f"predicate masks mismatch: {self.pred!r} vs {twin.pred!r}")
 
     def assemble(self, insn):
-        insn.prefix.rm.smask = int(self.pred)
+        selector = insn.select(record=self.record)
+        selector.smask = int(self.pred)
+        selector.mmode = (self.pred.mode is _SVP64PredMode.CR)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2799,12 +2728,13 @@ class SpecifierDM(SpecifierMask):
 
             if twin is None:
                 raise ValueError("missing source-mask in CR twin predication")
-            if self.pred != twin.pred:
-                raise ValueError(f"predicate masks mismatch: {self!r} vs {twin!r}")
+            if self.pred.mode != twin.pred.mode:
+                raise ValueError(f"predicate masks mismatch: {self.pred!r} vs {twin.pred!r}")
 
     def assemble(self, insn):
-        insn.prefix.rm.mask = int(self.pred)
-
+        selector = insn.select(record=self.record)
+        selector.mask = int(self.pred)
+        selector.mmode = (self.pred.mode is _SVP64PredMode.CR)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2818,18 +2748,18 @@ class SpecifierZZ(Specifier):
 
     def validate(self, others):
         for spec in others:
-            # Since m=xx takes precedence (overrides) sm=xx and dm=xx,
+            # Since zz takes precedence (overrides) sz and dz,
             # treat them as mutually exclusive.
             if isinstance(spec, (SpecifierSZ, SpecifierDZ)):
                 raise ValueError("mutually exclusive predicate masks")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        if hasattr(rm, "zz"):
-            rm.zz = 1
+        selector = insn.select(record=self.record)
+        if hasattr(selector, "zz"): # this should be done in a different way
+            selector.zz = 1
         else:
-            rm.sz = 1
-            rm.dz = 1
+            selector.sz = 1
+            selector.dz = 1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2851,17 +2781,17 @@ class SpecifierXZ(Specifier):
         if self.pred.mode is _SVP64PredMode.CR:
             twin = None
             for spec in others:
-                if isinstance(spec, SpecifierSM):
+                if isinstance(spec, SpecifierXZ):
                     twin = spec
 
             if twin is None:
                 raise ValueError(f"missing {self.hint} in CR twin predication")
             if self.pred != twin.pred:
-                raise ValueError(f"predicate masks mismatch: {self!r} vs {twin!r}")
+                raise ValueError(f"predicate masks mismatch: {self.pred!r} vs {twin.pred!r}")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        setattr(rm, self.desc, 1)
+        selector = insn.select(record=self.record)
+        setattr(selector, self.desc, 1)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2873,10 +2803,11 @@ class SpecifierSZ(SpecifierXZ):
 
     def validate(self, others):
         for spec in others:
-            if isinstance(spec, SpecifierFF):
-                raise ValueError("source-zero not allowed in ff mode")
-            elif isinstance(spec, SpecifierPR):
-                raise ValueError("source-zero not allowed in pr mode")
+            if self.record.svp64.mode is not _SVMode.CROP:
+                if isinstance(spec, SpecifierFF):
+                    raise ValueError("source-zero not allowed in ff mode")
+                elif isinstance(spec, SpecifierPR):
+                    raise ValueError("source-zero not allowed in pr mode")
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2888,7 +2819,8 @@ class SpecifierDZ(SpecifierXZ):
 
     def validate(self, others):
         for spec in others:
-            if (isinstance(spec, (SpecifierFF, SpecifierPR)) and
+            if ((self.record.svp64.mode is not _SVMode.CROP) and
+                    isinstance(spec, (SpecifierFF, SpecifierPR)) and
                     (spec.pred.mode is _SVP64PredMode.RC1)):
                 mode = "ff" if isinstance(spec, SpecifierFF) else "pr"
                 raise ValueError(f"dest-zero not allowed in {mode} mode BO")
@@ -2901,13 +2833,20 @@ class SpecifierEls(Specifier):
         if desc != "els":
             return None
 
+        if record.svp64.mode not in (_SVMode.LDST_IMM, _SVMode.LDST_IDX):
+            raise ValueError("els is only valid in ld/st modes")
+
         return cls(record=record)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.els = 1
-        if self.record.svp64.mode is _SVMode.LDST_IDX:
-            rm.mode.sel = 1
+        if self.record.svp64.mode is _SVMode.LDST_IDX: # stride mode
+            insn.prefix.rm.mode[0] = 0
+            insn.prefix.rm.mode[1] = 1
+
+        selector = insn.select(record=self.record)
+        if self.record.svp64.mode is not _SVMode.LDST_IDX: # stride mode
+            selector.els = 1
+
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2928,10 +2867,10 @@ class SpecifierSEA(Specifier):
                 raise ValueError(f"sea cannot be used in ff mode")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        if rm.mode.sel not in (0b00, 0b01):
+        selector = insn.select(record=self.record)
+        if selector.mode.sel not in (0b00, 0b01):
             raise ValueError("sea is only valid for normal and els modes")
-        rm.SEA = 1
+        selector.SEA = 1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2944,12 +2883,16 @@ class SpecifierSat(Specifier):
         if desc != etalon:
             return None
 
+        if record.svp64.mode not in (_SVMode.NORMAL, _SVMode.LDST_IMM, _SVMode.LDST_IDX):
+            raise ValueError("only normal, ld/st imm and ld/st idx modes supported")
+
         return cls(record=record, desc=desc, sign=sign)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.mode.sel = 2
-        rm.sat = 1 if self.sign else 0
+        selector = insn.select(record=self.record)
+        selector.mode[0] = 0b1
+        selector.mode[1] = 0b0
+        selector.N = int(self.sign)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -2969,50 +2912,44 @@ class SpecifierSatU(SpecifierSat):
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierMR(Specifier):
+class SpecifierMapReduce(Specifier):
+    RG: bool
+
     @classmethod
-    def match(cls, desc, record):
-        if desc != "mr":
-            return None
+    def match(cls, record, RG):
+        if record.svp64.mode not in (_SVMode.NORMAL, _SVMode.CROP):
+            raise ValueError("only normal and crop modes supported")
 
-        return cls(record=record)
+        return cls(record=record, RG=RG)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.mode.sel = 0
-        rm.mr = 1
-        rm.RG = 0
+        selector = insn.select(record=self.record)
+        if self.record.svp64.mode not in (_SVMode.NORMAL, _SVMode.CROP):
+            raise ValueError("only normal and crop modes supported")
+        selector.mode[0] = 0
+        selector.mode[1] = 0
+        selector.mode[2] = 1
+        selector.RG = self.RG
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierMRR(Specifier):
+class SpecifierMR(SpecifierMapReduce):
     @classmethod
     def match(cls, desc, record):
         if desc != "mr":
             return None
 
-        return cls(record=record)
-
-    def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.mode.sel = 0
-        rm.mr = 1
-        rm.RG = 1
+        return super().match(record=record, RG=False)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierCRM(Specifier):
+class SpecifierMRR(SpecifierMapReduce):
     @classmethod
     def match(cls, desc, record):
-        if desc != "crm":
+        if desc != "mrr":
             return None
 
-        return cls(record=record)
-
-    def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.mode.sel = 0
-        rm.crm = 1
+        return super().match(record=record, RG=True)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -3022,12 +2959,11 @@ class SpecifierBranch(Specifier):
         if desc != etalon:
             return None
 
-        return cls(record=record)
-
-    def validate(self, others):
-        if self.record.svp64.mode != _SVMode.BRANCH:
+        if record.svp64.mode is not _SVMode.BRANCH:
             raise ValueError("only branch modes supported")
 
+        return cls(record=record)
+
 
 @_dataclasses.dataclass(eq=True, frozen=True)
 class SpecifierAll(SpecifierBranch):
@@ -3036,20 +2972,30 @@ class SpecifierAll(SpecifierBranch):
         return super().match(desc=desc, record=record, etalon="all")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.ALL = 1
+        selector = insn.select(record=self.record)
+        selector.ALL = 1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierSNZ(SpecifierBranch):
+class SpecifierSNZ(Specifier):
     @classmethod
     def match(cls, desc, record):
-        return super().match(desc=desc, record=record, etalon="snz")
+        if desc != "snz":
+            return None
+
+        if record.svp64.mode not in (_SVMode.BRANCH, _SVMode.CROP):
+            raise ValueError("only branch and crop modes supported")
+
+        return cls(record=record)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.sz = 1
-        rm.SNZ = 1
+        selector = insn.select(record=self.record)
+        if self.record.svp64.mode in (_SVMode.CROP, _SVMode.BRANCH):
+            selector.SNZ = 1
+            if self.record.svp64.mode is _SVMode.BRANCH:
+                selector.sz = 1
+        else:
+            raise ValueError("only branch and crop modes supported")
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -3059,8 +3005,8 @@ class SpecifierSL(SpecifierBranch):
         return super().match(desc=desc, record=record, etalon="sl")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.SL = 1
+        selector = insn.select(record=self.record)
+        selector.SL = 1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -3070,8 +3016,8 @@ class SpecifierSLu(SpecifierBranch):
         return super().match(desc=desc, record=record, etalon="slu")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.SLu = 1
+        selector = insn.select(record=self.record)
+        selector.SLu = 1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -3081,89 +3027,98 @@ class SpecifierLRu(SpecifierBranch):
         return super().match(desc=desc, record=record, etalon="lru")
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.LRu = 1
+        selector = insn.select(record=self.record)
+        selector.LRu = 1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierVS(SpecifierBranch):
+class SpecifierVSXX(SpecifierBranch):
+    VSb: bool
+    VLi: bool
+
     @classmethod
-    def match(cls, desc, record):
-        return super().match(desc=desc, record=record, etalon="vs")
+    def match(cls, desc, record, etalon, VSb, VLi):
+        if desc != etalon:
+            return None
+
+        if record.svp64.mode is not _SVMode.BRANCH:
+            raise ValueError("only branch modes supported")
+
+        return cls(record=record, VSb=VSb, VLi=VLi)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.VLS = 1
-        rm.VLi = 0
-        rm.VSb = 0
+        selector = insn.select(record=self.record)
+        selector.VLS = 1
+        selector.VSb = int(self.VSb)
+        selector.VLi = int(self.VLi)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierVSi(SpecifierBranch):
+class SpecifierVS(SpecifierVSXX):
     @classmethod
     def match(cls, desc, record):
-        return super().match(desc=desc, record=record, etalon="vsi")
-
-    def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.VLS = 1
-        rm.VLi = 1
-        rm.VSb = 0
+        return super().match(desc=desc, record=record,
+            etalon="vs", VSb=False, VLi=False)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierVSb(SpecifierBranch):
+class SpecifierVSi(SpecifierVSXX):
     @classmethod
     def match(cls, desc, record):
-        return super().match(desc=desc, record=record, etalon="vsb")
-
-    def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.VLS = 1
-        rm.VLi = 0
-        rm.VSb = 1
+        return super().match(desc=desc, record=record,
+            etalon="vsi", VSb=False, VLi=True)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierVSbi(SpecifierBranch):
+class SpecifierVSb(SpecifierVSXX):
     @classmethod
     def match(cls, desc, record):
-        return super().match(desc=desc, record=record, etalon="vsbi")
-
-    def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.VLS = 1
-        rm.VLi = 1
-        rm.VSb = 1
+        return super().match(desc=desc, record=record,
+            etalon="vsb", VSb=True, VLi=False)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierCTR(SpecifierBranch):
+class SpecifierVSbi(SpecifierVSXX):
     @classmethod
     def match(cls, desc, record):
-        if desc != "ctr":
+        return super().match(desc=desc, record=record,
+            etalon="vsbi", VSb=True, VLi=True)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierCTX(Specifier):
+    CTi: bool
+
+    @classmethod
+    def match(cls, desc, record, etalon, CTi):
+        if desc != etalon:
             return None
 
-        return cls(record=record)
+        if record.svp64.mode is not _SVMode.BRANCH:
+            raise ValueError("only branch modes supported")
+
+        return cls(record=record, CTi=CTi)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.CTR = 1
+        selector = insn.select(record=self.record)
+        selector.CTR = 1
+        selector.CTi = int(self.CTi)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class SpecifierCTi(SpecifierBranch):
+class SpecifierCTR(SpecifierCTX):
     @classmethod
     def match(cls, desc, record):
-        if desc != "cti":
-            return None
+        return super().match(desc=desc, record=record,
+            etalon="ctr", CTi=False)
 
-        return cls(record=record)
 
-    def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.CTR = 1
-        rm.CTi = 1
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierCTi(SpecifierCTX):
+    @classmethod
+    def match(cls, desc, record):
+        return super().match(desc=desc, record=record,
+            etalon="cti", CTi=True)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -3173,11 +3128,17 @@ class SpecifierPI(Specifier):
         if desc != "pi":
             return None
 
+        if record.svp64.mode is not _SVMode.LDST_IMM:
+            raise ValueError("only ld/st imm mode supported")
+
         return cls(record=record)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.pi = 1
+        selector = insn.select(record=self.record)
+        selector.mode[0] = 0b0
+        selector.mode[1] = 0b0
+        selector.mode[2] = 0b1
+        selector.pi = 0b1
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
@@ -3187,11 +3148,36 @@ class SpecifierLF(Specifier):
         if desc != "lf":
             return None
 
+        if record.svp64.mode is not _SVMode.LDST_IMM:
+            raise ValueError("only ld/st imm mode supported")
+
         return cls(record=record)
 
     def assemble(self, insn):
-        rm = insn.prefix.rm.select(record=self.record)
-        rm.lf = 1
+        selector = insn.select(record=self.record)
+        selector.mode[2] = 1
+        selector.lf = 0b1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVLi(Specifier):
+    @classmethod
+    def match(cls, desc, record):
+        if desc != "vli":
+            return None
+
+        return cls(record=record)
+
+    def validate(self, others):
+        for spec in others:
+            if isinstance(spec, SpecifierFF):
+                return
+
+        raise ValueError("VLi only allowed in failfirst")
+
+    def assemble(self, insn):
+        selector = insn.select(record=self.record)
+        selector.VLi = 1
 
 
 class Specifiers(tuple):
@@ -3214,7 +3200,6 @@ class Specifiers(tuple):
         SpecifierSatU,
         SpecifierMR,
         SpecifierMRR,
-        SpecifierCRM,
         SpecifierAll,
         SpecifierSNZ,
         SpecifierSL,
@@ -3224,6 +3209,7 @@ class Specifiers(tuple):
         SpecifierVSi,
         SpecifierVSb,
         SpecifierVSbi,
+        SpecifierVLi,
         SpecifierCTR,
         SpecifierCTi,
         SpecifierPI,
@@ -3238,6 +3224,13 @@ class Specifiers(tuple):
                     return spec
             raise ValueError(item)
 
+        # TODO: remove this hack
+        items = dict.fromkeys(items)
+        if "vli" in items:
+            del items["vli"]
+            items["vli"] = None
+        items = tuple(items)
+
         specs = tuple(map(transform, items))
         for (index, spec) in enumerate(specs):
             head = specs[:index]
@@ -3247,6 +3240,163 @@ class Specifiers(tuple):
         return super().__new__(cls, specs)
 
 
+class SVP64OperandMeta(type):
+    class SVP64NonZeroOperand(NonZeroOperand):
+        def assemble(self, value, insn):
+            if isinstance(value, str):
+                value = int(value, 0)
+            if not isinstance(value, int):
+                raise ValueError("non-integer operand")
+
+            # FIXME: this is really weird
+            if self.record.name in ("svstep", "svstep."):
+                value += 1 # compensation
+
+            return super().assemble(value=value, insn=insn)
+
+    __TRANSFORM = {
+        NonZeroOperand: SVP64NonZeroOperand,
+    }
+
+    def __new__(metacls, name, bases, ns):
+        bases = list(bases)
+        for (origin_cls, target_cls) in metacls.__TRANSFORM.items():
+            for (index, base_cls) in enumerate(bases):
+                if base_cls is origin_cls:
+                    bases[index] = target_cls
+                    break
+        bases = tuple(bases)
+
+        return super().__new__(metacls, name, bases, ns)
+
+
+class SVP64Operand(Operand, metaclass=SVP64OperandMeta):
+    def __init__(self, record, *args, **kwargs):
+        return super().__init__(record=record, *args, **kwargs)
+
+    @property
+    def span(self):
+        return tuple(map(lambda bit: (bit + 32), super().span))
+
+
+class RMSelector:
+    def __init__(self, insn, record):
+        self.__insn = insn
+        self.__record = record
+        return super().__init__()
+
+    def __repr__(self):
+        return repr(self.rm)
+
+    @property
+    def insn(self):
+        return self.__insn
+
+    @property
+    def record(self):
+        return self.__record
+
+    @property
+    def rm(self):
+        rm = getattr(self.insn.prefix.rm, self.record.svp64.mode.name.lower())
+
+        # The idea behind these tables is that they are now literally
+        # in identical format to insndb.csv and minor_xx.csv and can
+        # be done precisely as that. The only thing to watch out for
+        # is the insertion of Rc=1 as a "mask/value" bit and likewise
+        # regtype detection (3-bit BF/BFA, 5-bit BA/BB/BT) also inserted
+        # as the LSB.
+        table = None
+        if self.record.svp64.mode is _SVMode.NORMAL:
+            # concatenate mode 5-bit with Rc (LSB) then do a mask/map search
+            #    mode  Rc  mask  Rc  member
+            table = (
+                (0b000000, 0b111000, "simple"), # simple     (no Rc)
+                (0b001000, 0b111000, "mr"),     # mapreduce  (no Rc)
+                (0b010001, 0b110001, "ffrc1"),  # ffirst,     Rc=1
+                (0b010000, 0b110001, "ffrc0"),  # ffirst,     Rc=0
+                (0b100000, 0b110000, "sat"),    # saturation (no Rc)
+                (0b110000, 0b110001, "prrc0"),  # predicate,  Rc=0
+                (0b110001, 0b110001, "prrc1"),  # predicate,  Rc=1
+            )
+            search = ((int(self.insn.prefix.rm.normal.mode) << 1) | self.record.Rc)
+
+        elif self.record.svp64.mode is _SVMode.LDST_IMM:
+            # concatenate mode 5-bit with Rc (LSB) then do a mask/map search
+            #    mode  Rc  mask  Rc  member
+            # ironically/coincidentally this table is identical to NORMAL
+            # mode except reserved in place of mr
+            table = (
+                (0b000000, 0b111000, "simple"), # simple     (no Rc)
+                (0b001000, 0b111000, "post"),   # post       (no Rc)
+                (0b010001, 0b110001, "ffrc1"),  # ffirst,     Rc=1
+                (0b010000, 0b110001, "ffrc0"),  # ffirst,     Rc=0
+                (0b100000, 0b110000, "sat"),    # saturation (no Rc)
+                (0b110001, 0b110001, "prrc1"),  # predicate,  Rc=1
+                (0b110000, 0b110001, "prrc0"),  # predicate,  Rc=0
+            )
+            search = ((int(self.insn.prefix.rm.ldst_imm.mode) << 1) | self.record.Rc)
+
+        elif self.record.svp64.mode is _SVMode.LDST_IDX:
+            # concatenate mode 5-bit with Rc (LSB) then do a mask/map search
+            #    mode  Rc  mask  Rc  member
+            table = (
+                (0b000000, 0b110000, "simple"), # simple     (no Rc)
+                (0b010000, 0b110000, "stride"), # strided,   (no Rc)
+                (0b100000, 0b110000, "sat"),    # saturation (no Rc)
+                (0b110001, 0b110001, "prrc1"),  # predicate,  Rc=1
+                (0b110000, 0b110001, "prrc0"),  # predicate,  Rc=0
+            )
+            search = ((int(self.insn.prefix.rm.ldst_idx.mode) << 1) | self.record.Rc)
+
+        elif self.record.svp64.mode is _SVMode.CROP:
+            # concatenate mode 5-bit with regtype (LSB) then do mask/map search
+            #    mode  3b  mask  3b  member
+            table = (
+                (0b000000, 0b111000, "simple"), # simple
+                (0b001000, 0b111000, "mr"),     # mapreduce
+                (0b100001, 0b100001, "ff3"),    # ffirst, 3-bit CR
+                (0b100000, 0b100000, "ff5"),    # ffirst, 5-bit CR
+            )
+            search = ((int(self.insn.prefix.rm.crop.mode) << 1) | int(self.record.svp64.extra_CR_3bit))
+
+        elif self.record.svp64.mode is _SVMode.BRANCH:
+            # just mode 2-bit
+            #    mode  mask  member
+            table = (
+                (0b00, 0b11, "simple"), # simple
+                (0b01, 0b11, "vls"),    # VLset
+                (0b10, 0b11, "ctr"),    # CTR mode
+                (0b11, 0b11, "ctrvls"), # CTR+VLset mode
+            )
+            # slightly weird: doesn't have a 5-bit "mode" field like others
+            search = int(self.insn.prefix.rm.branch.mode.sel)
+
+        # look up in table
+        if table is not None:
+            for (value, mask, field) in table:
+                if ((value & mask) == (search & mask)):
+                    return getattr(rm, field)
+
+        return rm
+
+    def __getattr__(self, key):
+        if key.startswith(f"_{self.__class__.__name__}__"):
+            return super().__getattribute__(key)
+
+        return getattr(self.rm, key)
+
+    def __setattr__(self, key, value):
+        if key.startswith(f"_{self.__class__.__name__}__"):
+            return super().__setattr__(key, value)
+
+        rm = self.rm
+        if not hasattr(rm, key):
+            raise AttributeError(key)
+
+        return setattr(rm, key, value)
+
+
 class SVP64Instruction(PrefixedInstruction):
     """SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
     class Prefix(PrefixedInstruction.Prefix):
@@ -3255,9 +3405,12 @@ class SVP64Instruction(PrefixedInstruction):
 
     prefix: Prefix
 
+    def select(self, record):
+        return RMSelector(insn=self, record=record)
+
     @classmethod
     def record(cls, db, entry):
-        if isinstance(entry, SVP64Instruction):
+        if isinstance(entry, cls):
             entry = entry.suffix
         return super().record(db=db, entry=entry)
 
@@ -3270,24 +3423,28 @@ class SVP64Instruction(PrefixedInstruction):
         return "".join(map(str, bits))
 
     @classmethod
-    def assemble(cls, db, opcode, arguments=None, specifiers=None):
+    def assemble(cls, db, entry, arguments=None, specifiers=None):
         if arguments is None:
             arguments = ()
         if specifiers is None:
             specifiers = ()
 
-        record = cls.record(db=db, entry=opcode)
+        record = cls.record(db=db, entry=entry)
         insn = cls.integer(value=0)
 
+        for (op_cls, op_kwargs) in record.static_operands:
+            op_cls = type(f"SVP64{op_cls.__name__}", (SVP64Operand, op_cls), {})
+            operand = op_cls(record=record, **op_kwargs)
+            operand.assemble(insn=insn)
+
         specifiers = Specifiers(items=specifiers, record=record)
         for specifier in specifiers:
             specifier.assemble(insn=insn)
 
-        for operand in record.static_operands:
-            operand.assemble(insn=insn)
-
         dynamic_operands = tuple(record.dynamic_operands)
-        for (value, operand) in Arguments(arguments, dynamic_operands):
+        for (value, (op_cls, op_kwargs)) in Arguments(arguments, dynamic_operands):
+            op_cls = type(f"SVP64{op_cls.__name__}", (SVP64Operand, op_cls), {})
+            operand = op_cls(record=record, **op_kwargs)
             operand.assemble(value=value, insn=insn)
 
         insn.prefix.PO = 0x1
@@ -3297,9 +3454,9 @@ class SVP64Instruction(PrefixedInstruction):
 
     def disassemble(self, db,
             byteorder="little",
-            verbosity=Verbosity.NORMAL):
+            style=Style.NORMAL):
         def blob(insn):
-            if verbosity <= Verbosity.SHORT:
+            if style <= Style.SHORT:
                 return ""
             else:
                 blob = insn.bytes(byteorder=byteorder)
@@ -3316,7 +3473,7 @@ class SVP64Instruction(PrefixedInstruction):
 
         name = f"sv.{record.name}"
 
-        rm = self.prefix.rm.select(record=record)
+        rm = self.select(record=record)
 
         # convert specifiers to /x/y/z (sorted lexicographically)
         specifiers = sorted(rm.specifiers(record=record))
@@ -3326,7 +3483,7 @@ class SVP64Instruction(PrefixedInstruction):
 
         # convert operands to " ,x,y,z"
         operands = tuple(map(_operator.itemgetter(1),
-            self.dynamic_operands(db=db, verbosity=verbosity)))
+            self.dynamic_operands(db=db, style=style)))
         operands = ",".join(operands)
         if len(operands) > 0: # if any separate with a space
             operands = (" " + operands)
@@ -3335,7 +3492,7 @@ class SVP64Instruction(PrefixedInstruction):
         if blob_suffix:
             yield f"{blob_suffix}"
 
-        if verbosity >= Verbosity.VERBOSE:
+        if style >= Style.VERBOSE:
             indent = (" " * 4)
             binary = self.binary
             spec = self.spec(db=db, prefix="sv.")
@@ -3360,13 +3517,43 @@ class SVP64Instruction(PrefixedInstruction):
             for (cls, kwargs) in record.mdwn.operands:
                 operand = cls(record=record, **kwargs)
                 yield from operand.disassemble(insn=self,
-                    verbosity=verbosity, indent=indent)
+                    style=style, indent=indent)
             yield f"{indent}RM"
             yield f"{indent}{indent}{rm.__doc__}"
-            for line in rm.disassemble(verbosity=verbosity):
+            for line in rm.disassemble(style=style):
                 yield f"{indent}{indent}{line}"
             yield ""
 
+    def static_operands(self, db):
+        record = self.record(db=db, entry=self)
+        for (op_cls, op_kwargs) in record.static_operands:
+            op_cls = type(f"SVP64{op_cls.__name__}", (SVP64Operand, op_cls), {})
+            operand = op_cls(record=record, **op_kwargs)
+            yield (operand.name, operand.value)
+
+    def dynamic_operands(self, db, style=Style.NORMAL):
+        record = self.record(db=db, entry=self)
+
+        imm = False
+        imm_name = ""
+        imm_value = ""
+        for (op_cls, op_kwargs) in record.dynamic_operands:
+            op_cls = type(f"SVP64{op_cls.__name__}", (SVP64Operand, op_cls), {})
+            operand = op_cls(record=record, **op_kwargs)
+            name = operand.name
+            value = " ".join(operand.disassemble(insn=self,
+                style=min(style, Style.NORMAL)))
+            if imm:
+                name = f"{imm_name}({name})"
+                value = f"{imm_value}({value})"
+                imm = False
+            if isinstance(operand, ImmediateOperand):
+                imm_name = name
+                imm_value = value
+                imm = True
+            if not imm:
+                yield (name, value)
+
 
 def parse(stream, factory):
     def match(entry):
@@ -3430,7 +3617,6 @@ class PPCDatabase:
         # The code below groups the instructions by name:section.
         # There can be multiple names for the same instruction.
         # The point is to capture different opcodes for the same instruction.
-        dd = _collections.defaultdict
         sections = {}
         records = _collections.defaultdict(set)
         path = (root / "insndb.csv")
@@ -3576,10 +3762,7 @@ class Database:
                 mdwn=mdwn, fields=fields)
             db.add(record)
             names[record.name] = record
-            PO = section.opcode
-            if PO is None:
-                PO = ppc[0].opcode
-            opcodes[section][PO.value].add(record)
+            opcodes[section][record.PO].add(record)
 
         self.__db = sorted(db)
         self.__names = dict(sorted(names.items()))
@@ -3602,7 +3785,9 @@ class Database:
         if isinstance(key, Instruction):
             PO = int(key.PO)
             key = int(key)
-            for (section, group) in self.__opcodes.items():
+            sections = sorted(self.__opcodes)
+            for section in sections:
+                group = self.__opcodes[section]
                 for record in group[PO]:
                     if record.match(key=key):
                         return record
index 332a0b2db0abb03b54be0608f0c993825a75e5b9..8b89212fd736d65a383ded16f2b770966efe9cb5 100644 (file)
@@ -808,7 +808,7 @@ def process_csvs(format):
             extra_classifier(insn_name, value, name, res, regs)
 
             # source-mask is hard to detect, it's part of RM-nn-nn.
-            # to make disassembler easier, create a yes/no decision here
+            # to make style easier, create a yes/no decision here
             # see https://libre-soc.org/openpower/sv/svp64/#extra_remap
             # MASK_SRC
             vstripped = value.replace("LDST", "")
index 9e89c5a7edbf5e60f28c431a49a8eed48e1af7f5..8479090cbdcc1a7a92221599fcf12273d1fb2aa2 100644 (file)
@@ -15,54 +15,6 @@ from openpower.decoder.power_insn import (
 )
 
 
-@_dataclasses.dataclass(eq=True, frozen=True)
-class StaticOperand:
-    name: str
-    value: int
-    span: tuple
-
-
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperand:
-    name: str
-    span: tuple
-
-
-@_functools.total_ordering
-@_dataclasses.dataclass(eq=True, frozen=True)
-class Entry:
-    name: str
-    static_operands: tuple
-    dynamic_operands: tuple
-
-    def __lt__(self, other):
-        if not isinstance(other, self.__class__):
-            return NotImplemented
-        return ((self.PO, self.XO, self.Rc) <
-            (other.PO, other.XO, other.Rc))
-
-    @property
-    def PO(self):
-        for operand in self.static_operands:
-            if operand.name == "PO":
-                return operand.value
-        raise ValueError(self)
-
-    @property
-    def XO(self):
-        for operand in self.static_operands:
-            if operand.name == "XO":
-                return operand.value
-        raise ValueError(self)
-
-    @property
-    def Rc(self):
-        for operand in self.static_operands:
-            if operand.name == "Rc":
-                return operand.value
-        raise ValueError(self)
-
-
 def collect(db):
     fptrans = tuple(_FPTRANS_INSNS)
     fptrans_Rc = tuple(map(lambda name: f"{name}.", fptrans))
@@ -73,56 +25,8 @@ def collect(db):
     for record in filter(fptrans_match, db):
         if len(record.opcodes) > 1:
             raise NotImplementedError(record.opcodes)
-        PO = record.section.opcode
-        if PO is None:
-            PO = tuple(record.ppc)[0].opcode
-            XO = None
-        else:
-            XO = tuple(record.ppc)[0].opcode
-
-        @_dataclasses.dataclass(eq=True, frozen=True)
-        class POStaticOperand(_StaticOperand):
-            @property
-            def span(self):
-                return tuple(range(0, 6))
-
-        @_dataclasses.dataclass(eq=True, frozen=True)
-        class XOStaticOperand(_StaticOperand):
-            @property
-            def span(self):
-                return tuple(self.record.section.bitsel)
-
-        static_operands = [(POStaticOperand, {
-            "name": "PO",
-            "value": (PO.value & PO.mask)
-        })]
-        if XO is not None:
-            static_operands.append((XOStaticOperand, {
-                "name": "XO",
-                "value": (XO.value & XO.mask)
-            }))
-
-        static_operands.extend(record.mdwn.operands.static)
-        dynamic_operands = record.mdwn.operands.dynamic
-
-        def static_operand(operand):
-            (cls, kwargs) = operand
-            operand = cls(record=record, **kwargs)
-            return StaticOperand(name=operand.name,
-                value=operand.value, span=operand.span)
-
-        def dynamic_operand(operand):
-            (cls, kwargs) = operand
-            operand = cls(record=record, **kwargs)
-            return DynamicOperand(name=operand.name,
-                span=operand.span)
-
-        static_operands = tuple(map(static_operand, static_operands))
-        dynamic_operands = tuple(map(dynamic_operand, dynamic_operands))
-
-        yield Entry(name=record.name,
-            static_operands=static_operands,
-            dynamic_operands=dynamic_operands)
+
+        yield record
 
 
 def opcodes(entry):
@@ -139,8 +43,8 @@ def opcodes(entry):
     return f"{{{string}}},"
 
 
-def asm(entry, binutils=False, regex=False):
-    operands = tuple(entry.dynamic_operands)
+def asm(record, binutils=False, regex=False):
+    operands = [op_cls(record=record, **op_kwargs) for (op_cls, op_kwargs) in record.dynamic_operands]
     for (idx, operand) in enumerate(operands):
         values = []
         for each in operands:
@@ -157,20 +61,21 @@ def asm(entry, binutils=False, regex=False):
             value = f"r{value}"
         values[idx] = value
         sep = "\s+" if regex else " "
-        yield f"{entry.name}{sep}{','.join(values)}"
+        yield f"{record.name}{sep}{','.join(values)}"
 
 
-def dis(entry, binutils=True):
+def dis(record, binutils=True):
     def objdump(byte):
         return f"{byte:02x}"
 
-    asm_plain = tuple(asm(entry, binutils=binutils, regex=False))
-    asm_regex = tuple(asm(entry, binutils=binutils, regex=True))
-    for (idx, dynamic_operand) in enumerate(entry.dynamic_operands):
+    asm_plain = tuple(asm(record, binutils=binutils, regex=False))
+    asm_regex = tuple(asm(record, binutils=binutils, regex=True))
+    for (idx, (op_cls, op_kwargs)) in enumerate(record.dynamic_operands):
+        dynamic_operand = op_cls(record=record, **op_kwargs)
         insn = _WordInstruction.integer(value=0)
-        for static_operand in entry.static_operands:
-            span = static_operand.span
-            insn[span] = static_operand.value
+        for (op_cls, op_kwargs) in record.static_operands:
+            static_operand = op_cls(record=record, **op_kwargs)
+            insn[static_operand.span] = static_operand.value
         span = dynamic_operand.span
         insn[span] = ((1 << len(span)) - 1)
         if binutils:
index 64ef66ece4d81e37d020ca0f48843a0135a4770c..203610a3ec5b08f7fc605e0f72bec6b2c25e0291 100644 (file)
@@ -7,7 +7,7 @@ from openpower.decoder.power_enums import (
     find_wiki_dir as _find_wiki_dir,
 )
 from openpower.decoder.power_insn import (
-    Verbosity as _Verbosity,
+    Style as _Style,
     Database as _Database,
     WordInstruction as _WordInstruction,
     PrefixedInstruction as _PrefixedInstruction,
@@ -51,10 +51,10 @@ def load(ifile, byteorder=ByteOrder.LITTLE, **_):
         yield insn
 
 
-def dump(insns, verbosity, **_):
+def dump(insns, style, **_):
     db = _Database(_find_wiki_dir())
     for insn in insns:
-        yield from insn.disassemble(db=db, verbosity=verbosity)
+        yield from insn.disassemble(db=db, style=style)
 
 
 # this is the entry-point for the console-script pysvp64dis
@@ -67,11 +67,11 @@ def main():
     parser.add_argument("-b", "--byteorder",
         type=ByteOrder, default=ByteOrder.LITTLE)
     parser.add_argument("-s", "--short",
-        dest="verbosity", default=_Verbosity.NORMAL,
-        action="store_const", const=_Verbosity.SHORT)
+        dest="style", default=_Style.NORMAL,
+        action="store_const", const=_Style.SHORT)
     parser.add_argument("-v", "--verbose",
-        dest="verbosity", default=_Verbosity.NORMAL,
-        action="store_const", const=_Verbosity.VERBOSE)
+        dest="style", default=_Style.NORMAL,
+        action="store_const", const=_Style.VERBOSE)
     parser.add_argument("-l", "--log",
         action="store_true", default=False)
 
index 1e38dc14a76ed4c77b2afa858548f52fdcfb7998..90063f33db42de505862d06801fd122c5dcaed8f 100644 (file)
@@ -30,6 +30,7 @@ from openpower.decoder.selectable_int import SelectableInt
 from openpower.consts import SVP64MODE
 from openpower.decoder.power_insn import SVP64Instruction
 from openpower.decoder.power_insn import Database
+from openpower.decoder.power_insn import Style
 from openpower.decoder.power_insn import WordInstruction
 from openpower.decoder.power_enums import find_wiki_dir
 
@@ -248,7 +249,10 @@ class SVP64Asm:
         macros.update(self.macros)
         isa = self.isa
         svp64 = self.svp64
-        insn_no_comments = insn.partition('#')[0]
+        insn_no_comments = insn.partition('#')[0].strip()
+        if not insn_no_comments:
+            return
+
         # find first space, to get opcode
         ls = insn_no_comments.split()
         opcode = ls[0]
@@ -268,7 +272,7 @@ class SVP64Asm:
             record = DB[opcode]
         if record is not None:
             insn = WordInstruction.assemble(db=DB,
-                opcode=opcode, arguments=fields)
+                entry=opcode, arguments=fields)
             yield " ".join((
                 f".long 0x{int(insn):08X}",
                 "#",
@@ -298,18 +302,13 @@ class SVP64Asm:
             record = DB[v30b_op]
         if record is not None:
             insn = SVP64Instruction.assemble(db=DB,
-                opcode=v30b_op_orig,
+                entry=v30b_op_orig,
                 arguments=fields,
                 specifiers=opmodes)
             prefix = int(insn.prefix)
             suffix = int(insn.suffix)
-            yield " ".join((
-                f".long 0x{prefix:08X};",
-                f".long 0x{suffix:08X};",
-                "#",
-                opcode,
-                ",".join(fields),
-            ))
+            yield f".long 0x{prefix:08X}"
+            yield from insn.suffix.disassemble(db=DB, style=Style.SHORT)
             return
 
         # look up the 32-bit op (original, with "." if it has it)
@@ -957,7 +956,7 @@ class SVP64Asm:
             record = DB[opcode]
         if record is not None:
             insn = WordInstruction.assemble(db=DB,
-                opcode=opcode, arguments=fields)
+                entry=opcode, arguments=fields)
             yield " ".join((
                 f".long 0x{int(insn):08X}",
                 "#",
@@ -1067,11 +1066,10 @@ def asm_process():
             macro = op[4:].split(",")
             (macro, value) = map(str.strip, macro)
             macros[macro] = value
-        record = DB[op]
-        if not op.startswith('sv.') and record is None:
+
+        if not op or op.startswith("#"):
             outfile.write(line)
             continue
-
         (ws, line) = get_ws(line)
         lst = isa.translate_one(op, macros)
         lst = '; '.join(lst)
index dedf12aef851af5f2d776c82326bc7bcb38f0962..03c9e571091821abda38a625d2e67e9b99b6c343 100644 (file)
@@ -1,7 +1,7 @@
 from openpower.simulator.program import Program
 from openpower.sv.trans.pysvp64dis import load, dump
 from openpower.sv.trans.svp64 import SVP64Asm
-from openpower.decoder.power_insn import Database, Verbosity
+from openpower.decoder.power_insn import Database, Style
 from openpower.decoder.power_enums import find_wiki_dir
 from openpower.sv import sv_binutils_fptrans
 import unittest
@@ -26,7 +26,7 @@ class SVSTATETestCase(unittest.TestCase):
                 #print ("insn", insn)
             insns = list(insns)
             print ("insns", insns)
-            for i, line in enumerate(dump(insns, verbosity=Verbosity.SHORT)):
+            for i, line in enumerate(dump(insns, style=Style.SHORT)):
                 name = expected[i].split(" ")[0]
                 with self.subTest("%d:%s" % (i, name)):
                     print("instruction", repr(line), repr(expected[i]))
@@ -75,8 +75,6 @@ class SVSTATETestCase(unittest.TestCase):
                     'sv.crand/ff=eq/m=r10 12,2,33',
                     'sv.crand/m=r10 12,2,33',
                     'sv.crand/m=r10/sz 12,2,33',
-                    # XXX dz/sz is not the canonical way, must be zz
-                    'sv.crand/dz/m=r10/sz 12,2,33', # NOT OK
                     'sv.crand/m=r10/zz 12,2,33',    # SHOULD PASS
                         ]
         self._do_tst(expected)
index 104330cb6322a1bec7f58b795e68751f9a08c143..9a2d54b9ce9683953e85b145f10ffd200c534544 100644 (file)
@@ -1,7 +1,7 @@
 from openpower.simulator.program import Program
 from openpower.sv.trans.pysvp64dis import load, dump
 from openpower.sv.trans.svp64 import SVP64Asm
-from openpower.decoder.power_insn import Database, Verbosity
+from openpower.decoder.power_insn import Database, Style
 from openpower.decoder.power_enums import find_wiki_dir
 from openpower.sv import sv_binutils_fptrans
 import unittest
@@ -21,7 +21,7 @@ class SVSTATETestCase(unittest.TestCase):
                 #print ("insn", insn)
             insns = list(insns)
             print ("insns", insns)
-            for i, line in enumerate(dump(insns, verbosity=Verbosity.SHORT)):
+            for i, line in enumerate(dump(insns, style=Style.SHORT)):
                 name = expected[i].split(" ")[0]
                 with self.subTest("%d:%s" % (i, name)):
                     print("instruction", repr(line), repr(expected[i]))