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
RCOE as _RCOE,
CryIn as _CryIn,
Form as _Form,
- SVEtype as _SVEtype,
- SVmask_src as _SVmask_src,
+ SVEType as _SVEType,
+ SVMaskSrc as _SVMaskSrc,
SVMode as _SVMode,
- SVPtype as _SVPtype,
+ SVPType as _SVPType,
SVExtra as _SVExtra,
RegType as _RegType,
+ SVP64RMMode as _SVP64RMMode,
SVExtraRegType as _SVExtraRegType,
SVExtraReg as _SVExtraReg,
+ SVP64SubVL as _SVP64SubVL,
+ SVP64Pred as _SVP64Pred,
+ SVP64PredMode as _SVP64PredMode,
+ SVP64Width as _SVP64Width,
)
from openpower.decoder.selectable_int import (
SelectableInt as _SelectableInt,
@_functools.total_ordering
-class Verbosity(_enum.Enum):
+class Style(_enum.Enum):
+ LEGACY = _enum.auto()
SHORT = _enum.auto()
NORMAL = _enum.auto()
VERBOSE = _enum.auto()
return "".join(pattern(self.value, self.mask, self.value.bit_length()))
def match(self, key):
- if isinstance(key, Instruction):
- key = int(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"):
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):
"sgl pipe",
)
- class Flags(frozenset, metaclass=FlagsMeta):
+ class Flags(tuple, metaclass=FlagsMeta):
def __new__(cls, flags=frozenset()):
flags = frozenset(flags)
diff = (flags - frozenset(cls))
if diff:
raise ValueError(flags)
- return super().__new__(cls, flags)
+ return super().__new__(cls, sorted(flags))
opcode: Opcode
comment: str
class PPCMultiRecord(tuple):
def __getattr__(self, attr):
if attr == "opcode":
- raise AttributeError(attr)
+ if len(self) != 1:
+ raise AttributeError(attr)
return getattr(self[0], attr)
return repr({index:self[index] for index in range(0, 4)})
name: str
- ptype: _SVPtype = _SVPtype.NONE
- etype: _SVEtype = _SVEtype.NONE
- msrc: _SVmask_src = _SVmask_src.NO # MASK_SRC is active
+ 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
extra_reg_cr_in2 = property(_functools.partial(extra_reg, key="cr_in2"))
extra_reg_cr_out = property(_functools.partial(extra_reg, key="cr_out"))
+ @cached_property
+ def extra_CR(self):
+ extra = None
+ for idx in range(0, 4):
+ for entry in self.extra[idx]:
+ if entry.regtype is _SVExtraRegType.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)):
class Operands:
+ __GPR_PAIRS = (
+ _SVExtraReg.RTp,
+ _SVExtraReg.RSp,
+ )
+ __FPR_PAIRS = (
+ _SVExtraReg.FRAp,
+ _SVExtraReg.FRBp,
+ _SVExtraReg.FRSp,
+ _SVExtraReg.FRTp,
+ )
+
def __init__(self, insn, iterable):
custom_insns = {
"b": {"target_addr": TargetAddrOperandLI},
"SIM": SignedOperand,
"SVD": SignedOperand,
"SVDS": SignedOperand,
+ "RSp": GPRPairOperand,
+ "RTp": GPRPairOperand,
+ "FRAp": FPRPairOperand,
+ "FRBp": FPRPairOperand,
+ "FRSp": FPRPairOperand,
+ "FRTp": FPRPairOperand,
}
custom_immediates = {
"DQ": EXTSOperandDQ,
if "=" in operand:
(name, value) = operand.split("=")
- mapping[name] = (StaticOperand, {"value": int(value)})
+ mapping[name] = (StaticOperand, {
+ "name": name,
+ "value": int(value),
+ })
else:
name = operand
if name.endswith(")"):
name = name.replace("(", " ").replace(")", "")
- (immediate, _, name) = name.partition(" ")
+ (imm_name, _, name) = name.partition(" ")
else:
- immediate = None
+ imm_name = None
- if immediate is not None:
- cls = custom_immediates.get(immediate, ImmediateOperand)
- mapping[name] = (cls, {})
+ 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]
- if name in _RegType.__members__:
- regtype = _RegType[name]
- if regtype is _RegType.GPR:
- cls = GPROperand
- elif regtype is _RegType.FPR:
- cls = FPROperand
- if regtype is _RegType.CR_BIT: # 5-bit
- cls = CR5Operand
- if regtype is _RegType.CR_REG: # actually CR Field, 3-bit
- cls = CR3Operand
-
- mapping[name] = (cls, {})
+ if name in _SVExtraReg.__members__:
+ reg = _SVExtraReg[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 = []
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__()
return self.__mapping.__contains__(key)
def __getitem__(self, key):
- (cls, kwargs) = self.__mapping.__getitem__(key)
- kwargs = dict(kwargs)
- kwargs["name"] = key
-
- return (cls, kwargs)
+ return self.__mapping.__getitem__(key)
@property
def static(self):
return self.__dynamic
+class Arguments(tuple):
+ def __new__(cls, arguments, operands):
+ arguments = iter(tuple(arguments))
+ operands = iter(tuple(operands))
+
+ 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 (lhs < rhs)
@cached_property
- def PO_XO(self):
- value = 0
- mask = 0
- for ppc in self.ppc:
- opcode = ppc.opcode
- value |= opcode.value
- mask |= opcode.mask
- value = Opcode.Value(value)
- mask = Opcode.Mask(mask)
- XO = Opcode(value=value, mask=mask)
-
- PO = self.section.opcode
- if PO is None:
- PO = XO
- XO = None
-
- PO = POStaticOperand(record=self,
- name="PO", value=int(PO.value), mask=int(PO.mask))
- if XO is None:
- XO = XOStaticOperand(record=self,
- name="XO", value=0, mask=0)
- else:
- XO = XOStaticOperand(record=self,
- name="XO", value=int(XO.value), mask=int(XO.mask))
-
- return (PO, XO)
-
- @cached_property
- def PO(self):
- return self.PO_XO[0]
+ def operands(self):
+ return (self.static_operands + self.dynamic_operands)
@cached_property
- def XO(self):
- return self.PO_XO[1]
-
- @property
def static_operands(self):
- yield from self.PO_XO
-
+ 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:
- yield cls(record=self, **kwargs)
+ 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)
- @property
+ @cached_property
def opcodes(self):
- def opcode(ppc):
- value = ([0] * 32)
- mask = ([0] * 32)
-
- for operand in self.static_operands:
+ 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] = int((operand.value & (1 << src)) != 0)
- mask[dst] = 1
+ 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))
- value = Opcode.Value(int(("".join(map(str, value))), 2))
- mask = Opcode.Mask(int(("".join(map(str, mask))), 2))
+ 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 Opcode(value=value, mask=mask)
+ return int(opcode.value)
- return tuple(sorted(map(opcode, self.ppc)))
+ @cached_property
+ def XO(self):
+ return tuple(ppc.opcode for ppc in self.ppc)
def match(self, key):
for opcode in self.opcodes:
def Rc(self):
if "Rc" not in self:
return False
- return bool(self["Rc"])
+ return self["Rc"].value
-@_dataclasses.dataclass(eq=True, frozen=True)
class Operand:
- name: str
- record: Record = _dataclasses.field(repr=False)
+ def __init__(self, record, name):
+ self.__record = record
+ self.__name = name
- def __post_init__(self):
- pass
+ def __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, value, insn):
+ 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(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
if isinstance(value, str):
value = int(value, 0)
if value < 0:
insn[span] = value
def disassemble(self, insn,
- verbosity=Verbosity.NORMAL, indent=""):
- raise NotImplementedError
-
-
-@_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperand(Operand):
- def disassemble(self, insn,
- verbosity=Verbosity.NORMAL, indent=""):
+ style=Style.NORMAL, indent=""):
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
span = map(str, span)
yield f"{indent}{self.name}"
yield f"{indent}{indent}{int(value):0{value.bits}b}"
yield str(int(value))
-@_dataclasses.dataclass(eq=True, frozen=True)
class SignedOperand(DynamicOperand):
- def assemble(self, value, insn):
+ 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,
- verbosity=Verbosity.NORMAL, indent=""):
+ style=Style.NORMAL, indent=""):
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
- value = insn[span]
+ value = insn[span].to_signed_int()
+ sign = "-" if (value < 0) else ""
+ value = abs(value)
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
span = map(str, span)
yield f"{indent}{self.name}"
- yield f"{indent}{indent}{int(value):0{value.bits}b}"
+ yield f"{indent}{indent}{sign}{value}"
yield f"{indent}{indent}{', '.join(span)}"
else:
- yield str(value.to_signed_int())
+ yield f"{sign}{value}"
-@_dataclasses.dataclass(eq=True, frozen=True)
class StaticOperand(Operand):
- value: int
+ def __init__(self, record, name, value):
+ self.__value = value
+ return super().__init__(record=record, name=name)
+
+ def __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):
- return super().assemble(value=self.value, insn=insn)
+ insn[self.span] = self.value
def disassemble(self, insn,
- verbosity=Verbosity.NORMAL, indent=""):
+ style=Style.NORMAL, indent=""):
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
span = map(str, span)
yield f"{indent}{self.name}"
yield f"{indent}{indent}{int(value):0{value.bits}b}"
yield str(int(value))
-@_dataclasses.dataclass(eq=True, frozen=True)
-class POStaticOperand(StaticOperand):
- mask: int
+class SpanStaticOperand(StaticOperand):
+ def __init__(self, record, name, value, span):
+ self.__span = tuple(span)
+ return super().__init__(record=record, name=name, value=value)
- @cached_property
+ def __iter__(self):
+ yield ("span", self.__span)
+ yield from super().__iter__()
+
+ @property
def span(self):
- return tuple(range(0, 6))
+ return self.__span
-@_dataclasses.dataclass(eq=True, frozen=True)
-class XOStaticOperand(StaticOperand):
- mask: int
-
- def __post_init__(self):
- if self.record.section.opcode is None:
- assert self.value == 0
- assert self.mask == 0
- object.__setattr__(self, "span", ())
- return
+class POStaticOperand(SpanStaticOperand):
+ def __init__(self, record, value):
+ return super().__init__(record=record, name="PO", value=value, span=range(0, 6))
+
+ def __iter__(self):
+ for (key, value) in super().__iter__():
+ if key not in {"name", "span"}:
+ yield (key, value)
- bits = self.record.section.bitsel
- value = _SelectableInt(value=self.value, bits=len(bits))
+
+class XOStaticOperand(SpanStaticOperand):
+ def __init__(self, record, value, span):
+ bits = record.section.bitsel
+ value = _SelectableInt(value=value, bits=len(bits))
span = dict(zip(bits, range(len(bits))))
span_rev = {value:key for (key, value) in span.items()}
- # This part is tricky: we could have used self.record.static_operands,
- # but this would cause an infinite recursion, since this code is called
- # from the self.record.static_operands method already.
- operands = []
- operands.extend(self.record.mdwn.operands.static)
- operands.extend(self.record.mdwn.operands.dynamic)
- for (cls, kwargs) in operands:
- operand = cls(record=self.record, **kwargs)
+ # 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)
- # This part is simpler: we drop bits which are not in the mask.
- for bit in tuple(span.values()):
- rev = (len(bits) - bit - 1)
- if ((self.mask & (1 << bit)) == 0):
- idx = span_rev.pop(rev, None)
- if idx is not None:
- span.pop(idx, None)
-
value = int(_selectconcat(*(value[bit] for bit in span.values())))
span = tuple(span.keys())
- object.__setattr__(self, "value", value)
- object.__setattr__(self, "span", span)
- return super().__post_init__()
+ return super().__init__(record=record, name="XO", value=value, span=span)
+
+ def __iter__(self):
+ for (key, value) in super().__iter__():
+ if key not in {"name"}:
+ yield (key, value)
-@_dataclasses.dataclass(eq=True, frozen=True)
class ImmediateOperand(DynamicOperand):
pass
-@_dataclasses.dataclass(eq=True, frozen=True)
class SignedImmediateOperand(SignedOperand, ImmediateOperand):
pass
-@_dataclasses.dataclass(eq=True, frozen=True)
class NonZeroOperand(DynamicOperand):
- def assemble(self, value, insn):
+ def assemble(self, insn, value):
if isinstance(value, str):
value = int(value, 0)
if not isinstance(value, int):
return super().assemble(value=value, insn=insn)
def disassemble(self, insn,
- verbosity=Verbosity.NORMAL, indent=""):
+ style=Style.NORMAL, indent=""):
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
span = map(str, span)
yield f"{indent}{self.name}"
yield f"{indent}{indent}{int(value):0{value.bits}b}"
yield str(int(value) + 1)
-@_dataclasses.dataclass(eq=True, frozen=True)
class ExtendableOperand(DynamicOperand):
def sv_spec_enter(self, value, span):
return (value, span)
def spec(self, insn):
vector = False
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
value = insn[span]
span = tuple(map(str, span))
if extra_idx is _SVExtra.NONE:
return (vector, value, span)
- if self.record.etype is _SVEtype.EXTRA3:
+ if self.record.etype is _SVEType.EXTRA3:
spec = insn.prefix.rm.extra3[extra_idx]
- elif self.record.etype is _SVEtype.EXTRA2:
+ 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:
+ 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:
+ 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:
@property
def extra_idx(self):
+ pairs = {
+ _SVExtraReg.RSp: _SVExtraReg.RS,
+ _SVExtraReg.RTp: _SVExtraReg.RT,
+ _SVExtraReg.FRAp: _SVExtraReg.FRA,
+ _SVExtraReg.FRBp: _SVExtraReg.FRB,
+ _SVExtraReg.FRSp: _SVExtraReg.FRS,
+ _SVExtraReg.FRTp: _SVExtraReg.FRT,
+ }
+
for key in frozenset({
"in1", "in2", "in3", "cr_in", "cr_in2",
"out", "out2", "cr_out",
}):
extra_reg = self.record.svp64.extra_reg(key=key)
- if extra_reg is self.extra_reg:
+ if pairs.get(extra_reg, extra_reg) is pairs.get(self.extra_reg, self.extra_reg):
return self.record.extra_idx(key=key)
return _SVExtra.NONE
+ 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):
+ value = value[len(prefix):]
+ value = int(value, 0)
+
+ if isinstance(insn, SVP64Instruction):
+ (value, extra) = self.remap(value=value, vector=vector)
+
+ extra_idx = self.extra_idx
+ if extra_idx is _SVExtra.NONE:
+ raise ValueError(self.record)
+
+ 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,
- verbosity=Verbosity.NORMAL, prefix="", indent=""):
+ style=Style.NORMAL, prefix="", indent=""):
(vector, value, span) = self.spec(insn=insn)
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
mode = "vector" if vector else "scalar"
yield f"{indent}{self.name} ({mode})"
yield f"{indent}{indent}{int(value):0{value.bits}b}"
yield f"{indent}{indent}{', '.join(span)}"
if isinstance(insn, SVP64Instruction):
extra_idx = self.extra_idx
- if self.record.etype is _SVEtype.NONE:
+ if self.record.etype is _SVEType.NONE:
yield f"{indent}{indent}extra[none]"
else:
etype = repr(self.record.etype).lower()
yield f"{vector}{prefix}{int(value)}"
-@_dataclasses.dataclass(eq=True, frozen=True)
-class GPROperand(ExtendableOperand):
- def assemble(self, value, insn):
- if isinstance(value, str):
- value = value.lower()
- if value.startswith("r"):
- value = value[1:]
- value = int(value, 0)
- return super().assemble(value=value, insn=insn)
+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,
- verbosity=Verbosity.NORMAL, indent=""):
- prefix = "" if (verbosity <= Verbosity.SHORT) else "r"
+ style=Style.NORMAL, indent=""):
+ prefix = "" if (style <= Style.SHORT) else "r"
yield from super().disassemble(prefix=prefix, insn=insn,
- verbosity=verbosity, indent=indent)
+ style=style, indent=indent)
-@_dataclasses.dataclass(eq=True, frozen=True)
-class FPROperand(ExtendableOperand):
- def assemble(self, value, insn):
- if isinstance(value, str):
- value = value.lower()
- if value.startswith("f"):
- value = value[1:]
- value = int(value, 0)
- return super().assemble(value=value, insn=insn)
+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,
- verbosity=Verbosity.NORMAL, indent=""):
- prefix = "" if (verbosity <= Verbosity.SHORT) else "f"
+ style=Style.NORMAL, indent=""):
+ prefix = "" if (style <= Style.SHORT) else "f"
yield from super().disassemble(prefix=prefix, insn=insn,
- verbosity=verbosity, indent=indent)
+ style=style, indent=indent)
-@_dataclasses.dataclass(eq=True, frozen=True)
-class CR3Operand(ExtendableOperand):
+class FPRPairOperand(FPROperand):
pass
-@_dataclasses.dataclass(eq=True, frozen=True)
-class CR5Operand(ExtendableOperand):
+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.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):
+ extra_idx = 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)
return (value, span)
-@_dataclasses.dataclass(eq=True, frozen=True)
-class EXTSOperand(DynamicOperand):
+class EXTSOperand(SignedOperand):
field: str # real name to report
nz: int = 0 # number of zeros
fmt: str = "d" # integer formatter
- def __post_init__(self):
- if not self.field:
- object.__setattr__(self, "field", self.name)
+ def __init__(self, record, name, field, nz=0, fmt="d"):
+ self.__field = field
+ self.__nz = nz
+ self.__fmt = fmt
+ return super().__init__(record=record, name=name)
+
+ @property
+ def field(self):
+ return self.__field
+
+ @property
+ def nz(self):
+ return self.__nz
+
+ @property
+ def fmt(self):
+ return self.__fmt
@cached_property
def span(self):
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,
- verbosity=Verbosity.NORMAL, indent=""):
+ style=Style.NORMAL, indent=""):
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
- value = insn[span]
+ value = insn[span].to_signed_int()
+ sign = "-" if (value < 0) else ""
+ value = (abs(value) << self.nz)
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
span = (tuple(map(str, span)) + (("{0}",) * self.nz))
zeros = ("0" * self.nz)
hint = f"{self.name} = EXTS({self.field} || {zeros})"
yield f"{indent * 1}{hint}"
yield f"{indent * 2}{self.field}"
- yield f"{indent * 3}{int(value):0{value.bits}b}{zeros}"
+ yield f"{indent * 3}{sign}{value:{self.fmt}}"
yield f"{indent * 3}{', '.join(span)}"
else:
- value = _selectconcat(value,
- _SelectableInt(value=0, bits=self.nz)).to_signed_int()
- yield f"{value:{self.fmt}}"
+ yield f"{sign}{value:{self.fmt}}"
-@_dataclasses.dataclass(eq=True, frozen=True)
class TargetAddrOperand(EXTSOperand):
- nz: int = 2
- fmt: str = "#x"
+ def __init__(self, record, name, field):
+ return super().__init__(record=record, name=name, field=field, nz=2, fmt="#x")
-@_dataclasses.dataclass(eq=True, frozen=True)
class TargetAddrOperandLI(TargetAddrOperand):
- field: str = "LI"
+ def __init__(self, record, name):
+ return super().__init__(record=record, name=name, field="LI")
-@_dataclasses.dataclass(eq=True, frozen=True)
class TargetAddrOperandBD(TargetAddrOperand):
- field: str = "BD"
+ def __init__(self, record, name):
+ return super().__init__(record=record, name=name, field="BD")
-@_dataclasses.dataclass(eq=True, frozen=True)
class EXTSOperandDS(EXTSOperand, ImmediateOperand):
- field: str = "DS"
- nz: int = 2
+ def __init__(self, record, name):
+ return super().__init__(record=record, name=name, field="DS", nz=2)
-@_dataclasses.dataclass(eq=True, frozen=True)
class EXTSOperandDQ(EXTSOperand, ImmediateOperand):
- field: str = "DQ"
- nz: int = 4
+ def __init__(self, record, name):
+ return super().__init__(record=record, name=name, field="DQ", nz=4)
-@_dataclasses.dataclass(eq=True, frozen=True)
class DOperandDX(SignedOperand):
@cached_property
def span(self):
return sum(spans, tuple())
def disassemble(self, insn,
- verbosity=Verbosity.NORMAL, indent=""):
+ style=Style.NORMAL, indent=""):
span = self.span
- if isinstance(insn, SVP64Instruction):
- span = tuple(map(lambda bit: (bit + 32), span))
- value = insn[span]
+ value = insn[span].to_signed_int()
+ sign = "-" if (value < 0) else ""
+ value = abs(value)
- if verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
yield f"{indent}D"
mapping = {
"d0": "[0:9]",
for (subname, subspan) in mapping.items():
operand = DynamicOperand(name=subname)
span = operand.span
- 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}{sign}{value}"
yield f"{indent}{indent}{indent}{', '.join(span)}"
else:
- yield str(value.to_signed_int())
+ yield f"{sign}{value}"
class Instruction(_Mapping):
return self.storage.__setitem__(key, value)
def bytes(self, byteorder="little"):
- nr_bytes = (self.storage.bits // 8)
+ nr_bytes = (len(self.__class__) // 8)
return int(self).to_bytes(nr_bytes, byteorder=byteorder)
- def record(self, db):
- record = db[self]
+ @classmethod
+ def record(cls, db, entry):
+ record = db[entry]
if record is None:
- raise KeyError(self)
+ raise KeyError(entry)
return record
- def spec(self, db, prefix):
- record = self.record(db=db)
+ @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.dynamic_operands(db=db)))
+ self.spec_dynamic_operands(record=record)))
static_operands = []
- for (name, value) in self.static_operands(db=db):
+ for (name, value) in self.spec_static_operands(record=record):
static_operands.append(f"{name}={value}")
operands = ""
return f"{prefix}{record.name}{operands}"
- def dynamic_operands(self, db, verbosity=Verbosity.NORMAL):
- record = self.record(db=db)
+ 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 record.dynamic_operands:
+ for operand in self.dynamic_operands(record=record):
name = operand.name
value = " ".join(operand.disassemble(insn=self,
- verbosity=min(verbosity, Verbosity.NORMAL)))
+ style=min(style, Style.NORMAL)))
if imm:
name = f"{imm_name}({name})"
value = f"{imm_value}({value})"
if not imm:
yield (name, value)
- def static_operands(self, db):
- record = self.record(db=db)
- for operand in record.static_operands:
- yield (operand.name, operand.value)
-
@classmethod
- def assemble(cls, db, opcode, arguments):
- raise NotImplementedError(f"{cls.__name__}.assemble")
+ def assemble(cls, record, arguments=None):
+ if arguments is None:
+ arguments = ()
+
+ insn = cls.integer(value=0)
- def disassemble(self, db,
+ for operand in cls.static_operands(record=record):
+ operand.assemble(insn=insn)
+
+ dynamic_operands = tuple(cls.dynamic_operands(record=record))
+ for (value, operand) in Arguments(arguments, dynamic_operands):
+ operand.assemble(insn=insn, value=value)
+
+ return insn
+
+ def disassemble(self, record,
byteorder="little",
- verbosity=Verbosity.NORMAL):
- raise NotImplementedError
+ style=Style.NORMAL):
+ raise NotImplementedError()
class WordInstruction(Instruction):
_: _Field = range(0, 32)
PO: _Field = range(0, 6)
- @classmethod
- def assemble(cls, db, opcode, arguments):
- record = db[opcode]
- insn = cls.integer(value=0)
- for operand in record.static_operands:
- operand.assemble(insn=insn)
-
- dynamic_operands = tuple(record.dynamic_operands)
- if len(dynamic_operands) != len(arguments):
- raise ValueError("operands count mismatch")
- for (value, operand) in zip(arguments, dynamic_operands):
- operand.assemble(value=value, insn=insn)
-
- return insn
-
@classmethod
def integer(cls, value, byteorder="little"):
return super().integer(bits=32, value=value, byteorder=byteorder)
bits.append(bit)
return "".join(map(str, bits))
- def disassemble(self, db,
+ def disassemble(self, record,
byteorder="little",
- verbosity=Verbosity.NORMAL):
- integer = int(self)
- if verbosity <= Verbosity.SHORT:
+ style=Style.NORMAL):
+ if style <= Style.SHORT:
blob = ""
else:
- blob = integer.to_bytes(length=4, byteorder=byteorder)
+ blob = self.bytes(byteorder=byteorder)
blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
blob += " "
- record = db[self]
if record is None:
- yield f"{blob}.long 0x{integer:08x}"
+ yield f"{blob}.long 0x{int(self):08x}"
return
- operands = tuple(map(_operator.itemgetter(1),
- self.dynamic_operands(db=db, verbosity=verbosity)))
- if operands:
- operands = ",".join(operands)
- yield f"{blob}{record.name} {operands}"
+ 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:
- yield f"{blob}{record.name}"
+ 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 verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
indent = (" " * 4)
binary = self.binary
- spec = self.spec(db=db, prefix="")
+ spec = self.spec(record=record, prefix="")
yield f"{indent}spec"
yield f"{indent}{indent}{spec}"
yield f"{indent}pcode"
yield f"{indent}opcodes"
for opcode in record.opcodes:
yield f"{indent}{indent}{opcode!r}"
- for (cls, kwargs) in record.mdwn.operands:
- operand = cls(record=record, **kwargs)
+ for operand in self.operands(record=record):
yield from operand.disassemble(insn=self,
- verbosity=verbosity, indent=indent)
+ style=style, indent=indent)
yield ""
class Mode(_Mapping):
_: _Field = range(0, 5)
+ sel: _Field = (0, 1)
class Extra(_Mapping):
3: "vec4",
}[subvl]
- def disassemble(self, verbosity=Verbosity.NORMAL):
- if verbosity >= Verbosity.VERBOSE:
+ def disassemble(self, style=Style.NORMAL):
+ if style >= Style.VERBOSE:
indent = (" " * 4)
for (name, span) in self.traverse(path="RM"):
value = self.storage[span]
yield from super().specifiers(record=record)
-class DZBaseRM(BaseRM):
+class ZZCombinedBaseRM(BaseRM):
def specifiers(self, record):
- if self.dz:
+ 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 SZBaseRM(BaseRM):
+class DZBaseRM(BaseRM):
def specifiers(self, record):
- if self.sz:
+ 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)
CR = (int(self.mmode) == 1)
mask = int(self.mask)
sm = dm = PredicateBaseRM.predicate(CR, mask)
- if record.svp64.ptype is _SVPtype.P2:
+ if record.svp64.ptype is _SVPType.P2:
smask = int(self.smask)
sm = PredicateBaseRM.predicate(CR, smask)
if sm == dm and dm:
pass
-class NormalSimpleRM(DZBaseRM, SZBaseRM, NormalBaseRM):
+class NormalSimpleRM(ZZCombinedBaseRM, NormalBaseRM):
"""normal: simple mode"""
dz: BaseRM.mode[3]
sz: BaseRM.mode[4]
yield from super().specifiers(record=record, mode="ff")
-class NormalSatRM(SatBaseRM, DZBaseRM, SZBaseRM, NormalBaseRM):
+class NormalSatRM(SatBaseRM, ZZCombinedBaseRM, NormalBaseRM):
"""normal: sat mode: N=0/1 u/s, SUBVL=1"""
N: BaseRM.mode[2]
dz: BaseRM.mode[3]
pass
-class LDSTIdxSimpleRM(SEABaseRM, DZBaseRM, SZBaseRM, LDSTIdxBaseRM):
+class LDSTIdxSimpleRM(SEABaseRM, ZZCombinedBaseRM, LDSTIdxBaseRM):
"""ld/st index: simple mode"""
SEA: BaseRM.mode[2]
dz: BaseRM.mode[3]
sz: BaseRM.mode[4]
-class LDSTIdxStrideRM(SEABaseRM, DZBaseRM, SZBaseRM, LDSTIdxBaseRM):
+class LDSTIdxStrideRM(SEABaseRM, ZZCombinedBaseRM, LDSTIdxBaseRM):
"""ld/st index: strided (scalar only source)"""
SEA: BaseRM.mode[2]
dz: BaseRM.mode[3]
yield from super().specifiers(record=record)
-class LDSTIdxSatRM(SatBaseRM, DZBaseRM, SZBaseRM, LDSTIdxBaseRM):
+class LDSTIdxSatRM(SatBaseRM, ZZCombinedBaseRM, LDSTIdxBaseRM):
"""ld/st index: sat mode: N=0/1 u/s"""
N: BaseRM.mode[2]
dz: BaseRM.mode[3]
SNZ: BaseRM[7]
-class CROpSimpleRM(PredicateBaseRM, DZBaseRM, SZBaseRM, CROpBaseRM):
- """cr_op: simple mode"""
+class CROpSimpleRM(PredicateBaseRM, ZZCombinedBaseRM, CROpBaseRM):
+ """crop: simple mode"""
RG: BaseRM[20]
dz: BaseRM[22]
sz: BaseRM[23]
yield from super().specifiers(record=record)
-class CROpMRRM(MRBaseRM, DZBaseRM, SZBaseRM, CROpBaseRM):
- """cr_op: scalar reduce mode (mapreduce), SUBVL=1"""
+
+class CROpMRRM(MRBaseRM, ZZCombinedBaseRM, CROpBaseRM):
+ """crop: scalar reduce mode (mapreduce), SUBVL=1"""
RG: BaseRM[20]
dz: BaseRM[22]
sz: BaseRM[23]
-class CROpFF3RM(FFPRRc1BaseRM, VLiBaseRM, ZZBaseRM, PredicateBaseRM, CROpBaseRM):
- """cr_op: ffirst 3-bit mode"""
+class CROpFF3RM(FFPRRc0BaseRM, PredicateBaseRM, VLiBaseRM, DZBaseRM, SZBaseRM, CROpBaseRM):
+ """crop: ffirst 3-bit mode"""
+ RC1: BaseRM[19]
VLi: BaseRM[20]
inv: BaseRM[21]
- CR: BaseRM[22, 23]
- zz: BaseRM[6]
- sz: BaseRM[6]
- dz: BaseRM[6]
+ dz: BaseRM[22]
+ sz: BaseRM[23]
def specifiers(self, record):
yield from super().specifiers(record=record, mode="ff")
-class CROpFF5RM(FFPRRc0BaseRM, PredicateBaseRM,
- VLiBaseRM, DZBaseRM, SZBaseRM, CROpBaseRM):
+# FIXME: almost everything in this class contradicts the specs.
+# However, this is the direct translation of the pysvp64asm code.
+# Please revisit this code; there is an inactive sketch below.
+class CROpFF5RM(FFPRRc1BaseRM, PredicateBaseRM, VLiBaseRM, CROpBaseRM):
"""cr_op: ffirst 5-bit mode"""
VLi: BaseRM[20]
inv: BaseRM[21]
- RC1: BaseRM[19] # cheat: set RC=1 based on ffirst mode being set
+ CR: BaseRM[22, 23]
dz: BaseRM[22]
sz: BaseRM[23]
normal: NormalRM
ldst_imm: LDSTImmRM
ldst_idx: LDSTIdxRM
- cr_op: CROpRM
+ crop: CROpRM
branch: BranchRM
- def select(self, record):
- rm = self
- Rc = record.Rc
- # the idea behind these tables is that they are now literally
+@_dataclasses.dataclass(eq=True, frozen=True)
+class Specifier:
+ record: Record
+
+ @classmethod
+ def match(cls, desc, record):
+ raise NotImplementedError()
+
+ def validate(self, others):
+ pass
+
+ def assemble(self, insn):
+ raise NotImplementedError()
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierWidth(Specifier):
+ width: _SVP64Width
+
+ @classmethod
+ def match(cls, desc, record, etalon):
+ (mode, _, value) = desc.partition("=")
+ mode = mode.strip()
+ value = value.strip()
+ if mode != etalon:
+ return None
+ width = _SVP64Width(value)
+
+ return cls(record=record, width=width)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierW(SpecifierWidth):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="w")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.ewsrc = self.width.value
+ selector.elwidth = self.width.value
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSW(SpecifierWidth):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="sw")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.ewsrc = self.width.value
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierDW(SpecifierWidth):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="dw")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.elwidth = self.width.value
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSubVL(Specifier):
+ value: _SVP64SubVL
+
+ @classmethod
+ def match(cls, desc, record):
+ try:
+ value = _SVP64SubVL(desc)
+ except ValueError:
+ return None
+
+ return cls(record=record, value=value)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.subvl = int(self.value.value)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierPredicate(Specifier):
+ mode: str
+ pred: _SVP64Pred
+
+ @classmethod
+ def match(cls, desc, record, mode_match, pred_match):
+ (mode, _, pred) = desc.partition("=")
+
+ mode = mode.strip()
+ if not mode_match(mode):
+ return None
+
+ pred = _SVP64Pred(pred.strip())
+ if not pred_match(pred):
+ raise ValueError(pred)
+
+ return cls(record=record, mode=mode, pred=pred)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierFFPR(SpecifierPredicate):
+ @classmethod
+ def match(cls, desc, record, mode):
+ return super().match(desc=desc, record=record,
+ mode_match=lambda mode_arg: mode_arg == mode,
+ pred_match=lambda pred_arg: pred_arg.mode in (
+ _SVP64PredMode.CR,
+ _SVP64PredMode.RC1,
+ ))
+
+ def validate(self, others):
+ if self.record.svp64.mode is _SVMode.CROP:
+ if self.mode == "pr":
+ raise ValueError("crop: 'pr' mode not supported")
+ if (self.record.svp64.extra_CR_3bit and
+ (self.pred.mode is not _SVP64PredMode.RC1)):
+ raise ValueError("3-bit CRs only support RC1/~RC1 BO")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ if selector.mode.sel != 0:
+ raise ValueError("cannot override mode")
+ if self.record.svp64.mode is _SVMode.CROP:
+ selector.mode.sel = 0b10
+ selector.inv = self.pred.inv
+ if not self.record.svp64.extra_CR_3bit:
+ selector.CR = self.pred.state
+ else:
+ selector.mode.sel = 0b01 if self.mode == "ff" else 0b11
+ selector.inv = self.pred.inv
+ if self.record.Rc:
+ selector.CR = self.pred.state
+ else:
+ selector.RC1 = self.pred.state
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierFF(SpecifierFFPR):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, mode="ff")
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierPR(SpecifierFFPR):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, mode="pr")
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierMask(SpecifierPredicate):
+ @classmethod
+ def match(cls, desc, record, mode):
+ return super().match(desc=desc, record=record,
+ mode_match=lambda mode_arg: mode_arg == mode,
+ pred_match=lambda pred_arg: pred_arg.mode in (
+ _SVP64PredMode.INT,
+ _SVP64PredMode.CR,
+ ))
+
+ def assemble(self, insn):
+ raise NotImplementedError()
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierM(SpecifierMask):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, mode="m")
+
+ def validate(self, others):
+ for spec in others:
+ if isinstance(spec, SpecifierSM):
+ raise ValueError("source-mask and predicate mask conflict")
+ elif isinstance(spec, SpecifierDM):
+ raise ValueError("dest-mask and predicate mask conflict")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.mask = int(self.pred)
+ if ((self.record.ptype is _SVPType.P2) and
+ (self.record.svp64.mode is not _SVMode.BRANCH)):
+ selector.smask = int(self.pred)
+ selector.mmode = (self.pred.mode is _SVP64PredMode.CR)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSM(SpecifierMask):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, mode="sm")
+
+ def validate(self, others):
+ if self.record.svp64.ptype is _SVPType.P1:
+ raise ValueError("source-mask on non-twin predicate")
+
+ if self.pred.mode is _SVP64PredMode.CR:
+ twin = None
+ for spec in others:
+ if isinstance(spec, SpecifierDM):
+ twin = spec
+
+ if twin is None:
+ raise ValueError("missing dest-mask in CR twin predication")
+ if self.pred.mode != twin.pred.mode:
+ raise ValueError(f"predicate masks mismatch: {self.pred!r} vs {twin.pred!r}")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.smask = int(self.pred)
+ selector.mmode = (self.pred.mode is _SVP64PredMode.CR)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierDM(SpecifierMask):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, mode="dm")
+
+ def validate(self, others):
+ if self.record.svp64.ptype is _SVPType.P1:
+ raise ValueError("dest-mask on non-twin predicate")
+
+ if self.pred.mode is _SVP64PredMode.CR:
+ twin = None
+ for spec in others:
+ if isinstance(spec, SpecifierSM):
+ twin = spec
+
+ if twin is None:
+ raise ValueError("missing source-mask in CR twin predication")
+ if self.pred.mode != twin.pred.mode:
+ raise ValueError(f"predicate masks mismatch: {self.pred!r} vs {twin.pred!r}")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.mask = int(self.pred)
+ selector.mmode = (self.pred.mode is _SVP64PredMode.CR)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierZZ(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "zz":
+ return None
+
+ return cls(record=record)
+
+ def validate(self, others):
+ for spec in others:
+ # Since zz takes precedence (overrides) sz and dz,
+ # treat them as mutually exclusive.
+ if isinstance(spec, (SpecifierSZ, SpecifierDZ)):
+ raise ValueError("mutually exclusive predicate masks")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ if hasattr(selector, "zz"): # this should be done in a different way
+ selector.zz = 1
+ else:
+ selector.sz = 1
+ selector.dz = 1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierXZ(Specifier):
+ desc: str
+ hint: str = _dataclasses.field(repr=False)
+
+ @classmethod
+ def match(cls, desc, record, etalon, hint):
+ if desc != etalon:
+ return None
+
+ return cls(desc=desc, record=record, hint=hint)
+
+ def validate(self, others):
+ if self.record.svp64.ptype is _SVPType.P1:
+ raise ValueError(f"{self.hint} on non-twin predicate")
+
+ if self.pred.mode is _SVP64PredMode.CR:
+ twin = None
+ for spec in others:
+ if isinstance(spec, SpecifierXZ):
+ twin = spec
+
+ if twin is None:
+ raise ValueError(f"missing {self.hint} in CR twin predication")
+ if self.pred != twin.pred:
+ raise ValueError(f"predicate masks mismatch: {self.pred!r} vs {twin.pred!r}")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ setattr(selector, self.desc, 1)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSZ(SpecifierXZ):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="sz", hint="source-mask")
+
+ def validate(self, others):
+ for spec in others:
+ if self.record.svp64.mode is not _SVMode.CROP:
+ if isinstance(spec, SpecifierFF):
+ raise ValueError("source-zero not allowed in ff mode")
+ elif isinstance(spec, SpecifierPR):
+ raise ValueError("source-zero not allowed in pr mode")
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierDZ(SpecifierXZ):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="dz", hint="dest-mask")
+
+ def validate(self, others):
+ for spec in others:
+ if ((self.record.svp64.mode is not _SVMode.CROP) and
+ isinstance(spec, (SpecifierFF, SpecifierPR)) and
+ (spec.pred.mode is _SVP64PredMode.RC1)):
+ mode = "ff" if isinstance(spec, SpecifierFF) else "pr"
+ raise ValueError(f"dest-zero not allowed in {mode} mode BO")
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierEls(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "els":
+ return None
+
+ if record.svp64.mode not in (_SVMode.LDST_IMM, _SVMode.LDST_IDX):
+ raise ValueError("els is only valid in ld/st modes")
+
+ return cls(record=record)
+
+ def assemble(self, insn):
+ if self.record.svp64.mode is _SVMode.LDST_IDX: # stride mode
+ insn.prefix.rm.mode[0] = 0
+ insn.prefix.rm.mode[1] = 1
+
+ selector = insn.select(record=self.record)
+ if self.record.svp64.mode is not _SVMode.LDST_IDX: # stride mode
+ selector.els = 1
+
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSEA(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "sea":
+ return None
+
+ return cls(record=record)
+
+ def validate(self, others):
+ if self.record.svp64.mode is not _SVMode.LDST_IDX:
+ raise ValueError("sea is only valid in ld/st modes")
+
+ for spec in others:
+ if isinstance(spec, SpecifierFF):
+ raise ValueError(f"sea cannot be used in ff mode")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ if selector.mode.sel not in (0b00, 0b01):
+ raise ValueError("sea is only valid for normal and els modes")
+ selector.SEA = 1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSat(Specifier):
+ desc: str
+ sign: bool
+
+ @classmethod
+ def match(cls, desc, record, etalon, sign):
+ if desc != etalon:
+ return None
+
+ if record.svp64.mode not in (_SVMode.NORMAL, _SVMode.LDST_IMM, _SVMode.LDST_IDX):
+ raise ValueError("only normal, ld/st imm and ld/st idx modes supported")
+
+ return cls(record=record, desc=desc, sign=sign)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.mode[0] = 0b1
+ selector.mode[1] = 0b0
+ selector.N = int(self.sign)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSatS(SpecifierSat):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="sats", sign=True)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSatU(SpecifierSat):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="satu", sign=False)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierMapReduce(Specifier):
+ RG: bool
+
+ @classmethod
+ def match(cls, record, RG):
+ if record.svp64.mode not in (_SVMode.NORMAL, _SVMode.CROP):
+ raise ValueError("only normal and crop modes supported")
+
+ return cls(record=record, RG=RG)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ if self.record.svp64.mode not in (_SVMode.NORMAL, _SVMode.CROP):
+ raise ValueError("only normal and crop modes supported")
+ selector.mode[0] = 0
+ selector.mode[1] = 0
+ selector.mode[2] = 1
+ selector.RG = self.RG
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierMR(SpecifierMapReduce):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "mr":
+ return None
+
+ return super().match(record=record, RG=False)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierMRR(SpecifierMapReduce):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "mrr":
+ return None
+
+ return super().match(record=record, RG=True)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierBranch(Specifier):
+ @classmethod
+ def match(cls, desc, record, etalon):
+ if desc != etalon:
+ return None
+
+ if record.svp64.mode is not _SVMode.BRANCH:
+ raise ValueError("only branch modes supported")
+
+ return cls(record=record)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierAll(SpecifierBranch):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="all")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.ALL = 1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSNZ(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "snz":
+ return None
+
+ if record.svp64.mode not in (_SVMode.BRANCH, _SVMode.CROP):
+ raise ValueError("only branch and crop modes supported")
+
+ return cls(record=record)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ if self.record.svp64.mode in (_SVMode.CROP, _SVMode.BRANCH):
+ selector.SNZ = 1
+ if self.record.svp64.mode is _SVMode.BRANCH:
+ selector.sz = 1
+ else:
+ raise ValueError("only branch and crop modes supported")
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSL(SpecifierBranch):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="sl")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.SL = 1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierSLu(SpecifierBranch):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="slu")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.SLu = 1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierLRu(SpecifierBranch):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record, etalon="lru")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.LRu = 1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVSXX(SpecifierBranch):
+ VSb: bool
+ VLi: bool
+
+ @classmethod
+ def match(cls, desc, record, etalon, VSb, VLi):
+ if desc != etalon:
+ return None
+
+ if record.svp64.mode is not _SVMode.BRANCH:
+ raise ValueError("only branch modes supported")
+
+ return cls(record=record, VSb=VSb, VLi=VLi)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.VLS = 1
+ selector.VSb = int(self.VSb)
+ selector.VLi = int(self.VLi)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVS(SpecifierVSXX):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="vs", VSb=False, VLi=False)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVSi(SpecifierVSXX):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="vsi", VSb=False, VLi=True)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVSb(SpecifierVSXX):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="vsb", VSb=True, VLi=False)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVSbi(SpecifierVSXX):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="vsbi", VSb=True, VLi=True)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierCTX(Specifier):
+ CTi: bool
+
+ @classmethod
+ def match(cls, desc, record, etalon, CTi):
+ if desc != etalon:
+ return None
+
+ if record.svp64.mode is not _SVMode.BRANCH:
+ raise ValueError("only branch modes supported")
+
+ return cls(record=record, CTi=CTi)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.CTR = 1
+ selector.CTi = int(self.CTi)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierCTR(SpecifierCTX):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="ctr", CTi=False)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierCTi(SpecifierCTX):
+ @classmethod
+ def match(cls, desc, record):
+ return super().match(desc=desc, record=record,
+ etalon="cti", CTi=True)
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierPI(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "pi":
+ return None
+
+ if record.svp64.mode is not _SVMode.LDST_IMM:
+ raise ValueError("only ld/st imm mode supported")
+
+ return cls(record=record)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.mode[0] = 0b0
+ selector.mode[1] = 0b0
+ selector.mode[2] = 0b1
+ selector.pi = 0b1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierLF(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "lf":
+ return None
+
+ if record.svp64.mode is not _SVMode.LDST_IMM:
+ raise ValueError("only ld/st imm mode supported")
+
+ return cls(record=record)
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.mode[2] = 1
+ selector.lf = 0b1
+
+
+@_dataclasses.dataclass(eq=True, frozen=True)
+class SpecifierVLi(Specifier):
+ @classmethod
+ def match(cls, desc, record):
+ if desc != "vli":
+ return None
+
+ return cls(record=record)
+
+ def validate(self, others):
+ for spec in others:
+ if isinstance(spec, SpecifierFF):
+ return
+
+ raise ValueError("VLi only allowed in failfirst")
+
+ def assemble(self, insn):
+ selector = insn.select(record=self.record)
+ selector.VLi = 1
+
+
+class Specifiers(tuple):
+ SPECS = (
+ SpecifierW,
+ SpecifierSW,
+ SpecifierDW,
+ SpecifierSubVL,
+ SpecifierFF,
+ SpecifierPR,
+ SpecifierM,
+ SpecifierSM,
+ SpecifierDM,
+ SpecifierZZ,
+ SpecifierSZ,
+ SpecifierDZ,
+ SpecifierEls,
+ SpecifierSEA,
+ SpecifierSatS,
+ SpecifierSatU,
+ SpecifierMR,
+ SpecifierMRR,
+ SpecifierAll,
+ SpecifierSNZ,
+ SpecifierSL,
+ SpecifierSLu,
+ SpecifierLRu,
+ SpecifierVS,
+ SpecifierVSi,
+ SpecifierVSb,
+ SpecifierVSbi,
+ SpecifierVLi,
+ SpecifierCTR,
+ SpecifierCTi,
+ SpecifierPI,
+ SpecifierLF,
+ )
+
+ def __new__(cls, items, record):
+ def transform(item):
+ for spec_cls in cls.SPECS:
+ spec = spec_cls.match(item, record=record)
+ if spec is not None:
+ return spec
+ raise ValueError(item)
+
+ # TODO: remove this hack
+ items = dict.fromkeys(items)
+ if "vli" in items:
+ del items["vli"]
+ items["vli"] = None
+ items = tuple(items)
+
+ specs = tuple(map(transform, items))
+ for (index, spec) in enumerate(specs):
+ head = specs[:index]
+ tail = specs[index + 1:]
+ spec.validate(others=(head + tail))
+
+ return super().__new__(cls, specs)
+
+
+class SVP64OperandMeta(type):
+ class SVP64NonZeroOperand(NonZeroOperand):
+ def assemble(self, insn, value):
+ if isinstance(value, str):
+ value = int(value, 0)
+ if not isinstance(value, int):
+ raise ValueError("non-integer operand")
+
+ # FIXME: this is really weird
+ if self.record.name in ("svstep", "svstep."):
+ value += 1 # compensation
+
+ return super().assemble(value=value, insn=insn)
+
+ class SVP64XOStaticOperand(SpanStaticOperand):
+ def __init__(self, record, value, span):
+ return super().__init__(record=record, name="XO", value=value, span=span)
+
+ __TRANSFORM = {
+ NonZeroOperand: SVP64NonZeroOperand,
+ XOStaticOperand: SVP64XOStaticOperand,
+ }
+
+ def __new__(metacls, name, bases, ns):
+ bases = list(bases)
+ for (index, base_cls) in enumerate(bases):
+ bases[index] = metacls.__TRANSFORM.get(base_cls, base_cls)
+
+ bases = tuple(bases)
+
+ return super().__new__(metacls, name, bases, ns)
+
+
+class SVP64Operand(Operand, metaclass=SVP64OperandMeta):
+ @property
+ def span(self):
+ return tuple(map(lambda bit: (bit + 32), super().span))
+
+
+class RMSelector:
+ def __init__(self, insn, record):
+ self.__insn = insn
+ self.__record = record
+ return super().__init__()
+
+ def __str__(self):
+ return self.rm.__doc__
+
+ def __repr__(self):
+ return repr(self.rm)
+
+ @property
+ def insn(self):
+ return self.__insn
+
+ @property
+ def record(self):
+ return self.__record
+
+ @property
+ def rm(self):
+ rm = getattr(self.insn.prefix.rm, self.record.svp64.mode.name.lower())
+
+ # The idea behind these tables is that they are now literally
# in identical format to insndb.csv and minor_xx.csv and can
- # be done precisely as that. the only thing to watch out for
+ # be done precisely as that. The only thing to watch out for
# is the insertion of Rc=1 as a "mask/value" bit and likewise
# regtype detection (3-bit BF/BFA, 5-bit BA/BB/BT) also inserted
# as the LSB.
table = None
- if record.svp64.mode is _SVMode.NORMAL:
+ if self.record.svp64.mode is _SVMode.NORMAL:
# concatenate mode 5-bit with Rc (LSB) then do a mask/map search
# mode Rc mask Rc member
table = (
(0b110000, 0b110001, "prrc0"), # predicate, Rc=0
(0b110001, 0b110001, "prrc1"), # predicate, Rc=1
)
- rm = rm.normal
- search = ((int(rm.mode) << 1) | Rc)
+ search = ((int(self.insn.prefix.rm.normal.mode) << 1) | self.record.Rc)
- elif record.svp64.mode is _SVMode.LDST_IMM:
+ elif self.record.svp64.mode is _SVMode.LDST_IMM:
# concatenate mode 5-bit with Rc (LSB) then do a mask/map search
# mode Rc mask Rc member
# ironically/coincidentally this table is identical to NORMAL
(0b110001, 0b110001, "prrc1"), # predicate, Rc=1
(0b110000, 0b110001, "prrc0"), # predicate, Rc=0
)
- rm = rm.ldst_imm
- search = ((int(rm.mode) << 1) | Rc)
+ search = ((int(self.insn.prefix.rm.ldst_imm.mode) << 1) | self.record.Rc)
- elif record.svp64.mode is _SVMode.LDST_IDX:
+ elif self.record.svp64.mode is _SVMode.LDST_IDX:
# concatenate mode 5-bit with Rc (LSB) then do a mask/map search
# mode Rc mask Rc member
table = (
(0b110001, 0b110001, "prrc1"), # predicate, Rc=1
(0b110000, 0b110001, "prrc0"), # predicate, Rc=0
)
- rm = rm.ldst_idx
- search = ((int(rm.mode) << 1) | Rc)
+ search = ((int(self.insn.prefix.rm.ldst_idx.mode) << 1) | self.record.Rc)
- elif record.svp64.mode is _SVMode.CROP:
+ elif self.record.svp64.mode is _SVMode.CROP:
# concatenate mode 5-bit with regtype (LSB) then do mask/map search
# mode 3b mask 3b member
table = (
(0b000000, 0b111000, "simple"), # simple
(0b001000, 0b111000, "mr"), # mapreduce
- (0b100001, 0b100001, "ff3"), # failfirst, 3-bit CR
- (0b100000, 0b100000, "ff5"), # failfirst, 5-bit CR
+ (0b100001, 0b100001, "ff3"), # ffirst, 3-bit CR
+ (0b100000, 0b100000, "ff5"), # ffirst, 5-bit CR
)
- # determine CR type, 5-bit (BA/BB/BT) or 3-bit Field (BF/BFA)
- regtype = None
- for idx in range(0, 4):
- for entry in record.svp64.extra[idx]:
- if entry.regtype is _SVExtraRegType.DST:
- if regtype is not None:
- raise ValueError(record.svp64)
- regtype = _RegType(entry.reg)
- if regtype is _RegType.CR_REG:
- regtype = 0 # 5-bit
- elif regtype is _RegType.CR_BIT:
- regtype = 1 # 3-bit
- else:
- raise ValueError(record.svp64)
- # finally provide info for search
- rm = rm.cr_op
- search = ((int(rm.mode) << 1) | (regtype or 0))
+ search = ((int(self.insn.prefix.rm.crop.mode) << 1) | int(self.record.svp64.extra_CR_3bit))
- elif record.svp64.mode is _SVMode.BRANCH:
+ elif self.record.svp64.mode is _SVMode.BRANCH:
# just mode 2-bit
# mode mask member
table = (
(0b11, 0b11, "ctrvls"), # CTR+VLset mode
)
# slightly weird: doesn't have a 5-bit "mode" field like others
- rm = rm.branch
- search = int(rm.mode[0, 1])
+ search = int(self.insn.prefix.rm.branch.mode.sel)
# look up in table
if table is not None:
- for (value, mask, member) in table:
+ for (value, mask, field) in table:
if ((value & mask) == (search & mask)):
- rm = getattr(rm, member)
- break
-
- if rm.__class__ is self.__class__:
- raise ValueError(self)
+ return getattr(rm, field)
return rm
+ def __getattr__(self, key):
+ if key.startswith(f"_{self.__class__.__name__}__"):
+ return super().__getattribute__(key)
+
+ return getattr(self.rm, key)
+
+ def __setattr__(self, key, value):
+ if key.startswith(f"_{self.__class__.__name__}__"):
+ return super().__setattr__(key, value)
+
+ rm = self.rm
+ if not hasattr(rm, key):
+ raise AttributeError(key)
+
+ return setattr(rm, key, value)
+
class SVP64Instruction(PrefixedInstruction):
"""SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
prefix: Prefix
- def record(self, db):
- record = db[self.suffix]
- if record is None:
- raise KeyError(self)
- return record
+ def select(self, record):
+ return RMSelector(insn=self, record=record)
@property
def binary(self):
bits.append(bit)
return "".join(map(str, bits))
- def disassemble(self, db,
+ @classmethod
+ def assemble(cls, record, arguments=None, specifiers=None):
+ insn = super().assemble(record=record, arguments=arguments)
+
+ specifiers = Specifiers(items=specifiers, record=record)
+ for specifier in specifiers:
+ specifier.assemble(insn=insn)
+
+ insn.prefix.PO = 0x1
+ insn.prefix.id = 0x3
+
+ return insn
+
+ def disassemble(self, record,
byteorder="little",
- verbosity=Verbosity.NORMAL):
- def blob(integer):
- if verbosity <= Verbosity.SHORT:
+ style=Style.NORMAL):
+ def blob(insn):
+ if style <= Style.SHORT:
return ""
else:
- blob = integer.to_bytes(length=4, byteorder=byteorder)
+ blob = insn.bytes(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))
- if record is None or record.svp64 is None:
+ blob_prefix = blob(self.prefix)
+ blob_suffix = blob(self.suffix)
+ if record is None:
yield f"{blob_prefix}.long 0x{int(self.prefix):08x}"
yield f"{blob_suffix}.long 0x{int(self.suffix):08x}"
return
+ assert record.svp64 is not None
+
name = f"sv.{record.name}"
- rm = self.prefix.rm.select(record=record)
+ rm = self.select(record=record)
# convert specifiers to /x/y/z (sorted lexicographically)
specifiers = sorted(rm.specifiers(record=record))
# convert operands to " ,x,y,z"
operands = tuple(map(_operator.itemgetter(1),
- self.dynamic_operands(db=db, verbosity=verbosity)))
+ self.spec_dynamic_operands(record=record, style=style)))
operands = ",".join(operands)
if len(operands) > 0: # if any separate with a space
operands = (" " + operands)
- yield f"{blob_prefix}{name}{specifiers}{operands}"
- if blob_suffix:
- yield f"{blob_suffix}"
+ 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 verbosity >= Verbosity.VERBOSE:
+ if style >= Style.VERBOSE:
indent = (" " * 4)
binary = self.binary
- spec = self.spec(db=db, prefix="sv.")
+ spec = self.spec(record=record, prefix="sv.")
yield f"{indent}spec"
yield f"{indent}{indent}{spec}"
yield f"{indent}opcodes"
for opcode in record.opcodes:
yield f"{indent}{indent}{opcode!r}"
- for (cls, kwargs) in record.mdwn.operands:
- operand = cls(record=record, **kwargs)
- yield from operand.disassemble(insn=self, record=record,
- verbosity=verbosity, indent=indent)
+ 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}{rm.__doc__}"
- for line in rm.disassemble(verbosity=verbosity):
+ 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):
# The code below groups the instructions by name:section.
# There can be multiple names for the same instruction.
# The point is to capture different opcodes for the same instruction.
- dd = _collections.defaultdict
sections = {}
records = _collections.defaultdict(set)
path = (root / "insndb.csv")
mdwn=mdwn, fields=fields)
db.add(record)
names[record.name] = record
- PO = section.opcode
- if PO is None:
- PO = ppc[0].opcode
- opcodes[section][PO.value].add(record)
+ opcodes[section][record.PO].add(record)
self.__db = sorted(db)
self.__names = dict(sorted(names.items()))
@_functools.lru_cache(maxsize=None)
def __getitem__(self, key):
- if isinstance(key, WordInstruction):
+ if isinstance(key, SVP64Instruction):
+ key = key.suffix
+
+ if isinstance(key, Instruction):
PO = int(key.PO)
- for (section, group) in self.__opcodes.items():
+ 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