This is a very tiny, tiny virtual machine. Compile it with an ISO-C compiler like GCC or MSVC or Clang.
Last active
November 9, 2024 05:42
-
-
Save Chubek/f3d7268d873c3a6e7564565a69b92806 to your computer and use it in GitHub Desktop.
LyVM: the Tiny VM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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