--- /dev/null
+import argparse
+import collections
+import contextlib
+import dataclasses
+import enum
+import itertools
+import pathlib
+import sys
+
+import mdis.dispatcher
+import mdis.visitor
+import mdis.walker
+
+from openpower.decoder.power_enums import (
+ find_wiki_dir,
+)
+
+import openpower.insndb.core as insndb
+
+
+def traverse(root, visitor, walker, **kwargs):
+ with visitor(root, **kwargs):
+ for (node, *_, path, pathcls) in walker(root):
+ traverse(node, visitor, walker, path=path, pathcls=pathcls)
+
+
+def fetch(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} */ (((insn >> {src}) & {one}) << {dst}) |"
+ yield f"UINT64_C(0)"
+
+
+class Mode(enum.Enum):
+ PPC_DIS_GEN_C = "svp64-dis-gen.c"
+ PPC_OPC_GEN_C = "svp64-opc-gen.c"
+
+ def __call__(self, db, **arguments):
+ def pairwise(iterable):
+ (a, b) = itertools.tee(iterable)
+ next(b, None)
+ return zip(a, b)
+
+ cache = Cache()
+ codegen = {
+ Mode.PPC_DIS_GEN_C: DisGenSource,
+ Mode.PPC_OPC_GEN_C: OpcGenSource,
+ }[self](cache=cache, **arguments)
+ for (root, visitor) in pairwise((db, cache, codegen),):
+ walker_cls = getattr(visitor, "Walker", Walker)
+ traverse(root=root, visitor=visitor, walker=walker_cls())
+
+
+class StructMeta(type):
+ def __new__(metacls, name, bases, ns):
+ cls = super().__new__(metacls, name, bases, ns)
+ return dataclasses.dataclass(cls, eq=True, frozen=True)
+
+
+class Struct(metaclass=StructMeta):
+ pass
+
+
+class DynamicOperandIds(tuple): pass
+class StaticOperands(tuple): pass
+class DynamicOperands(tuple): pass
+class POTable(tuple): pass
+class Records(tuple): pass
+
+
+class DynamicOperandId(Struct):
+ name: str = "NIL"
+ index: int = 0
+
+
+class DynamicOperand(Struct):
+ cls: type
+ span: tuple
+ names: tuple
+
+
+class Record(Struct):
+ name: str
+ opcode: insndb.Record.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.__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())
+ nil = DynamicOperandId()
+
+ def dynamic_operand_id(item):
+ (name, cls, span) = item
+ index = (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 record(item):
+ (opcode, name) = item
+ 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])
+
+ 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 Records(map(record, sorted(self.__records.items())))
+ yield POTable(self.__PO)
+
+ @mdis.dispatcher.Hook(insndb.Record)
+ @contextlib.contextmanager
+ def dispatch_record(self, node):
+ self.__record = node
+ yield node
+
+ @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.StaticOperand)
+ @contextlib.contextmanager
+ def dispatch_static_operand(self, node):
+ self.__static_operand[self.__record.name].append(node)
+ yield node
+
+ @mdis.dispatcher.Hook(insndb.DynamicOperand)
+ @contextlib.contextmanager
+ def dispatch_dynamic_operand(self, node):
+ (cls, span) = (node.__class__, node.span)
+ self.__dynamic_operand[cls, span].add(node.name)
+ self.__dynamic_operand_id[self.__record.name].append((node.name, cls, span),)
+ yield node
+
+
+class Walker(insndb.Walker):
+ @mdis.dispatcher.Hook(Cache)
+ def dispatch_cache(self, node):
+ yield from ()
+
+
+class Codegen(mdis.visitor.ContextVisitor):
+ def __init__(self, cache, **arguments):
+ self.__level = 0
+
+ return super().__init__()
+
+ def __enter__(self):
+ self.__level += 1
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_traceback):
+ self.__level -= 1
+
+ def emit(self, message=""):
+ indent = ((" " * 4 * self.__level) if message else "")
+ print(f"{indent}{message}")
+
+ @mdis.dispatcher.Hook(Cache)
+ @contextlib.contextmanager
+ def dispatch_cache(self, node):
+ self.emit("/*")
+ self.emit(" * Autogenerated by libresoc codegen script")
+ self.emit(" * DO NOT EDIT: all changes will be lost")
+ self.emit(" */")
+ self.emit("")
+ yield node
+
+
+class Header(Codegen):
+ pass
+
+
+class Source(Codegen):
+ @mdis.dispatcher.Hook(str)
+ @contextlib.contextmanager
+ def dispatch_str(self, node, *, path, pathcls):
+ self.emit(f"{pathcls(path)} = \"{node}\",")
+ with self: yield node
+
+ @mdis.dispatcher.Hook(object)
+ @contextlib.contextmanager
+ def dispatch_object(self, node, *, path, pathcls):
+ self.emit(f"{pathcls(path)} = {{")
+ with self: yield node
+ self.emit("},")
+
+
+class Record(Struct):
+ static_operands: StaticOperands
+ name: str
+ opcode: insndb.Record.Opcode
+ dynamic_operand_ids: DynamicOperandIds
+
+
+class DisGenSource(Source):
+ class Walker(Walker):
+ @mdis.dispatcher.Hook(DynamicOperand, Records)
+ 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(DynamicOperands)
+ @contextlib.contextmanager
+ def dispatch_operands(self, node):
+ self.emit("static inline enum svp64_state")
+ self.emit("svp64_disassemble_operand(struct svp64_ctx *ctx, uint32_t insn, size_t id) {")
+ with self:
+ self.emit("int64_t value;")
+ self.emit("")
+ self.emit(f"switch (ctx->record->operands[id]) {{")
+ yield node
+ self.emit("default:")
+ with self:
+ self.emit("return (enum svp64_state)((size_t)SVP64_ERROR_OPERAND_0 + id);")
+ self.emit("}")
+ self.emit("")
+ with self:
+ self.emit("ctx->operands[id] = value;")
+ self.emit("")
+ self.emit("return SVP64_SUCCESS;")
+ self.emit("}")
+ self.emit("")
+
+ self.emit("static inline enum svp64_state")
+ self.emit("svp64_disassemble_operands(struct svp64_ctx *ctx, uint32_t insn) {")
+ with self:
+ condition = " && ".join((
+ "(id < (sizeof(ctx->record->operands) / sizeof(*ctx->record->operands)))",
+ "ctx->record->operands[id]",
+ ))
+ self.emit(f"for (size_t id = 0; ({condition}); ++id) {{")
+ with self:
+ self.emit("enum svp64_state state;")
+ self.emit("")
+ self.emit("state = svp64_disassemble_operand(ctx, insn, id);")
+ self.emit("if (state != SVP64_SUCCESS)")
+ with self:
+ self.emit("return state;")
+ self.emit("}")
+ self.emit("")
+ self.emit("return SVP64_SUCCESS;")
+ self.emit("}")
+
+ @mdis.dispatcher.Hook(DynamicOperand)
+ @contextlib.contextmanager
+ def dispatch_operand(self, node, *, path, pathcls):
+ def generic_handler(span):
+ yield f"value = (int64_t)("
+ with self:
+ yield from fetch(span)
+ yield f");"
+ self.emit("break;")
+
+ def signed_handler(span):
+ mask = f"(UINT64_C(1) << (UINT64_C({len(span)}) - 1))"
+ yield "value = (int64_t)("
+ with self:
+ yield "("
+ with self:
+ yield "("
+ with self:
+ yield from fetch(span)
+ yield ")"
+ yield "^"
+ yield f"{mask}"
+ yield ")"
+ yield "-"
+ yield f"{mask}"
+ yield ");"
+ self.emit("break;")
+
+ handlers = {
+ insndb.SignedOperand: signed_handler,
+ }
+ self.emit(f"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */")
+ with self:
+ handler = handlers.get(node.cls, generic_handler)
+ for line in handler(span=node.span):
+ self.emit(line)
+ self.emit("")
+ yield node
+
+ @mdis.dispatcher.Hook(Cache)
+ @contextlib.contextmanager
+ def dispatch_cache(self, node):
+ self.emit("/*")
+ self.emit(" * Autogenerated by libresoc codegen script")
+ self.emit(" * DO NOT EDIT: all changes will be lost")
+ self.emit(" */")
+ self.emit("")
+ self.emit("#include <stdbool.h>")
+ self.emit("#include <stddef.h>")
+ self.emit("#include <stdint.h>")
+ self.emit("")
+ self.emit("#include \"svp64.h\"")
+ self.emit("")
+ yield node
+ self.emit("enum svp64_state")
+ self.emit("svp64_disassemble(struct svp64_ctx *ctx, uint32_t insn) {")
+ with self:
+ self.emit("ctx->record = svp64_lookup_insn(insn);")
+ self.emit("")
+ self.emit("if (ctx->record == NULL)")
+ with self:
+ self.emit("return SVP64_ERROR_LOOKUP;")
+ self.emit("")
+ self.emit("return svp64_disassemble_operands(ctx, insn);")
+ self.emit("}")
+
+
+class OpcGenSource(Source):
+ class Walker(Walker):
+ @mdis.dispatcher.Hook(DynamicOperandId, DynamicOperands, insndb.StaticOperand, POTable)
+ def dispatch_ignore(self, node):
+ yield from ()
+
+ @mdis.dispatcher.Hook(Record)
+ def dispatch_record(self, node):
+ keys = {
+ "dynamic_operand_ids": "operands",
+ }
+
+ for field in dataclasses.fields(node):
+ key = field.name
+ value = getattr(node, key)
+ key = keys.get(key, key)
+ yield (value, node, key, mdis.walker.AttributePath)
+
+ @mdis.dispatcher.Hook(Cache)
+ def dispatch_cache(self, node):
+ (_, records, potable) = node
+ yield from self([records, potable])
+
+ @mdis.dispatcher.Hook(DynamicOperandId)
+ @contextlib.contextmanager
+ def dispatch_dynamic_operand_id(self, node, *, path, pathcls):
+ index = f"UINT8_C(0x{node.index:02x})"
+ self.emit(f"{pathcls(path)} = {index}, /* {node.name} */")
+ with self: yield node
+
+ @mdis.dispatcher.Hook(StaticOperands)
+ @contextlib.contextmanager
+ def dispatch_static_operands(self, node):
+ if node:
+ self.emit("/*")
+ yield node
+ self.emit(" */")
+ else:
+ yield node
+
+ @mdis.dispatcher.Hook(insndb.StaticOperand)
+ @contextlib.contextmanager
+ def dispatch_static_operand(self, node):
+ self.emit(f" * {node.name}={node.value} [{', '.join(map(str, node.span))}]")
+ yield node
+
+ @mdis.dispatcher.Hook(insndb.Record.Opcode.Value, insndb.Record.Opcode.Mask)
+ @contextlib.contextmanager
+ def dispatch_opcode_parts(self, node, *, path, pathcls):
+ self.emit(f"{pathcls(path)} = UINT64_C(0x{node:016x}),")
+ with self: yield node
+
+ @mdis.dispatcher.Hook(insndb.Record.Opcode)
+ @contextlib.contextmanager
+ def dispatch_opcode(self, node):
+ self.emit(".opcode = {")
+ with self: yield node
+ self.emit("},")
+
+ @mdis.dispatcher.Hook(POTable)
+ @contextlib.contextmanager
+ def dispatch_potable(self, node):
+ heads = ([0] * (1 << 6))
+ tails = ([0] * (1 << 6))
+ for (index, counter) in enumerate(itertools.accumulate(node)):
+ heads[index] = (counter - node[index])
+ tails[index] = counter
+ heads = [(tail - node[index]) for (index, tail) in enumerate(tails)]
+ self.emit("static uint16_t const svp64_opcode_hash[64][2] = {")
+ with self:
+ for index in range(64):
+ head = heads[index]
+ tail = tails[index]
+ self.emit(f"[0x{index:02x}] = {{{head}, {tail}}},")
+ self.emit("};")
+ self.emit("")
+ yield node
+
+ @mdis.dispatcher.Hook(Records)
+ @contextlib.contextmanager
+ def dispatch_records(self, node):
+ self.emit("static struct svp64_record const svp64_records[] = {")
+ with self: yield node
+ self.emit("};")
+ self.emit("")
+
+ @mdis.dispatcher.Hook(Cache)
+ @contextlib.contextmanager
+ def dispatch_cache(self, node):
+ self.emit("/*")
+ self.emit(" * Autogenerated by libresoc codegen script")
+ self.emit(" * DO NOT EDIT: all changes will be lost")
+ self.emit(" */")
+ self.emit("")
+ self.emit("#include <stddef.h>")
+ self.emit("#include <stdint.h>")
+ self.emit("")
+ self.emit("#include \"svp64.h\"")
+ self.emit("")
+ yield node
+ self.emit("struct svp64_record const *")
+ self.emit("svp64_lookup_insn(uint32_t insn) {")
+ with self:
+ self.emit("uint32_t PO = (")
+ with self:
+ for line in fetch(range(6)):
+ self.emit(line)
+ self.emit(");")
+ self.emit("struct svp64_record const *iter = &svp64_records[svp64_opcode_hash[PO][0]];")
+ self.emit("struct svp64_record const *tail = &svp64_records[svp64_opcode_hash[PO][1]];")
+ self.emit("")
+ self.emit("for (; iter != tail; ++iter) {")
+ with self:
+ self.emit("struct svp64_opcode const *opcode = &iter->opcode;")
+ self.emit("")
+ self.emit("if ((opcode->value & opcode->mask) == (insn & opcode->mask))")
+ with self:
+ self.emit("return iter;")
+ self.emit("}")
+ self.emit("")
+ self.emit("return NULL;")
+ self.emit("}")
+
+
+def main():
+ table = {mode:{} for mode in Mode}
+ main_parser = argparse.ArgumentParser("codegen",
+ description="C code generator")
+ main_parser.add_argument("-d", "--database",
+ type=pathlib.Path,
+ default=pathlib.Path(find_wiki_dir()))
+ main_subprarsers = main_parser.add_subparsers(dest="mode", required=True)
+ for (mode, _) in table.items():
+ parser = main_subprarsers.add_parser(mode.value)
+
+ arguments = dict(vars(main_parser.parse_args()))
+ mode = Mode(arguments.pop("mode"))
+ db = insndb.Database(root=arguments.pop("database"))
+
+ return mode(db=db, **arguments)
+
+
+if __name__ == "__main__":
+ main()