From: Dmitry Selyutin Date: Fri, 8 Sep 2023 06:47:31 +0000 (+0300) Subject: libsvp64: rename into libopid X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=73bbc9af2ac5c42549a6a932d435d5ea055c330d;p=openpower-isa.git libsvp64: rename into libopid --- diff --git a/src/libopid/.gitignore b/src/libopid/.gitignore new file mode 100644 index 00000000..d72be3e8 --- /dev/null +++ b/src/libopid/.gitignore @@ -0,0 +1,3 @@ +*-gen.* +*.a +*.so diff --git a/src/libopid/Makefile b/src/libopid/Makefile new file mode 100644 index 00000000..46bfbf19 --- /dev/null +++ b/src/libopid/Makefile @@ -0,0 +1,50 @@ +CC?=gcc +AR?=ar +CFLAGS?=-O3 -Werror -Wall -Wextra +PYTHON?=python3 + + +GENS:=\ + opid-dis-gen.c \ + opid-opc-gen.c \ + +SRCS:=$(GENS) + +OBJS:=\ + opid-dis-gen.o \ + opid-opc-gen.o \ + + +.SUFFIX: .so .a .o .c + + +.PHONY: all +all: build + + +.PHONY: build +build: libopid.so + + +.PHONY: clean +clean: + rm -f $(GENS) + rm -f $(OBJS) + rm -f libopid.so + rm -f libopid.a + + +libopid.so: libopid.a + $(CC) -fPIC -shared -o $@ -L. -Wl,--whole-archive libopid.a -Wl,--no-whole-archive + + +libopid.a: $(OBJS) + $(AR) rcs $@ $^ + + + $(GENS): codegen.py + SILENCELOG=true $(PYTHON) codegen.py $@ > $@ + + +.c.o: + $(CC) $(CFLAGS) -fPIC -c $< -o $@ diff --git a/src/libopid/codegen.py b/src/libopid/codegen.py new file mode 100644 index 00000000..1d0f11d7 --- /dev/null +++ b/src/libopid/codegen.py @@ -0,0 +1,520 @@ +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 = "UINT32_C(1)" + for (dst, origin) in enumerate(span): + src = (32 - (origin + 1)) + dst = (bits - (dst + 1)) + dst = f"UINT32_C({dst})" + src = f"UINT32_C({src})" + yield f"/* {origin:<2} */ (((insn >> {src}) & {one}) << {dst}) |" + yield f"UINT32_C(0)" + + +class Mode(enum.Enum): + PPC_DIS_GEN_C = "opid-dis-gen.c" + PPC_OPC_GEN_C = "opid-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 opid_state") + self.emit("opid_disassemble_operand(struct opid_ctx *ctx, uint32_t insn, size_t id) {") + with self: + self.emit("uint32_t value;") + self.emit("uint32_t flags;") + self.emit("") + self.emit(f"switch (ctx->record->operands[id]) {{") + yield node + self.emit("default:") + with self: + self.emit("return (enum opid_state)((size_t)OPID_ERROR_OPERAND_0 + id);") + self.emit("}") + self.emit("") + with self: + self.emit("ctx->operands[id].value = value;") + self.emit("ctx->operands[id].flags = flags;") + self.emit("") + self.emit("return OPID_SUCCESS;") + self.emit("}") + self.emit("") + + self.emit("static inline enum opid_state") + self.emit("opid_disassemble_operands(struct opid_ctx *ctx, uint32_t insn) {") + with self: + self.emit(f"for (size_t id = 0; ((id != OPID_OPERANDS) && ctx->record->operands[id]); ++id) {{") + with self: + self.emit("enum opid_state state;") + self.emit("") + self.emit("state = opid_disassemble_operand(ctx, insn, id);") + self.emit("if (state != OPID_SUCCESS)") + with self: + self.emit("return state;") + self.emit("}") + self.emit("") + self.emit("return OPID_SUCCESS;") + self.emit("}") + + @mdis.dispatcher.Hook(DynamicOperand) + @contextlib.contextmanager + def dispatch_operand(self, node, *, path, pathcls): + def generic_handler(span, flags="UINT32_C(0)"): + yield f"value = (" + with self: + yield from fetch(span) + yield f");" + yield f"flags = {flags};" + self.emit("break;") + + def nonzero_handler(span): + yield from generic_handler(span, "OPID_OPERAND_NONZERO") + + def signed_handler(span, flags="OPID_OPERAND_SIGNED"): + mask = f"(UINT32_C(1) << (UINT32_C({len(span)}) - 1))" + yield "value = (" + with self: + yield "(" + with self: + yield "(" + with self: + yield from fetch(span) + yield ")" + yield "^" + yield f"{mask}" + yield ")" + yield "-" + yield f"{mask}" + yield ");" + yield f"flags = {flags};" + self.emit("break;") + + def address_handler(span): + yield from signed_handler(span, "(OPID_OPERAND_ADDRESS | OPID_OPERAND_SIGNED)") + + def gpr_handler(span, pair=False): + if not pair: + yield from generic_handler(span, "OPID_OPERAND_GPR") + else: + yield from generic_handler(span, "(OPID_OPERAND_GPR | OPID_OPERAND_PAIR)") + + def fpr_handler(span, pair=False): + if not pair: + yield from generic_handler(span, "OPID_OPERAND_FPR") + else: + yield from generic_handler(span, "(OPID_OPERAND_FPR | OPID_OPERAND_PAIR)") + + def cr3_handler(span): + yield from generic_handler(span, "OPID_OPERAND_CR3") + + def cr5_handler(span): + yield from generic_handler(span, "OPID_OPERAND_CR5") + + handlers = { + insndb.GPRPairOperand: lambda span: gpr_handler(span, True), + insndb.FPRPairOperand: lambda span: fpr_handler(span, True), + insndb.GPROperand: gpr_handler, + insndb.FPROperand: fpr_handler, + insndb.CR3Operand: cr3_handler, + insndb.CR5Operand: cr5_handler, + insndb.TargetAddrOperand: address_handler, + insndb.SignedOperand: signed_handler, + insndb.NonZeroOperand: nonzero_handler, + insndb.DynamicOperand: generic_handler, + object: None, + } + self.emit(f"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */") + with self: + for (cls, handler) in handlers.items(): + if issubclass(node.cls, cls): + break + if handler is None: + raise ValueError("unknown 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 ") + self.emit("#include ") + self.emit("#include ") + self.emit("") + self.emit("#include \"opid.h\"") + self.emit("") + yield node + self.emit("enum opid_state") + self.emit("opid_disassemble(struct opid_ctx *ctx, uint32_t insn) {") + with self: + self.emit("ctx->record = opid_lookup_insn(insn);") + self.emit("") + self.emit("if (ctx->record == NULL)") + with self: + self.emit("return OPID_ERROR_LOOKUP;") + self.emit("") + self.emit("return opid_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)} = UINT32_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 opid_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 opid_record const opid_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 ") + self.emit("#include ") + self.emit("") + self.emit("#include \"opid.h\"") + self.emit("") + yield node + self.emit("struct opid_record const *") + self.emit("opid_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 opid_record const *iter = &opid_records[opid_opcode_hash[PO][0]];") + self.emit("struct opid_record const *tail = &opid_records[opid_opcode_hash[PO][1]];") + self.emit("") + self.emit("for (; iter != tail; ++iter) {") + with self: + self.emit("struct opid_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() diff --git a/src/libopid/opid.h b/src/libopid/opid.h new file mode 100644 index 00000000..f0d01f88 --- /dev/null +++ b/src/libopid/opid.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +enum opid_state { + OPID_SUCCESS, + OPID_ERROR_LOOKUP, + OPID_ERROR_OPERAND_0, + OPID_ERROR_OPERAND_1, + OPID_ERROR_OPERAND_2, + OPID_ERROR_OPERAND_3, + OPID_ERROR_OPERAND_4, + OPID_ERROR_OPERAND_5, + OPID_ERROR_OPERAND_6, + OPID_ERROR_OPERAND_7, +}; + +#define OPID_OPERANDS 8 + +struct opid_opcode { + uint32_t value; + uint32_t mask; +}; + +struct opid_record { + struct opid_opcode opcode; + uint8_t operands[OPID_OPERANDS]; + char name[16]; +}; + +struct opid_operand { + uint32_t value; + uint32_t flags; +}; + +#define OPID_OPERAND_SIGNED (UINT32_C(1) << UINT32_C(0)) +#define OPID_OPERAND_GPR (UINT32_C(1) << UINT32_C(1)) +#define OPID_OPERAND_FPR (UINT32_C(1) << UINT32_C(2)) +#define OPID_OPERAND_PAIR (UINT32_C(1) << UINT32_C(3)) +#define OPID_OPERAND_CR3 (UINT32_C(1) << UINT32_C(4)) +#define OPID_OPERAND_CR5 (UINT32_C(1) << UINT32_C(5)) +#define OPID_OPERAND_NONZERO (UINT32_C(1) << UINT32_C(6)) +#define OPID_OPERAND_ADDRESS (UINT32_C(1) << UINT32_C(7)) + +struct opid_ctx { + struct opid_record const *record; + struct opid_operand operands[OPID_OPERANDS]; +}; + +#define opid_foreach_operand(ctx, operand) \ + for (size_t id = 0; \ + (((operand = &(ctx)->operands[id]), 1) && \ + ((id != OPID_OPERANDS) && (ctx)->record->operands[id])); \ + operand = &(ctx)->operands[++id]) + +enum opid_state +opid_disassemble(struct opid_ctx *ctx, uint32_t insn); + +struct opid_record const * +opid_lookup_insn(uint32_t insn); diff --git a/src/libsvp64/.gitignore b/src/libsvp64/.gitignore deleted file mode 100644 index 42376305..00000000 --- a/src/libsvp64/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -svp64-*-gen.* -*.a -*.so diff --git a/src/libsvp64/Makefile b/src/libsvp64/Makefile deleted file mode 100644 index 8121b9ff..00000000 --- a/src/libsvp64/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -CC?=gcc -AR?=ar -CFLAGS?=-O3 -Werror -Wall -Wextra -PYTHON?=python3 - - -GENS:=\ - svp64-dis-gen.c \ - svp64-opc-gen.c \ - -SRCS:=$(GENS) - -OBJS:=\ - svp64-dis-gen.o \ - svp64-opc-gen.o \ - - -.SUFFIX: .so .a .o .c - - -.PHONY: all -all: build - - -.PHONY: build -build: libsvp64.so - - -.PHONY: clean -clean: - rm -f $(GENS) - rm -f $(OBJS) - rm -f libsvp64.so - rm -f libsvp64.a - - -libsvp64.so: libsvp64.a - $(CC) -fPIC -shared -o $@ -L. -Wl,--whole-archive libsvp64.a -Wl,--no-whole-archive - - -libsvp64.a: $(OBJS) - $(AR) rcs $@ $^ - - - $(GENS): codegen.py - SILENCELOG=true $(PYTHON) codegen.py $@ > $@ - - -.c.o: - $(CC) $(CFLAGS) -fPIC -c $< -o $@ diff --git a/src/libsvp64/codegen.py b/src/libsvp64/codegen.py deleted file mode 100644 index 7577d507..00000000 --- a/src/libsvp64/codegen.py +++ /dev/null @@ -1,520 +0,0 @@ -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 = "UINT32_C(1)" - for (dst, origin) in enumerate(span): - src = (32 - (origin + 1)) - dst = (bits - (dst + 1)) - dst = f"UINT32_C({dst})" - src = f"UINT32_C({src})" - yield f"/* {origin:<2} */ (((insn >> {src}) & {one}) << {dst}) |" - yield f"UINT32_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("uint32_t value;") - self.emit("uint32_t flags;") - 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 = value;") - self.emit("ctx->operands[id].flags = flags;") - 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: - self.emit(f"for (size_t id = 0; ((id != SVP64_OPERANDS) && ctx->record->operands[id]); ++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, flags="UINT32_C(0)"): - yield f"value = (" - with self: - yield from fetch(span) - yield f");" - yield f"flags = {flags};" - self.emit("break;") - - def nonzero_handler(span): - yield from generic_handler(span, "SVP64_OPERAND_NONZERO") - - def signed_handler(span, flags="SVP64_OPERAND_SIGNED"): - mask = f"(UINT32_C(1) << (UINT32_C({len(span)}) - 1))" - yield "value = (" - with self: - yield "(" - with self: - yield "(" - with self: - yield from fetch(span) - yield ")" - yield "^" - yield f"{mask}" - yield ")" - yield "-" - yield f"{mask}" - yield ");" - yield f"flags = {flags};" - self.emit("break;") - - def address_handler(span): - yield from signed_handler(span, "(SVP64_OPERAND_ADDRESS | SVP64_OPERAND_SIGNED)") - - def gpr_handler(span, pair=False): - if not pair: - yield from generic_handler(span, "SVP64_OPERAND_GPR") - else: - yield from generic_handler(span, "(SVP64_OPERAND_GPR | SVP64_OPERAND_PAIR)") - - def fpr_handler(span, pair=False): - if not pair: - yield from generic_handler(span, "SVP64_OPERAND_FPR") - else: - yield from generic_handler(span, "(SVP64_OPERAND_FPR | SVP64_OPERAND_PAIR)") - - def cr3_handler(span): - yield from generic_handler(span, "SVP64_OPERAND_CR3") - - def cr5_handler(span): - yield from generic_handler(span, "SVP64_OPERAND_CR5") - - handlers = { - insndb.GPRPairOperand: lambda span: gpr_handler(span, True), - insndb.FPRPairOperand: lambda span: fpr_handler(span, True), - insndb.GPROperand: gpr_handler, - insndb.FPROperand: fpr_handler, - insndb.CR3Operand: cr3_handler, - insndb.CR5Operand: cr5_handler, - insndb.TargetAddrOperand: address_handler, - insndb.SignedOperand: signed_handler, - insndb.NonZeroOperand: nonzero_handler, - insndb.DynamicOperand: generic_handler, - object: None, - } - self.emit(f"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */") - with self: - for (cls, handler) in handlers.items(): - if issubclass(node.cls, cls): - break - if handler is None: - raise ValueError("unknown 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 ") - self.emit("#include ") - self.emit("#include ") - 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)} = UINT32_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 ") - self.emit("#include ") - 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() diff --git a/src/libsvp64/svp64.h b/src/libsvp64/svp64.h deleted file mode 100644 index 43e9717d..00000000 --- a/src/libsvp64/svp64.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include -#include - -enum svp64_state { - SVP64_SUCCESS, - SVP64_ERROR_LOOKUP, - SVP64_ERROR_OPERAND_0, - SVP64_ERROR_OPERAND_1, - SVP64_ERROR_OPERAND_2, - SVP64_ERROR_OPERAND_3, - SVP64_ERROR_OPERAND_4, - SVP64_ERROR_OPERAND_5, - SVP64_ERROR_OPERAND_6, - SVP64_ERROR_OPERAND_7, -}; - -#define SVP64_OPERANDS 8 - -struct svp64_opcode { - uint32_t value; - uint32_t mask; -}; - -struct svp64_record { - struct svp64_opcode opcode; - uint8_t operands[SVP64_OPERANDS]; - char name[16]; -}; - -struct svp64_operand { - uint32_t value; - uint32_t flags; -}; - -#define SVP64_OPERAND_SIGNED (UINT32_C(1) << UINT32_C(0)) -#define SVP64_OPERAND_GPR (UINT32_C(1) << UINT32_C(1)) -#define SVP64_OPERAND_FPR (UINT32_C(1) << UINT32_C(2)) -#define SVP64_OPERAND_PAIR (UINT32_C(1) << UINT32_C(3)) -#define SVP64_OPERAND_CR3 (UINT32_C(1) << UINT32_C(4)) -#define SVP64_OPERAND_CR5 (UINT32_C(1) << UINT32_C(5)) -#define SVP64_OPERAND_NONZERO (UINT32_C(1) << UINT32_C(6)) -#define SVP64_OPERAND_ADDRESS (UINT32_C(1) << UINT32_C(7)) - -struct svp64_ctx { - struct svp64_record const *record; - struct svp64_operand operands[SVP64_OPERANDS]; -}; - -#define svp64_foreach_operand(ctx, operand) \ - for (size_t id = 0; \ - (((operand = &(ctx)->operands[id]), 1) && \ - ((id != SVP64_OPERANDS) && (ctx)->record->operands[id])); \ - operand = &(ctx)->operands[++id]) - -enum svp64_state -svp64_disassemble(struct svp64_ctx *ctx, uint32_t insn); - -struct svp64_record const * -svp64_lookup_insn(uint32_t insn);