X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fopenpower%2Fdecoder%2Fpower_insn.py;h=bfaf2fa751216c70b5f4ad9942d14365e8d0cfe1;hb=b85e3da773bc38f4365746dfa28484f5845842db;hp=2e681bc7b2aa1b63443b19a02cab9b87827cf8e5;hpb=6106d80d37147711503e88c6891fa398e6caa04a;p=openpower-isa.git diff --git a/src/openpower/decoder/power_insn.py b/src/openpower/decoder/power_insn.py index 2e681bc7..bfaf2fa7 100644 --- a/src/openpower/decoder/power_insn.py +++ b/src/openpower/decoder/power_insn.py @@ -3,7 +3,9 @@ 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 import re as _re @@ -20,6 +22,7 @@ from openpower.decoder.power_enums import ( In3Sel as _In3Sel, OutSel as _OutSel, CRInSel as _CRInSel, + CRIn2Sel as _CRIn2Sel, CROutSel as _CROutSel, LDSTLen as _LDSTLen, LDSTMode as _LDSTMode, @@ -40,13 +43,24 @@ from openpower.decoder.selectable_int import ( ) from openpower.decoder.power_fields import ( Field as _Field, - Array as _Array, Mapping as _Mapping, DecodeFields as _DecodeFields, ) from openpower.decoder.pseudo.pagereader import ISA as _ISA +@_functools.total_ordering +class Verbosity(_enum.Enum): + SHORT = _enum.auto() + NORMAL = _enum.auto() + VERBOSE = _enum.auto() + + def __lt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.value < other.value) + + def dataclass(cls, record, keymap=None, typemap=None): if keymap is None: keymap = {} @@ -74,22 +88,37 @@ def dataclass(cls, record, keymap=None, typemap=None): @_functools.total_ordering @_dataclasses.dataclass(eq=True, frozen=True) class Opcode: - class Value(int): - def __repr__(self): - if self.bit_length() <= 32: - return f"0x{self:08x}" - else: - return f"0x{self:016x}" + class Integer(int): + def __new__(cls, value): + if isinstance(value, str): + value = int(value, 0) + if not isinstance(value, int): + raise ValueError(value) + + if value.bit_length() > 64: + raise ValueError(value) + + return super().__new__(cls, value) + + def __str__(self): + return super().__repr__() - class Mask(int): def __repr__(self): - if self.bit_length() <= 32: - return f"0x{self:08x}" - else: - return f"0x{self:016x}" + return f"{self:0{self.bit_length()}b}" + + def bit_length(self): + if super().bit_length() > 32: + return 64 + return 32 + + class Value(Integer): + pass + + class Mask(Integer): + pass value: Value - mask: Mask = None + mask: Mask def __lt__(self, other): if not isinstance(other, Opcode): @@ -97,39 +126,41 @@ class Opcode: return ((self.value, self.mask) < (other.value, other.mask)) def __post_init__(self): - (value, mask) = (self.value, self.mask) - - if isinstance(value, Opcode): - if mask is not None: - raise ValueError(mask) - (value, mask) = (value.value, value.mask) - elif isinstance(value, str): - if mask is not None: - raise ValueError(mask) - value = int(value, 0) - - if not isinstance(value, int): - raise ValueError(value) - if mask is None: - mask = value - if not isinstance(mask, int): - raise ValueError(mask) + if self.value.bit_length() != self.mask.bit_length(): + raise ValueError("bit length mismatch") - object.__setattr__(self, "value", self.__class__.Value(value)) - object.__setattr__(self, "mask", self.__class__.Mask(mask)) + def __repr__(self): + def pattern(value, mask, bit_length): + for bit in range(bit_length): + if ((mask & (1 << (bit_length - bit - 1))) == 0): + yield "-" + elif (value & (1 << (bit_length - bit - 1))): + yield "1" + else: + yield "0" + + return "".join(pattern(self.value, self.mask, self.value.bit_length())) class IntegerOpcode(Opcode): def __init__(self, value): - if isinstance(value, str): - value = int(value, 0) - return super().__init__(value=value, mask=None) + if value.startswith("0b"): + mask = int(("1" * len(value[2:])), 2) + else: + mask = 0b111111 + + value = Opcode.Value(value) + mask = Opcode.Mask(mask) + + return super().__init__(value=value, mask=mask) class PatternOpcode(Opcode): - def __init__(self, value): - (pattern, value, mask) = (value, 0, 0) + def __init__(self, pattern): + if not isinstance(pattern, str): + raise ValueError(pattern) + (value, mask) = (0, 0) for symbol in pattern: if symbol not in {"0", "1", "-"}: raise ValueError(pattern) @@ -140,22 +171,8 @@ class PatternOpcode(Opcode): value >>= 1 mask >>= 1 - return super().__init__(value=value, mask=mask) - - -class FieldsOpcode(Opcode): - def __init__(self, fields): - def field(opcode, field): - (value, mask) = opcode - (field, bits) = field - shifts = map(lambda bit: (31 - bit), reversed(tuple(bits))) - for (index, shift) in enumerate(shifts): - bit = ((field & (1 << index)) != 0) - value |= (bit << shift) - mask |= (1 << shift) - return (value, mask) - - (value, mask) = _functools.reduce(field, fields, (0, 0)) + value = Opcode.Value(value) + mask = Opcode.Mask(mask) return super().__init__(value=value, mask=mask) @@ -196,6 +213,7 @@ class PPCRecord: in3: _In3Sel = _In3Sel.NONE out: _OutSel = _OutSel.NONE cr_in: _CRInSel = _CRInSel.NONE + cr_in2: _CRIn2Sel = _CRIn2Sel.NONE cr_out: _CROutSel = _CROutSel.NONE cry_in: _CryIn = _CryIn.ZERO ldst_len: _LDSTLen = _LDSTLen.NONE @@ -217,23 +235,37 @@ class PPCRecord: } @classmethod - def CSV(cls, record, opcode_cls=Opcode): + def CSV(cls, record, opcode_cls): typemap = {field.name:field.type for field in _dataclasses.fields(cls)} typemap["opcode"] = opcode_cls + if record["CR in"] == "BA_BB": + record["cr_in"] = "BA" + record["cr_in2"] = "BB" + del record["CR in"] + flags = set() for flag in frozenset(PPCRecord.Flags): if bool(record.pop(flag, "")): flags.add(flag) record["flags"] = PPCRecord.Flags(flags) - return dataclass(cls, record, keymap=PPCRecord.__KEYMAP, typemap=typemap) + return dataclass(cls, record, + keymap=PPCRecord.__KEYMAP, + typemap=typemap) @cached_property def names(self): return frozenset(self.comment.split("=")[-1].split("/")) +class PPCMultiRecord(tuple): + def __getattr__(self, attr): + if attr == "opcode": + raise AttributeError(attr) + return getattr(self[0], attr) + + @_dataclasses.dataclass(eq=True, frozen=True) class SVP64Record: class ExtraMap(tuple): @@ -282,9 +314,9 @@ class SVP64Record: out: _OutSel = _OutSel.NONE out2: _OutSel = _OutSel.NONE cr_in: _CRInSel = _CRInSel.NONE + cr_in2: _CRIn2Sel = _CRIn2Sel.NONE cr_out: _CROutSel = _CROutSel.NONE extra: ExtraMap = ExtraMap() - pu: bool = False conditions: str = "" mode: _SVMode = _SVMode.NORMAL @@ -295,20 +327,88 @@ class SVP64Record: "Etype": "etype", "CR in": "cr_in", "CR out": "cr_out", - "PU": "pu", } @classmethod def CSV(cls, record): - for key in ("in1", "in2", "in3", "out", "out2", "CR in", "CR out"): + for key in frozenset({ + "in1", "in2", "in3", "CR in", + "out", "out2", "CR out", + }): value = record[key] if value == "0": record[key] = "NONE" - record["extra"] = cls.ExtraMap(record.pop(f"{index}") for index in range(0, 4)) + if record["CR in"] == "BA_BB": + record["cr_in"] = "BA" + record["cr_in2"] = "BB" + del record["CR in"] + + extra = [] + for idx in range(0, 4): + extra.append(record.pop(f"{idx}")) + + record["extra"] = cls.ExtraMap(extra) return dataclass(cls, record, keymap=cls.__KEYMAP) + @_functools.lru_cache(maxsize=None) + def extra_idx(self, key): + extra_idx = ( + _SVExtra.Idx0, + _SVExtra.Idx1, + _SVExtra.Idx2, + _SVExtra.Idx3, + ) + + if key not in frozenset({ + "in1", "in2", "in3", "cr_in", "cr_in2", + "out", "out2", "cr_out", + }): + raise KeyError(key) + + sel = getattr(self, key) + if sel is _CRInSel.BA_BB: + return _SVExtra.Idx_1_2 + reg = _SVExtraReg(sel) + if reg is _SVExtraReg.NONE: + return _SVExtra.NONE + + extra_map = { + _SVExtraRegType.SRC: {}, + _SVExtraRegType.DST: {}, + } + for index in range(0, 4): + for entry in self.extra[index]: + extra_map[entry.regtype][entry.reg] = extra_idx[index] + + for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST): + extra = extra_map[regtype].get(reg, _SVExtra.NONE) + if extra is not _SVExtra.NONE: + return extra + + return _SVExtra.NONE + + extra_idx_in1 = property(_functools.partial(extra_idx, key="in1")) + extra_idx_in2 = property(_functools.partial(extra_idx, key="in2")) + extra_idx_in3 = property(_functools.partial(extra_idx, key="in3")) + extra_idx_out = property(_functools.partial(extra_idx, key="out")) + extra_idx_out2 = property(_functools.partial(extra_idx, key="out2")) + extra_idx_cr_in = property(_functools.partial(extra_idx, key="cr_in")) + extra_idx_cr_out = property(_functools.partial(extra_idx, key="cr_out")) + + @_functools.lru_cache(maxsize=None) + def extra_reg(self, key): + return _SVExtraReg(getattr(self, key)) + + extra_reg_in1 = property(_functools.partial(extra_reg, key="in1")) + extra_reg_in2 = property(_functools.partial(extra_reg, key="in2")) + extra_reg_in3 = property(_functools.partial(extra_reg, key="in3")) + extra_reg_out = property(_functools.partial(extra_reg, key="out")) + extra_reg_out2 = property(_functools.partial(extra_reg, key="out2")) + extra_reg_cr_in = property(_functools.partial(extra_reg, key="cr_in")) + extra_reg_cr_out = property(_functools.partial(extra_reg, key="cr_out")) + class BitSel: def __init__(self, value=(0, 32)): @@ -330,6 +430,9 @@ class BitSel: def __iter__(self): yield from range(self.start, (self.end + 1)) + def __reversed__(self): + return tuple(reversed(tuple(self))) + @property def start(self): return self.__start @@ -370,14 +473,18 @@ class Section: return (bin(self) if self else "None") path: _pathlib.Path - opcode: Opcode bitsel: BitSel suffix: Suffix mode: Mode + opcode: IntegerOpcode = None @classmethod def CSV(cls, record): - return dataclass(cls, record) + typemap = {field.name:field.type for field in _dataclasses.fields(cls)} + if record["opcode"] == "NONE": + typemap["opcode"] = lambda _: None + + return dataclass(cls, record, typemap=typemap) class Fields: @@ -406,96 +513,381 @@ class Fields: return self.__mapping.get(key, None) -class Operands: - @_dataclasses.dataclass(eq=True, frozen=True) - class Operand: - name: str - - def disassemble(self, value, record): - raise NotImplementedError - - @_dataclasses.dataclass(eq=True, frozen=True) - class DynamicOperand(Operand): - def disassemble(self, value, record): - return str(int(value[record.fields[self.name]])) - - @_dataclasses.dataclass(eq=True, frozen=True) - class StaticOperand(Operand): - value: int = None - - @_dataclasses.dataclass(eq=True, frozen=True) - class DynamicOperandIFormLI(DynamicOperand): - def disassemble(self, value, record): - return hex(int(_selectconcat( - value[record.fields["LI"]], - _SelectableInt(value=0b00, bits=2)))) - - class DynamicOperandBFormBD(DynamicOperand): - def disassemble(self, value, record): - return hex(int(_selectconcat( - value[record.fields["BD"]], - _SelectableInt(value=0b00, bits=2)))) - - @_dataclasses.dataclass(eq=True, frozen=True) - class DynamicOperandGPR(DynamicOperand): - def disassemble(self, value, record): - return f"r{super().disassemble(value=value, record=record)}" - - @_dataclasses.dataclass(eq=True, frozen=True) - class DynamicOperandFPR(DynamicOperand): - def disassemble(self, value, record): - return f"f{super().disassemble(value=value, record=record)}" - - 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}, +@_dataclasses.dataclass(eq=True, frozen=True) +class Operand: + name: str + + def span(self, record): + return record.fields[self.name] + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + raise NotImplementedError + + +class DynamicOperand(Operand): + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + + if verbosity >= Verbosity.VERBOSE: + span = map(str, span) + yield f"{indent}{self.name}" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + else: + yield str(int(value)) + + +class SignedOperand(DynamicOperand): + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + + if verbosity >= Verbosity.VERBOSE: + span = map(str, span) + yield f"{indent}{self.name}" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + else: + yield str(value.to_signed_int()) + + +@_dataclasses.dataclass(eq=True, frozen=True) +class StaticOperand(Operand): + value: int + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + + if verbosity >= Verbosity.VERBOSE: + span = map(str, span) + yield f"{indent}{self.name}" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + else: + yield str(int(value)) + + +class ImmediateOperand(DynamicOperand): + pass + + +class NonZeroOperand(DynamicOperand): + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + + if verbosity >= Verbosity.VERBOSE: + span = map(str, span) + yield f"{indent}{self.name}" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + else: + yield str(int(value) + 1) + + +class RegisterOperand(DynamicOperand): + def sv_spec_enter(self, value, span): + return (value, span) + + def sv_spec_leave(self, value, span, origin_value, origin_span): + return (value, span) + + def spec(self, insn, record): + vector = False + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + span = tuple(map(str, span)) + + if isinstance(insn, SVP64Instruction): + (origin_value, origin_span) = (value, span) + (value, span) = self.sv_spec_enter(value=value, span=span) + + extra_idx = self.extra_idx(record=record) + if extra_idx is _SVExtra.NONE: + return (vector, value, span) + + if record.etype is _SVEtype.EXTRA3: + spec = insn.prefix.rm.extra3[extra_idx] + elif record.etype is _SVEtype.EXTRA2: + spec = insn.prefix.rm.extra2[extra_idx] + else: + raise ValueError(record.etype) + + if spec != 0: + vector = bool(spec[0]) + spec_span = spec.__class__ + if record.etype is _SVEtype.EXTRA3: + spec_span = tuple(map(str, spec_span[1, 2])) + spec = spec[1, 2] + elif record.etype is _SVEtype.EXTRA2: + spec_span = tuple(map(str, spec_span[1,])) + spec = _SelectableInt(value=spec[1].value, bits=2) + if vector: + spec <<= 1 + spec_span = (spec_span + ("{0}",)) + else: + spec_span = (("{0}",) + spec_span) + else: + raise ValueError(record.etype) + + vector_shift = (2 + (5 - value.bits)) + scalar_shift = value.bits + spec_shift = (5 - value.bits) + + bits = (len(span) + len(spec_span)) + value = _SelectableInt(value=value.value, bits=bits) + spec = _SelectableInt(value=spec.value, bits=bits) + if vector: + value = ((value << vector_shift) | (spec << spec_shift)) + span = (span + spec_span + ((spec_shift * ('{0}',)))) + else: + value = ((spec << scalar_shift) | value) + span = ((spec_shift * ('{0}',)) + spec_span + span) + + (value, span) = self.sv_spec_leave(value=value, span=span, + origin_value=origin_value, origin_span=origin_span) + + return (vector, value, span) + + @property + def extra_reg(self): + return _SVExtraReg(self.name) + + def extra_idx(self, record): + for key in frozenset({ + "in1", "in2", "in3", "cr_in", "cr_in2", + "out", "out2", "cr_out", + }): + extra_reg = record.svp64.extra_reg(key=key) + if extra_reg is self.extra_reg: + return record.extra_idx(key=key) + + return _SVExtra.NONE + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, prefix="", indent=""): + (vector, value, span) = self.spec(insn=insn, record=record) + + if verbosity >= Verbosity.VERBOSE: + mode = "vector" if vector else "scalar" + yield f"{indent}{self.name} ({mode})" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + if isinstance(insn, SVP64Instruction): + extra_idx = self.extra_idx(record) + if record.etype is _SVEtype.NONE: + yield f"{indent}{indent}extra[none]" + else: + etype = repr(record.etype).lower() + yield f"{indent}{indent}{etype}{extra_idx!r}" + else: + vector = "*" if vector else "" + yield f"{vector}{prefix}{int(value)}" + + +class GPROperand(RegisterOperand): + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + prefix = "" if (verbosity <= Verbosity.SHORT) else "r" + yield from super().disassemble(prefix=prefix, + insn=insn, record=record, + verbosity=verbosity, indent=indent) + + +class FPROperand(RegisterOperand): + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + prefix = "" if (verbosity <= Verbosity.SHORT) else "f" + yield from super().disassemble(prefix=prefix, + insn=insn, record=record, + verbosity=verbosity, indent=indent) + + +class CR3Operand(RegisterOperand): + pass + + +class CR5Operand(RegisterOperand): + def sv_spec_enter(self, value, span): + value = _SelectableInt(value=(value.value >> 2), bits=3) + return (value, span) + + def sv_spec_leave(self, value, span, origin_value, origin_span): + value = _selectconcat(value, origin_value[3:5]) + span += origin_span + return (value, span) + + +class TargetAddrOperand(RegisterOperand): + def disassemble(self, insn, record, field, + verbosity=Verbosity.NORMAL, indent=""): + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + + if verbosity >= Verbosity.VERBOSE: + span = tuple(map(str, span)) + yield f"{indent}{self.name} = EXTS({field} || 0b00))" + yield f"{indent}{indent}{field}" + yield f"{indent}{indent}{indent}{int(value):0{value.bits}b}00" + yield f"{indent}{indent}{indent}{', '.join(span + ('{0}', '{0}'))}" + else: + yield hex(_selectconcat(value, + _SelectableInt(value=0b00, bits=2)).to_signed_int()) + + +class TargetAddrOperandLI(TargetAddrOperand): + def span(self, record): + return record.fields["LI"] + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + return super().disassemble(field="LI", + insn=insn, record=record, + verbosity=verbosity, indent=indent) + + +class TargetAddrOperandBD(TargetAddrOperand): + def span(self, record): + return record.fields["BD"] + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + return super().disassemble(field="BD", + insn=insn, record=record, + verbosity=verbosity, indent=indent) + + +class DOperandDX(SignedOperand): + def span(self, record): + operands = map(DynamicOperand, ("d0", "d1", "d2")) + spans = map(lambda operand: operand.span(record=record), operands) + return sum(spans, tuple()) + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): + span = self.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + + if verbosity >= Verbosity.VERBOSE: + yield f"{indent}D" + mapping = { + "d0": "[0:9]", + "d1": "[10:15]", + "d2": "[16]", + } + for (subname, subspan) in mapping.items(): + operand = DynamicOperand(name=subname) + span = operand.span(record=record) + if isinstance(insn, SVP64Instruction): + span = tuple(map(lambda bit: (bit + 32), span)) + value = insn[span] + span = map(str, span) + yield f"{indent}{indent}{operand.name} = D{subspan}" + yield f"{indent}{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{indent}{', '.join(span)}" + else: + yield str(value.to_signed_int()) + + +class Operands(tuple): + def __new__(cls, insn, iterable): + custom_insns = { + "b": {"target_addr": TargetAddrOperandLI}, + "ba": {"target_addr": TargetAddrOperandLI}, + "bl": {"target_addr": TargetAddrOperandLI}, + "bla": {"target_addr": TargetAddrOperandLI}, + "bc": {"target_addr": TargetAddrOperandBD}, + "bca": {"target_addr": TargetAddrOperandBD}, + "bcl": {"target_addr": TargetAddrOperandBD}, + "bcla": {"target_addr": TargetAddrOperandBD}, + "addpcis": {"D": DOperandDX}, + "fishmv": {"D": DOperandDX}, + "fmvis": {"D": DOperandDX}, + } + custom_fields = { + "SVi": NonZeroOperand, + "SVd": NonZeroOperand, + "SVxd": NonZeroOperand, + "SVyd": NonZeroOperand, + "SVzd": NonZeroOperand, + "BD": SignedOperand, + "D": SignedOperand, + "DQ": SignedOperand, + "DS": SignedOperand, + "SI": SignedOperand, + "IB": SignedOperand, + "LI": SignedOperand, + "SIM": SignedOperand, + "SVD": SignedOperand, + "SVDS": SignedOperand, } operands = [] for operand in iterable: - dynamic_cls = self.__class__.DynamicOperand - static_cls = self.__class__.StaticOperand + dynamic_cls = DynamicOperand + static_cls = StaticOperand if "=" in operand: (name, value) = operand.split("=") operand = static_cls(name=name, value=int(value)) + operands.append(operand) else: - if insn in branches and operand in branches[insn]: - dynamic_cls = branches[insn][operand] + if operand.endswith(")"): + operand = operand.replace("(", " ").replace(")", "") + (immediate, _, operand) = operand.partition(" ") + else: + immediate = None + + if immediate is not None: + operands.append(ImmediateOperand(name=immediate)) + + if operand in custom_fields: + dynamic_cls = custom_fields[operand] + if insn in custom_insns and operand in custom_insns[insn]: + dynamic_cls = custom_insns[insn][operand] if operand in _RegType.__members__: regtype = _RegType[operand] if regtype is _RegType.GPR: - dynamic_cls = self.__class__.DynamicOperandGPR + dynamic_cls = GPROperand elif regtype is _RegType.FPR: - dynamic_cls = self.__class__.DynamicOperandFPR + dynamic_cls = FPROperand + if regtype is _RegType.CR_BIT: # 5-bit + dynamic_cls = CR5Operand + if regtype is _RegType.CR_REG: # actually CR Field, 3-bit + dynamic_cls = CR3Operand operand = dynamic_cls(name=operand) + operands.append(operand) - operands.append(operand) - - self.__operands = operands - - return super().__init__() - - def __repr__(self): - return self.__operands.__repr__() - - def __iter__(self): - yield from self.__operands + return super().__new__(cls, operands) def __contains__(self, key): return self.__getitem__(key) is not None def __getitem__(self, key): - for operand in self.__operands: + for operand in self: if operand.name == key: return operand @@ -504,16 +896,34 @@ class Operands: @property def dynamic(self): for operand in self: - if isinstance(operand, self.__class__.DynamicOperand): + if isinstance(operand, DynamicOperand): yield operand @property def static(self): for operand in self: - if isinstance(operand, self.__class__.StaticOperand): + if isinstance(operand, StaticOperand): yield operand +class PCode: + def __init__(self, iterable): + self.__pcode = tuple(iterable) + return super().__init__() + + def __iter__(self): + yield from self.__pcode + + def __repr__(self): + return self.__pcode.__repr__() + + +@_dataclasses.dataclass(eq=True, frozen=True) +class MarkdownRecord: + pcode: PCode + operands: Operands + + @_functools.total_ordering @_dataclasses.dataclass(eq=True, frozen=True) class Record: @@ -521,34 +931,49 @@ class Record: section: Section ppc: PPCRecord fields: Fields - operands: Operands + mdwn: MarkdownRecord svp64: SVP64Record = None - __EXTRA = ( - _SVExtra.Idx0, - _SVExtra.Idx1, - _SVExtra.Idx2, - _SVExtra.Idx3, - ) - def __lt__(self, other): if not isinstance(other, Record): return NotImplemented - return (self.opcode < other.opcode) + return (min(self.opcodes) < min(other.opcodes)) - @cached_property - def opcode(self): - fields = [] - if self.section.opcode: - fields += [(self.section.opcode.value, BitSel((0, 5)))] - fields += [(self.ppc.opcode.value, self.section.bitsel)] - else: - fields += [(self.ppc.opcode.value, self.section.bitsel)] + @property + def opcodes(self): + def opcode(ppc): + value = ([0] * 32) + mask = ([0] * 32) + + PO = self.section.opcode + if PO is not None: + for (src, dst) in enumerate(reversed(BitSel((0, 5)))): + value[dst] = int((PO.value & (1 << src)) != 0) + mask[dst] = int((PO.mask & (1 << src)) != 0) + + XO = ppc.opcode + for (src, dst) in enumerate(reversed(self.section.bitsel)): + value[dst] = int((XO.value & (1 << src)) != 0) + mask[dst] = int((XO.mask & (1 << src)) != 0) + + for operand in self.mdwn.operands.static: + for (src, dst) in enumerate(reversed(operand.span(record=self))): + value[dst] = int((operand.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)) - for operand in self.operands.static: - fields += [(operand.value, self.fields[operand.name])] + return Opcode(value=value, mask=mask) - return FieldsOpcode(fields) + return tuple(sorted(map(opcode, self.ppc))) + + def match(self, key): + for opcode in self.opcodes: + if ((opcode.value & opcode.mask) == + (key & opcode.mask)): + return True + return False @property def function(self): @@ -580,58 +1005,27 @@ class Record: def cr_in(self): return self.ppc.cr_in + @property + def cr_in2(self): + return self.ppc.cr_in2 + @property def cr_out(self): return self.ppc.cr_out - def sv_extra(self, key): - if key not in frozenset({ - "in1", "in2", "in3", "cr_in", - "out", "out2", "cr_out", - }): - raise KeyError(key) + ptype = property(lambda self: self.svp64.ptype) + etype = property(lambda self: self.svp64.etype) - sel = getattr(self.svp64, key) - if sel is _CRInSel.BA_BB: - return _SVExtra.Idx_1_2 - reg = _SVExtraReg(sel) - if reg is _SVExtraReg.NONE: - return _SVExtra.NONE - - extra_map = { - _SVExtraRegType.SRC: {}, - _SVExtraRegType.DST: {}, - } - for index in range(0, 4): - for entry in self.svp64.extra[index]: - extra_map[entry.regtype][entry.reg] = Record.__EXTRA[index] - - for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST): - extra = extra_map[regtype].get(reg, _SVExtra.NONE) - if extra is not _SVExtra.NONE: - return extra - - return _SVExtra.NONE - - sv_in1 = property(_functools.partial(sv_extra, key="in1")) - sv_in2 = property(_functools.partial(sv_extra, key="in2")) - sv_in3 = property(_functools.partial(sv_extra, key="in3")) - sv_out = property(_functools.partial(sv_extra, key="out")) - sv_out2 = property(_functools.partial(sv_extra, key="out2")) - sv_cr_in = property(_functools.partial(sv_extra, key="cr_in")) - sv_cr_out = property(_functools.partial(sv_extra, key="cr_out")) - - @property - def sv_ptype(self): - if self.svp64 is None: - return _SVPtype.NONE - return self.svp64.ptype + def extra_idx(self, key): + return self.svp64.extra_idx(key) - @property - def sv_etype(self): - if self.svp64 is None: - return _SVEtype.NONE - return self.svp64.etype + extra_idx_in1 = property(lambda self: self.svp64.extra_idx_in1) + extra_idx_in2 = property(lambda self: self.svp64.extra_idx_in2) + extra_idx_in3 = property(lambda self: self.svp64.extra_idx_in3) + extra_idx_out = property(lambda self: self.svp64.extra_idx_out) + extra_idx_out2 = property(lambda self: self.svp64.extra_idx_out2) + extra_idx_cr_in = property(lambda self: self.svp64.extra_idx_cr_in) + extra_idx_cr_out = property(lambda self: self.svp64.extra_idx_cr_out) class Instruction(_Mapping): @@ -664,7 +1058,70 @@ class Instruction(_Mapping): def __hash__(self): return hash(int(self)) - def disassemble(self, db, byteorder="little"): + def __getitem__(self, key): + return self.storage.__getitem__(key) + + def __setitem__(self, key, value): + return self.storage.__setitem__(key, value) + + def bytes(self, byteorder="little"): + nr_bytes = (self.storage.bits // 8) + return int(self).to_bytes(nr_bytes, byteorder=byteorder) + + def record(self, db): + record = db[self] + if record is None: + raise KeyError(self) + return record + + def spec(self, db, prefix): + record = self.record(db=db) + + dynamic_operands = tuple(map(_operator.itemgetter(0), + self.dynamic_operands(db=db))) + + static_operands = [] + for (name, value) in self.static_operands(db=db): + static_operands.append(f"{name}={value}") + + operands = "" + if dynamic_operands: + operands += f" {','.join(dynamic_operands)}" + if static_operands: + operands += f" ({' '.join(static_operands)})" + + return f"{prefix}{record.name}{operands}" + + def dynamic_operands(self, db, verbosity=Verbosity.NORMAL): + record = self.record(db=db) + + imm = False + imm_name = "" + imm_value = "" + for operand in record.mdwn.operands.dynamic: + name = operand.name + dis = operand.disassemble(insn=self, record=record, + verbosity=min(verbosity, Verbosity.NORMAL)) + value = " ".join(dis) + if imm: + name = f"{imm_name}({name})" + value = f"{imm_value}({value})" + imm = False + if isinstance(operand, ImmediateOperand): + imm_name = name + imm_value = value + imm = True + if not imm: + yield (name, value) + + def static_operands(self, db): + record = self.record(db=db) + for operand in record.mdwn.operands.static: + yield (operand.name, operand.value) + + def disassemble(self, db, + byteorder="little", + verbosity=Verbosity.NORMAL): raise NotImplementedError @@ -676,25 +1133,59 @@ class WordInstruction(Instruction): def integer(cls, value, byteorder="little"): return super().integer(bits=32, value=value, byteorder=byteorder) - def disassemble(self, db, byteorder="little"): + @property + def binary(self): + bits = [] + for idx in range(32): + bit = int(self[idx]) + bits.append(bit) + return "".join(map(str, bits)) + + def disassemble(self, db, + byteorder="little", + verbosity=Verbosity.NORMAL): integer = int(self) - blob = integer.to_bytes(length=4, byteorder=byteorder) - blob = " ".join(map(lambda byte: f"{byte:02x}", blob)) + if verbosity <= Verbosity.SHORT: + blob = "" + else: + blob = integer.to_bytes(length=4, byteorder=byteorder) + blob = " ".join(map(lambda byte: f"{byte:02x}", blob)) + blob += " " record = db[self] if record is None: - yield f"{blob} .long 0x{integer:08x}" + yield f"{blob}.long 0x{integer:08x}" + return + + operands = tuple(map(_operator.itemgetter(1), + self.dynamic_operands(db=db, verbosity=verbosity))) + if operands: + yield f"{blob}{record.name} {','.join(operands)}" else: - operands = [] - for operand in record.operands.dynamic: - operand = operand.disassemble(self, record) - operands.append(operand) - if operands: - operands = ",".join(operands) - operands = f" {operands}" - else: - operands = "" - yield f"{blob} {record.name}{operands}" + yield f"{blob}{record.name}" + + if verbosity >= Verbosity.VERBOSE: + indent = (" " * 4) + binary = self.binary + spec = self.spec(db=db, prefix="") + yield f"{indent}spec" + yield f"{indent}{indent}{spec}" + yield f"{indent}pcode" + for stmt in record.mdwn.pcode: + yield f"{indent}{indent}{stmt}" + yield f"{indent}binary" + yield f"{indent}{indent}[0:8] {binary[0:8]}" + yield f"{indent}{indent}[8:16] {binary[8:16]}" + yield f"{indent}{indent}[16:24] {binary[16:24]}" + yield f"{indent}{indent}[24:32] {binary[24:32]}" + yield f"{indent}opcodes" + for opcode in record.opcodes: + yield f"{indent}{indent}{opcode!r}" + for operand in record.mdwn.operands: + yield from operand.disassemble(insn=self, record=record, + verbosity=verbosity, indent=indent) + yield "" + class PrefixedInstruction(Instruction): class Prefix(WordInstruction.remap(range(0, 32))): @@ -721,79 +1212,146 @@ class PrefixedInstruction(Instruction): (prefix, suffix) = map(transform, (prefix, suffix)) value = _selectconcat(prefix, suffix) - return super().integer(value=value) + return super().integer(bits=64, value=value) 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 Extra(_Mapping): + _: _Field = range(0, 9) - class smr(Mode): - """scalar reduce mode (mapreduce), SUBVL=1""" - RG: Mode[4] - class pmr(Mode): - """parallel reduce mode (mapreduce), SUBVL=1""" +class Extra2(Extra): + idx0: _Field = range(0, 2) + idx1: _Field = range(2, 4) + idx2: _Field = range(4, 6) + idx3: _Field = range(6, 8) + + def __getitem__(self, key): + return { + 0: self.idx0, + 1: self.idx1, + 2: self.idx2, + 3: self.idx3, + _SVExtra.Idx0: self.idx0, + _SVExtra.Idx1: self.idx1, + _SVExtra.Idx2: self.idx2, + _SVExtra.Idx3: self.idx3, + }[key] + + def __setitem__(self, key, value): + self[key].assign(value) + + +class Extra3(Extra): + idx0: _Field = range(0, 3) + idx1: _Field = range(3, 6) + idx2: _Field = range(6, 9) + + def __getitem__(self, key): + return { + 0: self.idx0, + 1: self.idx1, + 2: self.idx2, + _SVExtra.Idx0: self.idx0, + _SVExtra.Idx1: self.idx1, + _SVExtra.Idx2: self.idx2, + }[key] + + def __setitem__(self, key, value): + self[key].assign(value) + + +class BaseRM(_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) + mode: Mode.remap(range(19, 24)) + smask: _Field = range(16, 19) + + extra: Extra.remap(range(10, 19)) + extra2: Extra2.remap(range(10, 19)) + extra3: Extra3.remap(range(10, 19)) + + def disassemble(self, verbosity=Verbosity.NORMAL): + if verbosity >= Verbosity.VERBOSE: + indent = (" " * 4) + for (name, value, members) in self.traverse(path="RM"): + yield f"{name}" + yield f"{indent}{int(value):0{value.bits}b}" + yield f"{indent}{', '.join(map(str, members))}" + + +class NormalRM(BaseRM): + class simple(BaseRM): + """normal: simple mode""" + dz: BaseRM.mode[3] + sz: BaseRM.mode[4] + + class smr(BaseRM): + """normal: scalar reduce mode (mapreduce), SUBVL=1""" + RG: BaseRM.mode[4] + + class pmr(BaseRM): + """normal: 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] + class svmr(BaseRM): + """normal: subvector reduce mode, SUBVL>1""" + SVM: BaseRM.mode[3] + + class pu(BaseRM): + """normal: Pack/Unpack mode, SUBVL>1""" + SVM: BaseRM.mode[3] + + class ffrc1(BaseRM): + """normal: Rc=1: ffirst CR sel""" + inv: BaseRM.mode[2] + CR: BaseRM.mode[3, 4] + + class ffrc0(BaseRM): + """normal: Rc=0: ffirst z/nonz""" + inv: BaseRM.mode[2] + VLi: BaseRM.mode[3] + RC1: BaseRM.mode[4] + + class sat(BaseRM): + """normal: sat mode: N=0/1 u/s, SUBVL=1""" + N: BaseRM.mode[2] + dz: BaseRM.mode[3] + sz: BaseRM.mode[4] + + class satx(BaseRM): + """normal: sat mode: N=0/1 u/s, SUBVL>1""" + N: BaseRM.mode[2] + zz: BaseRM.mode[3] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] + + class satpu(BaseRM): + """normal: Pack/Unpack sat mode: N=0/1 u/s, SUBVL>1""" + N: BaseRM.mode[2] + zz: BaseRM.mode[3] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] + + class prrc1(BaseRM): + """normal: Rc=1: pred-result CR sel""" + inv: BaseRM.mode[2] + CR: BaseRM.mode[3, 4] + + class prrc0(BaseRM): + """normal: Rc=0: pred-result z/nonz""" + inv: BaseRM.mode[2] + zz: BaseRM.mode[3] + RC1: BaseRM.mode[4] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] simple: simple smr: smr @@ -809,50 +1367,50 @@ class NormalMode(Mode): 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] +class LDSTImmRM(BaseRM): + class simple(BaseRM): + """ld/st immediate: simple mode""" + zz: BaseRM.mode[3] + els: BaseRM.mode[4] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] + + class spu(BaseRM): + """ld/st immediate: Structured Pack/Unpack""" + zz: BaseRM.mode[3] + els: BaseRM.mode[4] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] + + class ffrc1(BaseRM): + """ld/st immediate: Rc=1: ffirst CR sel""" + inv: BaseRM.mode[2] + CR: BaseRM.mode[3, 4] + + class ffrc0(BaseRM): + """ld/st immediate: Rc=0: ffirst z/nonz""" + inv: BaseRM.mode[2] + els: BaseRM.mode[3] + RC1: BaseRM.mode[4] + + class sat(BaseRM): + """ld/st immediate: sat mode: N=0/1 u/s""" + N: BaseRM.mode[2] + zz: BaseRM.mode[3] + els: BaseRM.mode[4] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] + + class prrc1(BaseRM): + """ld/st immediate: Rc=1: pred-result CR sel""" + inv: BaseRM.mode[2] + CR: BaseRM.mode[3, 4] + + class prrc0(BaseRM): + """ld/st immediate: Rc=0: pred-result z/nonz""" + inv: BaseRM.mode[2] + els: BaseRM.mode[3] + RC1: BaseRM.mode[4] simple: simple spu: spu @@ -863,37 +1421,37 @@ class LDSTImmMode(Mode): 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] +class LDSTIdxRM(BaseRM): + class simple(BaseRM): + """ld/st index: simple mode""" + SEA: BaseRM.mode[2] + sz: BaseRM.mode[3] + dz: BaseRM.mode[3] + + class stride(BaseRM): + """ld/st index: strided (scalar only source)""" + SEA: BaseRM.mode[2] + dz: BaseRM.mode[3] + sz: BaseRM.mode[4] + + class sat(BaseRM): + """ld/st index: sat mode: N=0/1 u/s""" + N: BaseRM.mode[2] + dz: BaseRM.mode[3] + sz: BaseRM.mode[4] + + class prrc1(BaseRM): + """ld/st index: Rc=1: pred-result CR sel""" + inv: BaseRM.mode[2] + CR: BaseRM.mode[3, 4] + + class prrc0(BaseRM): + """ld/st index: Rc=0: pred-result z/nonz""" + inv: BaseRM.mode[2] + zz: BaseRM.mode[3] + RC1: BaseRM.mode[4] + dz: BaseRM.mode[3] + sz: BaseRM.mode[3] simple: simple stride: stride @@ -902,32 +1460,215 @@ class LDSTIdxMode(Mode): prrc0: prrc0 -class RM(_Mapping): - class Mode(Mode): - normal: NormalMode - ldst_imm: LDSTImmMode - ldst_idx: LDSTIdxMode +class CROpRM(BaseRM): + class simple(BaseRM): + """cr_op: simple mode""" + sz: BaseRM[6] + SNZ: BaseRM[7] + RG: BaseRM[20] + dz: BaseRM[22] + + class smr(BaseRM): + """cr_op: scalar reduce mode (mapreduce), SUBVL=1""" + sz: BaseRM[6] + SNZ: BaseRM[7] + RG: BaseRM[20] + + class svmr(BaseRM): + """cr_op: subvector reduce mode, SUBVL>1""" + zz: BaseRM[6] + SNZ: BaseRM[7] + RG: BaseRM[20] + SVM: BaseRM[22] + dz: BaseRM[6] + sz: BaseRM[6] + + class reserved(BaseRM): + """cr_op: reserved""" + zz: BaseRM[6] + SNZ: BaseRM[7] + RG: BaseRM[20] + dz: BaseRM[6] + sz: BaseRM[6] + + class ff3(BaseRM): + """cr_op: ffirst 3-bit mode""" + zz: BaseRM[6] + SNZ: BaseRM[7] + VLI: BaseRM[20] + inv: BaseRM[21] + CR: BaseRM[22, 23] + dz: BaseRM[6] + sz: BaseRM[6] + + class ff5(BaseRM): + """cr_op: ffirst 5-bit mode""" + zz: BaseRM[6] + SNZ: BaseRM[7] + VLI: BaseRM[20] + inv: BaseRM[21] + dz: BaseRM[22] + dz: BaseRM[6] + sz: BaseRM[6] - _: _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), - ) + simple: simple + smr: smr + svmr: svmr + reserved: reserved + ff3: ff3 + ff5: ff5 + + +class BranchBaseRM(BaseRM): + ALL: BaseRM[4] + SNZ: BaseRM[5] + SL: BaseRM[17] + SLu: BaseRM[18] + LRu: BaseRM[22] + sz: BaseRM[23] + + +class BranchRM(BranchBaseRM): + class simple(BranchBaseRM): + """branch: simple mode""" + pass + + class vls(BranchBaseRM): + """branch: VLSET mode""" + VSb: BaseRM[7] + VLI: BaseRM[21] + + class ctr(BranchBaseRM): + """branch: CTR-test mode""" + CTi: BaseRM[6] + + class ctrvls(vls, ctr): + """branch: CTR-test+VLSET mode""" + pass + + +class RM(BaseRM): + normal: NormalRM + ldst_imm: LDSTImmRM + ldst_idx: LDSTIdxRM + cr_op: CROpRM + + def select(self, record, Rc): + rm = self + + if record.svp64.mode is _SVMode.NORMAL: + rm = rm.normal + if rm.mode[0:2] == 0b00: + if rm.mode[2] == 0b0: + rm = rm.simple + else: + if self.subvl == 0b00: + if rm.mode[3] == 0b0: + rm = rm.smr + else: + rm = rm.pmr + else: + if rm.mode[4] == 0b0: + rm = rm.svmr + else: + rm = rm.pu + elif rm.mode[0:2] == 0b01: + if Rc: + rm = rm.ffrc1 + else: + rm = rm.ffrc0 + elif rm.mode[0:2] == 0b10: + if self.subvl == 0b00: + rm = rm.sat + else: + if rm.mode[4]: + rm = rm.satx + else: + rm = rm.satpu + elif rm.mode[0:2] == 0b11: + if Rc: + rm = rm.prrc1 + else: + rm = rm.prrc0 + + elif record.svp64.mode is _SVMode.LDST_IMM: + rm = rm.ldst_imm + if rm.mode[0:2] == 0b00: + if rm.mode[2] == 0b0: + rm = rm.simple + else: + rm = rm.spu + elif rm.mode[0:2] == 0b01: + if Rc: + rm = rm.ffrc1 + else: + rm = rm.ffrc0 + elif rm.mode[0:2] == 0b10: + rm = rm.sat + elif rm.mode[0:2] == 0b11: + if Rc: + rm = rm.prrc1 + else: + rm = rm.prrc0 + + elif record.svp64.mode is _SVMode.LDST_IMM: + rm = rm.ldst_idx + if rm.mode[0:2] == 0b00: + rm = rm.simple + elif rm.mode[0:2] == 0b01: + rm = rm.stride + elif rm.mode[0:2] == 0b10: + rm = rm.sat + elif rm.mode[0:2] == 0b11: + if Rc: + rm = rm.prrc1 + else: + rm = rm.prrc0 + + elif record.svp64.mode is _SVMode.CROP: + rm = rm.cr_op + if rm[19] == 0b0: + if rm[21] == 0b0: + rm = rm.simple + else: + if self.subvl == 0: + rm = rm.smr + else: + if rm[23] == 0b0: + rm = rm.svmr + else: + rm = rm.reserved + else: + regtype = None + for idx in range(0, 4): + for entry in record.svp64.extra[idx]: + if entry.regtype is _SVExtraRegType.DST: + if regtype is not None: + raise ValueError(record.svp64) + regtype = _RegType(entry.reg) + if regtype is _RegType.CR_REG: + rm = rm.ff5 + elif regtype is _RegType.CR_BIT: + rm = rm.ff3 + else: + raise ValueError(record.svp64) + + elif record.svp64.mode is _SVMode.BRANCH: + if rm[19] == 0b0: + if rm[20] == 0b0: + rm = rm.simple + else: + rm = rm.vls + else: + if rm[20] == 0b0: + rm = rm.ctr + else: + rm = rm.ctrvls + + if rm.__class__ is self.__class__: + raise ValueError(self) + + return rm class SVP64Instruction(PrefixedInstruction): @@ -938,28 +1679,92 @@ class SVP64Instruction(PrefixedInstruction): prefix: Prefix - def disassemble(self, db, byteorder="little"): - 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)) + def record(self, db): + record = db[self.suffix] + if record is None: + raise KeyError(self) + return record - 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)) + @property + def binary(self): + bits = [] + for idx in range(64): + bit = int(self[idx]) + bits.append(bit) + return "".join(map(str, bits)) + + def disassemble(self, db, + byteorder="little", + verbosity=Verbosity.NORMAL): + def blob(integer): + if verbosity <= Verbosity.SHORT: + return "" + else: + blob = integer.to_bytes(length=4, byteorder=byteorder) + blob = " ".join(map(lambda byte: f"{byte:02x}", blob)) + return f"{blob} " - record = db[self.suffix] + record = self.record(db=db) + blob_prefix = blob(int(self.prefix)) + blob_suffix = blob(int(self.suffix)) if record is None or record.svp64 is None: - yield f"{blob_prefix} .long 0x{int(self.prefix):08x}" - yield f"{blob_suffix} .long 0x{int(self.suffix):08x}" + yield f"{blob_prefix}.long 0x{int(self.prefix):08x}" + yield f"{blob_suffix}.long 0x{int(self.suffix):08x}" + return + + operands = tuple(map(_operator.itemgetter(1), + self.dynamic_operands(db=db, verbosity=verbosity))) + if operands: + yield f"{blob_prefix}sv.{record.name} {','.join(operands)}" else: - yield f"{blob_prefix} sv.{record.name}" + yield f"{blob_prefix}{record.name}" + if blob_suffix: yield f"{blob_suffix}" + Rc = False + if record.mdwn.operands["Rc"] is not None: + Rc = bool(self.suffix[record.fields["Rc"]]) + + rm = self.prefix.rm.select(record=record, Rc=Rc) + if verbosity >= Verbosity.VERBOSE: + indent = (" " * 4) + binary = self.binary + spec = self.spec(db=db, prefix="sv.") + + yield f"{indent}spec" + yield f"{indent}{indent}{spec}" + yield f"{indent}pcode" + for stmt in record.mdwn.pcode: + yield f"{indent}{indent}{stmt}" + yield f"{indent}binary" + yield f"{indent}{indent}[0:8] {binary[0:8]}" + yield f"{indent}{indent}[8:16] {binary[8:16]}" + yield f"{indent}{indent}[16:24] {binary[16:24]}" + yield f"{indent}{indent}[24:32] {binary[24:32]}" + yield f"{indent}{indent}[32:40] {binary[32:40]}" + yield f"{indent}{indent}[40:48] {binary[40:48]}" + yield f"{indent}{indent}[48:56] {binary[48:56]}" + yield f"{indent}{indent}[56:64] {binary[56:64]}" + yield f"{indent}opcodes" + for opcode in record.opcodes: + yield f"{indent}{indent}{opcode!r}" + for operand in record.mdwn.operands: + yield from operand.disassemble(insn=self, record=record, + verbosity=verbosity, indent=indent) + yield f"{indent}RM" + yield f"{indent}{indent}{rm.__doc__}" + for line in rm.disassemble(verbosity=verbosity): + yield f"{indent}{indent}{line}" + yield "" + def parse(stream, factory): + def match(entry): + return ("TODO" not in frozenset(entry.values())) + lines = filter(lambda line: not line.strip().startswith("#"), stream) entries = _csv.DictReader(lines) - entries = filter(lambda entry: "TODO" not in frozenset(entry.values()), entries) + entries = filter(match, entries) return tuple(map(factory, entries)) @@ -972,13 +1777,20 @@ class MarkdownDatabase: (dynamic, *static) = desc.regs operands.extend(dynamic) operands.extend(static) - db[name] = Operands(insn=name, iterable=operands) + pcode = PCode(iterable=desc.pcode) + operands = Operands(insn=name, iterable=operands) + db[name] = MarkdownRecord(pcode=pcode, operands=operands) + self.__db = db + return super().__init__() def __iter__(self): yield from self.__db.items() + def __contains__(self, key): + return self.__db.__contains__(key) + def __getitem__(self, key): return self.__db.__getitem__(key) @@ -1004,7 +1816,7 @@ class FieldsDatabase: class PPCDatabase: - def __init__(self, root, mdwndb, fieldsdb): + def __init__(self, root, mdwndb): # 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. @@ -1018,98 +1830,70 @@ class PPCDatabase: section.Mode.INTEGER: IntegerOpcode, section.Mode.PATTERN: PatternOpcode, }[section.mode] - factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls) + factory = _functools.partial( + PPCRecord.CSV, opcode_cls=opcode_cls) with open(path, "r", encoding="UTF-8") as stream: for insn in parse(stream, factory): records[section][insn.comment].add(insn) - # Once we collected all instructions with the same identifier, - # it's time to merge the different opcodes into the single pattern. - # At this point, we only consider masks; the algorithm as follows: - # 1. If any of two masks ignores the bit, it's ignored entirely. - # 2. If the bit is not equal between masks, it's ignored. - # 3. Otherwise the bits are equal and considered. - def merge(lhs, rhs): - value = 0 - mask = 0 - lvalue = lhs.opcode.value - rvalue = rhs.opcode.value - lmask = lhs.opcode.mask - rmask = rhs.opcode.mask - bits = max(lmask.bit_length(), rmask.bit_length()) - for bit in range(bits): - lvstate = ((lvalue & (1 << bit)) != 0) - rvstate = ((rvalue & (1 << bit)) != 0) - lmstate = ((lmask & (1 << bit)) != 0) - rmstate = ((rmask & (1 << bit)) != 0) - vstate = lvstate - mstate = True - if (not lmstate or not rmstate) or (lvstate != rvstate): - vstate = 0 - mstate = 0 - value |= (vstate << bit) - mask |= (mstate << bit) - return _dataclasses.replace(lhs, opcode=Opcode(value=value, mask=mask)) - - db = dd(set) + sections = dd(set) for (section, group) in records.items(): for records in group.values(): - db[section].add(_functools.reduce(merge, records)) + sections[section].add(PPCMultiRecord(records)) + + db = {} + for (section, records) in sections.items(): + for record in records: + def exact_match(names): + for name in names: + if name in mdwndb: + yield name + + def Rc_match(names): + for name in names: + if f"{name}." in mdwndb: + yield f"{name}." + yield name + + def LK_match(names): + if "lk" not in record.flags: + yield from names + return + + for name in names: + if f"{name}l" in mdwndb: + yield f"{name}l" + yield name + + def AA_match(names): + if record.intop not in {_MicrOp.OP_B, _MicrOp.OP_BC}: + yield from names + return + + for name in names: + operands = mdwndb[name].operands["AA"] + if ((operands is not None) and + (f"{name}a" in mdwndb)): + yield f"{name}a" + yield name + + def reductor(names, match): + return match(names) + + matches = (exact_match, Rc_match, LK_match, AA_match) + + names = _functools.reduce(reductor, matches, record.names) + for name in names: + db[name] = (section, record) self.__db = db self.__mdwndb = mdwndb - self.__fieldsdb = fieldsdb return super().__init__() + @_functools.lru_cache(maxsize=512, typed=False) 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: - 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) + return self.__db.get(key, (None, None)) class SVP64Database: @@ -1144,14 +1928,16 @@ class SVP64Database: class Database: def __init__(self, root): root = _pathlib.Path(root) - mdwndb = MarkdownDatabase() fieldsdb = FieldsDatabase() - ppcdb = PPCDatabase(root=root, mdwndb=mdwndb, fieldsdb=fieldsdb) + ppcdb = PPCDatabase(root=root, mdwndb=mdwndb) svp64db = SVP64Database(root=root, ppcdb=ppcdb) db = set() - for (name, operands) in mdwndb: + names = {} + opcodes = _collections.defaultdict(set) + + for (name, mdwn) in mdwndb: (section, ppc) = ppcdb[name] if ppc is None: continue @@ -1159,10 +1945,17 @@ class Database: fields = fieldsdb[ppc.form] record = Record(name=name, section=section, ppc=ppc, svp64=svp64, - operands=operands, fields=fields) + mdwn=mdwn, fields=fields) db.add(record) + names[record.name] = record + PO = section.opcode + if PO is None: + PO = ppc[0].opcode + opcodes[PO.value].add(record) - self.__db = tuple(sorted(db)) + self.__db = db + self.__names = names + self.__opcodes = opcodes return super().__init__() @@ -1180,18 +1973,12 @@ class Database: def __getitem__(self, key): if isinstance(key, (int, Instruction)): key = int(key) - for record in self: - opcode = record.opcode - if ((opcode.value & opcode.mask) == - (key & opcode.mask)): - return record - return None - elif isinstance(key, Opcode): - for record in self: - if record.opcode == key: - return record + XO = int(_SelectableInt(value=int(key), bits=32)[0:6]) + for record in self.__opcodes[XO]: + if record.match(key=key): + return record + elif isinstance(key, str): - for record in self: - if record.name == key: - return record + return self.__names[key] + return None