From: Dmitry Selyutin Date: Wed, 13 Sep 2023 20:50:38 +0000 (+0300) Subject: libopid: cross-check assembly X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=refs%2Fheads%2Flibopid;p=openpower-isa.git libopid: cross-check assembly --- diff --git a/src/libopid/opid-check.c b/src/libopid/opid-check.c index 6640bde4..75053d74 100644 --- a/src/libopid/opid-check.c +++ b/src/libopid/opid-check.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -9,28 +10,123 @@ #include #include +#define SEP ":" + #define error(FORMAT, ...) \ do { \ (void)fprintf(stderr, FORMAT, ##__VA_ARGS__); \ } while (0) -int -main(void) { +struct uctx { + int insn_id; + int op_id; + int arg; + int argc; + char **argv; +}; + +static enum opid_state +operand_cb(void *vuctx, struct opid_operand *operand) { + char *arg; + char *iter; + long long value; + struct uctx *uctx = vuctx; + + if (uctx->arg == uctx->argc) { + error("missing operand"); + return OPID_ERROR_CALLBACK; + } + + arg = uctx->argv[uctx->arg]; + if (strcmp(SEP, arg) == 0) { + error("invalid operand \"%s\"", arg); + return OPID_ERROR_CALLBACK; + } + + errno = 0; + value = strtoll(uctx->argv[uctx->arg], &iter, 10); + if ((iter == arg) || (*iter != '\0') || + ((value == LONG_MIN || value == LONG_MAX) && errno == ERANGE)) { + error("invalid operand \"%s\"", arg); + return OPID_ERROR_CALLBACK; + } + + operand->value = (uint64_t)(int64_t)value; + + ++uctx->arg; + ++uctx->op_id; + + return OPID_SUCCESS; +} + +static int +assemble(int argc, char **argv) { + uint64_t insn; + struct uctx uctx = { + .insn_id = 0, + .op_id = 0, + .arg = 0, + .argc = argc, + .argv = argv, + }; + + while (uctx.arg != uctx.argc) { + ssize_t size; + uint32_t insn32; + struct opid_ctx ctx; + enum opid_state state; + char const *insn_name = uctx.argv[uctx.arg++]; + + uctx.op_id = 0; + state = opid_assemble(&ctx, insn_name, &insn, &uctx, operand_cb); + if (state != OPID_SUCCESS) { + if (state == OPID_ERROR_LOOKUP) + error("bad instruction name"); + error(" (insn_name=\"%s\", insn_id=%d, op_id=%d)\n", + insn_name, uctx.insn_id, uctx.op_id); + return EXIT_FAILURE; + } + if (uctx.arg != uctx.argc) { + if (strcmp(SEP, uctx.argv[uctx.arg]) != 0) { + error("invalid separator "); + error(" (insn_name=\"%s\", insn_id=%d, op_id=%d)\n", + insn_name, uctx.insn_id, uctx.op_id); + return EXIT_FAILURE; + } + ++uctx.arg; + } + ++uctx.insn_id; + + insn32 = (uint32_t)insn; + size = write(STDOUT_FILENO, &insn32, sizeof(insn32)); + if (size != sizeof(insn32)) { + error("cannot store instruction: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +static int +disassemble(void) { ssize_t size; - uint32_t insn; - struct opid_ctx ctx; - enum opid_state state; - struct opid_operand const *operand; do { + uint32_t insn; + 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)) { + struct opid_ctx ctx; + enum opid_state state; + struct opid_operand const *operand; + state = opid_disassemble(&ctx, insn); if (state != OPID_SUCCESS) { - error("invalid instruction: %08" PRIx32 "\n", insn); + error("invalid instruction: %016" PRIx32 "\n", insn); return EXIT_FAILURE; } @@ -65,3 +161,12 @@ main(void) { return EXIT_SUCCESS; } + +int +main(int argc, char *argv[]) { + if (argc > 1) { + return assemble(--argc, ++argv); + } else { + return disassemble(); + } +} diff --git a/src/libopid/opid-check.py b/src/libopid/opid-check.py index 27a3cfb0..94bbd419 100644 --- a/src/libopid/opid-check.py +++ b/src/libopid/opid-check.py @@ -27,76 +27,99 @@ DIS_PATTERN = fr"^{DIS_INSN_PATTERN}\s+\[({DIS_OPERAND_PATTERN})\]$" DIS_REGEX = re.compile(DIS_PATTERN) -def opid_check(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-check", - 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 disassemble(db, insns): + stdin = tempfile.TemporaryFile(mode="w+b") + + 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) + + stdout = subprocess.check_output(["./opid-check"], stdin=stdin, encoding="UTF-8") + 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]) + + stdin.seek(0) + + return stdin + + +def assemble(db, insns): + command = [ + "./opid-check", + ] + for (name, arguments) in insns: + command.append(name) + command.extend(arguments) + command.append(":") + command.pop() + + stdout = tempfile.TemporaryFile(mode="w+b") + subprocess.check_call(command, stdout=stdout) + stdout.seek(0) + + return stdout + + +def check(db, insns): + with disassemble(db=db, insns=insns) as origin: + with assemble(db=db, insns=insns) as target: + origin = origin.read() + target = target.read() + if origin != target: + print("actual ", "".join(f"\\x{byte:02x}" for byte in target)) + print("expected", "".join(f"\\x{byte:02x}" for byte in origin)) def main(): db = Database(find_wiki_dir()) - opid_check(db=db, insns=( + check(db=db, insns=( ("addpcis", ("1", "-1")), ("addpcis", ("1", "0")), ("addpcis", ("1", "+1")), diff --git a/src/libopid/opid.h b/src/libopid/opid.h index 341f4742..82c6cacb 100644 --- a/src/libopid/opid.h +++ b/src/libopid/opid.h @@ -6,6 +6,7 @@ enum opid_state { OPID_SUCCESS, OPID_ERROR_LOOKUP, + OPID_ERROR_CALLBACK, OPID_ERROR_OPERAND_0_LOOKUP, OPID_ERROR_OPERAND_1_LOOKUP, OPID_ERROR_OPERAND_2_LOOKUP,