Skip to content

Instantly share code, notes, and snippets.

@madmann91
Created February 14, 2021 14:26
Show Gist options
  • Save madmann91/1da57b5d3d6056297266f3fd5b9c4213 to your computer and use it in GitHub Desktop.
Save madmann91/1da57b5d3d6056297266f3fd5b9c4213 to your computer and use it in GitHub Desktop.
Small VM example
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
#define REG_COUNT 64
#define OPCODE_LIST(f) \
f(0, HALT, "halt") \
f(1, JUMP, "jump") \
f(4, CONST, "const") \
f(2, LOAD, "load") \
f(2, STORE, "store") \
f(3, ADD, "add") \
f(3, SUB, "sub") \
f(3, MUL, "mul") \
f(3, DIV, "div") \
f(3, REM, "rem") \
f(3, LSHFT, "lshft") \
f(3, RSHFT, "rshft") \
f(3, AND, "and") \
f(3, OR, "or") \
f(3, XOR, "xor") \
f(4, SEL, "sel") \
f(3, CMPEQ, "cmpeq") \
f(3, CMPNE, "cmpne") \
f(3, CMPGT, "cmpgt") \
f(3, CMPGE, "cmpge") \
f(3, CMPLT, "cmplt") \
f(3, CMPLE, "cmple")
enum opcode {
#define f(n, op, name) OP##n##_##op,
OPCODE_LIST(f)
#undef f
};
struct inst {
unsigned op : 5;
unsigned dst : 6;
unsigned src1 : 6;
unsigned src2 : 6;
unsigned src3 : 6;
};
typedef uint64_t reg_t;
struct state {
reg_t regs[REG_COUNT];
struct inst* prog;
size_t prog_counter;
};
static inline const char* op_name(enum opcode opcode) {
switch (opcode) {
#define f(n, op, name) case OP##n##_##op: return name;
OPCODE_LIST(f)
#undef f
default:
assert(false && "unsupported opcode");
return "";
}
}
static inline size_t op_count(enum opcode opcode) {
switch (opcode) {
#define f(n, op, name) case OP##n##_##op: return n;
OPCODE_LIST(f)
#undef f
default:
assert(false && "unsupported opcode");
return 0;
}
}
static inline reg_t const_value(const struct inst* inst) {
assert(inst->op == OP4_CONST);
return
((reg_t)inst->src1) |
(((reg_t)inst->src2) << 6) |
(((reg_t)inst->src3) << 12);
}
void print(const struct inst* inst) {
printf("%s", op_name(inst->op));
if (inst->op == OP4_CONST) {
printf(" r%d, %"PRIu64"\n", inst->dst, const_value(inst));
} else {
switch (op_count(inst->op)) {
case 0: printf("\n"); break;
case 1: printf(" r%d\n", inst->dst); break;
case 2: printf(" r%d, r%d\n", inst->dst, inst->src1); break;
case 3: printf(" r%d, r%d, r%d\n", inst->dst, inst->src1, inst->src2); break;
case 4: printf(" r%d, r%d, r%d, r%d\n", inst->dst, inst->src1, inst->src2, inst->src3); break;
default: break;
}
}
}
static inline bool eval(struct state* state, const struct inst* inst) {
switch (inst->op) {
#define BINARY_OP(opcode, op) \
case opcode: \
state->regs[inst->dst] = state->regs[inst->src1] op state->regs[inst->src2]; \
return true;
BINARY_OP(OP3_ADD, +)
BINARY_OP(OP3_SUB, -)
BINARY_OP(OP3_MUL, *)
BINARY_OP(OP3_DIV, /)
BINARY_OP(OP3_REM, %)
BINARY_OP(OP3_CMPEQ, ==)
BINARY_OP(OP3_CMPNE, !=)
BINARY_OP(OP3_CMPGT, >)
BINARY_OP(OP3_CMPGE, >=)
BINARY_OP(OP3_CMPLT, >)
BINARY_OP(OP3_CMPLE, >=)
case OP4_SEL:
state->regs[inst->dst] = state->regs[inst->src1]
? state->regs[inst->src2]
: state->regs[inst->src3];
return true;
case OP4_CONST:
state->regs[inst->dst] = const_value(inst);
return true;
case OP2_LOAD:
state->regs[inst->dst] = *(reg_t*)(state->regs[inst->src1]);
return true;
case OP2_STORE:
*(reg_t*)(state->regs[inst->dst]) = state->regs[inst->src1];
return true;
case OP1_JUMP:
state->prog_counter = state->regs[inst->dst];
return true;
default:
assert(false && "unsupported opcode");
case OP0_HALT:
return false;
}
}
void run(struct state* state) {
do {
//print(&state->prog[state->prog_counter]);
} while (eval(state, &state->prog[state->prog_counter++]));
}
int main() {
struct inst prog[] = {
{ OP4_CONST, 0, 0x00 },
{ OP4_CONST, 1, 0x01 },
{ OP4_CONST, 2, 63, 63, 63 },
{ OP4_CONST, 3, 0x09 },
{ OP4_CONST, 4, 0x05 },
{ OP3_SUB, 2, 2, 1 },
{ OP3_CMPEQ, 5, 2, 0 },
{ OP4_SEL, 6, 5, 3, 4 },
{ OP1_JUMP, 6 },
{ OP0_HALT }
};
struct state state = { .prog = prog };
run(&state);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment