import dataclasses
import enum
import itertools
+import operator
import pathlib
-import sys
import mdis.dispatcher
import mdis.visitor
yield f"UINT64_C(0)"
+def store(span):
+ bits = len(span)
+ one = "UINT64_C(1)"
+ for (dst, origin) in enumerate(span):
+ src = (32 - (origin + 1))
+ dst = (bits - (dst + 1))
+ dst = f"UINT64_C({dst})"
+ src = f"UINT64_C({src})"
+ yield f"/* {origin:<2} */ (((operand->value >> {dst}) & {one}) << {src}) |"
+ yield f"UINT64_C(0)"
+
+
+def unwrap(integer):
+ for bit in range(31, -1, -1):
+ yield ((integer >> bit) & 1)
+
+
+def wrap(bits):
+ value = 0
+ for (index, bit) in enumerate(reversed(bits)):
+ if bit:
+ value |= (1 << index)
+ return value
+
+
class Mode(enum.Enum):
- PPC_DIS_GEN_C = "opid-dis-gen.c"
- PPC_OPC_GEN_C = "opid-opc-gen.c"
+ DIS_GEN_C = "opid-dis-gen.c"
+ OPC_GEN_C = "opid-opc-gen.c"
def __call__(self, db, **arguments):
def pairwise(iterable):
cache = Cache()
codegen = {
- Mode.PPC_DIS_GEN_C: DisGenSource,
- Mode.PPC_OPC_GEN_C: OpcGenSource,
+ Mode.DIS_GEN_C: DisGenSource,
+ Mode.OPC_GEN_C: OpcGenSource,
}[self](cache=cache, **arguments)
for (root, visitor) in pairwise((db, cache, codegen),):
walker_cls = getattr(visitor, "Walker", Walker)
class DynamicOperandIds(tuple): pass
class StaticOperands(tuple): pass
class DynamicOperands(tuple): pass
-class POTable(tuple): pass
+class OpcodeIds(tuple): pass
+class NameTable(tuple): pass
+class OpcodeTable(tuple): pass
class RecordTable(tuple): pass
+class Opcode(insndb.Opcode):
+ @property
+ def PO(self):
+ (*_, result) = itertools.accumulate((
+ (((self.value >> 31) & 1) << 5), # 0
+ (((self.value >> 30) & 1) << 4), # 1
+ (((self.value >> 29) & 1) << 3), # 2
+ (((self.value >> 28) & 1) << 2), # 3
+ (((self.value >> 27) & 1) << 1), # 4
+ (((self.value >> 26) & 1) << 0), # 5
+ ), operator.or_)
+ return result
+
+ @property
+ def weight(self):
+ bits = tuple(unwrap(self.mask)).count(1)
+ return (self.PO, -bits, self.mask)
+
+ def __lt__(self, other):
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return self.weight < other.weight
+
class DynamicOperandId(Struct):
name: str = "NIL"
index: int = 0
+class NameId(Struct):
+ name: str
+ index: int
+
+
class DynamicOperand(Struct):
cls: type
span: tuple
class Record(Struct):
name: str
- opcode: insndb.Record.Opcode
+ opcode: Opcode
dynamic_operand_ids: DynamicOperandIds
static_operands: StaticOperands
class Cache(mdis.visitor.ContextVisitor):
def __init__(self):
self.__PO = ([0] * (1 << 6))
- self.__records = collections.defaultdict(list)
+ self.__records = []
self.__static_operand = collections.defaultdict(list)
self.__dynamic_operand = collections.defaultdict(set)
self.__dynamic_operand_id = collections.defaultdict(list)
return super().__init__()
def __iter__(self):
- table = tuple(self.__dynamic_operand.keys())
+ name_table = {}
+ operands_table = tuple(self.__dynamic_operand.keys())
nil = DynamicOperandId()
def dynamic_operand_id(item):
(name, cls, span) = item
- index = (table.index((cls, span),) + 1)
+ index = (operands_table.index((cls, span),) + 1)
return DynamicOperandId(name=name, index=index)
def dynamic_operand(item):
((cls, span), names) = item
return DynamicOperand(cls=cls, span=span, names=tuple(sorted(names)))
+ def name_id(item):
+ (name, index) = item
+ return NameId(name=name, index=index)
+
def record(item):
- (opcode, name) = item
+ (index, (opcode, name)) = item
+ name_table[name] = index
dynamic_operand_ids = map(dynamic_operand_id, self.__dynamic_operand_id[name])
dynamic_operand_ids = DynamicOperandIds(tuple(dynamic_operand_ids) + (nil,))
- static_operands = StaticOperands(self.__static_operand[name])
+ static_operands = StaticOperands(dict.fromkeys(self.__static_operand[name]))
return Record(opcode=opcode, name=name,
dynamic_operand_ids=dynamic_operand_ids,
static_operands=static_operands)
yield DynamicOperands(map(dynamic_operand, self.__dynamic_operand.items()))
- yield RecordTable(map(record, sorted(self.__records.items())))
- yield POTable(self.__PO)
+ yield RecordTable(map(record, enumerate(sorted(self.__records))))
+ yield OpcodeTable(self.__PO)
+ yield NameTable(sorted(map(name_id, name_table.items()), key=lambda item: item.name))
@mdis.dispatcher.Hook(insndb.Record)
@contextlib.contextmanager
@mdis.dispatcher.Hook(insndb.Record.Opcode)
@contextlib.contextmanager
def dispatch_record_opcode(self, node):
- self.__records[node] = self.__record.name
self.__PO[self.__record.PO] += 1
yield node
+ @mdis.dispatcher.Hook(insndb.Record.Opcodes)
+ @contextlib.contextmanager
+ def dispatch_record_opcodes(self, node):
+ masks = {subnode.mask for subnode in node}
+ if len(masks) != 1:
+ raise ValueError(masks)
+ mask = list(unwrap(masks.pop()))
+ states = tuple(unwrap(node[0].value))
+ for subnode in node[1:]:
+ for (index, bit) in enumerate(unwrap(subnode.value)):
+ if mask[index] and (states[index] != bit):
+ mask[index] = 0
+
+ mask = insndb.Record.Opcode.Mask(wrap(mask))
+ opcode = Opcode(node[0].value, mask)
+ self.__records.append((opcode, self.__record.name))
+
+ yield node
+
@mdis.dispatcher.Hook(insndb.StaticOperand)
@contextlib.contextmanager
def dispatch_static_operand(self, node):
class Source(Codegen):
+ class Walker(Walker):
+ @mdis.dispatcher.Hook(DynamicOperand, RecordTable)
+ def dispatch_ignore(self, node):
+ yield from ()
+
+ @mdis.dispatcher.Hook(Cache)
+ def dispatch_cache(self, node):
+ (operands, _, _, _) = node
+ yield from self([operands])
+
@mdis.dispatcher.Hook(str)
@contextlib.contextmanager
def dispatch_str(self, node, *, path, pathcls):
@mdis.dispatcher.Hook(Cache)
def dispatch_cache(self, node):
- (operands, _, _) = node
+ (operands, _, _, _) = node
yield from self([operands])
@mdis.dispatcher.Hook(DynamicOperands)
yield node
self.emit("default:")
with self:
- self.emit("return OPID_ERROR_OPERAND_0;")
+ self.emit("return OPID_ERROR_OPERAND_0_LOOKUP;")
self.emit("}")
self.emit("")
with self:
class OpcGenSource(Source):
class Walker(Walker):
- @mdis.dispatcher.Hook(DynamicOperandId, DynamicOperands, insndb.StaticOperand, POTable)
+ @mdis.dispatcher.Hook(DynamicOperandId, NameId, Opcode,
+ DynamicOperands, insndb.StaticOperand, OpcodeTable)
def dispatch_ignore(self, node):
yield from ()
@mdis.dispatcher.Hook(Cache)
def dispatch_cache(self, node):
- (_, records, potable) = node
- yield from self([records, potable])
+ (_, records, opcodes, names) = node
+ yield from self([records, opcodes, names])
@mdis.dispatcher.Hook(DynamicOperandId)
@contextlib.contextmanager
self.emit(f"{pathcls(path)} = UINT64_C(0x{node:016x}),")
with self: yield node
- @mdis.dispatcher.Hook(insndb.Record.Opcode)
+ @mdis.dispatcher.Hook(Opcode)
@contextlib.contextmanager
def dispatch_opcode(self, node):
self.emit(".opcode = {")
- with self: yield node
+ with self:
+ self.emit(f".value = UINT64_C(0x{node.value:08x}),")
+ self.emit(f".mask = UINT64_C(0x{node.mask:08x}),")
self.emit("},")
+ yield node
- @mdis.dispatcher.Hook(POTable)
+ @mdis.dispatcher.Hook(OpcodeTable)
@contextlib.contextmanager
def dispatch_potable(self, node):
heads = ([0] * (1 << 6))
for index in range(64):
head = heads[index]
tail = tails[index]
- self.emit(f"[0x{index:02x}] = {{{head}, {tail}}},")
+ self.emit(f"[{index}] = {{{head}, {tail}}},")
self.emit("};")
+ self.emit("")
+ yield node
+
+ @mdis.dispatcher.Hook(NameId)
+ @contextlib.contextmanager
+ def dispatch_name_id(self, node):
+ self.emit(f"{{\"{node.name}\", &opid_record_table[{node.index}]}},")
yield node
+ @mdis.dispatcher.Hook(NameTable)
+ @contextlib.contextmanager
+ def dispatch_name_table(self, node):
+ self.emit(f"static struct opid_name_id const opid_name_id_table[] = {{")
+ with self:
+ yield node
+ self.emit("};")
+
@mdis.dispatcher.Hook(RecordTable)
@contextlib.contextmanager
def dispatch_records(self, node):