lang/vm.c
r4 ba8d2f0702 add nilptrs, more conversion functions and change how ptrs work
Pointers now no longer point to the Value struct (the internal wrapper for
values) but to the value itself.
2021-12-28 17:53:27 +01:00

145 lines
3.7 KiB
C

#include "vm.h"
#include "runtime.h"
#include "util.h"
#define INIT_STACK_CAP 256
typedef struct Stack {
Value *mem;
size_t len, cap;
} Stack;
static Stack stack_make(void);
static void stack_term(Stack *s);
static void stack_fit(Stack *s, size_t idx);
static Stack stack_make(void) {
Stack s;
s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP);
s.cap = INIT_STACK_CAP;
s.len = 0;
return s;
}
static void stack_term(Stack *s) {
free(s->mem);
}
static void stack_fit(Stack *s, size_t idx) {
size_t size = idx+1;
if (size > s->cap) {
s->mem = xrealloc(s->mem, sizeof(Value) * (size + (s->cap *= 2)));
}
}
static Value *irparam_to_val(Stack *s, IRParam *v) {
if (v->kind == IRParamLiteral)
return &v->Literal;
else if (v->kind == IRParamAddr)
return &s->mem[v->Addr];
else
ASSERT_UNREACHED();
}
void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) {
/* so we don't have to call malloc on every function call */
size_t fn_args_cap = 16;
Value *fn_args = xmalloc(sizeof(Value) * fn_args_cap);
Stack s = stack_make();
for (size_t i = 0; i < ir->len;) {
IRTok *instr = &ir->toks[i];
err_ln = instr->ln;
err_col = instr->col;
switch (instr->instr) {
case IRSet:
case IRNeg:
case IRNot:
stack_fit(&s, instr->Unary.addr);
TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)),
{free(fn_args); stack_term(&s);});
break;
case IRAddrOf:
if (instr->Unary.val.kind != IRParamAddr) {
set_err("Unable to take the address of a literal");
return;
}
Value *v = &s.mem[instr->Unary.val.Addr];
s.mem[instr->Unary.addr] = (Value){
.type = TypePtr,
.Ptr = {
.type = v->type,
.val = &v->Void,
},
};
break;
case IRAdd:
case IRSub:
case IRDiv:
case IRMul:
case IREq:
case IRNeq:
case IRLt:
case IRLe:
case IRAnd:
case IROr:
stack_fit(&s, instr->Binary.addr);
TRY_ELSE(s.mem[instr->Binary.addr] = eval_binary(instr->instr,
irparam_to_val(&s, &instr->Binary.lhs),
irparam_to_val(&s, &instr->Binary.rhs)),
{free(fn_args); stack_term(&s);});
break;
case IRJmp:
i = instr->Jmp.iaddr;
continue;
case IRJnz:
if (is_nonzero(irparam_to_val(&s, &instr->CJmp.condition))) {
i = instr->Jmp.iaddr;
continue;
}
break;
case IRCallInternal: {
const BuiltinFunc *f = &builtin_funcs[instr->CallI.fid];
size_t n_args = instr->CallI.n_args;
/* make sure enough space for our arguments is allocated */
if (n_args > fn_args_cap)
fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = n_args));
/* copy arguments into buffer */
for (size_t i = 0; i < n_args; i++)
fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
if (f->returns) {
stack_fit(&s, instr->CallI.ret_addr);
if (f->kind == FuncVarArgs) {
size_t min_args = f->VarArgs.min_args;
TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->VarArgs.WithRet.func(n_args - min_args, fn_args),
{free(fn_args); stack_term(&s);});
} else if (f->kind == FuncFixedArgs) {
TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->FixedArgs.WithRet.func(fn_args),
{free(fn_args); stack_term(&s);});
} else
ASSERT_UNREACHED();
} else {
if (f->kind == FuncVarArgs) {
size_t min_args = f->VarArgs.min_args;
TRY_ELSE(f->VarArgs.NoRet.func(n_args - min_args, fn_args),
{free(fn_args); stack_term(&s);});
} else if (f->kind == FuncFixedArgs) {
TRY_ELSE(f->FixedArgs.NoRet.func(fn_args),
{free(fn_args); stack_term(&s);});
} else
ASSERT_UNREACHED();
}
break;
}
default:
ASSERT_UNREACHED();
}
i++;
}
stack_term(&s);
free(fn_args);
}