X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=inline;f=src%2Fopenpower%2Fdecoder%2Fpower_insn.py;h=1f216388bb574a20ed5e1828ca9d1c0b61bc1b04;hb=c4a41c639a14722625e9be0cea85dfc203afdcc3;hp=e9a51625c72378cf36c123ce4873da08359b9314;hpb=1c300aa269485debd09869ee15e4dcf8a93eee24;p=openpower-isa.git diff --git a/src/openpower/decoder/power_insn.py b/src/openpower/decoder/power_insn.py index e9a51625..1f216388 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, @@ -27,6 +30,7 @@ from openpower.decoder.power_enums import ( CryIn as _CryIn, Form as _Form, SVEtype as _SVEtype, + SVmask_src as _SVmask_src, SVMode as _SVMode, SVPtype as _SVPtype, SVExtra as _SVExtra, @@ -40,13 +44,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 +89,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 +127,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") + + 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" - object.__setattr__(self, "value", self.__class__.Value(value)) - object.__setattr__(self, "mask", self.__class__.Mask(mask)) + 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 +172,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 +214,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,10 +236,15 @@ 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, "")): @@ -236,38 +260,11 @@ class PPCRecord: return frozenset(self.comment.split("=")[-1].split("/")) -class PPCMultiRecord(frozenset): - @cached_property - def unified(self): - def merge(lhs, rhs): - value = 0 - mask = 0 - lvalue = lhs.opcode.value - rvalue = rhs.opcode.value - lmask = lhs.opcode.mask - rmask = rhs.opcode.mask - bits = max(lmask.bit_length(), rmask.bit_length()) - for bit in range(bits): - lvstate = ((lvalue & (1 << bit)) != 0) - rvstate = ((rvalue & (1 << bit)) != 0) - lmstate = ((lmask & (1 << bit)) != 0) - rmstate = ((rmask & (1 << bit)) != 0) - vstate = lvstate - mstate = True - if (not lmstate or not rmstate) or (lvstate != rvstate): - vstate = 0 - mstate = 0 - value |= (vstate << bit) - mask |= (mstate << bit) - - opcode = opcode=Opcode(value=value, mask=mask) - - return _dataclasses.replace(lhs, opcode=opcode) - - return _functools.reduce(merge, self) - +class PPCMultiRecord(tuple): def __getattr__(self, attr): - return getattr(self.unified, attr) + if attr == "opcode": + raise AttributeError(attr) + return getattr(self[0], attr) @_dataclasses.dataclass(eq=True, frozen=True) @@ -312,12 +309,14 @@ class SVP64Record: name: str ptype: _SVPtype = _SVPtype.NONE etype: _SVEtype = _SVEtype.NONE + msrc: _SVmask_src = _SVmask_src.NO # MASK_SRC is active in1: _In1Sel = _In1Sel.NONE in2: _In2Sel = _In2Sel.NONE in3: _In3Sel = _In3Sel.NONE 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() conditions: str = "" @@ -328,17 +327,26 @@ class SVP64Record: "CONDITIONS": "conditions", "Ptype": "ptype", "Etype": "etype", + "SM": "msrc", "CR in": "cr_in", "CR out": "cr_out", } @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" + 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}")) @@ -357,7 +365,7 @@ class SVP64Record: ) if key not in frozenset({ - "in1", "in2", "in3", "cr_in", + "in1", "in2", "in3", "cr_in", "cr_in2", "out", "out2", "cr_out", }): raise KeyError(key) @@ -425,6 +433,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 @@ -465,14 +476,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: @@ -482,7 +497,7 @@ class Fields: def transform(item): (name, bitrange) = item - return (name, list(bitrange.values())) + return (name, tuple(bitrange.values())) self.__mapping = dict(map(transform, items)) @@ -505,142 +520,330 @@ class Fields: class Operand: name: str - def disassemble(self, value, record, verbose=False): + def span(self, record): + return record.fields[self.name] + + def disassemble(self, insn, record, + verbosity=Verbosity.NORMAL, indent=""): raise NotImplementedError -@_dataclasses.dataclass(eq=True, frozen=True) class DynamicOperand(Operand): - def disassemble(self, value, record, verbose=False): - span = record.fields[self.name] - value = value[span] - if verbose: - yield f"{int(value):0{value.bits}b}" - yield repr(span) + 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 DynamicOperandReg(DynamicOperand): +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", + "in1", "in2", "in3", "cr_in", "cr_in2", "out", "out2", "cr_out", }): - if self.extra_reg == record.svp64.extra_reg(key): - return record.extra_idx(key) + extra_reg = record.svp64.extra_reg(key=key) + if extra_reg is self.extra_reg: + return record.extra_idx(key=key) return _SVExtra.NONE - -@_dataclasses.dataclass(eq=True, frozen=True) -class ImmediateOperand(DynamicOperand): - pass - - -@_dataclasses.dataclass(eq=True, frozen=True) -class StaticOperand(Operand): - value: int - - def disassemble(self, value, record, verbose=False): - span = record.fields[self.name] - value = value[span] - if verbose: - yield f"{int(value):0{value.bits}b}" - yield repr(span) + 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: - yield str(int(value)) + vector = "*" if vector else "" + yield f"{vector}{prefix}{int(value)}" -@_dataclasses.dataclass(eq=True, frozen=True) -class DynamicOperandTargetAddrLI(DynamicOperandReg): - @property - def name(self): - return "LI" +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) - @name.setter - def name(self, _): - pass - def disassemble(self, value, record, verbose=False): - span = record.fields["LI"] - value = value[span] - if verbose: - yield f"{int(value):0{value.bits}b}" - yield repr(span) - yield "target_addr = EXTS(LI || 0b00))" - else: - yield hex(int(_selectconcat(value, - _SelectableInt(value=0b00, bits=2)))) +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 DynamicOperandTargetAddrBD(DynamicOperand): - @property - def name(self): - return "BD" +class CR3Operand(RegisterOperand): + pass - @name.setter - def name(self, _): - pass - def disassemble(self, value, record, verbose=False): - span = record.fields["BD"] - value = value[span] - if verbose: - yield f"{int(value):0{value.bits}b}" - yield repr(span) - yield "target_addr = EXTS(BD || 0b00))" - else: - yield hex(int(_selectconcat(value, - _SelectableInt(value=0b00, bits=2)))) +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) -@_dataclasses.dataclass(eq=True, frozen=True) -class DynamicOperandGPR(DynamicOperandReg): - def disassemble(self, value, record, verbose=False): - svp64 = isinstance(value, SVP64Instruction) - span = record.fields[self.name] - value = value[span] - if verbose: - yield f"{int(value):0{value.bits}b}" - yield repr(span) - if svp64: - yield repr(self.extra_idx(record)) - else: - yield f"r{str(int(value))}" +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] -@_dataclasses.dataclass(eq=True, frozen=True) -class DynamicOperandFPR(DynamicOperandReg): - def disassemble(self, value, record, verbose=False): - svp64 = isinstance(value, SVP64Instruction) - span = record.fields[self.name] - value = value[span] - if verbose: - yield f"{int(value):0{value.bits}b}" - yield repr(span) - if svp64: - yield repr(self.extra_idx(record)) + 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 f"f{str(int(value))}" + yield str(value.to_signed_int()) class Operands(tuple): def __new__(cls, insn, iterable): - branches = { - "b": {"target_addr": DynamicOperandTargetAddrLI}, - "ba": {"target_addr": DynamicOperandTargetAddrLI}, - "bl": {"target_addr": DynamicOperandTargetAddrLI}, - "bla": {"target_addr": DynamicOperandTargetAddrLI}, - "bc": {"target_addr": DynamicOperandTargetAddrBD}, - "bca": {"target_addr": DynamicOperandTargetAddrBD}, - "bcl": {"target_addr": DynamicOperandTargetAddrBD}, - "bcla": {"target_addr": DynamicOperandTargetAddrBD}, + 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 = [] @@ -662,15 +865,21 @@ class Operands(tuple): if immediate is not None: operands.append(ImmediateOperand(name=immediate)) - if insn in branches and operand in branches[insn]: - dynamic_cls = branches[insn][operand] + if 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 = DynamicOperandGPR + dynamic_cls = GPROperand elif regtype is _RegType.FPR: - dynamic_cls = 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) @@ -700,6 +909,24 @@ class Operands(tuple): 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: @@ -707,27 +934,49 @@ class Record: section: Section ppc: PPCRecord fields: Fields - operands: Operands + mdwn: MarkdownRecord svp64: SVP64Record = None 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)) + + return Opcode(value=value, mask=mask) - for operand in self.operands.static: - fields += [(operand.value, self.fields[operand.name])] + return tuple(sorted(map(opcode, self.ppc))) - return FieldsOpcode(fields) + 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): @@ -759,22 +1008,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 + ptype = property(lambda self: self.svp64.ptype) + etype = property(lambda self: self.svp64.etype) + def extra_idx(self, key): return self.svp64.extra_idx(key) - ptype = property(lambda self: self.svp64.ptype) - etype = property(lambda self: self.svp64.etype) - extra_in1 = property(lambda self: self.svp64.extra_in1) - extra_in2 = property(lambda self: self.svp64.extra_in2) - extra_in3 = property(lambda self: self.svp64.extra_in3) - extra_out = property(lambda self: self.svp64.extra_out) - extra_out2 = property(lambda self: self.svp64.extra_out2) - extra_cr_in = property(lambda self: self.svp64.extra_cr_in) - extra_cr_out = property(lambda self: self.svp64.extra_cr_out) + 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): @@ -807,13 +1061,70 @@ class Instruction(_Mapping): def __hash__(self): return hash(int(self)) + 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 disassemble(self, db, byteorder="little", verbose=False): + 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 @@ -833,88 +1144,49 @@ class WordInstruction(Instruction): bits.append(bit) return "".join(map(str, bits)) - def spec(self, db): - record = self.record(db=db) - - immediate = "" - dynamic_operands = [] - for operand in record.operands.dynamic: - name = operand.name - if immediate: - name = f"{immediate}({name})" - immediate = "" - if isinstance(operand, ImmediateOperand): - immediate = operand.name - if not immediate: - dynamic_operands.append(name) - - static_operands = [] - for operand in record.operands.static: - static_operands.append(f"{operand.name}={operand.value}") - - operands = "" - if dynamic_operands: - operands += f" {','.join(dynamic_operands)}" - if static_operands: - operands += f" ({' '.join(static_operands)})" - - return f"{record.name}{operands}" - - def opcode(self, db): - record = self.record(db=db) - return f"0x{record.opcode.value:08x}" - - def mask(self, db): - record = self.record(db=db) - return f"0x{record.opcode.mask:08x}" - - def disassemble(self, db, byteorder="little", verbose=False): + 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 = self.record(db=db) + record = db[self] if record is None: - yield f"{blob} .long 0x{integer:08x}" + yield f"{blob}.long 0x{integer:08x}" return - operands = [] - for operand in record.operands.dynamic: - operand = " ".join(operand.disassemble(value=self, - record=record, verbose=False)) - operands.append(operand) + operands = tuple(map(_operator.itemgetter(1), + self.dynamic_operands(db=db, verbosity=verbosity))) if operands: - operands = ",".join(operands) - operands = f" {operands}" + yield f"{blob}{record.name} {','.join(operands)}" else: - operands = "" - - yield f"{blob} {record.name}{operands}" + yield f"{blob}{record.name}" - if verbose: + if verbosity >= Verbosity.VERBOSE: indent = (" " * 4) binary = self.binary - spec = self.spec(db=db) - opcode = self.opcode(db=db) - mask = self.mask(db=db) + 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}opcode" - yield f"{indent}{indent}{opcode}" - yield f"{indent}mask" - yield f"{indent}{indent}{mask}" - for operand in record.operands: - name = operand.name - yield f"{indent}{name}" - parts = operand.disassemble(value=self, - record=record, verbose=True) - for part in parts: - yield f"{indent}{indent}{part}" + 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 "" @@ -943,213 +1215,668 @@ 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""" - pass +class Extra2(Extra): + idx0: _Field = range(0, 2) + idx1: _Field = range(2, 4) + idx2: _Field = range(4, 6) + idx3: _Field = range(6, 8) - class svmr(Mode): - """subvector reduce mode, SUBVL>1""" - SVM: Mode[3] - - class pu(Mode): - """Pack/Unpack mode, SUBVL>1""" - SVM: Mode[3] - - class ffrc1(Mode): - """Rc=1: ffirst CR sel""" - inv: Mode[2] - CRbit: Mode[3, 4] - - class ffrc0(Mode): - """Rc=0: ffirst z/nonz""" - inv: Mode[2] - VLi: Mode[3] - RC1: Mode[4] - - class sat(Mode): - """sat mode: N=0/1 u/s, SUBVL=1""" - N: Mode[2] - dz: Mode[3] - sz: Mode[4] - - class satx(Mode): - """sat mode: N=0/1 u/s, SUBVL>1""" - N: Mode[2] - zz: Mode[3] - dz: Mode[3] - sz: Mode[3] - - class satpu(Mode): - """Pack/Unpack sat mode: N=0/1 u/s, SUBVL>1""" - N: Mode[2] - zz: Mode[3] - dz: Mode[3] - sz: Mode[3] - - class prrc1(Mode): - """Rc=1: pred-result CR sel""" - inv: Mode[2] - CRbit: Mode[3, 4] - - class prrc0(Mode): - """Rc=0: pred-result z/nonz""" - inv: Mode[2] - zz: Mode[3] - RC1: Mode[4] - dz: Mode[3] - sz: Mode[3] - - simple: simple - smr: smr - pmr: pmr - svmr: svmr - pu: pu - ffrc1: ffrc1 - ffrc0: ffrc0 - sat: sat - satx: satx - satpu: satpu - prrc1: prrc1 - prrc0: prrc0 - - -class LDSTImmMode(Mode): - class simple(Mode): - """simple mode""" - zz: Mode[3] - els: Mode[4] - dz: Mode[3] - sz: Mode[3] - - class spu(Mode): - """Structured Pack/Unpack""" - zz: Mode[3] - els: Mode[4] - dz: Mode[3] - sz: Mode[3] - - class ffrc1(Mode): - """Rc=1: ffirst CR sel""" - inv: Mode[2] - CRbit: Mode[3, 4] - - class ffrc0(Mode): - """Rc=0: ffirst z/nonz""" - inv: Mode[2] - els: Mode[3] - RC1: Mode[4] - - class sat(Mode): - """sat mode: N=0/1 u/s""" - N: Mode[2] - zz: Mode[3] - els: Mode[4] - dz: Mode[3] - sz: Mode[3] - - class prrc1(Mode): - """Rc=1: pred-result CR sel""" - inv: Mode[2] - CRbit: Mode[3, 4] - - class prrc0(Mode): - """Rc=0: pred-result z/nonz""" - inv: Mode[2] - els: Mode[3] - RC1: Mode[4] - - simple: simple - spu: spu - ffrc1: ffrc1 - ffrc0: ffrc0 - sat: sat - prrc1: prrc1 - prrc0: prrc0 - - -class LDSTIdxMode(Mode): - class simple(Mode): - """simple mode""" - SEA: Mode[2] - sz: Mode[3] - dz: Mode[3] - - class stride(Mode): - """strided (scalar only source)""" - SEA: Mode[2] - dz: Mode[3] - sz: Mode[4] - - class sat(Mode): - """sat mode: N=0/1 u/s""" - N: Mode[2] - dz: Mode[3] - sz: Mode[4] - - class prrc1(Mode): - """Rc=1: pred-result CR sel""" - inv: Mode[2] - CRbit: Mode[3, 4] - - class prrc0(Mode): - """Rc=0: pred-result z/nonz""" - inv: Mode[2] - zz: Mode[3] - RC1: Mode[4] - dz: Mode[3] - sz: Mode[3] - - simple: simple - stride: stride - sat: sat - prrc1: prrc1 - prrc0: prrc0 - - -class RM(_Mapping): - class Mode(Mode): - normal: NormalMode - ldst_imm: LDSTImmMode - ldst_idx: LDSTIdxMode + 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) - 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), - ) + extra: Extra.remap(range(10, 19)) + extra2: Extra2.remap(range(10, 19)) + extra3: Extra3.remap(range(10, 19)) + + def specifiers(self, record): + subvl = int(self.subvl) + if subvl > 0: + yield { + 1: "vec2", + 2: "vec3", + 3: "vec4", + }[subvl] + + 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 NormalLDSTBaseRM(BaseRM): + def specifiers(self, record): + widths = { + 0b11: "8", + 0b10: "16", + 0b01: "32", + } + predicates = { + # integer + (0, 0b001): "1<= Verbosity.VERBOSE: indent = (" " * 4) binary = self.binary - spec = self.spec(db=db) - opcode = self.opcode(db=db) - mask = self.mask(db=db) + 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]}" @@ -1325,20 +1964,16 @@ class SVP64Instruction(PrefixedInstruction): 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}opcode" - yield f"{indent}{indent}{opcode}" - yield f"{indent}mask" - yield f"{indent}{indent}{mask}" - for operand in record.operands: - name = operand.name - yield f"{indent}{name}" - parts = operand.disassemble(value=self, - record=record, verbose=True) - for part in parts: - yield f"{indent}{indent}{part}" - - yield f"{indent}mode" - yield f"{indent}{indent}{mode_desc}" + 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 "" @@ -1361,13 +1996,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) @@ -1413,67 +2055,64 @@ class PPCDatabase: for insn in parse(stream, factory): records[section][insn.comment].add(insn) - db = dd(set) + sections = dd(set) for (section, group) in records.items(): for records in group.values(): - db[section].add(PPCMultiRecord(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 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): - return (section, record) - - for record in records: - if (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: @@ -1508,14 +2147,16 @@ class SVP64Database: class Database: def __init__(self, root): root = _pathlib.Path(root) - mdwndb = MarkdownDatabase() fieldsdb = FieldsDatabase() 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 @@ -1523,10 +2164,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__() @@ -1544,18 +2192,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