libopid: introduce assembly sketch
authorDmitry Selyutin <ghostmansd@gmail.com>
Sun, 10 Sep 2023 21:03:25 +0000 (00:03 +0300)
committerDmitry Selyutin <ghostmansd@gmail.com>
Wed, 13 Sep 2023 20:09:20 +0000 (23:09 +0300)
src/libopid/Makefile
src/libopid/codegen.py
src/libopid/opid-asm.c [new file with mode: 0644]
src/libopid/opid-opc.c
src/libopid/opid.h

index 21ccf94b0538348cdbd94823bc83916228806948..8fcc4c1d85684dfc3d790b97bbd4b310dc3fb362 100644 (file)
@@ -6,6 +6,7 @@ PYTHON?=python3
 
 
 GENS:=\
+       opid-asm-gen.c \
        opid-dis-gen.c \
        opid-opc-gen.c \
 
@@ -14,6 +15,7 @@ SRCS:=\
        opid-check.c \
 
 OBJS:=\
+       opid-asm.o \
        opid-dis.o \
        opid-opc.o \
        opid-check.o \
@@ -56,8 +58,10 @@ libopid.a: $(OBJS)
        $(AR) rcs $@ $(OBJS)
 
 
+opid-asm.c: opid-asm-gen.c
 opid-dis.c: opid-dis-gen.c
 opid-opc.c: opid-opc-gen.c
+opid-asm.o: opid-asm.c opid-asm-gen.c
 opid-dis.o: opid-dis.c opid-dis-gen.c
 opid-opc.o: opid-opc.c opid-opc-gen.c
 
index f02ceb5570f15fb0f85bce70ba0345198061e600..7df84daff9547131e389963b280d0ef2ae362b22 100644 (file)
@@ -62,6 +62,7 @@ def wrap(bits):
 
 
 class Mode(enum.Enum):
+    ASM_GEN_C = "opid-asm-gen.c"
     DIS_GEN_C = "opid-dis-gen.c"
     OPC_GEN_C = "opid-opc-gen.c"
 
@@ -73,6 +74,7 @@ class Mode(enum.Enum):
 
         cache = Cache()
         codegen = {
+            Mode.ASM_GEN_C: AsmGenSource,
             Mode.DIS_GEN_C: DisGenSource,
             Mode.OPC_GEN_C: OpcGenSource,
         }[self](cache=cache, **arguments)
@@ -321,6 +323,124 @@ class Record(Struct):
     dynamic_operand_ids: DynamicOperandIds
 
 
+class AsmGenSource(Source):
+    @mdis.dispatcher.Hook(DynamicOperands)
+    @contextlib.contextmanager
+    def dispatch_operands(self, node):
+        self.emit("static inline enum opid_state")
+        self.emit("opid_assemble_operand(uint64_t *insn,")
+        with self:
+            with self:
+                self.emit("size_t category,")
+                self.emit("struct opid_operand *operand,")
+                self.emit("void *uctx,")
+                self.emit("enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)) {")
+            self.emit("enum opid_state state;")
+            self.emit("")
+            self.emit("state = operand_cb(uctx, operand);")
+            self.emit("if (state != OPID_SUCCESS)")
+            with self:
+                self.emit("return state;")
+            self.emit("")
+            self.emit(f"switch (category) {{")
+            yield node
+            self.emit("default:")
+            with self:
+                self.emit("return OPID_ERROR_OPERAND_0_LOOKUP;")
+            self.emit("}")
+        self.emit("")
+        with self:
+            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="UINT64_C(0)"):
+            limit = f"UINT64_C({((1 << len(span)) - 1)})"
+            yield f"if (operand->value > {limit})"
+            with self:
+                yield "return OPID_ERROR_OPERAND_0_RANGE;"
+            yield f"*insn |= ("
+            with self:
+                yield from store(span)
+            yield f");"
+            yield f"operand->flags = {flags};"
+            self.emit("break;")
+
+        def nonzero_handler(span):
+            yield "--operand->value;"
+            yield "*insn |= ("
+            with self:
+                yield from store(span)
+            yield ");"
+            yield "++operand->value;"
+            yield "operand->flags = OPID_OPERAND_NONZERO;"
+            self.emit("break;")
+
+        def signed_handler(span, flags="OPID_OPERAND_SIGNED"):
+            limit = ((1 << len(span)) - 1)
+            lolimit = f"INT64_C(-{((limit // 2) + 1)})"
+            hilimit = f"INT64_C({(limit // 2)})"
+            yield f"if (((int64_t)operand->value < {lolimit}) ||"
+            with self:
+                with self:
+                    yield f"((int64_t)operand->value > {hilimit}))"
+                yield "return OPID_ERROR_OPERAND_0_RANGE;"
+            yield f"*insn |= ("
+            with self:
+                yield from store(span)
+            yield f");"
+            yield f"operand->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
+
+
 class DisGenSource(Source):
     class Walker(Walker):
         @mdis.dispatcher.Hook(DynamicOperand, RecordTable)
