import dataclasses as _dataclasses
import enum as _enum
import functools as _functools
+import itertools as _itertools
import os as _os
import operator as _operator
import pathlib as _pathlib
from openpower.decoder.pseudo.pagereader import ISA as _ISA
+@_functools.total_ordering
+class Verbosity(_enum.Enum):
+ SHORT = _enum.auto()
+ NORMAL = _enum.auto()
+ VERBOSE = _enum.auto()
+
+ def __lt__(self, other):
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return (self.value < other.value)
+
+
def dataclass(cls, record, keymap=None, typemap=None):
if keymap is None:
keymap = {}
return super().__init__(value=value, mask=mask)
-class FieldsOpcode(Opcode):
- def __init__(self, fields):
- def field(opcode, field):
- (value, mask) = opcode
- (field, bits) = field
- shifts = map(lambda bit: (31 - bit), reversed(tuple(bits)))
- for (index, shift) in enumerate(shifts):
- bit = ((field & (1 << index)) != 0)
- value |= (bit << shift)
- mask |= (1 << shift)
- return (value, mask)
-
- (value, mask) = _functools.reduce(field, fields, (0, 0))
-
- return super().__init__(value=value, mask=mask)
-
-
@_dataclasses.dataclass(eq=True, frozen=True)
class PPCRecord:
class FlagsMeta(type):
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
class Operand:
name: str
- def disassemble(self, insn, record, verbose=False, indent=""):
+ def span(self, record):
+ return record.fields[self.name]
+
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
raise NotImplementedError
-@_dataclasses.dataclass(eq=True, frozen=True)
class DynamicOperand(Operand):
- def disassemble(self, insn, record, verbose=False, indent=""):
- span = record.fields[self.name]
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
+ span = self.span(record=record)
if isinstance(insn, SVP64Instruction):
span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
- if verbose:
+ if verbosity >= Verbosity.VERBOSE:
span = map(str, span)
yield f"{indent}{self.name}"
yield f"{indent}{indent}{int(value):0{value.bits}b}"
@_dataclasses.dataclass(eq=True, frozen=True)
+class StaticOperand(Operand):
+ value: int
+
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
+ span = self.span(record=record)
+ if isinstance(insn, SVP64Instruction):
+ span = tuple(map(lambda bit: (bit + 32), span))
+ value = insn[span]
+
+ if verbosity >= Verbosity.VERBOSE:
+ span = map(str, span)
+ yield f"{indent}{self.name}"
+ yield f"{indent}{indent}{int(value):0{value.bits}b}"
+ yield f"{indent}{indent}{', '.join(span)}"
+ else:
+ yield str(int(value))
+
+
+class ImmediateOperand(DynamicOperand):
+ pass
+
+
class DynamicOperandReg(DynamicOperand):
- def spec(self, insn, record):
+ def spec(self, insn, record, merge):
vector = False
- span = record.fields[self.name]
+ span = self.span(record=record)
if isinstance(insn, SVP64Instruction):
span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
+ span = tuple(map(str, span))
if isinstance(insn, SVP64Instruction):
extra_idx = self.extra_idx(record=record)
+ if extra_idx is _SVExtra.NONE:
+ return (vector, value, span)
if record.etype is _SVEtype.EXTRA3:
- extra = insn.prefix.rm.extra3[extra_idx]
+ spec = insn.prefix.rm.extra3[extra_idx]
elif record.etype is _SVEtype.EXTRA2:
- extra = insn.prefix.rm.extra2[extra_idx]
+ spec = insn.prefix.rm.extra2[extra_idx]
else:
raise ValueError(record.etype)
- if extra != 0:
- vector = bool(extra[0])
- span = tuple(map(str, span))
- extra_span = extra.__class__
+ if spec != 0:
+ vector = bool(spec[0])
+ spec_span = spec.__class__
if record.etype is _SVEtype.EXTRA3:
- extra_span = tuple(map(str, extra_span[1, 2]))
- extra = extra[1, 2]
+ spec_span = tuple(map(str, spec_span[1, 2]))
+ spec = spec[1, 2]
elif record.etype is _SVEtype.EXTRA2:
- extra_span = tuple(map(str, extra_span[1,]))
- extra = _SelectableInt(value=extra[1].value, bits=2)
+ spec_span = tuple(map(str, spec_span[1,]))
+ spec = _SelectableInt(value=spec[1].value, bits=2)
if vector:
- extra <<= 1
- extra_span = (extra_span + ("{0}",))
+ spec <<= 1
+ spec_span = (spec_span + ("{0}",))
else:
- extra_span = (("{0}",) + extra_span)
+ spec_span = (("{0}",) + spec_span)
else:
raise ValueError(record.etype)
- bits = (len(span) + len(extra_span))
- value = _SelectableInt(value=value.value, bits=bits)
- extra = _SelectableInt(value=extra.value, bits=bits)
- if vector:
- value = ((value << 2) | extra)
- span = (span + extra_span)
- else:
- value = ((extra << 5) | value)
- span = (extra_span + span)
-
- value = _SelectableInt(value=value, bits=bits)
-
- else:
- value = insn[span]
-
- span = tuple(map(str, span))
+ (value, span) = merge(vector, value, span, spec, spec_span)
return (vector, value, span)
return _SVExtra.NONE
- def disassemble(self, insn, record, verbose=False, prefix="", indent=""):
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, prefix="", indent=""):
(vector, value, span) = self.spec(insn=insn, record=record)
- if verbose:
+ if verbosity >= Verbosity.VERBOSE:
yield f"{indent}{self.name}"
yield f"{indent}{indent}{int(value):0{value.bits}b}"
yield f"{indent}{indent}{', '.join(span)}"
yield f"{vector}{prefix}{int(value)}"
-@_dataclasses.dataclass(eq=True, frozen=True)
-class ImmediateOperand(DynamicOperand):
- pass
+class DynamicOperandGPRFPR(DynamicOperandReg):
+ def spec(self, insn, record):
+ def merge(vector, value, span, spec, spec_span):
+ bits = (len(span) + len(spec_span))
+ value = _SelectableInt(value=value.value, bits=bits)
+ spec = _SelectableInt(value=spec.value, bits=bits)
+ if vector:
+ value = ((value << 2) | spec)
+ span = (span + spec_span)
+ else:
+ value = ((spec << 5) | value)
+ span = (spec_span + span)
+ return (value, span)
-@_dataclasses.dataclass(eq=True, frozen=True)
-class StaticOperand(Operand):
- value: int
+ return super().spec(insn=insn, record=record, merge=merge)
- def disassemble(self, insn, record, verbose=False, indent=""):
- span = record.fields[self.name]
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
- value = insn[span]
- if 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 DynamicOperandGPR(DynamicOperandGPRFPR):
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
+ prefix = "" if (verbosity <= Verbosity.SHORT) else "r"
+ yield from super().disassemble(prefix=prefix,
+ insn=insn, record=record,
+ verbosity=verbosity, indent=indent)
+
+
+class DynamicOperandFPR(DynamicOperandGPRFPR):
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
+ prefix = "" if (verbosity <= Verbosity.SHORT) else "f"
+ yield from super().disassemble(prefix=prefix,
+ insn=insn, record=record,
+ verbosity=verbosity, indent=indent)
-@_dataclasses.dataclass(eq=True, frozen=True)
class DynamicOperandTargetAddr(DynamicOperandReg):
- def disassemble(self, insn, record, field, verbose=False, indent=""):
- span = record.fields[field]
+ def disassemble(self, insn, record, field,
+ verbosity=Verbosity.NORMAL, indent=""):
+ span = self.span(record=record)
if isinstance(insn, SVP64Instruction):
span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
- if verbose:
+ if verbosity >= Verbosity.VERBOSE:
span = tuple(map(str, span))
yield f"{indent}{self.name}"
yield f"{indent}{indent}{int(value):0{value.bits}b}00"
_SelectableInt(value=0b00, bits=2))))
-@_dataclasses.dataclass(eq=True, frozen=True)
class DynamicOperandTargetAddrLI(DynamicOperandTargetAddr):
- def disassemble(self, insn, record, verbose=False, indent=""):
+ def span(self, record):
+ return record.fields["LI"]
+
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
return super().disassemble(field="LI",
- insn=insn, record=record, verbose=verbose, indent=indent)
+ insn=insn, record=record,
+ verbosity=verbosity, indent=indent)
class DynamicOperandTargetAddrBD(DynamicOperandTargetAddr):
- def disassemble(self, insn, record, verbose=False, indent=""):
+ def span(self, record):
+ return record.fields["BD"]
+
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
return super().disassemble(field="BD",
- insn=insn, record=record, verbose=verbose, indent=indent)
+ insn=insn, record=record,
+ verbosity=verbosity, indent=indent)
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandGPR(DynamicOperandReg):
- def disassemble(self, insn, record, verbose=False, indent=""):
- yield from super().disassemble(prefix="r",
- insn=insn, record=record, verbose=verbose, indent=indent)
+class DynamicOperandDDX(DynamicOperand):
+ def span(self, record):
+ operands = map(DynamicOperand, ("d0", "d1", "d2"))
+ spans = map(lambda operand: operand.span(record=record), operands)
+ return sum(spans, tuple())
+ def disassemble(self, insn, record,
+ verbosity=Verbosity.NORMAL, indent=""):
+ span = self.span(record=record)
+ if isinstance(insn, SVP64Instruction):
+ span = tuple(map(lambda bit: (bit + 32), span))
+ value = insn[span]
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandFPR(DynamicOperandReg):
- def disassemble(self, insn, record, verbose=False, indent=""):
- yield from super().disassemble(prefix="f",
- insn=insn, record=record, verbose=verbose, indent=indent)
+ if verbosity >= Verbosity.VERBOSE:
+ yield f"{indent}D"
+ mapping = {
+ "d0": "[0:9]",
+ "d1": "[10:15]",
+ "d2": "[16]",
+ }
+ for (subname, subspan) in mapping.items():
+ operand = DynamicOperand(name=subname)
+ span = operand.span(record=record)
+ if isinstance(insn, SVP64Instruction):
+ span = tuple(map(lambda bit: (bit + 32), span))
+ value = insn[span]
+ span = map(str, span)
+ yield f"{indent}{indent}{operand.name} = D{subspan}"
+ yield f"{indent}{indent}{indent}{int(value):0{value.bits}b}"
+ yield f"{indent}{indent}{indent}{', '.join(span)}"
+ else:
+ yield str(int(value))
class Operands(tuple):
def __new__(cls, insn, iterable):
- branches = {
+ custom = {
"b": {"target_addr": DynamicOperandTargetAddrLI},
"ba": {"target_addr": DynamicOperandTargetAddrLI},
"bl": {"target_addr": DynamicOperandTargetAddrLI},
"bca": {"target_addr": DynamicOperandTargetAddrBD},
"bcl": {"target_addr": DynamicOperandTargetAddrBD},
"bcla": {"target_addr": DynamicOperandTargetAddrBD},
+ "addpcis": {"D": DynamicOperandDDX},
+ "fishmv": {"D": DynamicOperandDDX},
+ "fmvis": {"D": DynamicOperandDDX},
}
operands = []
if immediate is not None:
operands.append(ImmediateOperand(name=immediate))
- if insn in branches and operand in branches[insn]:
- dynamic_cls = branches[insn][operand]
+ if insn in custom and operand in custom[insn]:
+ dynamic_cls = custom[insn][operand]
if operand in _RegType.__members__:
regtype = _RegType[operand]
@cached_property
def opcode(self):
- fields = []
+ value = ([0] * 32)
+ mask = ([0] * 32)
+
if self.section.opcode:
- fields += [(self.section.opcode.value, BitSel((0, 5)))]
- fields += [(self.ppc.opcode.value, self.section.bitsel)]
- else:
- fields += [(self.ppc.opcode.value, self.section.bitsel)]
+ for (src, dst) in enumerate(reversed(BitSel((0, 5)))):
+ value[dst] = ((self.section.opcode.value & (1 << src)) != 0)
+ mask[dst] = ((self.section.opcode.mask & (1 << src)) != 0)
+
+ for (src, dst) in enumerate(reversed(self.section.bitsel)):
+ value[dst] = ((self.ppc.opcode.value & (1 << src)) != 0)
+ mask[dst] = ((self.ppc.opcode.mask & (1 << src)) != 0)
for operand in self.operands.static:
- fields += [(operand.value, self.fields[operand.name])]
+ for (src, dst) in enumerate(reversed(operand.span(record=self))):
+ value[dst] = ((operand.value & (1 << src)) != 0)
+ mask[dst] = True
+
+ for operand in self.operands.dynamic:
+ for dst in operand.span(record=self):
+ value[dst] = False
+ mask[dst] = False
+
+ def onebit(bit):
+ return _SelectableInt(value=int(bit), bits=1)
+
+ value = _selectconcat(*map(onebit, value))
+ mask = _selectconcat(*map(onebit, mask))
- return FieldsOpcode(fields)
+ value = int(value)
+ mask = int(mask)
+
+ return Opcode(value=value, mask=mask)
@property
def function(self):
return f"{prefix}{record.name}{operands}"
- def dynamic_operands(self, db):
+ def dynamic_operands(self, db, verbosity=Verbosity.NORMAL):
record = self.record(db=db)
imm = False
imm_value = ""
for operand in record.operands.dynamic:
name = operand.name
- value = " ".join(operand.disassemble(insn=self,
- record=record, verbose=False))
+ dis = operand.disassemble(insn=self, record=record,
+ verbosity=min(verbosity, Verbosity.NORMAL))
+ value = " ".join(dis)
if imm:
name = f"{imm_name}({name})"
value = f"{imm_value}({value})"
for operand in record.operands.static:
yield (operand.name, operand.value)
- def disassemble(self, db, byteorder="little", verbose=False):
+ def disassemble(self, db,
+ byteorder="little",
+ verbosity=Verbosity.NORMAL):
raise NotImplementedError
record = self.record(db=db)
return f"0x{record.opcode.mask:08x}"
- def disassemble(self, db, byteorder="little", verbose=False):
+ def disassemble(self, db,
+ byteorder="little",
+ verbosity=Verbosity.NORMAL):
integer = int(self)
- blob = integer.to_bytes(length=4, byteorder=byteorder)
- blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+ if verbosity <= Verbosity.SHORT:
+ blob = ""
+ else:
+ blob = integer.to_bytes(length=4, byteorder=byteorder)
+ blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+ blob += " "
- record = self.record(db=db)
+ record = db[self]
if record is None:
- yield f"{blob} .long 0x{integer:08x}"
+ yield f"{blob}.long 0x{integer:08x}"
return
operands = tuple(map(_operator.itemgetter(1),
- self.dynamic_operands(db=db)))
+ self.dynamic_operands(db=db, verbosity=verbosity)))
if operands:
- yield f"{blob} {record.name} {','.join(operands)}"
+ yield f"{blob}{record.name} {','.join(operands)}"
else:
- yield f"{blob} {record.name}"
+ yield f"{blob}{record.name}"
- if verbose:
+ if verbosity >= Verbosity.VERBOSE:
indent = (" " * 4)
binary = self.binary
spec = self.spec(db=db, prefix="")
yield f"{indent}mask"
yield f"{indent}{indent}{mask}"
for operand in record.operands:
- yield from operand.disassemble(insn=self,
- record=record, verbose=True, indent=indent)
+ yield from operand.disassemble(insn=self, record=record,
+ verbosity=verbosity, indent=indent)
yield ""
prrc0: prrc0
-class ExtraSpec(_Mapping):
+class Extra(_Mapping):
_: _Field = range(0, 9)
-class Extra2Spec(ExtraSpec):
+class Extra2(Extra):
idx0: _Field = range(0, 2)
idx1: _Field = range(2, 4)
idx2: _Field = range(4, 6)
self[key].assign(value)
-class Extra3Spec(ExtraSpec):
+class Extra3(Extra):
idx0: _Field = range(0, 3)
idx1: _Field = range(3, 6)
idx2: _Field = range(6, 9)
mode: Mode.remap(range(19, 24))
smask: _Field = range(16, 19)
- extra: ExtraSpec.remap(range(10, 19))
- extra2: Extra2Spec.remap(range(10, 19))
- extra3: Extra3Spec.remap(range(10, 19))
+ extra: Extra.remap(range(10, 19))
+ extra2: Extra2.remap(range(10, 19))
+ extra3: Extra3.remap(range(10, 19))
class SVP64Instruction(PrefixedInstruction):
raise ValueError(self)
- def disassemble(self, db, byteorder="little", verbose=False):
- integer_prefix = int(self.prefix)
- blob_prefix = integer_prefix.to_bytes(length=4, byteorder=byteorder)
- blob_prefix = " ".join(map(lambda byte: f"{byte:02x}", blob_prefix))
-
- integer_suffix = int(self.suffix)
- blob_suffix = integer_suffix.to_bytes(length=4, byteorder=byteorder)
- blob_suffix = " ".join(map(lambda byte: f"{byte:02x}", blob_suffix))
+ def disassemble(self, db,
+ byteorder="little",
+ verbosity=Verbosity.NORMAL):
+ def blob(integer):
+ if verbosity <= Verbosity.SHORT:
+ return ""
+ else:
+ blob = integer.to_bytes(length=4, byteorder=byteorder)
+ blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+ return f"{blob} "
- record = self.record(db=db)
+ blob_prefix = blob(int(self.prefix))
+ blob_suffix = blob(int(self.suffix))
+ record = db[self]
if record is None or record.svp64 is None:
- yield f"{blob_prefix} .long 0x{int(self.prefix):08x}"
- yield f"{blob_suffix} .long 0x{int(self.suffix):08x}"
+ yield f"{blob_prefix}.long 0x{int(self.prefix):08x}"
+ yield f"{blob_suffix}.long 0x{int(self.suffix):08x}"
return
operands = tuple(map(_operator.itemgetter(1),
- self.dynamic_operands(db=db)))
+ self.dynamic_operands(db=db, verbosity=verbosity)))
if operands:
- yield f"{blob_prefix} sv.{record.name} {','.join(operands)}"
+ yield f"{blob_prefix}sv.{record.name} {','.join(operands)}"
else:
- yield f"{blob_prefix} {record.name}"
- yield f"{blob_suffix}"
+ yield f"{blob_prefix}{record.name}"
+ if blob_suffix:
+ yield f"{blob_suffix}"
(mode, mode_desc) = self.mode(db=db)
- if verbose:
+ if verbosity >= Verbosity.VERBOSE:
indent = (" " * 4)
binary = self.binary
spec = self.spec(db=db, prefix="sv.")
yield f"{indent}mask"
yield f"{indent}{indent}{mask}"
for operand in record.operands:
- yield from operand.disassemble(insn=self,
- record=record, verbose=True, indent=indent)
+ yield from operand.disassemble(insn=self, record=record,
+ verbosity=verbosity, indent=indent)
yield f"{indent}mode"
yield f"{indent}{indent}{mode_desc}"
def __getitem__(self, key):
if isinstance(key, (int, Instruction)):
key = int(key)
+ matches = []
for record in self:
opcode = record.opcode
if ((opcode.value & opcode.mask) ==
(key & opcode.mask)):
- return record
+ matches.append(record)
+ if matches:
+ return matches[-1]
return None
elif isinstance(key, Opcode):
for record in self: