From b752990843c5266ea0422e4e0efd5083993af08e Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Wed, 5 Mar 2025 11:56:39 +0100 Subject: [PATCH] re-add runtime without backend --- slige/runtime/.clang-format | 8 + slige/runtime/.gitignore | 2 + slige/runtime/Makefile | 53 ++ slige/runtime/compile_flags.txt | 9 + slige/runtime/src/main.c | 6 + slige/runtime/src/util.h | 8 + slige/runtime/src/vm.c | 899 ++++++++++++++++++++++++++++++++ slige/runtime/src/vm.h | 221 ++++++++ 8 files changed, 1206 insertions(+) create mode 100644 slige/runtime/.clang-format create mode 100644 slige/runtime/.gitignore create mode 100644 slige/runtime/Makefile create mode 100644 slige/runtime/compile_flags.txt create mode 100644 slige/runtime/src/main.c create mode 100644 slige/runtime/src/util.h create mode 100644 slige/runtime/src/vm.c create mode 100644 slige/runtime/src/vm.h diff --git a/slige/runtime/.clang-format b/slige/runtime/.clang-format new file mode 100644 index 0000000..620973d --- /dev/null +++ b/slige/runtime/.clang-format @@ -0,0 +1,8 @@ +Language: Cpp +BasedOnStyle: WebKit +IndentWidth: 4 +ColumnLimit: 80 +IndentCaseLabels: true +InsertNewlineAtEOF: true +AllowShortFunctionsOnASingleLine: None + diff --git a/slige/runtime/.gitignore b/slige/runtime/.gitignore new file mode 100644 index 0000000..5e19c1d --- /dev/null +++ b/slige/runtime/.gitignore @@ -0,0 +1,2 @@ +build/ +database.db diff --git a/slige/runtime/Makefile b/slige/runtime/Makefile new file mode 100644 index 0000000..43961f8 --- /dev/null +++ b/slige/runtime/Makefile @@ -0,0 +1,53 @@ +# Slige 2 Runtime +# +# To build for debug: +# $ make +# +# To build for release: +# $ make RELEASE=1 +# +# NOTICE that `RELEASE=1` is __after__ `make` +# + +C_FLAGS = \ + -std=c17 \ + -Wall -Wextra -Wpedantic -Wconversion \ + -pedantic -pedantic-errors \ + +L_FLAGS = -lm -pthread $(shell pkg-config sqlite3 openssl --libs) +C_FLAGS = $(shell pkg-config sqlite3 openssl --cflags) + +F_FLAGS = +OPTIMIZATION = + +RELEASE=0 + +ifeq ($(RELEASE),1) + C_FLAGS += -Werror + F_FLAGS += -flto=auto + OPTIMIZATION += -O3 +else + C_FLAGS += -g + F_FLAGS += -fsanitize=address,undefined + OPTIMIZATION += -Og +endif + +HEADERS = $(shell find src/ -name *.h) +C_FILES = $(shell find src/ -name *.c) +O_FILES = $(patsubst src/%.c,build/%.o,$(C_FILES)) + +CC = gcc + +all: build_dir runtime + +runtime: $(O_FILES) + $(CC) -o build/$@ $^ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS) + +build/%.o: src/%.c $(HEADERS) + $(CC) $< -c -o $@ $(C_FLAGS) $(OPTIMIZATION) $(F_FLAGS) + +build_dir: + mkdir -p build/ + +clean: + rm -rf build/ diff --git a/slige/runtime/compile_flags.txt b/slige/runtime/compile_flags.txt new file mode 100644 index 0000000..916642e --- /dev/null +++ b/slige/runtime/compile_flags.txt @@ -0,0 +1,9 @@ +xc +-std=c17 +-Wall +-Wextra +-Wpedantic +-Wconversion +-pedantic +-pedantic-errors + diff --git a/slige/runtime/src/main.c b/slige/runtime/src/main.c new file mode 100644 index 0000000..80fe9e8 --- /dev/null +++ b/slige/runtime/src/main.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + printf("hello world\n"); +} diff --git a/slige/runtime/src/util.h b/slige/runtime/src/util.h new file mode 100644 index 0000000..a871719 --- /dev/null +++ b/slige/runtime/src/util.h @@ -0,0 +1,8 @@ +#ifndef UTIL_H +#define UTIL_H + +#define ALWAYS_INLINE __attribute__((always_inline)) + +#define MAYBE_UNUSED __attribute__((unused)) + +#endif diff --git a/slige/runtime/src/vm.c b/slige/runtime/src/vm.c new file mode 100644 index 0000000..8cdb31a --- /dev/null +++ b/slige/runtime/src/vm.c @@ -0,0 +1,899 @@ +#include "vm.h" +#include "util.h" +#include +#include +#include + +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 = ®s->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; +} diff --git a/slige/runtime/src/vm.h b/slige/runtime/src/vm.h new file mode 100644 index 0000000..155a891 --- /dev/null +++ b/slige/runtime/src/vm.h @@ -0,0 +1,221 @@ +#ifndef VM_H +#define VM_H + +#include +#include + +/// 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