@@ -503,7 +623,7 @@ class OpcGenSource(Source):
 
     @mdis.dispatcher.Hook(OpcodeTable)
     @contextlib.contextmanager
-    def dispatch_potable(self, node):
+    def dispatch_opcode_table(self, node):
         heads = ([0] * (1 << 6))
         tails = ([0] * (1 << 6))
         for (index, counter) in enumerate(itertools.accumulate(node)):
diff --git a/src/libopid/opid-asm.c b/src/libopid/opid-asm.c
new file mode 100644 (file)
index 0000000..98234be
--- /dev/null
@@ -0,0 +1,58 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "opid.h"
+
+static inline enum opid_state
+opid_assemble_operand(uint64_t *insn,
+        size_t category,
+        struct opid_operand *operand,
+        void *uctx,
+        enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand));
+
+#include "opid-asm-gen.c"
+
+static inline enum opid_state
+opid_assemble_operands(struct opid_ctx *ctx,
+        struct opid_record const *record,
+        uint64_t *insn,
+        void *uctx,
+        enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)) {
+    enum opid_state state;
+    struct opid_opcode const *opcode = &record->opcode;
+
+    *insn = (opcode->value & opcode->mask);
+
+    for (size_t id = 0; ((id != OPID_OPERANDS_NR) && record->operands[id]); ++id) {
+        state = opid_assemble_operand(insn,
+            record->operands[id],
+            &ctx->operands[id],
+            uctx,
+            operand_cb);
+        if (state != OPID_SUCCESS)
+            return state;
+    }
+
+    return state;
+}
+
+enum opid_state
+opid_assemble(struct opid_ctx *ctx,
+        char const *name,
+        uint64_t *insn,
+        void *uctx,
+        enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)) {
+    struct opid_record const *record;
+
+    record = opid_lookup_name(name);
+    if (record == NULL)
+        return OPID_ERROR_LOOKUP;
+
+    _Static_assert(sizeof(record->name) == sizeof(ctx->name),
+        "record name length does not match context name length");
+    memcpy(ctx->name, record->name, sizeof(record->name));
+
+    return opid_assemble_operands(ctx, record, insn, uctx, operand_cb);
+}
index df8277118e593d1314ea276cbebea3abd5125279..9cebf609d65d95e9ddd5b0710c60aa394323cf4e 100644 (file)
@@ -1,5 +1,6 @@
 #include <stddef.h>
 #include <stdint.h>
+#include <string.h>
 
 #include "opid.h"
 
@@ -37,3 +38,34 @@ opid_lookup_insn(uint64_t insn) {
 
     return NULL;
 }
+
+static struct opid_name_id const *
+opid_lookup_name_id(char const *name) {
+    size_t head = 0U;
+    size_t tail = ((sizeof(opid_name_id_table) / sizeof(*opid_name_id_table)) - 1U);
+
+    while (head != tail) {
+        size_t iter = (head + ((tail - head) / 2U));
+        int state = strcmp(opid_name_id_table[iter].name, name);
+
+        if (state < 0)
+            head = (iter + 1);
+        else if (state > 0)
+            tail = iter;
+        else
+            return &opid_name_id_table[iter];
+    }
+
+    return NULL;
+}
+
+struct opid_record const *
+opid_lookup_name(char const *name) {
+    struct opid_name_id const *name_id;
+
+    name_id = opid_lookup_name_id(name);
+    if (name_id == NULL)
+        return NULL;
+
+    return name_id->record;
+}
index 9f064aff8b7aadfd6a20bb2a8a4511a4a6fa10f4..341f47425b6142a6349443142ad093bfaeed5037 100644 (file)
@@ -14,6 +14,14 @@ enum opid_state {
     OPID_ERROR_OPERAND_5_LOOKUP,
     OPID_ERROR_OPERAND_6_LOOKUP,
     OPID_ERROR_OPERAND_7_LOOKUP,
+    OPID_ERROR_OPERAND_0_RANGE,
+    OPID_ERROR_OPERAND_1_RANGE,
+    OPID_ERROR_OPERAND_2_RANGE,
+    OPID_ERROR_OPERAND_3_RANGE,
+    OPID_ERROR_OPERAND_4_RANGE,
+    OPID_ERROR_OPERAND_5_RANGE,
+    OPID_ERROR_OPERAND_6_RANGE,
+    OPID_ERROR_OPERAND_7_RANGE,
 };
 
 #define OPID_NAME_BYTES  16
@@ -56,8 +64,18 @@ struct opid_ctx {
             ((id != OPID_OPERANDS_NR) && (id != (ctx)->nr_operands))); \
         operand = &(ctx)->operands[++id])
 
+enum opid_state
+opid_assemble(struct opid_ctx *ctx,
+        char const *name,
+        uint64_t *insn,
+        void *uctx,
+        enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand));
+
 enum opid_state
 opid_disassemble(struct opid_ctx *ctx, uint64_t insn);
 
 struct opid_record const *
 opid_lookup_insn(uint64_t insn);
+
+struct opid_record const *
+opid_lookup_name(char const *name);