From: Dmitry Selyutin Date: Fri, 2 Jun 2023 16:20:09 +0000 (+0300) Subject: power_insn: decouple into separate module X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4109d0fb1f768db66883d771842536bb5f74aba0;p=openpower-isa.git power_insn: decouple into separate module --- diff --git a/src/openpower/decoder/isa/caller.py b/src/openpower/decoder/isa/caller.py index 9dd55b86..29691b92 100644 --- a/src/openpower/decoder/isa/caller.py +++ b/src/openpower/decoder/isa/caller.py @@ -35,7 +35,7 @@ from openpower.decoder.power_enums import (FPTRANS_INSNS, CRInSel, CROutSel, SVP64RMMode, SVPType, XER_bits, insns, spr_byname, spr_dict, BFP_FLAG_NAMES) -from openpower.decoder.power_insn import SVP64Instruction +from openpower.insndb.types import SVP64Instruction from openpower.decoder.power_svp64 import SVP64RM, decode_extra from openpower.decoder.selectable_int import (FieldSelectableInt, SelectableInt, selectconcat, diff --git a/src/openpower/decoder/power_insn.py b/src/openpower/decoder/power_insn.py deleted file mode 100644 index ca540386..00000000 --- a/src/openpower/decoder/power_insn.py +++ /dev/null @@ -1,3698 +0,0 @@ -import collections as _collections -import csv as _csv -import dataclasses as _dataclasses -import enum as _enum -import functools as _functools -import os as _os -import operator as _operator -import pathlib as _pathlib -import re as _re -import types as _types - -try: - from functools import cached_property -except ImportError: - from cached_property import cached_property - -from openpower.decoder.power_enums import ( - Function as _Function, - MicrOp as _MicrOp, - In1Sel as _In1Sel, - In2Sel as _In2Sel, - In3Sel as _In3Sel, - OutSel as _OutSel, - CRInSel as _CRInSel, - CRIn2Sel as _CRIn2Sel, - CROutSel as _CROutSel, - LDSTLen as _LDSTLen, - LDSTMode as _LDSTMode, - RCOE as _RCOE, - CryIn as _CryIn, - Form as _Form, - SVEType as _SVEType, - SVMaskSrc as _SVMaskSrc, - SVMode as _SVMode, - SVPType as _SVPType, - SVExtra as _SVExtra, - Reg as _Reg, - RegType as _RegType, - SelType as _SelType, - SVP64SubVL as _SVP64SubVL, - SVP64Pred as _SVP64Pred, - SVP64PredMode as _SVP64PredMode, - SVP64Width as _SVP64Width, -) -from openpower.decoder.selectable_int import ( - SelectableInt as _SelectableInt, - selectconcat as _selectconcat, -) -from openpower.decoder.power_fields import ( - Field as _Field, - Mapping as _Mapping, - DecodeFields as _DecodeFields, -) -from openpower.decoder.pseudo.pagereader import ISA as _ISA - - -@_functools.total_ordering -class Style(_enum.Enum): - LEGACY = _enum.auto() - 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) - - -@_functools.total_ordering -class Priority(_enum.Enum): - LOW = -1 - NORMAL = 0 - HIGH = +1 - - @classmethod - def _missing_(cls, value): - if isinstance(value, str): - value = value.upper() - try: - return cls[value] - except ValueError: - return super()._missing_(value) - - def __lt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - - # NOTE: the order is inversed, LOW < NORMAL < HIGH - return (self.value > other.value) - - -def dataclass(cls, record, keymap=None, typemap=None): - if keymap is None: - keymap = {} - if typemap is None: - typemap = {field.name:field.type for field in _dataclasses.fields(cls)} - - def transform(key_value): - (key, value) = key_value - key = keymap.get(key, key) - hook = typemap.get(key, lambda value: value) - if hook is bool and value in ("", "0"): - value = False - else: - value = hook(value) - return (key, value) - - record = dict(map(transform, record.items())) - for key in frozenset(record.keys()): - if record[key] == "": - record.pop(key) - - return cls(**record) - - -@_functools.total_ordering -@_dataclasses.dataclass(eq=True, frozen=True) -class Opcode: - 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 self.__repr__() - - def __repr__(self): - 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 - - def __lt__(self, other): - if not isinstance(other, Opcode): - return NotImplemented - return ((self.value, self.mask) < (other.value, other.mask)) - - def __int__(self): - return (self.value & self.mask) - - def __index__(self): - return int(self).__index__() - - 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())) - - def match(self, key): - return ((self.value & self.mask) == (key & self.mask)) - - -@_functools.total_ordering -@_dataclasses.dataclass(eq=True, frozen=True) -class IntegerOpcode(Opcode): - def __init__(self, value): - if value.startswith("0b"): - 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) - - -@_functools.total_ordering -@_dataclasses.dataclass(eq=True, frozen=True) -class PatternOpcode(Opcode): - 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) - value |= (symbol == "1") - mask |= (symbol != "-") - value <<= 1 - mask <<= 1 - value >>= 1 - mask >>= 1 - - value = Opcode.Value(value) - mask = Opcode.Mask(mask) - - return super().__init__(value=value, mask=mask) - - -@_dataclasses.dataclass(eq=True, frozen=True) -class PPCRecord: - class FlagsMeta(type): - def __iter__(cls): - yield from ( - "inv A", - "inv out", - "cry out", - "BR", - "sgn ext", - "rsrv", - "32b", - "sgn", - "lk", - "sgl pipe", - ) - - class Flags(tuple, metaclass=FlagsMeta): - def __new__(cls, flags=frozenset()): - flags = frozenset(flags) - diff = (flags - frozenset(cls)) - if diff: - raise ValueError(flags) - return super().__new__(cls, sorted(flags)) - - opcode: Opcode - comment: str - flags: Flags = Flags() - comment2: str = "" - function: _Function = _Function.NONE - intop: _MicrOp = _MicrOp.OP_ILLEGAL - in1: _In1Sel = _In1Sel.NONE - in2: _In2Sel = _In2Sel.NONE - 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 - upd: _LDSTMode = _LDSTMode.NONE - Rc: _RCOE = _RCOE.NONE - form: _Form = _Form.NONE - conditions: str = "" - unofficial: bool = False - - __KEYMAP = { - "unit": "function", - "internal op": "intop", - "CR in": "cr_in", - "CR out": "cr_out", - "cry in": "cry_in", - "ldst len": "ldst_len", - "rc": "Rc", - "CONDITIONS": "conditions", - } - - def __lt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - lhs = (self.opcode, self.comment) - rhs = (other.opcode, other.comment) - return (lhs < rhs) - - @classmethod - 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) - - @cached_property - def names(self): - return frozenset(self.comment.split("=")[-1].split("/")) - - -class PPCMultiRecord(tuple): - def __getattr__(self, attr): - if attr == "opcode": - if len(self) != 1: - raise AttributeError(attr) - return getattr(self[0], attr) - - -@_dataclasses.dataclass(eq=True, frozen=True) -class SVP64Record: - class ExtraMap(tuple): - class Extra(tuple): - @_dataclasses.dataclass(eq=True, frozen=True) - class Entry: - seltype: _SelType = _SelType.NONE - reg: _Reg = _Reg.NONE - - def __repr__(self): - return f"{self.seltype.value}:{self.reg.name}" - - def __new__(cls, value="0"): - if isinstance(value, str): - def transform(value): - (seltype, reg) = value.split(":") - seltype = _SelType(seltype) - reg = _Reg(reg) - return cls.Entry(seltype=seltype, reg=reg) - - if value == "0": - value = tuple() - else: - value = map(transform, value.split(";")) - - return super().__new__(cls, value) - - def __repr__(self): - return repr(list(self)) - - def __new__(cls, value=tuple()): - value = tuple(value) - if len(value) == 0: - value = (("0",) * 4) - return super().__new__(cls, map(cls.Extra, value)) - - def __repr__(self): - return repr({index:self[index] for index in range(0, 4)}) - - name: str - ptype: _SVPType = _SVPType.NONE - etype: _SVEType = _SVEType.NONE - msrc: _SVMaskSrc = _SVMaskSrc.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 = "" - mode: _SVMode = _SVMode.NORMAL - - __KEYMAP = { - "insn": "name", - "CONDITIONS": "conditions", - "Ptype": "ptype", - "Etype": "etype", - "SM": "msrc", - "CR in": "cr_in", - "CR out": "cr_out", - } - - @classmethod - def CSV(cls, record): - record["insn"] = record["insn"].split("=")[-1] - - 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}")) - - record["extra"] = cls.ExtraMap(extra) - - return dataclass(cls, record, keymap=cls.__KEYMAP) - - @cached_property - def extras(self): - keys = ( - "in1", "in2", "in3", "cr_in", "cr_in2", - "out", "out2", "cr_out", - ) - - idxmap = ( - _SVExtra.Idx0, - _SVExtra.Idx1, - _SVExtra.Idx2, - _SVExtra.Idx3, - ) - - def extra(reg): - extras = { - _SelType.DST: {}, - _SelType.SRC: {}, - } - for index in range(0, 4): - for entry in self.extra[index]: - extras[entry.seltype][entry.reg] = idxmap[index] - - for (seltype, regs) in extras.items(): - idx = regs.get(reg, _SVExtra.NONE) - if idx is not _SVExtra.NONE: - yield (reg, seltype, idx) - - sels = {} - idxs = {} - regs = {} - seltypes = {} - for key in keys: - sel = sels[key] = getattr(self, key) - reg = regs[key] = _Reg(sel) - seltypes[key] = _SelType.NONE - idxs[key] = _SVExtra.NONE - for (reg, seltype, idx) in extra(reg.alias): - if ((idx != idxs[key]) and (idxs[key] is not _SVExtra.NONE)): - raise ValueError(idxs[key]) - idxs[key] = idx - regs[key] = reg - seltypes[key] = seltype - - if sels["cr_in"] is _CRInSel.BA_BB: - sels["cr_in"] = _CRIn2Sel.BA - sels["cr_in2"] = _CRIn2Sel.BB - idxs["cr_in2"] = idxs["cr_in"] - for key in ("cr_in", "cr_in2"): - regs[key] = _Reg(sels[key]) - seltype[key] = _SelType.SRC - - records = {} - for key in keys: - records[key] = { - "sel": sels[key], - "reg": regs[key], - "seltype": seltypes[key], - "idx": idxs[key], - } - - return _types.MappingProxyType(records) - - extra_idx_in1 = property(lambda self: self.extras["in1"]["idx"]) - extra_idx_in2 = property(lambda self: self.extras["in2"]["idx"]) - extra_idx_in3 = property(lambda self: self.extras["in3"]["idx"]) - extra_idx_out = property(lambda self: self.extras["out"]["idx"]) - extra_idx_out2 = property(lambda self: self.extras["out2"]["idx"]) - extra_idx_cr_in = property(lambda self: self.extras["cr_in"]["idx"]) - extra_idx_cr_in2 = property(lambda self: self.extras["cr_in2"]["idx"]) - extra_idx_cr_out = property(lambda self: self.extras["cr_out"]["idx"]) - - @cached_property - def extra_CR(self): - extra = None - for idx in range(0, 4): - for entry in self.extra[idx]: - if entry.seltype is _SelType.DST: - if extra is not None: - raise ValueError(self.svp64) - extra = entry - break - - if _RegType(extra.reg) not in (_RegType.CR_3BIT, _RegType.CR_5BIT): - raise ValueError(self.svp64) - - return extra - - @cached_property - def extra_CR_3bit(self): - return (_RegType(self.extra_CR.reg) is _RegType.CR_3BIT) - - -class BitSel: - def __init__(self, value=(0, 32)): - if isinstance(value, str): - (start, end) = map(int, value.split(":")) - else: - (start, end) = value - if start < 0 or end < 0 or start >= end: - raise ValueError(value) - - self.__start = start - self.__end = end - - return super().__init__() - - def __len__(self): - return (self.__end - self.__start + 1) - - def __repr__(self): - return f"[{self.__start}:{self.__end}]" - - 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 - - @property - def end(self): - return self.__end - - -@_dataclasses.dataclass(eq=True, frozen=True) -class Section: - class Mode(_enum.Enum): - INTEGER = _enum.auto() - PATTERN = _enum.auto() - - @classmethod - def _missing_(cls, value): - if isinstance(value, str): - return cls[value.upper()] - return super()._missing_(value) - - class Suffix(int): - def __new__(cls, value=None): - if isinstance(value, str): - if value.upper() == "NONE": - value = None - else: - value = int(value, 0) - if value is None: - value = 0 - - return super().__new__(cls, value) - - def __str__(self): - return repr(self) - - def __repr__(self): - return (bin(self) if self else "None") - - path: _pathlib.Path - bitsel: BitSel - suffix: Suffix - mode: Mode - opcode: IntegerOpcode = None - priority: Priority = Priority.NORMAL - - def __lt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.priority < other.priority) - - @classmethod - def CSV(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: - def __init__(self, items): - if isinstance(items, dict): - items = items.items() - - def transform(item): - (name, bitrange) = item - return (name, tuple(bitrange.values())) - - self.__mapping = dict(map(transform, items)) - - return super().__init__() - - def __repr__(self): - return repr(self.__mapping) - - def __iter__(self): - yield from self.__mapping.items() - - def __contains__(self, key): - return self.__mapping.__contains__(key) - - def __getitem__(self, key): - return self.__mapping.get(key, None) - - -class Operands: - __GPR_PAIRS = ( - _Reg.RTp, - _Reg.RSp, - ) - __FPR_PAIRS = ( - _Reg.FRAp, - _Reg.FRBp, - _Reg.FRSp, - _Reg.FRTp, - ) - - def __init__(self, insn, operands): - 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": SignedImmediateOperand, - "SI": SignedOperand, - "IB": SignedOperand, - "LI": SignedOperand, - "SIM": SignedOperand, - "SVD": SignedOperand, - "SVDS": SignedOperand, - "RSp": GPRPairOperand, - "RTp": GPRPairOperand, - "FRAp": FPRPairOperand, - "FRBp": FPRPairOperand, - "FRSp": FPRPairOperand, - "FRTp": FPRPairOperand, - } - custom_immediates = { - "DQ": EXTSOperandDQ, - "DS": EXTSOperandDS, - } - - mapping = {} - for operand in operands: - cls = DynamicOperand - - if "=" in operand: - (name, value) = operand.split("=") - mapping[name] = (StaticOperand, { - "name": name, - "value": int(value), - }) - else: - name = operand - if name.endswith(")"): - name = name.replace("(", " ").replace(")", "") - (imm_name, _, name) = name.partition(" ") - else: - imm_name = None - - if imm_name is not None: - imm_cls = custom_immediates.get(imm_name, ImmediateOperand) - - if insn in custom_insns and name in custom_insns[insn]: - cls = custom_insns[insn][name] - elif name in custom_fields: - cls = custom_fields[name] - elif name in _Reg.__members__: - reg = _Reg[name] - if reg in self.__class__.__GPR_PAIRS: - cls = GPRPairOperand - elif reg in self.__class__.__FPR_PAIRS: - cls = FPRPairOperand - else: - regtype = _RegType[name] - if regtype is _RegType.GPR: - cls = GPROperand - elif regtype is _RegType.FPR: - cls = FPROperand - elif regtype is _RegType.CR_3BIT: - cls = CR3Operand - elif regtype is _RegType.CR_5BIT: - cls = CR5Operand - - if imm_name is not None: - mapping[imm_name] = (imm_cls, {"name": imm_name}) - mapping[name] = (cls, {"name": name}) - - static = [] - dynamic = [] - for (name, (cls, kwargs)) in mapping.items(): - kwargs = dict(kwargs) - kwargs["name"] = name - if issubclass(cls, StaticOperand): - static.append((cls, kwargs)) - elif issubclass(cls, DynamicOperand): - dynamic.append((cls, kwargs)) - else: - raise ValueError(name) - - self.__mapping = mapping - self.__static = tuple(static) - self.__dynamic = tuple(dynamic) - - return super().__init__() - - def __iter__(self): - for (_, items) in self.__mapping.items(): - (cls, kwargs) = items - yield (cls, kwargs) - - def __repr__(self): - return self.__mapping.__repr__() - - def __contains__(self, key): - return self.__mapping.__contains__(key) - - def __getitem__(self, key): - return self.__mapping.__getitem__(key) - - @property - def static(self): - return self.__static - - @property - def dynamic(self): - return self.__dynamic - - -class Arguments(tuple): - def __new__(cls, record, arguments, operands): - operands = iter(tuple(operands)) - arguments = iter(tuple(arguments)) - - items = [] - while True: - try: - operand = next(operands) - except StopIteration: - break - - try: - argument = next(arguments) - except StopIteration: - raise ValueError("operands count mismatch") - - if isinstance(operand, ImmediateOperand): - argument = argument.replace("(", " ").replace(")", "") - (imm_argument, _, argument) = argument.partition(" ") - try: - (imm_operand, operand) = (operand, next(operands)) - except StopIteration: - raise ValueError("operands count mismatch") - items.append((imm_argument, imm_operand)) - items.append((argument, operand)) - - try: - next(arguments) - except StopIteration: - pass - else: - raise ValueError("operands count mismatch") - - return super().__new__(cls, items) - - -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: - name: str - section: Section - ppc: PPCRecord - fields: Fields - mdwn: MarkdownRecord - svp64: SVP64Record = None - - def __lt__(self, other): - if not isinstance(other, Record): - return NotImplemented - lhs = (min(self.opcodes), self.name) - rhs = (min(other.opcodes), other.name) - return (lhs < rhs) - - @cached_property - def operands(self): - return (self.static_operands + self.dynamic_operands) - - @cached_property - def static_operands(self): - operands = [] - operands.append(POStaticOperand(record=self, value=self.PO)) - for ppc in self.ppc: - operands.append(XOStaticOperand( - record=self, - value=ppc.opcode.value, - span=self.section.bitsel, - )) - for (cls, kwargs) in self.mdwn.operands.static: - operands.append(cls(record=self, **kwargs)) - return tuple(operands) - - @cached_property - def dynamic_operands(self): - operands = [] - for (cls, kwargs) in self.mdwn.operands.dynamic: - operands.append(cls(record=self, **kwargs)) - return tuple(operands) - - @cached_property - def opcodes(self): - def binary(mapping): - return int("".join(str(int(mapping[bit])) \ - for bit in sorted(mapping)), 2) - - def PO_XO(value, mask, opcode, bits): - value = dict(value) - mask = dict(mask) - for (src, dst) in enumerate(reversed(bits)): - value[dst] = ((opcode.value & (1 << src)) != 0) - mask[dst] = ((opcode.mask & (1 << src)) != 0) - return (value, mask) - - def PO(value, mask, opcode, bits): - return PO_XO(value=value, mask=mask, opcode=opcode, bits=bits) - - def XO(value, mask, opcode, bits): - (value, mask) = PO_XO(value=value, mask=mask, - opcode=opcode, bits=bits) - for (op_cls, op_kwargs) in self.mdwn.operands.static: - operand = op_cls(record=self, **op_kwargs) - for (src, dst) in enumerate(reversed(operand.span)): - value[dst] = ((operand.value & (1 << src)) != 0) - mask[dst] = True - return (value, mask) - - pairs = [] - value = {bit:False for bit in range(32)} - mask = {bit:False for bit in range(32)} - if self.section.opcode is not None: - (value, mask) = PO(value=value, mask=mask, - opcode=self.section.opcode, bits=range(0, 6)) - for ppc in self.ppc: - pairs.append(XO(value=value, mask=mask, - opcode=ppc.opcode, bits=self.section.bitsel)) - - result = [] - for (value, mask) in pairs: - value = Opcode.Value(binary(value)) - mask = Opcode.Mask(binary(mask)) - result.append(Opcode(value=value, mask=mask)) - - return tuple(result) - - @cached_property - def PO(self): - opcode = self.section.opcode - if opcode is None: - opcode = self.ppc[0].opcode - if isinstance(opcode, PatternOpcode): - value = int(opcode.value) - bits = opcode.value.bit_length() - return int(_SelectableInt(value=value, bits=bits)[0:6]) - - return int(opcode.value) - - @cached_property - def XO(self): - return tuple(ppc.opcode for ppc in self.ppc) - - def match(self, key): - for opcode in self.opcodes: - if opcode.match(key): - return True - - return False - - @property - def mode(self): - return self.svp64.mode - - @property - def in1(self): - return self.ppc.in1 - - @property - def in2(self): - return self.ppc.in2 - - @property - def in3(self): - return self.ppc.in3 - - @property - def out(self): - return self.ppc.out - - @property - def out2(self): - if self.svp64 is None: - return _OutSel.NONE - return self.ppc.out - - @property - 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) - - 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_in2 = property(lambda self: self.svp64.extra_idx_cr_in2) - extra_idx_cr_out = property(lambda self: self.svp64.extra_idx_cr_out) - - def __contains__(self, key): - return self.mdwn.operands.__contains__(key) - - def __getitem__(self, key): - (cls, kwargs) = self.mdwn.operands.__getitem__(key) - return cls(record=self, **kwargs) - - @cached_property - def Rc(self): - if "Rc" not in self: - return False - return self["Rc"].value - - -class Operand: - def __init__(self, record, name): - self.__record = record - self.__name = name - - def __iter__(self): - yield ("record", self.record) - yield ("name", self.__name) - - def __repr__(self): - return f"{self.__class__.__name__}({self.name})" - - @property - def name(self): - return self.__name - - @property - def record(self): - return self.__record - - @cached_property - def span(self): - return self.record.fields[self.name] - - def assemble(self, insn): - raise NotImplementedError() - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - raise NotImplementedError() - - -class DynamicOperand(Operand): - def assemble(self, insn, value): - span = self.span - if isinstance(value, str): - value = int(value, 0) - if value < 0: - raise ValueError("signed operands not allowed") - insn[span] = value - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - span = self.span - value = insn[span] - - if style >= Style.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 assemble(self, insn, value): - if isinstance(value, str): - value = int(value, 0) - return super().assemble(value=value, insn=insn) - - def assemble(self, insn, value): - span = self.span - if isinstance(value, str): - value = int(value, 0) - insn[span] = value - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - span = self.span - value = insn[span].to_signed_int() - sign = "-" if (value < 0) else "" - value = abs(value) - - if style >= Style.VERBOSE: - span = map(str, span) - yield f"{indent}{self.name}" - yield f"{indent}{indent}{sign}{value}" - yield f"{indent}{indent}{', '.join(span)}" - else: - yield f"{sign}{value}" - - -class StaticOperand(Operand): - def __init__(self, record, name, value): - self.__value = value - return super().__init__(record=record, name=name) - - def __iter__(self): - yield ("value", self.__value) - yield from super().__iter__() - - def __repr__(self): - return f"{self.__class__.__name__}({self.name}, value={self.value})" - - @property - def value(self): - return self.__value - - def assemble(self, insn): - insn[self.span] = self.value - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - span = self.span - value = insn[span] - - if style >= Style.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 SpanStaticOperand(StaticOperand): - def __init__(self, record, name, value, span): - self.__span = tuple(span) - return super().__init__(record=record, name=name, value=value) - - def __iter__(self): - yield ("span", self.__span) - yield from super().__iter__() - - @property - def span(self): - return self.__span - - -class POStaticOperand(SpanStaticOperand): - def __init__(self, record, value): - return super().__init__(record=record, name="PO", - value=value, span=range(0, 6)) - - def __iter__(self): - for (key, value) in super().__iter__(): - if key not in {"name", "span"}: - yield (key, value) - - -class XOStaticOperand(SpanStaticOperand): - def __init__(self, record, value, span): - bits = record.section.bitsel - value = _SelectableInt(value=value, bits=len(bits)) - span = dict(zip(bits, range(len(bits)))) - span_rev = {value:key for (key, value) in span.items()} - - # This part is tricky: we cannot use record.operands, - # as this code is called by record.static_operands method. - for (cls, kwargs) in record.mdwn.operands: - operand = cls(record=record, **kwargs) - for idx in operand.span: - rev = span.pop(idx, None) - if rev is not None: - span_rev.pop(rev, None) - - value = int(_selectconcat(*(value[bit] for bit in span.values()))) - span = tuple(span.keys()) - - return super().__init__(record=record, name="XO", - value=value, span=span) - - def __iter__(self): - for (key, value) in super().__iter__(): - if key not in {"name"}: - yield (key, value) - - -class ImmediateOperand(DynamicOperand): - pass - - -class SignedImmediateOperand(SignedOperand, ImmediateOperand): - pass - - -class NonZeroOperand(DynamicOperand): - def assemble(self, insn, value): - if isinstance(value, str): - value = int(value, 0) - if not isinstance(value, int): - raise ValueError("non-integer operand") - if value == 0: - raise ValueError("non-zero operand") - value -= 1 - return super().assemble(value=value, insn=insn) - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - span = self.span - value = insn[span] - - if style >= Style.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 ExtendableOperand(DynamicOperand): - def sv_spec_enter(self, value, span): - return (value, span) - - def sv_spec(self, insn): - vector = False - span = self.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) - - for extra_idx in self.extra_idx: - if self.record.etype is _SVEType.EXTRA3: - spec = insn.prefix.rm.extra3[extra_idx] - elif self.record.etype is _SVEType.EXTRA2: - spec = insn.prefix.rm.extra2[extra_idx] - else: - raise ValueError(self.record.etype) - - if spec != 0: - vector = bool(spec[0]) - spec_span = spec.__class__ - if self.record.etype is _SVEType.EXTRA3: - spec_span = tuple(map(str, spec_span[1, 2])) - spec = spec[1, 2] - elif self.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(self.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) - - def sv_spec_leave(self, value, span, origin_value, origin_span): - return (value, span) - - @property - def extra_idx(self): - for (key, record) in self.record.svp64.extras.items(): - if record["reg"].alias is self.extra_reg.alias: - yield record["idx"] - - @cached_property - def extra_reg(self): - return _Reg(self.name) - - def remap(self, value, vector): - raise NotImplementedError() - - def assemble(self, value, insn, prefix): - vector = False - - if isinstance(value, str): - value = value.lower() - if value.startswith("%"): - value = value[1:] - if value.startswith("*"): - if not isinstance(insn, SVP64Instruction): - raise ValueError(value) - value = value[1:] - vector = True - if value.startswith(prefix): - if (self.extra_reg.or_zero and (value == f"{prefix}0")): - raise ValueError(value) - value = value[len(prefix):] - value = int(value, 0) - - if isinstance(insn, SVP64Instruction): - (value, extra) = self.remap(value=value, vector=vector) - - for extra_idx in self.extra_idx: - if self.record.etype is _SVEType.EXTRA3: - insn.prefix.rm.extra3[extra_idx] = extra - elif self.record.etype is _SVEType.EXTRA2: - insn.prefix.rm.extra2[extra_idx] = extra - else: - raise ValueError(self.record.etype) - - return super().assemble(value=value, insn=insn) - - def disassemble(self, insn, - style=Style.NORMAL, prefix="", indent=""): - (vector, value, span) = self.sv_spec(insn=insn) - - if (self.extra_reg.or_zero and (value == 0)): - prefix = "" - - if style >= Style.VERBOSE: - mode = "vector" if vector else "scalar" - yield f"{indent}{self.name} ({mode})" - yield f"{indent}{indent}{int(value):0{value.bits}b}" - yield f"{indent}{indent}{', '.join(span)}" - if isinstance(insn, SVP64Instruction): - for extra_idx in frozenset(self.extra_idx): - if self.record.etype is _SVEType.NONE: - yield f"{indent}{indent}extra[none]" - else: - etype = repr(self.record.etype).lower() - yield f"{indent}{indent}{etype}{extra_idx!r}" - else: - vector = "*" if vector else "" - yield f"{vector}{prefix}{int(value)}" - - -class SimpleRegisterOperand(ExtendableOperand): - def remap(self, value, vector): - if vector: - extra = (value & 0b11) - value = (value >> 2) - else: - extra = (value >> 5) - value = (value & 0b11111) - - # now sanity-check. EXTRA3 is ok, EXTRA2 has limits - # (and shrink to a single bit if ok) - if self.record.etype is _SVEType.EXTRA2: - if vector: - # range is r0-r127 in increments of 2 (r0 r2 ... r126) - assert (extra & 0b01) == 0, \ - ("vector field %s cannot fit into EXTRA2" % value) - extra = (0b10 | (extra >> 1)) - else: - # range is r0-r63 in increments of 1 - assert (extra >> 1) == 0, \ - ("scalar GPR %d cannot fit into EXTRA2" % value) - extra &= 0b01 - elif self.record.etype is _SVEType.EXTRA3: - if vector: - # EXTRA3 vector bit needs marking - extra |= 0b100 - else: - raise ValueError(self.record.etype) - - return (value, extra) - - -class GPROperand(SimpleRegisterOperand): - def assemble(self, insn, value): - return super().assemble(value=value, insn=insn, prefix="r") - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - prefix = "" if (style <= Style.SHORT) else "r" - yield from super().disassemble(prefix=prefix, insn=insn, - style=style, indent=indent) - - -class GPRPairOperand(GPROperand): - pass - - -class FPROperand(SimpleRegisterOperand): - def assemble(self, insn, value): - return super().assemble(value=value, insn=insn, prefix="f") - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - prefix = "" if (style <= Style.SHORT) else "f" - yield from super().disassemble(prefix=prefix, insn=insn, - style=style, indent=indent) - - -class FPRPairOperand(FPROperand): - pass - - -class ConditionRegisterFieldOperand(ExtendableOperand): - def pattern(name_pattern): - (name, pattern) = name_pattern - return (name, _re.compile(f"^{pattern}$", _re.S)) - - CONDS = { - "lt": 0, - "gt": 1, - "eq": 2, - "so": 3, - "un": 3, - } - CR = r"(?:CR|cr)([0-9]+)" - N = r"([0-9]+)" - BIT = rf"({'|'.join(CONDS.keys())})" - LBIT = fr"{BIT}\s*\+\s*" # BIT+ - RBIT = fr"\s*\+\s*{BIT}" # +BIT - CRN = fr"{CR}\s*\*\s*{N}" # CR*N - NCR = fr"{N}\s*\*\s*{CR}" # N*CR - XCR = fr"{CR}\.{BIT}" - PATTERNS = tuple(map(pattern, ( - ("CR", CR), - ("XCR", XCR), - ("CR*N", CRN), - ("N*CR", NCR), - ("BIT+CR", (LBIT + CR)), - ("CR+BIT", (CR + RBIT)), - ("BIT+CR*N", (LBIT + CRN)), - ("CR*N+BIT", (CRN + RBIT)), - ("BIT+N*CR", (LBIT + NCR)), - ("N*CR+BIT", (NCR + RBIT)), - ))) - - def remap(self, value, vector, regtype): - if regtype is _RegType.CR_5BIT: - subvalue = (value & 0b11) - value >>= 2 - - if vector: - extra = (value & 0b1111) - value >>= 4 - else: - extra = (value >> 3) - value &= 0b111 - - if self.record.etype is _SVEType.EXTRA2: - if vector: - assert (extra & 0b111) == 0, \ - "vector CR cannot fit into EXTRA2" - extra = (0b10 | (extra >> 3)) - else: - assert (extra >> 1) == 0, \ - "scalar CR cannot fit into EXTRA2" - extra &= 0b01 - elif self.record.etype is _SVEType.EXTRA3: - if vector: - assert (extra & 0b11) == 0, \ - "vector CR cannot fit into EXTRA3" - extra = (0b100 | (extra >> 2)) - else: - assert (extra >> 2) == 0, \ - "scalar CR cannot fit into EXTRA3" - extra &= 0b11 - - if regtype is _RegType.CR_5BIT: - value = ((value << 2) | subvalue) - - return (value, extra) - - def assemble(self, insn, value): - if isinstance(value, str): - vector = False - - if value.startswith("*"): - if not isinstance(insn, SVP64Instruction): - raise ValueError(value) - value = value[1:] - vector = True - - for (name, pattern) in reversed(self.__class__.PATTERNS): - match = pattern.match(value) - if match is not None: - keys = name.replace("+", "_").replace("*", "_").split("_") - values = match.groups() - match = dict(zip(keys, values)) - CR = int(match["CR"]) - if name == "XCR": - N = 4 - else: - N = int(match.get("N", "1")) - BIT = self.__class__.CONDS[match.get("BIT", "lt")] - value = ((CR * N) + BIT) - break - - value = str(value) - if vector: - value = f"*{value}" - - return super().assemble(value=value, insn=insn, prefix="cr") - - def disassemble(self, insn, - style=Style.NORMAL, prefix="", indent=""): - (vector, value, span) = self.sv_spec(insn=insn) - - if style >= Style.VERBOSE: - mode = "vector" if vector else "scalar" - yield f"{indent}{self.name} ({mode})" - yield f"{indent}{indent}{int(value):0{value.bits}b}" - yield f"{indent}{indent}{', '.join(span)}" - if isinstance(insn, SVP64Instruction): - for extra_idx in frozenset(self.extra_idx): - if self.record.etype is _SVEType.NONE: - yield f"{indent}{indent}extra[none]" - else: - etype = repr(self.record.etype).lower() - yield f"{indent}{indent}{etype}{extra_idx!r}" - else: - vector = "*" if vector else "" - CR = int(value >> 2) - CC = int(value & 3) - cond = ("lt", "gt", "eq", "so")[CC] - if style >= Style.NORMAL: - if CR != 0: - if isinstance(insn, SVP64Instruction): - yield f"{vector}cr{CR}.{cond}" - else: - yield f"4*cr{CR}+{cond}" - else: - yield cond - else: - yield f"{vector}{prefix}{int(value)}" - - -class CR3Operand(ConditionRegisterFieldOperand): - def remap(self, value, vector): - return super().remap(value=value, vector=vector, - regtype=_RegType.CR_3BIT) - - -class CR5Operand(ConditionRegisterFieldOperand): - def remap(self, value, vector): - return super().remap(value=value, vector=vector, - regtype=_RegType.CR_5BIT) - - 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 EXTSOperand(SignedOperand): - field: str # real name to report - nz: int = 0 # number of zeros - fmt: str = "d" # integer formatter - - def __init__(self, record, name, field, nz=0, fmt="d"): - self.__field = field - self.__nz = nz - self.__fmt = fmt - return super().__init__(record=record, name=name) - - @property - def field(self): - return self.__field - - @property - def nz(self): - return self.__nz - - @property - def fmt(self): - return self.__fmt - - @cached_property - def span(self): - return self.record.fields[self.field] - - def assemble(self, insn, value): - span = self.span - if isinstance(value, str): - value = int(value, 0) - insn[span] = (value >> self.nz) - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - span = self.span - value = insn[span].to_signed_int() - sign = "-" if (value < 0) else "" - value = (abs(value) << self.nz) - - if style >= Style.VERBOSE: - span = (tuple(map(str, span)) + (("{0}",) * self.nz)) - zeros = ("0" * self.nz) - hint = f"{self.name} = EXTS({self.field} || {zeros})" - yield f"{indent * 1}{hint}" - yield f"{indent * 2}{self.field}" - yield f"{indent * 3}{sign}{value:{self.fmt}}" - yield f"{indent * 3}{', '.join(span)}" - else: - yield f"{sign}{value:{self.fmt}}" - - -class TargetAddrOperand(EXTSOperand): - def __init__(self, record, name, field): - return super().__init__(record=record, name=name, field=field, - nz=2, fmt="#x") - - -class TargetAddrOperandLI(TargetAddrOperand): - def __init__(self, record, name): - return super().__init__(record=record, name=name, field="LI") - - -class TargetAddrOperandBD(TargetAddrOperand): - def __init__(self, record, name): - return super().__init__(record=record, name=name, field="BD") - - -class EXTSOperandDS(EXTSOperand, ImmediateOperand): - def __init__(self, record, name): - return super().__init__(record=record, name=name, field="DS", nz=2) - - -class EXTSOperandDQ(EXTSOperand, ImmediateOperand): - def __init__(self, record, name): - return super().__init__(record=record, name=name, field="DQ", nz=4) - - -class DOperandDX(SignedOperand): - @cached_property - def span(self): - cls = lambda name: DynamicOperand(record=self.record, name=name) - operands = map(cls, ("d0", "d1", "d2")) - spans = map(lambda operand: operand.span, operands) - return sum(spans, tuple()) - - def disassemble(self, insn, - style=Style.NORMAL, indent=""): - span = self.span - value = insn[span].to_signed_int() - sign = "-" if (value < 0) else "" - value = abs(value) - - if style >= Style.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 - span = map(str, span) - yield f"{indent}{indent}{operand.name} = D{subspan}" - yield f"{indent}{indent}{indent}{sign}{value}" - yield f"{indent}{indent}{indent}{', '.join(span)}" - else: - yield f"{sign}{value}" - - -class Instruction(_Mapping): - @classmethod - def integer(cls, value=0, bits=None, byteorder="little"): - if isinstance(value, (int, bytes)) and not isinstance(bits, int): - raise ValueError(bits) - - if isinstance(value, bytes): - if ((len(value) * 8) != bits): - raise ValueError(f"bit length mismatch") - value = int.from_bytes(value, byteorder=byteorder) - - if isinstance(value, int): - value = _SelectableInt(value=value, bits=bits) - elif isinstance(value, Instruction): - value = value.storage - - if not isinstance(value, _SelectableInt): - raise ValueError(value) - if bits is None: - bits = len(cls) - if len(value) != bits: - raise ValueError(value) - - value = _SelectableInt(value=value, bits=bits) - - return cls(storage=value) - - 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 = (len(self.__class__) // 8) - return int(self).to_bytes(nr_bytes, byteorder=byteorder) - - @classmethod - def record(cls, db, entry): - record = db[entry] - if record is None: - raise KeyError(entry) - return record - - @classmethod - def operands(cls, record): - yield from record.operands - - @classmethod - def static_operands(cls, record): - return filter(lambda operand: isinstance(operand, StaticOperand), - cls.operands(record=record)) - - @classmethod - def dynamic_operands(cls, record): - return filter(lambda operand: isinstance(operand, DynamicOperand), - cls.operands(record=record)) - - def spec(self, record, prefix): - dynamic_operands = tuple(map(_operator.itemgetter(0), - self.spec_dynamic_operands(record=record))) - - static_operands = [] - for (name, value) in self.spec_static_operands(record=record): - static_operands.append(f"{name}={value}") - - operands = "" - if dynamic_operands: - operands += " " - operands += ",".join(dynamic_operands) - if static_operands: - operands += " " - operands += " ".join(static_operands) - - return f"{prefix}{record.name}{operands}" - - def spec_static_operands(self, record): - for operand in self.static_operands(record=record): - if not isinstance(operand, (POStaticOperand, XOStaticOperand)): - yield (operand.name, operand.value) - - def spec_dynamic_operands(self, record, style=Style.NORMAL): - imm = False - imm_name = "" - imm_value = "" - for operand in self.dynamic_operands(record=record): - name = operand.name - value = " ".join(operand.disassemble(insn=self, - style=min(style, Style.NORMAL))) - if imm: - name = f"{imm_name}({name})" - value = f"{imm_value}({value})" - imm = False - if isinstance(operand, ImmediateOperand): - imm_name = name - imm_value = value - imm = True - if not imm: - yield (name, value) - - @classmethod - def assemble(cls, record, arguments=None): - if arguments is None: - arguments = () - - insn = cls.integer(value=0) - - for operand in cls.static_operands(record=record): - operand.assemble(insn=insn) - - arguments = Arguments(record=record, - arguments=arguments, operands=cls.dynamic_operands(record=record)) - for (value, operand) in arguments: - operand.assemble(insn=insn, value=value) - - return insn - - def disassemble(self, record, - byteorder="little", - style=Style.NORMAL): - raise NotImplementedError() - - -class WordInstruction(Instruction): - _: _Field = range(0, 32) - PO: _Field = range(0, 6) - - @classmethod - def integer(cls, value, byteorder="little"): - return super().integer(bits=32, value=value, byteorder=byteorder) - - @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, record, - byteorder="little", - style=Style.NORMAL): - if style <= Style.SHORT: - blob = "" - else: - blob = self.bytes(byteorder=byteorder) - blob = " ".join(map(lambda byte: f"{byte:02x}", blob)) - blob += " " - - if record is None: - yield f"{blob}.long 0x{int(self):08x}" - return - - # awful temporary hack: workaround for ld-update - # https://bugs.libre-soc.org/show_bug.cgi?id=1056#c2 - # XXX TODO must check that *EXTENDED* RA != extended-RT - if (record.svp64 is not None and - record.mode == _SVMode.LDST_IMM and - 'u' in record.name): - yield f"{blob}.long 0x{int(self):08x}" - return - - paired = False - if style is Style.LEGACY: - paired = False - for operand in self.dynamic_operands(record=record): - if isinstance(operand, (GPRPairOperand, FPRPairOperand)): - paired = True - - if style is Style.LEGACY and (paired or record.ppc.unofficial): - yield f"{blob}.long 0x{int(self):08x}" - else: - operands = tuple(map(_operator.itemgetter(1), - self.spec_dynamic_operands(record=record, style=style))) - if operands: - operands = ",".join(operands) - yield f"{blob}{record.name} {operands}" - else: - yield f"{blob}{record.name}" - - if style >= Style.VERBOSE: - indent = (" " * 4) - binary = self.binary - spec = self.spec(record=record, 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 self.operands(record=record): - yield from operand.disassemble(insn=self, - style=style, indent=indent) - yield "" - - -class PrefixedInstruction(Instruction): - class Prefix(WordInstruction.remap(range(0, 32))): - pass - - class Suffix(WordInstruction.remap(range(32, 64))): - pass - - _: _Field = range(64) - prefix: Prefix - suffix: Suffix - PO: Suffix.PO - - @classmethod - def integer(cls, value, byteorder="little"): - return super().integer(bits=64, value=value, byteorder=byteorder) - - @classmethod - def pair(cls, prefix=0, suffix=0, byteorder="little"): - def transform(value): - return WordInstruction.integer(value=value, - byteorder=byteorder)[0:32] - - (prefix, suffix) = map(transform, (prefix, suffix)) - value = _selectconcat(prefix, suffix) - - return super().integer(bits=64, value=value) - - -class Mode(_Mapping): - _: _Field = range(0, 5) - sel: _Field = (0, 1) - - -class Extra(_Mapping): - _: _Field = range(0, 9) - - -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 specifiers(self, record): - subvl = int(self.subvl) - if subvl > 0: - yield { - 1: "vec2", - 2: "vec3", - 3: "vec4", - }[subvl] - - def disassemble(self, style=Style.NORMAL): - if style >= Style.VERBOSE: - indent = (" " * 4) - for (name, span) in self.traverse(path="RM"): - value = self.storage[span] - yield f"{name}" - yield f"{indent}{int(value):0{value.bits}b}" - yield f"{indent}{', '.join(map(str, span))}" - - -class FFRc1BaseRM(BaseRM): - def specifiers(self, record, mode): - inv = _SelectableInt(value=int(self.inv), bits=1) - CR = _SelectableInt(value=int(self.CR), bits=2) - mask = int(_selectconcat(CR, inv)) - predicate = PredicateBaseRM.predicate(True, mask) - yield f"{mode}={predicate}" - - yield from super().specifiers(record=record) - - -class FFRc0BaseRM(BaseRM): - def specifiers(self, record, mode): - if self.RC1: - inv = "~" if self.inv else "" - yield f"{mode}={inv}RC1" - - yield from super().specifiers(record=record) - - -class SatBaseRM(BaseRM): - def specifiers(self, record): - if self.N: - yield "sats" - else: - yield "satu" - - yield from super().specifiers(record=record) - - -class ZZBaseRM(BaseRM): - def specifiers(self, record): - if self.zz: - yield "zz" - - yield from super().specifiers(record=record) - - -class ZZCombinedBaseRM(BaseRM): - def specifiers(self, record): - if self.sz and self.dz: - yield "zz" - elif self.sz: - yield "sz" - elif self.dz: - yield "dz" - - yield from super().specifiers(record=record) - - -class DZBaseRM(BaseRM): - def specifiers(self, record): - if self.dz: - yield "dz" - - yield from super().specifiers(record=record) - - -class SZBaseRM(BaseRM): - def specifiers(self, record): - if self.sz: - yield "sz" - - yield from super().specifiers(record=record) - - -class MRBaseRM(BaseRM): - def specifiers(self, record): - if self.RG: - yield "mrr" - else: - yield "mr" - - yield from super().specifiers(record=record) - - -class ElsBaseRM(BaseRM): - def specifiers(self, record): - if self.els: - yield "els" - - yield from super().specifiers(record=record) - - -class WidthBaseRM(BaseRM): - @staticmethod - def width(FP, width): - width = { - 0b11: "8", - 0b10: "16", - 0b01: "32", - }.get(width) - if width is None: - return None - if FP: - width = ("fp" + width) - return width - - def specifiers(self, record): - # elwidths: use "w=" if same otherwise dw/sw - # FIXME this should consider FP instructions - FP = False - dw = WidthBaseRM.width(FP, int(self.elwidth)) - sw = WidthBaseRM.width(FP, int(self.ewsrc)) - if record.svp64.mode is _SVMode.CROP: - if dw: - yield ("dw=" + dw) - else: - sw = WidthBaseRM.width(FP, int(self.ewsrc)) - if dw == sw and dw: - yield ("w=" + dw) - else: - if dw: - yield ("dw=" + dw) - if sw: - yield ("sw=" + sw) - - yield from super().specifiers(record=record) - - -class PredicateBaseRM(BaseRM): - @staticmethod - def predicate(CR, mask): - return { - # integer - (False, 0b001): "1< 0: # if any separate with a space - operands = (" " + operands) - - if style <= Style.LEGACY: - yield f"{blob_prefix}.long 0x{int(self.prefix):08x}" - suffix = WordInstruction.integer(value=int(self.suffix)) - yield from suffix.disassemble(record=record, - byteorder=byteorder, style=style) - else: - yield f"{blob_prefix}{name}{specifiers}{operands}" - if blob_suffix: - yield f"{blob_suffix}" - - if style >= Style.VERBOSE: - indent = (" " * 4) - binary = self.binary - spec = self.spec(record=record, 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 self.operands(record=record): - yield from operand.disassemble(insn=self, - style=style, indent=indent) - yield f"{indent}RM" - yield f"{indent}{indent}{str(rm)}" - for line in rm.disassemble(style=style): - yield f"{indent}{indent}{line}" - yield "" - - @classmethod - def operands(cls, record): - for operand in super().operands(record=record): - parent = operand.__class__ - name = f"SVP64{parent.__name__}" - bases = (SVP64Operand, parent) - child = type(name, bases, {}) - yield child(**dict(operand)) - - -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(match, entries) - return tuple(map(factory, entries)) - - -class MarkdownDatabase: - def __init__(self): - db = {} - for (name, desc) in _ISA(): - operands = [] - if desc.regs: - (dynamic, *static) = desc.regs - operands.extend(dynamic) - operands.extend(static) - pcode = PCode(iterable=filter(str.strip, desc.pcode)) - operands = Operands(insn=name, operands=operands) - db[name] = MarkdownRecord(pcode=pcode, operands=operands) - - self.__db = dict(sorted(db.items())) - - 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) - - -class FieldsDatabase: - def __init__(self): - db = {} - df = _DecodeFields() - df.create_specs() - for (form, fields) in df.instrs.items(): - if form in {"DQE", "TX"}: - continue - if form == "all": - form = "NONE" - db[_Form[form]] = Fields(fields) - - self.__db = db - - return super().__init__() - - def __getitem__(self, key): - return self.__db.__getitem__(key) - - -class PPCDatabase: - def __init__(self, root, mdwndb): - # The code below groups the instructions by name:section. - # There can be multiple names for the same instruction. - # The point is to capture different opcodes for the same instruction. - sections = {} - records = _collections.defaultdict(set) - path = (root / "insndb.csv") - with open(path, "r", encoding="UTF-8") as stream: - for section in sorted(parse(stream, Section.CSV)): - path = (root / section.path) - opcode_cls = { - section.Mode.INTEGER: IntegerOpcode, - section.Mode.PATTERN: PatternOpcode, - }[section.mode] - factory = _functools.partial( - PPCRecord.CSV, opcode_cls=opcode_cls) - with open(path, "r", encoding="UTF-8") as stream: - for insn in parse(stream, factory): - for name in insn.names: - records[name].add(insn) - sections[name] = section - - items = sorted(records.items()) - records = {} - for (name, multirecord) in items: - records[name] = PPCMultiRecord(sorted(multirecord)) - - def exact_match(name): - record = records.get(name) - if record is None: - return None - return name - - def LK_match(name): - if not name.endswith("l"): - return None - alias = exact_match(name[:-1]) - if alias is None: - return None - record = records[alias] - if "lk" not in record.flags: - raise ValueError(record) - return alias - - def AA_match(name): - if not name.endswith("a"): - return None - alias = LK_match(name[:-1]) - if alias is None: - alias = name[:-1] - record = records[alias] - if record.intop not in {_MicrOp.OP_B, _MicrOp.OP_BC}: - raise ValueError(record) - if "AA" not in mdwndb[name].operands: - raise ValueError(record) - return alias - - def Rc_match(name): - if not name.endswith("."): - return None - alias = exact_match(name[:-1]) - if alias is None: - return None - record = records[alias] - if record.Rc is _RCOE.NONE: - raise ValueError(record) - return alias - - db = {} - matches = (exact_match, LK_match, AA_match, Rc_match) - for (name, _) in mdwndb: - if name.startswith("sv."): - continue - alias = None - for match in matches: - alias = match(name) - if alias is not None: - break - if alias is None: - continue - section = sections[alias] - record = records[alias] - db[name] = (section, record) - - self.__db = dict(sorted(db.items())) - - return super().__init__() - - @_functools.lru_cache(maxsize=512, typed=False) - def __getitem__(self, key): - return self.__db.get(key, (None, None)) - - -class SVP64Database: - def __init__(self, root, ppcdb): - db = set() - pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$") - for (prefix, _, names) in _os.walk(root): - prefix = _pathlib.Path(prefix) - for name in filter(lambda name: pattern.match(name), names): - path = (prefix / _pathlib.Path(name)) - with open(path, "r", encoding="UTF-8") as stream: - db.update(parse(stream, SVP64Record.CSV)) - db = {record.name:record for record in db} - - self.__db = dict(sorted(db.items())) - self.__ppcdb = ppcdb - - return super().__init__() - - def __getitem__(self, key): - (_, record) = self.__ppcdb[key] - if record is None: - return None - - for name in record.names: - record = self.__db.get(name, None) - if record is not None: - return record - - return None - - -class 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() - names = {} - opcodes = _collections.defaultdict( - lambda: _collections.defaultdict(set)) - - for (name, mdwn) in mdwndb: - if name.startswith("sv."): - continue - (section, ppc) = ppcdb[name] - if ppc is None: - continue - svp64 = svp64db[name] - fields = fieldsdb[ppc.form] - record = Record(name=name, - section=section, ppc=ppc, svp64=svp64, - mdwn=mdwn, fields=fields) - db.add(record) - names[record.name] = record - opcodes[section][record.PO].add(record) - - self.__db = sorted(db) - self.__names = dict(sorted(names.items())) - self.__opcodes = dict(sorted(opcodes.items())) - - return super().__init__() - - def __repr__(self): - return repr(self.__db) - - def __iter__(self): - yield from self.__db - - @_functools.lru_cache(maxsize=None) - def __contains__(self, key): - return self.__getitem__(key) is not None - - @_functools.lru_cache(maxsize=None) - def __getitem__(self, key): - if isinstance(key, SVP64Instruction): - key = key.suffix - - if isinstance(key, Instruction): - PO = int(key.PO) - key = int(key) - sections = sorted(self.__opcodes) - for section in sections: - group = self.__opcodes[section] - for record in group[PO]: - if record.match(key=key): - return record - - return None - - elif isinstance(key, str): - return self.__names.get(key) - - raise ValueError("instruction or name expected") diff --git a/src/openpower/decoder/power_svp64_rm.py b/src/openpower/decoder/power_svp64_rm.py index eeeede53..c14ead3e 100644 --- a/src/openpower/decoder/power_svp64_rm.py +++ b/src/openpower/decoder/power_svp64_rm.py @@ -3,7 +3,7 @@ # Funded by NLnet http://nlnet.nl # sigh this entire module is a laborious mess. it really should be -# auto-generated from the power_insn.py database but a technique +# auto-generated from the insndb/types.py database but a technique # for doing so (similar to python HTML/XML-node-walking) is needed """SVP64 RM (Remap) Record. diff --git a/src/openpower/decoder/power_table.py b/src/openpower/decoder/power_table.py index 985c6e52..7b83ba50 100644 --- a/src/openpower/decoder/power_table.py +++ b/src/openpower/decoder/power_table.py @@ -1,6 +1,6 @@ import collections from openpower.decoder.power_enums import find_wiki_dir -from openpower.decoder.power_insn import Database +from openpower.insndb.types import Database from openpower.util import log sections = {} diff --git a/src/openpower/insndb/__init__.py b/src/openpower/insndb/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/openpower/insndb/types.py b/src/openpower/insndb/types.py new file mode 100644 index 00000000..ca540386 --- /dev/null +++ b/src/openpower/insndb/types.py @@ -0,0 +1,3698 @@ +import collections as _collections +import csv as _csv +import dataclasses as _dataclasses +import enum as _enum +import functools as _functools +import os as _os +import operator as _operator +import pathlib as _pathlib +import re as _re +import types as _types + +try: + from functools import cached_property +except ImportError: + from cached_property import cached_property + +from openpower.decoder.power_enums import ( + Function as _Function, + MicrOp as _MicrOp, + In1Sel as _In1Sel, + In2Sel as _In2Sel, + In3Sel as _In3Sel, + OutSel as _OutSel, + CRInSel as _CRInSel, + CRIn2Sel as _CRIn2Sel, + CROutSel as _CROutSel, + LDSTLen as _LDSTLen, + LDSTMode as _LDSTMode, + RCOE as _RCOE, + CryIn as _CryIn, + Form as _Form, + SVEType as _SVEType, + SVMaskSrc as _SVMaskSrc, + SVMode as _SVMode, + SVPType as _SVPType, + SVExtra as _SVExtra, + Reg as _Reg, + RegType as _RegType, + SelType as _SelType, + SVP64SubVL as _SVP64SubVL, + SVP64Pred as _SVP64Pred, + SVP64PredMode as _SVP64PredMode, + SVP64Width as _SVP64Width, +) +from openpower.decoder.selectable_int import ( + SelectableInt as _SelectableInt, + selectconcat as _selectconcat, +) +from openpower.decoder.power_fields import ( + Field as _Field, + Mapping as _Mapping, + DecodeFields as _DecodeFields, +) +from openpower.decoder.pseudo.pagereader import ISA as _ISA + + +@_functools.total_ordering +class Style(_enum.Enum): + LEGACY = _enum.auto() + 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) + + +@_functools.total_ordering +class Priority(_enum.Enum): + LOW = -1 + NORMAL = 0 + HIGH = +1 + + @classmethod + def _missing_(cls, value): + if isinstance(value, str): + value = value.upper() + try: + return cls[value] + except ValueError: + return super()._missing_(value) + + def __lt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + + # NOTE: the order is inversed, LOW < NORMAL < HIGH + return (self.value > other.value) + + +def dataclass(cls, record, keymap=None, typemap=None): + if keymap is None: + keymap = {} + if typemap is None: + typemap = {field.name:field.type for field in _dataclasses.fields(cls)} + + def transform(key_value): + (key, value) = key_value + key = keymap.get(key, key) + hook = typemap.get(key, lambda value: value) + if hook is bool and value in ("", "0"): + value = False + else: + value = hook(value) + return (key, value) + + record = dict(map(transform, record.items())) + for key in frozenset(record.keys()): + if record[key] == "": + record.pop(key) + + return cls(**record) + + +@_functools.total_ordering +@_dataclasses.dataclass(eq=True, frozen=True) +class Opcode: + 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 self.__repr__() + + def __repr__(self): + 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 + + def __lt__(self, other): + if not isinstance(other, Opcode): + return NotImplemented + return ((self.value, self.mask) < (other.value, other.mask)) + + def __int__(self): + return (self.value & self.mask) + + def __index__(self): + return int(self).__index__() + + 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())) + + def match(self, key): + return ((self.value & self.mask) == (key & self.mask)) + + +@_functools.total_ordering +@_dataclasses.dataclass(eq=True, frozen=True) +class IntegerOpcode(Opcode): + def __init__(self, value): + if value.startswith("0b"): + 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) + + +@_functools.total_ordering +@_dataclasses.dataclass(eq=True, frozen=True) +class PatternOpcode(Opcode): + 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) + value |= (symbol == "1") + mask |= (symbol != "-") + value <<= 1 + mask <<= 1 + value >>= 1 + mask >>= 1 + + value = Opcode.Value(value) + mask = Opcode.Mask(mask) + + return super().__init__(value=value, mask=mask) + + +@_dataclasses.dataclass(eq=True, frozen=True) +class PPCRecord: + class FlagsMeta(type): + def __iter__(cls): + yield from ( + "inv A", + "inv out", + "cry out", + "BR", + "sgn ext", + "rsrv", + "32b", + "sgn", + "lk", + "sgl pipe", + ) + + class Flags(tuple, metaclass=FlagsMeta): + def __new__(cls, flags=frozenset()): + flags = frozenset(flags) + diff = (flags - frozenset(cls)) + if diff: + raise ValueError(flags) + return super().__new__(cls, sorted(flags)) + + opcode: Opcode + comment: str + flags: Flags = Flags() + comment2: str = "" + function: _Function = _Function.NONE + intop: _MicrOp = _MicrOp.OP_ILLEGAL + in1: _In1Sel = _In1Sel.NONE + in2: _In2Sel = _In2Sel.NONE + 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 + upd: _LDSTMode = _LDSTMode.NONE + Rc: _RCOE = _RCOE.NONE + form: _Form = _Form.NONE + conditions: str = "" + unofficial: bool = False + + __KEYMAP = { + "unit": "function", + "internal op": "intop", + "CR in": "cr_in", + "CR out": "cr_out", + "cry in": "cry_in", + "ldst len": "ldst_len", + "rc": "Rc", + "CONDITIONS": "conditions", + } + + def __lt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + lhs = (self.opcode, self.comment) + rhs = (other.opcode, other.comment) + return (lhs < rhs) + + @classmethod + 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) + + @cached_property + def names(self): + return frozenset(self.comment.split("=")[-1].split("/")) + + +class PPCMultiRecord(tuple): + def __getattr__(self, attr): + if attr == "opcode": + if len(self) != 1: + raise AttributeError(attr) + return getattr(self[0], attr) + + +@_dataclasses.dataclass(eq=True, frozen=True) +class SVP64Record: + class ExtraMap(tuple): + class Extra(tuple): + @_dataclasses.dataclass(eq=True, frozen=True) + class Entry: + seltype: _SelType = _SelType.NONE + reg: _Reg = _Reg.NONE + + def __repr__(self): + return f"{self.seltype.value}:{self.reg.name}" + + def __new__(cls, value="0"): + if isinstance(value, str): + def transform(value): + (seltype, reg) = value.split(":") + seltype = _SelType(seltype) + reg = _Reg(reg) + return cls.Entry(seltype=seltype, reg=reg) + + if value == "0": + value = tuple() + else: + value = map(transform, value.split(";")) + + return super().__new__(cls, value) + + def __repr__(self): + return repr(list(self)) + + def __new__(cls, value=tuple()): + value = tuple(value) + if len(value) == 0: + value = (("0",) * 4) + return super().__new__(cls, map(cls.Extra, value)) + + def __repr__(self): + return repr({index:self[index] for index in range(0, 4)}) + + name: str + ptype: _SVPType = _SVPType.NONE + etype: _SVEType = _SVEType.NONE + msrc: _SVMaskSrc = _SVMaskSrc.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 = "" + mode: _SVMode = _SVMode.NORMAL + + __KEYMAP = { + "insn": "name", + "CONDITIONS": "conditions", + "Ptype": "ptype", + "Etype": "etype", + "SM": "msrc", + "CR in": "cr_in", + "CR out": "cr_out", + } + + @classmethod + def CSV(cls, record): + record["insn"] = record["insn"].split("=")[-1] + + 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}")) + + record["extra"] = cls.ExtraMap(extra) + + return dataclass(cls, record, keymap=cls.__KEYMAP) + + @cached_property + def extras(self): + keys = ( + "in1", "in2", "in3", "cr_in", "cr_in2", + "out", "out2", "cr_out", + ) + + idxmap = ( + _SVExtra.Idx0, + _SVExtra.Idx1, + _SVExtra.Idx2, + _SVExtra.Idx3, + ) + + def extra(reg): + extras = { + _SelType.DST: {}, + _SelType.SRC: {}, + } + for index in range(0, 4): + for entry in self.extra[index]: + extras[entry.seltype][entry.reg] = idxmap[index] + + for (seltype, regs) in extras.items(): + idx = regs.get(reg, _SVExtra.NONE) + if idx is not _SVExtra.NONE: + yield (reg, seltype, idx) + + sels = {} + idxs = {} + regs = {} + seltypes = {} + for key in keys: + sel = sels[key] = getattr(self, key) + reg = regs[key] = _Reg(sel) + seltypes[key] = _SelType.NONE + idxs[key] = _SVExtra.NONE + for (reg, seltype, idx) in extra(reg.alias): + if ((idx != idxs[key]) and (idxs[key] is not _SVExtra.NONE)): + raise ValueError(idxs[key]) + idxs[key] = idx + regs[key] = reg + seltypes[key] = seltype + + if sels["cr_in"] is _CRInSel.BA_BB: + sels["cr_in"] = _CRIn2Sel.BA + sels["cr_in2"] = _CRIn2Sel.BB + idxs["cr_in2"] = idxs["cr_in"] + for key in ("cr_in", "cr_in2"): + regs[key] = _Reg(sels[key]) + seltype[key] = _SelType.SRC + + records = {} + for key in keys: + records[key] = { + "sel": sels[key], + "reg": regs[key], + "seltype": seltypes[key], + "idx": idxs[key], + } + + return _types.MappingProxyType(records) + + extra_idx_in1 = property(lambda self: self.extras["in1"]["idx"]) + extra_idx_in2 = property(lambda self: self.extras["in2"]["idx"]) + extra_idx_in3 = property(lambda self: self.extras["in3"]["idx"]) + extra_idx_out = property(lambda self: self.extras["out"]["idx"]) + extra_idx_out2 = property(lambda self: self.extras["out2"]["idx"]) + extra_idx_cr_in = property(lambda self: self.extras["cr_in"]["idx"]) + extra_idx_cr_in2 = property(lambda self: self.extras["cr_in2"]["idx"]) + extra_idx_cr_out = property(lambda self: self.extras["cr_out"]["idx"]) + + @cached_property + def extra_CR(self): + extra = None + for idx in range(0, 4): + for entry in self.extra[idx]: + if entry.seltype is _SelType.DST: + if extra is not None: + raise ValueError(self.svp64) + extra = entry + break + + if _RegType(extra.reg) not in (_RegType.CR_3BIT, _RegType.CR_5BIT): + raise ValueError(self.svp64) + + return extra + + @cached_property + def extra_CR_3bit(self): + return (_RegType(self.extra_CR.reg) is _RegType.CR_3BIT) + + +class BitSel: + def __init__(self, value=(0, 32)): + if isinstance(value, str): + (start, end) = map(int, value.split(":")) + else: + (start, end) = value + if start < 0 or end < 0 or start >= end: + raise ValueError(value) + + self.__start = start + self.__end = end + + return super().__init__() + + def __len__(self): + return (self.__end - self.__start + 1) + + def __repr__(self): + return f"[{self.__start}:{self.__end}]" + + 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 + + @property + def end(self): + return self.__end + + +@_dataclasses.dataclass(eq=True, frozen=True) +class Section: + class Mode(_enum.Enum): + INTEGER = _enum.auto() + PATTERN = _enum.auto() + + @classmethod + def _missing_(cls, value): + if isinstance(value, str): + return cls[value.upper()] + return super()._missing_(value) + + class Suffix(int): + def __new__(cls, value=None): + if isinstance(value, str): + if value.upper() == "NONE": + value = None + else: + value = int(value, 0) + if value is None: + value = 0 + + return super().__new__(cls, value) + + def __str__(self): + return repr(self) + + def __repr__(self): + return (bin(self) if self else "None") + + path: _pathlib.Path + bitsel: BitSel + suffix: Suffix + mode: Mode + opcode: IntegerOpcode = None + priority: Priority = Priority.NORMAL + + def __lt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.priority < other.priority) + + @classmethod + def CSV(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: + def __init__(self, items): + if isinstance(items, dict): + items = items.items() + + def transform(item): + (name, bitrange) = item + return (name, tuple(bitrange.values())) + + self.__mapping = dict(map(transform, items)) + + return super().__init__() + + def __repr__(self): + return repr(self.__mapping) + + def __iter__(self): + yield from self.__mapping.items() + + def __contains__(self, key): + return self.__mapping.__contains__(key) + + def __getitem__(self, key): + return self.__mapping.get(key, None) + + +class Operands: + __GPR_PAIRS = ( + _Reg.RTp, + _Reg.RSp, + ) + __FPR_PAIRS = ( + _Reg.FRAp, + _Reg.FRBp, + _Reg.FRSp, + _Reg.FRTp, + ) + + def __init__(self, insn, operands): + 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": SignedImmediateOperand, + "SI": SignedOperand, + "IB": SignedOperand, + "LI": SignedOperand, + "SIM": SignedOperand, + "SVD": SignedOperand, + "SVDS": SignedOperand, + "RSp": GPRPairOperand, + "RTp": GPRPairOperand, + "FRAp": FPRPairOperand, + "FRBp": FPRPairOperand, + "FRSp": FPRPairOperand, + "FRTp": FPRPairOperand, + } + custom_immediates = { + "DQ": EXTSOperandDQ, + "DS": EXTSOperandDS, + } + + mapping = {} + for operand in operands: + cls = DynamicOperand + + if "=" in operand: + (name, value) = operand.split("=") + mapping[name] = (StaticOperand, { + "name": name, + "value": int(value), + }) + else: + name = operand + if name.endswith(")"): + name = name.replace("(", " ").replace(")", "") + (imm_name, _, name) = name.partition(" ") + else: + imm_name = None + + if imm_name is not None: + imm_cls = custom_immediates.get(imm_name, ImmediateOperand) + + if insn in custom_insns and name in custom_insns[insn]: + cls = custom_insns[insn][name] + elif name in custom_fields: + cls = custom_fields[name] + elif name in _Reg.__members__: + reg = _Reg[name] + if reg in self.__class__.__GPR_PAIRS: + cls = GPRPairOperand + elif reg in self.__class__.__FPR_PAIRS: + cls = FPRPairOperand + else: + regtype = _RegType[name] + if regtype is _RegType.GPR: + cls = GPROperand + elif regtype is _RegType.FPR: + cls = FPROperand + elif regtype is _RegType.CR_3BIT: + cls = CR3Operand + elif regtype is _RegType.CR_5BIT: + cls = CR5Operand + + if imm_name is not None: + mapping[imm_name] = (imm_cls, {"name": imm_name}) + mapping[name] = (cls, {"name": name}) + + static = [] + dynamic = [] + for (name, (cls, kwargs)) in mapping.items(): + kwargs = dict(kwargs) + kwargs["name"] = name + if issubclass(cls, StaticOperand): + static.append((cls, kwargs)) + elif issubclass(cls, DynamicOperand): + dynamic.append((cls, kwargs)) + else: + raise ValueError(name) + + self.__mapping = mapping + self.__static = tuple(static) + self.__dynamic = tuple(dynamic) + + return super().__init__() + + def __iter__(self): + for (_, items) in self.__mapping.items(): + (cls, kwargs) = items + yield (cls, kwargs) + + def __repr__(self): + return self.__mapping.__repr__() + + def __contains__(self, key): + return self.__mapping.__contains__(key) + + def __getitem__(self, key): + return self.__mapping.__getitem__(key) + + @property + def static(self): + return self.__static + + @property + def dynamic(self): + return self.__dynamic + + +class Arguments(tuple): + def __new__(cls, record, arguments, operands): + operands = iter(tuple(operands)) + arguments = iter(tuple(arguments)) + + items = [] + while True: + try: + operand = next(operands) + except StopIteration: + break + + try: + argument = next(arguments) + except StopIteration: + raise ValueError("operands count mismatch") + + if isinstance(operand, ImmediateOperand): + argument = argument.replace("(", " ").replace(")", "") + (imm_argument, _, argument) = argument.partition(" ") + try: + (imm_operand, operand) = (operand, next(operands)) + except StopIteration: + raise ValueError("operands count mismatch") + items.append((imm_argument, imm_operand)) + items.append((argument, operand)) + + try: + next(arguments) + except StopIteration: + pass + else: + raise ValueError("operands count mismatch") + + return super().__new__(cls, items) + + +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: + name: str + section: Section + ppc: PPCRecord + fields: Fields + mdwn: MarkdownRecord + svp64: SVP64Record = None + + def __lt__(self, other): + if not isinstance(other, Record): + return NotImplemented + lhs = (min(self.opcodes), self.name) + rhs = (min(other.opcodes), other.name) + return (lhs < rhs) + + @cached_property + def operands(self): + return (self.static_operands + self.dynamic_operands) + + @cached_property + def static_operands(self): + operands = [] + operands.append(POStaticOperand(record=self, value=self.PO)) + for ppc in self.ppc: + operands.append(XOStaticOperand( + record=self, + value=ppc.opcode.value, + span=self.section.bitsel, + )) + for (cls, kwargs) in self.mdwn.operands.static: + operands.append(cls(record=self, **kwargs)) + return tuple(operands) + + @cached_property + def dynamic_operands(self): + operands = [] + for (cls, kwargs) in self.mdwn.operands.dynamic: + operands.append(cls(record=self, **kwargs)) + return tuple(operands) + + @cached_property + def opcodes(self): + def binary(mapping): + return int("".join(str(int(mapping[bit])) \ + for bit in sorted(mapping)), 2) + + def PO_XO(value, mask, opcode, bits): + value = dict(value) + mask = dict(mask) + for (src, dst) in enumerate(reversed(bits)): + value[dst] = ((opcode.value & (1 << src)) != 0) + mask[dst] = ((opcode.mask & (1 << src)) != 0) + return (value, mask) + + def PO(value, mask, opcode, bits): + return PO_XO(value=value, mask=mask, opcode=opcode, bits=bits) + + def XO(value, mask, opcode, bits): + (value, mask) = PO_XO(value=value, mask=mask, + opcode=opcode, bits=bits) + for (op_cls, op_kwargs) in self.mdwn.operands.static: + operand = op_cls(record=self, **op_kwargs) + for (src, dst) in enumerate(reversed(operand.span)): + value[dst] = ((operand.value & (1 << src)) != 0) + mask[dst] = True + return (value, mask) + + pairs = [] + value = {bit:False for bit in range(32)} + mask = {bit:False for bit in range(32)} + if self.section.opcode is not None: + (value, mask) = PO(value=value, mask=mask, + opcode=self.section.opcode, bits=range(0, 6)) + for ppc in self.ppc: + pairs.append(XO(value=value, mask=mask, + opcode=ppc.opcode, bits=self.section.bitsel)) + + result = [] + for (value, mask) in pairs: + value = Opcode.Value(binary(value)) + mask = Opcode.Mask(binary(mask)) + result.append(Opcode(value=value, mask=mask)) + + return tuple(result) + + @cached_property + def PO(self): + opcode = self.section.opcode + if opcode is None: + opcode = self.ppc[0].opcode + if isinstance(opcode, PatternOpcode): + value = int(opcode.value) + bits = opcode.value.bit_length() + return int(_SelectableInt(value=value, bits=bits)[0:6]) + + return int(opcode.value) + + @cached_property + def XO(self): + return tuple(ppc.opcode for ppc in self.ppc) + + def match(self, key): + for opcode in self.opcodes: + if opcode.match(key): + return True + + return False + + @property + def mode(self): + return self.svp64.mode + + @property + def in1(self): + return self.ppc.in1 + + @property + def in2(self): + return self.ppc.in2 + + @property + def in3(self): + return self.ppc.in3 + + @property + def out(self): + return self.ppc.out + + @property + def out2(self): + if self.svp64 is None: + return _OutSel.NONE + return self.ppc.out + + @property + 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) + + 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_in2 = property(lambda self: self.svp64.extra_idx_cr_in2) + extra_idx_cr_out = property(lambda self: self.svp64.extra_idx_cr_out) + + def __contains__(self, key): + return self.mdwn.operands.__contains__(key) + + def __getitem__(self, key): + (cls, kwargs) = self.mdwn.operands.__getitem__(key) + return cls(record=self, **kwargs) + + @cached_property + def Rc(self): + if "Rc" not in self: + return False + return self["Rc"].value + + +class Operand: + def __init__(self, record, name): + self.__record = record + self.__name = name + + def __iter__(self): + yield ("record", self.record) + yield ("name", self.__name) + + def __repr__(self): + return f"{self.__class__.__name__}({self.name})" + + @property + def name(self): + return self.__name + + @property + def record(self): + return self.__record + + @cached_property + def span(self): + return self.record.fields[self.name] + + def assemble(self, insn): + raise NotImplementedError() + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + raise NotImplementedError() + + +class DynamicOperand(Operand): + def assemble(self, insn, value): + span = self.span + if isinstance(value, str): + value = int(value, 0) + if value < 0: + raise ValueError("signed operands not allowed") + insn[span] = value + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + span = self.span + value = insn[span] + + if style >= Style.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 assemble(self, insn, value): + if isinstance(value, str): + value = int(value, 0) + return super().assemble(value=value, insn=insn) + + def assemble(self, insn, value): + span = self.span + if isinstance(value, str): + value = int(value, 0) + insn[span] = value + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + span = self.span + value = insn[span].to_signed_int() + sign = "-" if (value < 0) else "" + value = abs(value) + + if style >= Style.VERBOSE: + span = map(str, span) + yield f"{indent}{self.name}" + yield f"{indent}{indent}{sign}{value}" + yield f"{indent}{indent}{', '.join(span)}" + else: + yield f"{sign}{value}" + + +class StaticOperand(Operand): + def __init__(self, record, name, value): + self.__value = value + return super().__init__(record=record, name=name) + + def __iter__(self): + yield ("value", self.__value) + yield from super().__iter__() + + def __repr__(self): + return f"{self.__class__.__name__}({self.name}, value={self.value})" + + @property + def value(self): + return self.__value + + def assemble(self, insn): + insn[self.span] = self.value + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + span = self.span + value = insn[span] + + if style >= Style.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 SpanStaticOperand(StaticOperand): + def __init__(self, record, name, value, span): + self.__span = tuple(span) + return super().__init__(record=record, name=name, value=value) + + def __iter__(self): + yield ("span", self.__span) + yield from super().__iter__() + + @property + def span(self): + return self.__span + + +class POStaticOperand(SpanStaticOperand): + def __init__(self, record, value): + return super().__init__(record=record, name="PO", + value=value, span=range(0, 6)) + + def __iter__(self): + for (key, value) in super().__iter__(): + if key not in {"name", "span"}: + yield (key, value) + + +class XOStaticOperand(SpanStaticOperand): + def __init__(self, record, value, span): + bits = record.section.bitsel + value = _SelectableInt(value=value, bits=len(bits)) + span = dict(zip(bits, range(len(bits)))) + span_rev = {value:key for (key, value) in span.items()} + + # This part is tricky: we cannot use record.operands, + # as this code is called by record.static_operands method. + for (cls, kwargs) in record.mdwn.operands: + operand = cls(record=record, **kwargs) + for idx in operand.span: + rev = span.pop(idx, None) + if rev is not None: + span_rev.pop(rev, None) + + value = int(_selectconcat(*(value[bit] for bit in span.values()))) + span = tuple(span.keys()) + + return super().__init__(record=record, name="XO", + value=value, span=span) + + def __iter__(self): + for (key, value) in super().__iter__(): + if key not in {"name"}: + yield (key, value) + + +class ImmediateOperand(DynamicOperand): + pass + + +class SignedImmediateOperand(SignedOperand, ImmediateOperand): + pass + + +class NonZeroOperand(DynamicOperand): + def assemble(self, insn, value): + if isinstance(value, str): + value = int(value, 0) + if not isinstance(value, int): + raise ValueError("non-integer operand") + if value == 0: + raise ValueError("non-zero operand") + value -= 1 + return super().assemble(value=value, insn=insn) + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + span = self.span + value = insn[span] + + if style >= Style.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 ExtendableOperand(DynamicOperand): + def sv_spec_enter(self, value, span): + return (value, span) + + def sv_spec(self, insn): + vector = False + span = self.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) + + for extra_idx in self.extra_idx: + if self.record.etype is _SVEType.EXTRA3: + spec = insn.prefix.rm.extra3[extra_idx] + elif self.record.etype is _SVEType.EXTRA2: + spec = insn.prefix.rm.extra2[extra_idx] + else: + raise ValueError(self.record.etype) + + if spec != 0: + vector = bool(spec[0]) + spec_span = spec.__class__ + if self.record.etype is _SVEType.EXTRA3: + spec_span = tuple(map(str, spec_span[1, 2])) + spec = spec[1, 2] + elif self.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(self.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) + + def sv_spec_leave(self, value, span, origin_value, origin_span): + return (value, span) + + @property + def extra_idx(self): + for (key, record) in self.record.svp64.extras.items(): + if record["reg"].alias is self.extra_reg.alias: + yield record["idx"] + + @cached_property + def extra_reg(self): + return _Reg(self.name) + + def remap(self, value, vector): + raise NotImplementedError() + + def assemble(self, value, insn, prefix): + vector = False + + if isinstance(value, str): + value = value.lower() + if value.startswith("%"): + value = value[1:] + if value.startswith("*"): + if not isinstance(insn, SVP64Instruction): + raise ValueError(value) + value = value[1:] + vector = True + if value.startswith(prefix): + if (self.extra_reg.or_zero and (value == f"{prefix}0")): + raise ValueError(value) + value = value[len(prefix):] + value = int(value, 0) + + if isinstance(insn, SVP64Instruction): + (value, extra) = self.remap(value=value, vector=vector) + + for extra_idx in self.extra_idx: + if self.record.etype is _SVEType.EXTRA3: + insn.prefix.rm.extra3[extra_idx] = extra + elif self.record.etype is _SVEType.EXTRA2: + insn.prefix.rm.extra2[extra_idx] = extra + else: + raise ValueError(self.record.etype) + + return super().assemble(value=value, insn=insn) + + def disassemble(self, insn, + style=Style.NORMAL, prefix="", indent=""): + (vector, value, span) = self.sv_spec(insn=insn) + + if (self.extra_reg.or_zero and (value == 0)): + prefix = "" + + if style >= Style.VERBOSE: + mode = "vector" if vector else "scalar" + yield f"{indent}{self.name} ({mode})" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + if isinstance(insn, SVP64Instruction): + for extra_idx in frozenset(self.extra_idx): + if self.record.etype is _SVEType.NONE: + yield f"{indent}{indent}extra[none]" + else: + etype = repr(self.record.etype).lower() + yield f"{indent}{indent}{etype}{extra_idx!r}" + else: + vector = "*" if vector else "" + yield f"{vector}{prefix}{int(value)}" + + +class SimpleRegisterOperand(ExtendableOperand): + def remap(self, value, vector): + if vector: + extra = (value & 0b11) + value = (value >> 2) + else: + extra = (value >> 5) + value = (value & 0b11111) + + # now sanity-check. EXTRA3 is ok, EXTRA2 has limits + # (and shrink to a single bit if ok) + if self.record.etype is _SVEType.EXTRA2: + if vector: + # range is r0-r127 in increments of 2 (r0 r2 ... r126) + assert (extra & 0b01) == 0, \ + ("vector field %s cannot fit into EXTRA2" % value) + extra = (0b10 | (extra >> 1)) + else: + # range is r0-r63 in increments of 1 + assert (extra >> 1) == 0, \ + ("scalar GPR %d cannot fit into EXTRA2" % value) + extra &= 0b01 + elif self.record.etype is _SVEType.EXTRA3: + if vector: + # EXTRA3 vector bit needs marking + extra |= 0b100 + else: + raise ValueError(self.record.etype) + + return (value, extra) + + +class GPROperand(SimpleRegisterOperand): + def assemble(self, insn, value): + return super().assemble(value=value, insn=insn, prefix="r") + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + prefix = "" if (style <= Style.SHORT) else "r" + yield from super().disassemble(prefix=prefix, insn=insn, + style=style, indent=indent) + + +class GPRPairOperand(GPROperand): + pass + + +class FPROperand(SimpleRegisterOperand): + def assemble(self, insn, value): + return super().assemble(value=value, insn=insn, prefix="f") + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + prefix = "" if (style <= Style.SHORT) else "f" + yield from super().disassemble(prefix=prefix, insn=insn, + style=style, indent=indent) + + +class FPRPairOperand(FPROperand): + pass + + +class ConditionRegisterFieldOperand(ExtendableOperand): + def pattern(name_pattern): + (name, pattern) = name_pattern + return (name, _re.compile(f"^{pattern}$", _re.S)) + + CONDS = { + "lt": 0, + "gt": 1, + "eq": 2, + "so": 3, + "un": 3, + } + CR = r"(?:CR|cr)([0-9]+)" + N = r"([0-9]+)" + BIT = rf"({'|'.join(CONDS.keys())})" + LBIT = fr"{BIT}\s*\+\s*" # BIT+ + RBIT = fr"\s*\+\s*{BIT}" # +BIT + CRN = fr"{CR}\s*\*\s*{N}" # CR*N + NCR = fr"{N}\s*\*\s*{CR}" # N*CR + XCR = fr"{CR}\.{BIT}" + PATTERNS = tuple(map(pattern, ( + ("CR", CR), + ("XCR", XCR), + ("CR*N", CRN), + ("N*CR", NCR), + ("BIT+CR", (LBIT + CR)), + ("CR+BIT", (CR + RBIT)), + ("BIT+CR*N", (LBIT + CRN)), + ("CR*N+BIT", (CRN + RBIT)), + ("BIT+N*CR", (LBIT + NCR)), + ("N*CR+BIT", (NCR + RBIT)), + ))) + + def remap(self, value, vector, regtype): + if regtype is _RegType.CR_5BIT: + subvalue = (value & 0b11) + value >>= 2 + + if vector: + extra = (value & 0b1111) + value >>= 4 + else: + extra = (value >> 3) + value &= 0b111 + + if self.record.etype is _SVEType.EXTRA2: + if vector: + assert (extra & 0b111) == 0, \ + "vector CR cannot fit into EXTRA2" + extra = (0b10 | (extra >> 3)) + else: + assert (extra >> 1) == 0, \ + "scalar CR cannot fit into EXTRA2" + extra &= 0b01 + elif self.record.etype is _SVEType.EXTRA3: + if vector: + assert (extra & 0b11) == 0, \ + "vector CR cannot fit into EXTRA3" + extra = (0b100 | (extra >> 2)) + else: + assert (extra >> 2) == 0, \ + "scalar CR cannot fit into EXTRA3" + extra &= 0b11 + + if regtype is _RegType.CR_5BIT: + value = ((value << 2) | subvalue) + + return (value, extra) + + def assemble(self, insn, value): + if isinstance(value, str): + vector = False + + if value.startswith("*"): + if not isinstance(insn, SVP64Instruction): + raise ValueError(value) + value = value[1:] + vector = True + + for (name, pattern) in reversed(self.__class__.PATTERNS): + match = pattern.match(value) + if match is not None: + keys = name.replace("+", "_").replace("*", "_").split("_") + values = match.groups() + match = dict(zip(keys, values)) + CR = int(match["CR"]) + if name == "XCR": + N = 4 + else: + N = int(match.get("N", "1")) + BIT = self.__class__.CONDS[match.get("BIT", "lt")] + value = ((CR * N) + BIT) + break + + value = str(value) + if vector: + value = f"*{value}" + + return super().assemble(value=value, insn=insn, prefix="cr") + + def disassemble(self, insn, + style=Style.NORMAL, prefix="", indent=""): + (vector, value, span) = self.sv_spec(insn=insn) + + if style >= Style.VERBOSE: + mode = "vector" if vector else "scalar" + yield f"{indent}{self.name} ({mode})" + yield f"{indent}{indent}{int(value):0{value.bits}b}" + yield f"{indent}{indent}{', '.join(span)}" + if isinstance(insn, SVP64Instruction): + for extra_idx in frozenset(self.extra_idx): + if self.record.etype is _SVEType.NONE: + yield f"{indent}{indent}extra[none]" + else: + etype = repr(self.record.etype).lower() + yield f"{indent}{indent}{etype}{extra_idx!r}" + else: + vector = "*" if vector else "" + CR = int(value >> 2) + CC = int(value & 3) + cond = ("lt", "gt", "eq", "so")[CC] + if style >= Style.NORMAL: + if CR != 0: + if isinstance(insn, SVP64Instruction): + yield f"{vector}cr{CR}.{cond}" + else: + yield f"4*cr{CR}+{cond}" + else: + yield cond + else: + yield f"{vector}{prefix}{int(value)}" + + +class CR3Operand(ConditionRegisterFieldOperand): + def remap(self, value, vector): + return super().remap(value=value, vector=vector, + regtype=_RegType.CR_3BIT) + + +class CR5Operand(ConditionRegisterFieldOperand): + def remap(self, value, vector): + return super().remap(value=value, vector=vector, + regtype=_RegType.CR_5BIT) + + 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 EXTSOperand(SignedOperand): + field: str # real name to report + nz: int = 0 # number of zeros + fmt: str = "d" # integer formatter + + def __init__(self, record, name, field, nz=0, fmt="d"): + self.__field = field + self.__nz = nz + self.__fmt = fmt + return super().__init__(record=record, name=name) + + @property + def field(self): + return self.__field + + @property + def nz(self): + return self.__nz + + @property + def fmt(self): + return self.__fmt + + @cached_property + def span(self): + return self.record.fields[self.field] + + def assemble(self, insn, value): + span = self.span + if isinstance(value, str): + value = int(value, 0) + insn[span] = (value >> self.nz) + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + span = self.span + value = insn[span].to_signed_int() + sign = "-" if (value < 0) else "" + value = (abs(value) << self.nz) + + if style >= Style.VERBOSE: + span = (tuple(map(str, span)) + (("{0}",) * self.nz)) + zeros = ("0" * self.nz) + hint = f"{self.name} = EXTS({self.field} || {zeros})" + yield f"{indent * 1}{hint}" + yield f"{indent * 2}{self.field}" + yield f"{indent * 3}{sign}{value:{self.fmt}}" + yield f"{indent * 3}{', '.join(span)}" + else: + yield f"{sign}{value:{self.fmt}}" + + +class TargetAddrOperand(EXTSOperand): + def __init__(self, record, name, field): + return super().__init__(record=record, name=name, field=field, + nz=2, fmt="#x") + + +class TargetAddrOperandLI(TargetAddrOperand): + def __init__(self, record, name): + return super().__init__(record=record, name=name, field="LI") + + +class TargetAddrOperandBD(TargetAddrOperand): + def __init__(self, record, name): + return super().__init__(record=record, name=name, field="BD") + + +class EXTSOperandDS(EXTSOperand, ImmediateOperand): + def __init__(self, record, name): + return super().__init__(record=record, name=name, field="DS", nz=2) + + +class EXTSOperandDQ(EXTSOperand, ImmediateOperand): + def __init__(self, record, name): + return super().__init__(record=record, name=name, field="DQ", nz=4) + + +class DOperandDX(SignedOperand): + @cached_property + def span(self): + cls = lambda name: DynamicOperand(record=self.record, name=name) + operands = map(cls, ("d0", "d1", "d2")) + spans = map(lambda operand: operand.span, operands) + return sum(spans, tuple()) + + def disassemble(self, insn, + style=Style.NORMAL, indent=""): + span = self.span + value = insn[span].to_signed_int() + sign = "-" if (value < 0) else "" + value = abs(value) + + if style >= Style.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 + span = map(str, span) + yield f"{indent}{indent}{operand.name} = D{subspan}" + yield f"{indent}{indent}{indent}{sign}{value}" + yield f"{indent}{indent}{indent}{', '.join(span)}" + else: + yield f"{sign}{value}" + + +class Instruction(_Mapping): + @classmethod + def integer(cls, value=0, bits=None, byteorder="little"): + if isinstance(value, (int, bytes)) and not isinstance(bits, int): + raise ValueError(bits) + + if isinstance(value, bytes): + if ((len(value) * 8) != bits): + raise ValueError(f"bit length mismatch") + value = int.from_bytes(value, byteorder=byteorder) + + if isinstance(value, int): + value = _SelectableInt(value=value, bits=bits) + elif isinstance(value, Instruction): + value = value.storage + + if not isinstance(value, _SelectableInt): + raise ValueError(value) + if bits is None: + bits = len(cls) + if len(value) != bits: + raise ValueError(value) + + value = _SelectableInt(value=value, bits=bits) + + return cls(storage=value) + + 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 = (len(self.__class__) // 8) + return int(self).to_bytes(nr_bytes, byteorder=byteorder) + + @classmethod + def record(cls, db, entry): + record = db[entry] + if record is None: + raise KeyError(entry) + return record + + @classmethod + def operands(cls, record): + yield from record.operands + + @classmethod + def static_operands(cls, record): + return filter(lambda operand: isinstance(operand, StaticOperand), + cls.operands(record=record)) + + @classmethod + def dynamic_operands(cls, record): + return filter(lambda operand: isinstance(operand, DynamicOperand), + cls.operands(record=record)) + + def spec(self, record, prefix): + dynamic_operands = tuple(map(_operator.itemgetter(0), + self.spec_dynamic_operands(record=record))) + + static_operands = [] + for (name, value) in self.spec_static_operands(record=record): + static_operands.append(f"{name}={value}") + + operands = "" + if dynamic_operands: + operands += " " + operands += ",".join(dynamic_operands) + if static_operands: + operands += " " + operands += " ".join(static_operands) + + return f"{prefix}{record.name}{operands}" + + def spec_static_operands(self, record): + for operand in self.static_operands(record=record): + if not isinstance(operand, (POStaticOperand, XOStaticOperand)): + yield (operand.name, operand.value) + + def spec_dynamic_operands(self, record, style=Style.NORMAL): + imm = False + imm_name = "" + imm_value = "" + for operand in self.dynamic_operands(record=record): + name = operand.name + value = " ".join(operand.disassemble(insn=self, + style=min(style, Style.NORMAL))) + if imm: + name = f"{imm_name}({name})" + value = f"{imm_value}({value})" + imm = False + if isinstance(operand, ImmediateOperand): + imm_name = name + imm_value = value + imm = True + if not imm: + yield (name, value) + + @classmethod + def assemble(cls, record, arguments=None): + if arguments is None: + arguments = () + + insn = cls.integer(value=0) + + for operand in cls.static_operands(record=record): + operand.assemble(insn=insn) + + arguments = Arguments(record=record, + arguments=arguments, operands=cls.dynamic_operands(record=record)) + for (value, operand) in arguments: + operand.assemble(insn=insn, value=value) + + return insn + + def disassemble(self, record, + byteorder="little", + style=Style.NORMAL): + raise NotImplementedError() + + +class WordInstruction(Instruction): + _: _Field = range(0, 32) + PO: _Field = range(0, 6) + + @classmethod + def integer(cls, value, byteorder="little"): + return super().integer(bits=32, value=value, byteorder=byteorder) + + @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, record, + byteorder="little", + style=Style.NORMAL): + if style <= Style.SHORT: + blob = "" + else: + blob = self.bytes(byteorder=byteorder) + blob = " ".join(map(lambda byte: f"{byte:02x}", blob)) + blob += " " + + if record is None: + yield f"{blob}.long 0x{int(self):08x}" + return + + # awful temporary hack: workaround for ld-update + # https://bugs.libre-soc.org/show_bug.cgi?id=1056#c2 + # XXX TODO must check that *EXTENDED* RA != extended-RT + if (record.svp64 is not None and + record.mode == _SVMode.LDST_IMM and + 'u' in record.name): + yield f"{blob}.long 0x{int(self):08x}" + return + + paired = False + if style is Style.LEGACY: + paired = False + for operand in self.dynamic_operands(record=record): + if isinstance(operand, (GPRPairOperand, FPRPairOperand)): + paired = True + + if style is Style.LEGACY and (paired or record.ppc.unofficial): + yield f"{blob}.long 0x{int(self):08x}" + else: + operands = tuple(map(_operator.itemgetter(1), + self.spec_dynamic_operands(record=record, style=style))) + if operands: + operands = ",".join(operands) + yield f"{blob}{record.name} {operands}" + else: + yield f"{blob}{record.name}" + + if style >= Style.VERBOSE: + indent = (" " * 4) + binary = self.binary + spec = self.spec(record=record, 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 self.operands(record=record): + yield from operand.disassemble(insn=self, + style=style, indent=indent) + yield "" + + +class PrefixedInstruction(Instruction): + class Prefix(WordInstruction.remap(range(0, 32))): + pass + + class Suffix(WordInstruction.remap(range(32, 64))): + pass + + _: _Field = range(64) + prefix: Prefix + suffix: Suffix + PO: Suffix.PO + + @classmethod + def integer(cls, value, byteorder="little"): + return super().integer(bits=64, value=value, byteorder=byteorder) + + @classmethod + def pair(cls, prefix=0, suffix=0, byteorder="little"): + def transform(value): + return WordInstruction.integer(value=value, + byteorder=byteorder)[0:32] + + (prefix, suffix) = map(transform, (prefix, suffix)) + value = _selectconcat(prefix, suffix) + + return super().integer(bits=64, value=value) + + +class Mode(_Mapping): + _: _Field = range(0, 5) + sel: _Field = (0, 1) + + +class Extra(_Mapping): + _: _Field = range(0, 9) + + +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 specifiers(self, record): + subvl = int(self.subvl) + if subvl > 0: + yield { + 1: "vec2", + 2: "vec3", + 3: "vec4", + }[subvl] + + def disassemble(self, style=Style.NORMAL): + if style >= Style.VERBOSE: + indent = (" " * 4) + for (name, span) in self.traverse(path="RM"): + value = self.storage[span] + yield f"{name}" + yield f"{indent}{int(value):0{value.bits}b}" + yield f"{indent}{', '.join(map(str, span))}" + + +class FFRc1BaseRM(BaseRM): + def specifiers(self, record, mode): + inv = _SelectableInt(value=int(self.inv), bits=1) + CR = _SelectableInt(value=int(self.CR), bits=2) + mask = int(_selectconcat(CR, inv)) + predicate = PredicateBaseRM.predicate(True, mask) + yield f"{mode}={predicate}" + + yield from super().specifiers(record=record) + + +class FFRc0BaseRM(BaseRM): + def specifiers(self, record, mode): + if self.RC1: + inv = "~" if self.inv else "" + yield f"{mode}={inv}RC1" + + yield from super().specifiers(record=record) + + +class SatBaseRM(BaseRM): + def specifiers(self, record): + if self.N: + yield "sats" + else: + yield "satu" + + yield from super().specifiers(record=record) + + +class ZZBaseRM(BaseRM): + def specifiers(self, record): + if self.zz: + yield "zz" + + yield from super().specifiers(record=record) + + +class ZZCombinedBaseRM(BaseRM): + def specifiers(self, record): + if self.sz and self.dz: + yield "zz" + elif self.sz: + yield "sz" + elif self.dz: + yield "dz" + + yield from super().specifiers(record=record) + + +class DZBaseRM(BaseRM): + def specifiers(self, record): + if self.dz: + yield "dz" + + yield from super().specifiers(record=record) + + +class SZBaseRM(BaseRM): + def specifiers(self, record): + if self.sz: + yield "sz" + + yield from super().specifiers(record=record) + + +class MRBaseRM(BaseRM): + def specifiers(self, record): + if self.RG: + yield "mrr" + else: + yield "mr" + + yield from super().specifiers(record=record) + + +class ElsBaseRM(BaseRM): + def specifiers(self, record): + if self.els: + yield "els" + + yield from super().specifiers(record=record) + + +class WidthBaseRM(BaseRM): + @staticmethod + def width(FP, width): + width = { + 0b11: "8", + 0b10: "16", + 0b01: "32", + }.get(width) + if width is None: + return None + if FP: + width = ("fp" + width) + return width + + def specifiers(self, record): + # elwidths: use "w=" if same otherwise dw/sw + # FIXME this should consider FP instructions + FP = False + dw = WidthBaseRM.width(FP, int(self.elwidth)) + sw = WidthBaseRM.width(FP, int(self.ewsrc)) + if record.svp64.mode is _SVMode.CROP: + if dw: + yield ("dw=" + dw) + else: + sw = WidthBaseRM.width(FP, int(self.ewsrc)) + if dw == sw and dw: + yield ("w=" + dw) + else: + if dw: + yield ("dw=" + dw) + if sw: + yield ("sw=" + sw) + + yield from super().specifiers(record=record) + + +class PredicateBaseRM(BaseRM): + @staticmethod + def predicate(CR, mask): + return { + # integer + (False, 0b001): "1< 0: # if any separate with a space + operands = (" " + operands) + + if style <= Style.LEGACY: + yield f"{blob_prefix}.long 0x{int(self.prefix):08x}" + suffix = WordInstruction.integer(value=int(self.suffix)) + yield from suffix.disassemble(record=record, + byteorder=byteorder, style=style) + else: + yield f"{blob_prefix}{name}{specifiers}{operands}" + if blob_suffix: + yield f"{blob_suffix}" + + if style >= Style.VERBOSE: + indent = (" " * 4) + binary = self.binary + spec = self.spec(record=record, 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 self.operands(record=record): + yield from operand.disassemble(insn=self, + style=style, indent=indent) + yield f"{indent}RM" + yield f"{indent}{indent}{str(rm)}" + for line in rm.disassemble(style=style): + yield f"{indent}{indent}{line}" + yield "" + + @classmethod + def operands(cls, record): + for operand in super().operands(record=record): + parent = operand.__class__ + name = f"SVP64{parent.__name__}" + bases = (SVP64Operand, parent) + child = type(name, bases, {}) + yield child(**dict(operand)) + + +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(match, entries) + return tuple(map(factory, entries)) + + +class MarkdownDatabase: + def __init__(self): + db = {} + for (name, desc) in _ISA(): + operands = [] + if desc.regs: + (dynamic, *static) = desc.regs + operands.extend(dynamic) + operands.extend(static) + pcode = PCode(iterable=filter(str.strip, desc.pcode)) + operands = Operands(insn=name, operands=operands) + db[name] = MarkdownRecord(pcode=pcode, operands=operands) + + self.__db = dict(sorted(db.items())) + + 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) + + +class FieldsDatabase: + def __init__(self): + db = {} + df = _DecodeFields() + df.create_specs() + for (form, fields) in df.instrs.items(): + if form in {"DQE", "TX"}: + continue + if form == "all": + form = "NONE" + db[_Form[form]] = Fields(fields) + + self.__db = db + + return super().__init__() + + def __getitem__(self, key): + return self.__db.__getitem__(key) + + +class PPCDatabase: + def __init__(self, root, mdwndb): + # The code below groups the instructions by name:section. + # There can be multiple names for the same instruction. + # The point is to capture different opcodes for the same instruction. + sections = {} + records = _collections.defaultdict(set) + path = (root / "insndb.csv") + with open(path, "r", encoding="UTF-8") as stream: + for section in sorted(parse(stream, Section.CSV)): + path = (root / section.path) + opcode_cls = { + section.Mode.INTEGER: IntegerOpcode, + section.Mode.PATTERN: PatternOpcode, + }[section.mode] + factory = _functools.partial( + PPCRecord.CSV, opcode_cls=opcode_cls) + with open(path, "r", encoding="UTF-8") as stream: + for insn in parse(stream, factory): + for name in insn.names: + records[name].add(insn) + sections[name] = section + + items = sorted(records.items()) + records = {} + for (name, multirecord) in items: + records[name] = PPCMultiRecord(sorted(multirecord)) + + def exact_match(name): + record = records.get(name) + if record is None: + return None + return name + + def LK_match(name): + if not name.endswith("l"): + return None + alias = exact_match(name[:-1]) + if alias is None: + return None + record = records[alias] + if "lk" not in record.flags: + raise ValueError(record) + return alias + + def AA_match(name): + if not name.endswith("a"): + return None + alias = LK_match(name[:-1]) + if alias is None: + alias = name[:-1] + record = records[alias] + if record.intop not in {_MicrOp.OP_B, _MicrOp.OP_BC}: + raise ValueError(record) + if "AA" not in mdwndb[name].operands: + raise ValueError(record) + return alias + + def Rc_match(name): + if not name.endswith("."): + return None + alias = exact_match(name[:-1]) + if alias is None: + return None + record = records[alias] + if record.Rc is _RCOE.NONE: + raise ValueError(record) + return alias + + db = {} + matches = (exact_match, LK_match, AA_match, Rc_match) + for (name, _) in mdwndb: + if name.startswith("sv."): + continue + alias = None + for match in matches: + alias = match(name) + if alias is not None: + break + if alias is None: + continue + section = sections[alias] + record = records[alias] + db[name] = (section, record) + + self.__db = dict(sorted(db.items())) + + return super().__init__() + + @_functools.lru_cache(maxsize=512, typed=False) + def __getitem__(self, key): + return self.__db.get(key, (None, None)) + + +class SVP64Database: + def __init__(self, root, ppcdb): + db = set() + pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$") + for (prefix, _, names) in _os.walk(root): + prefix = _pathlib.Path(prefix) + for name in filter(lambda name: pattern.match(name), names): + path = (prefix / _pathlib.Path(name)) + with open(path, "r", encoding="UTF-8") as stream: + db.update(parse(stream, SVP64Record.CSV)) + db = {record.name:record for record in db} + + self.__db = dict(sorted(db.items())) + self.__ppcdb = ppcdb + + return super().__init__() + + def __getitem__(self, key): + (_, record) = self.__ppcdb[key] + if record is None: + return None + + for name in record.names: + record = self.__db.get(name, None) + if record is not None: + return record + + return None + + +class 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() + names = {} + opcodes = _collections.defaultdict( + lambda: _collections.defaultdict(set)) + + for (name, mdwn) in mdwndb: + if name.startswith("sv."): + continue + (section, ppc) = ppcdb[name] + if ppc is None: + continue + svp64 = svp64db[name] + fields = fieldsdb[ppc.form] + record = Record(name=name, + section=section, ppc=ppc, svp64=svp64, + mdwn=mdwn, fields=fields) + db.add(record) + names[record.name] = record + opcodes[section][record.PO].add(record) + + self.__db = sorted(db) + self.__names = dict(sorted(names.items())) + self.__opcodes = dict(sorted(opcodes.items())) + + return super().__init__() + + def __repr__(self): + return repr(self.__db) + + def __iter__(self): + yield from self.__db + + @_functools.lru_cache(maxsize=None) + def __contains__(self, key): + return self.__getitem__(key) is not None + + @_functools.lru_cache(maxsize=None) + def __getitem__(self, key): + if isinstance(key, SVP64Instruction): + key = key.suffix + + if isinstance(key, Instruction): + PO = int(key.PO) + key = int(key) + sections = sorted(self.__opcodes) + for section in sections: + group = self.__opcodes[section] + for record in group[PO]: + if record.match(key=key): + return record + + return None + + elif isinstance(key, str): + return self.__names.get(key) + + raise ValueError("instruction or name expected") diff --git a/src/openpower/sv/sv_binutils.py b/src/openpower/sv/sv_binutils.py index b4dea0ac..f08f70c6 100644 --- a/src/openpower/sv/sv_binutils.py +++ b/src/openpower/sv/sv_binutils.py @@ -22,8 +22,8 @@ from openpower.decoder.power_enums import ( find_wiki_dir as _find_wiki_dir, ) from openpower.consts import SVP64MODE as _SVP64MODE -from openpower.decoder.power_insn import Database as _Database -from openpower.decoder.power_insn import SVP64Instruction as _SVP64Instruction +from openpower.insndb.types import Database as _Database +from openpower.insndb.types import SVP64Instruction as _SVP64Instruction DISCLAIMER = """\ diff --git a/src/openpower/sv/sv_binutils_fptrans.py b/src/openpower/sv/sv_binutils_fptrans.py index cbe59b8e..ac26657b 100644 --- a/src/openpower/sv/sv_binutils_fptrans.py +++ b/src/openpower/sv/sv_binutils_fptrans.py @@ -8,7 +8,7 @@ from openpower.decoder.power_enums import ( FPTRANS_INSNS as _FPTRANS_INSNS, find_wiki_dir as _find_wiki_dir, ) -from openpower.decoder.power_insn import ( +from openpower.insndb.types import ( Database as _Database, StaticOperand as _StaticOperand, WordInstruction as _WordInstruction, diff --git a/src/openpower/sv/trans/pysvp64dis.py b/src/openpower/sv/trans/pysvp64dis.py index 5e1d6ad0..720963ba 100644 --- a/src/openpower/sv/trans/pysvp64dis.py +++ b/src/openpower/sv/trans/pysvp64dis.py @@ -6,7 +6,7 @@ import os from openpower.decoder.power_enums import ( find_wiki_dir, ) -from openpower.decoder.power_insn import ( +from openpower.insndb.types import ( Style, Database, WordInstruction, diff --git a/src/openpower/sv/trans/svp64.py b/src/openpower/sv/trans/svp64.py index e5a59ffc..f05cefd1 100644 --- a/src/openpower/sv/trans/svp64.py +++ b/src/openpower/sv/trans/svp64.py @@ -28,10 +28,10 @@ from openpower.decoder.pseudo.pagereader import ISA from openpower.decoder.power_svp64 import SVP64RM, get_regtype, decode_extra from openpower.decoder.selectable_int import SelectableInt from openpower.consts import SVP64MODE -from openpower.decoder.power_insn import SVP64Instruction -from openpower.decoder.power_insn import Database -from openpower.decoder.power_insn import Style -from openpower.decoder.power_insn import WordInstruction +from openpower.insndb.types import SVP64Instruction +from openpower.insndb.types import Database +from openpower.insndb.types import Style +from openpower.insndb.types import WordInstruction from openpower.decoder.power_enums import find_wiki_dir # for debug logging diff --git a/src/openpower/sv/trans/test_pysvp64dis.py b/src/openpower/sv/trans/test_pysvp64dis.py index e06fe45e..13c2152d 100644 --- a/src/openpower/sv/trans/test_pysvp64dis.py +++ b/src/openpower/sv/trans/test_pysvp64dis.py @@ -1,7 +1,7 @@ from openpower.simulator.program import Program from openpower.sv.trans.pysvp64dis import load, dump from openpower.sv.trans.svp64 import SVP64Asm -from openpower.decoder.power_insn import Database, Style +from openpower.insndb.types import Database, Style from openpower.decoder.power_enums import find_wiki_dir from openpower.sv import sv_binutils_fptrans import unittest diff --git a/src/openpower/sv/trans/test_pysvp64dis_branch.py b/src/openpower/sv/trans/test_pysvp64dis_branch.py index 9a2d54b9..c809cba8 100644 --- a/src/openpower/sv/trans/test_pysvp64dis_branch.py +++ b/src/openpower/sv/trans/test_pysvp64dis_branch.py @@ -1,7 +1,7 @@ from openpower.simulator.program import Program from openpower.sv.trans.pysvp64dis import load, dump from openpower.sv.trans.svp64 import SVP64Asm -from openpower.decoder.power_insn import Database, Style +from openpower.insndb.types import Database, Style from openpower.decoder.power_enums import find_wiki_dir from openpower.sv import sv_binutils_fptrans import unittest