From 77fc07a0cb5819a908da042d7a980b44ca782d74 Mon Sep 17 00:00:00 2001 From: Dmitry Selyutin Date: Sat, 9 Sep 2023 21:18:04 +0300 Subject: [PATCH] libopid: provide tests --- src/libopid/.gitignore | 1 + src/libopid/Makefile | 18 ++++++- src/libopid/opid-dis.c | 76 ++++++++++++++++++++++++++++ src/libopid/test.py | 110 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 src/libopid/opid-dis.c create mode 100644 src/libopid/test.py diff --git a/src/libopid/.gitignore b/src/libopid/.gitignore index 162bd1db..5c09e306 100644 --- a/src/libopid/.gitignore +++ b/src/libopid/.gitignore @@ -2,3 +2,4 @@ *.a *.so *.o +opid-dis diff --git a/src/libopid/Makefile b/src/libopid/Makefile index 46bfbf19..f82d8ba0 100644 --- a/src/libopid/Makefile +++ b/src/libopid/Makefile @@ -1,5 +1,6 @@ CC?=gcc AR?=ar +CPPFLAGS?=-I. CFLAGS?=-O3 -Werror -Wall -Wextra PYTHON?=python3 @@ -8,11 +9,14 @@ GENS:=\ opid-dis-gen.c \ opid-opc-gen.c \ -SRCS:=$(GENS) +SRCS:=\ + $(GENS) \ + opid-dis.c \ OBJS:=\ opid-dis-gen.o \ opid-opc-gen.o \ + opid-dis.o \ .SUFFIX: .so .a .o .c @@ -28,12 +32,22 @@ build: libopid.so .PHONY: clean clean: + rm -f opid-dis rm -f $(GENS) rm -f $(OBJS) rm -f libopid.so rm -f libopid.a +.PHONY: tests +tests: test.py opid-dis + python3 test.py + + +opid-dis: opid-dis.o libopid.a + $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $< -o $@ -L. -l:libopid.a + + libopid.so: libopid.a $(CC) -fPIC -shared -o $@ -L. -Wl,--whole-archive libopid.a -Wl,--no-whole-archive @@ -47,4 +61,4 @@ libopid.a: $(OBJS) .c.o: - $(CC) $(CFLAGS) -fPIC -c $< -o $@ + $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -c $< -o $@ diff --git a/src/libopid/opid-dis.c b/src/libopid/opid-dis.c new file mode 100644 index 00000000..de04e125 --- /dev/null +++ b/src/libopid/opid-dis.c @@ -0,0 +1,76 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define error(FORMAT, ...) \ + do { \ + (void)fprintf(stderr, FORMAT, ##__VA_ARGS__); \ + } while (0) + +#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)) + +int +main(void) { + ssize_t size; + uint32_t insn; + struct opid_ctx ctx; + enum opid_state state; + struct opid_operand const *operand; + + do { + size = read(STDIN_FILENO, &insn, sizeof(insn)); + if (size < 0) { + error("cannot fetch instruction: %s\n", strerror(errno)); + return EXIT_FAILURE; + } else if (size == sizeof(uint32_t)) { + state = opid_disassemble(&ctx, insn); + if (state != OPID_SUCCESS) { + error("invalid instruction: %08" PRIx32 "\n", insn); + return EXIT_FAILURE; + } + + printf("%s [", ctx.record->name); + opid_foreach_operand(&ctx, operand) { + printf("(%" PRIi32 " ", + (int32_t)operand->value); + if (operand->flags & OPID_OPERAND_SIGNED) + printf("s"); + if (operand->flags & OPID_OPERAND_GPR) + printf("g"); + if (operand->flags & OPID_OPERAND_FPR) + printf("f"); + if (operand->flags & OPID_OPERAND_PAIR) + printf("p"); + if (operand->flags & OPID_OPERAND_CR3) + printf("3"); + if (operand->flags & OPID_OPERAND_CR5) + printf("5"); + if (operand->flags & OPID_OPERAND_NONZERO) + printf("z"); + if (operand->flags & OPID_OPERAND_ADDRESS) + printf("a"); + printf(")"); + } + printf("]\n"); + } else if (size != 0) { + error("truncated instruction: %zu\n", (size_t)size); + return EXIT_FAILURE; + } + } while (size != 0); + + return EXIT_SUCCESS; +} diff --git a/src/libopid/test.py b/src/libopid/test.py new file mode 100644 index 00000000..311812ef --- /dev/null +++ b/src/libopid/test.py @@ -0,0 +1,110 @@ +import os +import re +import subprocess +import tempfile +import sys + +from openpower.insndb.core import ( + Database, + Style, + WordInstruction, + SignedOperand, + GPROperand, + FPROperand, + PairOperand, + CR3Operand, + CR5Operand, + NonZeroOperand, +) +from openpower.decoder.power_enums import find_wiki_dir + + +DIS_INSN_PATTERN = r"([A-Za-z0-9_\.]+)" +DIS_VALUE_PATTERN = r"-?\d+" +DIS_FLAGS_PATTERN = r"[sgfp35za]*" +DIS_OPERAND_PATTERN = rf"(?:\({DIS_VALUE_PATTERN}\s+{DIS_FLAGS_PATTERN}\))*" +DIS_PATTERN = fr"^{DIS_INSN_PATTERN}\s+\[({DIS_OPERAND_PATTERN})\]$" +DIS_REGEX = re.compile(DIS_PATTERN) + + +def opid_dis(db, insns): + with tempfile.TemporaryFile(mode="w+b") as stdin: + def remap_insn(insn): + (name, arguments) = insn + record = db[name] + insn = WordInstruction.assemble(record=db[name], + arguments=arguments) + + def remap_operand(operand): + table = { + SignedOperand: "s", + GPROperand: "g", + FPROperand: "f", + PairOperand: "p", + CR3Operand: "3", + NonZeroOperand: "z", + CR5Operand: "5", + } + flags = [] + for (cls, flag) in table.items(): + if isinstance(operand, cls): + flags.append(flag) + flags = "".join(flags) + value = next(operand.disassemble(insn, style=Style.LEGACY)) + return (value, flags) + + operands = tuple(map(remap_operand, + insn.dynamic_operands(record=record))) + + return (insn, name, operands) + + def assert_eq(index, actual, expected): + if actual != expected: + print(f"[{index}]", "actual ", actual) + print(f"[{index}]", "expected", expected) + return False + return True + + insns = tuple(map(remap_insn, insns)) + + for (insn, *_) in insns: + stdin.write(insn.bytes()) + stdin.seek(0) + + sp = subprocess.Popen("./opid-dis", + stdin=stdin, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="UTF-8") + (stdout, stderr) = sp.communicate() + if stderr: + sys.stderr.write(map(str.strip, stderr.splitlines())) + raise ValueError(stderr) + + for (index, line) in enumerate(map(str.strip, stdout.splitlines())): + match = DIS_REGEX.match(line) + if match is None: + raise ValueError(line) + (name, operands) = match.groups() + operands = operands.replace(")(", ":") + operands = operands.replace("(", "").replace(")", "") + operands = tuple(tuple(operand.split(" ")) for operand in operands.split(":")) + print(line) + assert_eq(index=index, actual=name, expected=insns[index][1]) + assert_eq(index=index, actual=operands, expected=insns[index][2]) + + +def main(): + db = Database(find_wiki_dir()) + opid_dis(db=db, insns=( + ("addpcis", ("1", "-1")), + ("addpcis", ("1", "0")), + ("addpcis", ("1", "+1")), + ("svshape2", ("8", "1", "31", "7", "1", "1")), + ("svshape", ("8", "1", "1", "14", "0")), + )) + + +if __name__ == "__main__": + os.environ["SILENCELOG"] = "1" + main() -- 2.30.2