mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
re-add runtime without backend
This commit is contained in:
parent
ed565a5733
commit
b752990843
8
slige/runtime/.clang-format
Normal file
8
slige/runtime/.clang-format
Normal file
@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: WebKit
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 80
|
||||
IndentCaseLabels: true
|
||||
InsertNewlineAtEOF: true
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
|
2
slige/runtime/.gitignore
vendored
Normal file
2
slige/runtime/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build/
|
||||
database.db
|
53
slige/runtime/Makefile
Normal file
53
slige/runtime/Makefile
Normal file
@ -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/
|
9
slige/runtime/compile_flags.txt
Normal file
9
slige/runtime/compile_flags.txt
Normal file
@ -0,0 +1,9 @@
|
||||
xc
|
||||
-std=c17
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wconversion
|
||||
-pedantic
|
||||
-pedantic-errors
|
||||
|
6
slige/runtime/src/main.c
Normal file
6
slige/runtime/src/main.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("hello world\n");
|
||||
}
|
8
slige/runtime/src/util.h
Normal file
8
slige/runtime/src/util.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||
|
||||
#define MAYBE_UNUSED __attribute__((unused))
|
||||
|
||||
#endif
|
899
slige/runtime/src/vm.c
Normal file
899
slige/runtime/src/vm.c
Normal file
@ -0,0 +1,899 @@
|
||||
#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 = ®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;
|
||||
}
|
221
slige/runtime/src/vm.h
Normal file
221
slige/runtime/src/vm.h
Normal file
@ -0,0 +1,221 @@
|
||||
#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
|
Loading…
x
Reference in New Issue
Block a user