remove runtime from backend

This commit is contained in:
SimonFJ20 2025-03-05 11:57:07 +01:00
parent b752990843
commit a38c518569
2 changed files with 0 additions and 1120 deletions

View File

@ -1,899 +0,0 @@
#include "vm.h"
#include "util.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
void vm_construct(VM* vm)
{
size_t files_capacity = 64;
FsFile* files = malloc(sizeof(FsFile) * files_capacity);
*vm = (VM) {
.files = files,
.files_size = 0,
.files_capacity = files_capacity,
.next_file_id = 0,
};
vm->files[vm->files_size++] = (FsFile) { .id = 0, .fp = stdin };
vm->files[vm->files_size++] = (FsFile) { .id = 1, .fp = stdout };
vm->files[vm->files_size++] = (FsFile) { .id = 2, .fp = stderr };
vm->next_file_id += 3;
}
void vm_destroy(VM* vm)
{
(void)vm;
}
ALWAYS_INLINE static inline Op line_op(uint32_t line)
{
return line & 0xff;
}
ALWAYS_INLINE static inline Reg line_dst(uint32_t line)
{
return line >> 8 & 0xff;
}
ALWAYS_INLINE static inline Reg line_src_right(uint32_t line)
{
return line >> 16 & 0xff;
}
ALWAYS_INLINE static inline Reg line_src_left(uint32_t line)
{
return line >> 24 & 0xff;
}
ALWAYS_INLINE static inline uint32_t eat_i32(const uint32_t** pc)
{
uint32_t imm = **pc;
++*pc;
return imm;
}
ALWAYS_INLINE static inline uint64_t eat_i64(const uint32_t** pc)
{
uint64_t imm = **pc;
++*pc;
imm &= (uint64_t)**pc << 32;
++*pc;
return imm;
}
ALWAYS_INLINE static inline double eat_f64(const uint32_t** pc)
{
return (double)eat_i64(pc);
}
int vm_run(VM* vm, const uint32_t* program)
{
(void)vm;
Regs regs = {
.iregs = { 0 },
.fregs = { 0.0 },
};
uint64_t* const stack = malloc(STACK_SIZE * sizeof(uint64_t));
Call* const call_stack_base = malloc(CALL_STACK_SIZE * sizeof(Call));
Call* call_stack = call_stack_base;
const uint32_t* program_base = program;
const uint32_t* pc = program_base;
uint64_t* sb = stack;
uint64_t* sp = sb;
for (;;) {
uint32_t line = *pc;
Op op = line_op(line);
++pc;
switch (op) {
case Op_Nop: {
break;
}
case Op_Halt: {
goto halt_program;
}
case Op_Builtin: {
break;
}
// ---
case Op_Call: {
Reg reg = line_src_left(line);
*call_stack = (Call) { .caller_sb = sb, .return_ptr = pc + 1 };
++call_stack;
pc = program_base + regs.iregs[reg];
break;
}
case Op_CallI: {
uint32_t ptr = eat_i32(&pc);
*call_stack = (Call) { .caller_sb = sb, .return_ptr = pc + 1 };
++call_stack;
pc = program_base + ptr;
break;
}
case Op_Ret: {
--call_stack;
sb = call_stack->caller_sb;
pc = call_stack->return_ptr;
break;
}
case Op_Alloca: {
uint32_t size = eat_i32(&pc);
sp += size;
break;
}
// ---
case Op_Jmp: {
uint32_t ptr = eat_i32(&pc);
pc = program_base + ptr;
break;
}
case Op_Jnz: {
uint32_t ptr = eat_i32(&pc);
if (regs.iregs[line_src_right(line)] != 0) {
pc = program_base + ptr;
} else {
++pc;
}
break;
}
case Op_Jz: {
Reg reg = line_src_left(line);
uint32_t ptr = eat_i32(&pc);
if (regs.iregs[reg] == 0) {
pc = program_base + ptr;
} else {
++pc;
}
break;
}
// ---
case Op_Load8: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.iregs[dst] = *(uint8_t*)regs.iregs[src];
break;
}
case Op_LoadI8: {
Reg dst = line_dst(line);
uint64_t addr = eat_i64(&pc);
regs.iregs[dst] = *(uint8_t*)addr;
break;
}
case Op_LoadA8: {
Reg dst = line_dst(line);
Reg base = line_src_left(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
regs.iregs[dst] = *(uint8_t*)(addr);
break;
}
case Op_Load16: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.iregs[dst] = *(uint16_t*)regs.iregs[src];
break;
}
case Op_LoadI16: {
Reg dst = line_dst(line);
uint64_t addr = eat_i64(&pc);
regs.iregs[dst] = *(uint16_t*)addr;
break;
}
case Op_LoadA16: {
Reg dst = line_dst(line);
Reg base = line_src_left(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
regs.iregs[dst] = *(uint16_t*)(addr);
break;
}
case Op_Load32: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.iregs[dst] = *(uint32_t*)regs.iregs[src];
break;
}
case Op_LoadI32: {
Reg dst = line_dst(line);
uint64_t addr = eat_i64(&pc);
regs.iregs[dst] = *(uint32_t*)addr;
break;
}
case Op_LoadA32: {
Reg dst = line_dst(line);
Reg base = line_src_left(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
regs.iregs[dst] = *(uint32_t*)(addr);
break;
}
case Op_Load64: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.iregs[dst] = *(uint64_t*)regs.iregs[src];
break;
}
case Op_LoadI64: {
Reg dst = line_dst(line);
uint64_t addr = eat_i64(&pc);
regs.iregs[dst] = *(uint64_t*)addr;
break;
}
case Op_LoadA64: {
Reg dst = line_dst(line);
Reg base = line_src_left(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
regs.iregs[dst] = *(uint64_t*)(addr);
break;
}
case Op_LoadF: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.fregs[dst] = *(double*)regs.iregs[src];
break;
}
case Op_LoadIF: {
Reg dst = line_dst(line);
uint64_t addr = eat_i64(&pc);
regs.fregs[dst] = *(double*)addr;
break;
}
case Op_LoadAF: {
Reg dst = line_dst(line);
Reg base = line_src_left(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
regs.fregs[dst]
= *(double*)(regs.iregs[base] + regs.iregs[offset] * incr);
break;
}
// ---
case Op_Store8: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
*(uint8_t*)regs.iregs[dst] = (uint8_t)regs.iregs[src];
break;
}
case Op_StoreA8: {
Reg src = line_src_left(line);
Reg base = line_dst(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
*(uint8_t*)addr = (uint8_t)regs.iregs[src];
break;
}
case Op_Store16: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
*(uint16_t*)regs.iregs[dst] = (uint16_t)regs.iregs[src];
break;
}
case Op_StoreA16: {
Reg src = line_src_left(line);
Reg base = line_dst(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
*(uint16_t*)addr = (uint16_t)regs.iregs[src];
break;
}
case Op_Store32: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
*(uint32_t*)regs.iregs[dst] = (uint32_t)regs.iregs[src];
break;
}
case Op_StoreA32: {
Reg src = line_src_left(line);
Reg base = line_dst(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
*(uint32_t*)addr = (uint32_t)regs.iregs[src];
break;
}
case Op_Store64: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
*(uint64_t*)regs.iregs[dst] = regs.iregs[src];
break;
}
case Op_StoreA64: {
Reg src = line_src_left(line);
Reg base = line_dst(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
*(uint64_t*)addr = regs.iregs[src];
break;
}
case Op_StoreF: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
*(double*)regs.iregs[dst] = regs.fregs[src];
break;
}
case Op_StoreAF: {
Reg src = line_src_left(line);
Reg base = line_dst(line);
Reg offset = line_src_right(line);
uint32_t incr = eat_i32(&pc);
uint64_t addr = regs.iregs[base] + regs.iregs[offset] * incr;
*(double*)addr = regs.fregs[src];
break;
}
// ---
case Op_LoadImm32: {
Reg dst = line_dst(line);
regs.iregs[dst] = eat_i32(&pc);
break;
}
case Op_LoadImm64: {
Reg dst = line_dst(line);
regs.iregs[dst] = eat_i64(&pc);
break;
}
case Op_LoadImmF: {
Reg dst = line_dst(line);
regs.fregs[dst] = eat_f64(&pc);
break;
}
case Op_LoadSb: {
Reg dst = line_dst(line);
regs.iregs[dst] = (uint64_t)sb;
break;
}
case Op_LoadSp: {
Reg dst = line_dst(line);
regs.iregs[dst] = (uint64_t)sp;
break;
}
// ---
case Op_MovII: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.iregs[dst] = regs.iregs[src];
break;
}
case Op_MovIF: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.fregs[dst] = (double)regs.iregs[src];
break;
}
case Op_MovFI: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.iregs[dst] = (uint64_t)regs.fregs[src];
break;
}
case Op_MovFF: {
Reg dst = line_dst(line);
Reg src = line_src_left(line);
regs.fregs[dst] = regs.fregs[src];
break;
}
case Op_Push: {
Reg src = line_src_left(line);
*sp = regs.iregs[src];
++sp;
break;
}
case Op_Pop: {
Reg dst = line_dst(line);
--sp;
regs.iregs[dst] = *sp;
break;
}
case Op_PushF: {
Reg src = line_src_left(line);
*sp = (uint64_t)regs.fregs[src];
++sp;
break;
}
case Op_PopF: {
Reg dst = line_dst(line);
--sp;
regs.fregs[dst] = (double)*sp;
break;
}
// ---
case Op_Eq: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] == regs.iregs[right] ? 1 : 0;
break;
}
case Op_Ne: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] != regs.iregs[right] ? 1 : 0;
break;
}
case Op_Lt: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] < regs.iregs[right] ? 1 : 0;
break;
}
case Op_Gt: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] > regs.iregs[right] ? 1 : 0;
break;
}
case Op_Lte: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] <= regs.iregs[right] ? 1 : 0;
break;
}
case Op_Gte: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] >= regs.iregs[right] ? 1 : 0;
break;
}
case Op_And: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] & regs.iregs[right];
break;
}
case Op_Or: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] | regs.iregs[right];
break;
}
case Op_Xor: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] ^ regs.iregs[right];
break;
}
case Op_Add: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] + regs.iregs[right];
break;
}
case Op_Sub: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] - regs.iregs[right];
break;
}
case Op_Mul: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] * regs.iregs[right];
break;
}
case Op_Div: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] / regs.iregs[right];
break;
}
case Op_Rem: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.iregs[left] / regs.iregs[right];
break;
}
case Op_IMul: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
int64_t val
= (int64_t)regs.iregs[left] * (int64_t)regs.iregs[right];
regs.iregs[dst] = (uint64_t)val;
break;
}
case Op_IDiv: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
int64_t val
= (int64_t)regs.iregs[left] / (int64_t)regs.iregs[right];
regs.iregs[dst] = (uint64_t)val;
break;
}
// ---
case Op_EqI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] == right ? 1 : 0;
break;
}
case Op_NeI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] != right ? 1 : 0;
break;
}
case Op_LtI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] < right ? 1 : 0;
break;
}
case Op_GtI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] > right ? 1 : 0;
break;
}
case Op_LteI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] <= right ? 1 : 0;
break;
}
case Op_GteI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] >= right ? 1 : 0;
break;
}
case Op_AndI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] & right;
break;
}
case Op_OrI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] | right;
break;
}
case Op_XorI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] ^ right;
break;
}
case Op_AddI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] + right;
break;
}
case Op_SubI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] - right;
break;
}
case Op_RSubI: {
Reg dst = line_dst(line);
uint64_t left = eat_i32(&pc);
Reg right = line_src_right(line);
regs.iregs[dst] = left - regs.iregs[right];
break;
}
case Op_MulI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] * right;
break;
}
case Op_DivI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] / right;
break;
}
case Op_RemI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
regs.iregs[dst] = regs.iregs[left] % right;
break;
}
case Op_IMulI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
int64_t val = (int64_t)regs.iregs[left] * (int64_t)right;
regs.iregs[dst] = (uint64_t)val;
break;
}
case Op_IDivI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
uint64_t right = eat_i32(&pc);
int64_t val = (int64_t)regs.iregs[left] / (int64_t)right;
regs.iregs[dst] = (uint64_t)val;
break;
}
// ---
case Op_EqF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.fregs[left] == regs.fregs[right] ? 1 : 0;
break;
}
case Op_NeF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.fregs[left] != regs.fregs[right] ? 1 : 0;
break;
}
case Op_LtF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.fregs[left] < regs.fregs[right] ? 1 : 0;
break;
}
case Op_GtF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.fregs[left] > regs.fregs[right] ? 1 : 0;
break;
}
case Op_LteF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.fregs[left] <= regs.fregs[right] ? 1 : 0;
break;
}
case Op_GteF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.iregs[dst] = regs.fregs[left] >= regs.fregs[right] ? 1 : 0;
break;
}
case Op_AddF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.fregs[dst] = regs.fregs[left] + regs.fregs[right];
break;
}
case Op_SubF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.fregs[dst] = regs.fregs[left] - regs.fregs[right];
break;
}
case Op_MulF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.fregs[dst] = regs.fregs[left] * regs.fregs[right];
break;
}
case Op_DivF: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
Reg right = line_src_right(line);
regs.fregs[dst] = regs.fregs[left] / regs.fregs[right];
break;
}
// ---
case Op_EqFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.iregs[dst] = regs.fregs[left] == right ? 1 : 0;
break;
}
case Op_NeFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.iregs[dst] = regs.fregs[left] != right ? 1 : 0;
break;
}
case Op_LtFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.iregs[dst] = regs.fregs[left] < right ? 1 : 0;
break;
}
case Op_GtFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.iregs[dst] = regs.fregs[left] > right ? 1 : 0;
break;
}
case Op_LteFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.iregs[dst] = regs.fregs[left] <= right ? 1 : 0;
break;
}
case Op_GteFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.iregs[dst] = regs.fregs[left] >= right ? 1 : 0;
break;
}
case Op_AddFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.fregs[dst] = regs.fregs[left] + right;
break;
}
case Op_SubFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.fregs[dst] = regs.fregs[left] - right;
break;
}
case Op_RSubFI: {
Reg dst = line_dst(line);
double left = eat_f64(&pc);
Reg right = line_src_right(line);
regs.fregs[dst] = left - regs.fregs[right];
break;
}
case Op_MulFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.fregs[dst] = regs.fregs[left] * right;
break;
}
case Op_DivFI: {
Reg dst = line_dst(line);
Reg left = line_src_left(line);
double right = eat_f64(&pc);
regs.fregs[dst] = regs.fregs[left] / right;
break;
}
}
}
halt_program:
free(stack);
free(call_stack_base);
return 0;
}
int vm_exec_builtin(VM* vm, Builtin builtin, Regs* regs)
{
MAYBE_UNUSED uint64_t* res = &regs->iregs[0];
MAYBE_UNUSED uint64_t arg1 = regs->iregs[1];
MAYBE_UNUSED uint64_t arg2 = regs->iregs[2];
MAYBE_UNUSED uint64_t arg3 = regs->iregs[3];
MAYBE_UNUSED uint64_t arg4 = regs->iregs[4];
switch (builtin) {
case Builtin_Alloc: {
break;
}
case Builtin_FsOpen: {
uint64_t id = 0;
int r = vm_open_file(vm, &id, (char*)arg1, (char*)arg2);
*res = r == 0 ? 0 : 1;
break;
}
case Builtin_FsClose: {
vm_close_file(vm, arg1);
break;
}
case Builtin_FsWrite:
fwrite((void*)arg2, arg3, 1, vm_file_fp(vm, arg1));
break;
case Builtin_FsRead:
fread((void*)arg2, arg3, 1, vm_file_fp(vm, arg1));
break;
case Builtin_FsFlush:
fflush(vm_file_fp(vm, arg1));
break;
case Builtin_FsEof:
*res = feof(vm_file_fp(vm, arg1)) == EOF ? 1 : 0;
break;
}
return 0;
}
int vm_open_file(VM* vm, uint64_t* id, const char* path, const char* mode)
{
FILE* fp = fopen(path, mode);
if (fp == NULL)
return -1;
*id = vm->next_file_id;
++vm->next_file_id;
vm->files[vm->files_size++] = (FsFile) { *id, fp };
return 0;
}
void vm_close_file(VM* vm, uint64_t id)
{
size_t i = 0;
for (; i < vm->files_size; ++i)
if (vm->files[i].id == id)
break;
if (i == vm->files_size)
return;
for (size_t j = i; j < vm->files_size - 1; ++j)
vm->files[j] = vm->files[j + 1];
}
FILE* vm_file_fp(VM* vm, uint64_t id)
{
for (size_t i = 0; i < vm->files_size; ++i)
if (vm->files[i].id == id)
return vm->files[i].fp;
return NULL;
}

