Skip to content

Instantly share code, notes, and snippets.

@Chubek
Last active November 9, 2024 05:42
Show Gist options
  • Save Chubek/f3d7268d873c3a6e7564565a69b92806 to your computer and use it in GitHub Desktop.
Save Chubek/f3d7268d873c3a6e7564565a69b92806 to your computer and use it in GitHub Desktop.
LyVM: the Tiny VM

This is a very tiny, tiny virtual machine. Compile it with an ISO-C compiler like GCC or MSVC or Clang.

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#define MAX_CODE_SIZE 4096
#define MAX_DATA_SIZE 8192
#define MAX_LOCALS_NUM 128
#define MAX_GLOBALS_NUM 256
#define OPCODE_iadd 0
#define OPCODE_isub 1
#define OPCODE_imul 2
#define OPCODE_ilt 3
#define OPCODE_ieq 4
#define OPCODE_br 6
#define OPCODE_brt 7
#define OPCODE_brf 8
#define OPCODE_iconst 9
#define OPCODE_gload 10
#define OPCODE_gstore 11
#define OPCODE_load 12
#define OPCODE_store 13
#define OPCODE_print 14
#define OPCODE_call 15
#define OPCODE_ret 16
#define OPCODE_halt 17
#define NO_OPERAND -1
static struct Instr {
uint64_t opcode : 5;
uint64_t operand : 59;
} CODE[MAX_CODE_SIZE];
static int64_t DATA[MAX_DATA_SIZE], GLOBALS[MAX_GLOBALS_NUM],
LOCALS[MAX_LOCALS_NUM];
static ssize_t INSTR_POINTER = 0, FRAME_POINTER = 0, STACK_POINTER = 0,
CODE_BOTTOM = 0;
static inline void Raise(const char *msg) {
fprintf(stderr, "Error occurred: %s\n", msg);
exit(EX_SOFTWARE);
}
static inline void BlankOutLocalsSafe(void) {
memset(&LOCALS[0], 0, MAX_LOCALS_NUM);
}
static inline void BlankOutGlobalsSafe(void) {
memset(&GLOBALS[0], 0, MAX_GLOBALS_NUM);
}
static inline intmax_t DataStackPopSafe(void) {
if (STACK_POINTER > 0)
return DATA[--STACK_POINTER];
else
Raise("Stack underflow");
}
static inline void DataStackPushSafe(intmax_t item) {
if (STACK_POINTER < MAX_DATA_SIZE)
DATA[STACK_POINTER++] = item;
else
Raise("Stack overflow");
}
static inline intmax_t GlobalsGetSafe(ssize_t index) {
if (index < MAX_GLOBALS_NUM)
return GLOBALS[index];
else
Raise("Globals GET index error");
}
static inline void GlobalsSetSafe(ssize_t index, intmax_t value) {
if (index < MAX_GLOBALS_NUM)
GLOBALS[index] = value;
else
Raise("Globals SET index error");
}
static inline intmax_t LocalsGetSafe(ssize_t index) {
if (index < MAX_LOCALS_NUM)
return LOCALS[index];
else
Raise("Locals GET index error");
}
static inline void LocalsSetSafe(ssize_t index, intmax_t value) {
if (index < MAX_LOCALS_NUM)
LOCALS[index] = value;
else
Raise("Locals SET index error");
}
static inline intptr_t GetFnAddr(intmax_t operand) {
return ((uintmax_t)operand) >> 3;
}
static inline uint8_t GetNumArgs(intmax_t operand) {
return (((uintmax_t)operand) & ~7) >> 61;
}
static inline bool IsTruthy(intmax_t operand) { return operand >= 1; }
static inline bool IsFalsy(intmax_t operand) { return operand <= 0; }
static inline void SetIPSafe(intmax_t operand) {
if (operand < MAX_CODE_SIZE)
INSTR_POINTER = operand;
else
Raise("Large instruction pointer set");
}
static inline void UnwindStackSafe(void) { /* todo */ }
static inline ssize_t InsertInstr(uint8_t opcode, intmax_t operand) {
CODE[CODE_BOTTOM].opcode = opcode;
CODE[CODE_BOTTOM].operand = operand;
return CODE_BOTTOM++;
}
static void Interpret(void) {
register uint8_t opcode = -1;
register intmax_t operand = -1, temp1 = -1, temp2 = -1;
BlankOutGlobalsSafe();
while (INSTR_POINTER < CODE_BOTTOM) {
opcode = CODE[INSTR_POINTER].opcode;
operand = CODE[INSTR_POINTER].operand;
INSTR_POINTER++;
switch (opcode) {
case OPCODE_iadd:
temp2 = DataStackPopSafe();
temp1 = DataStackPopSafe();
DataStackPushSafe(temp1 + temp2);
continue;
case OPCODE_isub:
temp2 = DataStackPopSafe();
temp1 = DataStackPopSafe();
DataStackPushSafe(temp1 - temp2);
continue;
case OPCODE_imul:
temp2 = DataStackPopSafe();
temp1 = DataStackPopSafe();
DataStackPushSafe(temp1 * temp2);
continue;
case OPCODE_ilt:
temp2 = DataStackPopSafe();
temp1 = DataStackPopSafe();
DataStackPushSafe(temp1 < temp2);
continue;
case OPCODE_ieq:
temp2 = DataStackPopSafe();
temp1 = DataStackPopSafe();
DataStackPushSafe(temp1 == temp2);
continue;
case OPCODE_iconst:
DataStackPushSafe(operand);
continue;
case OPCODE_br:
SetIPSafe(operand);
continue;
case OPCODE_brt:
temp1 = DataStackPopSafe();
if (IsTruthy(temp1)) {
SetIPSafe(operand);
}
continue;
case OPCODE_brf:
temp1 = DataStackPopSafe();
if (IsFalsy(temp1)) {
SetIPSafe(operand);
}
continue;
case OPCODE_gload:
temp1 = GlobalsGetSafe(operand);
DataStackPushSafe(temp1);
continue;
case OPCODE_gstore:
temp1 = DataStackPopSafe();
GlobalsSetSafe(operand, temp1);
continue;
case OPCODE_load:
temp1 = LocalsGetSafe(operand);
DataStackPushSafe(temp1);
continue;
case OPCODE_store:
temp1 = DataStackPopSafe();
LocalsSetSafe(operand, temp1);
continue;
case OPCODE_print:
temp1 = DataStackPopSafe();
printf("%ld\n", temp1);
continue;
case OPCODE_call:
temp1 = GetFnAddr(operand);
temp2 = GetNumArgs(operand);
DataStackPushSafe(temp2);
DataStackPushSafe(FRAME_POINTER);
DataStackPushSafe(INSTR_POINTER);
FRAME_POINTER = STACK_POINTER;
SetIPSafe(temp1);
BlankOutLocalsSafe();
continue;
case OPCODE_ret:
temp1 = DataStackPopSafe();
STACK_POINTER = FRAME_POINTER;
INSTR_POINTER = DataStackPopSafe();
FRAME_POINTER = DataStackPopSafe();
temp2 = DataStackPopSafe();
STACK_POINTER -= temp2;
DataStackPushSafe(temp1);
continue;
case OPCODE_halt:
UnwindStackSafe();
return;
default:
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment