From 0ffe4b600df51fd8ee5658f9a9296efe86cb67ea Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 30 Oct 2016 19:20:47 +0000 Subject: [PATCH] Interpreter - call internal procedures --- code/demo.odin | 5 + src/ssa/make.cpp | 1 + src/ssa/ssa.cpp | 1 + src/vm/vm.cpp | 575 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 582 insertions(+) create mode 100644 src/vm/vm.cpp diff --git a/code/demo.odin b/code/demo.odin index 0899cc111..b29ef97ec 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,6 +1,11 @@ #import "fmt.odin" main :: proc() { + foo :: proc(x: i64) -> i64 { + return -x + 1 + } + x, y: i64 = 123, 321 y = x + 2 - y + x = foo(y) } diff --git a/src/ssa/make.cpp b/src/ssa/make.cpp index 8441dac5d..47ae3a305 100644 --- a/src/ssa/make.cpp +++ b/src/ssa/make.cpp @@ -450,6 +450,7 @@ ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = tr instr->Instr.parent = b; array_add(&b->instrs, instr); array_add(&b->locals, instr); + proc->local_count++; // if (zero_initialized) { ssa_emit_zero_init(proc, instr); diff --git a/src/ssa/ssa.cpp b/src/ssa/ssa.cpp index a28f84e26..d8c4bca65 100644 --- a/src/ssa/ssa.cpp +++ b/src/ssa/ssa.cpp @@ -103,6 +103,7 @@ struct ssaProcedure { ssaTargetList * target_list; Array referrers; + i32 local_count; i32 instr_count; i32 block_count; }; diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp new file mode 100644 index 000000000..e806bcdfb --- /dev/null +++ b/src/vm/vm.cpp @@ -0,0 +1,575 @@ +struct VirtualMachine; + +struct vmValueProc { + ssaProcedure *proc; // If `NULL`, use `ptr` instead and call external procedure + void * ptr; +}; + + +struct vmValue { + // NOTE(bill): Shouldn't need to store type here as the type checking + // has already been handled in the SSA + union { + f32 val_f32; + f64 val_f64; + void * val_ptr; + i64 val_int; + vmValueProc val_proc; + Array val_comp; // NOTE(bill): Will be freed through stack + }; +}; + +vmValue vm_make_value_ptr(void *ptr) { + vmValue v = {}; + v.val_ptr = ptr; + return v; +} + + +struct vmFrame { + VirtualMachine * vm; + vmFrame * caller; + ssaProcedure * curr_proc; + ssaBlock * curr_block; + isize instr_index; + + Map values; // Key: ssaValue * + gbTempArenaMemory temp_arena_memory; + gbAllocator stack_allocator; + Array locals; // Memory to locals + vmValue result; +}; + +struct VirtualMachine { + ssaModule * module; + gbArena stack_arena; + gbAllocator stack_allocator; + gbAllocator heap_allocator; + Array frame_stack; + Map globals; // Key: ssaValue * + vmValue exit_value; +}; + +void vm_exec_instr(VirtualMachine *vm, ssaValue *value); + +vmFrame *vm_back_frame(VirtualMachine *vm) { + if (vm->frame_stack.count > 0) { + return &vm->frame_stack[vm->frame_stack.count-1]; + } + return NULL; +} + + + +void vm_init(VirtualMachine *vm, ssaModule *module) { + gb_arena_init_from_allocator(&vm->stack_arena, heap_allocator(), gb_megabytes(64)); + + vm->module = module; + vm->stack_allocator = gb_arena_allocator(&vm->stack_arena); + vm->heap_allocator = heap_allocator(); + array_init(&vm->frame_stack, vm->heap_allocator); + map_init(&vm->globals, vm->heap_allocator); +} +void vm_destroy(VirtualMachine *vm) { + array_free(&vm->frame_stack); + map_destroy(&vm->globals); + gb_arena_free(&vm->stack_arena); +} + + + + + +i64 vm_type_size_of(VirtualMachine *vm, Type *type) { + return type_size_of(vm->module->sizes, vm->heap_allocator, type); +} +i64 vm_type_align_of(VirtualMachine *vm, Type *type) { + return type_align_of(vm->module->sizes, vm->heap_allocator, type); +} +i64 vm_type_offset_of(VirtualMachine *vm, Type *type, i64 offset) { + return type_offset_of(vm->module->sizes, vm->heap_allocator, type, offset); +} + +void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) { + map_set(&f->values, hash_pointer(v), val); +} + + + +vmFrame *vm_push_frame(VirtualMachine *vm, ssaProcedure *proc) { + vmFrame frame = {}; + + frame.vm = vm; + frame.curr_proc = proc; + frame.curr_block = proc->blocks[0]; + frame.instr_index = 0; + frame.caller = vm_back_frame(vm); + frame.stack_allocator = vm->stack_allocator; + frame.temp_arena_memory = gb_temp_arena_memory_begin(&vm->stack_arena); + + map_init(&frame.values, vm->heap_allocator); + array_init(&frame.locals, vm->heap_allocator, proc->local_count); + array_add(&vm->frame_stack, frame); + return vm_back_frame(vm); +} + +void vm_pop_frame(VirtualMachine *vm) { + vmFrame *f = vm_back_frame(vm); + + gb_temp_arena_memory_end(f->temp_arena_memory); + array_free(&f->locals); + map_destroy(&f->values); + + array_pop(&vm->frame_stack); + +} + +vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array values) { + GB_ASSERT_MSG(proc->params.count == values.count, + "Incorrect number of arguments passed into procedure call!"); + + + vmValue result = {}; + + if (proc->body == NULL) { + GB_PANIC("TODO(bill): external procedure"); + return result; + } + gb_printf("call: %.*s\n", LIT(proc->name)); + + vmFrame *f = vm_push_frame(vm, proc); + for_array(i, proc->params) { + vm_set_value(f, proc->params[i], values[i]); + } + + while (f->curr_block != NULL) { + ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++]; + vm_exec_instr(vm, curr_instr); + } + + if (base_type(proc->type)->Proc.result_count > 0) { + result = f->result; + } + vm_pop_frame(vm); + return result; +} + + +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value) { + vmFrame *f = vm_back_frame(vm); + vmValue v = {}; + switch (value->kind) { + case ssaValue_Constant: { + auto *c = &value->Constant; + Type *t = base_type(c->type); + // i64 size = vm_type_size_of(vm, t); + if (is_type_boolean(t)) { + v.val_int = c->value.value_bool != 0; + } else if (is_type_integer(t)) { + v.val_int = c->value.value_integer; + } else if (is_type_float(t)) { + if (t->Basic.kind == Basic_f32) { + v.val_f32 = cast(f32)c->value.value_float; + } else if (t->Basic.kind == Basic_f64) { + v.val_f64 = cast(f64)c->value.value_float; + } + } else if (is_type_pointer(t)) { + v.val_ptr = cast(void *)cast(intptr)c->value.value_pointer; + } else if (is_type_string(t)) { + array_init(&v.val_comp, vm->heap_allocator, 2); + + String str = c->value.value_string; + i64 len = str.len; + u8 *text = gb_alloc_array(vm->heap_allocator, u8, len); + gb_memcopy(text, str.text, len); + + vmValue data = {}; + vmValue count = {}; + data.val_ptr = text; + count.val_int = len; + array_add(&v.val_comp, data); + array_add(&v.val_comp, count); + } else { + GB_PANIC("TODO(bill): Other constant types: %s", type_to_string(c->type)); + } + } break; + case ssaValue_ConstantSlice: { + array_init(&v.val_comp, vm->heap_allocator, 3); + + auto *cs = &value->ConstantSlice; + vmValue data = {}; + vmValue count = {}; + data = vm_operand_value(vm, cs->backing_array); + count.val_int = cs->count; + array_add(&v.val_comp, data); + array_add(&v.val_comp, count); + array_add(&v.val_comp, count); + } break; + case ssaValue_Nil: + GB_PANIC("TODO(bill): ssaValue_Nil"); + break; + case ssaValue_TypeName: + GB_PANIC("TODO(bill): ssaValue_TypeName"); + break; + case ssaValue_Global: + v = *map_get(&vm->globals, hash_pointer(value)); + break; + case ssaValue_Param: + v = *map_get(&f->values, hash_pointer(value)); + break; + case ssaValue_Proc: { + v.val_proc.proc = &value->Proc; + // GB_PANIC("TODO(bill): ssaValue_Proc"); + } break; + case ssaValue_Block: + GB_PANIC("TODO(bill): ssaValue_Block"); + break; + case ssaValue_Instr: { + vmValue *found = map_get(&f->values, hash_pointer(value)); + if (found) { + v = *found; + } + } break; + } + + return v; +} + +void vm_store_integer(VirtualMachine *vm, vmValue *dst, vmValue val, i64 store_bytes) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + gb_memcopy(&dst->val_int, &val.val_int, store_bytes); + +} + +void vm_store(VirtualMachine *vm, vmValue *dst, vmValue val, Type *type) { + i64 size = vm_type_size_of(vm, type); + type = base_type(type); + + // TODO(bill): I assume little endian here + + switch (type->kind) { + case Type_Basic: + switch (type->Basic.kind) { + case Basic_bool: + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_int: + case Basic_uint: + vm_store_integer(vm, dst, val, size); + break; + case Basic_f32: + dst->val_f32 = val.val_f32; + break; + case Basic_f64: + dst->val_f64 = val.val_f64; + break; + case Basic_rawptr: + dst->val_ptr = val.val_ptr; + break; + default: + GB_PANIC("TODO(bill): other basic types for `vm_store`"); + break; + } + break; + + default: + GB_PANIC("TODO(bill): other types for `vm_store`"); + break; + } +} + +vmValue vm_load_integer(VirtualMachine *vm, vmValue *ptr, i64 store_bytes) { + // TODO(bill): I assume little endian here + vmValue v = {}; + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr->val_ptr, store_bytes); + return v; +} + +vmValue vm_load(VirtualMachine *vm, vmValue *ptr, Type *type) { + i64 size = vm_type_size_of(vm, type); + type = base_type(type); + + vmValue v = {}; + + switch (type->kind) { + case Type_Basic: + switch (type->Basic.kind) { + case Basic_bool: + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_int: + case Basic_uint: + v = vm_load_integer(vm, ptr, size); + break; + case Basic_f32: + v.val_f32 = *cast(f32 *)ptr; + break; + case Basic_f64: + v.val_f64 = *cast(f64 *)ptr; + break; + case Basic_rawptr: + v.val_ptr = *cast(void **)ptr; + break; + default: + GB_PANIC("TODO(bill): other basic types for `vm_load`"); + break; + } + break; + + default: + GB_PANIC("TODO(bill): other types for `vm_load`"); + break; + } + + return v; +} + + +void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + vmFrame *f = vm_back_frame(vm); + +#if 0 + if (instr->kind != ssaInstr_Comment) { + gb_printf("exec_instr: %.*s\n", LIT(ssa_instr_strings[instr->kind])); + } +#endif + + switch (instr->kind) { + case ssaInstr_StartupRuntime: { +#if 0 + ssaValue *v = ssa_lookup_member(vm->module, make_string(SSA_STARTUP_RUNTIME_PROC_NAME)); + GB_ASSERT(v->kind == ssaValue_Proc); + ssaProcedure *proc = &v->Proc; + Array args = {}; // Empty + vm_call_procedure(vm, proc, args); // NOTE(bill): No return value +#endif + } break; + + case ssaInstr_Comment: + break; + + case ssaInstr_Local: { + Type *type = ssa_type(value); + isize size = gb_max(1, vm_type_size_of(vm, type)); + isize align = gb_max(1, vm_type_align_of(vm, type)); + void *memory = gb_alloc_align(vm->stack_allocator, size, align); + GB_ASSERT(memory != NULL); + vmValue v = vm_make_value_ptr(memory); + vm_set_value(f, value, v); + array_add(&f->locals, memory); + } break; + + case ssaInstr_ZeroInit: { + + } break; + + case ssaInstr_Store: { + vmValue addr = vm_operand_value(vm, instr->Store.address); + vmValue val = vm_operand_value(vm, instr->Store.value); + vmValue *address = cast(vmValue *)addr.val_ptr; + Type *t = ssa_type(instr->Store.value); + vm_store(vm, address, val, t); + } break; + + case ssaInstr_Load: { + vmValue addr = vm_operand_value(vm, instr->Load.address); + vmValue v = vm_load(vm, &addr, ssa_type(value)); + vm_set_value(f, value, v); + } break; + + case ssaInstr_ArrayElementPtr: { + vmValue address = vm_operand_value(vm, instr->ArrayElementPtr.address); + vmValue elem_index = vm_operand_value(vm, instr->ArrayElementPtr.elem_index); + + Type *t = ssa_type(instr->ArrayElementPtr.address); + i64 elem_size = vm_type_size_of(vm, type_deref(t)); + void *ptr = cast(u8 *)address.val_ptr + elem_index.val_int*elem_size; + vm_set_value(f, value, vm_make_value_ptr(ptr)); + } break; + + case ssaInstr_StructElementPtr: { + vmValue address = vm_operand_value(vm, instr->StructElementPtr.address); + i32 elem_index = instr->StructElementPtr.elem_index; + + Type *t = ssa_type(instr->StructElementPtr.address); + i64 offset_in_bytes = vm_type_offset_of(vm, type_deref(t), elem_index); + void *ptr = cast(u8 *)address.val_ptr + offset_in_bytes; + vm_set_value(f, value, vm_make_value_ptr(ptr)); + } break; + + case ssaInstr_PtrOffset: { + Type *t = ssa_type(instr->PtrOffset.address); + i64 elem_size = vm_type_size_of(vm, type_deref(t)); + vmValue address = vm_operand_value(vm, instr->PtrOffset.address); + vmValue offset = vm_operand_value(vm, instr->PtrOffset.offset); + + void *ptr = cast(u8 *)address.val_ptr + offset.val_int*elem_size; + vm_set_value(f, value, vm_make_value_ptr(ptr)); + } break; + + case ssaInstr_Phi: { + + } break; + + case ssaInstr_ArrayExtractValue: { + vmValue s = vm_operand_value(vm, instr->ArrayExtractValue.address); + vmValue v = s.val_comp[instr->ArrayExtractValue.index]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_StructExtractValue: { + vmValue s = vm_operand_value(vm, instr->StructExtractValue.address); + vmValue v = s.val_comp[instr->StructExtractValue.index]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_Jump: { + f->curr_block = instr->Jump.block; + f->instr_index = 0; + } break; + + case ssaInstr_If: {; + vmValue cond = vm_operand_value(vm, instr->If.cond); + if (cond.val_int != 0) { + f->curr_block = instr->If.true_block; + } else { + f->curr_block = instr->If.false_block; + } + f->instr_index = 0; + } break; + + case ssaInstr_Return: { + Type *return_type = NULL; + vmValue result = {}; + + if (instr->Return.value != NULL) { + return_type = ssa_type(instr->Return.value); + result = vm_operand_value(vm, instr->Return.value); + } + + f->result = result; + f->curr_block = NULL; + return; + } break; + + case ssaInstr_Conv: { + + } break; + + case ssaInstr_Unreachable: { + GB_PANIC("Unreachable"); + } break; + + case ssaInstr_BinaryOp: { + auto *bo = &instr->BinaryOp; + Type *t = base_type(ssa_type(bo->left)); + Type *et = t; + while (et->kind == Type_Vector) { + et = base_type(et->Vector.elem); + } + + if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + GB_PANIC("TODO(bill): Comparison operations"); + } else { + vmValue v = {}; + vmValue l = vm_operand_value(vm, bo->left); + vmValue r = vm_operand_value(vm, bo->right); + + if (is_type_integer(t)) { + switch (bo->op) { + case Token_Add: v.val_int = l.val_int + r.val_int; break; + case Token_Sub: v.val_int = l.val_int - r.val_int; break; + case Token_And: v.val_int = l.val_int & r.val_int; break; + case Token_Or: v.val_int = l.val_int | r.val_int; break; + case Token_Xor: v.val_int = l.val_int ^ r.val_int; break; + case Token_Shl: v.val_int = l.val_int << r.val_int; break; + case Token_Shr: v.val_int = l.val_int >> r.val_int; break; + case Token_Mul: v.val_int = l.val_int * r.val_int; break; + case Token_Not: v.val_int = l.val_int ^ r.val_int; break; + + case Token_AndNot: v.val_int = l.val_int & (~r.val_int); break; + + case Token_Quo: GB_PANIC("TODO(bill): BinaryOp Integer Token_Quo"); break; + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp Integer Token_Mod"); break; + + } + } else if (is_type_float(t)) { + GB_PANIC("TODO(bill): Float BinaryOp"); + } else { + GB_PANIC("TODO(bill): Vector BinaryOp"); + } + + vm_set_value(f, value, v); + } + } break; + + case ssaInstr_Call: { + Array args = {}; + array_init(&args, f->stack_allocator, instr->Call.arg_count); + for (isize i = 0; i < instr->Call.arg_count; i++) { + array_add(&args, vm_operand_value(vm, instr->Call.args[i])); + } + vmValue proc = vm_operand_value(vm, instr->Call.value); + if (proc.val_proc.proc != NULL) { + vmValue result = vm_call_procedure(vm, proc.val_proc.proc, args); + vm_set_value(f, value, result); + } else { + GB_PANIC("TODO(bill): external procedure calls"); + } + + } break; + + case ssaInstr_Select: { + vmValue v = {}; + vmValue cond = vm_operand_value(vm, instr->Select.cond); + if (cond.val_int != 0) { + v = vm_operand_value(vm, instr->Select.true_value); + } else { + v = vm_operand_value(vm, instr->Select.false_value); + } + + vm_set_value(f, value, v); + } break; + + case ssaInstr_VectorExtractElement: { + + } break; + + case ssaInstr_VectorInsertElement: { + + } break; + + case ssaInstr_VectorShuffle: { + + } break; + + case ssaInstr_BoundsCheck: { + + } break; + + case ssaInstr_SliceBoundsCheck: { + + } break; + + + default: { + GB_PANIC(" %d\n", instr->kind); + } break; + } +}