View File

@ -1,221 +0,0 @@
#ifndef VM_H
#define VM_H
#include <stdint.h>
#include <stdio.h>
/// Instruction operations.
///
/// These definitions dictate how instructions should be decoded.
///
/// `%ireg` is an int or pointer register.
/// `%i32` is a 32-bit immediate int value.
/// `%i64` is a 64-bit immediate int value.
///
/// `%freg` is a float register.
/// `%f64` is a 64-bit immediate float value.
///
/// The instruction header is 4 bytes. Usually instruction headers are encoding
/// like this:
/// ```
/// XX = 1 byte
///
/// XX XX XX XX
/// ^^ ^^ ^^ ^^ - Op
/// | | |
/// | | ++-- destination register
/// | |
/// | ++-- right/second operand register
/// |
/// ++--- left/first operand register
/// ```
///
/// Then immediate values are appended after the header.
///
/// Immediates are little endian. This means that the smallest byte has the
/// smallest address. Example: take a value like `4660`, convert it to hex
/// `0x1234`, split up in bytes `12 34`, put it into an array `[12, 34]`. This
/// is big endian ie. WRONG. Put the bytes `12 34` into the array in reverse
/// order `[34, 12]`. This is little endiang ie. CORRECT.
///
typedef enum {
Op_Nop,
Op_Halt,
Op_Builtin,
Op_Call,
Op_CallI,
Op_Ret,
Op_Alloca,
Op_Jmp,
Op_Jnz,
Op_Jz,
Op_Load8,
Op_LoadI8,
Op_LoadA8,
Op_Load16,
Op_LoadI16,
Op_LoadA16,
Op_Load32,
Op_LoadI32,
Op_LoadA32,
Op_Load64,
Op_LoadI64,
Op_LoadA64,
Op_LoadF,
Op_LoadIF,
Op_LoadAF,
Op_Store8,
Op_StoreA8,
Op_Store16,
Op_StoreA16,
Op_Store32,
Op_StoreA32,
Op_Store64,
Op_StoreA64,
Op_StoreF,
Op_StoreAF,
Op_LoadImm32,
Op_LoadImm64,
Op_LoadImmF,
Op_LoadSb,
Op_LoadSp,
Op_MovII,
Op_MovIF,
Op_MovFI,
Op_MovFF,
Op_Push,
Op_Pop,
Op_PushF,
Op_PopF,
Op_Eq,
Op_Ne,
Op_Lt,
Op_Gt,
Op_Lte,
Op_Gte,
Op_And,
Op_Or,
Op_Xor,
Op_Add,
Op_Sub,
Op_Mul,
Op_Div,
Op_Rem,
Op_IMul,
Op_IDiv,
Op_EqI,
Op_NeI,
Op_LtI,
Op_GtI,
Op_LteI,
Op_GteI,
Op_AndI,
Op_OrI,
Op_XorI,
Op_AddI,
Op_SubI,
Op_RSubI,
Op_MulI,
Op_DivI,
Op_RemI,
Op_IMulI,
Op_IDivI,
Op_EqF,
Op_NeF,
Op_LtF,
Op_GtF,
Op_LteF,
Op_GteF,
Op_AddF,
Op_SubF,
Op_MulF,
Op_DivF,
Op_EqFI,
Op_NeFI,
Op_LtFI,
Op_GtFI,
Op_LteFI,
Op_GteFI,
Op_AddFI,
Op_SubFI,
Op_RSubFI,
Op_MulFI,
Op_DivFI,
} Op;
typedef enum {
Builtin_Alloc,
Builtin_FsOpen,
Builtin_FsClose,
Builtin_FsWrite,
Builtin_FsRead,
Builtin_FsFlush,
Builtin_FsEof,
} Builtin;
#define IREGS 32
#define FREGS 16
#define STACK_SIZE 65536
#define CALL_STACK_SIZE 65536
typedef struct {
uint64_t id;
FILE* fp;
} FsFile;
///
/// Main data structure for the runtime virtual machine.
///
/// NOTICE: This structure is not necessarily used actively when running the
/// program. This is because the runner caches some values instead of storing
/// them in the struct. The point of this struct is mostly to provide some form
/// of cohesion of the virtual machine. This means that for debugging, this
/// internals of this struct CANNOT be relied on for live data.
///
typedef struct {
FsFile* files;
size_t files_size;
size_t files_capacity;
uint64_t next_file_id;
} VM;
void vm_construct(VM* vm);
void vm_destroy(VM* vm);
typedef uint8_t Reg;
typedef struct {
uint64_t iregs[IREGS];
double fregs[FREGS];
} Regs;
typedef struct {
uint64_t* caller_sb;
const uint32_t* return_ptr;
} Call;
/// Runner function for the VM.
///
/// 'program` is a program encoded according to the encoding rules. The program
/// is expected to exit before hitting the end, hence there's no size parameter.
int vm_run(VM* vm, const uint32_t* program);
int vm_exec_builtin(VM* vm, Builtin builtin, Regs* regs);
int vm_open_file(VM* vm, uint64_t* id, const char* path, const char* mode);
void vm_close_file(VM* vm, uint64_t id);
FILE* vm_file_fp(VM* vm, uint64_t id);
#endif